[Bug] Responses combo treats completed text as success despite tool_choice=required (NVIDIA target)
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 30/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- typescript
- Domain
- ai-infra-agents, api, backend
Research direction
Start with src/server/responses/core-combo.ts and src/server/responses/combo-stream-preflight.ts, then trace how the Responses request's required tool choice reaches the upstream adapter and how completed child responses are validated. Add isolated tests for required calls, zero-call completions, safe retries before output, and committed streaming output, including the 151-tool and continuation cases. Done means a completed response with no required function call is never accepted as a successful tool round, while streaming is not replayed after commitment.
Written by the indexing model from the issue text.
Description
Client or integration
Direct HTTP/API client. Also affects Codex CLI when its RHODIZ combo selects the NVIDIA target.
Area
Proxy and routing; Streaming; Tools / MCP / web search.
Summary
A Responses request with a declared function and tool_choice: "required" can return HTTP 200, status: "completed" but zero function_call items when the selected target is NVIDIA Nemotron Ultra. The combo accepts that response as a successful target outcome, instead of identifying that the required-tool contract was unsatisfied. Consequently a Codex tool round-trip may make no progress, despite the client seeing a successful completion.
This differs from the existing issues about forwarding tool-choice settings on the Chat Completions path (e.g. #5211) and DeepSeek rejecting forced choices (#4631). This is a successful-but-semantically-invalid Responses result that a combo marks terminal-success.
Reproduction
Use a separately authorized OpenCodex instance with a configured NVIDIA provider; credential details are not needed in the report.
{
"model": "nvidia/nvidia/nemotron-3-ultra-550b-a55b",
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Call get_fixture_marker with key=status. Do not answer with text before the tool result."
}
]
}
],
"tools": [
{
"type": "function",
"name": "get_fixture_marker",
"description": "Return a fixed test value",
"parameters": {
"type": "object",
"properties": { "key": { "type": "string" } },
"required": ["key"],
"additionalProperties": false
}
}
],
"tool_choice": "required",
"store": false,
"stream": false,
"max_output_tokens": 750
}
Observed against the installed OpenCodex 2.89.0:
| Route | HTTP | Response status | Output item types | Required function? |
|---|---|---|---|---|
| NVIDIA direct | 200 | completed | reasoning, message |
No (0 calls) |
| GPT-6.1 Sol direct | 200 | completed | function_call |
Yes (1 call) |
| Claude Opus 5.5 direct | 200 | completed | function_call |
Yes (1 call) |
With the exact same one-function request sent to the 11-target combo alias RHODIZ, the combo selected NVIDIA and returned completed without a function call. This reproduced 2/2 times. A separate 151-declared-function test reproduced the same outcome via the combo, whereas the GPT-6.1 Sol route completed the required function call and the subsequent function_call_output continuation 2/2 times.
An earlier 25-turn text-response stress test with 127/128/129/151 declared (but unused) tools had 25/25 HTTP 200 success. That test did not establish actual tool-call correctness: a real Codex CLI read-tool test subsequently showed intermittent failures on the combo (1/5 passes in one batch) versus GPT-6.1 Sol direct (5/5). This comparison is corroborating context, not proof of which exact model served every failed Codex CLI turn.
Version
@bitkyc08/opencodex 2.89.0, Codex CLI 0.163.0.
Operating system
Linux, x86_64 (VM; distribution not independently verified).
Provider and model
NVIDIA / nvidia/nemotron-3-ultra-550b-a55b; controls: openai/gpt-6.1-sol and anthropic/claude-opus-5-5.
Logs or error output
In src/server/responses/core-combo.ts, the combo returns a child immediately when response.ok after its stream preflight and success bookkeeping. The existing preflight in src/server/responses/combo-stream-preflight.ts detects certain retryable zero-output terminals, but does not enforce the caller's required-function selection contract for a completed 200 response. The upstream adapter may be disregarding required; the proxy currently does not detect that and makes the misbehavior a successful combo result.
See the same-issue streaming follow-up comment for per-event SSE counts. No raw user prompts or access logs are included.
Screenshots and supporting files
A minimal synthetic JSON reproducer and comparative results are embedded above. The follow-up comment documents streaming response.completed with zero calls and the isolated test counts. No sensitive attachments.
Redacted configuration
{
"combos": {
"rhodiz-coding-primary": {
"alias": "RHODIZ",
"strategy": "failover",
"targets": [
{"provider": "nvidia", "model": "nvidia/nemotron-3-ultra-550b-a55b"},
{"provider": "openai", "model": "gpt-6.1-sol"},
{"provider": "anthropic", "model": "claude-opus-5-5"}
]
}
}
}
The snippet is illustrative: the actual 11-target combo contains additional routes, whose details do not affect the minimal required-function reproduction.
Expected behavior / proposed acceptance
- Preserve explicit
tool_choice: "required"or a named mandatory tool at the upstream adapter boundary, and verify the upstream wire really supports it. - For a fully buffered, successful non-streaming Responses child, a
completedresponse with zero requiredfunction_callitems must not be reported as a valid completed tool round. Either safely select an eligible alternative before any output/effects, or return a typed compatibility error rather than a false success. - Streaming handling must remain conservative: never replay or hop after client-visible output, a tool side effect, or ambiguous commitment. Do not turn every text-only 200 into a retry. Handle only requests explicitly requiring a tool and only if it is provably safe.
- Preserve complete tool catalogs (including 151 tools), tool call IDs,
function_call_outputcontinuation, caller model identity, authorization boundaries, and cancellation. - Add isolated positive/negative tests for direct and combo paths, no output vs. committed output, one required tool and 151 declared tools. A passing end-to-end sequence is: request with
required→ exactly one declared function call → caller-provided function result → completed answer using that result.
Privacy and provenance
Reported by the affected operator via their connected GitHub account. Examples use synthetic markers only; no account IDs, internal addresses, tokens, raw user prompts, or private logs are attached. The production installation has not been modified as part of this upstream report.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
- Dominant language
- TypeScript
- Stars
- 16.9k
- Forks
- 1.3k
- Avg merge
- 7h 46m
- Merged PRs (30d)
- 638
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from lidge-jun/opencodex
-
[Bug]: Business Premium seat plan self_serve_business_prolite is counted at baseline capacity weight 1 instead of 5Possibly taken A pull request linked to this issue is open or already merged. Openaccount-pool bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
lidge-jun/opencodex#7440 · 2 comments ·
Maintainers usually reply within 1 day
-
[Docs]: translate the #7395 OpenGateway and Mistral provider-guide sectionsPossibly taken @MannXo claimed this 3 days ago. Opendocumentation
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
lidge-jun/opencodex#7405 · 3 comments ·
Maintainers usually reply within 1 day
-
[Bug]: resolveCodexRuntime test depends on the host's installed CodexPossibly taken A pull request linked to this issue is open or already merged. Openbug cli
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
lidge-jun/opencodex#7403 · 2 comments ·
Maintainers usually reply within 1 day
-
account-pool enhancement needs-design proxy
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
lidge-jun/opencodex#6737 · 1 comment ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 45/100
Maintainers usually reply within 1 day
All issues in lidge-jun/opencodex
Similar issues
-
area:agent-view area:tui bug platform:macos
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
anthropics/claude-code#104558 ·
Maintainers usually reply within 1 day
-
updater
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
UniversityOfHelsinkiCS/oodikone#5141 ·
-
maintanance
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
inmanta/web-console#7606 ·
Maintainers usually reply within 1 day
-
bug skills
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
equinor/fusion-framework#5846 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 74/100