Files
pipecat/examples/foundational/07za-interruptible-camb.py
2026-01-16 01:18:36 +08:00

208 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#
# Copyright (c) 20242025, Daily
#
# SPDX-License-Identifier: BSD 2-Clause License
#
"""Camb.ai MARS-8 TTS example with interruption handling.
This example demonstrates:
- Basic TTS synthesis with Camb.ai MARS-8
- Voice selection
- Speed control
- Handling interruptions
Requirements:
- CAMB_API_KEY environment variable
- OPENAI_API_KEY environment variable (for LLM)
- DEEPGRAM_API_KEY environment variable (for STT)
Usage:
export CAMB_API_KEY=your_camb_api_key
export OPENAI_API_KEY=your_openai_api_key
export DEEPGRAM_API_KEY=your_deepgram_api_key
python 07za-interruptible-camb.py --transport daily
For more information:
- Camb.ai API docs: https://camb.mintlify.app/
- Pipecat docs: https://docs.pipecat.ai/
"""
import os
import aiohttp
from dotenv import load_dotenv
from loguru import logger
from pipecat.audio.vad.silero import SileroVADAnalyzer
from pipecat.audio.vad.vad_analyzer import VADParams
from pipecat.frames.frames import LLMRunFrame
from pipecat.pipeline.pipeline import Pipeline
from pipecat.pipeline.runner import PipelineRunner
from pipecat.pipeline.task import PipelineParams, PipelineTask
from pipecat.processors.aggregators.llm_context import LLMContext
from pipecat.processors.aggregators.llm_response_universal import (
LLMContextAggregatorPair,
)
from pipecat.runner.types import RunnerArguments
from pipecat.runner.utils import create_transport
from pipecat.services.camb.tts import CambTTSService
from pipecat.services.deepgram.stt import DeepgramSTTService
from pipecat.services.openai.llm import OpenAILLMService
from pipecat.transports.base_transport import BaseTransport, TransportParams
from pipecat.transports.daily.transport import DailyParams
from pipecat.transports.websocket.fastapi import FastAPIWebsocketParams
load_dotenv(override=True)
# Transport configuration for different platforms
transport_params = {
"daily": lambda: DailyParams(
audio_in_enabled=True,
audio_out_enabled=True,
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
),
"twilio": lambda: FastAPIWebsocketParams(
audio_in_enabled=True,
audio_out_enabled=True,
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
),
"webrtc": lambda: TransportParams(
audio_in_enabled=True,
audio_out_enabled=True,
vad_analyzer=SileroVADAnalyzer(params=VADParams(stop_secs=0.2)),
),
}
async def run_bot(transport: BaseTransport, runner_args: RunnerArguments):
"""Run the bot with Camb.ai TTS.
Args:
transport: The transport to use for audio I/O.
runner_args: Runner arguments from the CLI.
"""
logger.info("Starting Camb.ai TTS bot")
# Create an HTTP session for the TTS service
async with aiohttp.ClientSession() as session:
# Initialize Deepgram STT for speech recognition
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
# Initialize Camb.ai TTS with MARS-8-flash model (fastest)
tts = CambTTSService(
api_key=os.getenv("CAMB_API_KEY"),
aiohttp_session=session,
voice_id=2681, # Attic voice (default)
model="mars-8-flash", # Fast inference model
params=CambTTSService.InputParams(
speed=1.0, # Normal speed (0.5-2.0 range)
),
)
# Initialize OpenAI LLM
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"))
# System prompt for the assistant
messages = [
{
"role": "system",
"content": """You are a helpful voice assistant powered by Camb.ai's MARS-8
text-to-speech technology. Your goal is to have natural conversations and demonstrate
high-quality speech synthesis. Keep your responses concise and conversational since
they will be spoken aloud. Avoid special characters, emojis, or bullet points that
can't easily be spoken.""",
},
]
# Set up context management
context = LLMContext(messages)
context_aggregator = LLMContextAggregatorPair(context)
# Build the pipeline
pipeline = Pipeline(
[
transport.input(), # Transport user input
stt, # Speech-to-text
context_aggregator.user(), # User context aggregation
llm, # Language model
tts, # Camb.ai TTS
transport.output(), # Transport bot output
context_aggregator.assistant(), # Assistant context aggregation
]
)
# Create the pipeline task
task = PipelineTask(
pipeline,
params=PipelineParams(
enable_metrics=True,
enable_usage_metrics=True,
),
idle_timeout_secs=runner_args.pipeline_idle_timeout_secs,
)
@transport.event_handler("on_client_connected")
async def on_client_connected(transport, client):
logger.info("Client connected")
# Start the conversation with a greeting
messages.append(
{
"role": "system",
"content": "Please introduce yourself briefly and ask how you can help.",
}
)
await task.queue_frames([LLMRunFrame()])
@transport.event_handler("on_client_disconnected")
async def on_client_disconnected(transport, client):
logger.info("Client disconnected")
await task.cancel()
# Run the pipeline
runner = PipelineRunner(handle_sigint=runner_args.handle_sigint)
await runner.run(task)
async def bot(runner_args: RunnerArguments):
"""Main bot entry point compatible with Pipecat Cloud.
Args:
runner_args: Arguments passed from the runner.
"""
transport = await create_transport(runner_args, transport_params)
await run_bot(transport, runner_args)
async def list_available_voices():
"""Helper function to list available Camb.ai voices.
Run this to see what voices are available for your API key.
"""
async with aiohttp.ClientSession() as session:
voices = await CambTTSService.list_voices(
api_key=os.getenv("CAMB_API_KEY"),
aiohttp_session=session,
)
print("\nAvailable Camb.ai voices:")
print("-" * 50)
for voice in voices:
print(f" ID: {voice['id']}, Name: {voice['name']}, Gender: {voice['gender']}")
print("-" * 50)
print(f"Total: {len(voices)} voices\n")
if __name__ == "__main__":
import sys
# If --list-voices flag is passed, list voices and exit
if "--list-voices" in sys.argv:
import asyncio
asyncio.run(list_available_voices())
else:
from pipecat.runner.run import main
main()