Conversation
davidzhao
added this pull request to stack #2502
September 15, 2026 06:32
|
davidzhao
force-pushed
the
dz/telemetry-trace-schema
branch
2 times, most recently
from
September 16, 2026 04:28
0a979d8 to
473c53d
Compare
Port of livekit/agents#7148. The nesting of spans is an emergent property of many call sites; a refactor can move a span under the wrong parent while every existing test passes, because each asserts one edge. agents/src/telemetry/testing/trace_schema.ts writes the rules down once: - SPAN_PARENTS: for every span the JS framework emits, the parents it may have (ROOT for none, ANY for spans that follow their caller). Unknown names are violations, so a new span must be registered. - MAY_OUTLIVE_PARENT: the child/parent edges where the child may end after its parent, each with its reason. Everything else must sit inside its parent, with 2 ms of slack. - checkTrace(): one trace id, every parent present and allowed, bounds, one agent_turn per lk.speech_id, lk.generation_count equal to the speech's own generation events, every eou_wait with an outcome. - fromReadableSpans / fromOtlpJson: an in-memory exporter or an export downloaded from LiveKit Cloud, same rules. As a CLI (`pnpm exec tsx agents/src/telemetry/testing/trace_schema.ts x.json`) it prints the span summary and the violations, tolerating orphans in a partial export. assertTraceWellFormed() now ends the full-session tests: the tool call and plain reply in agent_turn_span, the barge-in and handoff in coverage_spans, the hook and redaction sessions in eou_wait_span, the lifecycle and SIP sessions in session_lifecycle_span. The module is test support: imported by tests only, not exported from the package, and free of framework imports so the CLI runs on the source file. Schema differences from Python, all from what the JS code emits: answering_machine_detection is JS's name for `amd`; JS has no llm_fallback_adapter / tts_fallback_adapter / tts_stream_adapter spans (its adapters emit the plain request spans, which nest under the attempt's *_request_run), no wait_for_video_track, no judge_evaluation; rpc_handler may be a root, since without a session the SDK dispatches on a context carrying no span. Test hardening: the eou_wait full-session helper decided the turn 20 ms after the fake STT final was due, so a loaded host could open a second user turn (the intermittent failure seen in full-suite runs); the endpointing delay now leaves a 170 ms margin. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
davidzhao
force-pushed
the
dz/telemetry-trace-schema
branch
from
September 16, 2026 04:34
473c53d to
70effd6
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.
Port of livekit/agents#7148. Stacked on #2500.
Description
The nesting of spans is an emergent property of many call sites. A refactor can move a span under the wrong parent while every existing test passes, because each test asserts the one edge it was written for. This layer writes the rules down once and checks every span in every full-session test against them, and applies the same rules to a real run's export.
agents/src/telemetry/testing/trace_schema.ts(test support, not exported from the package):SPAN_PARENTS: for every span name the JS framework emits, the parents it may have (ROOTfor none,ANYfor spans that follow their caller:rpc_call,event_loop_blocked). An unknown name is a violation, so a new span must be registered.MAY_OUTLIVE_PARENT: the child/parent edges where the child may end after its parent, each with its reason (the startup spanssession.start()does not wait for,on_enter, and stalls whose end is one heartbeat late). Everything else must sit inside its parent, with 2 ms of slack.checkTrace(): one trace id, every parent present, parent allowed by the schema, bounds, and the per-turn invariants: oneagent_turnperlk.speech_id,lk.generation_countequal to the number ofgenerationevents, everyeou_waitwith an outcome.fromReadableSpansfor an in-memory exporter,fromOtlpJsonfor an export downloaded from LiveKit Cloud. As a CLI,pnpm exec tsx agents/src/telemetry/testing/trace_schema.ts traces.jsonprints the span summary and the violations, tolerating orphans in a partial export.assertTraceWellFormed(exporter.getFinishedSpans())now ends the full-session tests of the earlier layers:agent_turn_span,coverage_spans,eou_wait_span,session_lifecycle_span.Schema differences from Python
All from what the JS code emits:
answering_machine_detectionis JS's name foramd; JS has nollm_fallback_adapter/tts_fallback_adapter/tts_stream_adapterspans (its adapters emit the plain request spans, which nest under the attempt's*_request_run), nowait_for_video_track, nojudge_evaluation;rpc_handlermay be a root, since without a session the SDK dispatches on a context carrying no span.MAY_OUTLIVE_PARENTis identical to Python's.Testing
telemetry/testing/trace_schema.test.ts(9 tests): schema self-consistency; a sound trace passes; a misparentedllm_request, aneou_detectionoutside its wait, an unknown span, a missing parent, a child outside its parent, a duplicated speech turn, a generation-count mismatch and a second trace id are each reported; the deliberate overruns pass; OTLP and in-memory sources agree; a full fake session is well-formed.eou_waitfull-session helper decided the turn 20 ms after the fake STT final was due, which under load opened a second user turn; the endpointing delay now leaves a 170 ms margin.agentssuite green.🤖 Generated with Claude Code