Skip to content

feat(evaluator): agent-evaluate wire publish_to_intake() to API - #1148

Merged
nv-odrulea merged 6 commits into
mainfrom
od/agent-eval-api-publishes-to-intake
Aug 7, 2026
Merged

feat(evaluator): agent-evaluate wire publish_to_intake() to API#1148
nv-odrulea merged 6 commits into
mainfrom
od/agent-eval-api-publishes-to-intake

Conversation

@nv-odrulea

@nv-odrulea nv-odrulea commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Agent-evaluation jobs can now publish their results to Intake, so eval runs show up under
Experiments and are associated with a specific agent. Previously publish_to_intake existed but
nothing called it except tests — there was no API to request publication, so Studio couldn't
trigger it.

Related Issue

https://linear.app/nvidia/issue/ASTD-384/agent-experiments-evaluator-publish-to-intake-from-api

Changes

Adds an opt-in publication.intake block to the agent-evaluate job spec:

{
  "publication": {
    "intake": {
      "evaluation_id": "email-security-baseline",
      "agent_name": "email-security-analyst",
      "required": true
    }
    "intake": {
      "evaluation_id": "email-security-baseline",
      "agent_name": "email-security-analyst",
      "required": true
    }
  }
}

Absent → no publish, zero Intake calls. Present → the worker publishes after the run, under an
Evaluation that must already exist (it

Also fixes: publishing was not idemp

trial_to_atif_ingest emitted a step with no timestamp, so Intake fell back to a per-request
ingest clock — and spans is a ReplacingMergeTree keyed on start_time. Re-publishing wrote
rows that never collapse. Measured agaise, one re-published trial:

spans (FINAL, and via the public API)
before 4 (should be 2)
after 2

traces.list returned 1 either way, which is why this would not have been noticed. Fix is one
field: stamp result.metadata.started_at on the step. publish_to_intake now refuses to publish
without it rather than writing uncollap

This matters because the recovery storye-publish" — without idempotency
that instruction corrupts the data inst

Notable decisions

  • required defaults to true. A run that asked to publish shouldn't report success with
    nothing in Experiments. The bundle isn runs, so a failed job still
    leaves results intact to re-publish. required: false opts out.
  • Publication runs last, after the bundle artifact and queryable result record are durable.
    It's the only step that can fail the job.
  • **Agent identity is derived only whergentTarget, HarborRunnerTarget). Otherwise agent_name` is required and rejected at submit with a 422 — not after a 20-minute eval.
  • Job output uses PublicationOutcome, typed with the shared PlatformJobStatus
    (COMPLETED / ERROR), not the publisher's internal PublishReport — which names the
    Evaluation experiment_id, contradiche API accepts.

Testing

  • 30 new unit tests; 748 total green in
  • 3 integration tests pass against live Intake + ClickHouse, including a new
    double-publish idempotency test.
  • Also fixes a pre-existing limitation in that integration fixture: it spawned nemo services run
    without --port, so it always bound the 8080 default and couldn't run alongside a local dev
    platform. Now derives the port from NMP_BASE_URL.

Follow-ups

  • nmp-v7j — recovery endpoint to re-pUntil it ships, recovery after a
    failed publication means re-running t
  • nmp-046 — Studio submit wiring. Splg with in-flight modal changes
    (nmp-bjm). Note web/packages/sdk/generated/ is gitignored, so run
    `pnpm --filter @nemo/sdk gen:evaluato

Not in this PR

Trajectories published today are single; real steps[] from
trial.evidence is deferred to D2 / AAscores plus a stub trace.

This reverses AALGO-290's stance that py* optional (no feature flag).
The explicit opt-in spec field preserve = no publish.

Summary by CodeRabbit

  • New Features

    • Added optional publication of agent-evaluation results to Intake, including evaluation ID, agent metadata, and configurable failure behavior.
    • Publication outcomes are returned when enabled.
    • Added scalar scores and expanded aggregate statistics, including median, sample variance, and standard deviation.
    • Aggregate score counts are now optional when sample counts are unknown.
    • Studio now supports displaying scalar aggregate scores.
  • Bug Fixes

    • Improved republishing safety with timestamps and stable identifiers to prevent duplicate records.
    • Added validation and clearer handling for missing timestamps, evaluations, clients, or publication failures.

@nv-odrulea
nv-odrulea requested review from a team as code owners August 6, 2026 22:10
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Agent-evaluation specifications now support optional Intake publication and expanded aggregate score schemas. Jobs publish persisted results with stable run timestamps, structured outcomes, and configurable failure behavior. Tests cover validation, failures, timestamp propagation, and idempotent republishing.

Agent evaluation publication

Layer / File(s) Summary
Publication contracts and job wiring
plugins/nemo-evaluator/openapi/openapi.yaml, plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_spec.py, plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_evaluate.py, web/packages/studio/src/api/evaluation/agent-evaluations.ts
Adds publication schemas, aggregate score fields, scalar score support, target identity resolution, validation, and canonical specification propagation.
Timestamped Intake ingest
plugins/nemo-evaluator/src/nemo_evaluator/intake/mapping.py, plugins/nemo-evaluator/src/nemo_evaluator/intake/publish.py, plugins/nemo-evaluator/tests/intake/*
Requires started_at, includes it in generated agent steps, and validates it before publishing.
Publication execution and failure handling
plugins/nemo-evaluator/src/nemo_evaluator/jobs/publication.py, plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_evaluate.py, plugins/nemo-evaluator/tests/jobs/test_publication.py
Adds Intake publication outcomes, evaluation checks, synchronous bridging, and required versus optional failure behavior.
End-to-end publication idempotency
plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
Publishes identical results twice and verifies stable identities without duplicate Intake rows.

Sequence Diagram(s)

sequenceDiagram
  participant AgentEvalJob
  participant publish_agent_eval_result
  participant Workspace
  participant Intake
  AgentEvalJob->>publish_agent_eval_result: publish persisted result
  publish_agent_eval_result->>Workspace: verify evaluation_id
  Workspace-->>publish_agent_eval_result: evaluation exists
  publish_agent_eval_result->>Intake: submit result and metadata
  Intake-->>publish_agent_eval_result: return publication outcome
  publish_agent_eval_result-->>AgentEvalJob: return outcome or raise required failure
Loading

Possibly related PRs

Suggested reviewers: arpitsardhana, sandychapman, ngoncharenko

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.49% 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 identifies the main change: wiring agent evaluation to publish results to the Intake API.
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 od/agent-eval-api-publishes-to-intake

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 `@plugins/nemo-evaluator/src/nemo_evaluator/jobs/publication.py`:
- Around line 149-168: Update the exception handling around the _publish call in
publish_agent_eval_result to catch unexpected Exception instances and route them
through fail, preserving the existing required=False behavior. Keep the specific
PublishError, NotFoundError, and NeMoPlatformError handlers unchanged, and
include the unexpected exception details in the fallback failure result.
🪄 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: d5cb7e44-96b6-467b-94c3-bd8172e59a51

📥 Commits

Reviewing files that changed from the base of the PR and between c71ca67 and c1130f1.

📒 Files selected for processing (10)
  • plugins/nemo-evaluator/openapi/openapi.yaml
  • plugins/nemo-evaluator/src/nemo_evaluator/intake/mapping.py
  • plugins/nemo-evaluator/src/nemo_evaluator/intake/publish.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_evaluate.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_spec.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/publication.py
  • plugins/nemo-evaluator/tests/intake/test_mapping.py
  • plugins/nemo-evaluator/tests/intake/test_publish.py
  • plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
  • plugins/nemo-evaluator/tests/jobs/test_publication.py

Comment thread plugins/nemo-evaluator/src/nemo_evaluator/jobs/publication.py
@nv-odrulea nv-odrulea changed the title feat(evaluator): wire publish_to_intake() to API surface feat(evaluator): agent-evaluate wire publish_to_intake() to API Aug 6, 2026
@nv-odrulea nv-odrulea self-assigned this Aug 6, 2026
@github-actions github-actions Bot added the feat label Aug 6, 2026
@nv-odrulea
nv-odrulea force-pushed the od/agent-eval-api-publishes-to-intake branch from 77edb93 to d90c4d4 Compare August 7, 2026 05:50
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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: 3

🤖 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 `@plugins/nemo-evaluator/openapi/openapi.yaml`:
- Around line 3897-3901: The agent_name field in the OpenAPI schema must reject
empty strings. Update the corresponding IntakePublicationSpec validation and the
agent_spec identity check to enforce a non-empty value, preserve valid-name
behavior, then regenerate the OpenAPI schema.
- Around line 2678-2683: Version the aggregate-score response contract so
existing v2 clients retain the required count behavior while the
nullable/omitted count form is exposed only through a newer version. Apply the
same compatibility strategy to both the aggregate-score count schema at
plugins/nemo-evaluator/openapi/openapi.yaml:2678-2683 and the rubric-score count
schema at plugins/nemo-evaluator/openapi/openapi.yaml:2761-2766.

In `@plugins/nemo-evaluator/tests/jobs/test_publication.py`:
- Around line 398-407: The bridge test should exercise loop affinity: update
_FakeEvaluator.run_sync to actually run its coroutine on an event loop, and
update _FakeClient to bind to the first loop used and reject calls from any
different loop. Preserve the existing AgentEvalJob().run invocation so
regressions to asyncio.run or reuse of a closed-loop SDK client fail.
🪄 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: d8dac1b9-0c46-4f5a-be1b-4d15998e524c

📥 Commits

Reviewing files that changed from the base of the PR and between 20e39e0 and d90c4d4.

📒 Files selected for processing (10)
  • plugins/nemo-evaluator/openapi/openapi.yaml
  • plugins/nemo-evaluator/src/nemo_evaluator/intake/mapping.py
  • plugins/nemo-evaluator/src/nemo_evaluator/intake/publish.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_evaluate.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_spec.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/publication.py
  • plugins/nemo-evaluator/tests/intake/test_mapping.py
  • plugins/nemo-evaluator/tests/intake/test_publish.py
  • plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
  • plugins/nemo-evaluator/tests/jobs/test_publication.py
🚧 Files skipped from review as they are similar to previous changes (8)
  • plugins/nemo-evaluator/src/nemo_evaluator/intake/publish.py
  • plugins/nemo-evaluator/src/nemo_evaluator/intake/mapping.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_evaluate.py
  • plugins/nemo-evaluator/tests/intake/test_mapping.py
  • plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_spec.py
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/publication.py
  • plugins/nemo-evaluator/tests/intake/test_publish.py

Comment thread plugins/nemo-evaluator/openapi/openapi.yaml
Comment thread plugins/nemo-evaluator/openapi/openapi.yaml
Comment thread plugins/nemo-evaluator/tests/jobs/test_publication.py
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 31628/40251 78.6% 63.2%
Integration Tests 18380/38186 48.1% 20.8%

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugins/nemo-evaluator/openapi/openapi.yaml (1)

2678-2683: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Regenerate the OpenAPI schema with nullable aggregate fields.

AggregateScoreBase defines count, sample_std_dev, and sample_variance as nullable, and runtime code emits null. Update all three aggregate schemas to accept null, then refresh the generated OpenAPI file.

🤖 Prompt for 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.

In `@plugins/nemo-evaluator/openapi/openapi.yaml` around lines 2678 - 2683, Update
the OpenAPI definitions for AggregateScoreBase and the other two aggregate
schemas so count, sample_std_dev, and sample_variance accept null in addition to
integer/number values, matching the nullable fields and runtime output.
Regenerate the openapi.yaml artifact after updating the source schema rather
than editing only the generated entry.
🤖 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.

Outside diff comments:
In `@plugins/nemo-evaluator/openapi/openapi.yaml`:
- Around line 2678-2683: Update the OpenAPI definitions for AggregateScoreBase
and the other two aggregate schemas so count, sample_std_dev, and
sample_variance accept null in addition to integer/number values, matching the
nullable fields and runtime output. Regenerate the openapi.yaml artifact after
updating the source schema rather than editing only the generated entry.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 51a0f1e0-6e9f-46b3-8021-e9a63105e0a3

📥 Commits

Reviewing files that changed from the base of the PR and between d90c4d4 and a39f75b.

📒 Files selected for processing (3)
  • plugins/nemo-evaluator/openapi/openapi.yaml
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_spec.py
  • plugins/nemo-evaluator/tests/jobs/test_publication.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_spec.py

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

thanks for adding this! I left a couple comments, but overall this lgtm!

Comment thread plugins/nemo-evaluator/openapi/openapi.yaml
Comment thread plugins/nemo-evaluator/src/nemo_evaluator/intake/mapping.py
Comment thread plugins/nemo-evaluator/src/nemo_evaluator/intake/publish.py
Comment thread plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_spec.py Outdated
Comment thread plugins/nemo-evaluator/src/nemo_evaluator/jobs/agent_evaluate.py
@coderabbitai coderabbitai Bot mentioned this pull request Aug 7, 2026
15 tasks
Signed-off-by: Octavian Drulea <odrulea@nvidia.com>
Signed-off-by: Octavian Drulea <odrulea@nvidia.com>
Mechanical regeneration, unrelated to this branch's feature work. #1065 added
`median`, `sample_std_dev` and `sample_variance` to `AggregateScore` in
`nemo_evaluator_sdk` and reworded the variance/std_dev descriptions, but did not
regenerate the evaluator plugin spec that re-exports those types.

Nothing was set up to catch it: the openapi pre-commit hook is `stages: [manual]`
and its `files:` pattern does not cover `packages/nemo_evaluator_sdk/`, and CI's
`tools/lint/lint-openapi.sh` only diffs the platform specs, never the per-plugin
ones under `plugins/*/openapi/`.

Regenerating here rather than leaving it for whoever next touches this file. No
publication/intake schema changes are in this diff.

Signed-off-by: Octavian Drulea <odrulea@nvidia.com>
Signed-off-by: Octavian Drulea <odrulea@nvidia.com>
Signed-off-by: Octavian Drulea <odrulea@nvidia.com>
Signed-off-by: Octavian Drulea <odrulea@nvidia.com>
@nv-odrulea
nv-odrulea force-pushed the od/agent-eval-api-publishes-to-intake branch from 677ef46 to 9180e45 Compare August 7, 2026 20:46
@nv-odrulea
nv-odrulea enabled auto-merge August 7, 2026 20:49
@nv-odrulea
nv-odrulea added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit e32287b Aug 7, 2026
55 of 56 checks passed
@nv-odrulea
nv-odrulea deleted the od/agent-eval-api-publishes-to-intake branch August 7, 2026 21:20
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