mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:50:42 +00:00
fix(opencode): expose Claude thinking levels (#72778)
* fix(opencode): expose claude thinking levels * test(opencode): cover adaptive claude thinking bounds * docs(changelog): credit opencode thinking contributor --------- Co-authored-by: haishmg <4529977+haishmg@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
committed by
GitHub
parent
1b1916053f
commit
f75d8827f2
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { registerProviderPlugin } from "../../test/helpers/plugins/provider-registration.js";
|
||||
import {
|
||||
registerProviderPlugin,
|
||||
requireRegisteredProvider,
|
||||
} from "../../test/helpers/plugins/provider-registration.js";
|
||||
import { expectPassthroughReplayPolicy } from "../../test/helpers/provider-replay-policy.ts";
|
||||
import plugin from "./index.js";
|
||||
|
||||
@@ -40,4 +43,46 @@ describe("opencode provider plugin", () => {
|
||||
modelId: "claude-opus-4.6",
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes Anthropic thinking levels for proxied Claude models", async () => {
|
||||
const { providers } = await registerProviderPlugin({
|
||||
plugin,
|
||||
id: "opencode",
|
||||
name: "OpenCode Zen Provider",
|
||||
});
|
||||
const provider = requireRegisteredProvider(providers, "opencode");
|
||||
const resolveThinkingProfile = provider.resolveThinkingProfile!;
|
||||
|
||||
expect(
|
||||
resolveThinkingProfile({
|
||||
provider: "opencode",
|
||||
modelId: "claude-opus-4-7",
|
||||
}),
|
||||
).toMatchObject({
|
||||
levels: expect.arrayContaining([{ id: "xhigh" }, { id: "adaptive" }, { id: "max" }]),
|
||||
defaultLevel: "off",
|
||||
});
|
||||
const opus46Profile = resolveThinkingProfile({
|
||||
provider: "opencode",
|
||||
modelId: "claude-opus-4.6",
|
||||
});
|
||||
expect(opus46Profile).toMatchObject({
|
||||
levels: expect.arrayContaining([{ id: "adaptive" }]),
|
||||
defaultLevel: "adaptive",
|
||||
});
|
||||
expect(opus46Profile?.levels.some((level) => level.id === "xhigh" || level.id === "max")).toBe(
|
||||
false,
|
||||
);
|
||||
const sonnet46Profile = resolveThinkingProfile({
|
||||
provider: "opencode",
|
||||
modelId: "claude-sonnet-4-6",
|
||||
});
|
||||
expect(sonnet46Profile).toMatchObject({
|
||||
levels: expect.arrayContaining([{ id: "adaptive" }]),
|
||||
defaultLevel: "adaptive",
|
||||
});
|
||||
expect(
|
||||
sonnet46Profile?.levels.some((level) => level.id === "xhigh" || level.id === "max"),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { definePluginEntry, type ProviderThinkingProfile } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
|
||||
import {
|
||||
matchesExactOrPrefix,
|
||||
@@ -17,6 +17,20 @@ const OPENCODE_SHARED_WIZARD_GROUP = {
|
||||
groupLabel: "OpenCode",
|
||||
groupHint: OPENCODE_SHARED_HINT,
|
||||
} as const;
|
||||
const ANTHROPIC_OPUS_47_MODEL_PREFIXES = ["claude-opus-4-7", "claude-opus-4.7"] as const;
|
||||
const ANTHROPIC_ADAPTIVE_MODEL_PREFIXES = [
|
||||
"claude-opus-4-6",
|
||||
"claude-opus-4.6",
|
||||
"claude-sonnet-4-6",
|
||||
"claude-sonnet-4.6",
|
||||
] as const;
|
||||
const BASE_ANTHROPIC_THINKING_LEVELS = [
|
||||
{ id: "off" },
|
||||
{ id: "minimal" },
|
||||
{ id: "low" },
|
||||
{ id: "medium" },
|
||||
{ id: "high" },
|
||||
] as const satisfies ProviderThinkingProfile["levels"];
|
||||
|
||||
function isModernOpencodeModel(modelId: string): boolean {
|
||||
const lower = normalizeLowercaseStringOrEmpty(modelId);
|
||||
@@ -26,6 +40,32 @@ function isModernOpencodeModel(modelId: string): boolean {
|
||||
return !matchesExactOrPrefix(lower, MINIMAX_MODERN_MODEL_MATCHERS);
|
||||
}
|
||||
|
||||
function matchesAnyPrefix(modelId: string, prefixes: readonly string[]): boolean {
|
||||
const lower = normalizeLowercaseStringOrEmpty(modelId);
|
||||
return prefixes.some((prefix) => lower.startsWith(prefix));
|
||||
}
|
||||
|
||||
function resolveOpencodeThinkingProfile(modelId: string): ProviderThinkingProfile {
|
||||
if (matchesAnyPrefix(modelId, ANTHROPIC_OPUS_47_MODEL_PREFIXES)) {
|
||||
return {
|
||||
levels: [
|
||||
...BASE_ANTHROPIC_THINKING_LEVELS,
|
||||
{ id: "xhigh" },
|
||||
{ id: "adaptive" },
|
||||
{ id: "max" },
|
||||
],
|
||||
defaultLevel: "off",
|
||||
};
|
||||
}
|
||||
if (matchesAnyPrefix(modelId, ANTHROPIC_ADAPTIVE_MODEL_PREFIXES)) {
|
||||
return {
|
||||
levels: [...BASE_ANTHROPIC_THINKING_LEVELS, { id: "adaptive" }],
|
||||
defaultLevel: "adaptive",
|
||||
};
|
||||
}
|
||||
return { levels: BASE_ANTHROPIC_THINKING_LEVELS };
|
||||
}
|
||||
|
||||
export default definePluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "OpenCode Zen Provider",
|
||||
@@ -66,6 +106,7 @@ export default definePluginEntry({
|
||||
],
|
||||
...PASSTHROUGH_GEMINI_REPLAY_HOOKS,
|
||||
isModernModelRef: ({ modelId }) => isModernOpencodeModel(modelId),
|
||||
resolveThinkingProfile: ({ modelId }) => resolveOpencodeThinkingProfile(modelId),
|
||||
});
|
||||
api.registerMediaUnderstandingProvider(opencodeMediaUnderstandingProvider);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user