-
Notifications
You must be signed in to change notification settings - Fork 163
feat(pipecat): add auto instrumentor for pipecat #2441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
...openinference-instrumentation-pipecat/src/openinference/instrumentation/pipecat/_observer.py
Outdated
Show resolved
Hide resolved
...eninference-instrumentation-pipecat/src/openinference/instrumentation/pipecat/_attributes.py
Outdated
Show resolved
Hide resolved
...nce-instrumentation-pipecat/tests/openinference/instrumentation/pipecat/test_instrumentor.py
Outdated
Show resolved
Hide resolved
...nce-instrumentation-pipecat/tests/openinference/instrumentation/pipecat/test_instrumentor.py
Outdated
Show resolved
Hide resolved
...eninference-instrumentation-pipecat/src/openinference/instrumentation/pipecat/_attributes.py
Outdated
Show resolved
Hide resolved
...eninference-instrumentation-pipecat/src/openinference/instrumentation/pipecat/_attributes.py
Show resolved
Hide resolved
| "openinference-semantic-conventions>=0.1.21", | ||
| "websockets>=13.1,<16.0", | ||
| "mypy>=1.18.2", | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Missing wrapt dependency in package dependencies
The code imports wrapt in __init__.py with from wrapt import wrap_function_wrapper, but wrapt is not listed in the dependencies section of pyproject.toml. While it's listed in the mypy overrides to ignore missing imports, it's not actually installed as a dependency. This will cause import errors when the package is installed and used.
| elif isinstance(frame, TextFrame): | ||
| # Generic text frame (output) | ||
| results[SpanAttributes.OUTPUT_VALUE] = text | ||
| results["llm.output_messages.0.message.role"] = "user" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Incorrect message role for generic text output
Generic TextFrame output is assigned message role "user" but should be "assistant" since it represents output from the model. This violates OpenInference semantic conventions where output messages should have role "assistant". The code already correctly sets this as OUTPUT_VALUE on line 205, confirming this is output data that should be attributed to the assistant.
Add OpenInference Instrumentation for Pipecat
This PR implements comprehensive OpenTelemetry tracing for Pipecat voice agents using OpenInference semantic conventions, enabling production-ready observability for voice AI applications.
Overview
Adds automatic instrumentation for Pipecat pipelines that captures:
Key Features
1. Automatic Instrumentation via Observer Pattern
The instrumentor wraps
PipelineTask.__init__to automatically inject an observer into every task:No code changes needed in your pipeline - just instrument once and all
PipelineTaskinstances get automatic tracing.2. Intelligent Turn Tracking
Implements conversation turn tracking using speaking events to define natural conversation boundaries:
UserStartedSpeakingFrameorStartFrame(first pipeline frame)BotStoppedSpeakingFrame(configurable, default 2.5s)This approach ensures one turn span per actual conversation exchange with proper handling of multi-part bot responses (e.g., function calls causing multiple TTS segments).
3. Bidirectional Frame Processing with Deduplication
Captures frames both entering (input) and leaving (output) services with intelligent deduplication:
is_input=True, OUTPUT_VALUE only whenis_input=FalseTranscriptionFrameis OUTPUT from STT but recorded as INPUT for observabilityBaseOutputTransport(final output)Deduplication handles cumulative chunks (e.g., "Hello" → "Hello world" → "Hello world!") by detecting overlaps and extracting only new content.
4. Multiple LLM Invocations Per Turn
Properly handles multiple LLM calls within a single turn (e.g., function calling flows):
LLMContextFrame5. Flattened Message Format for Arize
Message history is exported in the flattened format that Arize expects:
This enables proper display in Arize's UI with message-level filtering and analysis.
6. Consistent Time Handling
Uses
time.time_ns()(Unix epoch) consistently for all span timestamps:start_time_ns + processing_time_secondswhen metrics availablemonotonic_ns()timestamps which are relative to pipeline startEnsures
end_time >= start_timeinvariant required by OpenTelemetry.7. Multi-Provider Service Detection
Automatically detects and attributes service types and providers:
llm.provider,llm.model_name)audio.voice,audio.voice_id)Sets
service.nameto the actual service class name for unique identification.8. Session Tracking
Automatically extracts
conversation_idfromPipelineTaskand sets assession.idattribute on all spans, enabling conversation-level filtering in observability platforms.Implementation Details
Core Components
PipecatInstrumentor(init.py)PipelineTask.__init__usingwraptOpenInferenceObserverinto each taskdebug_log_filenameparameter for detailed frame loggingOpenInferenceObserver(_observer.py)BaseObserverinterfaceon_push_frameevents with bidirectional processing (input/output)Frame Attribute Extractors (_attributes.py)
TranscriptionFrame,LLMContextFrame,LLMTextFrame,TTSTextFrame,MetricsFrame, etc.Span Hierarchy
Flat hierarchy: All service spans are siblings under the turn span (no nesting) for clearer visualization in tracing UIs. All spans within a turn share the same
trace_idand havesession.idattribute set.Context Propagation
Service spans are created with the turn span's context:
This ensures proper parent-child relationships and enables distributed tracing.
Testing
Test Coverage
69 tests covering:
Instrumentor Basics (
test_instrumentor.py):Turn Tracking (
test_turn_tracking.py):Service Detection (
test_service_detection.py):Provider Spans (
test_provider_spans.py):Mock Infrastructure
Comprehensive mocks in
conftest.py:All tests use in-memory span exporters for fast, isolated testing.
Example Usage
Complete Tracing Example
See examples/trace/001-trace.py for a full working example:
What Gets Traced
For a single user query → bot response with a follow-up question:
Turn Span (
pipecat.conversation.turn):session.id: "conversation-20251113_152502"input.value: "What is quantum computing?"output.value: "Quantum computing is a type of computing that uses quantum mechanics..."conversation.turn_number: 1conversation.turn_duration_seconds: 3.5conversation.end_reason: "completed"STT Span (
pipecat.stt):service.name: "OpenAISTTService"service.type: "stt"llm.provider: "openai"llm.model_name: "gpt-4o-transcribe"input.value: "What is quantum computing?" (transcribed text)audio.transcript: "What is quantum computing?"LLM Span (
pipecat.llm):service.name: "OpenAILLMService"service.type: "llm"llm.provider: "openai"llm.model_name: "gpt-4"input.value: "What is quantum computing?" (last user message)output.value: "Quantum computing is..." (accumulated streaming response)llm.input_messages.0.message.role: "system"llm.input_messages.0.message.content: "You are a helpful assistant"llm.input_messages.1.message.role: "user"llm.input_messages.1.message.content: "What is quantum computing?"llm.output_messages.0.message.role: "assistant"llm.output_messages.0.message.content: "Quantum computing is..."llm.token_count.total: 520llm.token_count.prompt: 380llm.token_count.completion: 140TTS Span (
pipecat.tts):service.name: "OpenAITTSService"service.type: "tts"llm.provider: "openai"llm.model_name: "gpt-4o-mini-tts"audio.voice: "ballad"audio.voice_id: "ballad"output.value: "Quantum computing is..." (synthesized text)service.processing_time_seconds: 1.57Key Improvements in This PR
TranscriptionFrameas OUTPUT from STT but recorded as INPUT for observabilitytime.time_ns()) instead of mixing with Pipecat's monotonic clockllm.input_messages.{index}.message.{field}) instead of single JSON stringNote
Adds a Pipecat auto-instrumentation package that converts Pipecat frames into OpenInference/OTel spans with turn tracking, service spans (LLM/TTS/STT), rich attributes, examples, tests, and CI integration.
python/instrumentation/openinference-instrumentation-pipecat:PipecatInstrumentorauto-injects an observer by wrappingPipelineTask.__init__.OpenInferenceObservercreatespipecat.conversation.turnspans and sibling service spans (pipecat.llm,pipecat.tts,pipecat.stt), with session IDs, streaming chunk deduplication, timing, and interruption handling._attributes.pyextracts frame/service attributes (flattened LLM messages, tool calls, token/latency metrics, provider/model detection) and sets OpenInference/GenAI semconv fields.pyproject.toml, entry points for OTel/OpenInference, version0.1.0, LICENSE, CHANGELOG, README.examples/trace/001-trace.pyandexample.envfor Phoenix/OTLP setup.python/tox.ini: addpipecatandpipecat-latestenvs and install steps..gitignore: ignore*.code-workspace.Written by Cursor Bugbot for commit 9cb7459. This will update automatically on new commits. Configure here.