Update Claude Opus and GPT thinking metadata
This commit is contained in:
@@ -2,22 +2,10 @@ import { describe, expect, it } from "vitest";
|
||||
import { getModels, getProviders } from "../src/models.ts";
|
||||
import type { Api, Model } from "../src/types.ts";
|
||||
|
||||
const EXPECTED_ADAPTIVE_THINKING_MODELS = [
|
||||
"anthropic/claude-opus-4-6",
|
||||
"anthropic/claude-opus-4-7",
|
||||
"anthropic/claude-sonnet-4-6",
|
||||
"cloudflare-ai-gateway/claude-opus-4-6",
|
||||
"cloudflare-ai-gateway/claude-opus-4-7",
|
||||
"cloudflare-ai-gateway/claude-sonnet-4-6",
|
||||
"github-copilot/claude-opus-4.6",
|
||||
"github-copilot/claude-opus-4.7",
|
||||
"github-copilot/claude-sonnet-4.6",
|
||||
"opencode/claude-opus-4-6",
|
||||
"opencode/claude-opus-4-7",
|
||||
"opencode/claude-sonnet-4-6",
|
||||
"vercel-ai-gateway/anthropic/claude-opus-4.6",
|
||||
"vercel-ai-gateway/anthropic/claude-opus-4.7",
|
||||
"vercel-ai-gateway/anthropic/claude-sonnet-4.6",
|
||||
const EXPECTED_CURRENT_ADAPTIVE_THINKING_MODELS = [
|
||||
"anthropic/claude-opus-4-8",
|
||||
"opencode/claude-opus-4-8",
|
||||
"vercel-ai-gateway/anthropic/claude-opus-4.8",
|
||||
];
|
||||
|
||||
function getAllModels(): Model<Api>[] {
|
||||
@@ -25,13 +13,16 @@ function getAllModels(): Model<Api>[] {
|
||||
}
|
||||
|
||||
describe("Anthropic adaptive thinking model metadata", () => {
|
||||
it("marks exactly the built-in Anthropic Messages models that use adaptive thinking", () => {
|
||||
it("marks built-in Anthropic Messages models that use adaptive thinking", () => {
|
||||
const flaggedModels = getAllModels()
|
||||
.filter((model): model is Model<"anthropic-messages"> => model.api === "anthropic-messages")
|
||||
.filter((model) => model.compat?.forceAdaptiveThinking === true)
|
||||
.map((model) => `${model.provider}/${model.id}`)
|
||||
.sort();
|
||||
|
||||
expect(flaggedModels).toEqual([...EXPECTED_ADAPTIVE_THINKING_MODELS].sort());
|
||||
expect(flaggedModels).toEqual(expect.arrayContaining([...EXPECTED_CURRENT_ADAPTIVE_THINKING_MODELS].sort()));
|
||||
expect(flaggedModels).toEqual(
|
||||
flaggedModels.filter((modelId) => /(opus[-.]4[-.][678]|sonnet[-.]4[-.]6)/.test(modelId)),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,8 +12,8 @@ interface CapturedRequest {
|
||||
|
||||
function createModel(baseUrl: string, compat?: Model<"anthropic-messages">["compat"]): Model<"anthropic-messages"> {
|
||||
return {
|
||||
id: "claude-opus-4-7",
|
||||
name: "Claude Opus 4.7",
|
||||
id: "claude-opus-4-8",
|
||||
name: "Claude Opus 4.8",
|
||||
api: "anthropic-messages",
|
||||
provider: "test-anthropic",
|
||||
baseUrl,
|
||||
|
||||
@@ -85,7 +85,7 @@ describe("Anthropic forceAdaptiveThinking compat override", () => {
|
||||
|
||||
it("allows built-in adaptive models to opt out with compat.forceAdaptiveThinking false", async () => {
|
||||
const model: Model<"anthropic-messages"> = {
|
||||
...getModel("anthropic", "claude-opus-4-7"),
|
||||
...getModel("anthropic", "claude-opus-4-8"),
|
||||
compat: { forceAdaptiveThinking: false },
|
||||
};
|
||||
const payload = await capturePayload(model, { reasoning: "medium" });
|
||||
|
||||
@@ -22,9 +22,9 @@ function makeContext(): Context {
|
||||
};
|
||||
}
|
||||
|
||||
describe.skipIf(!process.env.ANTHROPIC_API_KEY)("Anthropic Opus 4.7 smoke", () => {
|
||||
it("streams Claude Opus 4.7 with reasoning enabled", { retry: 2, timeout: 30000 }, async () => {
|
||||
const model = getModel("anthropic", "claude-opus-4-7");
|
||||
describe.skipIf(!process.env.ANTHROPIC_API_KEY)("Anthropic Opus 4.8 smoke", () => {
|
||||
it("streams Claude Opus 4.8 with reasoning enabled", { retry: 2, timeout: 30000 }, async () => {
|
||||
const model = getModel("anthropic", "claude-opus-4-8");
|
||||
let capturedPayload: AnthropicThinkingPayload | undefined;
|
||||
const s = streamSimple(model, makeContext(), {
|
||||
reasoning: "high",
|
||||
@@ -53,12 +53,12 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY)("Anthropic Opus 4.7 smoke", () =
|
||||
const thinkingBlock = response.content.find((block) => block.type === "thinking");
|
||||
expect(thinkingBlock?.type).toBe("thinking");
|
||||
if (!thinkingBlock || thinkingBlock.type !== "thinking") {
|
||||
throw new Error("Expected thinking block from Claude Opus 4.7");
|
||||
throw new Error("Expected thinking block from Claude Opus 4.8");
|
||||
}
|
||||
expect(typeof thinkingBlock.thinkingSignature).toBe("string");
|
||||
const thinkingSignature = thinkingBlock.thinkingSignature;
|
||||
if (!thinkingSignature) {
|
||||
throw new Error("Expected thinking signature from Claude Opus 4.7");
|
||||
throw new Error("Expected thinking signature from Claude Opus 4.8");
|
||||
}
|
||||
expect(thinkingSignature.length).toBeGreaterThan(0);
|
||||
|
||||
@@ -125,22 +125,22 @@ describe("Anthropic thinking disable payload", () => {
|
||||
expect(payload.output_config).toBeUndefined();
|
||||
});
|
||||
|
||||
it("sends thinking.type=disabled for Claude Opus 4.7 when thinking is off", async () => {
|
||||
const payload = await capturePayload(getModel("anthropic", "claude-opus-4-7"));
|
||||
it("sends thinking.type=disabled for Claude Opus 4.8 when thinking is off", async () => {
|
||||
const payload = await capturePayload(getModel("anthropic", "claude-opus-4-8"));
|
||||
|
||||
expect(payload.thinking).toEqual({ type: "disabled" });
|
||||
expect(payload.output_config).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses adaptive thinking for Claude Opus 4.7 when reasoning is enabled", async () => {
|
||||
const payload = await capturePayload(getModel("anthropic", "claude-opus-4-7"), { reasoning: "high" });
|
||||
it("uses adaptive thinking for Claude Opus 4.8 when reasoning is enabled", async () => {
|
||||
const payload = await capturePayload(getModel("anthropic", "claude-opus-4-8"), { reasoning: "high" });
|
||||
|
||||
expect(payload.thinking).toEqual({ type: "adaptive", display: "summarized" });
|
||||
expect(payload.output_config).toEqual({ effort: "high" });
|
||||
});
|
||||
|
||||
it("maps xhigh reasoning to effort=xhigh for Claude Opus 4.7", async () => {
|
||||
const payload = await capturePayload(getModel("anthropic", "claude-opus-4-7"), { reasoning: "xhigh" });
|
||||
it("maps xhigh reasoning to effort=xhigh for Claude Opus 4.8", async () => {
|
||||
const payload = await capturePayload(getModel("anthropic", "claude-opus-4-8"), { reasoning: "xhigh" });
|
||||
|
||||
expect(payload.thinking).toEqual({ type: "adaptive", display: "summarized" });
|
||||
expect(payload.output_config).toEqual({ effort: "xhigh" });
|
||||
|
||||
@@ -98,7 +98,7 @@ describe("bedrock endpoint resolution", () => {
|
||||
|
||||
it("does not pin standard AWS endpoints when AWS_REGION is configured", async () => {
|
||||
process.env.AWS_REGION = "us-east-2";
|
||||
const model = getModel("amazon-bedrock", "us.anthropic.claude-opus-4-7");
|
||||
const model = getModel("amazon-bedrock", "us.anthropic.claude-opus-4-8");
|
||||
|
||||
const config = await captureClientConfig(model);
|
||||
|
||||
@@ -117,7 +117,7 @@ describe("bedrock endpoint resolution", () => {
|
||||
|
||||
it("still passes custom Bedrock endpoints through to the SDK client", async () => {
|
||||
process.env.AWS_REGION = "us-west-2";
|
||||
const baseModel = getModel("amazon-bedrock", "us.anthropic.claude-opus-4-7");
|
||||
const baseModel = getModel("amazon-bedrock", "us.anthropic.claude-opus-4-8");
|
||||
const model: Model<"bedrock-converse-stream"> = {
|
||||
...baseModel,
|
||||
baseUrl: "https://bedrock-vpc.example.com",
|
||||
|
||||
@@ -53,12 +53,12 @@ async function capturePayload(
|
||||
}
|
||||
|
||||
describe("Bedrock thinking payload", () => {
|
||||
it("uses adaptive thinking for Claude Opus 4.7 when reasoning is enabled", async () => {
|
||||
it("uses adaptive thinking for Claude Opus 4.8 when reasoning is enabled", async () => {
|
||||
const baseModel = getModel("amazon-bedrock", "global.anthropic.claude-opus-4-6-v1");
|
||||
const model: Model<"bedrock-converse-stream"> = {
|
||||
...baseModel,
|
||||
id: "global.anthropic.claude-opus-4-7-v1",
|
||||
name: "Claude Opus 4.7 (Global)",
|
||||
id: "global.anthropic.claude-opus-4-8-v1",
|
||||
name: "Claude Opus 4.8 (Global)",
|
||||
};
|
||||
|
||||
const payload = await capturePayload(model);
|
||||
@@ -68,12 +68,12 @@ describe("Bedrock thinking payload", () => {
|
||||
expect(payload.additionalModelRequestFields?.anthropic_beta).toBeUndefined();
|
||||
});
|
||||
|
||||
it("maps xhigh reasoning to effort=xhigh for Claude Opus 4.7", async () => {
|
||||
it("maps xhigh reasoning to effort=xhigh for Claude Opus 4.8", async () => {
|
||||
const baseModel = getModel("amazon-bedrock", "global.anthropic.claude-opus-4-6-v1");
|
||||
const model: Model<"bedrock-converse-stream"> = {
|
||||
...baseModel,
|
||||
id: "global.anthropic.claude-opus-4-7-v1",
|
||||
name: "Claude Opus 4.7 (Global)",
|
||||
id: "global.anthropic.claude-opus-4-8-v1",
|
||||
name: "Claude Opus 4.8 (Global)",
|
||||
};
|
||||
|
||||
const payload = await capturePayload(model, { reasoning: "xhigh" });
|
||||
@@ -101,8 +101,8 @@ describe("Bedrock thinking payload", () => {
|
||||
const baseModel = getModel("amazon-bedrock", "global.anthropic.claude-opus-4-6-v1");
|
||||
const model: Model<"bedrock-converse-stream"> = {
|
||||
...baseModel,
|
||||
id: "global.anthropic.claude-opus-4-7-v1",
|
||||
name: "Claude Opus 4.7 (Global)",
|
||||
id: "global.anthropic.claude-opus-4-8-v1",
|
||||
name: "Claude Opus 4.8 (Global)",
|
||||
};
|
||||
|
||||
const payload = await capturePayload(model, { region: "us-gov-west-1" });
|
||||
|
||||
@@ -97,8 +97,8 @@ function createFireworksModel(compat?: Model<"anthropic-messages">["compat"]): M
|
||||
|
||||
function createAnthropicModel(): Model<"anthropic-messages"> {
|
||||
return {
|
||||
id: "claude-opus-4-7",
|
||||
name: "Claude Opus 4.7",
|
||||
id: "claude-opus-4-8",
|
||||
name: "Claude Opus 4.8",
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "http://127.0.0.1:0", // overridden by captureAnthropicRequest
|
||||
|
||||
@@ -60,10 +60,10 @@ describe("openai-completions responseModel", () => {
|
||||
|
||||
it("surfaces routed chunk.model on responseModel without changing model", async () => {
|
||||
mockState.chunks = [
|
||||
{ id: "chatcmpl-1", model: "anthropic/claude-opus-4.7", choices: [{ index: 0, delta: { content: "hi" } }] },
|
||||
{ id: "chatcmpl-1", model: "anthropic/claude-opus-4.8", choices: [{ index: 0, delta: { content: "hi" } }] },
|
||||
{
|
||||
id: "chatcmpl-1",
|
||||
model: "anthropic/claude-opus-4.7",
|
||||
model: "anthropic/claude-opus-4.8",
|
||||
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
||||
usage: {
|
||||
prompt_tokens: 10,
|
||||
@@ -81,7 +81,7 @@ describe("openai-completions responseModel", () => {
|
||||
);
|
||||
|
||||
expect(message.model).toBe("openrouter/auto");
|
||||
expect(message.responseModel).toBe("anthropic/claude-opus-4.7");
|
||||
expect(message.responseModel).toBe("anthropic/claude-opus-4.8");
|
||||
expect(message.provider).toBe("openrouter");
|
||||
expect(message.stopReason).toBe("stop");
|
||||
});
|
||||
|
||||
@@ -8,8 +8,14 @@ describe("getSupportedThinkingLevels", () => {
|
||||
expect(getSupportedThinkingLevels(model!)).toContain("xhigh");
|
||||
});
|
||||
|
||||
it("includes xhigh for Anthropic Opus 4.7 on anthropic-messages API", () => {
|
||||
const model = getModel("anthropic", "claude-opus-4-7");
|
||||
it("includes xhigh for Anthropic Opus 4.8 on anthropic-messages API", () => {
|
||||
const model = getModel("anthropic", "claude-opus-4-8");
|
||||
expect(model).toBeDefined();
|
||||
expect(getSupportedThinkingLevels(model!)).toContain("xhigh");
|
||||
});
|
||||
|
||||
it("includes xhigh for Anthropic Opus 4.8 on anthropic-messages API", () => {
|
||||
const model = getModel("anthropic", "claude-opus-4-8");
|
||||
expect(model).toBeDefined();
|
||||
expect(getSupportedThinkingLevels(model!)).toContain("xhigh");
|
||||
});
|
||||
@@ -26,6 +32,18 @@ describe("getSupportedThinkingLevels", () => {
|
||||
expect(getSupportedThinkingLevels(model!)).toContain("xhigh");
|
||||
});
|
||||
|
||||
it("includes only medium/high/xhigh for OpenAI GPT-5.5 Pro", () => {
|
||||
const model = getModel("openai", "gpt-5.5-pro");
|
||||
expect(model).toBeDefined();
|
||||
expect(getSupportedThinkingLevels(model!)).toEqual(["medium", "high", "xhigh"]);
|
||||
});
|
||||
|
||||
it("includes only medium/high/xhigh for OpenRouter GPT-5.5 Pro", () => {
|
||||
const model = getModel("openrouter", "openai/gpt-5.5-pro");
|
||||
expect(model).toBeDefined();
|
||||
expect(getSupportedThinkingLevels(model!)).toEqual(["medium", "high", "xhigh"]);
|
||||
});
|
||||
|
||||
it("includes only high/xhigh plus off for DeepSeek V4 Flash on the DeepSeek provider", () => {
|
||||
const model = getModel("deepseek", "deepseek-v4-flash");
|
||||
expect(model).toBeDefined();
|
||||
|
||||
Reference in New Issue
Block a user