Use ThunderPhone from LiveKit Agents
Run a ThunderPhone voice agent as the realtime (speech-to-speech) model of a LiveKit Agents session, with LiveKit rooms, SIP and telephony around it.
LiveKit Agents is an open-source framework for realtime voice agents on top of LiveKit rooms. ThunderPhone plugs in as the realtime model: the framework sends the participant's audio, ThunderPhone returns the agent's voice, transcripts and function calls, and LiveKit's room, SIP trunks and telephony carry the audio. Speech recognition, the language model, the voice, turn-taking, 47 languages and tools all run on ThunderPhone.
Calls made this way appear in call history and are billed at your product's per-minute rate like any other realtime call. There is no subscription and no ThunderPhone phone number involved.
Install
pip install livekit-plugins-thunderphone
export THUNDERPHONE_API_KEY=sk_live_... # a secret API keyThe plugin wraps LiveKit's OpenAI Realtime model, because ThunderPhone's
Realtime WebSocket speaks the same protocol. It
needs livekit-agents 1.8 or newer.
Run a saved agent
Everything the agent does (prompt, voice, engine, languages, tools, greeting, silence check-ins) is configured on ThunderPhone. The session only moves audio.
from livekit.agents import Agent, AgentSession, JobContext, WorkerOptions, cli
from livekit.plugins import thunderphone
async def entrypoint(ctx: JobContext):
session = AgentSession(llm=thunderphone.RealtimeModel(agent_id=12))
await session.start(agent=Agent(instructions=""), room=ctx.room)
if __name__ == "__main__":
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))A saved agent greets and runs its own tools on ThunderPhone; the LiveKit
Agent's instructions and tools are not sent. The call ends when the
ThunderPhone agent hangs up, and the session closes with it.
Configure the session inline
Without agent_id, instructions and tools come from the LiveKit Agent, the
same way they do for OpenAI. product picks the engine and voice picks the
ThunderPhone voice.
from livekit.agents import Agent, AgentSession, RunContext, function_tool
from livekit.plugins import thunderphone
class Receptionist(Agent):
def __init__(self):
super().__init__(instructions="You are Acme Dental's receptionist. Be brief.")
@function_tool
async def check_availability(self, context: RunContext, date: str) -> dict:
"""Free appointment slots on a date."""
return {"slots": await calendar.free_slots(date)}
async def entrypoint(ctx: JobContext):
session = AgentSession(
llm=thunderphone.RealtimeModel(product="bolt", voice="olivia", language="es"),
)
await session.start(agent=Receptionist(), room=ctx.room)
await session.generate_reply() # the agent speaks firstThunderPhone freezes instructions and tools when the call starts, so
update_instructions, update_tools and agent hand-offs that change them are
not supported mid-call. Inline sessions are client-steered: the agent speaks
when you call generate_reply() or when the participant finishes a turn.
Options
| Argument | Meaning |
|---|---|
api_key | Secret key (sk_live_...). Defaults to THUNDERPHONE_API_KEY. |
agent_id | Run a saved agent. Mutually exclusive with product and voice. |
product | Engine for inline sessions: spark, bolt or storm. |
voice | ThunderPhone voice name for inline sessions. |
language | Primary language hint for inline sessions, e.g. es. |
from_number, to_number | Numbers to record on the call when the room fronts a phone line. |
live_transcripts | Stream caller transcript fragments mid-utterance (billed extra). |
base_url | Endpoint override, default wss://api.thunderphone.com/v1/realtime. |
Events
ThunderPhone adds platform events on top of the realtime protocol. The session emits them:
@session.llm.session.on("thunderphone_call_ended") # or on the RealtimeSession you hold
def on_call_ended(event):
print("call ended:", event["reason"])The RealtimeSession exposes call_id once the session is live. Use it with
GET /v1/calls/{call_id} to fetch the recording,
transcript and grade after the call. Every other call.* event (transfer,
keypad, speech ignored) arrives as thunderphone_call_event.
Limits
- Turn detection is server-side and always on; framework-side turn detection
(
turn_detectionon theAgentSession) is ignored. - Instructions and tools cannot change once the call starts.
- Audio is 16-bit mono PCM at 24 kHz in both directions.
- Video frames are ignored.