Conversation
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 Report❌ Patch coverage is
... and 68 files with indirect coverage changes 🚀 New features to boost your workflow:
|
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.
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.
There was a problem hiding this comment.
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 topipecat.audio.utilswith unit tests. - Extended
AudioBufferProcessorto optionally number turn-audio events using an attachedTurnTrackingObserver(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.
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>
|
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 |
|
Ok, I've thought more about it. I'm going to close this out. I have one more PR: The rest of this code, including the I'll submit a PR to pipecat-examples that we can use, pending the Pipecat release with the two updates we've made. |
|
@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 |
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 anAudioBufferProcessorand, 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'sturnspan as itsinput/output.AudioBufferProcessor.set_turn_tracker(): attach the pipeline'sTurnTrackingObserverandon_user_turn_audio_data/on_bot_turn_audio_datareport 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()inpipecat.audio.utils, next to the other PCM helpers. Every example that persists audio hand-rolls this, including thepipecat initserver 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.pyalready 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_dataandon_bot_turn_audio_datagain a trailing positionalturn_numberargument (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