Skip to content

fix(responses-converter): strip strict from function tools before engine dispatch - #2892

Merged
ananthsub merged 5 commits into
mainfrom
pthombre/fix-strict-strip
Aug 31, 2026
Merged

fix(responses-converter): strip strict from function tools before engine dispatch#2892
ananthsub merged 5 commits into
mainfrom
pthombre/fix-strict-strip

Conversation

@pthombre

Copy link
Copy Markdown
Contributor

Summary

The openai-2.44 pin rework (65129dd, #2456) stopped popping strict from function tool definitions in responses_to_chat_completion_create_params. This restores the pre-#2456 strip.

strict is an OpenAI-hosted enforcement flag with no Chat Completions semantics on self-hosted engines: vLLM 0.25.1's FunctionDefinition keeps it, and chat templates that render unknown function keys (e.g. Nemotron's render_extra_keys) inject <strict>True</strict> into every <function> block. That perturbs the prompt of every tool-bearing request off-distribution.

Observed impact

In RL training (jobs 6707880/6712632 vs baseline 6682989): ~17% of sequences per step ran away to the max-token cap, collected reward-0 truncation penalties, and were masked by sequence logprob error (1e4–1e14). Restoring the strip returns the engine request to byte-parity with the pre-#2456 tree.

Question for reviewers

Was dropping the strip in #2456 intentional? If strict support on self-hosted engines is planned, a config flag may be preferable — but unconditional stripping matches pre-#2456 behavior and strict has no Chat Completions semantics today.

Changes

  • nemo_gym/responses_converter.py: pop strict from function tool dicts before building NeMoGymChatCompletionToolParam
  • tests/unit_tests/test_responses_converter.py: assert strict does not reach the engine params

🤖 Generated with Claude Code

…ine dispatch

