Skip to content

Attach call recordings to Langfuse traces - #242

Merged
markbackman merged 2 commits into
mainfrom
mb/langfuse-recording
Aug 27, 2026
Merged

markbackman merged 2 commits into
mainfrom
mb/langfuse-recording

Conversation

@markbackman

@markbackman markbackman commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

⚠️ Requires Pipecat 1.8.0 before it can be merged / used.

Summary

Adds call recording to the Langfuse example. Each call's audio is attached to its trace, giving two levels of playback in the Langfuse UI:

  • The whole call on the trace root — stereo, user left and bot right, so barge-in stays audible.
  • Each turn on that turn's turn span — the user's speech as the span's input, the bot's reply as its output.

Langfuse media travels over the REST media API rather than OTLP and is linked to a trace by id, so everything uploads after the pipeline has shut down and the call itself pays nothing for it.

langfuse_recording.py holds the integration and uses only public Pipecat API, so it can be lifted into another bot as a single file. Wiring it up is an AudioBufferProcessor in the pipeline plus three calls:

recorder = LangfuseRecorder.from_env()
if recorder:
    recorder.attach(audiobuffer, worker)

# on disconnect, while the pipeline is still alive
await recorder.stop_and_collect()

# after runner.run()
await recorder.upload()

Credentials are read from OTEL_EXPORTER_OTLP_HEADERS and the host from OTEL_EXPORTER_OTLP_ENDPOINT, since Langfuse authenticates its OTLP endpoint with the same key pair the REST API wants. Media and spans therefore reach the same project by construction, with no second pair of environment variables to keep in sync. LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY / LANGFUSE_HOST override it for a self-hosted media deployment.

Depends on unreleased pipecat

The recording needs pcm_to_wav() (pipecat-ai/pipecat#5326, merged) and per-turn turn audio events (pipecat-ai/pipecat#5329, open). Until both are in a release, pyproject.toml pins pipecat-ai to a local checkout through [tool.uv.sources]; that section and its comment come out when they ship.

This replaces #240, which took the same feature through a helper in pipecat core.

Testing

  • Driven end to end against a real pipeline with the Langfuse HTTP calls stubbed: 8/8 clips attached, one whole-call recording and seven per-turn clips across four turns, each on its own turn span and all under one trace id.
  • Checked against a barge-in with the mic open through the bot's reply: the interrupted turn keeps the audio it was cut off in the middle of, and open-mic audio does not leak into it.
  • Run it for real with uv run bot.py and open the trace in Langfuse; the players appear on the trace root and on each turn span.

The bot's audio buffer processor feeds a recorder that uploads through the
Langfuse media API once the pipeline has shut down: the whole call on the trace
root, stereo with the user left and the bot right, and each turn's user and bot
audio on that turn's span as its input and output.

Media does not travel over OTLP and is linked to a trace by id, so the upload
happens after the conversation ends and costs it nothing. The keys and host come
from the OTLP exporter's own configuration, which puts the audio in the same
Langfuse project as the spans.
@markbackman
markbackman force-pushed the mb/langfuse-recording branch from 5cccce1 to e8a8007 Compare August 15, 2026 19:22
@jamsea

jamsea commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Works great, I can hear the full call audio WAV and the turn by turn splits 🎉
image

@markbackman
markbackman merged commit 7b14761 into main Aug 27, 2026
46 checks passed
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