if we want a different transcription model than th...
# ask-ai
a
if we want a different transcription model than the gpt-realtime, is it possible to set the stt node for that?
✅ 1
t
Thanks for reaching out, Sebastián. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — regarding using a different transcription model than `gpt-realtime`: yes, you can. You can set the STT model in two ways: • Pass an
stt
parameter when creating the
AgentSession
(string descriptor like
"deepgram/nova-3:multi"
or a provider-specific class such as
inference.STT(model="deepgram/flux-general")
). • Override the agent’s
stt_node
method to implement a fully custom STT node. Supported providers include Deepgram, Cartesia, ElevenLabs, AssemblyAI, xAI, Sarvam, etc. Provider plugins require installing the plugin and configuring API keys (for example,
DEEPGRAM_API_KEY
,
ELEVEN_API_KEY
) in your environment. If you want, tell me which provider/model you’d like to use and whether you prefer the
AgentSession
parameter approach or a custom
stt_node
, and I’ll give a short code example. Sources: Build Your First AI Voice Agent in Python: Complete Tutorial | LiveKit | ElevenLabs STT | LiveKit Documentation | Cartesia STT | LiveKit Documentation | AssemblyAI STT | LiveKit Documentation | Sarvam STT plugin guide | LiveKit Documentation, with interactive elements
a
i will try Soniox, btw do i have to disable Realtime model transcribe right?
t
Short answer: you don't need to separately "disable" gpt-realtime. Specifying a different STT for your agent overrides the default. To use Soniox: • Set the
stt
parameter on your
AgentSession
(string or provider class) to the Soniox model identifier, or implement a custom
stt_node
. • Install/configure any Soniox plugin and set your Soniox API key in your environment (e.g., in
.env
). If you want, tell me whether you'd like a brief code example for
AgentSession
vs. a custom
stt_node
, and I’ll provide it. , with interactive elements
a
help me please
t
Got it — I’ll walk you through a minimal example to use Soniox for STT and confirm you do NOT need to explicitly disable
gpt-realtime
(specifying a different STT overrides the default). 1) Set your Soniox API key in your environment (example .env):
Copy code
SONIOX_API_KEY=your_soniox_key_here
2) Install the Soniox plugin (if provided by LiveKit) or the Soniox client library per Soniox instructions. 3) Minimal AgentSession example (AgentSession
stt
set to Soniox):
Copy code
from livekit import AgentSession, inference

# Option A: string descriptor (if Soniox model identifier is supported)
agent = AgentSession(
    stt="soniox/your-model-id",
    # other params...
)

# Option B: provider class if available
agent = AgentSession(
    stt=inference.STT(model="soniox/your-model-id"),
    # other params...
)
4) Optional: custom stt_node (only if you need advanced control)
Copy code
class MyAgent(Agent):
    async def stt_node(self, audio_chunk):
        # call Soniox client directly using SONIOX_API_KEY
        # return transcription text
        ...
What I did not change: you do not need to disable
gpt-realtime
. Setting
stt
to Soniox tells the agent to use that provider instead. Do you want a full runnable example for your language (Python/Node), or help with the exact Soniox model identifier and plugin install commands? , with interactive elements
a
but where should i capture those transcript? we use conversation_item_added but i see some "user transcript" debug events with different values
t
Thanks — I’ll help you capture the final transcripts and explain the debug events. • Capture the final transcript from the
conversation_item_added
event. That event contains the committed message used in the conversation history and is the source of truth. • The
"user transcript"
debug events you’re seeing are intermediate/partial transcripts and can change; don’t rely on them for final text. • Filter for user messages and extract the text, e.g.:
Copy code
@session.on("conversation_item_added")
def on_item_added(event):
    if event.item.role != "user":
        return
    text = "".join(c for c in event.item.content if isinstance(c, str))
    if not text.strip():
        return
    print("Final transcript:", text)
