8000 feat(eval): show the breakdown behind a metric's score on hover by i-yliu · Pull Request #544 · google/adk-web · GitHub
[go: up one dir, main page]

Skip to content

feat(eval): show the breakdown behind a metric's score on hover - #544

Open
i-yliu wants to merge 1 commit into
google:mainfrom
i-yliu:eval-metric-breakdown-tooltip
Open

i-yliu wants to merge 1 commit into
google:mainfrom
i-yliu:eval-metric-breakdown-tooltip

Conversation

@i-yliu
@i-yliu i-yliu commented Oct 14, 2026 •
Copy link
Copy Markdown
Contributor

What

The eval result cards show one number per metric. Two of them are composites the backend already breaks down, and nothing surfaced it:

  • token_usage_v1 carries the per-type counts
  • invocation_duration_v1 carries the split between time awaiting a model, time inside tools, and the framework's own

Without it a reader sees 1651 or 7.37s with no way to ask where it went. The duration split in particular exists to answer "is the framework the cost", which a single number cannot.

This renders both in the hover card the metric block already has, under the existing actual/threshold grid.

Token breakdown                 Duration breakdown
  total          1651             total        7.366s
    input        1180               model       3.55s
      prompt     1180               tool        0.003s
        cached   n/a                framework   3.813s
      tool use   0
    output        471
      candidates  343
      reasoning   128

Rendered

From a live adk web eval run (3-turn case, gemini-2.5-flash). cached and tool use read n/a because that run's backend did not report them.

invocation_duration_v1

Duration breakdown in the metric hover card

token_usage_v1

Token breakdown in the metric hover card

Notes for review

Indentation means what it means in adk eval's own output, so the two surfaces read the same way. For tokens it is containment — cached is a portion of prompt, not an addition to it. For the duration the parts partition the turn and sit under the total only to show they add back up to it.

A figure the run never reported reads n/a, never 0. A backend may not publish a given token count, and a turn timed before the duration split existed carries a total and no parts; showing those as zero would claim the turn spent nothing there.

The rows are declared in the component, not the template — each breakdown is one data structure and the template is a single loop, so adding a field later is a one-line change and the shape is unit-testable without the DOM.

No API change: details.tokenUsageDetails and details.durationDetails are already on the wire.

Testing

npm run test — 831 passing, including 7 new specs for metricBreakdown:

  • null for a metric carrying no details
  • token rows, labels and depths, with an unreported count as n/a
  • duration rows, all three parts one level under the total
  • an unmeasured part as n/a rather than 0
  • trailing zeros trimmed off an averaged figure (3.0 → 3s, 2.123456 → 2.123s)
  • token split preferred when a metric somehow carries both

ng build --configuration=production passes.

Verified end to end against a running backend: ng serve proxied to adk web, eval run from the Dev UI, both hover cards checked against the values in the saved .evalset_result.json (screenshots above).

The eval result cards show one number per metric. Two of them are composites
that the backend already breaks down and nothing surfaced: `token_usage_v1`
carries the per-type counts, and `invocation_duration_v1` carries the split
between time awaiting a model, time inside tools, and the framework's own.
Without it, a reader sees "1651 tokens" or "7.37s" with no way to ask where
it went -- the duration split in particular exists to answer "is the
framework the cost", which a single number cannot.

Render both in the hover card the metric block already has, under the
existing actual/threshold grid. The rows and their depth are declared in the
component rather than the template, so each breakdown is one data structure
and the template is one loop.

Indentation means what it means in `adk eval`'s own output, so the two
surfaces read the same way: for tokens it is containment, `cached` being a
portion of `prompt` rather than an addition to it; for the duration the parts
partition the turn and sit under the total only to show they add back up to
it.

A figure the run never reported reads `n/a`, never `0`. A backend may not
publish a given token count, and a turn timed before the duration split
existed carries a total and no parts -- showing those as zero would claim the
turn spent nothing there.
@gemini-code-assist
Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

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.

1 participant

0