Skip to content

Commit 03891c0

Browse files
authored
fix(openai-agents): align AgentSpanData stubs and span processor with real SDK (#4229)
1 parent 7701174 commit 03891c0

File tree

5 files changed

+43
-72
lines changed

5 files changed

+43
-72
lines changed

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## Unreleased
9+
- Align AgentSpanData test stubs and span processor with real OpenAI Agents SDK;
10+
remove non-existent `operation`, `description`, `agent_id`, and `model` fields.
11+
([#4229](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/4229))
912
- Document official package metadata and README for the OpenAI Agents instrumentation.
1013
([#3859](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3859))
1114

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents/span_processor.py

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,17 +1519,8 @@ def _get_operation_name(self, span_data: Any) -> str:
15191519
return GenAIOperationName.CHAT
15201520
return GenAIOperationName.TEXT_COMPLETION
15211521
if _is_instance_of(span_data, AgentSpanData):
1522-
# Could be create_agent or invoke_agent based on context
1523-
operation = getattr(span_data, "operation", None)
1524-
normalized = (
1525-
operation.strip().lower()
1526-
if isinstance(operation, str)
1527-
else None
1528-
)
1529-
if normalized in {"create", "create_agent"}:
1530-
return GenAIOperationName.CREATE_AGENT
1531-
if normalized in {"invoke", "invoke_agent"}:
1532-
return GenAIOperationName.INVOKE_AGENT
1522+
# The OpenAI Agents SDK AgentSpanData has no "operation" field;
1523+
# agent spans always represent invoke_agent.
15331524
return GenAIOperationName.INVOKE_AGENT
15341525
if _is_instance_of(span_data, FunctionSpanData):
15351526
return GenAIOperationName.EXECUTE_TOOL
@@ -1831,24 +1822,20 @@ def _get_attributes_from_agent_span_data(
18311822
if name:
18321823
yield GEN_AI_AGENT_NAME, name
18331824

1834-
agent_id = (
1835-
self.agent_id
1836-
or getattr(span_data, "agent_id", None)
1837-
or self._agent_id_default
1838-
)
1825+
# agent_id and description are not available on the OpenAI Agents SDK
1826+
# AgentSpanData; only use user-configured overrides.
1827+
agent_id = self.agent_id or self._agent_id_default
18391828
if agent_id:
18401829
yield GEN_AI_AGENT_ID, agent_id
18411830

1842-
description = (
1843-
self.agent_description
1844-
or getattr(span_data, "description", None)
1845-
or self._agent_description_default
1846-
)
1831+
description = self.agent_description or self._agent_description_default
18471832
if description:
18481833
yield GEN_AI_AGENT_DESCRIPTION, description
18491834

1850-
model = getattr(span_data, "model", None)
1851-
if not model and agent_content:
1835+
# The OpenAI Agents SDK AgentSpanData has no "model" field; fall back to
1836+
# the model aggregated from child generation/response spans.
1837+
model = None
1838+
if agent_content:
18521839
model = agent_content.get("request_model")
18531840
if model:
18541841
yield GEN_AI_REQUEST_MODEL, model

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/stubs/agents/tracing/__init__.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,9 @@
3535
@dataclass
3636
class AgentSpanData:
3737
name: str | None = None
38+
handoffs: list[str] | None = None
3839
tools: list[str] | None = None
3940
output_type: str | None = None
40-
description: str | None = None
41-
agent_id: str | None = None
42-
model: str | None = None
43-
operation: str | None = None
4441

4542
@property
4643
def type(self) -> str:
@@ -200,8 +197,16 @@ def generation_span(**kwargs: Any):
200197

201198

202199
@contextmanager
203-
def agent_span(**kwargs: Any):
204-
data = AgentSpanData(**kwargs)
200+
def agent_span(
201+
name: str,
202+
handoffs: list[str] | None = None,
203+
tools: list[str] | None = None,
204+
output_type: str | None = None,
205+
**kwargs: Any,
206+
):
207+
data = AgentSpanData(
208+
name=name, handoffs=handoffs, tools=tools, output_type=output_type
209+
)
205210
span = _PROVIDER.create_span(data, parent=_CURRENT_TRACE)
206211
span.start()
207212
try:

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_tracer.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -171,40 +171,31 @@ def test_function_span_records_tool_attributes():
171171
exporter.clear()
172172

173173

174-
def test_agent_create_span_records_attributes():
174+
def test_agent_invoke_span_records_attributes():
175175
instrumentor, exporter = _instrument_with_provider()
176176

177177
try:
178178
with trace("workflow"):
179179
with agent_span(
180-
operation="create",
181180
name="support_bot",
182-
description="Answers support questions",
183-
agent_id="agt_123",
184-
model="gpt-4o-mini",
181+
handoffs=["escalation_bot"],
182+
tools=["search"],
183+
output_type="str",
185184
):
186185
pass
187186

188187
spans = exporter.get_finished_spans()
189-
create_span = next(
188+
invoke_span = next(
190189
span
191190
for span in spans
192191
if span.attributes[GenAI.GEN_AI_OPERATION_NAME]
193-
== GenAI.GenAiOperationNameValues.CREATE_AGENT.value
192+
== GenAI.GenAiOperationNameValues.INVOKE_AGENT.value
194193
)
195194

196-
assert create_span.kind is SpanKind.CLIENT
197-
assert create_span.name == "create_agent support_bot"
198-
assert create_span.attributes[GEN_AI_PROVIDER_NAME] == "openai"
199-
assert create_span.attributes[GenAI.GEN_AI_AGENT_NAME] == "support_bot"
200-
assert (
201-
create_span.attributes[GenAI.GEN_AI_AGENT_DESCRIPTION]
202-
== "Answers support questions"
203-
)
204-
assert create_span.attributes[GenAI.GEN_AI_AGENT_ID] == "agt_123"
205-
assert (
206-
create_span.attributes[GenAI.GEN_AI_REQUEST_MODEL] == "gpt-4o-mini"
207-
)
195+
assert invoke_span.kind is SpanKind.CLIENT
196+
assert invoke_span.name == "invoke_agent support_bot"
197+
assert invoke_span.attributes[GEN_AI_PROVIDER_NAME] == "openai"
198+
assert invoke_span.attributes[GenAI.GEN_AI_AGENT_NAME] == "support_bot"
208199
finally:
209200
instrumentor.uninstrument()
210201
exporter.clear()
@@ -425,7 +416,7 @@ def test_agent_name_override_applied_to_agent_spans():
425416

426417
try:
427418
with trace("workflow"):
428-
with agent_span(operation="invoke", name="support_bot"):
419+
with agent_span(name="support_bot"):
429420
pass
430421

431422
spans = exporter.get_finished_spans()

instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/tests/test_z_span_processor_unit.py

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -156,19 +156,14 @@ def test_operation_and_span_naming(processor_setup):
156156
== sp.GenAIOperationName.EMBEDDINGS
157157
)
158158

159-
agent_create = AgentSpanData(operation=" CREATE ")
159+
# AgentSpanData always maps to invoke_agent (no operation field in OpenAI Agents SDK)
160+
agent_data = AgentSpanData(name="bot")
160161
assert (
161-
processor._get_operation_name(agent_create)
162-
== sp.GenAIOperationName.CREATE_AGENT
163-
)
164-
165-
agent_invoke = AgentSpanData(operation="invoke_agent")
166-
assert (
167-
processor._get_operation_name(agent_invoke)
162+
processor._get_operation_name(agent_data)
168163
== sp.GenAIOperationName.INVOKE_AGENT
169164
)
170165

171-
agent_default = AgentSpanData(operation=None)
166+
agent_default = AgentSpanData()
172167
assert (
173168
processor._get_operation_name(agent_default)
174169
== sp.GenAIOperationName.INVOKE_AGENT
@@ -315,26 +310,19 @@ def __init__(self) -> None:
315310
agent_span = AgentSpanData(
316311
name="helper",
317312
output_type="json",
318-
description="desc",
319-
agent_id="agent-123",
320-
model="model-x",
321-
operation="invoke_agent",
322313
)
323314
agent_attrs = _collect(
324315
processor._get_attributes_from_agent_span_data(agent_span, None)
325316
)
326317
assert agent_attrs[sp.GEN_AI_AGENT_NAME] == "helper"
327-
assert agent_attrs[sp.GEN_AI_AGENT_ID] == "agent-123"
328-
assert agent_attrs[sp.GEN_AI_REQUEST_MODEL] == "model-x"
318+
assert sp.GEN_AI_AGENT_ID not in agent_attrs
319+
assert sp.GEN_AI_REQUEST_MODEL not in agent_attrs
329320
assert agent_attrs[sp.GEN_AI_OUTPUT_TYPE] == sp.GenAIOutputType.TEXT
330321

331322
# Fallback to aggregated model when span data lacks it
332323
agent_span_no_model = AgentSpanData(
333324
name="helper-2",
334325
output_type="json",
335-
description="desc",
336-
agent_id="agent-456",
337-
operation="invoke_agent",
338326
)
339327
agent_content = {
340328
"input_messages": [],
@@ -435,9 +423,7 @@ def test_span_lifecycle_and_shutdown(processor_setup):
435423
parent_span = FakeSpan(
436424
trace_id="trace-1",
437425
span_id="span-1",
438-
span_data=AgentSpanData(
439-
operation="invoke", name="agent", model="gpt-4o"
440-
),
426+
span_data=AgentSpanData(name="agent"),
441427
started_at="2024-01-01T00:00:00Z",
442428
ended_at="2024-01-01T00:00:02Z",
443429
)
@@ -476,7 +462,7 @@ def test_span_lifecycle_and_shutdown(processor_setup):
476462
linger_span = FakeSpan(
477463
trace_id="trace-2",
478464
span_id="span-3",
479-
span_data=AgentSpanData(operation=None),
465+
span_data=AgentSpanData(),
480466
started_at="2024-01-01T00:00:06Z",
481467
)
482468
processor.on_span_start(linger_span)
@@ -518,7 +504,6 @@ def test_chat_span_renamed_with_model(processor_setup):
518504
trace_id=trace.trace_id,
519505
span_id="agent-span",
520506
span_data=AgentSpanData(
521-
operation="invoke_agent",
522507
name="Agent",
523508
),
524509
started_at="2025-01-01T00:00:00Z",

0 commit comments

Comments
 (0)