From c9a85cd7ba865b29456b354d660c41932458c0b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 14 Apr 2026 19:50:11 +0000 Subject: [PATCH] feat(examples): add 540-livekit-voice-agent-python Relates to #225 --- .../.env.example | 10 + .../540-livekit-voice-agent-python/BLOG.md | 418 ++++++++++++++++++ .../540-livekit-voice-agent-python/README.md | 160 +++++++ .../requirements.txt | 14 + .../screenshot.png | Bin 0 -> 78394 bytes .../src/__init__.py | 1 + .../src/agent.py | 102 +++++ .../tests/__init__.py | 1 + .../tests/run_tests.py | 82 ++++ .../tests/test_deepgram_connection.py | 210 +++++++++ .../tests/test_integration.py | 183 ++++++++ .../tests/test_livekit_connection.py | 131 ++++++ 12 files changed, 1312 insertions(+) create mode 100644 examples/540-livekit-voice-agent-python/.env.example create mode 100644 examples/540-livekit-voice-agent-python/BLOG.md create mode 100644 examples/540-livekit-voice-agent-python/README.md create mode 100644 examples/540-livekit-voice-agent-python/requirements.txt create mode 100644 examples/540-livekit-voice-agent-python/screenshot.png create mode 100644 examples/540-livekit-voice-agent-python/src/__init__.py create mode 100644 examples/540-livekit-voice-agent-python/src/agent.py create mode 100644 examples/540-livekit-voice-agent-python/tests/__init__.py create mode 100644 examples/540-livekit-voice-agent-python/tests/run_tests.py create mode 100644 examples/540-livekit-voice-agent-python/tests/test_deepgram_connection.py create mode 100644 examples/540-livekit-voice-agent-python/tests/test_integration.py create mode 100644 examples/540-livekit-voice-agent-python/tests/test_livekit_connection.py diff --git a/examples/540-livekit-voice-agent-python/.env.example b/examples/540-livekit-voice-agent-python/.env.example new file mode 100644 index 0000000..c9198b1 --- /dev/null +++ b/examples/540-livekit-voice-agent-python/.env.example @@ -0,0 +1,10 @@ +# Deepgram API key for speech-to-text and text-to-speech +DEEPGRAM_API_KEY= + +# LiveKit server connection +LIVEKIT_URL=wss://your-livekit-server.livekit.cloud +LIVEKIT_API_KEY= +LIVEKIT_API_SECRET= + +# OpenAI API key for the LLM +OPENAI_API_KEY= diff --git a/examples/540-livekit-voice-agent-python/BLOG.md b/examples/540-livekit-voice-agent-python/BLOG.md new file mode 100644 index 0000000..8e42536 --- /dev/null +++ b/examples/540-livekit-voice-agent-python/BLOG.md @@ -0,0 +1,418 @@ +# Building a Real-Time Voice Agent with LiveKit and Deepgram + +In this tutorial, we'll build a voice-based AI assistant that can have natural conversations with users in real-time. We'll use LiveKit's Agents framework for the real-time communication infrastructure, Deepgram for speech recognition and synthesis, and OpenAI for the conversational intelligence. + +## What We're Building + +Our voice agent will: +- Join a LiveKit room and listen for user speech +- Transcribe speech in real-time using Deepgram's Nova-3 model +- Generate intelligent responses using OpenAI's GPT-4o-mini +- Speak responses back using Deepgram's Aura-2 voices +- Handle natural turn-taking and interruptions + +The end result is a conversational AI you can talk to just like a human—with low latency and natural voice quality. + +## Prerequisites + +Before we start, make sure you have: +- Python 3.10 or later +- A Deepgram account ([sign up free](https://console.deepgram.com/)) +- A LiveKit Cloud account ([get started](https://cloud.livekit.io/)) +- An OpenAI account with API access + +## Project Setup + +Let's start by creating our project structure: + +```bash +mkdir livekit-voice-agent +cd livekit-voice-agent +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +Create a `requirements.txt` file with our dependencies: + +``` +# LiveKit Agents framework +livekit-agents>=1.5.0 + +# Deepgram plugins for STT and TTS +livekit-plugins-deepgram>=1.5.0 + +# OpenAI plugin for LLM +livekit-plugins-openai>=1.5.0 + +# Silero plugin for Voice Activity Detection +livekit-plugins-silero>=1.5.0 + +# Additional dependencies +python-dotenv>=1.0.0 +``` + +Install the dependencies: + +```bash +pip install -r requirements.txt +``` + +## Understanding the Architecture + +Before we write code, let's understand how LiveKit Agents works: + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ LiveKit Room │ +│ │ +│ ┌──────────┐ Audio ┌──────────────────────────────────┐ │ +│ │ User │ ──────────► │ Voice Agent │ │ +│ │ (Browser)│ │ │ │ +│ │ │ ◄────────── │ VAD → STT → LLM → TTS │ │ +│ └──────────┘ Audio │ (Deepgram) (Deepgram) │ │ +│ └──────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +1. **User connects** to a LiveKit room via browser +2. **Audio streams** to the agent in real-time +3. **VAD** (Voice Activity Detection) detects when the user speaks +4. **STT** (Deepgram Nova-3) transcribes speech to text +5. **LLM** (OpenAI) generates a response +6. **TTS** (Deepgram Aura-2) synthesizes speech +7. **Audio streams** back to the user + +The LiveKit Agents framework handles all the complexity of managing these streams, detecting turns, handling interruptions, and more. + +## Creating the Agent + +Create `src/agent.py`: + +```python +""" +LiveKit Voice Agent with Deepgram STT and TTS. +""" + +import logging +from livekit.agents import Agent, AgentSession, JobContext +from livekit.agents.cli import run_app +from livekit.plugins import deepgram, openai, silero + +logger = logging.getLogger("voice-agent") + + +async def entrypoint(ctx: JobContext) -> None: + """Main entrypoint for the agent session.""" + + logger.info(f"Agent joining room: {ctx.room.name}") + + # Connect to the LiveKit room + await ctx.connect() + + # Initialize the Deepgram STT plugin + deepgram_stt = deepgram.STT( + model="nova-3", + language="en-US", + interim_results=True, + punctuate=True, + filler_words=True, + endpointing_ms=25, + ) + + # Initialize the Deepgram TTS plugin + deepgram_tts = deepgram.TTS( + model="aura-2-andromeda-en", + sample_rate=24000, + ) + + # Initialize the OpenAI LLM + openai_llm = openai.LLM( + model="gpt-4o-mini", + temperature=0.7, + ) + + # Initialize Voice Activity Detection + vad = silero.VAD.load() + + # Create the agent with instructions + agent = Agent( + instructions="""You are a helpful voice assistant powered by Deepgram and LiveKit. + +Your role is to: +- Have natural, friendly conversations with users +- Answer questions clearly and concisely +- Be helpful and informative +- Keep responses brief since this is a voice conversation + +Remember: You're speaking, not writing. Keep your responses conversational.""", + ) + + # Create and start the agent session + session = AgentSession( + stt=deepgram_stt, + tts=deepgram_tts, + llm=openai_llm, + vad=vad, + ) + + await session.start(room=ctx.room, agent=agent) + logger.info("Agent session started successfully") + + +if __name__ == "__main__": + from livekit.agents import AgentServer + + server = AgentServer() + + @server.rtc_session + async def _entrypoint(ctx: JobContext) -> None: + await entrypoint(ctx) + + run_app(server) +``` + +Let's break down the key components: + +### Deepgram STT Configuration + +```python +deepgram_stt = deepgram.STT( + model="nova-3", # Latest and most accurate model + language="en-US", # Primary language + interim_results=True, # Get partial transcripts as user speaks + punctuate=True, # Automatic punctuation + filler_words=True, # Include "um", "uh" for natural turn detection + endpointing_ms=25, # Quick response to silence +) +``` + +Nova-3 is Deepgram's latest model with the best accuracy. The `interim_results=True` setting is crucial for responsive agents—it lets the agent start processing before the user finishes speaking. + +### Deepgram TTS Configuration + +```python +deepgram_tts = deepgram.TTS( + model="aura-2-andromeda-en", # Natural female voice + sample_rate=24000, # High quality audio +) +``` + +Aura-2 is Deepgram's latest text-to-speech model, offering natural-sounding voices with low latency. The `andromeda-en` voice is conversational and works well for assistant use cases. + +### Agent Instructions + +The agent's instructions shape its personality and behavior. For voice agents, it's important to emphasize: +- **Brevity**: Long responses are tiresome to listen to +- **Conversational tone**: Written text sounds robotic when spoken +- **Clarity**: Avoid complex sentence structures + +## Environment Configuration + +Create a `.env` file (copy from `.env.example`): + +```bash +# Deepgram API key +DEEPGRAM_API_KEY=your_deepgram_api_key + +# LiveKit credentials +LIVEKIT_URL=wss://your-app.livekit.cloud +LIVEKIT_API_KEY=your_livekit_api_key +LIVEKIT_API_SECRET=your_livekit_api_secret + +# OpenAI API key +OPENAI_API_KEY=your_openai_api_key +``` + +### Getting Your API Keys + +**Deepgram:** +1. Go to [console.deepgram.com](https://console.deepgram.com/) +2. Create a new API key with STT and TTS permissions + +**LiveKit:** +1. Go to [cloud.livekit.io](https://cloud.livekit.io/) +2. Create a new project +3. Copy the URL, API Key, and API Secret from the Settings page + +**OpenAI:** +1. Go to [platform.openai.com](https://platform.openai.com/) +2. Create a new API key + +## Running the Agent + +Start the agent in development mode: + +```bash +python src/agent.py dev +``` + +You'll see output like: + +``` +INFO - Starting agent in development mode +INFO - Agent server listening on port 8081 +INFO - Registered with LiveKit server +``` + +Now open the [LiveKit Agents Playground](https://agents-playground.livekit.io/): + +1. Enter your LiveKit URL +2. Click "Connect" +3. Grant microphone access +4. Start talking! + +The agent will transcribe your speech, generate a response, and speak back to you. + +## Understanding the Agent Pipeline + +When you speak, here's what happens: + +1. **Audio capture**: LiveKit captures your microphone audio +2. **VAD processing**: Silero VAD detects speech boundaries +3. **Streaming STT**: Deepgram transcribes audio in real-time +4. **Turn detection**: Agent decides when you've finished speaking +5. **LLM inference**: OpenAI generates a response +6. **Streaming TTS**: Deepgram synthesizes speech +7. **Audio playback**: LiveKit plays audio to your speakers + +The framework handles all of this automatically, including: +- **Interruption handling**: If you speak while the agent is talking, it stops +- **Turn management**: Natural conversation flow +- **Error recovery**: Automatic retries on transient failures + +## Customizing the Agent + +### Changing the Voice + +Deepgram offers several Aura-2 voices: + +```python +deepgram_tts = deepgram.TTS( + model="aura-2-orion-en", # Male voice + # model="aura-2-luna-en", # Female, more casual + # model="aura-2-stella-en", # British female +) +``` + +### Adjusting STT Sensitivity + +For noisy environments, adjust endpointing: + +```python +deepgram_stt = deepgram.STT( + model="nova-3", + endpointing_ms=100, # Wait longer before ending turn + filler_words=False, # Ignore filler words +) +``` + +### Multi-language Support + +Deepgram supports 30+ languages: + +```python +deepgram_stt = deepgram.STT( + model="nova-3", + language="es", # Spanish + # language="fr", # French + # language="de", # German +) +``` + +### Adding Tools + +Give your agent capabilities: + +```python +from livekit.agents import llm + +@llm.function_tool +async def get_weather(location: str) -> str: + """Get the current weather for a location.""" + # Implementation here + return f"The weather in {location} is sunny and 72°F" + +agent = Agent( + instructions="You are a helpful assistant that can check the weather.", + tools=[get_weather], +) +``` + +## Testing Your Agent + +Create `tests/test_deepgram_connection.py` to verify your setup: + +```python +import asyncio +import os +import aiohttp +from livekit.plugins import deepgram + +async def test_tts(): + """Test Deepgram TTS.""" + async with aiohttp.ClientSession() as session: + tts = deepgram.TTS( + model="aura-2-andromeda-en", + http_session=session, + ) + + audio_bytes = 0 + async for chunk in tts.synthesize("Hello, this is a test."): + audio_bytes += len(chunk.frame.data) + + print(f"✓ TTS generated {audio_bytes} bytes of audio") + +if __name__ == "__main__": + asyncio.run(test_tts()) +``` + +Run the test: + +```bash +python tests/test_deepgram_connection.py +``` + +## Production Deployment + +For production, run the agent in server mode: + +```bash +python src/agent.py start +``` + +This registers the agent with your LiveKit server. When users join rooms, LiveKit automatically dispatches agents to serve them. + +### Scaling Considerations + +- **Multiple workers**: Run multiple agent processes for high availability +- **Load balancing**: LiveKit handles distribution automatically +- **Monitoring**: Use the built-in Prometheus metrics endpoint + +## What's Next + +Now that you have a basic voice agent working, consider: + +1. **Add memory**: Store conversation history for context +2. **Implement tools**: Give the agent capabilities like web search, calendar access +3. **Custom wake words**: Trigger the agent with specific phrases +4. **Sentiment analysis**: Adjust responses based on user emotion +5. **Multi-modal**: Add video understanding using the video sampler + +## Resources + +- [LiveKit Agents Documentation](https://docs.livekit.io/agents/) +- [Deepgram API Reference](https://developers.deepgram.com/reference/) +- [LiveKit Agents GitHub](https://github.com/livekit/agents) +- [Deepgram Nova-3 Announcement](https://deepgram.com/learn/nova-3-speech-to-text-api) +- [Deepgram Aura-2 TTS](https://deepgram.com/learn/aura-2-text-to-speech-api) + +## Conclusion + +You've built a fully functional voice AI agent that combines: +- LiveKit's real-time infrastructure +- Deepgram's industry-leading speech AI +- OpenAI's conversational intelligence + +The LiveKit Agents framework handles the complexity of real-time voice applications, letting you focus on building great experiences. Deepgram's low-latency STT and natural TTS make conversations feel fluid and natural. + +Happy building! 🎙️ diff --git a/examples/540-livekit-voice-agent-python/README.md b/examples/540-livekit-voice-agent-python/README.md new file mode 100644 index 0000000..69f5dae --- /dev/null +++ b/examples/540-livekit-voice-agent-python/README.md @@ -0,0 +1,160 @@ +# LiveKit Voice Agent with Deepgram STT/TTS + +A real-time voice AI agent using LiveKit Agents framework with Deepgram for speech-to-text and text-to-speech. + +![Screenshot](./screenshot.png) + +## What This Does + +This example creates a voice-based AI assistant that: +- Joins a LiveKit room and listens for user speech +- Uses **Deepgram Nova-3** for real-time speech recognition +- Processes user input with **OpenAI GPT-4o-mini** (configurable) +- Responds with natural voice using **Deepgram Aura-2** text-to-speech +- Handles turn-taking and interruptions automatically + +## Prerequisites + +- Python 3.10+ +- A [Deepgram account](https://console.deepgram.com/) with API key +- A [LiveKit Cloud account](https://cloud.livekit.io/) or self-hosted LiveKit server +- An [OpenAI account](https://platform.openai.com/) with API key + +## Environment Variables + +| Variable | Description | +|----------|-------------| +| `DEEPGRAM_API_KEY` | Your Deepgram API key for STT and TTS | +| `LIVEKIT_URL` | LiveKit server WebSocket URL (e.g., `wss://your-app.livekit.cloud`) | +| `LIVEKIT_API_KEY` | LiveKit API key | +| `LIVEKIT_API_SECRET` | LiveKit API secret | +| `OPENAI_API_KEY` | OpenAI API key for the LLM | + +## Quick Start + +1. **Clone and navigate to the example:** + ```bash + cd examples/540-livekit-voice-agent-python + ``` + +2. **Create a virtual environment:** + ```bash + python -m venv venv + source venv/bin/activate # On Windows: venv\Scripts\activate + ``` + +3. **Install dependencies:** + ```bash + pip install -r requirements.txt + ``` + +4. **Configure environment variables:** + ```bash + cp .env.example .env + # Edit .env with your API keys + ``` + +5. **Run the agent in development mode:** + ```bash + python src/agent.py dev + ``` + +6. **Connect to the agent:** + - Open the [LiveKit Playground](https://agents-playground.livekit.io/) + - Enter your LiveKit URL + - Click "Connect" to join the same room as the agent + - Start speaking! + +## How It Works + +The agent uses the LiveKit Agents framework pipeline: + +``` +User Speech → Deepgram STT → OpenAI LLM → Deepgram TTS → Audio Output + ↑ ↓ + (Nova-3) (Aura-2) +``` + +1. **Voice Activity Detection (VAD)**: Silero VAD detects when the user starts/stops speaking +2. **Speech-to-Text**: Deepgram Nova-3 transcribes user speech in real-time +3. **LLM Processing**: OpenAI generates a response based on conversation history +4. **Text-to-Speech**: Deepgram Aura-2 synthesizes natural-sounding audio +5. **Turn Management**: LiveKit Agents handles interruptions and turn-taking + +## Running in Production + +For production deployment: + +```bash +python src/agent.py start +``` + +This registers the agent with your LiveKit server so it can be dispatched to rooms automatically. + +## Configuration Options + +### Deepgram STT Options + +```python +deepgram.STT( + model="nova-3", # Latest Deepgram model + language="en-US", # Language code + interim_results=True, # Enable partial transcripts + punctuate=True, # Add punctuation + filler_words=True, # Include um, uh, etc. + endpointing_ms=25, # Silence detection threshold +) +``` + +### Deepgram TTS Options + +```python +deepgram.TTS( + model="aura-2-andromeda-en", # Voice model + sample_rate=24000, # Audio sample rate +) +``` + +### Available Deepgram TTS Voices + +- `aura-2-andromeda-en` - Female, American English +- `aura-2-orion-en` - Male, American English +- `aura-2-luna-en` - Female, American English (conversational) +- `aura-2-stella-en` - Female, British English +- `aura-2-athena-en` - Female, British English + +## Testing + +Run the test suite: + +```bash +python tests/run_tests.py +``` + +Tests verify: +- Deepgram STT connection and transcription +- Deepgram TTS connection and audio generation +- Plugin initialization +- Agent module structure + +## Troubleshooting + +### "Connection error" on startup +- Verify your `DEEPGRAM_API_KEY` is correct +- Check your internet connection +- Ensure the API key has STT and TTS permissions + +### Agent doesn't respond to speech +- Check that your microphone is working in the LiveKit Playground +- Verify the VAD is detecting speech (check console logs) +- Ensure `OPENAI_API_KEY` is set correctly + +### Audio quality issues +- Try adjusting `sample_rate` in TTS settings +- Check your network latency to LiveKit server + +## Resources + +- [LiveKit Agents Documentation](https://docs.livekit.io/agents/) +- [Deepgram Documentation](https://developers.deepgram.com/) +- [LiveKit Deepgram Plugin](https://github.com/livekit/agents/tree/main/livekit-plugins/livekit-plugins-deepgram) diff --git a/examples/540-livekit-voice-agent-python/requirements.txt b/examples/540-livekit-voice-agent-python/requirements.txt new file mode 100644 index 0000000..f5c4ecf --- /dev/null +++ b/examples/540-livekit-voice-agent-python/requirements.txt @@ -0,0 +1,14 @@ +# LiveKit Agents framework +livekit-agents>=1.5.0 + +# Deepgram plugins for STT and TTS +livekit-plugins-deepgram>=1.5.0 + +# OpenAI plugin for LLM +livekit-plugins-openai>=1.5.0 + +# Silero plugin for Voice Activity Detection +livekit-plugins-silero>=1.5.0 + +# Additional dependencies +python-dotenv>=1.0.0 diff --git a/examples/540-livekit-voice-agent-python/screenshot.png b/examples/540-livekit-voice-agent-python/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..1782dd4855f968e3535c6dccacf078690f8c9373 GIT binary patch literal 78394 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%0A$`y}*YL&UPRR2>{W~ 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 None: + """Main entrypoint for the agent session.""" + + logger.info(f"Agent joining room: {ctx.room.name}") + + # Connect to the LiveKit room + await ctx.connect() + + # Initialize the Deepgram STT (Speech-to-Text) plugin + # Nova-3 is Deepgram's latest and most accurate model + deepgram_stt = deepgram.STT( + model="nova-3", + language="en-US", + interim_results=True, + punctuate=True, + filler_words=True, + endpointing_ms=25, + ) + + # Initialize the Deepgram TTS (Text-to-Speech) plugin + # Aura-2 provides natural, conversational voice synthesis + deepgram_tts = deepgram.TTS( + model="aura-2-andromeda-en", + sample_rate=24000, + ) + + # Initialize the OpenAI LLM + openai_llm = openai.LLM( + model="gpt-4o-mini", + temperature=0.7, + ) + + # Initialize Voice Activity Detection (VAD) using Silero + # This helps detect when the user starts and stops speaking + vad = silero.VAD.load() + + # Create the agent with custom instructions + agent = Agent( + instructions="""You are a helpful voice assistant powered by Deepgram and LiveKit. + +Your role is to: +- Have natural, friendly conversations with users +- Answer questions clearly and concisely +- Be helpful and informative +- Keep responses brief since this is a voice conversation + +Remember: You're speaking, not writing. Keep your responses conversational and +avoid overly long explanations. If you need to explain something complex, +break it into digestible parts and check if the user wants more detail.""", + ) + + # Create and start the agent session + session = AgentSession( + stt=deepgram_stt, + tts=deepgram_tts, + llm=openai_llm, + vad=vad, + ) + + # Start the agent in the room + await session.start( + room=ctx.room, + agent=agent, + ) + + logger.info("Agent session started successfully") + + +if __name__ == "__main__": + # Create and run the agent server + from livekit.agents import AgentServer + + server = AgentServer() + + @server.rtc_session + async def _entrypoint(ctx: JobContext) -> None: + await entrypoint(ctx) + + run_app(server) diff --git a/examples/540-livekit-voice-agent-python/tests/__init__.py b/examples/540-livekit-voice-agent-python/tests/__init__.py new file mode 100644 index 0000000..92c15a6 --- /dev/null +++ b/examples/540-livekit-voice-agent-python/tests/__init__.py @@ -0,0 +1 @@ +# Tests for LiveKit Voice Agent diff --git a/examples/540-livekit-voice-agent-python/tests/run_tests.py b/examples/540-livekit-voice-agent-python/tests/run_tests.py new file mode 100644 index 0000000..21de839 --- /dev/null +++ b/examples/540-livekit-voice-agent-python/tests/run_tests.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +Test runner that runs all tests and reports results. + +Exit codes: +- 0: All tests passed +- 1: Some tests failed +- 2: Missing credentials (tests skipped) +""" + +import asyncio +import os +import subprocess +import sys +from pathlib import Path + + +def run_test(test_file: str) -> int: + """Run a single test file and return exit code.""" + print(f"\n{'='*60}") + print(f"Running: {test_file}") + print('='*60 + "\n") + + result = subprocess.run( + [sys.executable, test_file], + cwd=Path(__file__).parent, + env=os.environ.copy(), + ) + + return result.returncode + + +def main() -> int: + """Run all tests.""" + test_dir = Path(__file__).parent + + # List of test files to run + test_files = [ + "test_deepgram_connection.py", + "test_livekit_connection.py", + "test_integration.py", + ] + + results = {} + has_failure = False + all_skipped = True + + for test_file in test_files: + test_path = test_dir / test_file + if test_path.exists(): + exit_code = run_test(str(test_path)) + results[test_file] = exit_code + + if exit_code == 1: + has_failure = True + if exit_code != 2: + all_skipped = False + + # Summary + print("\n" + "="*60) + print("TEST SUMMARY") + print("="*60) + + for test_file, code in results.items(): + status = {0: "PASS ✓", 1: "FAIL ✗", 2: "SKIP ⊘"}.get(code, f"UNKNOWN ({code})") + print(f" {test_file}: {status}") + + print() + + if has_failure: + print("Some tests failed!") + return 1 + elif all_skipped: + print("All tests skipped due to missing credentials") + return 2 + else: + print("All tests passed!") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/examples/540-livekit-voice-agent-python/tests/test_deepgram_connection.py b/examples/540-livekit-voice-agent-python/tests/test_deepgram_connection.py new file mode 100644 index 0000000..64767dd --- /dev/null +++ b/examples/540-livekit-voice-agent-python/tests/test_deepgram_connection.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +""" +Test Deepgram STT and TTS connections. + +This test verifies that: +1. The Deepgram API key is valid +2. Deepgram STT (speech-to-text) works correctly +3. Deepgram TTS (text-to-speech) works correctly + +Exit codes: +- 0: All tests passed +- 1: Test failed +- 2: Missing credentials (skip) +""" + +import asyncio +import os +import sys +import aiohttp + + +def check_credentials() -> bool: + """Check if required credentials are available.""" + deepgram_key = os.environ.get("DEEPGRAM_API_KEY") + if not deepgram_key: + print("SKIP: DEEPGRAM_API_KEY not set") + return False + return True + + +async def test_deepgram_tts() -> bool: + """Test Deepgram TTS by synthesizing audio.""" + from livekit.plugins import deepgram + + print("Testing Deepgram TTS...") + + async with aiohttp.ClientSession() as session: + try: + tts = deepgram.TTS( + model="aura-2-andromeda-en", + sample_rate=24000, + http_session=session, + ) + + # Synthesize a test phrase + test_text = "Hello! This is a test of Deepgram text to speech." + + audio_data = [] + async for chunk in tts.synthesize(test_text): + audio_data.append(chunk.frame.data) + + # Verify we got audio data back + total_bytes = sum(len(chunk) for chunk in audio_data) + + if total_bytes > 0: + print(f" ✓ TTS generated {total_bytes} bytes of audio") + return True + else: + print(" ✗ TTS returned no audio data") + return False + + except Exception as e: + print(f" ✗ TTS error: {e}") + return False + + +async def test_deepgram_stt() -> bool: + """Test Deepgram STT by transcribing audio.""" + from livekit.plugins import deepgram + from livekit.agents.stt import SpeechEventType + + print("Testing Deepgram STT...") + + async with aiohttp.ClientSession() as session: + try: + stt = deepgram.STT( + model="nova-3", + language="en-US", + punctuate=True, + http_session=session, + ) + + # First generate some audio using TTS + tts = deepgram.TTS( + model="aura-2-andromeda-en", + sample_rate=16000, # Match STT sample rate + http_session=session, + ) + + test_phrase = "The quick brown fox jumps over the lazy dog." + + # Collect audio frames from TTS + audio_frames = [] + async for chunk in tts.synthesize(test_phrase): + audio_frames.append(chunk.frame) + + if not audio_frames: + print(" ✗ No audio frames generated for STT test") + return False + + print(f" - Generated {len(audio_frames)} audio frames for transcription") + + # Create STT stream + stream = stt.stream() + + # Push audio frames to the stream + for frame in audio_frames: + stream.push_frame(frame) + + # Signal end of input + stream.end_input() + + # Collect transcription results + final_transcript = "" + async for event in stream: + if event.type == SpeechEventType.FINAL_TRANSCRIPT and event.alternatives: + final_transcript += event.alternatives[0].text + " " + + final_transcript = final_transcript.strip() + + if final_transcript: + print(f" ✓ STT transcribed: '{final_transcript}'") + + # Check if transcription is reasonably accurate + original_words = set(test_phrase.lower().replace(".", "").split()) + transcribed_words = set(final_transcript.lower().replace(".", "").split()) + + common_words = original_words.intersection(transcribed_words) + + if len(common_words) >= 3: + print(f" ✓ Transcription accuracy check passed ({len(common_words)} matching words)") + return True + else: + print(f" ⚠ Transcription may be inaccurate (only {len(common_words)} matching words)") + return True # Still pass - Deepgram is working + else: + print(" ✗ STT returned empty transcription") + return False + + except Exception as e: + print(f" ✗ STT error: {e}") + import traceback + traceback.print_exc() + return False + + +async def test_plugin_initialization() -> bool: + """Test that Deepgram plugins initialize correctly.""" + from livekit.plugins import deepgram + + print("Testing plugin initialization...") + + async with aiohttp.ClientSession() as session: + try: + # Test STT initialization + stt = deepgram.STT( + model="nova-3", + language="en-US", + http_session=session, + ) + print(f" ✓ STT initialized - model: {stt.model}, provider: {stt.provider}") + + # Test TTS initialization + tts = deepgram.TTS( + model="aura-2-andromeda-en", + http_session=session, + ) + print(f" ✓ TTS initialized - model: {tts.model}, provider: {tts.provider}") + + return True + + except Exception as e: + print(f" ✗ Plugin initialization error: {e}") + return False + + +async def main() -> int: + """Run all tests.""" + print("=" * 60) + print("Deepgram Connection Tests") + print("=" * 60) + print() + + # Check credentials + if not check_credentials(): + return 2 + + results = [] + + # Run tests + results.append(await test_plugin_initialization()) + results.append(await test_deepgram_tts()) + results.append(await test_deepgram_stt()) + + print() + print("=" * 60) + + if all(results): + print("All tests passed! ✓") + return 0 + else: + passed = sum(results) + total = len(results) + print(f"Tests: {passed}/{total} passed") + return 1 + + +if __name__ == "__main__": + exit_code = asyncio.run(main()) + sys.exit(exit_code) diff --git a/examples/540-livekit-voice-agent-python/tests/test_integration.py b/examples/540-livekit-voice-agent-python/tests/test_integration.py new file mode 100644 index 0000000..acb1e3a --- /dev/null +++ b/examples/540-livekit-voice-agent-python/tests/test_integration.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +""" +Integration test for the LiveKit Voice Agent. + +This test verifies that: +1. All plugins can be loaded and configured +2. An AgentSession can be created with Deepgram STT/TTS +3. The agent module imports correctly + +Exit codes: +- 0: All tests passed +- 1: Test failed +- 2: Missing credentials (skip) +""" + +import asyncio +import os +import sys + + +def check_credentials() -> bool: + """Check if required credentials are available.""" + if not os.environ.get("DEEPGRAM_API_KEY"): + print("SKIP: DEEPGRAM_API_KEY not set") + return False + return True + + +async def test_imports() -> bool: + """Test that all required modules can be imported.""" + print("Testing module imports...") + + try: + from livekit.agents import Agent, AgentSession, JobContext + from livekit.agents.cli import run_app + from livekit.plugins import deepgram, silero + + print(" ✓ All modules imported successfully") + return True + + except ImportError as e: + print(f" ✗ Import error: {e}") + return False + + +async def test_agent_session_creation() -> bool: + """Test creating an AgentSession with Deepgram plugins.""" + print("Testing AgentSession creation...") + + try: + from livekit.agents import Agent, AgentSession + from livekit.plugins import deepgram, silero + + # Initialize Deepgram STT + stt = deepgram.STT( + model="nova-3", + language="en-US", + interim_results=True, + punctuate=True, + ) + + # Initialize Deepgram TTS + tts = deepgram.TTS( + model="aura-2-andromeda-en", + sample_rate=24000, + ) + + # Initialize VAD + vad = silero.VAD.load() + + # Create agent + agent = Agent( + instructions="You are a helpful assistant.", + ) + + # Create session (without starting it) + session = AgentSession( + stt=stt, + tts=tts, + vad=vad, + ) + + print(" ✓ AgentSession created successfully") + print(f" - STT model: {stt.model}") + print(f" - TTS model: {tts.model}") + + return True + + except Exception as e: + print(f" ✗ AgentSession creation error: {e}") + import traceback + traceback.print_exc() + return False + + +async def test_deepgram_models() -> bool: + """Test that Deepgram models are available.""" + print("Testing Deepgram models...") + + try: + from livekit.plugins.deepgram import models + + # Check if expected models are available + print(f" - Available model definitions loaded") + + from livekit.plugins import deepgram + + # Verify STT model options + stt = deepgram.STT(model="nova-3") + print(f" ✓ Nova-3 STT model available") + + # Verify TTS model options + tts = deepgram.TTS(model="aura-2-andromeda-en") + print(f" ✓ Aura-2 TTS model available") + + return True + + except Exception as e: + print(f" ✗ Model test error: {e}") + return False + + +async def test_agent_module_import() -> bool: + """Test importing the main agent module.""" + print("Testing agent module...") + + try: + # Add src to path + sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + + # Import should not fail + import agent + + # Verify entrypoint exists + if hasattr(agent, 'entrypoint'): + print(" ✓ Agent module loaded, entrypoint found") + return True + else: + print(" ✗ Agent module missing entrypoint") + return False + + except Exception as e: + print(f" ✗ Agent module error: {e}") + import traceback + traceback.print_exc() + return False + + +async def main() -> int: + """Run all tests.""" + print("=" * 60) + print("Integration Tests") + print("=" * 60) + print() + + # Check credentials + if not check_credentials(): + return 2 + + results = [] + + # Run tests + results.append(await test_imports()) + results.append(await test_deepgram_models()) + results.append(await test_agent_session_creation()) + results.append(await test_agent_module_import()) + + print() + print("=" * 60) + + if all(results): + print("All tests passed! ✓") + return 0 + else: + passed = sum(results) + total = len(results) + print(f"Tests: {passed}/{total} passed") + return 1 + + +if __name__ == "__main__": + exit_code = asyncio.run(main()) + sys.exit(exit_code) diff --git a/examples/540-livekit-voice-agent-python/tests/test_livekit_connection.py b/examples/540-livekit-voice-agent-python/tests/test_livekit_connection.py new file mode 100644 index 0000000..d34515e --- /dev/null +++ b/examples/540-livekit-voice-agent-python/tests/test_livekit_connection.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +""" +Test LiveKit server connection. + +This test verifies that: +1. LiveKit credentials are valid +2. Can connect to the LiveKit server + +Exit codes: +- 0: All tests passed +- 1: Test failed +- 2: Missing credentials (skip) +""" + +import asyncio +import os +import sys + + +def check_credentials() -> bool: + """Check if required credentials are available.""" + missing = [] + + if not os.environ.get("LIVEKIT_URL"): + missing.append("LIVEKIT_URL") + if not os.environ.get("LIVEKIT_API_KEY"): + missing.append("LIVEKIT_API_KEY") + if not os.environ.get("LIVEKIT_API_SECRET"): + missing.append("LIVEKIT_API_SECRET") + + if missing: + print(f"SKIP: Missing LiveKit credentials: {', '.join(missing)}") + return False + return True + + +async def test_livekit_api() -> bool: + """Test LiveKit API connection by listing rooms.""" + from livekit import api + + print("Testing LiveKit API connection...") + + try: + livekit_api = api.LiveKitAPI( + url=os.environ.get("LIVEKIT_URL"), + api_key=os.environ.get("LIVEKIT_API_KEY"), + api_secret=os.environ.get("LIVEKIT_API_SECRET"), + ) + + # List rooms to verify connection + rooms = await livekit_api.room.list_rooms(api.ListRoomsRequest()) + + print(f" ✓ Connected to LiveKit server") + print(f" - Active rooms: {len(rooms.rooms)}") + + await livekit_api.aclose() + return True + + except Exception as e: + print(f" ✗ LiveKit API error: {e}") + return False + + +async def test_token_generation() -> bool: + """Test LiveKit access token generation.""" + from livekit import api + + print("Testing token generation...") + + try: + api_key = os.environ.get("LIVEKIT_API_KEY") + api_secret = os.environ.get("LIVEKIT_API_SECRET") + + # Create an access token + token = api.AccessToken(api_key, api_secret) + token.with_identity("test-agent") + token.with_name("Test Agent") + token.with_grants(api.VideoGrants( + room_join=True, + room="test-room", + can_publish=True, + can_subscribe=True, + )) + + jwt = token.to_jwt() + + if jwt and len(jwt) > 100: + print(f" ✓ Token generated successfully ({len(jwt)} chars)") + return True + else: + print(" ✗ Token generation produced invalid token") + return False + + except Exception as e: + print(f" ✗ Token generation error: {e}") + return False + + +async def main() -> int: + """Run all tests.""" + print("=" * 60) + print("LiveKit Connection Tests") + print("=" * 60) + print() + + # Check credentials + if not check_credentials(): + return 2 + + results = [] + + # Run tests + results.append(await test_token_generation()) + results.append(await test_livekit_api()) + + print() + print("=" * 60) + + if all(results): + print("All tests passed! ✓") + return 0 + else: + passed = sum(results) + total = len(results) + print(f"Tests: {passed}/{total} passed") + return 1 + + +if __name__ == "__main__": + exit_code = asyncio.run(main()) + sys.exit(exit_code)