Hi,
I am using an OpenAI Agent SDK for agentic workflow, which calls tools which are simple LLM calls.
I have a simple tool responsible for making a string into lowercase using LLM as well as an triage Agent responsible for picking up the right tool for the task, in this case there is only one tool. I run the code with the OpenAIAgentsTracingProcessor
defined.
This approach works well, the issue is that it created in the LangSmith UI a separate trace for the Agent vs Tool calls (LLM).
My understanding is that OpenAIAgentsTracingProcessor
and langsmith specific tracing either via @traceable
, wrap_openai
create different tracing session and thus everything is separate.
I tried to solve the issue of creation of separate traces by assigning custom metadata to both agent and tool calls but I can not assign the metadata to OpenAIAgentsTracingProcessor
.
How can I have the agent and tool call under the same trace?\ How can I add metadata to OpenAIAgentsTracingProcessor?
if __name__ == "__main__":
set_trace_processors([OpenAIAgentsTracingProcessor()])
asyncio.run(main())
import json
from typing import Any
from pydantic import BaseModel, Field
from agents import Agent, Runner, RunContextWrapper, function_tool, FunctionTool
from openai import AsyncOpenAI
from dotenv import load_dotenv
from langsmith import traceable
from langsmith.wrappers import wrap_openai
load_dotenv()
class TextInput(BaseModel):
text: str = Field(..., min_length=1, max_length=10_000, description="Text to process")
@traceable(run_type="tool", metadata={"daniel": "tool"})
async def _make_lowercase_on_invoke(
ctx: RunContextWrapper[Any],
args_json: str
) -> str:
"""Tool that uses LLM to convert text to lowercase."""
args = TextInput.model_validate_json(args_json)
client = wrap_openai(AsyncOpenAI(), tracing_extra={"metadata": {"service": "smart-text-agent", "team": "draftwise"}})
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Convert the given text to lowercase. Return only the result."},
{"role": "user", "content": f"Convert to lowercase: {args.text}"}
],
max_tokens=100,
temperature=0)
result = (response.choices[0].message.content or "").strip()
usage_info = {
"model": response.model,
"prompt_tokens": getattr(response.usage, "prompt_tokens", None),
"completion_tokens": getattr(response.usage, "completion_tokens", None),
"total_tokens": getattr(response.usage, "total_tokens", None),
"request_id": response.id,
"created": response.created,
"finish_reason": response.choices[0].finish_reason,
}
print(f"make_lowercase_tool tokens | {usage_info.get("total_tokens")}")
return json.dumps({
"result": result,
"operation": "lowercase",
"original_text": args.text,
"usage_metadata": {
"input_tokens": usage_info.get("prompt_tokens"),
"output_tokens": usage_info.get("completion_tokens"),
"total_tokens": usage_info.get("total_tokens")
}})
MAKE_LOWERCASE_SCHEMA = {
"type": "object",
"properties": {
"text": {
"type": "string",
"minLength": 1,
"maxLength": 10_000,
"description": "Text to process",
}
},
"required": ["text"],
"additionalProperties": False,}
make_lowercase_tool = FunctionTool(
name="make_lowercase_tool",
description="Convert the given text to lowercase.",
params_json_schema=MAKE_LOWERCASE_SCHEMA,
on_invoke_tool=_make_lowercase_on_invoke,
)
@traceable(run_type='llm', metadata={"daniel": "agent"})
async def run_smart_text_agent(user_query: str, text: str) -> str:
"""Run the agent that uses the tool to process the input."""
instructions = (
"You are a text processing agent. You have 3 tools:\n"
"- make_lowercase_tool: converts text to lowercase\n"
"- make_capitalize_tool: capitalizes first letter of each word\n"
"- count_words_tool: counts words in text\n\n"
"IMPORTANT: When given a text and a request, immediately use the appropriate tool to process the text. "
"Do NOT explain your capabilities - just do the work and return the results."
)
agent = Agent(
name="Smart Text Processing Agent",
instructions=instructions,
tools=[make_lowercase_tool], #, make_capitalize_tool, count_words_tool],
model="gpt-4o-mini-2024-07-18"
)
prompt = f"Process this text: '{text}'. Request: {user_query}. Use the appropriate tool immediately."
result = await Runner.run(agent, prompt)
# (optional) create agent insight summary
summary = []
for i, mr in enumerate(result.raw_responses):
model = result._last_agent.model
if i < len(result.new_items) and hasattr(result.new_items[i], "agent"):
model = result.new_items[i].agent.model or model
u = mr.usage
summary.append({
"step": i,
"model": model,
"requests": u.requests,
"input_tokens": u.input_tokens,
"output_tokens": u.output_tokens,
"total_tokens": u.total_tokens,
"response_id": getattr(mr, "response_id", None),
})
# print(summary)
print("Agent tokens |: ",result.context_wrapper.usage.total_tokens)
return result.final_output
The final output:
The
run_smart_text_agent
has the metadata labels I created both the {daniel: tool}
and {daniel: agent}
but the trace created by OpenAIAgentsTracingProcessor
i.e. Agent workflow
doesnt have any metadata and I cant attach any.