Skip to content

Commit bcfefd8

Browse files
committed
update step type
1 parent ffc8c13 commit bcfefd8

File tree

2 files changed

+48
-20
lines changed
  • instrumentation-genai/opentelemetry-instrumentation-openai-agents-v2/src/opentelemetry/instrumentation/openai_agents
  • util/opentelemetry-util-genai/src/opentelemetry/util/genai/emitters

2 files changed

+48
-20
lines changed

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

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
TelemetryHandler,
2929
get_telemetry_handler,
3030
)
31-
from opentelemetry.util.genai.types import AgentInvocation, Workflow
31+
from opentelemetry.util.genai.types import AgentInvocation, Workflow, Step
3232
from opentelemetry.util.genai.utils import gen_ai_json_dumps
3333

3434
try:
@@ -73,6 +73,7 @@
7373
Status,
7474
StatusCode,
7575
Tracer,
76+
get_current_span,
7677
set_span_in_context,
7778
)
7879
from opentelemetry.util.types import AttributeValue
@@ -521,6 +522,7 @@ def __init__(
521522
# util/genai integration (step 1 – workflow only)
522523
self._handler = get_telemetry_handler()
523524
self._workflow: Workflow | None = None
525+
self._steps: dict[str, Step] = {}
524526

525527
# Metrics configuration
526528
self._metrics_enabled = metrics_enabled
@@ -1341,28 +1343,15 @@ def on_trace_start(self, trace: Trace) -> None:
13411343
except Exception: # defensive – don’t break existing spans
13421344
self._workflow = None
13431345

1344-
otel_span = self._tracer.start_span(
1345-
name=trace.name,
1346-
attributes=attributes,
1347-
kind=SpanKind.SERVER, # Root span is typically server
1348-
)
1349-
self._root_spans[trace.trace_id] = otel_span
1350-
1351-
token = attach(set_span_in_context(otel_span))
1352-
self._tokens[trace.trace_id] = token
1353-
13541346
def on_trace_end(self, trace: Trace) -> None:
13551347
"""End root span when trace ends."""
13561348
if root_span := self._root_spans.pop(trace.trace_id, None):
13571349
if root_span.is_recording():
13581350
root_span.set_status(Status(StatusCode.OK))
13591351
root_span.end()
1360-
try:
1361-
if self._workflow is not None:
1362-
self._handler.stop_workflow(self._workflow)
1363-
finally:
1364-
self._workflow = None
1365-
self._cleanup_spans_for_trace(trace.trace_id)
1352+
if self._workflow is not None:
1353+
self._handler.stop_workflow(self._workflow)
1354+
13661355

13671356
def on_span_start(self, span: Span[Any]) -> None:
13681357
"""Start child span for agent span."""
@@ -1432,6 +1421,35 @@ def on_span_start(self, span: Span[Any]) -> None:
14321421
attributes[GEN_AI_AGENT_DESCRIPTION] = agent_desc_override
14331422
attributes.update(self._get_server_attributes())
14341423

1424+
# For agent spans, create a GenAI Step under the workflow and
1425+
# then make the OpenAI Agents span a child of that Step span.
1426+
if _is_instance_of(span.span_data, AgentSpanData) and self._workflow is not None:
1427+
try:
1428+
step_attrs: dict[str, Any] = dict(attributes)
1429+
step = Step(
1430+
name=agent_name or span_name,
1431+
step_type="agent_start", # or "chain" if you prefer
1432+
attributes=step_attrs,
1433+
)
1434+
1435+
# Parent the Step to the Workflow entity.
1436+
step.parent_run_id = self._workflow.run_id
1437+
1438+
content = self._agent_content.get(span.span_id)
1439+
if content and content.get("input_messages"):
1440+
step.input_data = safe_json_dumps(content["input_messages"])
1441+
1442+
self._handler.start_step(step)
1443+
self._steps[str(span.span_id)] = step
1444+
1445+
# After starting the Step, use its span as the parent context
1446+
# for the OpenAI Agents span.
1447+
parent_span = get_current_span()
1448+
context = set_span_in_context(parent_span)
1449+
except Exception:
1450+
# Defensive: do not break agent spans if util/genai fails.
1451+
pass
1452+
14351453
otel_span = self._tracer.start_span(
14361454
name=span_name,
14371455
context=context,
@@ -1441,7 +1459,7 @@ def on_span_start(self, span: Span[Any]) -> None:
14411459
self._otel_spans[span.span_id] = otel_span
14421460
self._tokens[span.span_id] = attach(set_span_in_context(otel_span))
14431461

1444-
# util/genai step 2: create AgentInvocation entity for agent spans.
1462+
# util/genai step 3: create AgentInvocation entity for agent spans.
14451463
if _is_instance_of(span.span_data, AgentSpanData):
14461464
try:
14471465
# Prepare attributes for the AgentInvocation entity. We start
@@ -1515,7 +1533,17 @@ def on_span_end(self, span: Span[Any]) -> None:
15151533
self._agent_content.pop(span.span_id, None)
15161534
self._span_parents.pop(span.span_id, None)
15171535
return
1518-
1536+
key = str(span.span_id)
1537+
step = self._steps.pop(key, None)
1538+
if step is not None:
1539+
try:
1540+
# Optional: attach output from agent/span
1541+
content = self._agent_content.get(span.span_id)
1542+
if content and content.get("output_messages"):
1543+
step.output_data = safe_json_dumps(content["output_messages"])
1544+
self._handler.stop_step(step)
1545+
except Exception:
1546+
pass
15191547
try:
15201548
# Extract and set attributes
15211549
attributes: dict[str, AttributeValue] = {}

util/opentelemetry-util-genai/src/opentelemetry/util/genai/emitters/span.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ def _error_agent(
595595
# ---- Step lifecycle --------------------------------------------------
596596
def _start_step(self, step: Step) -> None:
597597
"""Start a step span."""
598-
span_name = f"step_execution {step.name}"
598+
span_name = f"step {step.name}"
599599
parent_span = getattr(step, "parent_span", None)
600600
parent_ctx = (
601601
trace.set_span_in_context(parent_span)

0 commit comments

Comments
 (0)