From 42e6972d354dbdd6edfc435d5585fa453a59579e Mon Sep 17 00:00:00 2001 From: mushuiyu886 Date: Sat, 18 Jul 2026 18:14:05 +0800 Subject: [PATCH] fix(opencode): Zen GPT-5.6 models are missing from discovery (#110405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(opencode): add Zen GPT-5.6 models * test(opencode): narrow GPT-5.6 policy fixture * fix(opencode): align GPT-5.6 thinking levels Co-authored-by: 杨浩宇0668001029 --------- Co-authored-by: Peter Steinberger --- extensions/opencode/index.test.ts | 110 +++++++++++++++++- extensions/opencode/index.ts | 4 +- extensions/opencode/provider-catalog.ts | 45 +++++++ .../opencode/provider-policy-api.test.ts | 19 +++ extensions/opencode/provider-policy-api.ts | 25 +++- 5 files changed, 197 insertions(+), 6 deletions(-) diff --git a/extensions/opencode/index.test.ts b/extensions/opencode/index.test.ts index b71cf969fd58..b8561dd41220 100644 --- a/extensions/opencode/index.test.ts +++ b/extensions/opencode/index.test.ts @@ -97,6 +97,9 @@ describe("opencode provider plugin", () => { "gemini-3.5-flash", "gemini-3.1-pro", "gemini-3-flash", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.5", "gpt-5.5-pro", "gpt-5.4", @@ -169,6 +172,39 @@ describe("opencode provider plugin", () => { api: "openai-responses", baseUrl: "https://opencode.ai/zen/v1", }); + expect(requireMapEntry(models, "gpt-5.6-luna")).toMatchObject({ + name: "GPT-5.6 Luna", + api: "openai-responses", + baseUrl: "https://opencode.ai/zen/v1", + input: ["text", "image"], + contextWindow: 1_050_000, + maxTokens: 128_000, + cost: { + input: 1, + output: 6, + cacheRead: 0.1, + cacheWrite: 1.25, + tieredPricing: [ + { input: 1, output: 6, cacheRead: 0.1, cacheWrite: 1.25, range: [0, 272_000] }, + { input: 2, output: 9, cacheRead: 0.2, cacheWrite: 2.5, range: [272_000] }, + ], + }, + compat: { + supportedReasoningEfforts: ["none", "low", "medium", "high", "xhigh", "max"], + }, + }); + expect(requireMapEntry(models, "gpt-5.6-terra")).toMatchObject({ + name: "GPT-5.6 Terra", + contextWindow: 1_050_000, + maxTokens: 128_000, + cost: { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 3.125 }, + }); + expect(requireMapEntry(models, "gpt-5.6-sol")).toMatchObject({ + name: "GPT-5.6 Sol", + contextWindow: 1_050_000, + maxTokens: 128_000, + cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 }, + }); expect(requireMapEntry(models, "gemini-3.5-flash")).toMatchObject({ api: "google-generative-ai", baseUrl: "https://opencode.ai/zen/v1", @@ -330,6 +366,57 @@ describe("opencode provider plugin", () => { ["claude-opus-4-5", { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }], ["claude-opus-4-1", { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 }], ["claude-sonnet-5", { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 }], + [ + "gpt-5.6-luna", + { + input: 1, + output: 6, + cacheRead: 0.1, + cacheWrite: 1.25, + tieredPricing: [ + { input: 1, output: 6, cacheRead: 0.1, cacheWrite: 1.25, range: [0, 272_000] }, + { input: 2, output: 9, cacheRead: 0.2, cacheWrite: 2.5, range: [272_000] }, + ], + }, + ], + [ + "gpt-5.6-terra", + { + input: 2.5, + output: 15, + cacheRead: 0.25, + cacheWrite: 3.125, + tieredPricing: [ + { + input: 2.5, + output: 15, + cacheRead: 0.25, + cacheWrite: 3.125, + range: [0, 272_000], + }, + { + input: 5, + output: 22.5, + cacheRead: 0.5, + cacheWrite: 6.25, + range: [272_000], + }, + ], + }, + ], + [ + "gpt-5.6-sol", + { + input: 5, + output: 30, + cacheRead: 0.5, + cacheWrite: 6.25, + tieredPricing: [ + { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25, range: [0, 272_000] }, + { input: 10, output: 45, cacheRead: 1, cacheWrite: 12.5, range: [272_000] }, + ], + }, + ], ["gpt-5.4-mini", { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 }], ["glm-5.2", { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 }], ["hy3-free", { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }], @@ -372,7 +459,7 @@ describe("opencode provider plugin", () => { throw new Error("expected OpenCode Zen static provider"); } - expect(result.provider.models).toHaveLength(52); + expect(result.provider.models).toHaveLength(55); expect(result.provider.models.map((model) => model.id)).toContain("claude-opus-4-8"); expect(result.provider.models.map((model) => model.id)).toContain("claude-sonnet-5"); expect(result.provider.models.map((model) => model.id)).toContain("glm-5.2"); @@ -381,6 +468,7 @@ describe("opencode provider plugin", () => { expect(result.provider.models.map((model) => model.id)).toContain("kimi-k2.7-code"); expect(result.provider.models.map((model) => model.id)).toContain("minimax-m2.7"); expect(result.provider.models.map((model) => model.id)).toContain("minimax-m3"); + expect(result.provider.models.map((model) => model.id)).toContain("gpt-5.6-luna"); expect(result.provider.models.find((model) => model.id === "minimax-m2.7")).toMatchObject({ api: "openai-completions", baseUrl: "https://opencode.ai/zen/v1", @@ -395,8 +483,9 @@ describe("opencode provider plugin", () => { throw new Error("expected registered OpenCode Zen static provider"); } - expect(result.provider.models).toHaveLength(52); + expect(result.provider.models).toHaveLength(55); expect(result.provider.models.map((model) => model.id)).toContain("claude-sonnet-5"); + expect(result.provider.models.map((model) => model.id)).toContain("gpt-5.6-sol"); expect(result.provider.models.map((model) => model.id)).toContain("minimax-m3"); expect(result.provider.models.find((model) => model.id === "grok-4.5")).toMatchObject({ api: "openai-completions", @@ -616,7 +705,7 @@ describe("opencode provider plugin", () => { }); }); - it("exposes Anthropic thinking levels for proxied Claude models", async () => { + it("exposes provider-owned thinking levels for proxied models", async () => { const { providers } = await registerProviderPlugin({ plugin, id: "opencode", @@ -655,5 +744,20 @@ describe("opencode provider plugin", () => { expect(sonnet46LevelIds).toContain("adaptive"); expect(sonnet46LevelIds).not.toContain("xhigh"); expect(sonnet46LevelIds).not.toContain("max"); + + const gpt56Profile = resolveThinkingProfile({ + provider: "opencode", + modelId: "gpt-5.6-luna", + api: "openai-responses", + reasoning: true, + compat: { + supportedReasoningEfforts: ["none", "low", "medium", "high", "xhigh", "max"], + }, + }); + const gpt56LevelIds = gpt56Profile?.levels.map((level) => level.id) ?? []; + expect(gpt56Profile?.defaultLevel).toBe("medium"); + expect(gpt56LevelIds).not.toContain("minimal"); + expect(gpt56LevelIds).toContain("xhigh"); + expect(gpt56LevelIds).toContain("max"); }); }); diff --git a/extensions/opencode/index.ts b/extensions/opencode/index.ts index a4e360426706..0f1bc0e29594 100644 --- a/extensions/opencode/index.ts +++ b/extensions/opencode/index.ts @@ -4,7 +4,6 @@ import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-aut import { buildProviderReplayFamilyHooks, matchesExactOrPrefix, - resolveClaudeThinkingProfile, } from "openclaw/plugin-sdk/provider-model-shared"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime"; import { applyOpencodeZenConfig, OPENCODE_ZEN_DEFAULT_MODEL } from "./api.js"; @@ -16,6 +15,7 @@ import { normalizeOpencodeZenBaseUrl, resolveOpencodeZenModel, } from "./provider-catalog.js"; +import { resolveThinkingProfile as resolveOpencodeThinkingProfile } from "./provider-policy-api.js"; import { registerOpenCodeSessionCatalog } from "./session-catalog-plugin.js"; const PROVIDER_ID = "opencode"; @@ -151,7 +151,7 @@ export default definePluginEntry({ augmentModelCatalog: () => listOpencodeZenModelCatalogEntries(), ...buildProviderReplayFamilyHooks({ family: "passthrough-gemini" }), isModernModelRef: ({ modelId }) => isModernOpencodeModel(modelId), - resolveThinkingProfile: ({ modelId }) => resolveClaudeThinkingProfile(modelId), + resolveThinkingProfile: resolveOpencodeThinkingProfile, }); api.registerMediaUnderstandingProvider(opencodeMediaUnderstandingProvider); registerOpenCodeSessionCatalog(api); diff --git a/extensions/opencode/provider-catalog.ts b/extensions/opencode/provider-catalog.ts index f10eee6ed2a5..b895bf734c1a 100644 --- a/extensions/opencode/provider-catalog.ts +++ b/extensions/opencode/provider-catalog.ts @@ -30,6 +30,9 @@ const FREE_COST: ModelDefinitionConfig["cost"] = { // Zen publishes route-specific limits that differ from the family defaults below. const MODEL_LIMITS: Record = { "claude-sonnet-5": { contextWindow: 1_000_000, maxTokens: 128_000 }, + "gpt-5.6-luna": { contextWindow: 1_050_000, maxTokens: 128_000 }, + "gpt-5.6-sol": { contextWindow: 1_050_000, maxTokens: 128_000 }, + "gpt-5.6-terra": { contextWindow: 1_050_000, maxTokens: 128_000 }, "glm-5.2": { contextWindow: 1_000_000, maxTokens: 131_072 }, "grok-4.5": { contextWindow: 500_000, maxTokens: 500_000 }, "hy3-free": { contextWindow: 256_000, maxTokens: 64_000 }, @@ -89,6 +92,36 @@ const MODEL_COSTS: Record = { ], }, "gemini-3.5-flash": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 }, + "gpt-5.6-luna": { + input: 1, + output: 6, + cacheRead: 0.1, + cacheWrite: 1.25, + tieredPricing: [ + { input: 1, output: 6, cacheRead: 0.1, cacheWrite: 1.25, range: [0, 272_000] }, + { input: 2, output: 9, cacheRead: 0.2, cacheWrite: 2.5, range: [272_000] }, + ], + }, + "gpt-5.6-sol": { + input: 5, + output: 30, + cacheRead: 0.5, + cacheWrite: 6.25, + tieredPricing: [ + { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25, range: [0, 272_000] }, + { input: 10, output: 45, cacheRead: 1, cacheWrite: 12.5, range: [272_000] }, + ], + }, + "gpt-5.6-terra": { + input: 2.5, + output: 15, + cacheRead: 0.25, + cacheWrite: 3.125, + tieredPricing: [ + { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 3.125, range: [0, 272_000] }, + { input: 5, output: 22.5, cacheRead: 0.5, cacheWrite: 6.25, range: [272_000] }, + ], + }, "glm-5": { input: 1, output: 3.2, cacheRead: 0.2, cacheWrite: 0 }, "glm-5.1": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 }, "glm-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 }, @@ -171,6 +204,9 @@ const MODEL_NAMES: Record = { "gemini-3-flash": "Gemini 3 Flash", "gemini-3.1-pro": "Gemini 3.1 Pro", "gemini-3.5-flash": "Gemini 3.5 Flash", + "gpt-5.6-luna": "GPT-5.6 Luna", + "gpt-5.6-sol": "GPT-5.6 Sol", + "gpt-5.6-terra": "GPT-5.6 Terra", "glm-5": "GLM-5", "glm-5.1": "GLM-5.1", "glm-5.2": "GLM-5.2", @@ -207,6 +243,9 @@ const MODEL_NAMES: Record = { "qwen3.6-plus": "Qwen3.6 Plus", }; +const GPT_56_MODEL_IDS = new Set(["gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"]); +const GPT_56_REASONING_EFFORTS = ["none", "low", "medium", "high", "xhigh", "max"]; + type OpencodeZenModelDefinition = ModelDefinitionConfig & { provider: typeof PROVIDER_ID; api: NonNullable; @@ -343,6 +382,9 @@ function buildOpencodeZenModel(modelId: string): OpencodeZenModelDefinition { compat: { supportsUsageInStreaming: true, supportsReasoningEffort: true, + ...(GPT_56_MODEL_IDS.has(normalizedModelId) + ? { supportedReasoningEfforts: GPT_56_REASONING_EFFORTS } + : {}), maxTokensField: "max_tokens", }, }) as OpencodeZenModelDefinition; @@ -363,6 +405,9 @@ const OPENCODE_ZEN_MODELS = [ "gemini-3.5-flash", "gemini-3.1-pro", "gemini-3-flash", + "gpt-5.6-sol", + "gpt-5.6-terra", + "gpt-5.6-luna", "gpt-5.5", "gpt-5.5-pro", "gpt-5.4", diff --git a/extensions/opencode/provider-policy-api.test.ts b/extensions/opencode/provider-policy-api.test.ts index 99b086e11246..c45dbcd18267 100644 --- a/extensions/opencode/provider-policy-api.test.ts +++ b/extensions/opencode/provider-policy-api.test.ts @@ -42,4 +42,23 @@ describe("opencode provider policy public artifact", () => { defaultLevel: "adaptive", }); }); + + it("exposes the full GPT-5.6 reasoning profile", () => { + expect( + resolveThinkingProfile({ + provider: "opencode", + modelId: "gpt-5.6-luna", + }), + ).toEqual({ + levels: [ + { id: "off" }, + { id: "low" }, + { id: "medium" }, + { id: "high" }, + { id: "xhigh" }, + { id: "max" }, + ], + defaultLevel: "medium", + }); + }); }); diff --git a/extensions/opencode/provider-policy-api.ts b/extensions/opencode/provider-policy-api.ts index 72fe0de83440..4262e4e61bfb 100644 --- a/extensions/opencode/provider-policy-api.ts +++ b/extensions/opencode/provider-policy-api.ts @@ -1,6 +1,29 @@ // Opencode API module exposes the plugin public contract. +import type { + ProviderDefaultThinkingPolicyContext, + ProviderThinkingProfile, +} from "openclaw/plugin-sdk/plugin-entry"; import { resolveClaudeThinkingProfile } from "openclaw/plugin-sdk/provider-model-shared"; -export function resolveThinkingProfile(params: { provider?: string; modelId: string }) { +const GPT_56_THINKING_PROFILE = { + levels: [ + { id: "off" }, + { id: "low" }, + { id: "medium" }, + { id: "high" }, + { id: "xhigh" }, + { id: "max" }, + ], + defaultLevel: "medium", +} as const satisfies ProviderThinkingProfile; + +function isGpt56Model(modelId: string): boolean { + return /^gpt-5\.6(?:-|$)/u.test(modelId.trim().toLowerCase()); +} + +export function resolveThinkingProfile(params: ProviderDefaultThinkingPolicyContext) { + if (isGpt56Model(params.modelId)) { + return GPT_56_THINKING_PROFILE; + } return resolveClaudeThinkingProfile(params.modelId); }