Skip to content

Expose the conversation span on TurnTraceObserver - #5272

Closed
jamsea wants to merge 2 commits into
mainfrom
jh/conversation-span-accessor
Closed

jamsea wants to merge 2 commits into
mainfrom
jh/conversation-span-accessor

Conversation

@jamsea

@jamsea jamsea commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What

Adds a read-only conversation_span property to TurnTraceObserver.

Why

additional_span_attributes is applied once, when the conversation span is created on the
first StartFrame. That covers attributes you know up front, but not ones you only learn
late in a call, and the observer exposes get_current_turn_context() and get_turn_context()
for turn spans while offering nothing for the conversation. Integrations that want the
conversation span end up reading _conversation_span directly.

Scope note, please read before reviewing

This started as a prerequisite for attaching call recordings to Langfuse traces, where I
expected to have to write a media reference onto the conversation span before it closed. That
turned out to be unnecessary: Langfuse links media by trace and observation id and renders a
player from the link alone, so nothing has to be written into the span and no span has to be
open. The downstream example (pipecat-ai/pipecat-examples#240) now uses only public API,
turn_tracking_observer and get_turn_context(), and does not need this property.

Update: it has a concrete consumer again. The Langfuse recording example resolves the
trace id by walking retained turn contexts, falling back to get_current_turn_context(),
and finally to the conversation span, where it has to reach for the private
_conversation_span on today's releases. Its _resolve_trace_id already prefers this
property when present, and once this ships that whole fallback chain collapses to reading
conversation_span.get_span_context().trace_id. That uploader is now moving into core
itself (#5285), where its trace-id resolution has the same fallback and would simplify the
same way.

Notes

  • Read-only, and returns None both before the conversation starts and after it ends, so
    callers cannot accidentally write to a closed span.
  • No behavior change. Nothing in the framework reads it.
  • The docstring is explicit that attributes set after the span ends are dropped, since that is
    the easy mistake to make here.

Test plan

  • New test_conversation_span_accessor in tests/test_turn_trace_observer.py: asserts the
    property is None before the conversation exists, that an attribute set through it lands on
    the exported span, and that it returns to None after end_conversation_tracing().
  • uv run pytest tests/test_turn_trace_observer.py → 11 passed.

jamsea added 2 commits August 10, 2026 16:58
Observability integrations sometimes need to set a trace-level attribute that is
only known late in a call, after additional_span_attributes has already been
applied at conversation start. Attaching a call recording is the motivating case:
the audio only exists once the call ends, and some backends treat a persisted
trace as immutable, so the reference has to be written while the conversation
span is still open.

The observer already owned the span but only exposed turn-level contexts, so
integrations had to reach for _conversation_span. Add a read-only property.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a small, targeted public accessor on TurnTraceObserver so integrations can safely reach the live OpenTelemetry “conversation” span while it’s still open, enabling late-arriving trace attributes (e.g., call recording references) without relying on private attributes.

Changes:

  • Added a read-only conversation_span property to TurnTraceObserver that exposes the currently-open conversation span (or None before start / after end).
  • Added a unit test covering the new accessor and validating that attributes set through it are exported.
  • Added a changelog fragment documenting the new API surface.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/pipecat/utils/tracing/turn_trace_observer.py Exposes the open conversation span via a new conversation_span property with clarifying docstring.
tests/test_turn_trace_observer.py Adds test_conversation_span_accessor to verify None before start, mutability while open, and None after end.
changelog/5272.added.md Documents the new public accessor and its intended usage window.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@jamsea
jamsea marked this pull request as ready for review August 10, 2026 09:04
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/pipecat/utils/tracing/turn_trace_observer.py 86.23% <100.00%> (+0.38%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jamsea
jamsea requested a review from vipyne August 10, 2026 09:05
@jamsea
jamsea marked this pull request as draft August 11, 2026 02:17
@jamsea

jamsea commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Closing. The consumer this was resurrected for (the Langfuse recording uploader) now lives in core (#5285), where it reads the observer's conversation span as same-package internals, and in practice that path barely runs: turn 1 starts with the conversation, so trace ids resolve from retained turn contexts. What remains is the secondary use case (setting conversation-span attributes learned late in a call), which has no concrete consumer today. Happy to resubmit if one shows up.

@jamsea jamsea closed this Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants