Skip to content

Attach call recordings to Langfuse traces - #5285

Closed
jamsea wants to merge 6 commits into
mainfrom
jh/turn-audio-turn-number
Closed

jamsea wants to merge 6 commits into
mainfrom
jh/turn-audio-turn-number

Conversation

@jamsea

@jamsea jamsea commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What

Brings the Langfuse call-recording integration into core as pipecat.utils.tracing.langfuse.LangfuseRecordingUploader, together with the two primitives it stands on:

  • LangfuseRecordingUploader: captures audio from an AudioBufferProcessor and, after the pipeline has shut down, uploads it through the Langfuse media API and links it to the conversation's trace by id. Two levels of playback: the whole call (stereo, user left / bot right) on the trace root, and each turn's user/bot audio on that turn's turn span as its input/output.
  • AudioBufferProcessor.set_turn_tracker(): attach the pipeline's TurnTrackingObserver and on_user_turn_audio_data / on_bot_turn_audio_data report the tracker's turn number for each clip. The tracker is the source of truth for turn numbering (it owns interruption and timeout semantics), so the processor takes the number from it instead of keeping a parallel counter that could drift.
  • pcm_to_wav() in pipecat.audio.utils, next to the other PCM helpers. Every example that persists audio hand-rolls this, including the pipecat init server template.

Why

This integration has lived as a 469-line copy-paste file in pipecat-examples (pipecat-ai/pipecat-examples#240). That is a bad distribution mechanism: users copy it and it rots in their repos when the media API or pipecat internals change. Most of the file is generic Pipecat glue (audio collection, turn correlation, trace-id resolution) that core can do better than any example, and processors/metrics/sentry.py already set the precedent for a vendor observability integration in core. With this PR the example collapses to a few lines: from_env(), attach(), stop_and_collect(), upload().

Breaking change

on_user_turn_audio_data and on_bot_turn_audio_data gain a trailing positional turn_number argument (0 when no tracker is attached). Event handlers are called with exact positional args, so existing handlers for these two events need the parameter added. These events are recent and niche; flagging for discussion whether a documented break or new event names is preferred.

Test plan

jamsea added 2 commits August 11, 2026 12:25
on_user_turn_audio_data and on_bot_turn_audio_data had no way to say which
turn a clip belongs to, so anything matching turn audio to turns (turn spans
in tracing, per-turn evals, storage keys) had to count turns itself off the
turn tracker. The tracker is the source of truth for turn numbers, so attach
it with set_turn_tracker() and the events now report its number directly.
Without a tracker the turn number is 0.

The tracker is attached after construction because the pipeline is usually
built before the object that owns the tracker (the pipeline worker).

BREAKING: both events gain a trailing turn_number argument, so existing
handlers need it added to their signature.
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.57991% with 71 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/pipecat/utils/tracing/langfuse.py 65.19% 71 Missing ⚠️
Files with missing lines Coverage Δ
src/pipecat/audio/utils.py 76.69% <100.00%> (+2.50%) ⬆️
...pipecat/processors/audio/audio_buffer_processor.py 96.25% <100.00%> (+7.24%) ⬆️
src/pipecat/utils/tracing/langfuse.py 65.19% <65.19%> (ø)

... and 68 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

jamsea added 2 commits August 11, 2026 14:17
Every example that persists or uploads audio from AudioBufferProcessor
hand-rolls the same wave.open block, including the pipecat init template.
Give it a home next to the other PCM helpers.
Attaching call audio to a Langfuse trace previously lived as a 469-line file
in pipecat-examples that users had to copy. Bring it into core, built on the
turn-numbered audio events and pcm_to_wav from this branch.

The Langfuse media API is called directly over aiohttp (already a core
dependency). The langfuse package is deliberately not used: its client starts
a second OTel tracer provider that conflicts with setup_tracing(), and its
LangfuseMedia class only uploads media embedded in spans the Langfuse SDK
itself created.
@jamsea jamsea changed the title Report turn numbers on AudioBufferProcessor turn audio events Attach call recordings to Langfuse traces Aug 11, 2026
jamsea added a commit to pipecat-ai/pipecat-examples that referenced this pull request Aug 11, 2026
The 469-line langfuse_media.py moved into pipecat as
pipecat.utils.tracing.langfuse (pipecat-ai/pipecat#5285), so the example is
now just the wiring: from_env(), attach(), stop_and_collect(), upload().

That module is not in a release yet, so pipecat installs from the PR branch.
Switch back to a version pin once it ships.
@jamsea
jamsea requested review from markbackman and a lite review from Copilot August 11, 2026 08:00

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 first-class Langfuse call-recording support to Pipecat core by introducing an uploader that captures audio from AudioBufferProcessor, converts PCM to WAV, and uploads/links recordings to Langfuse traces and per-turn spans.

Changes:

  • Added LangfuseRecordingUploader (pipecat.utils.tracing.langfuse) for post-run upload/linking of whole-call and per-turn audio via Langfuse Media REST API (aiohttp).
  • Added pcm_to_wav() helper to pipecat.audio.utils with unit tests.
  • Extended AudioBufferProcessor to optionally number turn-audio events using an attached TurnTrackingObserver (set_turn_tracker()), including tests and changelog entries.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/pipecat/utils/tracing/langfuse.py New core Langfuse media uploader that captures, resolves trace/span IDs, and uploads WAV clips.
src/pipecat/processors/audio/audio_buffer_processor.py Adds set_turn_tracker() and passes turn_number on per-turn audio events.
src/pipecat/audio/utils.py Adds pcm_to_wav() utility for wrapping s16le PCM in a WAV container.
tests/test_langfuse_uploader.py Adds local aiohttp fake server tests covering Langfuse media upload flows and failure handling.
tests/test_audio_utils.py Adds round-trip WAV tests for pcm_to_wav() (mono/stereo/empty).
tests/test_audio_buffer_processor.py Adds tests validating turn_number behavior with/without a TurnTrackingObserver.
changelog/5285.changed.md Documents breaking change to turn-audio event handler signatures.
changelog/5285.added.md Documents new LangfuseRecordingUploader.
changelog/5285.added.2.md Documents new AudioBufferProcessor.set_turn_tracker().
changelog/5285.added.3.md Documents new pcm_to_wav() helper.

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

Comment thread src/pipecat/utils/tracing/langfuse.py
jamsea and others added 2 commits August 12, 2026 09:43
The uploader lives in the same package as TurnTraceObserver now, so it can
read _conversation_span without a public property. PR 5272 is being closed
as speculative API surface with no consumer.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@jamsea
jamsea marked this pull request as ready for review August 14, 2026 09:53
@jamsea jamsea self-assigned this Aug 14, 2026
@jamsea
jamsea marked this pull request as draft August 14, 2026 10:12
@markbackman

Copy link
Copy Markdown
Contributor

Thanks for looking into this. I think most of this falls into application concern and shouldn't be in Pipecat.

The one interesting piece is the pcm_to_wav utility. I've pulled out your comment and extended it to replace pcm_to_wav duplicate uses in Pipecat here: #5326.

Stepping back, I think we want to hook into the on_user_turn_audio_data and on_bot_turn_audio_data events to push the audio to Langfuse. I'm going to consider what our options are. Thinking this through more first.

@markbackman

Copy link
Copy Markdown
Contributor

Ok, I've thought more about it. I'm going to close this out. I have one more PR:
#5329

The rest of this code, including the LangfuseRecordingUploader falls under the application concern, so let's keep that outside of Pipecat.

I'll submit a PR to pipecat-examples that we can use, pending the Pipecat release with the two updates we've made.

@jamsea

jamsea commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

@markbackman sounds good! I've been on and off the fence about keeping this in pipecat core vs outside of pipecat core for the last few days too

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.

3 participants