System log events
Besides the lines your agent writes, VoiceThere writes its own lines into the session's agent logs, so you can see why a final was held, when a language switch started and finished, and which voice settings changed. Each of these lines has fields.source = "voicethere" and a dotted name in fields.event.
These lines do not count toward your plan's agent-log overage. They never contain transcripts or credentials. The only text they carry is your own configured wait message, on language_switch.wait_message.
Finding them in the dashboard
Open Project → Agent logsor a session's logs. Every line has a VoiceThere or Agent badge. The Source filter shows All, Agent or VoiceThere lines, and System event narrows VoiceThere lines to language_id.*, language_switch.* or voice.*. The logs API takes the same filters as source (all, agent, voicethere) and eventGroup (language_id, language_switch, voice) query parameters.
Language events
| fields.event | Level | Fields | When |
|---|---|---|---|
language_id.decision | info | detected, current, outcome (same_language | switch | lid_no_switch | confirm_pending), detail?, utteranceId? | Language ID reached a decision for an utterance. confirm_pending means the switch waits for another utterance (autoSwitch.confirmUtterances). |
language_id.skipped | info | reason (too_short | deferred_tts | no_audio | undetermined), speechMs?, utteranceId? | Language ID did not run or did not decide. The final went to the agent in the current language. |
language_switch.final_held | info | reason (language_check | language_switch), holdMs | A final is held until language ID decides or a switch finishes. holdMs is the most it will wait. |
language_switch.final_released | info | reason (same_language | lid_no_switch | lid_timeout | lid_skipped | replaced_by_replay | no_replay | switch_failed | switch_timeout), heldMs | The hold ended after heldMs. |
language_switch.started | info | from, to, trigger (lid | agent), pools (warm | cold) | A switch started, automatic (lid) or from setVoiceLanguage (agent). |
language_switch.wait_message | info | mode, language, played, text?, reason?, skipReason?, repeat? | The runner played the holding message in the language being left, or skipped it. text is the wait text that was spoken. repeat is true for the second holding message, played after the first attempt timed out. reason immediate_unavailable means an immediate message was moved to the end of the utterance. |
language_switch.pools_ready | info | stt, tts (pool ids), coldStartMs | The target STT and TTS models are running. coldStartMs is how long they took to start. |
language_switch.stt_hold | info | holdMode, holdOutcome, bufferedMs, droppedMs, reason? | How inbound speech during the wait was handled. holdOutcome not_held with reason final_already_seen means the decision came after the final, so nothing could be buffered. |
language_switch.replay | info | ok, ageMs?, reason? | The held speech was replayed into the new STT (ok: false with a reason if it could not be). |
language_switch.committed | info | from, to, durationMs, replayed | The session now runs in the new language. |
language_switch.decision | info | decision (switch | same | no_switch | dwell | confirm_pending), from, to, confirmCount?, confirmNeeded? | The runner decided what to do after language identification. No transcript text is logged. |
language_switch.pools | info | to, warm, poolsMs? | The target models were already running (warm: true) or had to be started. |
language_switch.swap | info | from, to, scope (both | stt), durationMs | The new STT (and TTS when scope is both) settings were applied. |
language_switch.ready_message | info | language, played, reason?, durationMs | The ready message was played in the new voice, or skipped. The text is not logged. |
language_switch.prepare | info | to, ready, reason?, durationMs, trigger (agent) | Your agent called prepareVoiceLanguage. ready says whether the target models are running. |
language_switch.auto_unavailable | info | reason (requires_voicethere_speech), stt, tts | Automatic switching is stored as on but STT or TTS is not VoiceThere, so it stays off. Logged once per session. |
language_switch.retry | warn | from, to, attempt, reason (pool_timeout | pool_unavailable), trigger (lid | agent) | The target models were not running after 20 seconds, so the runner asked for them again once. trigger is lid for an automatic switch and agent for a setVoiceLanguage call from your agent. No extra speech event is sent for the retry. |
language_switch.failed | warn | from, to, reason, detail, fallbackLanguage, attempts, trigger (lid | agent) | The switch did not complete. The session stays in fallbackLanguage. trigger is lid or agent. reason is language_unavailable when both attempts failed (detail holds pool_timeout or pool_unavailable, attempts is 2); otherwise it is the error code and attempts is 1. |
language_switch.unavailable_message | info | played, reason? (disabled | no_text | no_host | speak_failed), fallbackLanguage | After a failed switch, the built-in sentence saying the language is not available was played in the old voice, or skipped. Only used when the wait message is on; the text is not logged. |
Voice settings events
| fields.event | Level | Fields | When |
|---|---|---|---|
voice.settings_change_requested | info | scope, via (setVoiceLanguage | lid_auto_switch), from {stt?, tts?}, to {stt?, tts?} | A voice settings change was requested. scope is stt, tts or both; stt and tts each carry provider, model?, voice? and language?. provider is voicethere for the built-in speech. |
voice.settings_changed | info | scope, via, to, durationMs, unchanged? | The runner applied the change. unchanged means the requested settings were already active. Providers show voicethere for the built-in speech. |
voice.settings_change_failed | warn | scope?, via, reason, message | The change was rejected or could not be applied. The line reads Voice settings change failed: <reason>. <message>, where message says what to change. Reasons are listed in Switching language mid-conversation. |
Example: automatic switch to a cold language
The session runs in English with auto-switch on and languageId.timing at early. The caller speaks German and the German models are not running yet. Oldest line first, fields shortened.
10:02:09.820 info language_id.decision {"detected":"de","current":"en","outcome":"switch"}
10:02:09.822 info voice.settings_change_requested {"scope":"both","via":"lid_auto_switch","from":{"stt":{"provider":"voicethere","language":"en"}},"to":{"stt":{"provider":"voicethere","language":"de"}}}
10:02:09.823 info language_switch.started {"from":"en","to":"de","trigger":"lid","pools":"cold"}
10:02:11.204 info language_switch.final_held {"reason":"language_switch","holdMs":45000}
10:02:12.610 info language_switch.wait_message {"mode":"end_of_utterance","language":"en","played":true,"text":"One moment, switching to German."}
10:02:15.950 info language_switch.pools_ready {"stt":"…","tts":"…","coldStartMs":6127}
10:02:15.952 info language_switch.stt_hold {"holdMode":"buffer_replay","holdOutcome":"released_replay","bufferedMs":5700,"droppedMs":0}
10:02:15.953 info language_switch.replay {"ok":true,"ageMs":4749}
10:02:15.960 info voice.settings_changed {"scope":"both","via":"lid_auto_switch","durationMs":6138}
10:02:16.840 info language_switch.committed {"from":"en","to":"de","durationMs":7017,"replayed":true}
10:02:16.841 info language_switch.final_released {"reason":"replaced_by_replay","heldMs":5637}If language ID had decided only after the final (for example with end_of_utterance timing), the hold line would read {"holdOutcome":"not_held","reason":"final_already_seen"} and only the first utterance would be replayed. With waitMessage.mode set to immediate, the wait message starts while the caller is still speaking and their speech does not interrupt it. The line shows "mode":"immediate","reason":"immediate_unavailable" only when a session could not play it that way and moved it to the end of the utterance.
Example: manual switch from your agent
The agent calls setVoiceLanguage with de. The runner plays no wait message (the agent speaks its own), sends no voice_language_* client events, and logs the switch as a voice settings change. A cold start shows up as the gap between the two settings lines.
10:05:40.100 info voice.settings_change_requested {"scope":"both","via":"setVoiceLanguage","from":{"tts":{"provider":"voicethere","language":"en"}},"to":{"tts":{"provider":"voicethere","language":"de"}}}
10:05:40.102 info language_switch.started {"from":"en","to":"de","trigger":"agent","pools":"cold"}
10:05:45.031 info voice.settings_changed {"scope":"both","via":"setVoiceLanguage","durationMs":4931}Many of these lines mirror the client events described in Switching language mid-conversation. New fields may be added; existing ones keep their meaning.