From f2d94957c8291967be091bce56cb73a07f2528b6 Mon Sep 17 00:00:00 2001 From: Chewji <126886556+Chewji9875@users.noreply.github.com> Date: Thu, 13 Aug 2026 10:48:35 +0700 Subject: [PATCH] fix(ollama-cloud): map xhigh reasoning effort to max (#10160) --- .../providers/registry/ollama-cloud/index.ts | 15 ++++++- open-sse/executors/base/reasoningEffort.ts | 15 ++++++- .../base-executor-sanitize-effort.test.ts | 41 +++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/open-sse/config/providers/registry/ollama-cloud/index.ts b/open-sse/config/providers/registry/ollama-cloud/index.ts index 37f560fa0d..05b28df65f 100644 --- a/open-sse/config/providers/registry/ollama-cloud/index.ts +++ b/open-sse/config/providers/registry/ollama-cloud/index.ts @@ -27,7 +27,20 @@ export const ollama_cloudProvider: RegistryEntry = { { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", supportsReasoning: true }, { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", supportsReasoning: true }, { id: "kimi-k2.6", name: "Kimi K2.6" }, - { id: "glm-5.1", name: "GLM 5.1" }, + // Ollama Cloud accepts low|medium|high|max|none and rejects xhigh, so the + // explicit supportsXHighEffort:false makes the sanitizer map xhigh → max. + { + id: "glm-5.1", + name: "GLM 5.1", + supportsReasoning: true, + supportsXHighEffort: false, + }, + { + id: "glm-5.2", + name: "GLM 5.2", + supportsReasoning: true, + supportsXHighEffort: false, + }, // #3110: MiniMax M3 via Ollama { id: "minimax-m3", name: "MiniMax M3", contextLength: 1048576, supportsVision: true }, { id: "minimax-m2.7", name: "MiniMax M2.7" }, diff --git a/open-sse/executors/base/reasoningEffort.ts b/open-sse/executors/base/reasoningEffort.ts index 5edf1188ae..8356e6e131 100644 --- a/open-sse/executors/base/reasoningEffort.ts +++ b/open-sse/executors/base/reasoningEffort.ts @@ -154,7 +154,8 @@ export function supportsMaxEffortForProvider(provider: string, model: string): b // upstream. Scoped to opencode-go deliberately: OpenRouter's DeepSeek path // (pi#4055) is the documented inverse and expects xhigh, not max. // Ollama Cloud also accepts literal max (for example GLM 5.2 supports - // low|medium|high|max|none) and rejects xhigh. + // low|medium|high|max|none) and rejects xhigh; xhigh is mapped to max by the + // provider guard in sanitizeReasoningEffortForProvider. const isOpencodeGoDeepSeek = (provider === "opencode-go" || provider === "opencode-zen") && resolvedModelId.toLowerCase().includes("deepseek"); @@ -284,6 +285,18 @@ export function sanitizeReasoningEffortForProvider( return writeEffortValue(b, "max", c); } + // Ollama Cloud accepts low|medium|high|max|none and rejects xhigh. Map + // xhigh → max (its literal top tier) before the generic xhigh handling so + // passthrough (unregistered) models are covered too — the registry opt-out + // only covers known models. + if (provider === "ollama-cloud" && effortStr === "xhigh") { + log?.info?.( + "REASONING_SANITIZE", + `${provider}/${modelStr}: mapped reasoning_effort xhigh → max` + ); + return writeEffortValue(b, "max", c); + } + // Native DeepSeek (api.deepseek.com) — V4 thinking mode uses the native // {low, high, max} vocabulary on Flash and {high, max} on Pro. OmniRoute's // internal top tier xhigh maps to DeepSeek's literal max. Pro's unsupported diff --git a/tests/unit/base-executor-sanitize-effort.test.ts b/tests/unit/base-executor-sanitize-effort.test.ts index 2a712106c5..ba7ce28955 100644 --- a/tests/unit/base-executor-sanitize-effort.test.ts +++ b/tests/unit/base-executor-sanitize-effort.test.ts @@ -118,6 +118,47 @@ test("sanitizeReasoningEffortForProvider: Ollama Cloud preserves nested max", () assert.equal((result as Record).reasoning.summary, "auto"); }); +test("sanitizeReasoningEffortForProvider: Ollama Cloud maps registry model xhigh → max", () => { + const log = makeLog(); + const body = { + model: "glm-5.2", + reasoning_effort: "xhigh", + messages: [{ role: "user", content: "hi" }], + }; + const result = sanitizeReasoningEffortForProvider(body, "ollama-cloud", "glm-5.2", log) as Record< + string, + unknown + >; + assert.notEqual(result, body, "must return a new object when mutating"); + assert.equal(result.reasoning_effort, "max"); + assert.equal(result.model, "glm-5.2", "other fields preserved"); + assert.ok( + log.messages.some(([tag, m]) => tag === "REASONING_SANITIZE" && /xhigh → max/.test(m)), + "logs the xhigh → max mapping" + ); +}); + +test("sanitizeReasoningEffortForProvider: Ollama Cloud maps passthrough unknown model xhigh → max", () => { + const log = makeLog(); + const body = { + model: "some-future-glm-model", + reasoning_effort: "xhigh", + messages: [{ role: "user", content: "hi" }], + }; + const result = sanitizeReasoningEffortForProvider( + body, + "ollama-cloud", + "some-future-glm-model", + log + ) as Record; + assert.notEqual(result, body, "must return a new object when mutating"); + assert.equal(result.reasoning_effort, "max"); + assert.ok( + log.messages.some(([tag, m]) => tag === "REASONING_SANITIZE" && /xhigh → max/.test(m)), + "logs the xhigh → max mapping" + ); +}); + test("sanitizeReasoningEffortForProvider: OpenRouter DeepSeek passes max through (new default)", () => { const log = makeLog(); const body = {