mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
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:
co-authored by
Peter Steinberger
parent
04d7462806
commit
42e6972d35
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user