Skip to content

openai_agents tracing: spans after a model call nest under its temporal:startActivity span instead of the turn #1855

Description

@DABH

When an agent running in a workflow calls a tool after a model call, the trace shows the tool's span nested inside the model call's temporal:startActivity span instead of next to it under the turn. This happens because the tracing interceptor makes temporal:startActivity the current Agents SDK span while it writes the span id into the activity header and never restores the previous span, so every span created afterwards in that context is parented to it. The fix is to keep the span current only while the header is captured.

Details

_ContextPropagationWorkflowOutboundInterceptor.start_activity (likewise start_child_workflow and start_local_activity) in temporalio/contrib/openai_agents/_trace_interceptor.py does:

span = custom_span(name="temporal:startActivity", data={...})
span.start(mark_as_current=True)
self.root().set_header_from_context(input)   # reads get_current_span()
handle = self.next.start_activity(input)
handle.add_done_callback(lambda _: span.finish())

Nothing resets the current span, and finish() in the done callback does not either (it could not safely: the callback runs in a copy of the contextvars context, so a token reset there raises ValueError). The span stays current until the enclosing span exits, and every span created in between without an explicit parent (tool calls, handoffs, guardrails within the same turn) is parented to it.

Observed:

turn
└─ temporal:startActivity            (model call)
   ├─ temporal:executeActivity
   └─ lookup_account (tool)
      └─ temporal:startActivity → temporal:executeActivity

Expected:

turn
├─ temporal:startActivity            (model call)
│  └─ temporal:executeActivity
└─ lookup_account (tool)
   └─ temporal:startActivity → temporal:executeActivity

The interceptor is shared by the OpenAI platform and OpenTelemetry tracing modes, so both show the same nesting. tests/contrib/openai_agents/test_openai_tracing.py::test_tracing asserts turn → temporal:startActivity → temporal:executeActivity, which is the intended shape, but its workflow makes no tool calls, so this was not covered.

Related but separate: #1852. Found while validating temporalio/samples-python#365.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions