From 55384bc23667437bcc3cfad26da584bb4d796e65 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 10 Jun 2026 23:38:19 +0900 Subject: [PATCH] fix(foundry): clamp Mythos Preview thinking --- extensions/microsoft-foundry/index.test.ts | 20 ++++++++++++++++++-- extensions/microsoft-foundry/provider.ts | 21 ++++++++++++++++++--- extensions/microsoft-foundry/shared.ts | 14 +++++++------- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/extensions/microsoft-foundry/index.test.ts b/extensions/microsoft-foundry/index.test.ts index 864a04b83660..de32c8ff90c7 100644 --- a/extensions/microsoft-foundry/index.test.ts +++ b/extensions/microsoft-foundry/index.test.ts @@ -1437,9 +1437,25 @@ describe("microsoft-foundry plugin", () => { modelId: "prod-opaque", } as never), ).toBeUndefined(); + expect( + provider.resolveThinkingProfile?.({ + provider: "microsoft-foundry", + modelId: "prod-mythos-preview", + modelName: "claude-mythos-preview", + } as never), + ).toMatchObject({ + defaultLevel: "adaptive", + levels: [ + { id: "minimal" }, + { id: "low" }, + { id: "medium" }, + { id: "high" }, + { id: "adaptive" }, + ], + }); }); - it("records max-only thinking maps for Foundry Mythos Preview deployments", () => { + it("does not record native max thinking maps for Foundry Mythos Preview deployments", () => { const result = buildFoundryAuthResult({ profileId: "microsoft-foundry:entra", apiKey: "__entra_id_dynamic__", @@ -1451,7 +1467,7 @@ describe("microsoft-foundry plugin", () => { }); const model = result.configPatch?.models?.providers?.["microsoft-foundry"]?.models[0]; - expect(model?.thinkingLevelMap).toEqual({ max: "max" }); + expect(model?.thinkingLevelMap).toBeUndefined(); }); it("keeps Foundry chat reasoning_effort enabled for GPT-5 reasoning deployments", () => { diff --git a/extensions/microsoft-foundry/provider.ts b/extensions/microsoft-foundry/provider.ts index c057d6e12e97..42fcd16990dd 100644 --- a/extensions/microsoft-foundry/provider.ts +++ b/extensions/microsoft-foundry/provider.ts @@ -1,8 +1,9 @@ // Microsoft Foundry provider module implements model/runtime integration. import type { ProviderNormalizeResolvedModelContext } from "openclaw/plugin-sdk/core"; -import type { - ModelProviderConfig, - ProviderPlugin, +import { + resolveClaudeThinkingProfile, + type ModelProviderConfig, + type ProviderPlugin, } from "openclaw/plugin-sdk/provider-model-shared"; import { OPENAI_RESPONSES_STREAM_HOOKS } from "openclaw/plugin-sdk/provider-stream-family"; import { apiKeyAuthMethod, entraIdAuthMethod } from "./auth.js"; @@ -13,6 +14,7 @@ import { applyFoundryProviderConfig, buildFoundryProviderBaseUrl, extractFoundryEndpoint, + isFoundryClaudeMythosPreview, isFoundryProviderApi, normalizeFoundryEndpoint, resolveFoundryModelCapabilities, @@ -175,6 +177,19 @@ export function buildMicrosoftFoundryProvider(): ProviderPlugin { } applyFoundryProviderConfig(ctx.config, nextProviderConfig); }, + resolveThinkingProfile: ({ modelId, modelName }) => { + const capabilities = resolveFoundryModelCapabilities(modelId, modelName); + if (!capabilities.reasoning || capabilities.api !== "anthropic-messages") { + return undefined; + } + const profile = resolveClaudeThinkingProfile(capabilities.modelName); + return isFoundryClaudeMythosPreview(capabilities.modelName) + ? { + ...profile, + levels: profile.levels.filter((level) => level.id !== "xhigh" && level.id !== "max"), + } + : profile; + }, normalizeResolvedModel: ({ modelId, model }: ProviderNormalizeResolvedModelContext) => { const endpoint = extractFoundryEndpoint(model.baseUrl ?? ""); if (!endpoint) { diff --git a/extensions/microsoft-foundry/shared.ts b/extensions/microsoft-foundry/shared.ts index 1fb05a32ffb5..55304628a7e8 100644 --- a/extensions/microsoft-foundry/shared.ts +++ b/extensions/microsoft-foundry/shared.ts @@ -9,7 +9,6 @@ import { import { isClaudeMandatoryAdaptiveThinkingModelId, supportsClaudeAdaptiveThinking, - supportsClaudeNativeMaxEffort, supportsClaudeNativeXhighEffort, type ModelApi, type ModelProviderConfig, @@ -157,6 +156,10 @@ export function isAnthropicFoundryDeployment(modelName?: string | null): boolean return normalized ? normalized.startsWith("claude") : false; } +export function isFoundryClaudeMythosPreview(value?: string | null): boolean { + return normalizeFoundryModelName(value) === "claude-mythos-preview"; +} + export function usesFoundryResponsesByDefault(value?: string | null): boolean { const normalized = normalizeFoundryModelName(value); if (!normalized) { @@ -417,7 +420,6 @@ export function resolveFoundryModelCapabilities( const supportedReasoningEfforts = resolveFoundryReasoningEfforts(modelName); const isAnthropic = api === ANTHROPIC_MESSAGES_API || isAnthropicFoundryDeployment(modelName); const supportsClaudeThinking = isAnthropic && supportsClaudeAdaptiveThinking(modelName); - const supportsClaudeMaxThinking = isAnthropic && supportsClaudeNativeMaxEffort(modelName); const supportsClaudeXhighThinking = isAnthropic && supportsClaudeNativeXhighEffort(modelName); const tokenLimits = resolveFoundryModelTokenLimits(modelName); return { @@ -429,11 +431,9 @@ export function resolveFoundryModelCapabilities( supportsFoundryReasoningContent(modelName), ...(supportsClaudeXhighThinking ? { thinkingLevelMap: { xhigh: "xhigh", max: "max" } } - : supportsClaudeMaxThinking - ? { thinkingLevelMap: { max: "max" } } - : supportedReasoningEfforts - ? { thinkingLevelMap: buildFoundryThinkingLevelMap(supportedReasoningEfforts) } - : {}), + : supportedReasoningEfforts + ? { thinkingLevelMap: buildFoundryThinkingLevelMap(supportedReasoningEfforts) } + : {}), input: normalizedInput.includes("image") || supportsFoundryImageInput(modelName) ? ["text", "image"]