Skip to content

Commit ebf200d

Browse files
fix(openai-agents): correct root step timestamp and use semantic step types
Review of the OpenAI Agents SDK tracing processor (OPEN-10883). Addresses three issues affecting how traces render in Openlayer: 1. Root step timestamp (the reported bug): the synthetic "Agent Workflow" root step was constructed inside on_trace_end, so steps.Step.__init__ stamped its start_time with time.time() at trace-end. This placed the parent step after its own children on the timeline and produced an incorrect inferenceTimestamp for the whole trace. The root step is now anchored to the start time captured in on_trace_start, clamped to the earliest child start time. This confirms the issue was in the instrumentation rather than the UI. 2. Step types: agent, function and handoff spans were all emitted as generic UserCallStep, so the backend bucketed them as user_call and lost dedicated rendering. They now use the SDK's typed steps via step_factory (AgentStep/ToolStep/HandoffStep) with their typed fields populated, matching the convention used by the langchain/google_adk/claude_agent_sdk integrations. 3. New SDK span types (openai-agents 0.4.2): guardrail, mcp_tools, speech, transcription and speech_group spans previously fell through to the generic handler. They are now parsed and mapped to GuardrailStep, ToolStep and ChatCompletionStep (speech/transcription) respectively; speech_group remains a generic container step. The TracingProcessor interface itself is unchanged. Also adds a guardrail scenario to the existing examples/tracing/openai/openai_agents_tracing.ipynb notebook so it exercises the GUARDRAIL step type alongside the existing agent/tool/handoff steps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d6852f1 commit ebf200d

2 files changed

Lines changed: 292 additions & 13 deletions

File tree

examples/tracing/openai/openai_agents_tracing.ipynb

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,95 @@
467467
"response\n"
468468
]
469469
},
470+
{
471+
"cell_type": "markdown",
472+
"metadata": {},
473+
"source": [
474+
"## 11. Add an input guardrail\n",
475+
"\n",
476+
"Guardrails run *before* the main agent and can short-circuit a request. Here we add a relevance guardrail that blocks questions unrelated to airline customer service. A small classifier agent decides whether the message is on-topic; if not, the guardrail trips its tripwire and the SDK raises `InputGuardrailTripwireTriggered`.\n",
477+
"\n",
478+
"In Openlayer, the guardrail appears as a dedicated `GUARDRAIL` step whose `metadata.triggered` reflects the tripwire result."
479+
]
480+
},
470481
{
471482
"cell_type": "code",
472483
"execution_count": null,
473484
"metadata": {},
474485
"outputs": [],
475-
"source": []
486+
"source": [
487+
"from agents import (\n",
488+
" GuardrailFunctionOutput,\n",
489+
" InputGuardrailTripwireTriggered,\n",
490+
" input_guardrail,\n",
491+
")\n",
492+
"\n",
493+
"\n",
494+
"class RelevanceCheck(BaseModel):\n",
495+
" \"\"\"Output schema for the relevance classifier.\"\"\"\n",
496+
" is_relevant: bool\n",
497+
" reasoning: str\n",
498+
"\n",
499+
"\n",
500+
"relevance_agent = Agent(\n",
501+
" name=\"Relevance guardrail\",\n",
502+
" instructions=(\n",
503+
" \"Decide whether the user's message is related to airline customer \"\n",
504+
" \"service (flights, seats, baggage, check-in, wifi, etc.).\"\n",
505+
" ),\n",
506+
" output_type=RelevanceCheck,\n",
507+
")\n",
508+
"\n",
509+
"\n",
510+
"@input_guardrail\n",
511+
"async def relevance_guardrail(context, agent, user_input):\n",
512+
" \"\"\"Trip the tripwire when the request is off-topic.\"\"\"\n",
513+
" check = await Runner.run(relevance_agent, user_input, context=context.context)\n",
514+
" return GuardrailFunctionOutput(\n",
515+
" output_info=check.final_output,\n",
516+
" tripwire_triggered=not check.final_output.is_relevant,\n",
517+
" )\n",
518+
"\n",
519+
"\n",
520+
"# Attach the guardrail to the triage agent (the entry point of the workflow).\n",
521+
"triage_agent.input_guardrails.append(relevance_guardrail)"
522+
]
523+
},
524+
{
525+
"cell_type": "markdown",
526+
"metadata": {},
527+
"source": [
528+
"### Test 4: guardrail trips on an off-topic request\n",
529+
"\n",
530+
"The first request is unrelated to the airline and should be blocked; the second is on-topic and should pass through to the triage agent as usual."
531+
]
532+
},
533+
{
534+
"cell_type": "code",
535+
"execution_count": null,
536+
"metadata": {},
537+
"outputs": [],
538+
"source": [
539+
"async def run_guarded(user_input: str) -> str:\n",
540+
" \"\"\"Run the triage agent with the relevance guardrail enabled.\"\"\"\n",
541+
" with agent_trace(\n",
542+
" \"Customer service (guarded)\", group_id=uuid.uuid4().hex[:16]\n",
543+
" ):\n",
544+
" try:\n",
545+
" result = await Runner.run(\n",
546+
" triage_agent, user_input, context=AirlineAgentContext()\n",
547+
" )\n",
548+
" return f\"✅ Allowed → {result.final_output}\"\n",
549+
" except InputGuardrailTripwireTriggered:\n",
550+
" return \"🛑 Guardrail tripped — off-topic request blocked.\"\n",
551+
"\n",
552+
"\n",
553+
"# Off-topic: should trip the guardrail\n",
554+
"print(await run_guarded(\"Can you give me a recipe for chocolate cake?\"))\n",
555+
"\n",
556+
"# On-topic: should pass\n",
557+
"print(await run_guarded(\"What are the baggage restrictions?\"))"
558+
]
476559
}
477560
],
478561
"metadata": {

0 commit comments

Comments
 (0)