microscopic-dress-75199
09/17/2025, 3:02 PMtall-airline-16756
09/17/2025, 3:02 PMtall-airline-16756
09/17/2025, 3:03 PMmicroscopic-dress-75199
09/17/2025, 3:06 PM{
"dispatch_rule": {
"roomConfig": {
"agents": [
{
"agentName": "telephony-voice-assistant"
}
]
},
"rule": {
"dispatchRuleIndividual": {
"roomPrefix": "telephony-voice-assistant_"
}
}
}
}
services:
redis:
image: redis
command: redis-server --requirepass ""
volumes:
- redis_data:/data
ports:
- 6379:6379
livekit:
image: livekit/livekit-server
command: --dev --redis-host redis:6379 --bind 0.0.0.0
ports:
- "7880:7880"
- "7881:7881"
- "7882-7900:7882-7900/udp"
depends_on:
redis:
condition: service_started
sip:
image: livekit/sip
ports:
- "5060:5060/udp"
- "5060:5060/tcp"
- "10000-10100:10000-10100/udp"
environment:
SIP_CONFIG_BODY: |
api_key: 'devkey'
api_secret: 'secret'
ws_url: '<ws://livekit:7880>'
redis:
address: 'redis:6379'
sip_port: 5060
rtp_port: 10000-10100
use_external_ip: true
media_use_external_ip: true
logging:
level: debug
depends_on:
livekit:
condition: service_started
redis:
condition: service_started
volumes:
redis_data:tall-airline-16756
09/17/2025, 3:06 PM--network host so they can access the host network directly.
• Verify Port Configuration: Double-check that the necessary UDP ports are open and mapped correctly. For instance, RTP media usually uses ports 10000-20000, and SIP signaling generally uses port 5060.
• Check Configuration: Make sure your ws_url and redis.address are set to the host’s accessible IP. On Linux, this often looks like 172.17.0.1.
• Inspect Logs: Take a look at your Docker logs for any errors about port binding or ICE candidate gathering. If you notice missing public IPv4 UDP candidates, that’s a clue something might be off in the setup.
• Test Connectivity: You can use LiveKit’s Connection Test utility to help diagnose any WebRTC, TURN, or WebSocket connectivity hiccups.
If you’d like, I’m happy to help you set up a minimal agent to test inbound calls and audio. Just let me know which programming language or framework you prefer, and we can take it from there!
Sources: Diagnosing Connection Errors with Connection Test Utility | Deploy to a VM | LiveKit Docs | pkg/sip/server.go | Self-hosting the Ingress Service | LiveKit Docs | config-sample.yamlmicroscopic-dress-75199
09/17/2025, 3:14 PMtall-airline-16756
09/17/2025, 3:15 PMLIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET).
2. Create the Agent: You can use the LiveKit CLI to scaffold a voice agent like this:
lk app create --template voice-pipeline-agent-python
Next, you’ll want to modify the agent.py file to assign an agent_name for dispatching calls:
if __name__ == "__main__":
cli.run_app(
WorkerOptions(
entrypoint_fnc=entrypoint,
prewarm_fnc=prewarm,
agent_name="inbound-agent", # Required for telephony dispatch
),
)
Then, start your agent in development mode with:
python3 agent.py dev
1. Set Up Inbound SIP Trunk: You’ll want to create a trunk configuration and apply it either through the LiveKit CLI or programmatically using the LiveKit Python SDK.
2. Create Dispatch Rule: Define a dispatch rule to route incoming calls to your agent. This can also be done programmatically if you prefer.
3. Handle Inbound Calls: In your agent’s entry point, use session.start() and generate_reply() to answer calls and provide friendly voice responses like this:
@entrypoint
async def entrypoint(ctx: JobContext):
session = VoicePipelineAgent(ctx)
await session.start()
# Answer the call with a voice response
await session.generate_reply(
instructions="Greet the user and offer your assistance."
)
await session.say("How can I help you today?")
If you’re interested in more advanced audio handling, I recommend checking out the playing_audio.py example in the official repository—it’s a great resource.
Please don’t hesitate to reach out if you need any further help or specific examples. I’m here to support you every step of the way!
Sources: Agents telephony integration | LiveKit Docs | Creating SIP Inbound Trunks and Dispatch Rules with Python SDK | README.md | Accepting incoming calls | LiveKit Docs | Accepting incoming calls | LiveKit Docsmicroscopic-dress-75199
09/17/2025, 3:32 PMRoomIO -> AgentSession -> TranscriptSynchronizer -> RoomIO {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
2025-09-17 113121,636 - DEBUG livekit.agents - using transcript io: AgentSession -> TranscriptSynchronizer -> RoomIO {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
not authenticated
2025-09-17 113121,737 - DEBUG livekit.agents - start reading stream {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "participant": "sip_1200", "source": "SOURCE_MICROPHONE", "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
2025-09-17 113121,737 - ERROR livekit - livekit_ffi:serveraudio stream123livekit ffiserver:audio_stream - failed to initialize the audio filter. it will not be enabled for this session. {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
2025-09-17 113126,533 - INFO livekit.agents - STT metrics {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "audio_duration": 4.75, "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
2025-09-17 113131,583 - INFO livekit.agents - STT metrics {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "audio_duration": 5.05, "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
2025-09-17 113136,632 - INFO livekit.agents - STT metrics {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "audio_duration": 5.05, "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
2025-09-17 113141,682 - INFO livekit.agents - STT metrics {"room": "telephony-voice-assistant__1200_TVb36y8kcubh", "audio_duration": 5.05, "pid": 45056, "job_id": "AJ_DkdKU7Brx7fr"}
And it looks like the agent is streaming audio but i dont hear any audio on the call.tall-airline-16756
09/17/2025, 3:33 PMAudioOutput implementation is connected and actively forwarding audio frames.
3. Inspect Call State: Using the LiveKit CLI or SDK, confirm that the SIP participant’s call state changes to "active" before expecting audio. For instance, look at the sip.callStatus attribute.
4. Enable Noise and Echo Cancellation: If audio seems muffled or suppressed, turning on background noise and echo cancellation can really improve clarity.
5. Debug Audio Tracks: The LiveKit Chrome Developer Extension is a handy tool to check audio tracks being published and subscribed to in real time. This can help ensure the SIP participant’s audio track is active and playing correctly.
6. Check Logs for Warnings: Keep an eye out for warnings about silent audio frames or media timeouts in the server logs—they often give clues about audio pipeline issues.
7. Validate Network Configuration: Lastly, please verify that the necessary UDP ports for RTP media (like 10000-20000) are open and reachable.
If you try these steps and the problem is still there, just let me know! I’m here to help and can guide you through more detailed troubleshooting or review specific logs with you.
Sources: SIP troubleshooting guide | LiveKit Docs | Development Tools | livekit/livekit_composite | DeepWiki | livekit-agents/livekit/agents/voice/agent.py | agents/src/voice/agent_activity.ts | Make outbound calls | LiveKit Docsmicroscopic-dress-75199
09/17/2025, 3:59 PMimport logging
from dotenv import load_dotenv
from livekit.agents import (
NOT_GIVEN,
Agent,
AgentFalseInterruptionEvent,
AgentSession,
JobContext,
JobProcess,
MetricsCollectedEvent,
RoomInputOptions,
RunContext,
WorkerOptions,
cli,
metrics,
)
from livekit.agents.llm import function_tool
from livekit.plugins import cartesia, deepgram, noise_cancellation, openai, silero
from livekit.plugins.turn_detector.multilingual import MultilingualModel
logger = logging.getLogger("agent")
load_dotenv(".env.local")
class Assistant(Agent):
def __init__(self) -> None:
super().__init__(
instructions="""You are a helpful voice AI assistant.
You eagerly assist users with their questions by providing information from your extensive knowledge.
Your responses are concise, to the point, and without any complex formatting or punctuation including emojis, asterisks, or other symbols.
You are curious, friendly, and have a sense of humor.""",
)
# all functions annotated with @function_tool will be passed to the LLM when this
# agent is active
@function_tool
async def lookup_weather(self, context: RunContext, location: str):
"""Use this tool to look up current weather information in the given location.
If the location is not supported by the weather service, the tool will indicate this. You must tell the user the location's weather is unavailable.
Args:
location: The location to look up weather information for (e.g. city name)
"""
<http://logger.info|logger.info>(f"Looking up weather for {location}")
return "sunny with a temperature of 70 degrees."
def prewarm(proc: JobProcess):
proc.userdata["vad"] = silero.VAD.load()
async def entrypoint(ctx: JobContext):
# Logging setup
# Add any other context you want in all log entries here
ctx.log_context_fields = {
"room": ctx.room.name,
}
# Set up a voice AI pipeline using OpenAI, Cartesia, Deepgram, and the LiveKit turn detector
session = AgentSession(
# A Large Language Model (LLM) is your agent's brain, processing user input and generating a response
# See all providers at <https://docs.livekit.io/agents/integrations/llm/>
llm=openai.LLM.with_ollama(
model="qwen2.5:3b", base_url="<http://localhost:11434/v1>", temperature=0.3
),
# Speech-to-text (STT) is your agent's ears, turning the user's speech into text that the LLM can understand
# See all providers at <https://docs.livekit.io/agents/integrations/stt/>
stt=deepgram.STT(model="nova-3", language="multi"),
# Text-to-speech (TTS) is your agent's voice, turning the LLM's text into speech that the user can hear
# See all providers at <https://docs.livekit.io/agents/integrations/tts/>
tts=cartesia.TTS(voice="6f84f4b8-58a2-430c-8c79-688dad597532"),
# VAD and turn detection are used to determine when the user is speaking and when the agent should respond
# See more at <https://docs.livekit.io/agents/build/turns>
turn_detection=MultilingualModel(),
vad=ctx.proc.userdata["vad"],
# allow the LLM to generate a response while waiting for the end of turn
# See more at <https://docs.livekit.io/agents/build/audio/#preemptive-generation>
preemptive_generation=True,
)
# To use a realtime model instead of a voice pipeline, use the following session setup instead:
# session = AgentSession(
# # See all providers at <https://docs.livekit.io/agents/integrations/realtime/>
# llm=openai.realtime.RealtimeModel(voice="marin")
# )
# sometimes background noise could interrupt the agent session, these are considered false positive interruptions
# when it's detected, you may resume the agent's speech
@session.on("agent_false_interruption")
def _on_agent_false_interruption(ev: AgentFalseInterruptionEvent):
<http://logger.info|logger.info>("false positive interruption, resuming")
session.generate_reply(instructions=ev.extra_instructions or NOT_GIVEN)
# Metrics collection, to measure pipeline performance
# For more information, see <https://docs.livekit.io/agents/build/metrics/>
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():
summary = usage_collector.get_summary()
<http://logger.info|logger.info>(f"Usage: {summary}")
ctx.add_shutdown_callback(log_usage)
# # Add a virtual avatar to the session, if desired
# # For other providers, see <https://docs.livekit.io/agents/integrations/avatar/>
# avatar = hedra.AvatarSession(
# avatar_id="...", # See <https://docs.livekit.io/agents/integrations/avatar/hedra>
# )
# # Start the avatar and wait for it to join
# await avatar.start(session, room=ctx.room)
# Start the session, which initializes the voice pipeline and warms up the models
await session.start(
agent=Assistant(),
room=ctx.room,
room_input_options=RoomInputOptions(
# LiveKit Cloud enhanced noise cancellation
# - If self-hosting, omit this parameter
# - For telephony applications, use `BVCTelephony` for best results
noise_cancellation=noise_cancellation.BVC(),
),
)
# Join the room and connect to the user
await ctx.connect()
if __name__ == "__main__":
cli.run_app(
WorkerOptions(
entrypoint_fnc=entrypoint,
prewarm_fnc=prewarm,
agent_name="telephony-voice-assistant",
)
)microscopic-dress-75199
09/17/2025, 3:59 PMtall-airline-16756
09/17/2025, 4:00 PMNewParticipantTrack and WriteAudioTo(). This helps ensure the audio is properly shared.
3. Enable Echo and Noise Cancellation: Try applying echo cancellation and noise suppression settings for SIP audio, similar to what’s used in console mode. This can really improve the call quality.
4. Increase Audio Queue Size: If you notice audio frames are dropping, increasing the queue_size_ms in rtc.AudioSource to somewhere between 1000–2000ms might help smooth things out.
5. Verify Codec Compatibility: Check that your SIP trunk supports wideband codecs like G.722 for HD voice. If needed, enabling G.711u can help with compatibility.
6. Debug Audio Tracks: Using LiveKit’s Chrome Developer Extension to inspect audio tracks can be really useful. It lets you see if the tracks are active and properly rendered during calls.
7. Match Agent and Dispatch Rule Names: Lastly, ensure the agent_name matches exactly in both the agent’s configuration and the dispatch rule to avoid any mismatches.
If you’d like, I’m here to help you with any of these steps or answer any questions you have. Just let me know!
Sources: Getting Help | livekit/livekit_composite | DeepWiki | HD voice for SIP | LiveKit Docs | livekit-agents/livekit/agents/voice/chat_cli.py | test/lktest/livekit.go | SIP troubleshooting guide | LiveKit Docsmicroscopic-dress-75199
09/17/2025, 4:04 PMservices:
redis:
image: redis
command: redis-server --requirepass ""
volumes:
- redis_data:/data
ports:
- 6379:6379
livekit:
image: livekit/livekit-server
command: --dev --redis-host redis:6379 --bind 0.0.0.0
ports:
- "7880:7880"
- "7881:7881"
- "50000-60000:50000-60000/udp" # Correct LiveKit ICE/UDP range
depends_on:
redis:
condition: service_started
sip:
image: livekit/sip
ports:
- "5060:5060/udp"
- "5060:5060/tcp"
- "10000-20000:10000-20000/udp" # Correct SIP RTP range per docs
environment:
SIP_CONFIG_BODY: |
api_key: 'devkey'
api_secret: 'secret'
ws_url: '<ws://livekit:7880>'
redis:
address: 'redis:6379'
sip_port: 5060
rtp_port: 10000-20000 # Match LiveKit docs for SIP RTP
use_external_ip: true
media_use_external_ip: true
logging:
level: debug
depends_on:
livekit:
condition: service_started
redis:
condition: service_started
volumes:
redis_data:tall-airline-16756
09/17/2025, 4:04 PMservices:
redis:
image: redis
volumes:
- redis_data:/data
ports:
- 6379:6379
livekit:
image: livekit/livekit-server
command: --dev --redis-host redis:6379 --bind 0.0.0.0
ports:
- "7880:7880"
- "7881:7881"
- "50000-60000:50000-60000/udp" # ICE/UDP range for LiveKit
depends_on:
redis:
condition: service_started
sip:
image: livekit/sip
ports:
- "5060:5060/udp"
- "5060:5060/tcp"
- "10000-20000:10000-20000/udp" # RTP range for SIP
environment:
SIP_CONFIG_BODY: |
api_key: 'devkey'
api_secret: 'secret'
ws_url: '<ws://livekit:7880>'
redis:
address: 'redis:6379'
sip_port: 5060
rtp_port: 10000-20000
use_external_ip: true
media_use_external_ip: true
logging:
level: debug
depends_on:
livekit:
condition: service_started
redis:
condition: service_started
volumes:
redis_data:
Here are a few key points to keep in mind:
• Redis: This stores your session data and is accessible on port 6379.
• LiveKit: It’s set up with ICE/UDP ports (50000-60000) to handle WebRTC media smoothly.
• SIP: Handles SIP signaling on port 5060 and RTP media on ports 10000-20000.
• Environment Variables: Make sure the ws_url correctly points to your LiveKit server, and that Redis is properly linked.
When you’re ready, simply start the services by running:
docker compose up
If you have any questions or need a hand setting up SIP trunks or dispatch rules, please don’t hesitate to reach out. I’m here to help!
Sources: Self-hosted SIP server | LiveKit Docs | Accepting inbound calls | LiveKit Docs | test/integration/sip_test.go