Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions examples/telnyx/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# LiveKit Configuration
LIVEKIT_URL="wss://your-project.livekit.cloud"
LIVEKIT_API_KEY="your_api_key"
LIVEKIT_API_SECRET="your_api_secret"

# Telnyx SIP Trunk Configuration
# Configure these in LiveKit Cloud with Telnyx as your SIP provider
LIVEKIT_SIP_OUTBOUND_TRUNK="ST_xxxxxxxxxxxxx" # Your Telnyx trunk ID from LiveKit Cloud
LIVEKIT_SIP_NUMBER="+1xxxxxxxxxx" # Your Telnyx phone number (caller ID)

# Optional: Supervisory phone number for transfers
LIVEKIT_SUPERVISOR_PHONE_NUMBER="+1xxxxxxxxxx"

# Agent dispatch name (must match the agent_name in your SIP trunk configuration)
TELNYX_AGENT_DISPATCH_NAME="telnyx-agent"

# Model Configuration (using LiveKit Inference)
OPENAI_API_KEY="sk-xxxxxxx"
DEEPGRAM_API_KEY="xxxxxxxxx"
CARTESIA_API_KEY="xxxxxxxxx"
122 changes: 122 additions & 0 deletions examples/telnyx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Telnyx SIP Trunk Provider Example

This example demonstrates how to configure LiveKit Agents to work with Telnyx as a SIP trunk provider for telephony integrations.

## Prerequisites

1. **LiveKit Cloud Account** - Sign up at [livekit.io](https://livekit.io)
2. **Telnyx Account** - Sign up at [telnyx.com](https://telnyx.com)
3. **Phone Number** - A Telnyx phone number for receiving/making calls

## Telnyx Configuration

### 1. Set Up Telnyx

1. Log in to the [Telnyx Mission Control Portal](https://portal.telnyx.com)
2. Create a new Connection (SIP Trunk)
3. Configure your inbound/outbound routes
4. Purchase or assign a phone number to your connection

### 2. Configure LiveKit SIP

In the LiveKit Cloud dashboard:

1. Navigate to **SIP** > **Trunks**
2. Create a new SIP trunk with Telnyx credentials:
- **Trunk Type**: Outbound (for making calls)
- **SIP Domain**: Your Telnyx SIP domain (e.g., `sip.telnyx.com`)
- **Authentication**: Use Telnyx API credentials or IP-based auth
3. Note your **Trunk ID** (starts with `ST_`)

## Environment Variables

Create a `.env` file in the `telnyx` directory:

```bash
# LiveKit Configuration
LIVEKIT_URL="wss://your-project.livekit.cloud"
LIVEKIT_API_KEY="your_api_key"
LIVEKIT_API_SECRET="your_api_secret"

# Telnyx SIP Trunk Configuration
# The SIP trunk ID from LiveKit Cloud (configured with Telnyx as the provider)
LIVEKIT_SIP_OUTBOUND_TRUNK="ST_xxxxxxxxxxxxx"

# Phone number for caller ID (your Telnyx phone number)
LIVEKIT_SIP_NUMBER="+1xxxxxxxxxx"

# Optional: Supervisory phone number for transfers
LIVEKIT_SUPERVISOR_PHONE_NUMBER="+1xxxxxxxxxx"

# Model Configuration (using LiveKit Inference)
OPENAI_API_KEY="sk-xxxxxxx"
DEEPGRAM_API_KEY="xxxxxxxxx"
CARTESIA_API_KEY="xxxxxxxxx"
```

## Running the Example

```bash
# From the repository root
uv run examples/telnyx/basic_telnyx_agent.py console
```

## Architecture

```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Caller │────▶│ LiveKit │────▶│ LiveKit │────▶│ Telnyx │
│ Phone │ │ Cloud │ │ Agents │ │ SIP Trunk │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │
│ │
└─────────────▶ PSTN ◀──────────────────┘
```

## Key Configuration Points

### Telnyx Connection Settings

In Telnyx Mission Control Portal, ensure:

- **SIP Connection**: Enabled with appropriate codecs (G711, opus)
- **IP Authentication**: Add your LiveKit server IPs if using IP-based auth
- **Called Number Routes**: Configure patterns to route calls to your agents
- **Calling Number**: Set your Telnyx phone number as the caller ID

### LiveKit SIP Trunk Settings

In LiveKit Cloud:

- **SIP Domain**: `sip.telnyx.com` (or your dedicated Telnyx SIP domain)
- **Port**: 5060 (UDP/TCP) or 5061 (TLS)
- **Codecs**: G711, opus
- **Authentication**: Match your Telnyx configuration

## Example Features

This example includes:

- Basic telephony agent with voice input/output
- DTMF input support for IVR interactions
- Transfer capability using Telnyx trunk

## Troubleshooting

### Calls Not Connecting

1. Verify Telnyx connection status in Mission Control Portal
2. Check LiveKit SIP trunk configuration
3. Ensure firewall allows SIP traffic (5060/5061)

### One-Way Audio

1. Check NAT settings in Telnyx and LiveKit
2. Verify RTP port ranges are open
3. Ensure STUN/TURN servers are configured

### Authentication Failures

1. Verify SIP credentials match between Telnyx and LiveKit
2. Check IP allowlist if using IP-based auth
3. Review TLS certificates if using secure SIP
170 changes: 170 additions & 0 deletions examples/telnyx/basic_telnyx_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
"""
Basic Telnyx SIP Trunk Provider Example

This example demonstrates how to configure LiveKit Agents to work with Telnyx
as a SIP trunk provider for telephony integrations.

Requirements:
- LiveKit Cloud account with SIP trunk configured using Telnyx
- Telnyx account with phone number and SIP connection
- Environment variables set (see README.md)

Usage:
python basic_telnyx_agent.py console
"""

import logging
import os

from dotenv import load_dotenv

from livekit.agents import (
Agent,
AgentServer,
AgentSession,
JobContext,
MetricsCollectedEvent,
cli,
inference,
metrics,
room_io,
)
from livekit.agents.llm import function_tool
from livekit.agents.voice.events import RunContext
from livekit.plugins import silero
from livekit.plugins.turn_detector.multilingual import MultilingualModel

logger = logging.getLogger("telnyx-agent")

load_dotenv()

# Telnyx SIP Trunk Configuration
# These environment variables should be set in your .env file
# See README.md for configuration instructions
SIP_TRUNK_ID = os.getenv("LIVEKIT_SIP_OUTBOUND_TRUNK", "")
SIP_NUMBER = os.getenv("LIVEKIT_SIP_NUMBER", "")
SUPERVISOR_PHONE_NUMBER = os.getenv("LIVEKIT_SUPERVISOR_PHONE_NUMBER", "")


class TelnyxAgent(Agent):
"""Example agent configured for Telnyx SIP trunk integration."""

def __init__(self) -> None:
super().__init__(
instructions=(
"You are a friendly customer service agent speaking with a caller "
"over the phone via Telnyx SIP trunking. "
"Keep your responses concise, warm, and professional. "
"Speak clearly and at a measured pace. "
"Introduce yourself and ask how you can help."
),
)

async def on_enter(self) -> None:
"""Called when the agent joins the call."""
self.session.generate_reply(
instructions=(
"Greet the caller warmly, introduce yourself as a customer service "
"representative, and ask how you can help them today."
),
allow_interruptions=False,
)

@function_tool
async def get_store_hours(self, context: RunContext) -> str:
"""Get the current store hours for the business."""
return (
"Our store is open Monday through Friday, 9 AM to 6 PM, "
"and Saturday, 10 AM to 4 PM. We're closed on Sundays."
)

@function_tool
async def get_location(self, context: RunContext) -> str:
"""Get the physical location of the business."""
return (
"We are located at 123 Main Street, San Francisco, CA 94102. "
"We're downtown near Union Square."
)

@function_tool
async def confirm_appointment(self, context: RunContext, phone: str, time: str) -> str:
"""Confirm an appointment for the caller.

Args:
phone: The phone number to confirm the appointment for
time: The requested appointment time
"""
logger.info(f"Confirming appointment for {phone} at {time}")
return f"I've confirmed your appointment for {phone} at {time}. You should receive a text reminder before your appointment."


server = AgentServer()


@server.rtc_session(agent_name="telnyx-agent")
async def entrypoint(ctx: JobContext):
"""Entry point for the Telnyx agent.

This function is called when LiveKit dispatches an agent to handle
an incoming call through the Telnyx SIP trunk.
"""
ctx.log_context_fields = {
"room": ctx.room.name,
"sip_trunk": SIP_TRUNK_ID[:8] + "..." if SIP_TRUNK_ID else "not configured",
"sip_number": SIP_NUMBER,
}

session = AgentSession(
# Speech-to-text (STT) - converts caller speech to text
# Using Deepgram Nova-3 for telephony-optimized transcription
stt=inference.STT("deepgram/nova-3", language="en"),
# Large Language Model (LLM) - processes user input and generates responses
# Using OpenAI GPT-4.1-mini for efficient, fast responses
llm=inference.LLM("openai/gpt-4.1-mini"),
# Text-to-speech (TTS) - generates voice output for the caller
# Using Cartesia Sonic for low-latency, natural speech
tts=inference.TTS("cartesia/sonic-3", voice="9626c31c-bec5-4cca-baa8-f8ba9e84c8bc"),
# Voice Activity Detection (VAD) - detects when the caller is speaking
vad=ctx.proc.userdata.get("vad", silero.VAD.load()),
# Turn detection - determines when to respond in the conversation
turn_detection=MultilingualModel(),
# Enable preemptive generation for faster response times
preemptive_generation=True,
)

# Collect metrics for monitoring and debugging
usage_collector = metrics.UsageCollector()

@session.on("metrics_collected")
def _on_metrics_collected(ev: MetricsCollectedEvent):
metrics.log_metrics(ev.metrics)
usage_collector.collect(ev.metrics)

async def log_usage() -> None:
summary = usage_collector.get_summary()
logger.info(f"Usage: {summary}")

ctx.add_shutdown_callback(log_usage)

await session.start(
agent=TelnyxAgent(),
room=ctx.room,
room_options=room_io.RoomOptions(
audio_input=room_io.AudioInputOptions(),
audio_output=room_io.AudioOutputOptions(),
),
)


def prewarm(proc):
"""Prewarm the job process with VAD model."""
proc.userdata["vad"] = silero.VAD.load()


server.setup_fnc = prewarm


if __name__ == "__main__":
# Run the agent server
# Use 'console' mode for testing without a frontend
cli.run_app(server)
Loading