Support adaptive thinking for Anthropic-compatible aliases
closes #4790
This commit is contained in:
@@ -1723,6 +1723,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"max"},
|
||||
input: ["text", "image"],
|
||||
@@ -1741,6 +1742,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"xhigh"},
|
||||
input: ["text", "image"],
|
||||
@@ -1827,6 +1829,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "anthropic",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
@@ -2826,6 +2829,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "cloudflare-ai-gateway",
|
||||
baseUrl: "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"max"},
|
||||
input: ["text", "image"],
|
||||
@@ -2844,6 +2848,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "cloudflare-ai-gateway",
|
||||
baseUrl: "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"xhigh"},
|
||||
input: ["text", "image"],
|
||||
@@ -2896,6 +2901,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "cloudflare-ai-gateway",
|
||||
baseUrl: "https://gateway.ai.cloudflare.com/v1/{CLOUDFLARE_ACCOUNT_ID}/{CLOUDFLARE_GATEWAY_ID}/anthropic",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
@@ -3924,6 +3930,7 @@ export const MODELS = {
|
||||
provider: "github-copilot",
|
||||
baseUrl: "https://api.individual.githubcopilot.com",
|
||||
headers: {"User-Agent":"GitHubCopilotChat/0.35.0","Editor-Version":"vscode/1.107.0","Editor-Plugin-Version":"copilot-chat/0.35.0","Copilot-Integration-Id":"vscode-chat"},
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"max"},
|
||||
input: ["text", "image"],
|
||||
@@ -3943,6 +3950,7 @@ export const MODELS = {
|
||||
provider: "github-copilot",
|
||||
baseUrl: "https://api.individual.githubcopilot.com",
|
||||
headers: {"User-Agent":"GitHubCopilotChat/0.35.0","Editor-Version":"vscode/1.107.0","Editor-Plugin-Version":"copilot-chat/0.35.0","Copilot-Integration-Id":"vscode-chat"},
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"xhigh"},
|
||||
input: ["text", "image"],
|
||||
@@ -3981,6 +3989,7 @@ export const MODELS = {
|
||||
provider: "github-copilot",
|
||||
baseUrl: "https://api.individual.githubcopilot.com",
|
||||
headers: {"User-Agent":"GitHubCopilotChat/0.35.0","Editor-Version":"vscode/1.107.0","Editor-Plugin-Version":"copilot-chat/0.35.0","Copilot-Integration-Id":"vscode-chat"},
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
@@ -7226,6 +7235,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "opencode",
|
||||
baseUrl: "https://opencode.ai/zen",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"max"},
|
||||
input: ["text", "image"],
|
||||
@@ -7244,6 +7254,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "opencode",
|
||||
baseUrl: "https://opencode.ai/zen",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"xhigh"},
|
||||
input: ["text", "image"],
|
||||
@@ -7296,6 +7307,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "opencode",
|
||||
baseUrl: "https://opencode.ai/zen",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
@@ -13369,6 +13381,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "vercel-ai-gateway",
|
||||
baseUrl: "https://ai-gateway.vercel.sh",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"max"},
|
||||
input: ["text", "image"],
|
||||
@@ -13387,6 +13400,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "vercel-ai-gateway",
|
||||
baseUrl: "https://ai-gateway.vercel.sh",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
thinkingLevelMap: {"xhigh":"xhigh"},
|
||||
input: ["text", "image"],
|
||||
@@ -13439,6 +13453,7 @@ export const MODELS = {
|
||||
api: "anthropic-messages",
|
||||
provider: "vercel-ai-gateway",
|
||||
baseUrl: "https://ai-gateway.vercel.sh",
|
||||
compat: {"forceAdaptiveThinking":true},
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
|
||||
@@ -164,7 +164,9 @@ export type AnthropicThinkingDisplay = "summarized" | "omitted";
|
||||
const FINE_GRAINED_TOOL_STREAMING_BETA = "fine-grained-tool-streaming-2025-05-14";
|
||||
const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
|
||||
|
||||
function getAnthropicCompat(model: Model<"anthropic-messages">): Required<AnthropicMessagesCompat> {
|
||||
function getAnthropicCompat(
|
||||
model: Model<"anthropic-messages">,
|
||||
): Required<Omit<AnthropicMessagesCompat, "forceAdaptiveThinking">> {
|
||||
// Auto-detect session affinity and cache control support from provider
|
||||
const isFireworks = model.provider === "fireworks";
|
||||
const isCloudflareAiGatewayAnthropic =
|
||||
@@ -181,29 +183,34 @@ function getAnthropicCompat(model: Model<"anthropic-messages">): Required<Anthro
|
||||
export interface AnthropicOptions extends StreamOptions {
|
||||
/**
|
||||
* Enable extended thinking.
|
||||
* For Opus 4.6 and Sonnet 4.6: uses adaptive thinking (model decides when/how much to think).
|
||||
* For adaptive thinking models: the model decides when/how much to think.
|
||||
* For older models: uses budget-based thinking with thinkingBudgetTokens.
|
||||
* Default: undefined (thinking is omitted unless `streamSimpleAnthropic()` maps
|
||||
* a simple reasoning level to this option, or callers set it explicitly).
|
||||
*/
|
||||
thinkingEnabled?: boolean;
|
||||
/**
|
||||
* Token budget for extended thinking (older models only).
|
||||
* Ignored for Opus 4.6 and Sonnet 4.6, which use adaptive thinking.
|
||||
* Ignored for adaptive thinking models.
|
||||
* Default: 1024 when `thinkingEnabled` is true and no budget is provided.
|
||||
*/
|
||||
thinkingBudgetTokens?: number;
|
||||
/**
|
||||
* Effort level for adaptive thinking (Opus 4.6+ and Sonnet 4.6).
|
||||
* Effort level for adaptive thinking models.
|
||||
* Controls how much thinking Claude allocates:
|
||||
* - "max": Always thinks with no constraints (Opus 4.6 only)
|
||||
* - "xhigh": Highest reasoning level (Opus 4.7)
|
||||
* - "high": Always thinks, deep reasoning (default)
|
||||
* - "high": Always thinks, deep reasoning
|
||||
* - "medium": Moderate thinking, may skip for simple queries
|
||||
* - "low": Minimal thinking, skips for simple tasks
|
||||
* Ignored for older models.
|
||||
* Default: omitted unless `streamSimpleAnthropic()` maps a simple reasoning
|
||||
* level to this option.
|
||||
*/
|
||||
effort?: AnthropicEffort;
|
||||
/**
|
||||
* Controls how thinking content is returned in API responses.
|
||||
* - "summarized": Thinking blocks contain summarized thinking text (default here).
|
||||
* - "summarized": Thinking blocks contain summarized thinking text.
|
||||
* - "omitted": Thinking blocks return an empty thinking field; the encrypted
|
||||
* signature still travels back for multi-turn continuity. Use for faster
|
||||
* time-to-first-text-token when your UI does not surface thinking.
|
||||
@@ -211,9 +218,21 @@ export interface AnthropicOptions extends StreamOptions {
|
||||
* Note: Anthropic's API default for Claude Opus 4.7 and Claude Mythos Preview
|
||||
* is "omitted". We default to "summarized" here to keep behavior consistent
|
||||
* with older Claude 4 models. Set this explicitly to "omitted" to opt in.
|
||||
* Default: "summarized" when thinking is enabled.
|
||||
*/
|
||||
thinkingDisplay?: AnthropicThinkingDisplay;
|
||||
/**
|
||||
* Whether to request the interleaved thinking beta header for non-adaptive
|
||||
* thinking models. Adaptive thinking models have interleaved thinking built in,
|
||||
* so the header is skipped for them regardless of this setting.
|
||||
* Default: true.
|
||||
*/
|
||||
interleavedThinking?: boolean;
|
||||
/**
|
||||
* Anthropic tool choice behavior. String values map to Anthropic's built-in
|
||||
* choices; `{ type: "tool", name }` forces a specific tool.
|
||||
* Default: omitted (Anthropic default behavior, currently equivalent to auto).
|
||||
*/
|
||||
toolChoice?: "auto" | "any" | "none" | { type: "tool"; name: string };
|
||||
/**
|
||||
* Pre-built Anthropic client instance. When provided, skips internal client
|
||||
@@ -686,21 +705,6 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
|
||||
return stream;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a model supports adaptive thinking (Opus 4.6+, Sonnet 4.6)
|
||||
*/
|
||||
function supportsAdaptiveThinking(modelId: string): boolean {
|
||||
// Adaptive-thinking model IDs (with or without date suffix)
|
||||
return (
|
||||
modelId.includes("opus-4-6") ||
|
||||
modelId.includes("opus-4.6") ||
|
||||
modelId.includes("opus-4-7") ||
|
||||
modelId.includes("opus-4.7") ||
|
||||
modelId.includes("sonnet-4-6") ||
|
||||
modelId.includes("sonnet-4.6")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map ThinkingLevel to Anthropic effort levels for adaptive thinking.
|
||||
* Note: effort "max" is only valid on Opus 4.6, while Opus 4.7 supports "xhigh".
|
||||
@@ -740,9 +744,9 @@ export const streamSimpleAnthropic: StreamFunction<"anthropic-messages", SimpleS
|
||||
return streamAnthropic(model, context, { ...base, thinkingEnabled: false } satisfies AnthropicOptions);
|
||||
}
|
||||
|
||||
// For Opus 4.6 and Sonnet 4.6: use adaptive thinking with effort level
|
||||
// For older models: use budget-based thinking
|
||||
if (supportsAdaptiveThinking(model.id)) {
|
||||
// For models with adaptive thinking: use an effort level.
|
||||
// For older models: use budget-based thinking.
|
||||
if (model.compat?.forceAdaptiveThinking === true) {
|
||||
const effort = mapThinkingLevelToEffort(model, options.reasoning);
|
||||
return streamAnthropic(model, context, {
|
||||
...base,
|
||||
@@ -781,9 +785,8 @@ function createClient(
|
||||
dynamicHeaders?: Record<string, string>,
|
||||
sessionId?: string,
|
||||
): { client: Anthropic; isOAuthToken: boolean } {
|
||||
// Adaptive thinking models (Opus 4.6, Sonnet 4.6) have interleaved thinking built-in.
|
||||
// The beta header is deprecated on Opus 4.6 and redundant on Sonnet 4.6, so skip it.
|
||||
const needsInterleavedBeta = interleavedThinking && !supportsAdaptiveThinking(model.id);
|
||||
// Adaptive thinking models have interleaved thinking built in, so skip the beta header.
|
||||
const needsInterleavedBeta = interleavedThinking && model.compat?.forceAdaptiveThinking !== true;
|
||||
const betaFeatures: string[] = [];
|
||||
if (useFineGrainedToolStreamingBeta) {
|
||||
betaFeatures.push(FINE_GRAINED_TOOL_STREAMING_BETA);
|
||||
@@ -939,14 +942,13 @@ function buildParams(
|
||||
);
|
||||
}
|
||||
|
||||
// Configure thinking mode: adaptive (Opus 4.6+ and Sonnet 4.6),
|
||||
// budget-based (older models), or explicitly disabled.
|
||||
// Configure thinking mode: adaptive, budget-based, or explicitly disabled.
|
||||
if (model.reasoning) {
|
||||
if (options?.thinkingEnabled) {
|
||||
// Default to "summarized" so Opus 4.7 and Mythos Preview behave like
|
||||
// older Claude 4 models (whose API default is also "summarized").
|
||||
const display: AnthropicThinkingDisplay = options.thinkingDisplay ?? "summarized";
|
||||
if (supportsAdaptiveThinking(model.id)) {
|
||||
if (model.compat?.forceAdaptiveThinking === true) {
|
||||
// Adaptive thinking: Claude decides when and how much to think.
|
||||
params.thinking = { type: "adaptive", display };
|
||||
if (options.effort) {
|
||||
|
||||
@@ -435,6 +435,16 @@ export interface AnthropicMessagesCompat {
|
||||
* Default: true.
|
||||
*/
|
||||
supportsCacheControlOnTools?: boolean;
|
||||
/**
|
||||
* Whether to force adaptive thinking (`thinking.type: "adaptive"` plus
|
||||
* `output_config.effort`) regardless of the model id. Built-in models that
|
||||
* require adaptive thinking set this in generated metadata. Custom
|
||||
* Anthropic-compatible providers can set this to `true` for any model whose
|
||||
* upstream requires the adaptive format. Set to `false` to
|
||||
* opt out on overridden built-in models.
|
||||
* Default: false.
|
||||
*/
|
||||
forceAdaptiveThinking?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user