Skip to content

feat(evaluator): give AgentEvalResult the dataset path's display surface - #1201

Merged
SandyChapman merged 3 commits into
mainfrom
aalgo-447-agent-eval-display-surface/schapman
Aug 10, 2026
Merged

feat(evaluator): give AgentEvalResult the dataset path's display surface#1201
SandyChapman merged 3 commits into
mainfrom
aalgo-447-agent-eval-display-surface/schapman

Conversation

@SandyChapman

@SandyChapman SandyChapman commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

EvaluationResult ships format_summary(), print_summary(), to_records(), to_table(), to_pandas(), and __str__. AgentEvalResult had none of them, so anyone inspecting an agent-eval run wrote their own formatting — two result types in one SDK with unrelated ergonomics. This adds the same six methods, with the same signatures and the same view values.

Linear: AALGO-447

Changes

  • to_records(view="rows"|"aggregate") on AgentEvalResult, plus to_table, to_pandas, format_summary, print_summary, __str__
  • Row projection, error-text and diagnostics columns, header, and failed-score section as module-level helpers alongside the existing ones

Design: no mixin

The ticket proposed extracting a shared mixin. BenchmarkEvaluationResult (values/multi_metric_results.py) already faced this and answered it differently — import the module-level helpers, implement the methods directly. The genuinely shared logic (format_table, summary_aggregate_record, serialize_value, flatten_dict) is already free functions, so a mixin would share method names while every body still needed overriding, and would add a third pattern to unify two that already agree. This follows the established one.

Reversible, but reversing it means refactoring all three result types rather than just this one.

What a "row" is here

One record per metric score. The fan-out is preserved rather than collapsed: task_id and trial_id are columns, so a consumer can still group by task — which is what pass@k depends on. The aggregate view matches the dataset path exactly (percentiles flattened, histograms as JSON strings).

The error section is agent-eval's own, because it has a distinction the dataset path cannot make: a failed trial is an attempt the agent is answerable for; a failed metric is a measurement that never happened. Both arrive as FAILED.

One deliberate divergence — to_table column union

pa.Table.from_pylist takes its schema from the first record alone. Here error and diagnostics.* appear only on failed scores, so a run whose first score passed exported a table missing exactly the columns you needed — while to_pandas, which unions keys, included them. Verified: 5 columns vs 7 on the same run.

Columns are now unioned before the table is built. Both siblings have the same latent behaviour. Matching a known-lossy export seemed worse than diverging and saying so — but that makes this the odd one out until EvaluationResult and BenchmarkEvaluationResult get the same fix. Happy to file that follow-up, or to drop this and match the siblings if reviewers would rather keep the three identical.

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 and mirrors an existing documented API; there is no SDK reference page enumerating result methods 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 1464 passed
pytest .../test_result_display.py 16 passed (new)
ruff check + ruff format --check on changed files clean
tools/lint/lint-python-types.sh exit 0, no diagnostics in changed files
Mutation check (14 mutants against the new tests) 14/14 caught
uv run pre-commit run -a 2 hooks failed for local-environment reasons — see below

Three adversarial review rounds were run before this was opened. Round 1 surfaced three tests that passed against a broken implementation — most notably, the failed-trial/failed-metric assertion still passed when the two labels were swapped — plus an unreachable if count guard, now removed. Round 2 left one survivor (max_error_rows defaulting to max_rows was documented but untested). Round 3: 14/14 caught, zero survivors.

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 while the repo pins >=0.9.14 for lockfile writes. 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 in this worktree. This branch touches no web files.

Every other hook passed, including ruff, ruff format, ty, copyright headers, config-reference doc, Helm docs, and the plugin import-boundary check.

Summary by CodeRabbit

  • New Features

    • Added options to export evaluation results as row-based or aggregate records, tables, and pandas dataframes.
    • Added formatted summaries, console printing, and compact string representations.
    • Improved visibility into score outputs, diagnostic severity, metadata, errors, and failed trials or metrics.
    • Added controls for limiting displayed rows and error details.
  • Bug Fixes

    • Improved consistency of exported data and diagnostic information across supported formats.

`EvaluationResult` ships format_summary/print_summary/to_records/to_table/
to_pandas/__str__. `AgentEvalResult` had none of them, so anyone inspecting an
agent-eval run wrote their own formatting -- two result types in one SDK with
unrelated ergonomics.

Adds the same six methods, with the same signatures and the same `view` values.

## No mixin

The ticket proposed extracting a shared mixin. `BenchmarkEvaluationResult`
(values/multi_metric_results.py) already faced this and answered it differently:
import the module-level helpers, implement the methods directly. The genuinely
shared logic -- format_table, summary_aggregate_record, serialize_value,
flatten_dict -- is already free functions, so a mixin would share method *names*
while every body still needed overriding, and would add a third pattern to
unify two that already agree. This follows the established one instead.

## What a row is here

