How can i dynamically set the stt / tts model para...
# ask-ai
h
How can i dynamically set the stt / tts model params like language, voice, etc based on the caller number for an inbound call? When a call comes in, i'll have to do an http request to an external api to get the context and in that context, we'll have values like language, voice, etc and I need to set that before the call initiates.
t
Thanks for reaching out, Roman. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
You're asking how to fetch caller-specific context (language, voice, models) from an external API and apply those values to STT/TTS before an inbound call session starts. Here’s a concise, actionable approach using LiveKit agent sessions. High-level flow 1. Wait for the SIP participant to join the room and read their phone number (
participant.attributes['sip.phoneNumber']
). 2. Call your external API with that phone number to get context (language, voice, stt/tts model names). 3. Construct the AgentSession with
stt
and
tts
parameters set from that context. 4. Start the session so the configured STT/TTS are used for the call. Minimal Node.js example
Copy code
await ctx.connect(undefined, AutoSubscribe.AUDIO_ONLY);
const participant = await ctx.waitForParticipant();

if (participant.kind === ParticipantKind.SIP) {
  const phone = participant.attributes['sip.phoneNumber'];
  const resp = await fetch(`<https://your-api.com/context?number=${encodeURIComponent(phone)}`>);
  const ctxData = await resp.json();

  const sttModel = ctxData.stt_model || 'deepgram/nova-2-general';
  const ttsModel = ctxData.tts_model || 'cartesia/sonic-3';
  const ttsVoice = ctxData.voice || 'default-voice';
  const language = ctxData.language || 'en';

  const session = new voice.AgentSession({
    stt: new inference.STT({ model: sttModel, language }),
    tts: new inference.TTS({ model: ttsModel, voice: ttsVoice, language }),
    // other session options...
  });

  await session.start(ctx.room);
}
Minimal Python example
Copy code
await ctx.connect(auto_subscribe=rtc.AutoSubscribe.AUDIO_ONLY)
participant = await ctx.wait_for_participant()

if participant.kind == rtc.ParticipantKind.PARTICIPANT_KIND_SIP:
  phone = participant.attributes.get('sip.phoneNumber')
  resp = await fetch_caller_context(phone)  # your HTTP call
  stt_model = resp.get('stt_model', 'deepgram/nova-2-general')
  tts_model = resp.get('tts_model', 'cartesia/sonic-3')
  voice = resp.get('voice', 'default-voice')
  lang = resp.get('language', 'en')

  session = AgentSession(
    stt=stt.STT(model=stt_model, language=lang),
    tts=tts.TTS(model=tts_model, voice=voice, language=lang),
  )
  await session.start(ctx.room)
Notes and tips • Use
participant.attributes['sip.phoneNumber']
to get the caller number. • Make the external API call before constructing/starting AgentSession so STT/TTS are set ahead of audio processing. • Provide sensible defaults if the external API lacks fields. • If you need per-turn changes (not only at start), you can stop/start or reconfigure the session at runtime—tell me if you want examples for hot-swapping models/voices mid-call. Would you like a tailored example for your language/runtime or help handling errors/timeouts from the external API? Sources: SIP participant | LiveKit docs | Models | LiveKit Docs
h
so this would be inside the 'on_participant_connected_handler'?
t
Yes — do this when the participant connects. Putting the logic in your
on_participant_connected_handler
(or the equivalent place where you first observe the SIP participant) is the right spot. What to do there: • Read
participant.attributes['sip.phoneNumber']
. • Call your external API to fetch language/voice/model values. • Construct the
AgentSession
(or configure `stt`/`tts`) using those values. • Start the session. If you use the agent entrypoint pattern instead, the same flow can live after
ctx.wait_for_participant()
before
session.start(ctx.room)
— both approaches are equivalent; pick the one that fits your app architecture. Would you like a short snippet showing this inside your specific handler signature (Node or Python)?
h
show me a full agent example with your recommendations
t
Below are two full agent examples (Node.js and Python). Each shows: • listening for the SIP participant, • extracting
participant.attributes['sip.phoneNumber']
, • calling an external API for language/voice/model values, • constructing an AgentSession with dynamic
stt
and
tts
, • starting the session and generating an initial reply. Choose the one for your runtime. Node.js (TypeScript-style)
Copy code
import fetch from 'node-fetch';
import { defineAgent } from '@livekit/agents';
import { ParticipantKind, TrackKind } from '@livekit/rtc-node';
import { voice, inference } from '@livekit/agents/voice'; // adjust imports per SDK

export default defineAgent({
  entry: async (ctx) => {
    // Connect to the room; auto-subscribe audio only
    await ctx.connect(undefined, { autoSubscribe: false });
    await ctx.waitForParticipant(); // ensure a participant will join if needed

    // Wait for the SIP participant (this returns the first participant)
    const participant = await ctx.waitForParticipant();
    if (participant.kind !== ParticipantKind.SIP) return;

    const phone = participant.attributes['sip.phoneNumber'];
    if (!phone) throw new Error('No SIP phone number found');

    // Fetch caller context from your external API
    const resp = await fetch(`<https://your-api.com/context?number=${encodeURIComponent(phone)}`>);
    const data = await resp.json();

    const sttModel = data.stt_model || 'deepgram/nova-2-general';
    const ttsModel = data.tts_model || 'cartesia/sonic-3';
    const voiceName = data.voice || 'default-voice';
    const language = data.language || 'en';

    // Construct AgentSession with dynamic STT/TTS
    const session = new voice.AgentSession({
      stt: new inference.STT({ model: sttModel, language }),
      tts: new inference.TTS({ model: ttsModel, voice: voiceName, language }),
      // add other options like VAD, turn detection if desired
    });

    // Start using the room from ctx
    await session.start(ctx.room);

    // Send an initial prompt (optional)
    await session.generateReply({ instructions: `Hello — I will use ${language} for speech.` });

    // Keep session alive and stream audio until participant disconnects
    const audioTrack = participant.tracks.get(TrackKind.AUDIO)?.track;
    if (audioTrack) {
      const audioStream = new ctx.rtc.AudioStream(audioTrack);
      for await (const _chunk of audioStream) { /* keep reading */ }
    }
  },
});
Python
Copy code
import aiohttp
import asyncio
from livekit import agents, rtc
from livekit.agents import JobContext, AgentSession
from livekit.agents import inference as inference_mod  # adjust per SDK layout

async def fetch_caller_context(phone_number: str) -> dict:
    async with aiohttp.ClientSession() as session:
        async with session.get(f'<https://your-api.com/context?number={phone_number}>') as resp:
            return await resp.json()

@agents.room.on('participant_connected')
async def on_participant_connected(room: rtc.Room, participant: rtc.Participant):
    # spawn a background task so handler returns quickly
    asyncio.create_task(handle_participant(room, participant))

async def handle_participant(room: rtc.Room, participant: rtc.Participant):
    if participant.kind != rtc.ParticipantKind.PARTICIPANT_KIND_SIP:
        return

    phone = participant.attributes.get('sip.phoneNumber')
    if not phone:
        return

    try:
        ctx_data = await fetch_caller_context(phone)
        stt_model = ctx_data.get('stt_model', 'deepgram/nova-2-general')
        tts_model = ctx_data.get('tts_model', 'cartesia/sonic-3')
        voice_name = ctx_data.get('voice', 'default-voice')
        language = ctx_data.get('language', 'en')

        # Build AgentSession with dynamic parameters
        session = AgentSession(
            stt=inference_mod.STT(model=stt_model, language=language),
            tts=inference_mod.TTS(model=tts_model, voice=voice_name, language=language),
            # add vad/turn_detection if needed
        )

        # Start session bound to the room
        await session.start(room)

        await session.generate_reply(instructions=f"Hello — I'll use {language} for speech.")

        # Subscribe to audio to keep session alive (simple loop)
        audio_track = participant.tracks.get(rtc.TrackKind.KIND_AUDIO).track
        async for _ in rtc.AudioStream(audio_track):
            pass

    except Exception as e:
        # handle/log error
        print('Error handling participant:', e)

async def entrypoint(ctx: JobContext):
    # If using event handlers above, entrypoint can be minimal
    pass

if __name__ == '__main__':
    agents.cli.run_app(agents.WorkerOptions(entrypoint_fnc=entrypoint, worker_type=agents.WorkerType.ROOM))
Notes • Put this logic where you first detect the SIP participant:
on_participant_connected
or right after
ctx.wait_for_participant()
. • Make the external HTTP call before creating/starting
AgentSession
so STT/TTS are configured before audio processing begins. • Provide defaults for missing fields from your API. • If you need mid-call reconfiguration (hot-swap models/voices), say so and I can show runtime reconfiguration patterns. Would you like this adapted to your exact SDK import layout or to include VAD/turn-detection examples? Sources: Company directory phone assistant | LiveKit docs | Job lifecycle | LiveKit docs | SIP participant | LiveKit docs | Creating SIP Inbound Trunks and Dispatch Rules with Python SDK