diff --git a/CHANGELOG.md b/CHANGELOG.md index b56334bcfb54..618641566d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - **oauth:** repair native Google loopback callback flow and support remote callbacks via state matching on 127.0.0.1 (#2796 โ€” thanks @akarray) - **combos:** fix combo handling so transient 429 rate limit errors do not poison or persist the rate limited state for the same-provider connection (#2800 โ€” thanks @apoapostolov) - **gemini:** translate signature-less Gemini thinking model tool calls to text parts to prevent `400 "missing thought_signature"` errors (#2801 โ€” thanks @herjarsa) +- **translator:** strip `safety_identifier` from `/v1/responses` body before forwarding to Chat Completions upstream; fixes LobeHub-originated `400` errors (#2770) - **warning-cleanup:** relax node engine constraint to `>=22.0.0` and clean dependencies (keeping `marked-terminal` to prevent TUI REPL crash) (#2792 โ€” thanks @oyi77) ### ๐Ÿงน Chores diff --git a/open-sse/translator/request/openai-responses.ts b/open-sse/translator/request/openai-responses.ts index eadb2e8811ca..8b25bbb67e02 100644 --- a/open-sse/translator/request/openai-responses.ts +++ b/open-sse/translator/request/openai-responses.ts @@ -345,6 +345,9 @@ export function openaiResponsesToOpenAIRequest( } } delete result.reasoning; + // Strip Responses-API-only fields that Chat Completions rejects with 400. + // safety_identifier is sent by LobeHub and has no Chat Completions equivalent (#2770). + delete result.safety_identifier; return result; } diff --git a/tests/unit/translator-openai-responses-req.test.ts b/tests/unit/translator-openai-responses-req.test.ts index f7e3587a98d8..ece658aa5de3 100644 --- a/tests/unit/translator-openai-responses-req.test.ts +++ b/tests/unit/translator-openai-responses-req.test.ts @@ -216,6 +216,23 @@ test("Responses -> Chat passes through when background flag is unset or false (n } }); +test("Responses -> Chat strips safety_identifier (LobeHub #2770)", () => { + // LobeHub sends safety_identifier in Responses API bodies. Chat Completions rejects it + // with HTTP 400. The translator must strip it in the Responses-API cleanup block. + const result = openaiResponsesToOpenAIRequest( + "gpt-4o", + { + input: [{ role: "user", content: [{ type: "input_text", text: "hi" }] }], + safety_identifier: "sid-xyz", + }, + false, + null + ) as Record; + + assert.equal(result.safety_identifier, undefined, "safety_identifier must be stripped before forwarding to Chat Completions"); + assert.ok(Array.isArray(result.messages), "translation must still produce messages"); +}); + test("Chat -> Responses converts messages, tool calls, tool outputs, tools and pass-through params", () => { const result = openaiToOpenAIResponsesRequest( "gpt-4o",