mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-26 01:29:34 +00:00
fix(anthropic): require Mythos adaptive thinking
This commit is contained in:
@@ -308,6 +308,7 @@ describe("anthropic transport stream", () => {
|
||||
} as AnthropicStreamContext,
|
||||
{
|
||||
apiKey: "sk-ant-api",
|
||||
toolChoice: { type: "tool", name: "read_file" },
|
||||
} as AnthropicStreamOptions,
|
||||
);
|
||||
|
||||
@@ -2121,6 +2122,7 @@ describe("anthropic transport stream", () => {
|
||||
const payload = latestAnthropicRequest().payload;
|
||||
expect(payload.thinking).toEqual({ type: "adaptive" });
|
||||
expect(payload.output_config).toEqual({ effort: "high" });
|
||||
expect(payload.tool_choice).toEqual({ type: "auto" });
|
||||
});
|
||||
|
||||
it("does not infer adaptive thinking from forward-compatible effort maps", async () => {
|
||||
@@ -2302,6 +2304,33 @@ describe("anthropic transport stream", () => {
|
||||
expect(payload.output_config).toEqual({ effort: "high" });
|
||||
});
|
||||
|
||||
it("uses mandatory adaptive thinking for canonical Claude Mythos Preview transport aliases", async () => {
|
||||
const model = makeAnthropicTransportModel({
|
||||
id: "prod-mythos-preview",
|
||||
name: "Production Claude",
|
||||
provider: "microsoft-foundry",
|
||||
params: { canonicalModelId: "claude-mythos-preview" },
|
||||
reasoning: false,
|
||||
baseUrl: "https://example.services.ai.azure.com/anthropic",
|
||||
maxTokens: 128_000,
|
||||
});
|
||||
|
||||
guardedFetchMock.mockResolvedValueOnce(createSseResponse());
|
||||
await runTransportStream(
|
||||
model,
|
||||
{
|
||||
messages: [{ role: "user", content: "Think." }],
|
||||
} as AnthropicStreamContext,
|
||||
{
|
||||
apiKey: "sk-ant-api",
|
||||
} as AnthropicStreamOptions,
|
||||
);
|
||||
|
||||
const payload = latestAnthropicRequest().payload;
|
||||
expect(payload.thinking).toEqual({ type: "adaptive" });
|
||||
expect(payload.output_config).toEqual({ effort: "high" });
|
||||
});
|
||||
|
||||
it("maps Claude Fable 5 transport thinking levels to adaptive effort", async () => {
|
||||
const model = makeAnthropicTransportModel({
|
||||
id: "claude-fable-5",
|
||||
|
||||
@@ -17,6 +17,7 @@ import type {
|
||||
import { parseStreamingJson } from "../llm/utils/json-parse.js";
|
||||
import {
|
||||
resolveClaudeNativeThinkingLevelMap,
|
||||
requiresClaudeAdaptiveThinking,
|
||||
supportsClaudeAdaptiveThinking,
|
||||
supportsClaudeNativeMaxEffort,
|
||||
supportsClaudeNativeXhighEffort,
|
||||
@@ -141,7 +142,7 @@ function normalizeAnthropicToolChoice(
|
||||
toolChoice: AnthropicTransportOptions["toolChoice"],
|
||||
) {
|
||||
if (
|
||||
usesClaudeFable5MessagesContract(model) &&
|
||||
requiresClaudeAdaptiveThinking(model) &&
|
||||
(toolChoice === "any" || (typeof toolChoice === "object" && toolChoice.type === "tool"))
|
||||
) {
|
||||
return { type: "auto" as const };
|
||||
@@ -1025,7 +1026,7 @@ function resolveAnthropicTransportOptions(
|
||||
reasoning: options?.reasoning,
|
||||
};
|
||||
if (!options?.reasoning) {
|
||||
resolved.thinkingEnabled = usesClaudeFable5MessagesContract(model);
|
||||
resolved.thinkingEnabled = requiresClaudeAdaptiveThinking(model);
|
||||
if (resolved.thinkingEnabled) {
|
||||
resolved.effort = "high";
|
||||
}
|
||||
|
||||
@@ -537,6 +537,38 @@ describe("Anthropic provider", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("uses mandatory adaptive thinking for Foundry Mythos Preview", async () => {
|
||||
let capturedPayload: unknown;
|
||||
const stream = streamSimpleAnthropic(
|
||||
makeAnthropicModel({
|
||||
id: "prod-mythos-preview",
|
||||
name: "Production Claude",
|
||||
provider: "microsoft-foundry",
|
||||
params: { canonicalModelId: "claude-mythos-preview" },
|
||||
reasoning: false,
|
||||
}),
|
||||
{
|
||||
messages: [{ role: "user", content: "hello", timestamp: 0 }],
|
||||
},
|
||||
{
|
||||
apiKey: "sk-ant-provider",
|
||||
toolChoice: "any",
|
||||
onPayload: (payload) => {
|
||||
capturedPayload = payload;
|
||||
throw new Error("stop before network");
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await stream.result();
|
||||
|
||||
expect(capturedPayload).toMatchObject({
|
||||
thinking: { type: "adaptive" },
|
||||
output_config: { effort: "high" },
|
||||
tool_choice: { type: "auto" },
|
||||
});
|
||||
});
|
||||
|
||||
it("uses adaptive high effort for Foundry Mythos Preview without native max metadata", async () => {
|
||||
let capturedPayload: unknown;
|
||||
const stream = streamSimpleAnthropic(
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from "../../agents/system-prompt-cache-boundary.js";
|
||||
import {
|
||||
resolveClaudeNativeThinkingLevelMap,
|
||||
requiresClaudeAdaptiveThinking,
|
||||
supportsClaudeAdaptiveThinking,
|
||||
supportsClaudeNativeMaxEffort,
|
||||
supportsClaudeNativeXhighEffort,
|
||||
@@ -802,7 +803,7 @@ function normalizeAnthropicToolChoice(
|
||||
toolChoice: AnthropicOptions["toolChoice"],
|
||||
) {
|
||||
if (
|
||||
usesClaudeFable5MessagesContract(model) &&
|
||||
requiresClaudeAdaptiveThinking(model) &&
|
||||
(toolChoice === "any" || (typeof toolChoice === "object" && toolChoice.type === "tool"))
|
||||
) {
|
||||
return { type: "auto" as const };
|
||||
@@ -875,11 +876,11 @@ export const streamSimpleAnthropic: StreamFunction<"anthropic-messages", SimpleS
|
||||
|
||||
const base = buildBaseOptions(model, options, apiKey);
|
||||
if (!options?.reasoning) {
|
||||
const fable5 = usesClaudeFable5MessagesContract(model);
|
||||
const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
|
||||
return streamAnthropic(model, context, {
|
||||
...base,
|
||||
thinkingEnabled: fable5,
|
||||
...(fable5 ? { effort: "high" as const } : {}),
|
||||
thinkingEnabled: mandatoryAdaptiveThinking,
|
||||
...(mandatoryAdaptiveThinking ? { effort: "high" as const } : {}),
|
||||
} satisfies AnthropicOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,21 @@ export function usesClaudeFable5MessagesContract(model: {
|
||||
);
|
||||
}
|
||||
|
||||
export function requiresClaudeAdaptiveThinking(model: {
|
||||
id?: string;
|
||||
params?: Record<string, unknown>;
|
||||
api?: string;
|
||||
}): boolean {
|
||||
if (normalizeApi(model.api) !== "anthropic-messages") {
|
||||
return false;
|
||||
}
|
||||
const modelId = resolveClaudeModelIdentity(model);
|
||||
return (
|
||||
resolveClaudeFable5ModelIdentity(model) !== undefined ||
|
||||
/(?:^|-)claude-mythos-preview(?=$|[^a-z0-9])/.test(modelId)
|
||||
);
|
||||
}
|
||||
|
||||
function resolveReplayFableIdentity(ref: ReplayModelRef): string | undefined {
|
||||
if (normalizeApi(ref.api) !== "anthropic-messages") {
|
||||
return undefined;
|
||||
|
||||
Reference in New Issue
Block a user