From 9c70ab93d38e8ceba75128caa7ce1cd1e3b41d42 Mon Sep 17 00:00:00 2001 From: Dizzle <112548150+maxmad64bis@users.noreply.github.com> Date: Sun, 25 Oct 2026 14:38:55 +0100 Subject: [PATCH] fix(sse): keep explicit thinking type over connection default explicit thinking type survives the connection default; missing type still receives it --- ...m-thinking-type-ecrase-client-explicite.md | 1 + open-sse/executors/glm.ts | 23 ++++++-- tests/unit/glm-thinking-type-explicit.test.ts | 54 +++++++++++++++++++ 3 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 changelog.d/fixes/18114-glm-thinking-type-ecrase-client-explicite.md create mode 100644 tests/unit/glm-thinking-type-explicit.test.ts diff --git a/changelog.d/fixes/18114-glm-thinking-type-ecrase-client-explicite.md b/changelog.d/fixes/18114-glm-thinking-type-ecrase-client-explicite.md new file mode 100644 index 000000000000..4486b8971a7d --- /dev/null +++ b/changelog.d/fixes/18114-glm-thinking-type-ecrase-client-explicite.md @@ -0,0 +1 @@ +- **fix(sse):** keep an explicit client thinking type instead of overwriting it with the connection default ([#18114](https://github.com/cheaper-inference/OmniRoute/pull/18114)) — thanks @maxmad64bis diff --git a/open-sse/executors/glm.ts b/open-sse/executors/glm.ts index 581849a43ac2..e7c798591ee8 100644 --- a/open-sse/executors/glm.ts +++ b/open-sse/executors/glm.ts @@ -129,16 +129,24 @@ function isGlmThinkingModel(model: string): boolean { */ const GLM_THINKING_DEFAULT_MAX_TOKENS = 131072; -function applyGlmRequestDefaults(body: unknown, defaults?: JsonRecord | null): unknown { +function applyGlmRequestDefaults( + body: unknown, + defaults?: JsonRecord | null, + clientThinkingType?: unknown +): unknown { const record = asRecord(body); if (!record || !defaults) return body; const next = { ...(applyProviderRequestDefaults(record, defaults) as JsonRecord) }; const thinkingType = typeof defaults.thinkingType === "string" ? defaults.thinkingType : null; - if (thinkingType && next.thinking === undefined) { - next.thinking = { type: thinkingType }; - } else if (thinkingType && asRecord(next.thinking)?.type === "enabled") { + // Apply the connection default only when the client did not set a thinking + // type of its own; other thinking fields are merged and preserved. + if ( + thinkingType && + clientThinkingType === undefined && + (next.thinking === undefined || asRecord(next.thinking)) + ) { next.thinking = { ...asRecord(next.thinking), type: thinkingType }; } @@ -310,8 +318,13 @@ export class GlmExecutor extends DefaultExecutor { stream: boolean, credentials: ProviderCredentials ) { + const clientThinkingType = asRecord(asRecord(body)?.thinking)?.type; const cleanedBody = super.transformRequest(model, body, stream, credentials); - return applyGlmRequestDefaults(cleanedBody, this.config.requestDefaults as JsonRecord | null); + return applyGlmRequestDefaults( + cleanedBody, + this.config.requestDefaults as JsonRecord | null, + clientThinkingType + ); } transformForTransport( diff --git a/tests/unit/glm-thinking-type-explicit.test.ts b/tests/unit/glm-thinking-type-explicit.test.ts new file mode 100644 index 000000000000..e5494b0461b6 --- /dev/null +++ b/tests/unit/glm-thinking-type-explicit.test.ts @@ -0,0 +1,54 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +import { GlmExecutor } from "../../open-sse/executors/glm.ts"; + +type ThinkingBody = { thinking?: unknown }; + +test("GlmExecutor keeps explicit enabled thinking type over adaptive default", () => { + const executor = new GlmExecutor("glmt"); + const body = { + messages: [{ role: "user", content: "hi" }], + thinking: { type: "enabled", budget_tokens: 1000 }, + }; + + const transformed = executor.transformRequest("glm-5.1", body, true, { + apiKey: "glm-key", + }) as ThinkingBody; + + assert.deepEqual(transformed.thinking, { type: "enabled", budget_tokens: 1000 }); +}); + +test("GlmExecutor applies adaptive default when thinking type is missing", () => { + const executor = new GlmExecutor("glmt"); + + const withoutThinking = executor.transformRequest( + "glm-5.1", + { messages: [{ role: "user", content: "hi" }] }, + true, + { apiKey: "glm-key" } + ) as ThinkingBody; + assert.deepEqual(withoutThinking.thinking, { type: "adaptive", budget_tokens: 24_576 }); + + const typeless = executor.transformRequest( + "glm-5.1", + { messages: [{ role: "user", content: "hi" }], thinking: { budget_tokens: 1000 } }, + true, + { apiKey: "glm-key" } + ) as ThinkingBody; + assert.deepEqual(typeless.thinking, { type: "adaptive", budget_tokens: 1000 }); +}); + +test("GlmExecutor leaves thinking untouched without connection default", () => { + const executor = new GlmExecutor("glm"); + const body = { + messages: [{ role: "user", content: "hi" }], + thinking: { type: "enabled", budget_tokens: 1000 }, + }; + + const transformed = executor.transformRequest("glm-5.1", body, false, { + apiKey: "glm-key", + }) as ThinkingBody; + + assert.deepEqual(transformed.thinking, { type: "enabled", budget_tokens: 1000 }); +});