From 970e14725be19213c5ac921add17e746cdd995ae Mon Sep 17 00:00:00 2001 From: OmniRoute Ops Date: Mon, 11 May 2026 13:23:57 +0000 Subject: [PATCH] feat(responses): degrade background mode to synchronous execution OpenAI Responses API supports background: true for async/deferred runs (returns 202 with response_id + GET /responses/ polling). Implementing the full background contract requires queue/poll infrastructure that OmniRoute does not have as a forward proxy. Previously the translator rejected such requests with HTTP 400 "Unsupported Responses API feature: background mode is not supported by omniroute". This breaks clients that opportunistically set background=true for long-running tasks (Capy Captain Pro, Codex agents) even when the underlying execution would complete in a normal HTTP timeout window. Degrade silently: strip the background flag and run synchronously. The client receives the full response in one round-trip with status=completed. Clients that strictly require the async contract still observe a completed response on the first poll and can adapt. The existing test that asserted background=true throws is split into two: - the unsupported-tool-type branch keeps the throw assertion - the background branch now asserts the flag is stripped and translation completes with the expected messages Co-Authored-By: Claude Opus 4.7 (1M context) --- .../translator/request/openai-responses.ts | 20 ++++++++---- .../translator-openai-responses-req.test.ts | 32 +++++++++++-------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/open-sse/translator/request/openai-responses.ts b/open-sse/translator/request/openai-responses.ts index 45beee74b0..97c0b77a27 100644 --- a/open-sse/translator/request/openai-responses.ts +++ b/open-sse/translator/request/openai-responses.ts @@ -77,13 +77,21 @@ export function openaiResponsesToOpenAIRequest( } } - if (root.background) { - throw unsupportedFeature( - "Unsupported Responses API feature: background mode is not supported by omniroute" - ); - } - const result: JsonRecord = { ...root }; + + // background: true requests a deferred Responses API run (the upstream + // returns 202 with response_id and the client polls GET /responses/). + // OmniRoute is a forward proxy that streams responses synchronously — + // implementing the queue/poll contract would require persistence and a + // separate retrieval surface. Degrade silently: strip the flag and run + // synchronously. The client receives the full response in one round-trip + // with status=completed. Clients that set background=true opportunistically + // (Capy Captain Pro, Codex agents) work unchanged. Clients that strictly + // require the async contract still observe a completed response on the + // first poll and can adapt. + if (result.background !== undefined) { + delete result.background; + } const messages: JsonRecord[] = []; result.messages = messages; diff --git a/tests/unit/translator-openai-responses-req.test.ts b/tests/unit/translator-openai-responses-req.test.ts index e66c4c7469..68863af30b 100644 --- a/tests/unit/translator-openai-responses-req.test.ts +++ b/tests/unit/translator-openai-responses-req.test.ts @@ -110,7 +110,7 @@ test("Responses -> Chat filters orphan tool outputs and supports role-based mess }); }); -test("Responses -> Chat rejects unsupported built-in tools and background mode", () => { +test("Responses -> Chat rejects unsupported built-in tools", () => { assert.throws( () => openaiResponsesToOpenAIRequest( @@ -124,20 +124,26 @@ test("Responses -> Chat rejects unsupported built-in tools and background mode", ), (error: any) => error.statusCode === 400 && error.errorType === "unsupported_feature" ); +}); - assert.throws( - () => - openaiResponsesToOpenAIRequest( - "gpt-4o", - { - input: [], - background: true, - }, - false, - null - ), - (error: any) => error.statusCode === 400 && error.errorType === "unsupported_feature" +test("Responses -> Chat strips background flag and degrades to synchronous execution", () => { + // Previously this threw 400 unsupported_feature. OmniRoute is a forward proxy + // and cannot host the deferred run + poll contract, so background=true is + // silently dropped and the request runs synchronously. Clients that set the + // flag opportunistically (Capy Captain Pro, Codex agents) work unchanged. + const result = openaiResponsesToOpenAIRequest( + "gpt-5.5", + { + input: [{ role: "user", content: [{ type: "input_text", text: "hi" }] }], + background: true, + }, + true, + null ); + const r = result as Record; + assert.equal(r.background, undefined, "background flag must be stripped from output"); + assert.ok(Array.isArray(r.messages), "translation must complete and produce messages"); + assert.equal((r.messages as unknown[]).length, 1, "user message must be preserved"); }); test("Chat -> Responses converts messages, tool calls, tool outputs, tools and pass-through params", () => {