Skip to content

feat(evaluator): add by-name aggregate lookup to AgentEvalSummary - #1198

Merged
SandyChapman merged 3 commits into
mainfrom
aalgo-445-score-accessor-v2/schapman
Aug 10, 2026
Merged

feat(evaluator): add by-name aggregate lookup to AgentEvalSummary#1198
SandyChapman merged 3 commits into
mainfrom
aalgo-445-score-accessor-v2/schapman

Conversation

@SandyChapman

@SandyChapman SandyChapman commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Reading a single aggregate from an eval run required a hand-written scan over a doubly-nested attribute (next(s for s in result.summary.scores.scores if s.name == "...")). This adds score(name) and a scores_by_name mapping view so the same read is one call, and converts the seven sites that were scanning by hand.

Linear: AALGO-445

Changes

  • AggregatedMetricResult.score(name) — returns the aggregate, raises KeyError on a miss. Placed here rather than only on the agent-eval summary so the dataset path (EvaluationResult.aggregate_scores) gains the same surface.
  • AggregatedMetricResult.scores_by_name — mapping view for in, .get(), and iteration, for when a score's absence is a legitimate outcome. First-wins on a duplicate name, matching the next(...) scans it replaces.
  • One-hop delegates on AgentEvalSummary.
  • Converted 7 hand-rolled scans: the private _score helper plus 4 call sites in test_evaluator.py, one each in test_gym_aggregate_scores.py, test_hermes_example.py, two in test_pass_at_k.py, and the user-facing examples/hermes/example.py.

Design notes

A miss raises instead of returning None. An unknown name is nearly always a typo or a metric that did not run. Both are bugs worth surfacing at the lookup, where the name is in hand, rather than as an AttributeError on .mean further downstream. scores_by_name.get() covers legitimate absence.

The miss message leads with close matches. A run with several metrics times pass@k carries dozens of names, so enumerating them all buries the answer exactly when it is most needed. It reports how many others exist so a wrong suggestion is not a dead end, and truncates the fallback listing at 10.

Left alone deliberately. harbor_runtime.py and dashboard.py iterate rather than look up by name; plugin_examples.py filters on two candidate names. Converting any of them would change behaviour, not just style.

No schema impact. score is a method and scores_by_name a property, so neither enters the JSON schema — verified against AggregatedMetricResult, AgentEvalSummary, and EvaluationResult. The committed OpenAPI spec is unchanged.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with documentation updates
  • Documentation only
  • Contributor tooling or automation
  • CI, build, or test infrastructure

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Documentation updated for user-visible behavior
  • Documentation not applicable — justification: the new surface is documented in its own docstrings; there is no SDK reference page enumerating result accessors to update.

Verification

  • Pull request title follows the repository's Conventional Commit format
  • Every commit includes an appropriate Signed-off-by: trailer
  • uv run pre-commit run -a passes, or any blocked checks are identified below
  • Targeted tests pass, or tests are marked not applicable above
  • No secrets, API keys, or credentials are included

Targeted validation:

Command Result
pytest packages/nemo_evaluator_sdk/tests/agent_eval packages/nemo_evaluator_sdk/tests/values 598 passed
pytest .../test_summary_accessors.py 11 passed (new)
ruff check + ruff format --check on changed files clean
tools/lint/lint-python-types.sh exit 0, no diagnostics in changed files
JSON-schema drift check on the 3 affected models no drift
uv run pre-commit run -a 2 hooks failed for local-environment reasons — see below

uv run pre-commit run -a — two failures, neither caused by this change:

  1. uv-lock — refuses to run because the local uv is 0.9.30 and the repo pins >=0.9.14 for lockfile writes. This is a toolchain mismatch, not lock drift: the separate Check for uv.lock drift hook passed, and this branch touches no pyproject.toml or uv.lock.
  2. studio-lint-stagedmise has no pnpm shim configured in this worktree. This branch touches no web files.

Every other hook passed, including ruff, ty, copyright headers, config-reference doc, and Helm docs. Flagging both rather than marking the gate green; happy to re-run under a corrected local toolchain if a reviewer wants the clean sweep.

Summary by CodeRabbit

  • New Features

    • Added convenient score lookup by name for evaluation summaries and aggregated results.
    • Added score mappings, membership checks, and optional lookups.
    • Improved missing-score errors with typo suggestions and available-name guidance.
  • Tests

    • Expanded coverage for successful lookups, duplicate names, empty results, and long score lists.
    • Updated examples and tests to use streamlined score accessors.
    • Added validation for available-name reporting and first-match behavior.

Reading one aggregate from a run meant a hand-written scan over a doubly-nested
attribute:

    next(s for s in result.summary.scores.scores if s.name == "...").mean

Seven sites did this -- six in tests (one via a private `_score` helper) and one
in the user-facing Hermes example, which is precisely the audience an ergonomics
gap costs most.

Adds `score(name)` and a `scores_by_name` mapping view to AggregatedMetricResult,
so the dataset path (`EvaluationResult.aggregate_scores`) gains the same surface,
with one-hop delegates on AgentEvalSummary.

A miss raises KeyError rather than returning None: an unknown name is nearly
always a typo or a metric that did not run, and failing where the name is in hand
beats an AttributeError on `.mean` downstream. `scores_by_name.get()` covers
legitimate absence.

The miss message leads with close matches rather than enumerating everything --
a run with several metrics times pass@k carries dozens of names, and a wall of
them buries the answer. It reports how many others exist so a wrong suggestion is
not a dead end, and truncates the fallback listing.

`score` is a method and `scores_by_name` a property, so neither enters the JSON
schema; verified against AggregatedMetricResult, AgentEvalSummary, and
EvaluationResult. The committed OpenAPI spec is unchanged.

Left alone deliberately: harbor_runtime and the dashboard iterate rather than
look up, and plugin_examples filters on two candidate names -- converting those
would change behaviour, not just style.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
@github-actions github-actions Bot added the feat label Aug 10, 2026
@SandyChapman
SandyChapman marked this pull request as ready for review August 10, 2026 14:08
@SandyChapman
SandyChapman requested review from a team as code owners August 10, 2026 14:08
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 61c9ca02-0d45-4193-9066-57b568b81da2

📥 Commits

Reviewing files that changed from the base of the PR and between 690ab86 and abd342a.

⛔ Files ignored due to path filters (1)
  • sdk/python/nemo-platform/src/nemo_platform/beta/evaluator/values/results.py is excluded by !sdk/**
📒 Files selected for processing (1)
  • packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/results.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/results.py

📝 Walkthrough

Walkthrough

The SDK now provides named aggregate-score access through scores_by_name and score(name). Callers and tests use these accessors instead of manually searching score collections. Missing-name errors include suggestions or available names.

Changes

Aggregate score accessors

Layer / File(s) Summary
Implement named score lookup
packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/results.py, packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/results.py
AggregatedMetricResult exposes scores_by_name and score(name). AgentEvalSummary delegates both accessors to its aggregate result. Missing names raise diagnostic KeyError messages.
Migrate callers and validate behavior
packages/nemo_evaluator_sdk/examples/hermes/example.py, packages/nemo_evaluator_sdk/tests/agent_eval/*
Examples and evaluator tests use named score lookup. Tests cover suggestions, available-name reporting, empty results, optional access, duplicate names, and non-first matches.

Suggested reviewers: ngoncharenko

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding by-name aggregate lookup to AgentEvalSummary.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aalgo-445-score-accessor-v2/schapman

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

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 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 `@packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/results.py`:
- Around line 466-486: Update the available-name collection in the
aggregate-score miss diagnostics to build a set before sorting, ensuring each
aggregate name appears only once in suggestions and fallback enumeration. Add a
miss-case test covering duplicate aggregate names and verifying the diagnostic
contains no repeated names.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 6be49b3c-d3cc-4014-b6b8-9be74059f7ba

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2e9d6 and 753c54f.

📒 Files selected for processing (8)
  • packages/nemo_evaluator_sdk/examples/hermes/example.py
  • packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/results.py
  • packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/results.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_evaluator.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_gym_aggregate_scores.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_hermes_example.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_pass_at_k.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_summary_accessors.py

Comment thread packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/results.py Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 31924/40534 78.8% 63.5%
Integration Tests 18531/38460 48.2% 20.8%

…he vendored SDK

Two follow-ups on the by-name lookup.

**Duplicate names counted twice.** `available` was built from a generator, so a
repeated aggregate name was suggested twice, listed twice in the fallback
enumeration, and counted twice in the "N other aggregates" tally -- making one
collision read as two distinct near-misses. `scores_by_name` already collapsed
duplicates first-wins; the diagnostic path did not, and the two disagreed.
Deduplicated, with tests for both the suggestion and enumeration branches.

Reported by CodeRabbit on #1198.

**Vendored SDK out of sync.** `nemo_evaluator_sdk` is vendored into
`sdk/python/nemo-platform/src/nemo_platform/beta/evaluator/`, so any change to
its source needs `make vendor` re-run and the result committed. Without it
`lint-sdk-vendored` fails, and `lint-cli` fails after it because the vendor step
leaves `sdk/python/` dirty and lint-cli diffs that same path.

Verifying the JSON schema was unchanged was necessary but not sufficient: the
vendor copies source, not schema.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
Comment thread packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/values/results.py Outdated
…sage

Review question on #1198: what counts as a close match, and would listing the
names sorted be simpler?

Documents the answer where the code is rather than in a review thread. "Close"
is difflib.get_close_matches -- SequenceMatcher (Ratcliff/Obershelp) similarity
of at least 0.6, best three first -- which is a subsequence-overlap ratio, not
an edit distance.

Also records why the sorted list is not enough on its own: it is the better
answer left whole, but truncating one breaks it, because the name a caller
meant is not reliably in the first N. A typo'd `view.solved` sits behind a page
of `gym_reward.*` in a run carrying pass@1..8 for two metrics.

_MISS_NAME_LIMIT now says 10 is a judgement call rather than implying a measured
optimum, which is what the comment read like.

No behaviour change; the vendored SDK copy is re-synced.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>
@SandyChapman
SandyChapman added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit 378ca94 Aug 10, 2026
56 checks passed
@SandyChapman
SandyChapman deleted the aalgo-445-score-accessor-v2/schapman branch August 10, 2026 19:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants