fix(opencode): Zen GPT-5.6 models are missing from discovery (#110405)

* 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 <yang.haoyu@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
mushuiyu886
2026-07-18 11:14:05 +01:00
committed by GitHub
co-authored by Peter Steinberger
parent 04d7462806
commit 42e6972d35
5 changed files with 197 additions and 6 deletions
+107 -3
View File
@@ -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");
});
});
+2 -2
View File
@@ -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);
+45
View File
@@ -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<string, { contextWindow: number; maxTokens: number }> = {
"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<string, ModelDefinitionConfig["cost"]> = {
],
},
"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<string, string> = {
"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<string, string> = {
"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<ModelDefinitionConfig["api"]>;
@@ -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",
@@ -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",
});
});
});
+24 -1
View File
@@ -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);
}