The openai-2.44 pin rework (65129dd, #2456) stopped popping strict from
function tool definitions in responses_to_chat_completion_create_params.
strict is an OpenAI-hosted enforcement flag with no Chat Completions
semantics on self-hosted engines: vLLM 0.25.1's FunctionDefinition keeps it,
and chat templates that render unknown function keys (e.g. Nemotron's
render_extra_keys) inject <strict>True</strict> into every <function> block.
That perturbs the prompt of every tool-bearing request off-distribution.

Observed in RL training (jobs 6707880/6712632 vs baseline 6682989): ~17% of
sequences per step run away to the max-token cap, collect reward-0
truncation penalties, and are masked by sequence logprob error (1e4-1e14).
Restoring the pre-2456 strip returns the engine request to byte-parity with
the pre-rebase tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranav Thombre <pthombre@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 31, 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.

@pthombre

Copy link
Copy Markdown
Contributor Author

/ok to test be5128c

@pthombre

Copy link
Copy Markdown
Contributor Author

/claude review

@ananthsub ananthsub added the r0.6.0 Auto-cherrypick to release branch. Apply before merge; cherrypick happens after merge. label Aug 31, 2026
@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

SHIP — no reliability concerns.

Focused fix: drops the strict key from function tool definitions on the Responses→Chat conversion path (responses_converter.py:307). The reasoning holds up — on self-hosted engines, chat templates that render unknown function-definition keys (Nemotron's render_extra_keys) inject strict into the prompt, perturbing every tool-bearing request off-policy. That's a silent prompt-corruption risk across the training/eval majority path, so dropping it is the right call.

One thing worth noting (NOTE, author's call): the drop is unconditional, so it also affects the OpenAI-hosted path via inference_provider, where Chat Completions strict does carry real structured-output enforcement semantics. This restores pre-openai-2.44-pin behavior as the comment says, so it's a deliberate and defensible tradeoff — the self-hosted prompt-perturbation harm outweighs losing hosted schema enforcement — but if any downstream eval relied on hosted strict structured outputs, that enforcement is now gone silently. Test updated to assert the drop; correctness of verify()/scoring is untouched. LGTM.

The multistep tests asserted the post-#2456 payload in which strict was
forwarded to the engine. With the converter strip restored, the input
tool definitions still carry strict=True but the dispatched tools must
not — update the expectations to verify the strip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranav Thombre <pthombre@nvidia.com>
@pthombre

Copy link
Copy Markdown
Contributor Author

/ok to test 8135c93

Comment thread nemo_gym/responses_converter.py Outdated
Per review: strict is valid in both the Responses and Chat Completions
APIs and carries real schema-enforcement semantics on OpenAI-hosted
providers, so the converter keeps it. vLLM is the component that
mishandles it (FunctionDefinition keeps the unknown key and chat
templates like Nemotron's render_extra_keys inject it into the prompt),
so strip it in the vllm_model server instead:

- revert the responses_converter pop and its test to upstream
- add VLLMModel._strip_hosted_only_tool_fields, applied in
  _preprocess_chat_completion_create_params and in the
  use_completions_api path (which branches before preprocessing and
  renders tools through apply_chat_template locally)
- keep the multistep test expectations asserting strict never reaches
  the engine; add a direct unit test for the helper

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranav Thombre <pthombre@nvidia.com>
@pthombre

Copy link
Copy Markdown
Contributor Author

/claude review

@pthombre

Copy link
Copy Markdown
Contributor Author

/ok to test 134c3db

Comment thread responses_api_models/vllm_model/tests/test_app.py
@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

HOLD — the production change is sound, but two test edits will break CI.

The fix itself (app.py) is correct. Stripping OpenAI-hosted-only strict at both vLLM boundaries — _preprocess_chat_completion_create_params (chat path) and _chat_completions_via_completions_api (completions path) — is the right place. Rendering an unknown strict key into the prompt via render_extra_keys would perturb every tool-bearing request, so this is a real correctness fix for Nemotron-style templates. Guards (.get("tools") or [], .get("function") or {}, type=="function" check) are safe against missing/custom tools, and the dedicated unit test covers them.

BLOCKER (inline): The edits to test_responses_multistep and test_responses_multistep_multiturn remove "strict": True from expected_sent_tools, but those tests mock chat_completions — the exact method the strip lives inside. On the /v1/responses path the strip is bypassed, and the responses→chat converter (responses_converter.py:302) keeps strict, so the captured outbound tools still carry it. The base tests had "strict": True here and were green; removing it makes the assertions mismatch and gym env test --resources-server vllm_model fail. Fix: revert those two expected_sent_tools edits. The new test_strip_hosted_only_tool_fields_pops_strict already covers the strip directly.

Note: the strip runs only on the real vLLM HTTP boundary. Per CLAUDE.md ("green unit tests alone are not enough"), a real rollout against a Nemotron template would confirm the prompt no longer carries strict — worth doing before merge, but not a blocker on its own.

pthombre and others added 2 commits August 31, 2026 12:36
The multistep tests monkeypatch VLLMModel.chat_completions itself, so
they capture the converter output before _preprocess_chat_completion_create_params
runs — with the converter reverted to keep strict, that payload
correctly still carries it. Restore the upstream expectations there and
verify the strip where it now lives: a direct preprocess-hook test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pranav Thombre <pthombre@nvidia.com>
@pthombre

Copy link
Copy Markdown
Contributor Author

/ok to test dffac65

@pthombre

Copy link
Copy Markdown
Contributor Author

/claude review

@claude

claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

SHIP — LGTM, no reliability concerns.

_strip_hosted_only_tool_fields correctly removes the OpenAI-hosted-only strict flag at the vLLM boundary, preventing chat templates with render_extra_keys (Nemotron) from injecting the unknown key into every tool-bearing prompt.

Verified coverage of both tool-forwarding paths:

  • chat_completions()_preprocess_chat_completion_create_params (app.py:501) strips before create_chat_completion (app.py:801).
  • /v1/completions path (app.py:1110) strips before _render_messages_via_chat_templateapply_chat_template.

No third path hands tools to vLLM. The strip mutates the same body_dict that is forwarded, gates on type == "function", and tolerates missing/None tools/function. Hosted-provider strict semantics are preserved since this only runs in the vLLM model server. Tests assert real behavior on both the static helper and the preprocess integration.

@ananthsub
ananthsub merged commit 023b370 into main Aug 31, 2026
32 checks passed
@ananthsub
ananthsub deleted the pthombre/fix-strict-strip branch August 31, 2026 20:00
ananthsub pushed a commit that referenced this pull request Aug 31, 2026
…e engine dispatch (2892)` into `r0.6.0` (#2899)

beep boop [🤖]: Hi @pthombre 👋,

    we've cherry picked #2892 into  for you! 🚀

    Please review and approve this cherry pick by your convenience!

Signed-off-by: Pranav Thombre <pthombre@nvidia.com>
Signed-off-by: NeMo Bot <nemo-bot@nvidia.com>
Co-authored-by: Pranav Thombre <pthombre@nvidia.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

r0.6.0 Auto-cherrypick to release branch. Apply before merge; cherrypick happens after merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants