From e64bfcfa7f30bb83c256b1e4150e13e6dceb90ac Mon Sep 17 00:00:00 2001 From: bhumikadangayach Date: Sat, 20 Jun 2026 13:34:02 +0530 Subject: [PATCH 1/2] fix(openai-agents): use SpanKind.INTERNAL for invoke_agent root span The root span created in on_trace_start was hardcoded to SpanKind.SERVER, but this doesn't match the OTel definition of SERVER (handling a synchronous remote call) nor anything in the gen-ai-agent-spans semconv. OpenLLMetry's equivalent instrumentation uses INTERNAL for the same case. Fixes #154 --- .../genai/openai_agents/span_processor.py | 2 +- .../tests/test_z_span_processor_unit.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py b/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py index f78552f1..fec5c3f9 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py +++ b/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py @@ -1312,7 +1312,7 @@ def on_trace_start(self, trace: Trace) -> None: otel_span = self._tracer.start_span( name=trace.name, attributes=attributes, - kind=SpanKind.SERVER, # Root span is typically server + kind=SpanKind.INTERNAL, # Root span is typically server ) self._root_spans[trace.trace_id] = otel_span diff --git a/instrumentation/opentelemetry-instrumentation-genai-openai-agents/tests/test_z_span_processor_unit.py b/instrumentation/opentelemetry-instrumentation-genai-openai-agents/tests/test_z_span_processor_unit.py index 0a2d2cda..f1febb5f 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-openai-agents/tests/test_z_span_processor_unit.py +++ b/instrumentation/opentelemetry-instrumentation-genai-openai-agents/tests/test_z_span_processor_unit.py @@ -503,6 +503,22 @@ def test_span_lifecycle_and_shutdown(processor_setup): == sp.GenAIOperationName.INVOKE_AGENT ) +def test_root_span_kind_is_internal(processor_setup): + """Regression test for #154: root span must be INTERNAL, not SERVER. + + invoke_agent root spans don't represent the server side of a + synchronous remote call, so SpanKind.SERVER is incorrect per the + OTel semantic conventions for span kinds. + """ + processor, exporter = processor_setup + + trace = FakeTrace(name="workflow", trace_id="trace-root-kind") + processor.on_trace_start(trace) + processor.on_trace_end(trace) + + finished = exporter.get_finished_spans() + assert len(finished) == 1 + assert finished[0].kind is SpanKind.INTERNAL def test_chat_span_renamed_with_model(processor_setup): processor, exporter = processor_setup From 911a0d8e9275d461786dff92e0a12133f1b4a8ba Mon Sep 17 00:00:00 2001 From: bhumikadangayach Date: Sat, 20 Jun 2026 13:44:29 +0530 Subject: [PATCH 2/2] fix: update misleading comment on root span kind per Copilot review --- .../instrumentation/genai/openai_agents/span_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py b/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py index fec5c3f9..95fdff87 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py +++ b/instrumentation/opentelemetry-instrumentation-genai-openai-agents/src/opentelemetry/instrumentation/genai/openai_agents/span_processor.py @@ -1312,7 +1312,7 @@ def on_trace_start(self, trace: Trace) -> None: otel_span = self._tracer.start_span( name=trace.name, attributes=attributes, - kind=SpanKind.INTERNAL, # Root span is typically server + kind=SpanKind.INTERNAL, # Root span represents local workflow; not a remote server span ) self._root_spans[trace.trace_id] = otel_span