feat(eval): export Gym trajectories as ATIF - #2767
afourniernv wants to merge 17 commits into
Conversation
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
🌿 Preview your docs: https://nvidia-preview-feat-atif-export.docs.buildwithfern.com/nemo/gym Here are the markdown pages you've updated: |
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
Review cleanup and the completed hardening commits are now pushed through ce3a7d2. This PR remains draft. Suggested review order after #2737:
No supported behavior was removed. The shared ATIF schema and strict JSON helpers now match #2737 exactly. The PR is 193 lines smaller overall. Validation on the exact pushed tree: 2,875 unit tests passed, 2 skipped, 336 deselected, 15 subtests passed; 666 focused tests passed; all pre-commit hooks passed; and the real Gym trajectory to ATIF to Gym verifier-payload round trip passed. |
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com> # Conflicts: # fern/versions/latest/pages/reference/cli-commands.mdx
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
The latest egress hardening is pushed in 16bd3a4, and the PR description now records the current usage contract and round-trip evidence.
This PR remains draft until #2737 lands. After that, |
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
|
Review-order note: this PR currently duplicates |
Signed-off-by: Alex Fournier <afournier@nvidia.com>
| ) | ||
|
|
||
|
|
||
| def test_gym_rollout_to_atif_preserves_provenance_turns_metrics_and_tool_identity() -> None: |
There was a problem hiding this comment.
I don't think there's any test here or in #2737 that tests the combination of both PRs (eg feeds an exporter-produced file through the reverify projection logic). It's hard to test while the 2 PRs are separate branches, but I think this should be added once the first one is merged in before merging the other one
There was a problem hiding this comment.
Agreed. I am keeping this as a merge-order gate rather than copying the ingress implementation into this sibling branch. After #2737 lands, I will merge main into #2767, retain the canonical shared modules, and add one exporter-output to manifest-load to reverify-projection test before #2767 merges.
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
## What does this PR do? Adds a bounded ATIF v1.7 input path to `gym eval reverify`, allowing completed Relay trajectories to be scored by Gym's existing stateless verifier flow without rerunning the agent or policy model. Together, NVIDIA-NeMo#2737 and NVIDIA-NeMo#2767 establish a file-based interoperability path between NeMo Relay and NeMo Gym. Relay-exported ATIF trajectories can be evaluated in Gym, while Gym-native trajectories can be exported to the same ATIF format for downstream consumers. This does not couple the Relay and Gym runtimes or add Relay as a Gym dependency. Relates to NVIDIA-NeMo#2468. ## Before `gym eval reverify` only accepted native Gym rollout JSONL. ATIF contains the external agent trajectory, but not the Gym task, expected answer, verifier configuration, or resources-server routing needed to calculate a reward. ## After ```bash gym eval reverify \ --input-format atif \ --config my_resources_server.yaml \ --inputs materialized-inputs.jsonl \ --atif-manifest relay-trajectories.jsonl \ --output rewards.jsonl ``` The inputs have separate responsibilities: - Materialized Gym inputs provide the task, verifier metadata, request context, and routing. - ATIF provides the completed external-agent output. - The manifest explicitly joins each trajectory to `(_ng_task_index, _ng_rollout_index)` and may pin its SHA-256. Gym projects the supported ATIF output into its existing Responses-shaped verifier payload, calls the normal stateless `/verify` path, and computes aggregate metrics. ## Integration contract Gym consumes the canonical ATIF fields needed to build that bounded scoring payload. It does not reconstruct provider-native payloads stored in optional `extra` metadata; the ATIF producer owns that conversion. `projection_status=complete` means Gym constructed the supported verifier payload without guessing or dropping a field the adapter maps. It does not claim full-document ATIF fidelity, certify the producer's provider-to-ATIF conversion, or interpret arbitrary producer metadata. Persisted source and trajectory hashes retain that provenance. Supported in this initial path: - exact ATIF v1.7 schema and text-only trajectories; - canonical agent text and `reasoning_content`; - generic function calls with exactly one correlated result per call; - standard token usage and supported Relay usage details; - explicit one-to-one trajectory/task joins with optional source-hash verification; and - persisted trajectory ID, session ID, schema version, source hash, and projection status. Standard usage is strict. If any model step reports supported usage, every model-backed step must provide `prompt_tokens` and `completion_tokens`, and `final_metrics` must provide matching totals. Optional cached, reasoning, and reported-total details are projected only when their aggregate can be proven. Partially reported optional details remain unknown rather than being counted as zero. Equivalent Relay reasoning-token aliases are normalized, conflicting aliases are rejected, and cost metadata remains provenance rather than verifier input. The path fails closed on shapes that cannot be mapped without changing what the verifier scores, including: - multimodal, continued, copied-context, or subagent trajectories; - later user or system input after agent output; - aggregated LLM calls or ambiguous message/tool ordering; - incomplete, duplicated, or uncorrelated tool calls and results; and - training token IDs or log probabilities. Optional producer-specific status or provider payloads in `extra` do not change the canonical projection. A trajectory may end after a tool call when every call has a correlated observation result. Tool-bearing ATIF is rejected when routed to a resources server with `expose_tools_over_mcp: true`. ATIF proves generic call/result correlation, but does not carry Gym's canonical MCP `(server_name, tool_name)` identity. ATIF mode requires the verifiers selected by the batch to be stateless and does not support `--force`, `--resume`, `--judge-failed-only`, or `--append`. Native Gym reverification remains the default. This adds no Relay, NeMo Agent Toolkit, or Harbor runtime dependency. ## Suggested review order Most of the added lines are qualification tests and fixtures. The maintainer-facing behavior is concentrated in these areas: | Review area | Files | What to verify | | --- | --- | --- | | Strict ATIF boundary | `nemo_gym/atif_v1_7.py`, `nemo_gym/atif_json.py` | Exact v1.7/schema and lossless JSON rules | | Projection | `nemo_gym/atif_reverification.py` | Supported trajectory mapping, usage semantics, identity, and fail-closed boundaries | | Gym integration | `nemo_gym/rollout_reverification.py` | Selected-route preflight and reuse of the existing stateless verifier path | | Wiring and evidence | CLI, docs, fixtures, and tests | User-facing contract and regression coverage | Suggested pass: review the boundary first, then projection, then the small Gym integration. The remaining files document and prove that contract. ## Validation - Focused ATIF projection, rollout-reverification, and CLI suites: **491 passed**. - Changed-file pre-commit and `git diff --check`: passed. - Fixture tests prove reward equivalence between native and projected responses and show that the same ATIF trajectory can be rescored with a second stateless verifier. - The live Codex CLI → Relay → ATIF → Gym check executed a real tool call, preserved `function_call → function_call_output → message`, and returned reward `1.0` with aggregate accuracy `100%`. The broader local unit run reported the same five failures in both ATIF worktrees, all outside the affected paths: sandbox port binding, existing `aime24` metadata, macOS `/proc` behavior, system Python missing `aiohttp`, and sandbox semaphore permissions. The affected suites pass. ## Related work - NVIDIA-NeMo#2468 tracks ATIF input for Gym reverification. - NVIDIA/NeMo-Relay#887 fixes Responses tool-call correlation. - NVIDIA-NeMo#2673 covers Gym-native structured MCP tool identity and is complementary to this PR. ## Checklist - [x] I have read the contributing guidelines. - [x] The change is focused and adds no unrelated runtime dependency. - [x] Tests and documentation were added. - [x] Changed-file pre-commit checks pass. - [x] Commits include DCO sign-off. --------- Signed-off-by: Alex Fournier <afournier@nvidia.com>
What does this PR do?
Adds a strict offline exporter from completed Gym
ng_trajectoryv1.0 rollouts to ATIF v1.7. It writes one ATIF file per rollout plus a SHA-256-pinned manifest that Gym's ATIF reverification path can consume.Together, #2737 and #2767 establish a file-based interoperability path between NeMo Relay and NeMo Gym. Relay-exported ATIF trajectories can be evaluated in Gym, while Gym-native trajectories can be exported to the same ATIF format for downstream consumers. This does not couple the Relay and Gym runtimes or add Relay as a Gym dependency.
This is PR 2 of a two-PR sequence. #2737 owns ATIF ingress and the shared ATIF schema/JSON boundary; this PR owns Gym-to-ATIF egress. Related to #2468.
Before
Gym could store normalized rollout trajectories but could not export them in a portable trajectory format.
After
The exporter maps Gym's normalized trajectory, not provider-native payloads. Raw provider request and response copies are retained opaquely in ATIF extension metadata; they are never parsed to fill gaps in the normalized record.
Initial contract
Supported:
Rejected rather than guessed or dropped:
The exporter writes each known per-step usage field without inventing missing values.
reasoning_tokensandtotal_tokensuse the same step-level ATIF extension shape as Relay. A final prompt-, completion-, or cached-token aggregate is emitted only when every captured model call reports that field; an unavailable value is never counted as zero.An export with partial usage can therefore be valid ATIF for another consumer while remaining ineligible for Gym's strict reverify path. Gym reverify requires complete per-step prompt/completion counts whenever supported usage exists because one verifier request cannot represent partial standard usage. This asymmetry is deliberate.
The exported
extra.nemo_gym.conversion.status=completemeans every field present in the supported normalizedng_trajectoryprofile was represented without guessing. It does not certify opaque provider request or response copies retained as provenance.Out of scope
No ATOF parsing, Relay/NAT dependency, live instrumentation, provider-payload reconstruction, or best-effort export of unsupported structural or content fields is included. Preserving known step-level usage while omitting an unprovable aggregate is exact representation, not best-effort conversion.
Review order
This is the egress follow-on to #2737 and can be read while that PR is open. After #2737 lands,
mainwill be merged into this branch again and the shared ATIF boundary will be reconciled.atif_v1_7.pyshould then leave this diff after preserving #2737'sfinal_metrics.total_stepsvalidation.atif_json.pywill retain the small strict JSON equality helper used by the exporter, so reviewers should inspect that egress-specific delta rather than assume every shared-file change disappears.nemo_gym/atif_export.pynemo_gym/atif_json.pynemo_gym/cli/eval.py,nemo_gym/cli/main.py, docstests/unit_tests/test_atif_export.py, CLI testsSuggested pass: review the exporter first, then its small shared-boundary delta and wiring. The test file is intentionally large because unsupported shapes fail closed.
Validation
git diff --check: passed.ng_trajectorythrough this exporter and feat(eval): reverify Relay ATIF trajectories #2737's importer.1.0.The broader local unit run reported the same five non-ATIF failures listed on #2737. The affected exporter, importer, rollout, and CLI suites pass.
The earlier Codex → Relay → ATIF → Gym live qualification remains evidence for #2737; this offline exporter PR does not claim another provider run.
Checklist