Skip to content

fix(openai): strip reasoning_effort when GPT-5.x models carry function tools - #7101

Merged
diegosouzapw merged 2 commits into
release/v3.8.49from
fix/port-issue-2540-gpt5-reasoning-tools
Jul 17, 2026
Merged

diegosouzapw merged 2 commits into
release/v3.8.49from
fix/port-issue-2540-gpt5-reasoning-tools

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Summary

Raw api.openai.com Chat Completions rejects GPT-5.x reasoning models that carry both function tools and an active reasoning_effort with HTTP 400:

Function tools with reasoning_effort are not supported for <model> in /v1/chat/completions.
Please use /v1/responses instead.

This is hit by any coding client (e.g. Claude Code via an OpenAI-compatible bridge) that sends tool calls alongside an explicit "Thinking" level. The dashboard offers no reasoning_effort:"none" override, so it couldn't be worked around client-side either.

Root cause

OmniRoute already has a guard for a related case: shouldForceResponsesUpstream (open-sse/executors/forceResponsesUpstream.ts) reroutes openai-compatible-* connections carrying MCP/tool_search* tool shapes onto /v1/responses. But that guard only fires for openai-compatible-* providers — the plain, built-in openai provider (raw api.openai.com Chat Completions) has no equivalent guard, so GPT-5.x + function tools + active reasoning still reached the upstream 400 on /v1/chat/completions.

Fix

New stripGpt5ReasoningWhenTools() in open-sse/services/gpt5SamplingGuard.ts (alongside the existing stripGpt5SamplingWhenReasoning), wired into chatCore.ts right after the sampling guard: for the openai provider + gpt-5* models, when the request carries a non-empty function tools array AND an active reasoning_effort/reasoning.effort (anything other than "none"), it strips the reasoning field(s) so the request succeeds on /v1/chat/completions instead of 400ing.

Scoped narrowly:

  • provider === "openai" only (raw Chat Completions surface) — other providers/executors are untouched.
  • gpt-5* models only.
  • Only fires when function tools are present AND reasoning is active; reasoning_effort:"none" and tool-less requests pass through unchanged.

Test plan

  • TDD: tests/unit/gpt5-tools-reasoning-guard.test.ts written first, confirmed failing (stripGpt5ReasoningWhenTools did not exist) before the fix, now passing (9/9).
  • Existing tests/unit/gpt5-sampling-guard.test.ts still green (10/10) — no regression to the sibling sampling guard.
  • npm run typecheck:core — clean.
  • npx eslint open-sse/services/gpt5SamplingGuard.ts open-sse/handlers/chatCore.ts tests/unit/gpt5-tools-reasoning-guard.test.ts — clean.

Thanks @techsolutionmta for the detailed report.

…from 9router#2540)

Raw api.openai.com Chat Completions rejects GPT-5.x reasoning models that carry both function tools and an active reasoning_effort with HTTP 400 ("Function tools with reasoning_effort are not supported ... Please use /v1/responses instead"). The existing forceResponsesUpstream guard only reroutes openai-compatible-* connections carrying MCP/tool_search tool shapes; the plain openai provider had no equivalent guard, so gpt-5.x models used with a coding client (function tools + any explicit reasoning effort) still hit the upstream 400. Add stripGpt5ReasoningWhenTools() (gpt5SamplingGuard.ts), wired into chatCore.ts alongside the existing sampling guard, to drop reasoning_effort/reasoning when function tools are present and reasoning is active, letting the request succeed on /v1/chat/completions.

Reported-by: Tech Solution (@techsolutionmta) (decolua/9router#2540)
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@diegosouzapw

Copy link
Copy Markdown
Owner Author

Validado no probe: revertendo gpt5SamplingGuard.ts o import quebra (função não existe) e os 9 testes caem; com o fix, todos passam. Wiring em chatCore.ts consistente com o guard irmão (stripGpt5SamplingWhenReasoning), escopo corretamente restrito a provider==='openai'+gpt-5*. Único ponto de atenção (não bloqueante): ao stripar o campo aninhado 'reasoning' (vs. o 'reasoning_effort' string), o código apaga o objeto inteiro — se ele algum dia carregar sub-campos além de 'effort' eles somem junto. É o mesmo padrão já usado no guard irmão, então não é regressão introduzida por este PR — só deixo registrado para avaliação futura. Merge-ready.

stripGpt5ReasoningWhenTools gated on provider+model-name alone, so once
#7242 routes the public GPT-5.6 family to /v1/responses (targetFormat
"openai-responses", which natively supports tools + reasoning), the two
PRs would compose into the worst of both worlds: routed to the endpoint
that supports reasoning, but reasoning stripped anyway. Pass the
request's already-resolved targetFormat into the guard and skip the
strip whenever it is not going out over /chat/completions, so the
guard tracks the actual upstream surface instead of a model-name list
that would need updating for every future GPT-5.x family.

