Skip to content

Jnolan/gdpval hermes upstream - #3318

Open
j-nolan wants to merge 7 commits into
NVIDIA-NeMo:mainfrom
j-nolan:jnolan/gdpval-hermes-upstream
Open

j-nolan wants to merge 7 commits into
NVIDIA-NeMo:mainfrom
j-nolan:jnolan/gdpval-hermes-upstream

Conversation

@j-nolan

@j-nolan j-nolan commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Gym has a GDPVal implementation but it's wired to the stirrup harness, as per the GDPVal-AA-v2's methodology. Our team would like to run GDPVal using the Hermes harness. To do so, this PR adds a new GDPVal implementation where the harness can be swapped.

This new implementation differs from the original GDPVal as follows:

  1. The harness runs fully inside the benchmark container (the existing implementation runs stirrup on the host, only exposing its code execution provider to the benchmark container)
  2. The initial prompt instructs the agent to put its solution in a specific output directory, or write a text file there if it must abandon (stirrup has "finish" and "abandon" tools for this purpose, but those are not standard across harnesses). The Judge LLM scores the content of that directory
  3. It uses "rubric" mode only (the existing implementation allows multi-stage adaptive Elo ranking)

This PR doesn't aim to replace the existing GDPVal implementation. The generic implementation has a cost: the harness and its dependencies have to be provisioned into the container before the first task can run, which the host-side stirrup path avoids. It also diverges from AA-v2's methodology, which names stirrup as the harness, so a score from this path is not the published quantity.

Two changes fall outside the new directory. hermes_agent gains two optional config fields, supports_vision and api_call_stale_timeout, both defaulting to off. And NeMoGymChatCompletionToolMessageParam.content is widened to accept the same content parts a user message accepts, because a harness shows the model an image by returning it from a tool, and the text-only type rejects the whole conversation the first time that happens.

The PR looks large, but a lot of it is copied from the existing cvdp implementation. The duplication is not ideal, but it looks to be a common pattern when adding a new benchmark/agent in gym: setup_scripts/_portable_python.sh already exists in four agents with three different contents. TBD if there's a better approach or a plan to unify/reuse utilities.

Checklist

  • I have read the contributing guidelines.
  • The change is focused; unrelated "drive-by" edits are tracked as separate issues/PRs.
  • Tests added or updated and pass locally, or N/A for docs-only / non-code changes (so CI unit/server checks pass when applicable).
  • Pre-commit checks pass locally (pre-commit run --all-files) (so CI lint/format/copyright pass).
  • All commits have DCO sign-off (git commit -s) (so the DCO check passes).

GDPVal is wired to stirrup_agent, which drives the model itself, so a GDPVal score
says nothing about the harness under test. This agent provisions the GDPVal
container, stages the task's reference files, runs another Gym harness inside it and
collects the files that harness produced, which makes the benchmark usable for
harness comparison.

The harness has no tool for declaring its deliverables, so the prompt names an output
directory and only that directory is collected; if it is empty, files newer than a
start marker are swept instead.

The sandboxed harness reaches the model server over the host network, so its calls
carry the rollout prefix and the rollout id is handed to the harness as path_params.
Without them a harness takes an uninstrumented path that reports zero tokens and
emits no observations, and the model server has no rollout to file the calls under.
The harness run is timed and reported as agent_run_time so it can be measured.

Signed-off-by: James Nolan <jnolan@nvidia.com>
vision_analyze only registers when a vision route resolves. The probe asks whether a
vision client could be built, and it runs before the live endpoint is recorded, so a
self-served checkpoint absent from the models.dev catalogue fails twice over: no
declared capability, and no endpoint to construct a client from. It then falls
through to third-party vision providers we hold no credentials for, and the model is
told the tool does not exist.

Separately, hermes picks its non-stream stale timeout from an allowlist keyed on the
model name, which never matches here because the name it receives is
model_server.name, so long reasoning turns are abandoned at the 90s chat default.

Both are config fields defaulting to the previous behaviour: a text-only model is not
declared able to read images, and a caller that never asked keeps the harness's own
timeout.

Signed-off-by: James Nolan <jnolan@nvidia.com>
A harness shows the model a picture by returning it from a tool, but tool content was
annotated as text parts only, so the request schema rejected the whole conversation
the first time one came back:

  loc  body.messages.N.NeMoGymChatCompletionToolMessageParam.content
       .list[NeMoGymChatCompletionContentPartTextParam].1.type
  msg  Input should be 'text'
  got  image_url

Tool content now takes the same parts a user message may carry, like the name field
above it: the OpenAI type is narrower than the servers behind Gym.

Signed-off-by: James Nolan <jnolan@nvidia.com>
The verify response is built from the modelled request fields, so an unmodelled one
is dropped on the way back and never reaches the rollout: a full run recorded
agent_run_time on 0 of 1100 rollouts even though the agent measured it. Set it on the
returned record instead of relying on the resources server to echo it.

Signed-off-by: James Nolan <jnolan@nvidia.com>
At 5400s the sandbox killed 202 of 1100 rollouts on a full run. Each had already made
44 to 82 model calls, so the work was done and then discarded: the harness never wrote
its response, the agent fell back to an empty one, and the judge scored nothing. Those
rollouts averaged 0.30 against 0.84 for the ones that finished.

stirrup_agent, which owns this benchmark, allows 3h30m and describes it as a guard
against a task that would otherwise consume a whole Slurm walltime, not as a working
budget. Use the same figure.

Signed-off-by: James Nolan <jnolan@nvidia.com>
_collect truncates at 100 files. One task ran a package install inside the output directory, so
the harvest competed 98 vendored OpenZeppelin sources against the model's own contract, README
and circuit, and dropped the latter.

Exclude the directory names a build leaves behind, so a dependency tree cannot enter the
candidate set, and tell the model to build elsewhere. Across 1100 rollouts the cap binds once and
only in that task, and the exclusion alone resolves it.

Signed-off-by: James Nolan <jnolan@nvidia.com>
Hermes caps web_search at 50 calls per turn and resets the counter each turn, which never
starves a normal multi-turn session. A GDPVal task runs as a single Hermes turn, so the cap
acts as a whole-task ceiling instead, and tripping it halts the loop rather than warning. One
task lost its run to it after 50 searches that had all succeeded with distinct content, with
the document it was researching never written.

Expose the cap on the Hermes agent config and raise it here to match max_turns. Not disabled:
that would remove the runaway-loop protection entirely, where matching max_turns keeps it at
about one search per iteration while removing the ceiling our one-turn design created.

Signed-off-by: James Nolan <jnolan@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@nemo-automation-bot nemo-automation-bot Bot added the community-request Issue reported or requested by someone from the community label Sep 11, 2026
@yaoyu-33 yaoyu-33 added area:agent Agent harnesses and Responses API agent behavior feature New capabilities, enhancements, or enablement work needs-review PR is ready for code review and waiting on a reviewer and removed community-request Issue reported or requested by someone from the community labels Sep 11, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@yaoyu-33 yaoyu-33 removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@yaoyu-33 yaoyu-33 removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@yaoyu-33 yaoyu-33 removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@yaoyu-33 yaoyu-33 removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 13, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 14, 2026
@yaoyu-33 yaoyu-33 removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 14, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 14, 2026
@yaoyu-33 yaoyu-33 removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 14, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 14, 2026
@github-actions github-actions Bot added the sla:triage-overdue Review assignment is over the one-business-day SLA label Sep 14, 2026
@yaoyu-33 yaoyu-33 removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 14, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-maintainers Waiting on maintainers to respond label Sep 14, 2026
@agronskiy

Copy link
Copy Markdown
Contributor

@j-nolan I'm holding off till tonight because you can benefit from us (cc @Kh4L and Dan Zhao) merging hte gdpval-devel branch which has the prompts updated. Pls wait a bit

@nemo-automation-bot nemo-automation-bot Bot added the community-request Issue reported or requested by someone from the community label Sep 14, 2026
@yaoyu-33 yaoyu-33 removed community-request Issue reported or requested by someone from the community needs-review PR is ready for code review and waiting on a reviewer labels Sep 14, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-customer Waiting on the original author to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels Sep 14, 2026
@yaoyu-33 yaoyu-33 added the complexity:medium Single-domain change with interacting parts or a moderate review surface label Sep 15, 2026
@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-maintainers Waiting on maintainers to respond and removed waiting-on-customer Waiting on the original author to respond labels Oct 29, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agent Agent harnesses and Responses API agent behavior complexity:medium Single-domain change with interacting parts or a moderate review surface feature New capabilities, enhancements, or enablement work sla:triage-overdue Review assignment is over the one-business-day SLA waiting-on-maintainers Waiting on maintainers to respond

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants