fix(opencode): support sap-ai-core anthropic opus 4.7+ adaptive reasoning (#29991)

This commit is contained in:
Jérôme Benoit
2026-05-31 01:30:21 +02:00
committed by GitHub
parent 30f9780561
commit f4f508e659
2 changed files with 82 additions and 124 deletions
+18 -4
View File
@@ -597,10 +597,12 @@ function openaiCompatibleReasoningEfforts(id: string) {
}
function anthropicOpus47OrLater(apiId: string) {
const version = /opus-(\d+)[.-](\d+)(?:[.@-]|$)/i.exec(apiId)
// Matches "opus-4.7" (Anthropic/Bedrock/Vertex) and "claude-4.7-opus" (SAP AI Core inverted).
// Greedy \d+ correctly extends to multi-digit majors (e.g. "claude-10.0-opus") for forward compatibility.
const version = /opus-(\d+)[.-](\d+)(?:[.@-]|$)|claude-(\d+)[.-](\d+)-opus(?:[.@-]|$)/i.exec(apiId)
if (!version) return false
const major = Number(version[1])
const minor = Number(version[2])
const major = Number(version[1] ?? version[3])
const minor = Number(version[2] ?? version[4])
return major > 4 || (major === 4 && minor >= 7)
}
@@ -608,7 +610,18 @@ function anthropicAdaptiveEfforts(apiId: string): string[] | null {
if (anthropicOpus47OrLater(apiId)) {
return ["low", "medium", "high", "xhigh", "max"]
}
if (["opus-4-6", "opus-4.6", "sonnet-4-6", "sonnet-4.6"].some((v) => apiId.includes(v))) {
if (
[
"opus-4-6",
"opus-4.6",
"4-6-opus",
"4.6-opus",
"sonnet-4-6",
"sonnet-4.6",
"4-6-sonnet",
"4.6-sonnet",
].some((v) => apiId.includes(v))
) {
return ["low", "medium", "high", "max"]
}
return null
@@ -999,6 +1012,7 @@ export function variants(model: Provider.Model): Record<string, Record<string, a
{
thinking: {
type: "adaptive",
...(adaptiveOpus ? { display: "summarized" } : {}),
},
effort,
},
+64 -120
View File
@@ -3581,142 +3581,86 @@ describe("ProviderTransform.variants", () => {
})
describe("@jerome-benoit/sap-ai-provider-v2", () => {
test("anthropic models return thinking variants", () => {
const model = createMockModel({
id: "sap-ai-core/anthropic--claude-sonnet-4",
const sapModel = (apiId: string) =>
createMockModel({
id: `sap-ai-core/${apiId}`,
providerID: "sap-ai-core",
api: {
id: "anthropic--claude-sonnet-4",
id: apiId,
url: "https://api.ai.sap",
npm: "@jerome-benoit/sap-ai-provider-v2",
},
})
const result = ProviderTransform.variants(model)
for (const testCase of [
{
name: "sonnet 4.6",
apiIds: ["anthropic--claude-sonnet-4-6"],
efforts: ["low", "medium", "high", "max"],
expectedHigh: { thinking: { type: "adaptive" }, effort: "high" },
},
{
name: "opus 4.6",
apiIds: ["anthropic--claude-4.6-opus", "anthropic--claude-4-6-opus"],
efforts: ["low", "medium", "high", "max"],
expectedHigh: { thinking: { type: "adaptive" }, effort: "high" },
},
{
name: "opus 4.7",
apiIds: ["anthropic--claude-4.7-opus", "anthropic--claude-4-7-opus"],
efforts: ["low", "medium", "high", "xhigh", "max"],
expectedHigh: { thinking: { type: "adaptive", display: "summarized" }, effort: "high" },
},
{
name: "opus 4.8",
apiIds: ["anthropic--claude-4.8-opus", "anthropic--claude-4-8-opus"],
efforts: ["low", "medium", "high", "xhigh", "max"],
expectedHigh: { thinking: { type: "adaptive", display: "summarized" }, effort: "high" },
},
]) {
for (const apiId of testCase.apiIds) {
test(`${testCase.name} ${apiId} returns adaptive thinking variants`, () => {
const result = ProviderTransform.variants(sapModel(apiId))
expect(Object.keys(result)).toEqual(testCase.efforts)
expect(result.high).toEqual(testCase.expectedHigh)
if (testCase.efforts.includes("xhigh")) {
expect(result.xhigh).toEqual({ ...testCase.expectedHigh, effort: "xhigh" })
}
})
}
}
test("anthropic sonnet 4 returns budget-tokens variants", () => {
const result = ProviderTransform.variants(sapModel("anthropic--claude-sonnet-4"))
expect(Object.keys(result)).toEqual(["high", "max"])
expect(result.high).toEqual({
thinking: {
type: "enabled",
budgetTokens: 16000,
},
})
expect(result.max).toEqual({
thinking: {
type: "enabled",
budgetTokens: 31999,
},
})
expect(result.high).toEqual({ thinking: { type: "enabled", budgetTokens: 16000 } })
expect(result.max).toEqual({ thinking: { type: "enabled", budgetTokens: 31999 } })
})
test("anthropic 4.6 models return adaptive thinking variants", () => {
const model = createMockModel({
id: "sap-ai-core/anthropic--claude-sonnet-4-6",
providerID: "sap-ai-core",
api: {
id: "anthropic--claude-sonnet-4-6",
url: "https://api.ai.sap",
npm: "@jerome-benoit/sap-ai-provider-v2",
},
})
const result = ProviderTransform.variants(model)
expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"])
expect(result.low).toEqual({
thinking: {
type: "adaptive",
},
effort: "low",
})
expect(result.max).toEqual({
thinking: {
type: "adaptive",
},
effort: "max",
})
})
test("gemini 2.5 models return thinkingConfig variants", () => {
const model = createMockModel({
id: "sap-ai-core/gcp--gemini-2.5-pro",
providerID: "sap-ai-core",
api: {
id: "gcp--gemini-2.5-pro",
url: "https://api.ai.sap",
npm: "@jerome-benoit/sap-ai-provider-v2",
},
})
const result = ProviderTransform.variants(model)
test("gemini 2.5 returns thinkingConfig variants", () => {
const result = ProviderTransform.variants(sapModel("gcp--gemini-2.5-pro"))
expect(Object.keys(result)).toEqual(["high", "max"])
expect(result.high).toEqual({
thinkingConfig: {
includeThoughts: true,
thinkingBudget: 16000,
},
})
expect(result.max).toEqual({
thinkingConfig: {
includeThoughts: true,
thinkingBudget: 24576,
},
})
expect(result.high).toEqual({ thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } })
expect(result.max).toEqual({ thinkingConfig: { includeThoughts: true, thinkingBudget: 24576 } })
})
test("gpt models return reasoningEffort variants", () => {
const model = createMockModel({
id: "sap-ai-core/azure-openai--gpt-4o",
providerID: "sap-ai-core",
api: {
id: "azure-openai--gpt-4o",
url: "https://api.ai.sap",
npm: "@jerome-benoit/sap-ai-provider-v2",
},
for (const apiId of ["azure-openai--gpt-4o", "azure-openai--o3-mini"]) {
test(`${apiId} returns reasoningEffort variants`, () => {
const result = ProviderTransform.variants(sapModel(apiId))
expect(Object.keys(result)).toEqual(["low", "medium", "high"])
expect(result.low).toEqual({ reasoningEffort: "low" })
expect(result.high).toEqual({ reasoningEffort: "high" })
})
const result = ProviderTransform.variants(model)
expect(Object.keys(result)).toEqual(["low", "medium", "high"])
expect(result.low).toEqual({ reasoningEffort: "low" })
expect(result.high).toEqual({ reasoningEffort: "high" })
})
}
test("o-series models return reasoningEffort variants", () => {
const model = createMockModel({
id: "sap-ai-core/azure-openai--o3-mini",
providerID: "sap-ai-core",
api: {
id: "azure-openai--o3-mini",
url: "https://api.ai.sap",
npm: "@jerome-benoit/sap-ai-provider-v2",
},
for (const apiId of ["perplexity--sonar-pro", "mistral--mistral-large"]) {
test(`${apiId} returns empty object`, () => {
expect(ProviderTransform.variants(sapModel(apiId))).toEqual({})
})
const result = ProviderTransform.variants(model)
expect(Object.keys(result)).toEqual(["low", "medium", "high"])
expect(result.low).toEqual({ reasoningEffort: "low" })
expect(result.high).toEqual({ reasoningEffort: "high" })
})
}
test("sonar models return empty object", () => {
const model = createMockModel({
id: "sap-ai-core/perplexity--sonar-pro",
providerID: "sap-ai-core",
api: {
id: "perplexity--sonar-pro",
url: "https://api.ai.sap",
npm: "@jerome-benoit/sap-ai-provider-v2",
},
})
const result = ProviderTransform.variants(model)
expect(result).toEqual({})
})
test("mistral models return empty object", () => {
const model = createMockModel({
id: "sap-ai-core/mistral--mistral-large",
providerID: "sap-ai-core",
api: {
id: "mistral--mistral-large",
url: "https://api.ai.sap",
npm: "@jerome-benoit/sap-ai-provider-v2",
},
})
const result = ProviderTransform.variants(model)
expect(result).toEqual({})
test("non-anthropic models with opus-like substrings do not get adaptive thinking", () => {
expect(ProviderTransform.variants(sapModel("aws--llama-opus-4.7-fake"))).toEqual({})
})
})