A record per metric score. The fan-out is preserved rather than collapsed:
task_id and trial_id are columns, so a consumer can still group by task, which
is what pass@k depends on. The aggregate view is byte-for-byte the dataset
path's -- percentiles flattened, histograms as JSON strings.

The error section is agent-eval's own, because it has a distinction the dataset
path cannot make: a failed *trial* is an attempt the agent is answerable for, a
failed *metric* is a measurement that never happened. Both arrive as FAILED.

## to_table column union (deliberate divergence)

`pa.Table.from_pylist` takes its schema from the first record alone. Here
`error` and `diagnostics.*` appear only on failed scores, so a run whose first
score passed exported a table missing exactly the columns you needed, while
to_pandas -- which unions keys -- included them. Verified: 5 columns vs 7.

Columns are now unioned before the table is built. Both siblings have the same
latent behaviour; matching a known-lossy export seemed worse than diverging and
saying so. Worth a follow-up for EvaluationResult and BenchmarkEvaluationResult.

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 15:24
@SandyChapman
SandyChapman requested review from a team as code owners August 10, 2026 15:24
@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: 70f21895-9e7a-4082-8c88-f333bce0b3be

📥 Commits

Reviewing files that changed from the base of the PR and between 0b1281e and e7d30d0.

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

📝 Walkthrough

Walkthrough

Changes

Agent result presentation

Layer / File(s) Summary
Result export APIs
packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/results.py
Adds row and aggregate record conversion, PyArrow table export, and pandas dataframe export. Aggregate percentiles are flattened, histograms use sorted JSON strings, and metadata and diagnostics are serialized into fields.
Result summary formatting
packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/results.py
Adds formatted, printed, and string summaries with run status, score previews, failure classification, diagnostic text, and configurable limits.
Presentation behavior validation
packages/nemo_evaluator_sdk/tests/agent_eval/test_result_display.py
Tests record exports, table and pandas columns, diagnostics, aggregate values, empty results, status reporting, failure labels, output limits, and rendering.

Sequence Diagram(s)

sequenceDiagram
  participant AgentEvalResult
  participant PyArrow
  participant pandas
  AgentEvalResult->>AgentEvalResult: Build row or aggregate records
  AgentEvalResult->>PyArrow: Create table from records
  AgentEvalResult->>pandas: Create dataframe from records
Loading

Possibly related PRs

Suggested reviewers: ngoncharenko, arpitsardhana

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.90% 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 the dataset path's display surface to AgentEvalResult.
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-447-agent-eval-display-surface/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/agent_eval/results.py`:
- Around line 352-371: Update _score_record to retain AgentEvalTaskScore.id,
run_id, and metadata in every row export, serializing or flattening metadata
consistently with the existing export conventions. Ensure _score_preview_record
and downstream table/DataFrame exports preserve these fields as applicable, and
add coverage verifying their presence and values.
🪄 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: ff1e6c2d-09d1-4a59-963c-378d8c5cd9a8

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2e9d6 and 0b1281e.

📒 Files selected for processing (2)
  • packages/nemo_evaluator_sdk/src/nemo_evaluator_sdk/agent_eval/results.py
  • packages/nemo_evaluator_sdk/tests/agent_eval/test_result_display.py

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 31969/40576 78.8% 63.5%
Integration Tests 18527/38502 48.1% 20.8%

`nemo_evaluator_sdk` is vendored into
`sdk/python/nemo-platform/src/nemo_platform/beta/evaluator/`, so a 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.

Signed-off-by: Sandy Chapman <schapman@nvidia.com>

@arpitsardhana arpitsardhana 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.

minor nits comments

…ports

Review on #1201: `_score_record` dropped `AgentEvalTaskScore.id`, `run_id`, and
`metadata`, so every row, table, and DataFrame export lost them.

An export is the thing a caller joins, concatenates, and keeps. `id` is what a
row is addressable by, `run_id` keeps a frame self-describing once several runs
are stacked into one, and `metadata` is caller-supplied -- dropping it silently
discarded data the SDK never owned.

Metadata is flattened into dotted columns rather than JSON-encoded: it is
free-form but usually shallow and scalar, so flattening keeps it queryable.
Diagnostics keep the JSON treatment, because their shape is metric-defined and
variable-width.

The summary preview deliberately stays narrow -- it is read on a terminal --
which is the same split the dataset path makes between `to_records` and
`summary_row_base_record`. Covered by a test so the two don't drift together.

Also documents why the error-row limit is clamped with max(0, ...): slicing
would read a negative limit as an offset from the end, so `failed[:-2]` would
show all but the last two rather than none. An over-large limit needs no guard.

Vendored SDK 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 f365be9 Aug 10, 2026
56 of 57 checks passed
@SandyChapman
SandyChapman deleted the aalgo-447-agent-eval-display-surface/schapman branch August 10, 2026 18:21
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