• You do not need to disable `gpt-realtime`; setting
stt
to Soniox will override the default. Would you like a runnable Python example that shows Soniox setup + the event handler together? Sources: Agent session | LiveKit Documentation | How to Build a Background Observer for Voice AI Guardrails | LiveKit | How to Build a Background Observer for Voice AI Guardrails | LiveKit | Text and transcriptions | LiveKit Documentation | Soniox STT plugin guide | LiveKit Documentation, with interactive elements
a
we use text_content instead of joining content, why the change here?
t
Good question — thanks for pointing that out. •
text_content
is a convenience property that already returns the concatenated text for an item. It was added to simplify code so you don’t need to manually join the
content
array. • Both approaches produce the same final string; using
text_content
is just more readable and future‑proof. • Make sure your SDK version includes
text_content
— if you’re on an older version, keep using the
join
approach until you upgrade. • Example using
text_content
in the
conversation_item_added
handler:
Copy code
@session.on("conversation_item_added")
def on_item_added(event):
    if event.item.role != "user":
        return
    text = event.item.text_content or ""
    if not text.strip():
        return
    print("Final transcript:", text)
Would you like me to show the full Soniox + handler example for your codebase (Python or Node)? , with interactive elements
a
i see them having different values text_content was: "¿Y si pongo música?" but trasncript debug was: "{"user_transcript": " ¿Cómo se escucha? By the way, y si pongo música?", "language": "es"}"
t
Thanks — I can explain why those two values differ. •
text_content
on
conversation_item_added
is the finalized, committed utterance after voice activity detection (VAD) / segmentation. In your example it shows the single committed user turn: "¿Y si pongo música?" • The debug
"user_transcript"
is an incremental/streaming result from the STT node. It can include partial or previously received speech (or merged segments) while the engine is still listening, so it may contain extra words like "¿Cómo se escucha? ... By the way, y si pongo música?" Why this happens: • STT providers stream partial hypotheses as audio arrives. Those debug events surface those intermediate hypotheses. • The agent waits for VAD or finalization before creating a conversation item; only then does
conversation_item_added.text_content
contain the final committed text. What you can do: • Use
conversation_item_added.text_content
as the source of truth for final transcripts. • If you need earlier/near-real-time transcripts, listen to the STT debug/stream events but treat them as provisional. • You can tune VAD/finalization behavior or the STT provider settings (if supported) to change when finalization occurs. Do you want help: • showing how to capture both streaming partials and final items in code, or • adjusting VAD/finalization settings for Soniox?, with interactive elements
a
i don't get it, how do i know this isn't the openai transcription?
t
Good question — I’ll show how to verify which STT produced a transcript and how to log that source so you can be certain it’s Soniox (not
gpt-realtime
). 1) Confirm your AgentSession stt setting • Ensure your agent is created with Soniox, e.g.:
Copy code
session = AgentSession(
  stt=soniox.STT(params=soniox.STTOptions(model="stt-rt-v4")),
  ...
)
If
stt
is set to a Soniox plugin/class or Soniox model string, the agent will use Soniox. 2) Inspect STT debug events for provider/model info • Debug events often include metadata like provider/model. Log the raw debug payload to see the field that names the provider (e.g.,
"provider": "soniox"
or
"model": "stt-rt-v4"
). 3) Add logging in a custom
stt_node
(definitive) • If you need absolute proof, override
stt_node
to call Soniox directly (or wrap the plugin) and log provider/model with each transcript. Example:
Copy code
class MyAgent(Agent):
    async def stt_node(self, audio_chunk):
        # if using plugin, the plugin call will be here
        transcript, meta = await soniox_client.transcribe(audio_chunk)
        print("STT provider=soniox model=stt-rt-v4", "partial=" in meta)
        return transcript
• When you implement this, every transcript printed will include the provider/model you logged. 4) Correlate streaming debug -> final conversation item • Streaming debug (
user_transcript
) are provisional and may show different content. • The final committed text is in
conversation_item_added
. To confirm the final text came from Soniox, log both the debug event and the conversation item with timestamps or an utterance ID:
Copy code
# on debug event
print("DEBUG stt event:", debug_event)

# on final item
print("FINAL conversation_item_added:", event.item.text_content, "source_meta:", event.item.meta)
Check that the debug events you logged earlier reference Soniox/model and that the final item was created after those Soniox debug events. 5) Quick checklist to rule out gpt-realtime •
stt
in AgentSession is NOT
gpt-realtime
. • Debug event payloads or plugin logs indicate `soniox`/model. • Your custom
stt_node
or plugin wrapper logs show Soniox requests/responses. If you want, tell me: • whether you prefer a Python or Node example, and • whether you already have Soniox plugin logs enabled — I’ll provide a short runnable snippet that logs provider/model with each partial and final transcript., with interactive elements
a
i disabled transcription and now i don't have the conversation added event at all, what is going on?
t
Thanks — I’ll explain what’s happening and how to fix it. Short answer: when you disable transcription, the STT node stops producing transcripts, so no finalized user utterances are created and
conversation_item_added
won’t fire. That event is emitted only when the agent commits a user message (usually from the STT pipeline). What you can do • Re-enable STT (set
stt
to Soniox or another valid model) so the pipeline produces transcripts and
conversation_item_added
resumes. • OR if you intentionally want STT off but still need conversation items, add them yourself via the session API (manually create/commit conversation items). Examples 1) Re-enable Soniox STT (Python):
Copy code
session = AgentSession(
  stt=soniox.STT(params=soniox.STTOptions(model="stt-rt-v4")),
  ...
)
2) Manually add a conversation item if STT is disabled:
Copy code
# pseudo-code: create a conversation item and add to session
item = ConversationItem(role="user", content=["Transcribed text here"])
await session.add_conversation_item(item)
# this will trigger conversation_item_added
If you want, tell me which option you prefer (re-enable Soniox or add items manually) and whether you want a full runnable example in Python or Node. , with interactive elements
a
i meant. i disabled the audio transcript of realtime api but have stt for Soniox but i don't receive the conversation item added
t
Thanks — I’ll help you confirm why
conversation_item_added
stopped after disabling the realtime model transcription and how to ensure Soniox emits the final conversation items. Summary of the problem you described • You disabled the realtime model’s internal audio transcription but still expect Soniox STT to produce
conversation_item_added
. When the realtime model transcription is disabled, nothing will create conversation items unless Soniox’s STT output is properly wired to the session. What to check (quick checklist) • Confirm AgentSession
stt
is set to Soniox (not null). • Ensure the realtime model has transcription disabled:
input_audio_transcription=None
(or equivalent). • Verify Soniox plugin is initialized and receiving audio (API key present, plugin installed). • Confirm Soniox’s final transcripts are being forwarded/committed to the session (so they create conversation items). • Add logging on both the STT debug events and
conversation_item_added
with timestamps/IDs to correlate. Minimal Python example (Soniox + disabled realtime transcription)
Copy code
from livekit.plugins import soniox
from livekit import AgentSession, openai

session = AgentSession(
    stt=soniox.STT(params=soniox.STTOptions(model="stt-rt-v4")),
    llm=openai.realtime.RealtimeModel(input_audio_transcription=None),
    # ...other config
)

@session.on("stt_debug")  # if your SDK emits debug/partial events
def on_stt_debug(ev):
    print("STT DEBUG:", ev)  # inspect provider/model fields

@session.on("conversation_item_added")
def on_item_added(event):
    if event.item.role != "user":
        return
    print("FINAL transcript:", event.item.text_content)
Troubleshooting steps if you still don’t see
conversation_item_added
• Check Soniox logs: confirm it receives audio and returns final transcripts. • Make sure the Soniox STT instance isn’t failing silently (exceptions, auth errors). • Confirm the session actually wires Soniox transcripts into conversation commits (if you implemented a custom
stt_node
, ensure it calls the session API to add/commit items). • Temporarily re-enable realtime model transcription to verify events return — if they do, the issue is in the Soniox-to-session integration. If you want, tell me: • whether you’re using the Soniox plugin or a custom
stt_node
, and • whether you prefer a full runnable example for Python or Node — I’ll provide the exact code to log provider/model and force commits so you can confirm Soniox is the source. Sources: Realtime models overview | LiveKit Documentation | Soniox STT plugin guide | LiveKit Documentation | Speech-to-text (STT) models overview | LiveKit Documentation | Agent session | LiveKit Documentation | How to Build a Background Observer for Voice AI Guardrails | LiveKit, with interactive elements