From bd65eeb0450502dbcd2d26e29d673d3730c53be5 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Mon, 6 Jul 2026 19:24:08 -0300 Subject: [PATCH] fix(executors): strip client_metadata for NVIDIA requests (port from 9router#1887) (#6411) strip client_metadata for NVIDIA (port #1887) (net +1/-0, test OK). Integrated into release/v3.8.46. --- CHANGELOG.md | 1 + open-sse/executors/default.ts | 9 ++++++--- ...executor-default-strip-client-metadata.test.ts | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5211b391e0..01d60ea0b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ ### 🐛 Bug Fixes +- **fix(executors):** strip the OpenAI-Codex/Claude-CLI `client_metadata` passthrough field for **NVIDIA** requests. NVIDIA's OpenAI-compatible wrapper rejects it with `400 Unsupported parameter`, the same class already handled for `cerebras`/`mistral`; `nvidia` (executor `default`) was missing from the strip allowlist so Codex/Claude-Code passthrough requests 400'd. Regression guard: `tests/unit/executor-default-strip-client-metadata.test.ts` (+nvidia case). (thanks @phidinhmanh) - **fix(translator):** strip the Claude-style `thinking` field for **NVIDIA `z-ai/glm-5.2`**. NVIDIA's OpenAI-compatible wrapper 400s on `thinking` (a Claude-format client routed here leaves a `thinking:{type:"adaptive"}`); the existing strip rule only dropped `reasoning`. Same class already handled for `minimax-m2.7`. Regression guard: `tests/unit/nvidia-minimax-thinking-strip.test.ts` (+glm-5.2 case). (thanks @phidinhmanh) - **fix(translator):** suppress the streamed `` close marker for the **Antigravity IDE** client. On thinking-only turns Antigravity rendered a bare `` as the sole visible content, tripping its loop-detection and wasting requests. Antigravity's UA (`vscode/ (Antigravity/)`) is added to the marker-suppress allowlist (alongside OpenCode); Claude Code / Cursor still get the marker, and `x-omniroute-thinking-marker: on` force-restores it. Regression guard: `tests/unit/think-close-marker-suppress-5245.test.ts`. (thanks @abdofallah) - **fix(executors):** strip nested `reasoning_content` from messages for **Mistral**. Mistral's API returns `422 extra_forbidden` when an assistant message carries `reasoning_content` (replayed thinking from a prior turn, e.g. via the Codex `/responses` path); the generic top-level 400 field-downgrade retry never covered the nested per-message field. `DefaultExecutor` now strips it for provider `mistral` only, so DeepSeek (which requires replayed `reasoning_content`) is unaffected. Regression guard: `tests/unit/mistral-strip-reasoning-content-1649.test.ts`. (thanks @xxy9468615) diff --git a/open-sse/executors/default.ts b/open-sse/executors/default.ts index c643a9e65e..b2697a349e 100644 --- a/open-sse/executors/default.ts +++ b/open-sse/executors/default.ts @@ -549,8 +549,9 @@ export class DefaultExecutor extends BaseExecutor { withDefaults = this.defaultResponsesTextFormat(withDefaults); // Port of decolua/9router commit d652300e: - // Cerebras returns 400 (wrong_api_format) and Mistral returns 422 - // (extra_forbidden) when the forwarded body carries `client_metadata` + // Cerebras returns 400 (wrong_api_format), Mistral returns 422 + // (extra_forbidden), and NVIDIA's OpenAI-compatible wrapper returns 400 + // (Unsupported parameter) when the forwarded body carries `client_metadata` // (an OpenAI Codex / Claude CLI passthrough field with no equivalent on // these upstreams). Strip it before sending downstream. Other providers // (notably `openai` / `codex`) intentionally keep it. @@ -558,7 +559,9 @@ export class DefaultExecutor extends BaseExecutor { withDefaults && typeof withDefaults === "object" && !Array.isArray(withDefaults) && - (this.provider === "cerebras" || this.provider === "mistral") && + (this.provider === "cerebras" || + this.provider === "mistral" || + this.provider === "nvidia") && Object.prototype.hasOwnProperty.call(withDefaults, "client_metadata") ) { const withoutClientMetadata = { ...(withDefaults as Record) }; diff --git a/tests/unit/executor-default-strip-client-metadata.test.ts b/tests/unit/executor-default-strip-client-metadata.test.ts index 5430991806..b8b48ab4dc 100644 --- a/tests/unit/executor-default-strip-client-metadata.test.ts +++ b/tests/unit/executor-default-strip-client-metadata.test.ts @@ -54,6 +54,21 @@ test("DefaultExecutor.transformRequest strips client_metadata for mistral", () = ); }); +test("DefaultExecutor.transformRequest strips client_metadata for nvidia (port from 9router#1887)", () => { + const executor = new DefaultExecutor("nvidia"); + const out = executor.transformRequest( + "any", + bodyWithClientMetadata(), + STREAM, + CREDENTIALS + ) as Record; + assert.equal( + Object.prototype.hasOwnProperty.call(out, "client_metadata"), + false, + "nvidia forward body must not contain client_metadata" + ); +}); + test("DefaultExecutor.transformRequest preserves client_metadata for other providers", () => { // Sanity check: the strip must be scoped, not global. Openai keeps it // (codex flow needs it), the cerebras/mistral strip is the only carve-out.