From 63958bb19567e84dcde4bd4bb5253b6529e3cb2b Mon Sep 17 00:00:00 2001 From: Wauplin Date: Wed, 6 Aug 2025 14:39:33 +0200 Subject: [PATCH 1/3] Forward user headers --- src/routes/responses.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/routes/responses.ts b/src/routes/responses.ts index efaae7d..fa4a058 100644 --- a/src/routes/responses.ts +++ b/src/routes/responses.ts @@ -35,6 +35,17 @@ class StreamingError extends Error { type IncompleteResponse = Omit; const SEQUENCE_NUMBER_PLACEHOLDER = -1; +// All headers are forwarded by default, except these ones. +const NOT_FORWARDED_HEADERS = [ + "accept", + "accept-encoding", + "authorization", + "connection", + "content-length", + "content-type", + "host", +]; + export const postCreateResponse = async ( req: ValidatedRequest, res: ExpressResponse @@ -169,6 +180,11 @@ async function* innerRunStream( return; } + // Forward headers (except authorization handled separately) + const defaultHeaders = Object.fromEntries( + Object.entries(req.headers).filter(([key]) => !NOT_FORWARDED_HEADERS.includes(key.toLowerCase())) + ) as Record; + // Return early if not supported param if (req.body.reasoning?.summary && req.body.reasoning?.summary !== "auto") { throw new Error(`Not implemented: only 'auto' summary is supported. Got '${req.body.reasoning?.summary}'`); @@ -429,7 +445,7 @@ async function* innerRunStream( do { previousMessageCount = currentMessageCount; - for await (const event of handleOneTurnStream(apiKey, payload, responseObject, mcpToolsMapping)) { + for await (const event of handleOneTurnStream(apiKey, payload, responseObject, mcpToolsMapping, defaultHeaders)) { yield event; } @@ -499,11 +515,13 @@ async function* handleOneTurnStream( apiKey: string | undefined, payload: ChatCompletionCreateParamsStreaming, responseObject: IncompleteResponse, - mcpToolsMapping: Record + mcpToolsMapping: Record, + defaultHeaders: Record ): AsyncGenerator { const client = new OpenAI({ baseURL: process.env.OPENAI_BASE_URL ?? "https://router.huggingface.co/v1", apiKey: apiKey, + defaultHeaders, }); const stream = await client.chat.completions.create(payload); let previousInputTokens = responseObject.usage?.input_tokens ?? 0; From 1703c3d1131778f0bfe9b036f3d0e104cbb9defa Mon Sep 17 00:00:00 2001 From: Wauplin Date: Wed, 6 Aug 2025 14:47:24 +0200 Subject: [PATCH 2/3] complete list --- src/routes/responses.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/routes/responses.ts b/src/routes/responses.ts index fa4a058..0b0884b 100644 --- a/src/routes/responses.ts +++ b/src/routes/responses.ts @@ -44,6 +44,12 @@ const NOT_FORWARDED_HEADERS = [ "content-length", "content-type", "host", + "keep-alive", + "te", + "trailer", + "trailers", + "transfer-encoding", + "upgrade", ]; export const postCreateResponse = async ( From f8ae9683bdc6b1114705c8fd33649563139c6dce Mon Sep 17 00:00:00 2001 From: Wauplin Date: Wed, 6 Aug 2025 14:50:23 +0200 Subject: [PATCH 3/3] use set for O(1) search --- src/routes/responses.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/responses.ts b/src/routes/responses.ts index 0b0884b..d61206b 100644 --- a/src/routes/responses.ts +++ b/src/routes/responses.ts @@ -36,7 +36,7 @@ type IncompleteResponse = Omit, @@ -188,7 +188,7 @@ async function* innerRunStream( // Forward headers (except authorization handled separately) const defaultHeaders = Object.fromEntries( - Object.entries(req.headers).filter(([key]) => !NOT_FORWARDED_HEADERS.includes(key.toLowerCase())) + Object.entries(req.headers).filter(([key]) => !NOT_FORWARDED_HEADERS.has(key.toLowerCase())) ) as Record; // Return early if not supported param