feat(telemetry): trace the end-of-turn wait and the turn handoff - #7130
Open
davidzhao wants to merge 6 commits into
Open
feat(telemetry): trace the end-of-turn wait and the turn handoff#7130davidzhao wants to merge 6 commits into
davidzhao wants to merge 6 commits into
Conversation
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
from
September 5, 2026 20:07
71e137c to
f52117e
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
2 times, most recently
from
September 5, 2026 21:00
e5d040a to
dfdc981
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
2 times, most recently
from
September 5, 2026 21:10
0126c90 to
c248a86
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
3 times, most recently
from
September 6, 2026 01:09
bae9049 to
3fc7868
Compare
davidzhao
marked this pull request as ready for review
September 6, 2026 01:40
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
from
September 6, 2026 05:23
f1b1d9f to
1348da7
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
6 times, most recently
from
September 6, 2026 08:09
ea80860 to
cc498be
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
2 times, most recently
from
September 6, 2026 16:48
45988c2 to
bfa49c1
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
from
September 6, 2026 17:13
bfa49c1 to
cf9e01f
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
from
September 6, 2026 21:10
cf9e01f to
05c7265
Compare
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
from
September 6, 2026 22:13
05c7265 to
f337d61
Compare
`eou_detection` only covered the turn-detector inference, so the endpointing delay itself (often seconds) showed up as an empty gap between the user turn and the agent turn. - New `eot_wait` span, one per user turn, child of `user_turn`. It is back-dated to `last_speaking_time` so the bar starts where the user stopped talking, and ends on the turn decision with `lk.eou.outcome` = committed | user_resumed | dropped, `lk.eou.wait_duration`, `lk.eou.rearm_count`, the final `lk.eou.endpointing_delay`, and the last `lk.eou.source`. A later trigger for the same turn (late STT final, another VAD end of speech) re-arms the wait and records a `rearmed` event instead of starting a new span, so the bar stays whole; a rejected commit (min_words, realtime backchannel) records `not_committed` and keeps waiting. Resumed speech ends it at the VAD/STT speech start; teardown drops it. - `eou_detection` is renamed `eot_detection` and nests under `eot_wait`. The `lk.eou.*` attribute keys are unchanged. - New `on_user_turn_completed` span around the user hook that gates the reply, parented to the session root; exceptions are recorded (redaction-aware) and StopResponse is an event. - `agent_turn` gains `lk.speech.queue_wait`: seconds between scheduling the speech and its first generation authorization, recorded at all four authorization sites. Tests: tests/test_eot_wait_span.py drives `_run_eou_detection` with real spans (commit, rearm, user resumed, dropped, not committed, detection nesting) and a full fake session for the hook span and queue wait. Existing AudioRecognition test harnesses gain the new state fields. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
tests/test_realtime_reply_chat_ctx.py drives _realtime_reply_task with a SimpleNamespace standing in for the activity; a method call on self there raised AttributeError inside the task and the test then waited forever for a reply future, hanging the unit gate. Reply tasks must not depend on telemetry helpers living on the activity, so the helper is a module-level function. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lk.eou.wait_duration and the span length came from the same float seconds but were truncated to nanoseconds separately, so they could differ by a nanosecond. Keep the start in nanoseconds and compute the attribute from the same two integers the span is bounded by; the test asserts exact equality. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n is eou_wait A partial move to "eot" left a span named eot_detection carrying lk.eou.* attributes. Renaming the keys too would need a cloud-side migration that is not worth it right now, so both spans stay on the vocabulary the attribute keys already use. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…hanges, honour session redaction Review follow-ups on #7130: - a resumed-speech end time reported after the fact could land before an eou_detection that was still running or a re-arm recorded meanwhile; the wait now ends no earlier than what it contains, closes a running detection with a superseded event, and keeps the real resume time as a user_resumed event. - switching turn detection to or from manual cancelled the endpointing task but left the wait open; it is dropped with the decision. - on_user_turn_completed exceptions are redacted when redaction is enabled for the session alone, not only via the job flag. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ts under it The hook gates the reply, so the turn is not over until it has run. Recognition hands the open user_turn span to the activity when it schedules the hook; the activity parents the hook span to it and ends it after the hook (or when the turn is skipped), just before any reply is generated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
davidzhao
force-pushed
the
dz/telemetry-eot-wait
branch
from
September 6, 2026 22:18
f337d61 to
23f1cf4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
eou_detectiononly covered the turn-detector inference, so the endpointing delay itself (often seconds) showed up as an empty gap betweenuser_turnandagent_turn. This makes the wait, the user hook, and the speech queue visible.How
eou_waitspan, one per user turn, child ofuser_turn:last_speaking_time, so the bar starts where the user stopped talking;lk.eou.outcome(committed/user_resumed/dropped),lk.eou.wait_duration,lk.eou.rearm_count, the finallk.eou.endpointing_delay, and the lastlk.eou.source;rearmedevent instead of starting a new span, so the bar stays whole and late transcripts are readable off it;min_words, realtime backchannel) records anot_committedevent and keeps waiting; resumed speech ends the span at the VAD/STT speech start; teardown drops it.user_resumedevent; when that start is reported after the fact and a detection or re-arm was recorded in between, the bar runs to the last of those so it always contains its children (aneou_detectionstill running is closed with asupersededevent). A turn-detection mode change to or frommanualdrops the pending wait.eou_detectionkeeps its name and now nests undereou_wait; the span names and thelk.eou.*attribute keys stay on the same vocabulary, so nothing on the cloud side needs to change.on_user_turn_completedspan around the user hook that gates the reply, nested under theuser_turnit completes: recognition hands the openuser_turnspan to the activity when it schedules the hook, and the activity ends it after the hook (or when the turn is skipped), so the turn's duration covers the wait for the hook. Exceptions are recorded redaction-aware, honouringrecord={"redaction": True}on the session as well as the job's flag;StopResponseis an event.lk.speech.queue_waitonagent_turn: seconds between scheduling the speech and its first generation authorization.Tests
tests/test_eou_wait_span.pydrives_run_eou_detectionwith real spans: commit (parent, back-dated start, attributes), re-arm keeps one span, resumed speech ends at speech start and leavesuser_turnopen, teardown drops,not_committedkeeps waiting,eou_detectionnests under the wait and stays inside it when the user resumes mid-inference, a mode change drops the wait; plus a full fake session assertingeou_wait, the hook span, the queue-wait attribute, and a session-only redaction keeping a hook exception's message out of the trace.Stacked on #7128.
🤖 Generated with Claude Code