Merge pull request #1860 from pipecat-ai/mb/organize-otel-demos
Reorganize OpenTelemetry demos, add top-level README
This commit is contained in:
@@ -1,140 +0,0 @@
|
||||
# Langfuse Tracing for Pipecat via OpenTelemetry
|
||||
|
||||
This demo showcases [Langfuse](https://langfuse.com) tracing integration for Pipecat services via OpenTelemetry, allowing you to visualize service calls, performance metrics, and dependencies.
|
||||
|
||||
This is a fork of the [OpenTelemetry Tracing for Pipecat](../open-telemetry-tracing) demo, but uses Langfuse instead of Jaeger. In contrast to the original demo, this demo uses the `opentelemetry-exporter-otlp-proto-http` exporter as the `grpc` exporter is not supported by Langfuse.
|
||||
|
||||
Pipecat trace in Langfuse:
|
||||
|
||||
https://github.com/user-attachments/assets/13dd7431-bf5e-42e3-8d6d-2ed84c51195d
|
||||
|
||||
## Features
|
||||
|
||||
- **Hierarchical Tracing**: Track entire conversations, turns, and service calls
|
||||
- **Service Tracing**: Detailed spans for TTS, STT, and LLM services with rich context
|
||||
- **TTFB Metrics**: Capture Time To First Byte metrics for latency analysis
|
||||
- **Usage Statistics**: Track character counts for TTS and token usage for LLMs
|
||||
|
||||
## Trace Structure
|
||||
|
||||
Traces are organized hierarchically:
|
||||
|
||||
```
|
||||
Conversation (conversation-uuid)
|
||||
├── turn-1
|
||||
│ ├── stt_deepgramsttservice
|
||||
│ ├── llm_openaillmservice
|
||||
│ └── tts_cartesiattsservice
|
||||
└── turn-2
|
||||
├── stt_deepgramsttservice
|
||||
├── llm_openaillmservice
|
||||
└── tts_cartesiattsservice
|
||||
turn-N
|
||||
└── ...
|
||||
```
|
||||
|
||||
This organization helps you track conversation-to-conversation and turn-to-turn.
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Create a Langfuse Project and get API keys
|
||||
|
||||
[Self-host](https://langfuse.com/self-hosting) Langfuse or create a free [Langfuse Cloud](https://cloud.langfuse.com) account.
|
||||
Create a new project and get the API keys.
|
||||
|
||||
### 2. Environment Configuration
|
||||
|
||||
Base64 encode your Langfuse public and secret key:
|
||||
|
||||
```bash
|
||||
echo -n "pk-lf-1234567890:sk-lf-1234567890" | base64
|
||||
```
|
||||
|
||||
Create a `.env` file with your API keys to enable tracing:
|
||||
|
||||
```
|
||||
ENABLE_TRACING=true
|
||||
# OTLP endpoint (defaults to localhost:4317 if not set)
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://cloud.langfuse.com/api/public/otel
|
||||
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic%20<base64_encoded_api_key>
|
||||
# Set to any value to enable console output for debugging
|
||||
# OTEL_CONSOLE_EXPORT=true
|
||||
```
|
||||
|
||||
### 3. Configure Your Pipeline Task
|
||||
|
||||
Enable tracing in your Pipecat application:
|
||||
|
||||
```python
|
||||
# Initialize OpenTelemetry with your chosen exporter
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
||||
|
||||
# Configured automatically from .env
|
||||
exporter = OTLPSpanExporter()
|
||||
|
||||
setup_tracing(
|
||||
service_name="pipecat-demo",
|
||||
exporter=exporter,
|
||||
console_export=os.getenv("OTEL_CONSOLE_EXPORT", "false").lower() == "true",
|
||||
)
|
||||
|
||||
# Enable tracing in your PipelineTask
|
||||
task = PipelineTask(
|
||||
pipeline,
|
||||
params=PipelineParams(
|
||||
allow_interruptions=True,
|
||||
enable_metrics=True, # Required for some service metrics
|
||||
),
|
||||
enable_tracing=True, # Enables both turn and conversation tracing
|
||||
conversation_id="customer-123", # Optional - will auto-generate if not provided
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 5. Run the Demo
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
### 6. View Traces in Langfuse
|
||||
|
||||
Open your browser to [https://cloud.langfuse.com](https://cloud.langfuse.com) to view traces.
|
||||
|
||||
## Understanding the Traces
|
||||
|
||||
- **Conversation Spans**: The top-level span representing an entire conversation
|
||||
- **Turn Spans**: Child spans of conversations that represent each turn in the dialog
|
||||
- **Service Spans**: Detailed service operations nested under turns
|
||||
- **Service Attributes**: Each service includes rich context about its operation:
|
||||
- **TTS**: Voice ID, character count, service type
|
||||
- **STT**: Transcription text, language, model
|
||||
- **LLM**: Messages, tokens used, model, service configuration
|
||||
- **Metrics**: Performance data like `metrics.ttfb_ms` and processing durations
|
||||
|
||||
## How It Works
|
||||
|
||||
The tracing system consists of:
|
||||
|
||||
1. **TurnTrackingObserver**: Detects conversation turns
|
||||
2. **TurnTraceObserver**: Creates spans for turns and conversations
|
||||
3. **Service Decorators**: `@traced_tts`, `@traced_stt`, `@traced_llm` for service-specific tracing
|
||||
4. **Context Providers**: Share context between different parts of the pipeline
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No Traces in Langfuse**: Ensure that your credentials are correct and follow this [troubleshooting guide](https://langfuse.com/faq/all/missing-traces)
|
||||
- **Debugging Traces**: Set `OTEL_CONSOLE_EXPORT=true` to print traces to the console for debugging
|
||||
- **Missing Metrics**: Check that `enable_metrics=True` in PipelineParams
|
||||
- **Connection Errors**: Verify network connectivity to Langfuse
|
||||
- **Exporter Issues**: Try the Console exporter (`OTEL_CONSOLE_EXPORT=true`) to verify tracing works
|
||||
|
||||
## References
|
||||
|
||||
- [OpenTelemetry Python Documentation](https://opentelemetry-python.readthedocs.io/)
|
||||
- [Langfuse OpenTelemetry Documentation](https://langfuse.com/docs/opentelemetry/get-started)
|
||||
@@ -1,176 +0,0 @@
|
||||
# OpenTelemetry Tracing for Pipecat
|
||||
|
||||
This demo showcases OpenTelemetry tracing integration for Pipecat services, allowing you to visualize service calls, performance metrics, and dependencies in a Jaeger dashboard.
|
||||
|
||||
## Features
|
||||
|
||||
- **Hierarchical Tracing**: Track entire conversations, turns, and service calls
|
||||
- **Service Tracing**: Detailed spans for TTS, STT, and LLM services with rich context
|
||||
- **TTFB Metrics**: Capture Time To First Byte metrics for latency analysis
|
||||
- **Usage Statistics**: Track character counts for TTS and token usage for LLMs
|
||||
- **Flexible Exporters**: Use Jaeger, Zipkin, or any OpenTelemetry-compatible backend
|
||||
|
||||
## Trace Structure
|
||||
|
||||
Traces are organized hierarchically:
|
||||
|
||||
```
|
||||
Conversation (conversation-uuid)
|
||||
├── turn-1
|
||||
│ ├── stt_deepgramsttservice
|
||||
│ ├── llm_openaillmservice
|
||||
│ └── tts_cartesiattsservice
|
||||
└── turn-2
|
||||
├── stt_deepgramsttservice
|
||||
├── llm_openaillmservice
|
||||
└── tts_cartesiattsservice
|
||||
turn-N
|
||||
└── ...
|
||||
```
|
||||
|
||||
This organization helps you track conversation-to-conversation and turn-to-turn.
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Start the Jaeger Container
|
||||
|
||||
Run Jaeger in Docker to collect and visualize traces:
|
||||
|
||||
```bash
|
||||
docker run -d --name jaeger \
|
||||
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
|
||||
-p 16686:16686 \
|
||||
-p 4317:4317 \
|
||||
-p 4318:4318 \
|
||||
jaegertracing/all-in-one:latest
|
||||
```
|
||||
|
||||
### 2. Environment Configuration
|
||||
|
||||
Create a `.env` file with your API keys and enable tracing:
|
||||
|
||||
```
|
||||
ENABLE_TRACING=true
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 # Point to your preferred backend
|
||||
# OTEL_CONSOLE_EXPORT=true # Set to any value for debug output to console
|
||||
|
||||
# Service API keys
|
||||
DEEPGRAM_API_KEY=your_key_here
|
||||
CARTESIA_API_KEY=your_key_here
|
||||
OPENAI_API_KEY=your_key_here
|
||||
```
|
||||
|
||||
### 3. Configure Your Pipeline Task
|
||||
|
||||
Enable tracing in your Pipecat application:
|
||||
|
||||
```python
|
||||
# Initialize OpenTelemetry with your chosen exporter
|
||||
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
||||
|
||||
exporter = OTLPSpanExporter(
|
||||
endpoint="http://localhost:4317", # Jaeger OTLP endpoint
|
||||
insecure=True,
|
||||
)
|
||||
|
||||
setup_tracing(
|
||||
service_name="pipecat-demo",
|
||||
exporter=exporter,
|
||||
console_export=os.getenv("OTEL_CONSOLE_EXPORT", "false").lower() == "true",
|
||||
)
|
||||
|
||||
# Enable tracing in your PipelineTask
|
||||
task = PipelineTask(
|
||||
pipeline,
|
||||
params=PipelineParams(
|
||||
allow_interruptions=True,
|
||||
enable_metrics=True, # Required for some service metrics
|
||||
),
|
||||
enable_tracing=True, # Enables both turn and conversation tracing
|
||||
conversation_id="customer-123", # Optional - will auto-generate if not provided
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Exporter Options
|
||||
|
||||
While this demo uses Jaeger, you can configure any OpenTelemetry-compatible exporter:
|
||||
|
||||
#### Jaeger (Default for the demo)
|
||||
|
||||
```python
|
||||
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
||||
|
||||
exporter = OTLPSpanExporter(
|
||||
endpoint="http://localhost:4317", # Jaeger OTLP endpoint
|
||||
insecure=True,
|
||||
)
|
||||
```
|
||||
|
||||
#### Cloud Providers
|
||||
|
||||
Many cloud providers offer OpenTelemetry-compatible observability services:
|
||||
|
||||
- AWS X-Ray
|
||||
- Google Cloud Trace
|
||||
- Azure Monitor
|
||||
- Datadog APM
|
||||
|
||||
See the OpenTelemetry documentation for specific exporter configurations:
|
||||
https://opentelemetry.io/ecosystem/vendors/
|
||||
|
||||
#### LLM Tracing and Evaluation Providers
|
||||
|
||||
Many LLM-focused tracing and evaluation projects support OpenTelemetry, for example:
|
||||
|
||||
- Langfuse ([integration example](../open-telemetry-tracing-langfuse/))
|
||||
- Arize Phoenix
|
||||
|
||||
### 5. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 6. Run the Demo
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
### 7. View Traces in Jaeger
|
||||
|
||||
Open your browser to [http://localhost:16686](http://localhost:16686) and select the "pipecat-demo" service to view traces.
|
||||
|
||||
## Understanding the Traces
|
||||
|
||||
- **Conversation Spans**: The top-level span representing an entire conversation
|
||||
- **Turn Spans**: Child spans of conversations that represent each turn in the dialog
|
||||
- **Service Spans**: Detailed service operations nested under turns
|
||||
- **Service Attributes**: Each service includes rich context about its operation:
|
||||
- **TTS**: Voice ID, character count, service type
|
||||
- **STT**: Transcription text, language, model
|
||||
- **LLM**: Messages, tokens used, model, service configuration
|
||||
- **Metrics**: Performance data like `metrics.ttfb_ms` and processing durations
|
||||
|
||||
## How It Works
|
||||
|
||||
The tracing system consists of:
|
||||
|
||||
1. **TurnTrackingObserver**: Detects conversation turns
|
||||
2. **TurnTraceObserver**: Creates spans for turns and conversations
|
||||
3. **Service Decorators**: `@traced_tts`, `@traced_stt`, `@traced_llm` for service-specific tracing
|
||||
4. **Context Providers**: Share context between different parts of the pipeline
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No Traces in Jaeger**: Ensure the Docker container is running and the OTLP endpoint is correct
|
||||
- **Debugging Traces**: Set `OTEL_CONSOLE_EXPORT=true` to print traces to the console for debugging
|
||||
- **Missing Metrics**: Check that `enable_metrics=True` in PipelineParams
|
||||
- **Connection Errors**: Verify network connectivity to the Jaeger container
|
||||
- **Exporter Issues**: Try the Console exporter (`OTEL_CONSOLE_EXPORT=true`) to verify tracing works
|
||||
- **Other Backends**: If using a different backend, ensure you've configured the correct exporter and endpoint
|
||||
|
||||
## References
|
||||
|
||||
- [OpenTelemetry Python Documentation](https://opentelemetry-python.readthedocs.io/)
|
||||
- [Jaeger Documentation](https://www.jaegertracing.io/docs/latest/)
|
||||
@@ -1,205 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2024–2025, Daily
|
||||
#
|
||||
# SPDX-License-Identifier: BSD 2-Clause License
|
||||
#
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import importlib.util
|
||||
import os
|
||||
import sys
|
||||
from contextlib import asynccontextmanager
|
||||
from inspect import iscoroutinefunction, signature
|
||||
from typing import Any, Callable, Dict, Optional, Tuple
|
||||
|
||||
import uvicorn
|
||||
from dotenv import load_dotenv
|
||||
from fastapi import BackgroundTasks, FastAPI
|
||||
from fastapi.responses import RedirectResponse
|
||||
from loguru import logger
|
||||
from pipecat_ai_small_webrtc_prebuilt.frontend import SmallWebRTCPrebuiltUI
|
||||
|
||||
from pipecat.transports.network.webrtc_connection import IceServer, SmallWebRTCConnection
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv(override=True)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# Store connections by pc_id
|
||||
pcs_map: Dict[str, SmallWebRTCConnection] = {}
|
||||
|
||||
ice_servers = [
|
||||
IceServer(
|
||||
urls="stun:stun.l.google.com:19302",
|
||||
)
|
||||
]
|
||||
|
||||
# Mount the frontend at /
|
||||
app.mount("/client", SmallWebRTCPrebuiltUI)
|
||||
|
||||
# Store program arguments
|
||||
args: argparse.Namespace = argparse.Namespace()
|
||||
|
||||
# Store the bot module and function info
|
||||
bot_module: Any = None
|
||||
run_bot_func: Optional[Callable] = None
|
||||
is_webrtc_bot: bool = True
|
||||
|
||||
|
||||
def import_bot_file(file_path: str) -> Tuple[Any, Callable, bool]:
|
||||
"""Dynamically import the bot file and determine how to run it.
|
||||
|
||||
Returns:
|
||||
tuple: (module, run_function, is_webrtc_bot)
|
||||
- module: The imported module
|
||||
- run_function: Either run_bot or main function
|
||||
- is_webrtc_bot: True if run_bot function exists and accepts a WebRTC connection
|
||||
"""
|
||||
if not os.path.exists(file_path):
|
||||
raise FileNotFoundError(f"Bot file not found: {file_path}")
|
||||
|
||||
# Extract module name without extension
|
||||
module_name = os.path.splitext(os.path.basename(file_path))[0]
|
||||
|
||||
# Load the module
|
||||
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
||||
if not spec or not spec.loader:
|
||||
raise ImportError(f"Could not load spec for {file_path}")
|
||||
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module_name] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
# Check for run_bot function first
|
||||
if hasattr(module, "run_bot"):
|
||||
run_func = module.run_bot
|
||||
# Check if the function accepts a WebRTC connection
|
||||
sig = signature(run_func)
|
||||
is_webrtc = len(sig.parameters) > 0
|
||||
return module, run_func, is_webrtc
|
||||
|
||||
# Fall back to main function
|
||||
if hasattr(module, "main") and iscoroutinefunction(module.main):
|
||||
return module, module.main, False
|
||||
|
||||
raise AttributeError(f"No run_bot or async main function found in {file_path}")
|
||||
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
async def root_redirect():
|
||||
return RedirectResponse(url="/client/")
|
||||
|
||||
|
||||
@app.post("/api/offer")
|
||||
async def offer(request: dict, background_tasks: BackgroundTasks):
|
||||
global run_bot_func, is_webrtc_bot
|
||||
|
||||
if not run_bot_func:
|
||||
raise RuntimeError("No bot file has been loaded")
|
||||
|
||||
if not is_webrtc_bot:
|
||||
return {
|
||||
"error": "This bot doesn't support WebRTC connections, it's running in standalone mode"
|
||||
}
|
||||
|
||||
pc_id = request.get("pc_id")
|
||||
|
||||
if pc_id and pc_id in pcs_map:
|
||||
pipecat_connection = pcs_map[pc_id]
|
||||
logger.info(f"Reusing existing connection for pc_id: {pc_id}")
|
||||
await pipecat_connection.renegotiate(
|
||||
sdp=request["sdp"], type=request["type"], restart_pc=request.get("restart_pc", False)
|
||||
)
|
||||
else:
|
||||
pipecat_connection = SmallWebRTCConnection(ice_servers)
|
||||
await pipecat_connection.initialize(sdp=request["sdp"], type=request["type"])
|
||||
|
||||
@pipecat_connection.event_handler("closed")
|
||||
async def handle_disconnected(webrtc_connection: SmallWebRTCConnection):
|
||||
logger.info(f"Discarding peer connection for pc_id: {webrtc_connection.pc_id}")
|
||||
pcs_map.pop(webrtc_connection.pc_id, None)
|
||||
|
||||
# We've already checked that run_bot_func exists
|
||||
assert run_bot_func is not None
|
||||
background_tasks.add_task(run_bot_func, pipecat_connection, args)
|
||||
|
||||
answer = pipecat_connection.get_answer()
|
||||
# Updating the peer connection inside the map
|
||||
pcs_map[answer["pc_id"]] = pipecat_connection
|
||||
|
||||
return answer
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
yield # Run app
|
||||
coros = [pc.close() for pc in pcs_map.values()]
|
||||
await asyncio.gather(*coros)
|
||||
pcs_map.clear()
|
||||
|
||||
|
||||
async def run_standalone_bot() -> None:
|
||||
"""Run a standalone bot that doesn't require WebRTC"""
|
||||
global run_bot_func
|
||||
if run_bot_func is not None:
|
||||
await run_bot_func()
|
||||
else:
|
||||
raise RuntimeError("No bot function available to run")
|
||||
|
||||
|
||||
def main(parser: Optional[argparse.ArgumentParser] = None):
|
||||
global args
|
||||
|
||||
if not parser:
|
||||
parser = argparse.ArgumentParser(description="Pipecat Bot Runner")
|
||||
parser.add_argument("bot_file", nargs="?", help="Path to the bot file", default=None)
|
||||
parser.add_argument(
|
||||
"--host", default="localhost", help="Host for HTTP server (default: localhost)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port", type=int, default=7860, help="Port for HTTP server (default: 7860)"
|
||||
)
|
||||
parser.add_argument("--verbose", "-v", action="count", default=0)
|
||||
args = parser.parse_args()
|
||||
|
||||
logger.remove(0)
|
||||
if args.verbose:
|
||||
logger.add(sys.stderr, level="TRACE")
|
||||
else:
|
||||
logger.add(sys.stderr, level="DEBUG")
|
||||
|
||||
# Infer the bot file from the caller if not provided explicitly
|
||||
bot_file = args.bot_file
|
||||
if bot_file is None:
|
||||
# Get the __file__ of the script that called main()
|
||||
import inspect
|
||||
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_globals = caller_frame.frame.f_globals
|
||||
bot_file = caller_globals.get("__file__")
|
||||
|
||||
if not bot_file:
|
||||
print("❌ Could not determine the bot file. Pass it explicitly to main().")
|
||||
sys.exit(1)
|
||||
|
||||
# Import the bot file
|
||||
try:
|
||||
global run_bot_func, bot_module, is_webrtc_bot
|
||||
bot_module, run_bot_func, is_webrtc_bot = import_bot_file(bot_file)
|
||||
logger.info(f"Successfully loaded bot from {bot_file}")
|
||||
|
||||
if is_webrtc_bot:
|
||||
logger.info("Detected WebRTC-compatible bot, starting web server...")
|
||||
uvicorn.run(app, host=args.host, port=args.port)
|
||||
else:
|
||||
logger.info("Detected standalone bot, running directly...")
|
||||
asyncio.run(run_standalone_bot())
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading bot file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
69
examples/open-telemetry/README.md
Normal file
69
examples/open-telemetry/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# OpenTelemetry Tracing with Pipecat
|
||||
|
||||
This repository demonstrates OpenTelemetry tracing integration for Pipecat services, with examples for different backends.
|
||||
|
||||
## Tracing Features in Pipecat
|
||||
|
||||
- **Hierarchical Tracing**: Track entire conversations, turns, and service calls
|
||||
- **Service Tracing**: Detailed spans for TTS, STT, and LLM services with rich context
|
||||
- **TTFB Metrics**: Capture Time To First Byte metrics for latency analysis
|
||||
- **Usage Statistics**: Track character counts for TTS and token usage for LLMs
|
||||
|
||||
## Trace Structure
|
||||
|
||||
Traces are organized hierarchically:
|
||||
|
||||
```
|
||||
Conversation (conversation)
|
||||
├── turn
|
||||
│ ├── stt_deepgramsttservice
|
||||
│ ├── llm_openaillmservice
|
||||
│ └── tts_cartesiattsservice
|
||||
└── turn
|
||||
├── stt_deepgramsttservice
|
||||
├── llm_openaillmservice
|
||||
└── tts_cartesiattsservice
|
||||
turn
|
||||
└── ...
|
||||
```
|
||||
|
||||
This organization helps you track conversation-to-conversation and turn-to-turn interactions.
|
||||
|
||||
## Available Demos
|
||||
|
||||
| Demo | Description |
|
||||
| ------------------------------- | ------------------------------------------------------------------------- |
|
||||
| [Jaeger Tracing](./jaeger/) | Tracing with Jaeger, an open-source end-to-end distributed tracing system |
|
||||
| [Langfuse Tracing](./langfuse/) | Tracing with Langfuse, a specialized platform for LLM observability |
|
||||
|
||||
## Common Requirements
|
||||
|
||||
- Python 3.10+
|
||||
- Pipecat and its dependencies
|
||||
- API keys for the services used (Deepgram, Cartesia, OpenAI)
|
||||
- The appropriate OpenTelemetry exporters
|
||||
|
||||
## How Tracing Works
|
||||
|
||||
The tracing system consists of:
|
||||
|
||||
1. **TurnTrackingObserver**: Detects conversation turns
|
||||
2. **TurnTraceObserver**: Creates spans for turns and conversations
|
||||
3. **Service Decorators**: `@traced_tts`, `@traced_stt`, `@traced_llm` for service-specific tracing
|
||||
4. **Context Providers**: Share context between different parts of the pipeline
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Choose one of the demos from the table above
|
||||
2. Follow the README instructions in the respective directory
|
||||
|
||||
## Common Troubleshooting
|
||||
|
||||
- **Debugging Traces**: Set `OTEL_CONSOLE_EXPORT=true` to print traces to the console for debugging
|
||||
- **Missing Metrics**: Check that `enable_metrics=True` in PipelineParams
|
||||
- **API Key Issues**: Verify your API keys are set correctly in the .env file
|
||||
|
||||
## References
|
||||
|
||||
- [OpenTelemetry Python Documentation](https://opentelemetry-python.readthedocs.io/)
|
||||
- [Pipecat Documentation](https://docs.pipecat.ai/server/utilities/opentelemetry)
|
||||
80
examples/open-telemetry/jaeger/README.md
Normal file
80
examples/open-telemetry/jaeger/README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Jaeger Tracing for Pipecat
|
||||
|
||||
This demo showcases OpenTelemetry tracing integration for Pipecat services using Jaeger, allowing you to visualize service calls, performance metrics, and dependencies.
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Start the Jaeger Container
|
||||
|
||||
Run Jaeger in Docker to collect and visualize traces:
|
||||
|
||||
```bash
|
||||
docker run -d --name jaeger \
|
||||
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
|
||||
-p 16686:16686 \
|
||||
-p 4317:4317 \
|
||||
-p 4318:4318 \
|
||||
jaegertracing/all-in-one:latest
|
||||
```
|
||||
|
||||
### 2. Environment Configuration
|
||||
|
||||
Create a `.env` file with your API keys and enable tracing:
|
||||
|
||||
```
|
||||
ENABLE_TRACING=true
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 # Point to your Jaeger backend
|
||||
# OTEL_CONSOLE_EXPORT=true # Set to any value for debug output to console
|
||||
|
||||
# Service API keys
|
||||
DEEPGRAM_API_KEY=your_key_here
|
||||
CARTESIA_API_KEY=your_key_here
|
||||
OPENAI_API_KEY=your_key_here
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 4. Run the Demo
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
### 5. View Traces in Jaeger
|
||||
|
||||
Open your browser to [http://localhost:16686](http://localhost:16686) and select the "pipecat-demo" service to view traces.
|
||||
|
||||
## Jaeger-Specific Configuration
|
||||
|
||||
In the `bot.py` file, note the GRPC exporter configuration:
|
||||
|
||||
```python
|
||||
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
||||
|
||||
# Create the exporter
|
||||
otlp_exporter = OTLPSpanExporter(
|
||||
endpoint=os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317"),
|
||||
insecure=True,
|
||||
)
|
||||
|
||||
# Set up tracing with the exporter
|
||||
setup_tracing(
|
||||
service_name="pipecat-demo",
|
||||
exporter=otlp_exporter,
|
||||
console_export=bool(os.getenv("OTEL_CONSOLE_EXPORT")),
|
||||
)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No Traces in Jaeger**: Ensure the Docker container is running and the OTLP endpoint is correct
|
||||
- **Connection Errors**: Verify network connectivity to the Jaeger container
|
||||
- **Exporter Issues**: Try the Console exporter (`OTEL_CONSOLE_EXPORT=true`) to verify tracing works
|
||||
|
||||
## References
|
||||
|
||||
- [Jaeger Documentation](https://www.jaegertracing.io/docs/latest/)
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from loguru import logger
|
||||
@@ -154,6 +155,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
from run import main
|
||||
|
||||
main()
|
||||
82
examples/open-telemetry/langfuse/README.md
Normal file
82
examples/open-telemetry/langfuse/README.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Langfuse Tracing for Pipecat
|
||||
|
||||
This demo showcases [Langfuse](https://langfuse.com) tracing integration for Pipecat services via OpenTelemetry, allowing you to visualize service calls, performance metrics, and dependencies with a focus on LLM observability.
|
||||
|
||||
Pipecat trace in Langfuse:
|
||||
|
||||
https://github.com/user-attachments/assets/13dd7431-bf5e-42e3-8d6d-2ed84c51195d
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Create a Langfuse Project and get API keys
|
||||
|
||||
[Self-host](https://langfuse.com/self-hosting) Langfuse or create a free [Langfuse Cloud](https://cloud.langfuse.com) account.
|
||||
Create a new project and get the API keys.
|
||||
|
||||
### 2. Environment Configuration
|
||||
|
||||
Base64 encode your Langfuse public and secret key:
|
||||
|
||||
```bash
|
||||
echo -n "pk-lf-1234567890:sk-lf-1234567890" | base64
|
||||
```
|
||||
|
||||
Create a `.env` file with your API keys to enable tracing:
|
||||
|
||||
```
|
||||
ENABLE_TRACING=true
|
||||
# OTLP endpoint for Langfuse
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://cloud.langfuse.com/api/public/otel
|
||||
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic%20<base64_encoded_api_key>
|
||||
# Set to any value to enable console output for debugging
|
||||
# OTEL_CONSOLE_EXPORT=true
|
||||
|
||||
# Service API keys
|
||||
DEEPGRAM_API_KEY=your_key_here
|
||||
CARTESIA_API_KEY=your_key_here
|
||||
OPENAI_API_KEY=your_key_here
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 4. Run the Demo
|
||||
|
||||
```bash
|
||||
python bot.py
|
||||
```
|
||||
|
||||
### 5. View Traces in Langfuse
|
||||
|
||||
Open your browser to [https://cloud.langfuse.com](https://cloud.langfuse.com) to view traces.
|
||||
|
||||
## Langfuse-Specific Configuration
|
||||
|
||||
In the `bot.py` file, note the HTTP exporter configuration:
|
||||
|
||||
```python
|
||||
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
||||
|
||||
# Create the exporter - configured from environment variables
|
||||
otlp_exporter = OTLPSpanExporter()
|
||||
|
||||
# Set up tracing with the exporter
|
||||
setup_tracing(
|
||||
service_name="pipecat-demo",
|
||||
exporter=otlp_exporter,
|
||||
console_export=bool(os.getenv("OTEL_CONSOLE_EXPORT")),
|
||||
)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No Traces in Langfuse**: Ensure that your credentials are correct and follow this [troubleshooting guide](https://langfuse.com/faq/all/missing-traces)
|
||||
- **Connection Errors**: Verify network connectivity to Langfuse
|
||||
- **Authorization Issues**: Check that your base64 encoding is correct and the API keys are valid
|
||||
|
||||
## References
|
||||
|
||||
- [Langfuse OpenTelemetry Documentation](https://langfuse.com/docs/opentelemetry/get-started)
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from loguru import logger
|
||||
@@ -151,6 +152,7 @@ async def run_bot(webrtc_connection: SmallWebRTCConnection, _: argparse.Namespac
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
from run import main
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user