Repository navigation
Fix ElevenLabs end_call detection giving up before the transcript is ready - #235
Conversation
| max_attempts = 5 | ||
| # ~2 minutes in total. At 5 attempts (~34s) most medical_hr conversations were still | ||
| # in-progress, so valid endings were scored invalid and rerun. | ||
| max_attempts = 14 |
There was a problem hiding this comment.
how did we select this? should it be even higher just to be safe? i think the issue last time was i didn't check how high this needed to be
There was a problem hiding this comment.
14 attempts (~2 min) was a round number, not a measurement, and it wasn't enough: on a later run, 2 of ~40 conversations were still in-progress after 2 min, and both turned out to be done with end_call when re-queried a few minutes later.
Replaced the attempt count with a time budget, END_CALL_CHECK_BUDGET_SECONDS = 300 (same backoff, capped at 10s). The API only reports the current status, not when it changed, so I can't measure the lag distribution directly; 5 min is a safety margin rather than a fitted value. The cost is that a slow conversation holds its worker a bit longer before it's released. Anything that still slips through is now labelled (see the other thread), so if 5 min turns out too short we'll see it.
Problem
conversation_valid_endscores a conversation valid only if the ElevenLabs caller hung up withend_call._check_end_call_via_apifinds that out after the call by fetching the conversation from the ElevenLabs API, but it gave up after 5 polls (~34 s). ElevenLabs is often still processing at that point and returns an empty transcript, so a conversation that ended properly was scored invalid and rerun.Measured on a
medical_hrrun withgpt-realtime-2.1-mini(18 trials):donewithend_callin the transcript; the other 2 had hit ElevenLabs's maximum duration and were rightly invalid.metadata.termination_reasonalready read "end_call tool was called." while the transcript was still empty.Besides wasting conversations, the reruns bias the kept sample toward conversations ElevenLabs processes quickly.
Fix
metadata.termination_reasoncontainingend_call. It is set before the transcript is populated.done. A populated transcript alone no longer ends polling.END_CALL_CHECK_BUDGET_SECONDS) instead of 5 attempts, same backoff capped at 10 s. 2 min still missed about 1 in 20 conversations.end_call_unverifiedinstead ofsession_ended, visible inresult.jsonanduser_simulator_events.jsonl. A specific reason such asinactivity_timeoutis kept.After the fix, 13 of 13 conversations in a fresh run of the same setup ended with
reason=goodbyeand none were rerun.Tests
New tests:
termination_reasoncounts before the transcript arrives; polling continues past a minute untildone; a partial transcript while processing is not read as "no end_call"; the end-reason mapping forend_call_unverified. The two tests pinning the old attempt count and delays are updated.pytest tests/unit/user_simulatorpasses.