Skip to content

feat(openai-agents): map Trace.group_id to session.id on the root span - #3901

Open
robertwitt wants to merge 1 commit into
Arize-ai:mainfrom
robertwitt:main
Open

robertwitt wants to merge 1 commit into
Arize-ai:mainfrom
robertwitt:main

Conversation

@robertwitt

Copy link
Copy Markdown

The OpenAI Agents SDK lets callers group related traces into a single conversation via trace(group_id=...). This is the intended mechanism for multi-turn sessions — for example, a chat interface where each user message produces one Runner.run call. Before this change OpenInferenceTracingProcessor built the root span's attributes from trace.name and trace.trace_id only; trace.group_id was silently dropped. Each turn produced an independent OTel trace with no shared attribute, making it impossible for a backend such as Phoenix to group them into a session.

What changed

on_trace_start now reads trace.group_id and, when present, stamps session.id on the root span:

# before
otel_span = self._tracer.start_span(
    name=trace.name,
    attributes={OPENINFERENCE_SPAN_KIND: OpenInferenceSpanKindValues.AGENT.value},
)

# after
attributes = {OPENINFERENCE_SPAN_KIND: OpenInferenceSpanKindValues.AGENT.value}
group_id = getattr(trace, "group_id", None)
if group_id is not None:
    attributes[SESSION_ID] = group_id
otel_span = self._tracer.start_span(name=trace.name, attributes=attributes)

group_id is set in TraceImpl.__init__ before start() fires the hook, so it is always available at this point. A getattr guard keeps older SDK versions and None values safe — in both cases session.id is simply omitted. An explicit empty string is preserved as a present value.

Tests added (test_processor_span_attributes.py)

  • test_group_id_mapped_to_session_id — present string → session.id set
  • test_none_group_id_omits_session_id — None → attribute absent
  • test_empty_group_id_mapped_to_session_id — "" → mapped verbatim
  • test_missing_group_id_attr_no_crash — trace with no group_id attribute (older SDK) → no crash, attribute absent

No behavior change when group_id is unset. Fully backward compatible.

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@robertwitt

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@robertwitt

Copy link
Copy Markdown
Author

recheck

github-actions Bot added a commit that referenced this pull request Oct 1, 2026

@chrikrah chrikrah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robertwitt the code at c365061 works as measured: trace(group_id=...) now puts session.id on the root span. I am leaving a comment and not an approval, because this is feature work and CONTRIBUTING says features are not being accepted right now and asks for an issue first on a non-trivial change.

Probe with the real SDK (openai-agents==0.11.0): one trace("wf", group_id=...) around one custom_span("child"), in-memory exporter, instrumentor source exported from each commit.

$ python probe_group.py   # c365061
A group_id only: child session.id= None
A group_id only: wf session.id= conv-1
B using_session only: child session.id= ctx-1
B using_session only: wf session.id= ctx-1
C both: child session.id= ctx-1
C both: wf session.id= ctx-1
$ python probe_group.py   # 400013c, the merge base
A group_id only: child session.id= None
A group_id only: wf session.id= None
B using_session only: child session.id= ctx-1
B using_session only: wf session.id= ctx-1
C both: child session.id= ctx-1
C both: wf session.id= ctx-1

For Phoenix the root span should be enough, since src/phoenix/db/insertion/span.py:62 reads session.id from any span and attaches the whole trace (phoenix 7556cee, read, not run).

$ cd python/instrumentation/openinference-instrumentation-openai-agents && python -m pytest tests -q
299 passed in 2.12s   # c365061
295 passed in 1.91s   # 400013c, the merge base
# _processor.py from 400013c, tests kept
FAILED tests/test_processor_span_attributes.py::test_group_id_mapped_to_session_id
FAILED tests/test_processor_span_attributes.py::test_empty_group_id_mapped_to_session_id
2 failed, 297 passed in 1.72s
# not run: ruff, mypy, a live Phoenix server

non-blocking: CONTRIBUTING asks for a trace screenshot when emitted spans change, and the body has none yet.

non-blocking: _realtime.py:733 puts the provider session id on every span through the context and lets using_session win. This PR matches that precedence (case C), but only because of the call order in _tracers.py:199, with no test pinning it, and it stamps only the root (case A, child None).

@mikeldking, do you want trace group_id mapped to session.id at all? If yes, on the root span only, or on every span the way _realtime.py does it?

This branch has not been deployed

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants