Skip to main content
This guide covers the Python SDK for pipecat-ai. The package is available on PyPI. A Node.js SDK is coming soon.

Video Tutorial

Prerequisites

  • Tuner Active Account
  • Configured Agent with provider “Custom API” in Tuner
  • Python 3.11–3.13 (3.14 is not yet supported due to pipecat dependencies)
  • Python project running pipecat-ai v1.0.0 or later

Overview

The tuner-pipecat-sdk is a lightweight observer that captures flow transitions, latency signals, transcript segments, and usage metadata from your Pipecat pipeline, then sends a structured CallPayload to Tuner when the call ends — no manual API calls required. If your agent’s LLM step is a LangChain runnable or LangGraph graph, the same observer also captures node transitions, tool calls, and token usage from that layer — see LangChain and LangGraph observability below.
1

Install the SDK

Add the package to your project with pip.
2

Set Your Credentials

Configure your Tuner API key, workspace ID, and agent remote ID.
3

Add the Observer

Create an observer instance and attach it to pipecat.
Estimated time: 2 minutes from start to finish

Step 1: Install the SDK

For pipecat-ai pipelines:
Requirements: Python 3.11–3.13, pipecat-ai>=1.0.0. Do not use Python 3.14 — pipecat dependencies (onnxruntime, numba) do not yet have 3.14 wheels.

Step 2: Set Your Credentials

You can configure credentials via environment variables or pass them directly in code.
Use the Agent Remote ID from Agent Settings → Agent Connection — not your agent’s display name. It is a UUID (for example fa4da74c-...). Copy your value from the Tuner dashboard.

Step 3: Add the Observer

Create an Observer instance and attach turn tracking:
Attach tuner observer, latency_observer and turn_tracker to the Pipeline:
Without enable_metrics and enable_usage_metrics, the observer will log warnings and LLM/TTS metric fields will be absent from the payload. For more examples, see the tuner-pipecat-sdk-python examples.

End-of-Utterance (EOU) Delay

Wire your user context aggregator to the observer to capture how long the pipeline took to decide the user was done talking — the gap between the user going silent and the turn actually closing. This appears on every user turn in the transcript view as eou_delay, eou_confidence, and eou_reason.
eou_reason tells you which signal closed the turn, so eou_delay means something different depending on the value: All three fields are omitted from a turn’s metadata when nothing decided that turn’s end.

SIP / Telephony Calls

If you plan to run Call Simulation against this agent, the observer needs the SIP Call-ID of the inbound call. Tuner uses it to match the simulation call it dialled with the call your observer syncs back — without it, simulated calls show up as ordinary production traffic.
This applies to inbound simulation, where Tuner dials your agent. For outbound simulation, pass the dialed SIP URI as recipient instead, see Outbound Simulation.
The SDK handles SIP-field extraction internally. Pass the raw payload your server already has with one line per provider:
Built-in provider strings: "twilio", "telnyx", "plivo", "exotel", "jambonz". For an unlisted provider, pass a callable extractor instead:
For Daily PSTN/SIP dial-in:
If you already have the values (custom SIP trunk, FreeSWITCH, Asterisk, etc.):
For non-SIP (web) calls, skip all of the above — the observer falls back to its normal behaviour and SIP fields are omitted from the payload entirely.

LangChain and LangGraph observability

Using LangGraph or LangChain for your LLM step? The observer can capture node transitions, tool calls, and token usage from inside that runnable alongside the standard call data. See LangChain / LangGraph.

Configuration Options

Override the default call type label:
The phone number or SIP URI of the callee. Optional — pass it when you want to record who the call was directed to:
Provide a recording URL if available. Default is "pipecat://no-recording":
Record why a call ended by passing a disconnection_reason_resolver callable. The resolver is called at flush time and should return a string or None.Use the built-in constants from DisconnectReason:
Specify your ASR, LLM, and TTS models for metadata:
Attach arbitrary key-value data to every call record. Useful for environment, region, deployment version, tenant, or anything you want to filter and evaluate against later:
Metadata can also be used as an eval input — see Evaluate agents with per-call instructions.
Track which version of your agent handled each call. Set APP_VERSION in your environment:
Using GitHub Actions or CircleCI? Tuner reads GITHUB_RUN_NUMBER / CIRCLE_BUILD_NUM automatically. Manual override via constructor takes priority:
Log the full transcript when flushing:
Provide a callable that receives the call’s usage summary and returns the call cost in USD cents. Implement the method that matches your pricing plan; the snippet below is illustrative.

Full Example

Using LangChain or LangGraph for your LLM step instead of a native pipecat LLM service? Swap llm for a LangchainProcessor wrapping observer.wrap_graph(...) / observer.wrap_chain(...) — see LangChain / LangGraph.
If you prefer not to extract the field yourself, call observer.attach_sip_from_telephony(call_data, provider="twilio") after creating the observer — the SDK reads the SIP Call-ID from the payload for you.

OpenTelemetry traces

Pipecat already emits OpenTelemetry spans for every conversation. Point its exporter at Tuner and those spans appear as a span tree on the matching call, under a Traces tab. See OpenTelemetry Traces for how to read one.
This is optional and separate from the observer above. Trace export is configured through Pipecat’s own tracing setup.
1

Point your exporter at Tuner

Use the same TUNER_API_KEY you set in Step 2. It identifies your workspace, so nothing else needs configuring.
Use the signal-specific variable, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, and give the full URL. The generic OTEL_EXPORTER_OTLP_ENDPOINT breaks this setup in two ways: the exporter appends /v1/traces to it, which does not resolve to Tuner’s path, and it is shared with metrics and logs, so repointing it would send those to Tuner as well.
If you already send traces to your own observability backend, leave that variable alone and fan out with an OpenTelemetry Collector instead of switching endpoints.
2

Switch to the HTTP exporter

Tuner accepts OTLP over HTTP with protobuf encoding, which is the default for the HTTP exporter. gRPC is not supported.
Pipecat’s own tracing example uses the gRPC exporter. If you started from that example, this import is the one line you need to change.
3

Tag the trace with your call id

Tuner matches a trace to a call on a tuner.call_id span attribute. Its value has to be the same call_id you pass to the Observer, so reuse the variable rather than generating a second UUID.
Pipecat applies additional_span_attributes to the root conversation span only. That is enough, because Tuner correlates at the trace level and applies the id to every span in the trace.
Pass service_name as well. Without it, spans arrive with service.name set to unknown_service, which makes traces from different agents hard to tell apart.
4

Check that it worked

Open the call in Tuner. A Traces tab appears on the call details page once spans have been received and matched. Correlation completes when the call itself is ingested, so the tab can take a moment to appear after a call ends.

Span reference

The span names come from Pipecat, so they are the same ones you would see in any other OpenTelemetry backend. Service spans nest under turns, under one conversation span:
Realtime models such as Gemini Live and OpenAI Realtime replace the llm span with llm_setup, llm_response, and llm_tool_call or llm_tool_result. See Pipecat’s OpenTelemetry documentation for the full set, and OpenTelemetry Traces for how to read the tree in Tuner.

Webhooks

Tuner can notify your systems when it has something to report on a call — see Webhooks.

Troubleshooting

Python 3.14: Pipecat pins onnxruntime versions that have no 3.14 wheels. Switch to Python 3.12 or 3.13 and create a new venv.
Same as above: use Python 3.12 or 3.13.
  • Verify that agent_id is the Agent Remote ID from Agent Settings → Agent Connection (not your agent’s display name).
  • Confirm workspace_id is correct.
  • Ensure enable_metrics=True and enable_usage_metrics=True are set on PipelineParams.
  • Check your application logs for any error messages from the observer.
  • Ensure TUNER_API_KEY starts with tr_api_ and is valid.
  • Confirm the API key belongs to the correct workspace.
The langchain extra isn’t installed. Run pip install tuner-pipecat-sdk[langchain].
In order of likelihood:
  1. tuner.call_id does not match the call_id you passed to the Observer. They have to be identical strings. This is the most common cause by a wide margin.
  2. The attribute is missing entirely. Nothing in the trace carries it, so there is nothing for Tuner to match on.
  3. The gRPC exporter is still in use. Requests are rejected with 415. Pipecat’s own example uses gRPC, so check that import first.
  4. The API key is wrong or revoked. Requests are rejected with 401 or 403.

What’s Next?

Configuring Your Agent

Set up call outcomes, user intents, and evals.

Custom Integration

Learn about the underlying API if you need more control.

Classifying Calls

Define how Tuner categorizes your calls.

Real-Time Alerts

Get notified when issues are detected.