Documentation
Integration Examples/Pipecat/Pipecat Integration Overview

Pipecat Integration Overview

Add automatic tracing to Pipecat voice pipelines with Noveum Trace

Noveum Trace adds automatic tracing to your Pipecat voice pipeline in minutes. Every conversation is recorded as a structured trace with per-turn spans for STT, LLM, and TTS; tool/function-call details are attached to the LLM span as attributes (when available), along with latency and token usage.

Prerequisites

  • Python 3.11+
  • A working Pipecat pipeline (pipecat-ai)
  • A Noveum API key (get one at noveum.ai)

Installation

pip install "noveum-trace[pipecat]"

Quick Start

You only need three changes:

  1. Initialize noveum_trace once at startup.
  2. Create a NoveumTraceObserver.
  3. Attach the observer to your PipelineTask (and ensure turn tracking wiring happens before the runner starts).
import asyncio
 
import noveum_trace
from noveum_trace.integrations.pipecat import NoveumTraceObserver
 
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.task import PipelineTask
from pipecat.pipeline.runner import PipelineRunner
 
# 1) Initialize noveum-trace once at startup
noveum_trace.init(
    api_key="your-noveum-api-key",
    project="my-voice-bot",
)
 
# --- your existing pipeline setup ---
pipeline = Pipeline([
    transport.input(),
    stt,
    context_aggregator.user(),
    llm,
    tts,
    transport.output(),
    context_aggregator.assistant(),
])
 
async def main():
    # 2) Add NoveumTraceObserver to your PipelineTask
    trace_obs = NoveumTraceObserver()
 
    task = PipelineTask(
        pipeline,
        observers=[trace_obs],
    )
 
    # 3) Wire turn tracking
    await trace_obs.attach_to_task(task)
 
    runner = PipelineRunner()
    await runner.run(task)
 
asyncio.run(main())

Traces are flushed automatically when the pipeline ends (EndFrame / CancelFrame).

What Gets Traced

Each pipeline session produces one conversation trace containing a turn span per conversational exchange. Each turn has child spans for STT, LLM, and TTS. When record_audio=True and an AudioBufferProcessor is present, a full-conversation recording span is also created at the root.

Trace: pipecat.conversation
β”‚   pipeline.allow_interruptions, pipeline.sample_rate
β”‚   conversation.total_input_tokens, conversation.total_output_tokens
β”‚   conversation.total_cost, conversation.turn_count
β”‚
β”œβ”€β”€ Span: pipecat.turn  (one per userβ†’bot exchange)
β”‚   β”œβ”€β”€ turn.number, turn.user_input, turn.duration_seconds
β”‚   β”œβ”€β”€ turn.user_bot_latency_seconds  (when latency observer is wired)
β”‚   β”œβ”€β”€ turn.was_interrupted
β”‚   β”œβ”€β”€ turn.eou_is_complete, turn.eou_confidence  (SmartTurn, when available)
β”‚   β”‚
β”‚   β”œβ”€β”€ Span: pipecat.stt
β”‚   β”‚   β”œβ”€β”€ stt.text, stt.is_final, stt.language, stt.user_id
β”‚   β”‚   β”œβ”€β”€ stt.model, stt.confidence
β”‚   β”‚   β”œβ”€β”€ stt.vad_to_final_ms, stt.first_text_latency_ms
β”‚   β”‚   β”œβ”€β”€ stt.interim_results  (JSON list of {text, confidence})
β”‚   β”‚   └── stt.audio_uuid  (when record_audio=True)
β”‚   β”‚
β”‚   β”œβ”€β”€ Span: pipecat.llm
β”‚   β”‚   β”œβ”€β”€ llm.model, llm.system_prompt, llm.temperature, llm.max_tokens
β”‚   β”‚   β”œβ”€β”€ llm.input  (full message history JSON), llm.output
β”‚   β”‚   β”œβ”€β”€ llm.input_tokens, llm.output_tokens, llm.total_tokens
β”‚   β”‚   β”œβ”€β”€ llm.cost.input, llm.cost.output, llm.cost.total, llm.cost.currency
β”‚   β”‚   β”œβ”€β”€ llm.time_to_first_token_ms
β”‚   β”‚   β”œβ”€β”€ llm.thoughts[], llm.thought_signatures[]  (thinking-enabled models)
β”‚   β”‚   β”œβ”€β”€ llm.function_calls[], llm.function_call_results[]  (tool calls)
β”‚   β”‚   └── llm.tools, llm.tool_choice  (when set)
β”‚   β”‚
β”‚   └── Span: pipecat.tts
β”‚       β”œβ”€β”€ tts.input_text, tts.voice, tts.model
β”‚       β”œβ”€β”€ tts.time_to_first_byte_ms, tts.characters
β”‚       └── tts.audio_uuid  (when record_audio=True)
β”‚
└── Span: pipecat.full_conversation  (when record_audio=True + AudioBufferProcessor in pipeline)
    └── full_conversation.audio_uuid, full_conversation.audio_format
        full_conversation.audio_channels, full_conversation.duration_ms
        full_conversation.sample_rate
        (stereo WAV: left channel = user, right channel = bot)

Configuration Options

trace_obs = NoveumTraceObserver(
    trace_name_prefix="pipecat",   # produces trace named "pipecat.conversation"
    capture_text=True,             # capture LLM input/output and TTS text in spans
    capture_function_calls=True,   # record tool calls on the pipecat.llm span
    record_audio=True,             # upload per-span STT/TTS audio + full conversation WAV
)

Common tweaks:

  • Turn off capture_text if you want less text stored in spans.
  • Set capture_function_calls=False if your LLM never emits function-call frames.
  • record_audio=True does two things:
    • Uploads per-span audio and adds stt.audio_uuid / tts.audio_uuid attributes.
    • Captures a full stereo conversation WAV as a pipecat.full_conversation span. This requires AudioBufferProcessor(num_channels=2) to be present in your pipeline (see the basic example).

You can also use the convenience factory instead of constructing NoveumTraceObserver directly:

from noveum_trace.integrations.pipecat import setup_pipecat_tracing
 
trace_obs = setup_pipecat_tracing(record_audio=True)

Troubleshooting

No traces appearing

  • Verify noveum_trace.init() is called before the pipeline starts.
  • Confirm await trace_obs.attach_to_task(task) is called (from async code) after PipelineTask is constructed and before runner.run(task).
  • Confirm your API key and the project name match what you configured in the Noveum dashboard.

Turn spans missing or not splitting correctly

  • await trace_obs.attach_to_task(task) is required for accurate turn boundaries.
  • Ensure turn tracking is enabled in your Pipecat version so attach_to_task() can wire external boundaries.

LLM token counts not appearing

  • Confirm PipelineParams(enable_metrics=True, enable_usage_metrics=True) is set on your PipelineTask β€” Pipecat only emits MetricsFrame when metrics are enabled.
  • Token counts come from Pipecat's MetricsFrame. Most standard Pipecat LLM services emit them when metrics are enabled.

Function call spans missing

  • Set capture_function_calls=True.
  • Confirm your LLM processor emits FunctionCallInProgressFrame / FunctionCallResultFrame.

Next Steps

Exclusive Early Access

Get Early Access to Noveum.ai Platform

Be the first one to get notified when we open Noveum Platform to more users. All users get access to Observability suite for free, early users get free eval jobs and premium support for the first year.

Sign up now. We send access to new batch every week.

Early access members receive premium onboarding support and influence our product roadmap. Limited spots available.

On this page

Pipecat Integration Overview | Documentation | Noveum.ai