From 40cc555b6875d9a99182a3cf52d7079b5eb6772d Mon Sep 17 00:00:00 2001 From: Hernan Javier Ardila Sanchez Date: Sun, 24 May 2026 03:07:51 +0200 Subject: [PATCH] fix(combo): preserve omniModel tag in streaming output for round-trip context pinning (#2646) Integrated into release/v3.8.3 --- open-sse/services/combo.ts | 42 +------------------------ tests/unit/combo-routing-engine.test.ts | 9 +++--- 2 files changed, 5 insertions(+), 46 deletions(-) diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts index de6b428533..43341052e3 100644 --- a/open-sse/services/combo.ts +++ b/open-sse/services/combo.ts @@ -1611,47 +1611,7 @@ export async function handleComboChat({ }, }); - // FIX #585: Sanitize outbound stream — strip tags from - // visible content so they don't leak to the user. The tag is still - // present in the full response for round-trip context pinning, but - // we clean it from each SSE chunk's content field before delivery. - // - // IMPORTANT: Use a SEPARATE TextDecoder from the transform stream above. - // The transform stream's decoder accumulates UTF-8 state; reusing it here - // would corrupt multi-byte characters split across chunk boundaries. - const sanitizeDecoder = new TextDecoder(); - const sanitize = new TransformStream({ - transform(chunk, controller) { - const text = sanitizeDecoder.decode(chunk, { stream: true }); - if (text) { - if (text.includes("")) { - const cleaned = text.replaceAll( - /(?:\\n|\n|\r)*[^<]+<\/omniModel>(?:\\n|\n|\r)*/g, - "" - ); - if (cleaned) controller.enqueue(encoder.encode(cleaned)); - } else { - controller.enqueue(encoder.encode(text)); - } - } - }, - flush(controller) { - const tail = sanitizeDecoder.decode(); - if (tail) { - if (tail.includes("")) { - const cleaned = tail.replaceAll( - /(?:\\n|\n|\r)*[^<]+<\/omniModel>(?:\\n|\n|\r)*/g, - "" - ); - if (cleaned) controller.enqueue(encoder.encode(cleaned)); - } else { - controller.enqueue(encoder.encode(tail)); - } - } - }, - }); - - const transformedStream = res.body.pipeThrough(transform).pipeThrough(sanitize); + const transformedStream = res.body.pipeThrough(transform); // Add model info as response header for clients that support it const headers = new Headers(res.headers); headers.set("X-OmniRoute-Model", modelStr); diff --git a/tests/unit/combo-routing-engine.test.ts b/tests/unit/combo-routing-engine.test.ts index 6148b1a996..cd9edfd90e 100644 --- a/tests/unit/combo-routing-engine.test.ts +++ b/tests/unit/combo-routing-engine.test.ts @@ -1764,7 +1764,7 @@ test("handleComboChat context cache protection pins the model and tags tool-call ); }); -test("handleComboChat context cache protection sanitizes streamed text tags from client output", async () => { +test("handleComboChat context cache protection preserves omniModel tag in streamed output for round-trip pinning", async () => { const result = await handleComboChat({ body: { stream: true, messages: [{ role: "user", content: "stream it" }] }, combo: { @@ -1790,7 +1790,7 @@ test("handleComboChat context cache protection sanitizes streamed text tags from assert.equal(result.ok, true); assert.equal(result.headers.get("X-OmniRoute-Model"), "openai/gpt-4o-mini"); assert.match(text, /hello world/); - assert.doesNotMatch(text, //); + assert.match(text, /openai\/gpt-4o-mini<\/omniModel>/); }); test("handleComboChat context cache protection injects a hidden tag for tool-call-only streams", async () => { @@ -1818,7 +1818,7 @@ test("handleComboChat context cache protection injects a hidden tag for tool-cal const text = await result.text(); assert.equal(result.ok, true); assert.match(text, /"finish_reason":"tool_calls"/); - assert.doesNotMatch(text, //); + assert.match(text, /openai\/gpt-4o-mini<\/omniModel>/); }); test("handleComboChat context cache protection flushes cleanly when a stream ends without content", async () => { @@ -1842,8 +1842,7 @@ test("handleComboChat context cache protection flushes cleanly when a stream end assert.equal(result.ok, true); assert.equal(result.headers.get("X-OmniRoute-Model"), "openai/gpt-4o-mini"); assert.match(text, /data: \[DONE\]/); - assert.match(text, /"content":""/); - assert.doesNotMatch(text, //); + assert.match(text, /"content":"openai\/gpt-4o-mini<\/omniModel>"/); }); test("handleComboChat round-robin resolves nested combos and returns inactive when every target is skipped", async () => {