Reported-by: Tech Solution (@techsolutionmta) (decolua/9router#2540)
@diegosouzapw
diegosouzapw merged commit 6cdb77a into release/v3.8.49 Jul 17, 2026
14 checks passed
@diegosouzapw
diegosouzapw deleted the fix/port-issue-2540-gpt5-reasoning-tools branch July 19, 2026 21:01
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…n tools (diegosouzapw#7101)

* fix(openai): strip reasoning_effort when GPT-5.x tools present (port from 9router#2540)

Raw api.openai.com Chat Completions rejects GPT-5.x reasoning models that carry both function tools and an active reasoning_effort with HTTP 400 ("Function tools with reasoning_effort are not supported ... Please use /v1/responses instead"). The existing forceResponsesUpstream guard only reroutes openai-compatible-* connections carrying MCP/tool_search tool shapes; the plain openai provider had no equivalent guard, so gpt-5.x models used with a coding client (function tools + any explicit reasoning effort) still hit the upstream 400. Add stripGpt5ReasoningWhenTools() (gpt5SamplingGuard.ts), wired into chatCore.ts alongside the existing sampling guard, to drop reasoning_effort/reasoning when function tools are present and reasoning is active, letting the request succeed on /v1/chat/completions.

Reported-by: Tech Solution (@techsolutionmta) (decolua/9router#2540)

* fix(openai): scope reasoning-strip guard to /chat/completions only

stripGpt5ReasoningWhenTools gated on provider+model-name alone, so once
diegosouzapw#7242 routes the public GPT-5.6 family to /v1/responses (targetFormat
"openai-responses", which natively supports tools + reasoning), the two
PRs would compose into the worst of both worlds: routed to the endpoint
that supports reasoning, but reasoning stripped anyway. Pass the
request's already-resolved targetFormat into the guard and skip the
strip whenever it is not going out over /chat/completions, so the
guard tracks the actual upstream surface instead of a model-name list
that would need updating for every future GPT-5.x family.

Reported-by: Tech Solution (@techsolutionmta) (decolua/9router#2540)
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…n tools (diegosouzapw#7101)

* fix(openai): strip reasoning_effort when GPT-5.x tools present (port from 9router#2540)

Raw api.openai.com Chat Completions rejects GPT-5.x reasoning models that carry both function tools and an active reasoning_effort with HTTP 400 ("Function tools with reasoning_effort are not supported ... Please use /v1/responses instead"). The existing forceResponsesUpstream guard only reroutes openai-compatible-* connections carrying MCP/tool_search tool shapes; the plain openai provider had no equivalent guard, so gpt-5.x models used with a coding client (function tools + any explicit reasoning effort) still hit the upstream 400. Add stripGpt5ReasoningWhenTools() (gpt5SamplingGuard.ts), wired into chatCore.ts alongside the existing sampling guard, to drop reasoning_effort/reasoning when function tools are present and reasoning is active, letting the request succeed on /v1/chat/completions.

Reported-by: Tech Solution (@techsolutionmta) (decolua/9router#2540)

* fix(openai): scope reasoning-strip guard to /chat/completions only

stripGpt5ReasoningWhenTools gated on provider+model-name alone, so once
diegosouzapw#7242 routes the public GPT-5.6 family to /v1/responses (targetFormat
"openai-responses", which natively supports tools + reasoning), the two
PRs would compose into the worst of both worlds: routed to the endpoint
that supports reasoning, but reasoning stripped anyway. Pass the
request's already-resolved targetFormat into the guard and skip the
strip whenever it is not going out over /chat/completions, so the
guard tracks the actual upstream surface instead of a model-name list
that would need updating for every future GPT-5.x family.

Reported-by: Tech Solution (@techsolutionmta) (decolua/9router#2540)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant