Skip to content

fix(conversational_tool_use): start the nested simulation stack, constrain judge JSON, report mask_sample - #4568

Merged
yaoyu-33 merged 3 commits into
mainfrom
qiaochuz/nvb-6847461-slash-harness-import
Oct 14, 2026
Merged

yaoyu-33 merged 3 commits into
mainfrom
qiaochuz/nvb-6847461-slash-harness-import

Conversation

@qiaochuz-nv

Copy link
Copy Markdown
Contributor

What does this PR do?

The nested conversational tool-use environment (resources_servers/conversational_tool_use_simulation with the
conversational_tool_use/simulation agent) now starts from a source checkout, and its rollouts are graded and
masked correctly. Before this PR its stack never came up: every model server bound to the agent crashed during
startup. Behind that crash were two more defects, which this PR also fixes. The trajectory judge often returned JSON
that failed validation, and the server's mask decision never reached the mask_sample field that Gym and trainers read.

Fixes NVBug 6847461. Closes #4497.

Cause

  1. Startup crash. ServerClient.assistant_message_header (nemo_gym/server_utils.py:825, added by [1/5] feat(opencode): link assistant messages to captured model calls #3711)
    imports responses_api_agents.{harness} with the raw config key. Nested harnesses are keyed by directory path,
    so conversational_tool_use/simulation raises ModuleNotFoundError in policy_model setup
    (base_responses_api_model.py:286). The venv blocker in the same NVBug (a pin to nemo-gym==<version>) was
    already fixed by fix(cli): treat servers nested three levels deep as editable installs #4464.
  2. Unparseable judge output. The judge prompt contains Evaluation.model_json_schema() as text, and the reply
    is parsed with model_validate_json, with no decoding constraint (since feat: add end-to-end synthetic conversational tool-use generation and rollouts #2111). With the QA model (temperature 1.0), the
    step-0 judgement failed all 3 attempts in 3 of 3 runs. Replies either wrapped the instance in
    {"properties": {...}} or left a string unquoted, so the row got verification_generation_error.
  3. Mask dropped. feat: add end-to-end synthetic conversational tool-use generation and rollouts #2111 reported the mask decision only as instance_config.mask_sample. feat(core): report infrastructure failure from verify, not just a reward #2611 later made
    BaseVerifyResponse.mask_sample the contract. This environment was never migrated, so simulator, tool, and judge
    failures reached rollouts with mask_sample: false and reward 0.

Fix

  1. Map / to . before importing the harness package.
  2. When judge_responses_create_params.text is not configured, the judge requests Responses
    text.format (json_schema) for Evaluation, or for UserAgentEnvironmentEvaluation in combined mode. A
    configured text still wins. openai_model forwards it to the provider; the chat converter maps it to
    response_format.
  3. The resources server's /verify and the agent's transient-failure response set top-level mask_sample.
    instance_config is removed, and the checked-in example_rollouts.jsonl is migrated. The README states the
    masking rule.

Rejected: catching ImportError in assistant_message_header. It would hide a declared header from any nested
harness, and the new test fails on it.

Before the fix

Gym 4a101eeb5 (main), QA case conversational_tool_use_simulation:

./run.sh test conversational_tool_use_simulation --ref 4a101eeb5 --no-ai-log
nemo_gym/base_responses_api_model.py", line 286, in setup_webserver
    assistant_message_header=self.server_client.assistant_message_header(self.config.name),
nemo_gym/server_utils.py", line 825, in assistant_message_header
ModuleNotFoundError: No module named 'responses_api_agents.conversational_tool_use/simulation'
RuntimeError: Process `policy_model` finished unexpectedly!
  ERROR (30.2s): StackStartupError: `gym env start` exited with code 1 during child startup
conversational_tool_use_simulation: 0/3 passed in 90.6s

With only the import fixed (intermediate commit), the stack started but the judge failed to parse:

FAIL (78.7s): original conversation has no generation or verification failures: expected [], got ['verification_generation_error']
generation_error = 2 validation errors for Evaluation
success
  Field required [type=missing, input_value={'properties': {'success'...tion', 'type': 'object'}, input_type=dict]

After the fix

Gym 29d57f66c (this branch before a rebase onto 977583d3d; the one new main commit touches no file in this diff), same command:

./run.sh test conversational_tool_use_simulation --ref 29d57f66c --no-ai-log
PASS   373.9s  the canonical one-example conversation uses live roles and verifies session disposal and grading gates
PASS    79.1s  enable_llm_judge=false scores a complete conversation 1.0 without calling the judge
PASS    49.8s  complete_trajectory_combined_evaluation judges the whole conversation once with per-role verdicts
conversational_tool_use_simulation: 3/3 passed in 502.8s

Validation

  • QA testcase conversational_tool_use_simulation (3 cases: canonical message-level judge, enable_llm_judge=false,
    complete_trajectory_combined_evaluation): 0/3 on main (stack startup), 3/3 on this branch. The canonical case also replays the recorded conversation and checks the step limit (excessive_length, not masked), ###TRANSFER### with both scope labels, and rejection of malformed history (25 direct resources-server calls).
  • Judge-format probe against the QA endpoint, 6 calls each: prompt-only schema gave 5/6 valid replies, text.format
    gave 6/6.
  • After the rebase (head a3a6ab1f4), I reran test_server_utils.py (173), gym env test for the server (78), and the agent tests (36): all pass.
  • tests/unit_tests/test_server_utils.py: 173 passed. The new test_model_header_imports_nested_harness_key fails on
    main, and also fails on a catch-ImportError fix.
  • gym env test --resources-server conversational_tool_use_simulation: 78 passed. New judge structured-output
    tests cover both judge paths and a configured text. 8 mask assertions now check top-level mask_sample, and the
    example-rollout contract test checks the masking rule.
  • responses_api_agents/conversational_tool_use/simulation/tests: 36 passed. 4 mask assertions moved to mask_sample.
  • tests/unit_tests (Mac): same 43 pre-existing failures as main, plus the new passing test. In one full
    run, test_slurm_script.py::…multi_node_chain_survives_symmetric_run_entrypoint also failed (a bash subprocess
    with a 10 s timeout). It passes 3/3 alone on both trees and 226/226 as a file, and this diff does not touch it.
  • pre-commit run --all-files: passed, no changes
  • Rollout evidence: the QA case above runs live rollouts. The policy, simulator, and judge all use
    nvidia/nvidia/nemotron-3.5-super-vl-preview through openai_model.

Compatibility

  • Consumers that read instance_config.mask_sample from this environment, such as older NeMo RL, no longer get a
    mask. They should read top-level mask_sample, like every other environment.
  • The judge request now includes text.format. A provider that rejects json_schema structured output can turn it
    off by setting judge_responses_create_params.text.format.type=text.
  • With a live judge, rows that it scores as user or tool failures are now masked. Before this PR they counted as
    reward-0 policy samples.

🤖 Generated with Claude Code

qiaochuz-nv and others added 3 commits October 13, 2026 10:47
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: qiaochuz <qiaochuz@nvidia.com>
… trajectory judge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: qiaochuz <qiaochuz@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: qiaochuz <qiaochuz@nvidia.com>
@qiaochuz-nv qiaochuz-nv added bug Something isn't working QA_fix Fix implemented by a QA team member area:environment Individual environments, benchmarks, verifiers, and environment-specific resources servers area:agent Agent harnesses and Responses API agent behavior area:core Shared APIs, servers, telemetry, health, and registries needs-review PR is ready for code review and waiting on a reviewer complexity:medium Single-domain change with interacting parts or a moderate review surface labels Oct 13, 2026
@copy-pr-bot

copy-pr-bot Bot commented Oct 13, 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.

@qiaochuz-nv
qiaochuz-nv requested a review from yaoyu-33 October 13, 2026 17:49
@qiaochuz-nv

Copy link
Copy Markdown
Contributor Author

/ok to test a3a6ab1

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

LGTM. Reviewed the nested import, judge JSON schema, and top-level mask_sample changes; 250 focused tests passed locally. The Hermes model_error conformance check still needs to be cleared before merge.

@yaoyu-33 yaoyu-33 added ready-to-merge PR is approved, current, and waiting for final CI or merge and removed needs-review PR is ready for code review and waiting on a reviewer labels Oct 13, 2026
@yaoyu-33
yaoyu-33 added this pull request to the merge queue Oct 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/38486441840

@qiaochuz-nv

Copy link
Copy Markdown
Contributor Author

On the PR’s current head a3a6ab1, CI fails model_error because that base doesn’t have #4270. after rebase it work.

Merged via the queue into main with commit 07b9e2a Oct 14, 2026
59 of 61 checks passed
@yaoyu-33
yaoyu-33 deleted the qiaochuz/nvb-6847461-slash-harness-import branch October 14, 2026 00:05
jkyi-nvidia added a commit that referenced this pull request Oct 15, 2026
…_tool_use_{stage}

The four conversational tool-use agents were nested under
responses_api_agents/conversational_tool_use/, which Gym's flat
responses_api_agents/<name> layout does not expect. #4464 and #4568 added
framework workarounds for the nesting.

Move each stage to a top-level agent directory:

  conversational_tool_use/domain_generation       -> conversational_tool_use_domain_generation
  conversational_tool_use/policy_tool_generation  -> conversational_tool_use_policy_tool_generation
  conversational_tool_use/scenario_generation     -> conversational_tool_use_scenario_generation
  conversational_tool_use/simulation              -> conversational_tool_use_simulation

- Rename harness keys in agent configs and the resources-server config
  (including allowed_agents), and update imports, hardcoded paths in
  prepare.py, tests, scripts, and docs.
- Point each agent's requirements.txt at the repo root with ../../.
- Drop the orphaned parent __init__.py, requirements.txt, and index README
  (the resources-server README already carries the same component table
  and Prepare Assets section).
- Revert the nested-path workarounds in nemo_gym/server_utils.py and
  nemo_gym/cli/setup_command.py, with their tests; nothing else is nested.

Metric names under the conversational_tool_use/ namespace are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>
linj-glitch pushed a commit to linj-glitch/Gym that referenced this pull request Oct 16, 2026
…act when the opening user message fails (NVIDIA-NeMo#4641)

## What does this PR do?

Upstreams NVIDIA-NeMo#3111 to `main`. NVIDIA-NeMo#3111 was merged only into
`super-v3.5-posttraining`.

When the user simulator fails on the opening user message, the
conversational tool-use server now opens the
conversation with the scenario's `reason_for_contact`. Before, it ended
the trajectory before the policy spoke. A
failure here means the simulator raised an error, or its first message
already contained `###STOP###` or
`###TRANSFER###`.

### Cause

In both failure cases the server marked the trajectory invalid or
terminal at turn 0. The agent then returned a
response with an empty `output` list. NeMo RL `main` cannot represent a
sample with no policy tokens. It raises before
any mask is read, so one such row can fail the rollout batch.

### Fix

- When no user message exists yet, the server uses
`customer_scenario.reason_for_contact` as the first user message.
If that is empty or contains `###STOP###` or `###TRANSFER###`, it uses a
generic request
(`DEFAULT_FIRST_USER_MESSAGE`). A `###STOP###`/`###TRANSFER###` message
triggers the fallback only when nothing
precedes it in the conversation; a generation error triggers it whenever
no user message exists yet.
- Each fallback is appended to
`result.source_artifacts.first_user_message_fallbacks`, so each fallback
stays visible
  in the rollout result.
- Failures on later user turns keep the existing invalid/terminal
behavior.
- The new config flag `fallback_first_user_message` controls this. It
defaults to `true`, and the canonical YAML
  declares it. `test_config.py` asserts the canonical value.
- The README configuration table and `docs/rollout.md` document the
flag.

No issue is linked. The cause and fix are recorded in NVIDIA-NeMo#3111, which this
PR ports.

## Validation

Ran on this branch (base `main` at `974d1a921`). NVIDIA-NeMo#3111 cherry-picked
cleanly. Since `super-v3.5-posttraining` split from `main`,
`main` changed these files in 3 commits. The closest is NVIDIA-NeMo#4568 (judge
`text.format`, top-level `mask_sample`). None of them
touch the opening-message path.

- `ng_test
+entrypoint=resources_servers/conversational_tool_use_simulation
+should_validate_data=true`: 85 passed,
  and data validation passed.
- The 3 new fallback tests fail against `main`'s `app.py` and pass on
this branch:
  - `test_first_user_stop_falls_back_to_reason_for_contact`
  - `test_first_user_generation_error_falls_back_to_reason_for_contact`
-
`test_first_user_fallback_uses_default_when_reason_for_contact_is_unusable`

The 3 guard tests pass on both. They cover the flag turned off, for the
stop and error cases, and an error on a
  later turn.
- 3 more tests check that a fallback row is scored and not masked
through `verify()`, that the flag defaults to
`true`, and that a `###STOP###` first message is retried before the
fallback replaces it. Each fails if the behavior
  it covers is broken.
- Parity with `super-v3.5-posttraining`: the fallback code is identical
to that branch at every Gym commit the
training run below used (`e978899b7`, `34b99364`, `14317ecb5`). A local
run of the real agent and resource server
through `verify()` on 23 cases (user simulator, policy and judge
stubbed) matches that branch on reward, mask and
messages with the flag at its default, `true` or `false`. With `false`
it also matches `main` without this PR.
- `pre-commit run --all-files`: passed, no changes.
- DCO: every commit is signed off.

### Rollout evidence

The fix ran in a Nemotron 3.5 Super RLVR training run,
`super35-journey-rlvr-v43-yifuw-001`

([W&B](https://forge.coreweave.com/wandb/nvidia/super-v3p5-posttraining?nw=j0nn1gshftc)).
The run used NeMo RL
`super-v3.5-posttraining`, whose Gym pin has included NVIDIA-NeMo#3111 since
2026-09-04. The run's data blend includes
`conversational_tool_use_simulation`.

- The fallback fired twice. Each time, the user simulator returned only
a reasoning item and no message, so the server
logged `first user message fallback: message_generation_error` (`A
response does not contain a message output`).
Without the fix, each of these rows would have produced an empty agent
output. That branch's NeMo RL also has
RL NVIDIA-NeMo#4001, which turns such rows into masked placeholders, so this run
shows the fallback trains safely rather than
  that it prevented a failure. NeMo RL `main` doesn't have NVIDIA-NeMo#4001.
- Training kept going after both. The first fallback happened during
step 35 of W&B run `dvp5tc0d`, which went on to
step 38. The second happened during step 41 of `0g5thn9y`, which went on
to step 51. The run reached step 78.

## Compatibility

- This changes default behavior. On `main`, a first-turn simulator
failure produces a row with an empty agent output,
labeled `user_failure` and masked. With this PR, that row becomes a
normal conversation that starts from the
scenario's own request, and it is scored and trained on. If the judge
rejects that first message itself, the row is
masked as a user failure, as before. Set `fallback_first_user_message:
false` to restore the old
  behavior.
- Rows whose prefilled history already contains a user message never
fall back.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: Yi-Fu Wu <yifuw@nvidia.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
jkyi-nvidia added a commit that referenced this pull request Oct 16, 2026
…_tool_use_{stage}

The four conversational tool-use agents were nested under
responses_api_agents/conversational_tool_use/, which Gym's flat
responses_api_agents/<name> layout does not expect. #4464 and #4568 added
framework workarounds for the nesting.

Move each stage to a top-level agent directory:

  conversational_tool_use/domain_generation       -> conversational_tool_use_domain_generation
  conversational_tool_use/policy_tool_generation  -> conversational_tool_use_policy_tool_generation
  conversational_tool_use/scenario_generation     -> conversational_tool_use_scenario_generation
  conversational_tool_use/simulation              -> conversational_tool_use_simulation

- Rename harness keys in agent configs and the resources-server config
  (including allowed_agents), and update imports, hardcoded paths in
  prepare.py, tests, scripts, and docs.
- Point each agent's requirements.txt at the repo root with ../../.
- Drop the orphaned parent __init__.py, requirements.txt, and index README
  (the resources-server README already carries the same component table
  and Prepare Assets section).
- Revert the nested-path workarounds in nemo_gym/server_utils.py and
  nemo_gym/cli/setup_command.py, with their tests; nothing else is nested.

Metric names under the conversational_tool_use/ namespace are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Junkeun Yi <jkyi@nvidia.com>

This branch was previously deployed

1 inactive deployment
public — a3a6ab1f Deployed Oct 13, 2026 by copy-pr-bot[bot] via release / finalize / notify #5195
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 area:core Shared APIs, servers, telemetry, health, and registries area:environment Individual environments, benchmarks, verifiers, and environment-specific resources servers bug Something isn't working complexity:medium Single-domain change with interacting parts or a moderate review surface QA_fix Fix implemented by a QA team member ready-to-merge PR is approved, current, and waiting for final CI or merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NVBug 6847461] Fix slash-named harness import that crashes nested conversational simulation stack

2 participants