From 7fb3b7558a669cfeec5f2d289294bf924a7c8776 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Fri, 7 Aug 2026 20:52:29 -0300 Subject: [PATCH] feat(providers): support max reasoning effort for opencode-zen DeepSeek models (#9318) (#9334) Merge-train validated (tip 6ce4effef8). Vitest failures confirmed as base-red (#9679). --- .../9318-opencode-zen-reasoning-effort.md | 1 + open-sse/executors/base/reasoningEffort.ts | 3 +- .../opencode-zen-reasoning-effort.test.ts | 113 ++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 changelog.d/features/9318-opencode-zen-reasoning-effort.md create mode 100644 tests/unit/opencode-zen-reasoning-effort.test.ts diff --git a/changelog.d/features/9318-opencode-zen-reasoning-effort.md b/changelog.d/features/9318-opencode-zen-reasoning-effort.md new file mode 100644 index 0000000000..49f8e78a84 --- /dev/null +++ b/changelog.d/features/9318-opencode-zen-reasoning-effort.md @@ -0,0 +1 @@ +- **feat(providers):** support max reasoning effort for opencode-zen DeepSeek models (#9318) diff --git a/open-sse/executors/base/reasoningEffort.ts b/open-sse/executors/base/reasoningEffort.ts index 04db3c69d9..6e1528caff 100644 --- a/open-sse/executors/base/reasoningEffort.ts +++ b/open-sse/executors/base/reasoningEffort.ts @@ -151,7 +151,8 @@ export function supportsMaxEffortForProvider(provider: string, model: string): b // Ollama Cloud also accepts literal max (for example GLM 5.2 supports // low|medium|high|max|none) and rejects xhigh. const isOpencodeGoDeepSeek = - provider === "opencode-go" && model.toLowerCase().includes("deepseek"); + (provider === "opencode-go" || provider === "opencode-zen") && + model.toLowerCase().includes("deepseek"); const isOllamaCloud = provider === "ollama-cloud"; const isMoonshotK3 = (provider === "moonshot" || provider === "kimi") && /^kimi-k3(?:$|-)/i.test(model); diff --git a/tests/unit/opencode-zen-reasoning-effort.test.ts b/tests/unit/opencode-zen-reasoning-effort.test.ts new file mode 100644 index 0000000000..ee9ebc387f --- /dev/null +++ b/tests/unit/opencode-zen-reasoning-effort.test.ts @@ -0,0 +1,113 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; + +import { sanitizeReasoningEffortForProvider } from "../../open-sse/executors/base/reasoningEffort.ts"; + +/** + * Regression tests for #9318: OpenCode Zen DeepSeek models should accept `max` + * reasoning effort (not normalized to `xhigh`). + * + * OpenCode Zen proxies DeepSeek with the native DeepSeek API contract, which + * accepts {high, max} literally — same as opencode-go. Without this opt-in, + * `max` would be normalized to `xhigh` and rejected by the upstream. + */ +describe("opencode-zen reasoning effort — max support (#9318)", () => { + // ── max passes through for opencode-zen with DeepSeek models ────────── + it("opencode-zen + deepseek model with max keeps max (not downgraded)", () => { + const result = sanitizeReasoningEffortForProvider( + { reasoning_effort: "max", messages: [{ role: "user", content: "hi" }] }, + "opencode-zen", + "oc/deepseek-v4-flash-free" + ); + assert.equal( + (result as Record).reasoning_effort, + "max", + "expected max to pass through for opencode-zen + deepseek" + ); + }); + + it("opencode-zen + deepseek-v4-pro with max keeps max", () => { + const result = sanitizeReasoningEffortForProvider( + { reasoning_effort: "max", messages: [] }, + "opencode-zen", + "deepseek-v4-pro" + ); + assert.equal( + (result as Record).reasoning_effort, + "max", + "expected max to pass through for opencode-zen + deepseek-v4-pro" + ); + }); + + // ── high passes through for opencode-zen with any model (already works) ─ + it("opencode-zen + non-deepseek model with high keeps high", () => { + const result = sanitizeReasoningEffortForProvider( + { reasoning_effort: "high", messages: [] }, + "opencode-zen", + "oc/gpt-5" + ); + assert.equal( + (result as Record).reasoning_effort, + "high", + "expected high to pass through for opencode-zen + non-deepseek model" + ); + }); + + // ── opencode (noauth) behavior unchanged ───────────────────────────── + it("opencode (noauth) with max → normalized to xhigh (unchanged behavior)", () => { + const result = sanitizeReasoningEffortForProvider( + { reasoning_effort: "max", messages: [] }, + "opencode", + "deepseek-v4-flash" + ); + // opencode (noauth) is NOT in the supportsMaxEffortForProvider list, so + // max normalizes to xhigh (which is the xhigh-opt-in fallback). + // If xhigh is supported by the model, max→xhigh; otherwise max→high. + const eff = (result as Record).reasoning_effort; + assert.ok( + eff === "xhigh" || eff === "high", + `expected max to normalize to xhigh or high for opencode (noauth), got ${eff}` + ); + }); + + it("opencode (noauth) with high keeps high", () => { + const result = sanitizeReasoningEffortForProvider( + { reasoning_effort: "high", messages: [] }, + "opencode", + "deepseek-v4-flash" + ); + assert.equal( + (result as Record).reasoning_effort, + "high", + "expected high to remain high for opencode (noauth)" + ); + }); + + // ── opencode-go effort tiers unaffected (regression guard) ──────────── + it("opencode-go + deepseek model with max keeps max (regression guard)", () => { + const result = sanitizeReasoningEffortForProvider( + { reasoning_effort: "max", messages: [] }, + "opencode-go", + "deepseek-v4-pro" + ); + assert.equal( + (result as Record).reasoning_effort, + "max", + "expected max to pass through for opencode-go + deepseek" + ); + }); + + it("opencode-go + non-deepseek model with max normalizes (regression guard)", () => { + const result = sanitizeReasoningEffortForProvider( + { reasoning_effort: "max", messages: [] }, + "opencode-go", + "some-other-model" + ); + // opencode-go only supports max for deepseek models; other models normalize + const eff = (result as Record).reasoning_effort; + assert.ok( + eff === "xhigh" || eff === "high", + `expected max to normalize for opencode-go + non-deepseek model, got ${eff}` + ); + }); +});