Conversation
voice_agent_call.py builds the agent directly instead of going through AgentLauncher, so component warmup never runs. Smart Turn is the only turn detector in this pipeline (Telnyx STT emits no VAD signals), and its process_audio raises until the VAD and smart-turn ONNX models are loaded: ValueError: VAD model is not initialized, call warmup() first Every audio chunk hits that error, so turn boundaries are never detected. Callers can talk, but the agent never learns they stopped speaking and the conversation stalls after the opening greeting. Warm the turn detector explicitly before joining, mirroring what AgentLauncher._warmup_agent does.
📝 WalkthroughWalkthroughThe Telnyx voice agent imports Priority: ⬇️ Low Merge Risk: 🔵 Low · up to The example remains functionally usable, but the new shared cache should include its required type annotation before merging. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
A fresh WarmupCache per call reloads the VAD pool, smart-turn ONNX session, and Whisper feature extractor on every inbound call. Use one module-level cache, matching AgentLauncher which holds a single process-wide cache. Models load once and the per-class lock serializes cold-start downloads across concurrent calls.
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Advanced
Run ID: ef8f4adb-f08c-4951-903f-e140d12ef6be
📒 Files selected for processing (1)
plugins/telnyx/examples/voice_agent_call.py
Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.
| app = FastAPI() | ||
| app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=["*"]) | ||
| call_registry = telnyx.TelnyxCallRegistry() | ||
| warmup_cache = WarmupCache() |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add the required type annotation.
The Python guidelines require type annotations everywhere. Change this declaration to warmup_cache: WarmupCache = WarmupCache().
Source: Coding guidelines
|
Follow-up observation from live-testing this fix. The PR fixes the symptom at the example level, but the reproduction exposed something worth a maintainer decision. Before the fix, one call logged the same error 1,257 times while the call continued with broken turn-taking: The background loop catches Two questions this raises, both outside this PR's scope:
|
DaemonLoki
left a comment
There was a problem hiding this comment.
Nice find, and appreciate the before/after log counts!
And yeah, by calling agent.join() yourself, it never goes through the warmup agent phase. So the matching to what the launcher does was an important addition
One thought: warmup runs after the caller has already been answered, so on a cold start the Silero VAD, Smart Turn ONNX, and Whisper extractor downloads happen while someone is on the line. You COULD warm a throwaway smart_turn.TurnDetection() against the same cache at startup (or moving the warmup into prepare_call), which would hide that. But since this is just an example, it should also be fine as-is.
Agreed, Both points are valid, and I'll address them in a separate PR. |
Fix: warm up the turn detector in the all-Telnyx voice agent example
Problem
plugins/telnyx/examples/voice_agent_call.pybuilds the agent directly and callsagent.join(). It never goes throughAgentLauncher, so_warmup_agent()never runs and no component is warmed up.This example is the only one in the repo that needs a local turn detector.
inbound_call.pyusesllm=gemini.Realtime(), which does turn detection server-side, so it is unaffected.voice_agent_call.pyusestelnyx.STT(sample_rate=8000), and the comment above it notes why:SmartTurnDetectionis aWarmable. Untilwarmup()runs,process_audioraises on every chunk (smart_turn_detection.py:222):The turn detector is the one component standing in for the VAD signals Telnyx does not send, and it never starts. Turn boundaries are therefore never detected.
Impact
The caller can talk, and the agent transcribes turn boundaries as speech/silence, but it never learns the caller stopped speaking. The agent speaks its opening greeting and then does not reply again no matter what is said.
Reproduction
Before
Turn detector raises on every audio chunk. Counted from a single real inbound call:
The agent produced its opening line and one
Finalizing LLM turn, then stopped. Zero turn-boundary events for the caller:Reporter's description of the call: the agent spoke its greeting and then stayed silent no matter what was said.
After
Same example, same number, same pipeline, one line of warmup added:
Turn boundaries are detected now:
The agent opens with its greeting, the caller's speech is segmented, and barge-in interrupts the agent as expected.
Change
This mirrors what
AgentLauncher._warmup_agent()does for the turn detector (agent_launcher.py:449).Checks
Run against current
main(ba2fbf1d):ruff checkandruff format --checkpasspytest plugins/telnyx/tests/ -m "not integration": 67 passedNote, not in scope for this PR
A follow-up observation on transcripts, since the turn detector was masking it before: in one verification call after this fix, the turn boundaries fired but no transcript text arrived, so the agent did not learn the caller's words. A later call with identical configuration transcribed correctly and the agent responded, so this looks intermittent rather than systematic. I could not reproduce the silent call and am not filing it as a bug without a repro. For reference,
interim_resultsdefaults toFalse(stt.py:63) and is honored per engine rather than per endpoint (stt.py:14), but the default Telnyx engine does emit finals, which is what the later call showed.