From 731e766136cfc18abc94a8987a4821d85577cda5 Mon Sep 17 00:00:00 2001 From: Leonid Bondarenko <37963306+lordavadon2@users.noreply.github.com> Date: Thu, 21 May 2026 21:52:34 +0300 Subject: [PATCH] fix(embeddings): strip stale Content-Encoding headers from upstream response (#2477) Integrated into release/v3.8.2 --- open-sse/handlers/embeddings.ts | 5 +- tests/unit/embeddings-handler.test.ts | 72 +++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/open-sse/handlers/embeddings.ts b/open-sse/handlers/embeddings.ts index 0eebf52bc6..4bb0b70e76 100644 --- a/open-sse/handlers/embeddings.ts +++ b/open-sse/handlers/embeddings.ts @@ -23,6 +23,7 @@ import { createRequestLogger } from "../utils/requestLogger.ts"; import { isDetailedLoggingEnabled } from "@/lib/db/detailedLogs"; import { getCallLogPipelineCaptureStreamChunks } from "@/lib/logEnv"; import { toJsonErrorPayload } from "@/shared/utils/upstreamError"; +import { stripStaleEncodingHeaders } from "../utils/upstreamResponseHeaders.ts"; interface ClientRawRequest { endpoint: string; @@ -215,7 +216,7 @@ export async function handleEmbedding({ success: false, status: response.status, error: errorText, - headers: response.headers, + headers: stripStaleEncodingHeaders(response.headers), }; } @@ -266,7 +267,7 @@ export async function handleEmbedding({ return { success: true, data: normalizedResponse, - headers: response.headers, + headers: stripStaleEncodingHeaders(response.headers), }; } catch (err) { if (log) { diff --git a/tests/unit/embeddings-handler.test.ts b/tests/unit/embeddings-handler.test.ts index 87e23f8e0b..873d0d1ea2 100644 --- a/tests/unit/embeddings-handler.test.ts +++ b/tests/unit/embeddings-handler.test.ts @@ -215,3 +215,75 @@ test("handleEmbedding surfaces upstream failures", async () => { globalThis.fetch = originalFetch; } }); + +test("handleEmbedding strips content-encoding header on success path", async () => { + const originalFetch = globalThis.fetch; + + globalThis.fetch = async () => + new Response( + JSON.stringify({ + data: [{ object: "embedding", embedding: [0.1, 0.2], index: 0 }], + usage: { prompt_tokens: 5, total_tokens: 5 }, + }), + { + status: 200, + headers: { + "content-type": "application/json", + "content-encoding": "gzip", + "content-length": "512", + "transfer-encoding": "chunked", + "x-request-id": "abc123", + }, + } + ); + + try { + const result = await handleEmbedding({ + body: { model: "openai/text-embedding-3-small", input: "test" }, + credentials: { apiKey: "openai-key" }, + log: null, + }); + + assert.equal(result.success, true); + assert.strictEqual(result.headers.get("content-encoding"), null); + assert.strictEqual(result.headers.get("content-length"), null); + assert.strictEqual(result.headers.get("transfer-encoding"), null); + assert.strictEqual(result.headers.get("x-request-id"), "abc123"); + assert.strictEqual(result.headers.get("content-type"), "application/json"); + } finally { + globalThis.fetch = originalFetch; + } +}); + +test("handleEmbedding strips content-encoding header on error path", async () => { + const originalFetch = globalThis.fetch; + + globalThis.fetch = async () => + new Response("upstream error", { + status: 502, + headers: { + "content-type": "text/plain", + "content-encoding": "gzip", + "content-length": "42", + "transfer-encoding": "chunked", + "x-trace-id": "trace-456", + }, + }); + + try { + const result = await handleEmbedding({ + body: { model: "openai/text-embedding-3-small", input: "test" }, + credentials: { apiKey: "openai-key" }, + log: null, + }); + + assert.equal(result.success, false); + assert.equal(result.status, 502); + assert.strictEqual(result.headers.get("content-encoding"), null); + assert.strictEqual(result.headers.get("content-length"), null); + assert.strictEqual(result.headers.get("transfer-encoding"), null); + assert.strictEqual(result.headers.get("x-trace-id"), "trace-456"); + } finally { + globalThis.fetch = originalFetch; + } +});