From 5f886dc73a13cf1798a20f444aefb370797ae5d0 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 29 May 2026 00:58:01 -0300 Subject: [PATCH] fix(translator): strip safety_identifier in openai-responses cleanup (#2770) (#2809) Integrated into release/v3.8.6. --- CHANGELOG.md | 1 + open-sse/translator/request/openai-responses.ts | 3 +++ .../translator-openai-responses-req.test.ts | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b13475ba0f..428e911361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - **combo:** resolve custom `openai-compatible-responses-*` provider targets correctly when called via combo name — combo steps storing the internal UUID-prefixed provider id now match the provider node by id as well as by prefix, fixing 503 errors for users with custom providers used inside combos (#2778) - **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) - **fix(cli):** register openclaw in the CLI tool-detector so it appears in `omniroute status` alongside its existing API and config support ([#2833](https://github.com/diegosouzapw/OmniRoute/issues/2833)) diff --git a/open-sse/translator/request/openai-responses.ts b/open-sse/translator/request/openai-responses.ts index eadb2e8811..8b25bbb67e 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 f7e3587a98..ece658aa5d 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",