Skip to content

fix(translator): emit max_completion_tokens for gpt-5/o-series in *-to-openai request builders - #3657

Open
AVK-Git-giddy wants to merge 1 commit into
decolua:masterfrom
AVK-Git-giddy:fix/to-openai-max-completion-tokens
Open

AVK-Git-giddy wants to merge 1 commit into
decolua:masterfrom
AVK-Git-giddy:fix/to-openai-max-completion-tokens

Conversation

@AVK-Git-giddy

Copy link
Copy Markdown

Problem

OpenAI's Chat Completions API rejects max_tokens for gpt-5.x and hyphenated o-series models:

400 invalid_request_error / unsupported_parameter
"Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead."

The claude→openai request builder emits max_tokens unconditionally (open-sse/translator/request/claude-to-openai.js:22-24), and gemini→openai + antigravity→openai do the same. The GitHub executor already maps the param at its own layer (requiresMaxCompletionTokens, /gpt-5|o[134]-/i), but the native openai api-key provider goes through the default executor, which never touches token params — so every Claude-format request (Claude Code, etc.) routed to an openai/gpt-5.x model dies at the provider with the 400 above. We hit this in production routing Claude-format traffic to BYOK gpt-5 keys (observed live 2026-08-31).

Fix

  • Hoist the exact match the GitHub executor uses into translator/formats/maxTokens.js as requiresMaxCompletionTokens, next to adjustMaxTokens.
  • In the three *-to-openai request builders (claude-to-openai.js, gemini-to-openai.js, antigravity-to-openai.js), emit the adjusted budget as max_completion_tokens when the model matches, max_tokens otherwise.

Requests are born with the right param for every executor, and no downstream path regresses:

  • executors/github.js maps only when transformed.max_tokens !== undefined → clean no-op on the new field.
  • translator/request/openai-responses.js already prefers max_completion_tokens when mapping to max_output_tokens.
  • executors/opencode.js normalizes either field to max_output_tokens; codex.js / grok-cli.js delete both; qoder.js reads both.
  • handlers/chatCore/requestDetail.js OPTIONAL_PARAMS already lists both; nothing in open-sse/handlers/ reads .max_tokens post-translation.

The GitHub executor's private copy of the regex is left untouched to keep the diff minimal — happy to point it at the shared helper here if you prefer.

Tests

New tests/unit/to-openai-max-completion-tokens.test.js: the predicate plus each builder × (gpt-5.x, o3-mini, legacy gpt-4o) — 9/9 pass. The full tests/ suite has a byte-identical failure set with and without this change (the failures on a fresh clone of master are credential/environment-bound, none related).

Related

🤖 Generated with Claude Code

…openai request builders

OpenAI's Chat Completions API rejects max_tokens for gpt-5.x and
hyphenated o-series models with 400 unsupported_parameter ("Use
'max_completion_tokens' instead"). The claude-to-openai,
gemini-to-openai and antigravity-to-openai request builders emitted
max_tokens unconditionally, so translated requests routed to the native
openai api-key provider failed at the provider for those models. The
GitHub executor already maps the param (requiresMaxCompletionTokens);
hoist the same match next to adjustMaxTokens and apply it in all three
builders so requests are born with the right param for every executor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
luqman-v1 pushed a commit to luqman-v1/9router-go that referenced this pull request Sep 2, 2026
…#3657)

Port decolua/9router#3657: OpenAI rejects max_tokens for gpt-5.x and
o1/o3/o4 with 'Use max_completion_tokens instead'. Hoist matcher
/gpt-5|o[134]-/i into requiresMaxCompletionTokens() and emit the
correct param in TranslateClaudeToOpenAI. Add MaxCompletionTokens
field to OpenAIRequest. Verified with gpt-5/o1-mini/gpt-4o tests.
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