Skip to content

[BENCH-623] TTS TTFA breakdown: persist the roundtrip / leading-silence split and visualize it on Latency Variation - #454

Merged
cooperr1 merged 4 commits into
mainfrom
cooper/bench-623-ttfa-breakdown-toggle-like-stt-wer-dataset-breakdown
Aug 5, 2026
Merged

[BENCH-623] TTS TTFA breakdown: persist the roundtrip / leading-silence split and visualize it on Latency Variation#454
cooperr1 merged 4 commits into
mainfrom
cooper/bench-623-ttfa-breakdown-toggle-like-stt-wer-dataset-breakdown

Conversation

@cooperr1

@cooperr1 cooperr1 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

What

TTFA = network roundtrip + leading silence. The runner already measured both parts per sample and threw the split away; only the combined total was persisted. This PR persists the split and visualizes it.

Runner — two new metric rows per successful TTS sample, TTFARoundtrip and TTFALeadingSilence, written only when both are measured so they always sum back to the TTFA row exactly. A transport-gated (HTTP/1.1, cold connection) or arrival-only TTFA writes neither.

Web — the Latency Variation card gains a Distribution / Breakdown toggle on TTS. Breakdown renders every model as one stacked bar on the shared QualityMetricBars chassis (same frozen-axis horizontal scroll as the accuracy chart, so ~30 models read fine): the bar wears the model's palette color, texture carries the split — solid = roundtrip, hatched = leading silence — ranked fastest first with totals labeled. The headline flips to the field's leading-silence share; tooltips give each part's ms and percent.

Data integrity

  • Every displayed number is a served window-aggregate average (model_stats); the runner writes the component pair over the same samples, so avg roundtrip + avg silence equals the split runs' average TTFA. Nothing is derived, scaled, or estimated.
  • The view is labeled "Average TTFA" throughout so it never reads against the Distribution view's medians.
  • The toggle is data-gated: it appears only once component rows exist in the served window, so pre-deploy dashboards render exactly as today.

No schema, no API, no backfill

metric_type is a plain string dimension, so the new rows flow through the existing tables, matviews, and aggregates API untouched — no migration exists in this PR and db migrate is a no-op for it. No backfill: the feature is forward-only; the 24h window fills the day the runner deploys, 7d/30d as they roll over (until then, 7d/30d breakdown bars average only post-deploy runs — real values, newer coverage).

One payload guard: prod's 30d TTS aggregates response is already 16.9 MB / 66k series rows, and TTS buckets land ~48×/day. Since the breakdown reads only window aggregates, the component metrics are excluded from the per-bucket series rollup (SERIES_EXCLUDED_METRICS, tested) — every aggregates response stays byte-identical to today.

Rollout

Merge → deploy runner + web in either order (web-first hides the toggle; runner-first accumulates rows invisibly) → the next scheduled run makes the breakdown appear on the 24h view.

Testing

  • Runner: 1271 passed (orchestrator component-row emission + transport-gate suppression, finalize split propagation, series-rollup exclusion on a real Postgres, metric registry contracts); ruff format + check clean.
  • Web: 110 passed incl. tooltip reconciliation; tsc + eslint clean.
  • Verified end to end against a local Postgres + API seeded with all 29 active registry TTS models: bars/labels/tooltips reconcile, horizontal scroll matches the accuracy chart, mobile keeps 44px+ targets with tap-to-inspect tooltips. Verified against the prod API that pre-deploy dashboards render unchanged with the toggle hidden.

Greptile Summary

This PR persists the network-roundtrip and leading-silence components of successful, comparable TTS TTFA measurements and adds a data-gated stacked breakdown view to the Latency Variation card.

  • Registers and writes the two new component metrics atomically with TTFA.
  • Keeps component metrics in window aggregates while excluding them from per-bucket series payloads.
  • Adds shared stacked-bar support, breakdown tooltips, labels, export data, and mobile interaction behavior.

Confidence Score: 5/5

The PR appears safe to merge; no concrete blocking or independently actionable non-blocking issue was identified.

The component rows are emitted and persisted as an atomic pair, flow through the unrestricted window-statistics path, remain intentionally absent from series buckets, and are rendered only when both aggregates are available.

Reviews (1): Last reviewed commit: "[BENCH-623] Keep the TTFA split out of t..." | Re-trigger Greptile

Context used (3)

TTFA = network roundtrip + leading silence, and the runner already
computed both parts per sample before discarding the split. Persist the
components as two new metric rows (TTFARoundtrip, TTFALeadingSilence),
written only when both are known so they always sum back to the TTFA
row — a transport-gated or arrival-only TTFA writes neither. Because
metric_type is a plain string dimension everywhere downstream, the
matviews, series rollups and aggregates API carry the new metrics with
no schema or migration changes; every run's real split lands in the
pipeline from the next deploy onward.

Runner-only on purpose: the dashboard reads none of this yet. The
visualization lands separately once the split has accumulated, so it
can be built against real served data.

Full runner suite 1249 passed; ruff format and check clean.
The Latency Variation card gains a Distribution / Breakdown toggle on
TTS once the split metrics are served. Breakdown reuses the shared
QualityMetricBars chassis (the accuracy chart's): every model as one
stacked bar in its own palette color — the same color the Filters
sidebar and every other chart use — with texture carrying the split:
solid is the network roundtrip, hatched the leading silence. Ranked
fastest first with the same frozen-axis horizontal scroll, so it reads
identically at three models or thirty. The headline flips to the
field's leading-silence share; tooltips give each part's ms and share.

All values are window-aggregate averages served by the API: the runner
writes the component pair over the same samples, so avg roundtrip +
avg silence equals the split runs' average TTFA exactly — labeled
"Average TTFA" throughout so the breakdown never reads against the
distribution view's medians. Nothing is derived or estimated, and the
per-run timeline stays untouched from main.

The chassis grows optional stackSegments (fills resolvable per model),
svgDefs for the hatch patterns, and tickFormatter; stacked rows must
not carry a fill key (recharts prefers row fill over segment fill),
and stacked mode keeps mobile tap-to-inspect tooltips since those bars
have no click-to-compare action.

Verified against a local API seeded with all 29 active registry TTS
models: bars, labels and tooltips reconcile, the scroll matches the
accuracy chart, and mobile keeps 44px+ targets. Web 108 passed,
typecheck and lint clean.
The component metrics are consumed as window aggregates only (the
Latency Variation breakdown reads model_stats); nothing reads them per
bucket. TTS runs ~48x a day, so letting them flow into results_by_bucket
would double the 30d aggregates response — 66k series rows / 16.9 MB on
prod today — for rows no chart uses. Exclude them in refresh_bucket via
SERIES_EXCLUDED_METRICS, keeping every aggregates payload byte-identical
to today; the raw result rows still feed the stats matviews. Remove a
metric from the set if a per-run surface ever ships for it.
@cooperr1
cooperr1 requested a review from a team as a code owner August 5, 2026 20:53
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
benchmarks Ready Ready Preview Aug 5, 2026 9:03pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@cooperr1, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b594ffa8-8dad-4a60-88ae-c2325a8be474

📥 Commits

Reviewing files that changed from the base of the PR and between fb94d6b and 95247ea.

📒 Files selected for processing (4)
  • runner/src/coval_bench/runner/orchestrator.py
  • runner/tests/unit/test_orchestrator.py
  • web/components/charts/tooltips/BarTooltip.test.tsx
  • web/components/charts/tooltips/BarTooltip.tsx

Walkthrough

The backend adds TTFA roundtrip and leading-silence metrics, emits component rows when measurements are available, and excludes them from series rollups. The web layer prepares breakdown data and exposes it through dashboard state. The dashboard renders stacked TTFA bars, patterned silence segments, labels, legends, and tooltips. Tests cover metric registration, TTS measurement handling, orchestration, rollup filtering, chart data, and tooltip output.

Suggested reviewers: borgesius

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 54.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the PR's main change: persisting and visualizing the TTS TTFA roundtrip and leading-silence split.
Description check ✅ Passed The description directly explains the runner persistence, web visualization, data handling, rollout, and testing for the TTFA breakdown.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@runner/src/coval_bench/runner/orchestrator.py`:
- Around line 781-801: Update the component-row condition in the TTFA handling
flow around `ttfa_status` and `leading_silence_ms` to require `ttfa_status is
ResultStatus.SUCCESS` before appending `TTFA_ROUNDTRIP` and
`TTFA_LEADING_SILENCE` results. Add a regression test covering populated
`ttfa_ms` and `leading_silence_ms` with a provider-reported error, asserting
that no component rows are emitted.

In `@web/components/charts/tooltips/BarTooltip.tsx`:
- Around line 119-129: Update the percentage calculation in the tooltip
rendering within the parts map of BarTooltip so a zero row.ttfa produces 0% (or
omits the share) instead of dividing by zero; preserve the existing rounded
percentage behavior for nonzero totals and add a tooltip test covering a
zero-total TTFA.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c963af7a-9795-4494-837a-18c4d2a02134

📥 Commits

Reviewing files that changed from the base of the PR and between c3f7c76 and fb94d6b.

📒 Files selected for processing (18)
  • runner/src/coval_bench/db/writer.py
  • runner/src/coval_bench/providers/base.py
  • runner/src/coval_bench/providers/tts/_common.py
  • runner/src/coval_bench/registries/__init__.py
  • runner/src/coval_bench/registries/metrics.py
  • runner/src/coval_bench/runner/orchestrator.py
  • runner/tests/unit/test_db_writer.py
  • runner/tests/unit/test_metric_registry.py
  • runner/tests/unit/test_orchestrator.py
  • runner/tests/unit/test_tts_common.py
  • web/components/charts/QualityMetricBars.tsx
  • web/components/charts/tooltips/BarTooltip.test.tsx
  • web/components/charts/tooltips/BarTooltip.tsx
  • web/components/dashboard/BoxPlotSection.tsx
  • web/hooks/useChartData.ts
  • web/hooks/useDashboardState.tsx
  • web/lib/config/metrics.ts
  • web/types/benchmark.types.ts

Comment thread runner/src/coval_bench/runner/orchestrator.py
Comment thread web/components/charts/tooltips/BarTooltip.tsx

@coval-cale coval-cale left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YEWWWWW

…d zero-total shares

A provider error can arrive after audio has streamed, leaving ttfa_ms
and the silence offset populated on a FAILED row — the component gate
now requires the TTFA row's SUCCESS status, so failed samples never
leak into the breakdown aggregates (regression assertion added to the
provider-error test). The breakdown tooltip also skips the percent
share when the total is zero instead of rendering NaN%.
@cooperr1
cooperr1 added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 0799052 Aug 5, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants