feat(providers): refresh Fable, Cursor, and Devin catalogs (#12367)

Validado em lote numa worktree combinada com #12524, #12538, #12277 e #12367 sobre o tip de release/v3.8.51: os quatro boardaram sem conflito (áreas disjuntas — zai-web, nvidia, clova, cursor/devin/fable). typecheck:core limpo, check:provider-consistency OK (272 entradas REGISTRY, 355 providers canônicos), check:known-symbols OK, e 305/305 nos testes tocados pelos quatro PRs. Os IDs de modelo adicionados foram conferidos individualmente. Obrigado, @backryun.
This commit is contained in:
backryun
2026-09-03 19:50:41 +09:00
committed by GitHub
parent 82c64d76d3
commit 6795783228
42 changed files with 1835 additions and 921 deletions

View File

@@ -45,7 +45,7 @@ export const CLI_TOOLS: Record<string, CliCatalogEntry> = {
name: "Claude Fable",
alias: "fable",
envKey: "ANTHROPIC_DEFAULT_FABLE_MODEL",
defaultValue: _cc.fable ? `cc/${_cc.fable}` : "cc/claude-fable-5",
defaultValue: _cc.fable ? `cc/${_cc.fable}` : "cc/claude-fable-5-1",
isTopLevel: true,
},
{

View File

@@ -24,9 +24,13 @@ export interface ModelSpec {
// Model ONLY supports adaptive thinking: manual extended thinking was removed. Sending
// `thinking.type:"enabled"` or any `thinking.budget_tokens` returns HTTP 400; reasoning
// is steered exclusively by `output_config.effort` (low/medium/high/xhigh/max). True for
// Claude Opus 4.7 and later (Opus 4.7/4.8/5, Fable 5). Per Anthropic's migration guide,
// Claude Opus 4.7 and later (Opus 4.7/4.8/5, Fable 5/5.1). Per Anthropic's migration guide,
// any request that tries to set a fixed thinking budget gets a 400 error.
adaptiveThinkingOnly?: boolean;
// The model rejects tool_choice values that require a tool call. Keep tools available,
// but normalize a forced choice to the default auto behavior before dispatch. Fable 5.1 always runs
// adaptive thinking, so forced tool use cannot be combined with any valid request.
rejectsForcedToolChoice?: boolean;
// Highest effort accepted while `thinking.type:"disabled"` is present. Claude Opus 5
// rejects disabled thinking with xhigh/max, while accepting it through high.
maxEffortWhenThinkingDisabled?: "high";
@@ -371,6 +375,21 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
aliases: BEDROCK_CLAUDE_ALIASES("claude-opus-4-7", "claude-opus-4.7"),
},
// ── Claude Fable 5.1 ────────────────────────────────────────────
"claude-fable-5-1": {
maxOutputTokens: 128000,
contextWindow: 1000000,
defaultThinkingBudget: 32000,
thinkingBudgetCap: 120000,
supportsThinking: true,
supportsTools: true,
supportsVision: true,
rejectsThinkingDisabled: true,
adaptiveThinkingOnly: true,
rejectsForcedToolChoice: true,
aliases: BEDROCK_CLAUDE_ALIASES("claude-fable-5-1"),
},
// ── Claude Fable 5 ──────────────────────────────────────────────
"claude-fable-5": {
maxOutputTokens: 128000,
@@ -849,9 +868,39 @@ export function normalizeThinkingForModel<T extends Record<string, unknown>>(
getModelSpec(modelId)?.rejectsThinkingDisabled
) {
const { thinking: _omitted, ...rest } = body as Record<string, unknown>;
return rest as T;
return normalizeForcedToolChoiceForModel(rest as T, modelId);
}
return body;
return normalizeForcedToolChoiceForModel(body, modelId);
}
/**
* Normalize tool-choice constraints that a resolved model cannot accept.
*
* Claude Fable 5.1 always uses adaptive thinking and rejects tool choices that force
* either any tool or one named tool. Preserve the declared tools and every unrelated
* request field, but drop the choice to select the default `auto` behavior so routing a
* request to Fable 5.1 does not turn a recoverable preference into an upstream 400.
*/
export function normalizeForcedToolChoiceForModel<T extends Record<string, unknown>>(
body: T,
modelId: string
): T {
if (!getModelSpec(modelId)?.rejectsForcedToolChoice) return body;
const toolChoice = body.tool_choice;
const forced =
toolChoice === "required" ||
toolChoice === "any" ||
(toolChoice !== null &&
typeof toolChoice === "object" &&
!Array.isArray(toolChoice) &&
["any", "tool", "function"].includes(
String((toolChoice as Record<string, unknown>).type || "").toLowerCase()
));
if (!forced) return body;
const { tool_choice: _omitted, ...rest } = body;
return rest as T;
}
export function capMaxOutputTokens(modelId: string, requested?: number): number | undefined {

View File

@@ -7,10 +7,12 @@ import { DEFAULT_PRICING_OAUTH } from "./oauth-subscriptions";
import { DEFAULT_PRICING_FRONTIER } from "./frontier-labs";
import { DEFAULT_PRICING_INFERENCE } from "./inference-hosts";
import { DEFAULT_PRICING_REGIONAL } from "./regional";
import { DEFAULT_PRICING_DEVIN } from "./devin";
export const DEFAULT_PRICING = {
...DEFAULT_PRICING_OAUTH,
...DEFAULT_PRICING_FRONTIER,
...DEFAULT_PRICING_INFERENCE,
...DEFAULT_PRICING_REGIONAL,
...DEFAULT_PRICING_DEVIN,
};

View File

@@ -0,0 +1,142 @@
type DevinTokenPricing = {
input: number;
cached: number;
output: number;
};
const QUALITY_EFFORTS = ["max", "xhigh", "high", "medium", "low"] as const;
const GPT_EFFORTS = ["max", "xhigh", "high", "medium", "low", "none"] as const;
function variantIds(base: string, efforts: readonly string[]): string[] {
return efforts.map((effort) => `${base}-${effort}`);
}
function fastVariantIds(base: string): string[] {
return QUALITY_EFFORTS.map((effort) => `${base}-${effort}-fast`);
}
function priorityVariantIds(base: string): string[] {
return GPT_EFFORTS.map((effort) => `${base}-${effort}-priority`);
}
function priced(ids: readonly string[], pricing: DevinTokenPricing) {
return Object.fromEntries(ids.map((id) => [id, pricing]));
}
const CLAUDE_FABLE_5_1 = { input: 10, cached: 0.25, output: 50 };
const CLAUDE_OPUS = { input: 5, cached: 0.5, output: 25 };
const CLAUDE_OPUS_FAST = { input: 10, cached: 1, output: 50 };
const CLAUDE_SONNET_5 = { input: 2, cached: 0.2, output: 10 };
const CLAUDE_SONNET_4_6 = { input: 3, cached: 0.3, output: 15 };
const CLAUDE_HAIKU_4_5 = { input: 1, cached: 0.1, output: 5 };
const GPT_5_6_SOL = { input: 4, cached: 0.4, output: 20 };
const GPT_5_6_SOL_FAST = { input: 8, cached: 0.8, output: 40 };
const GPT_5_6_TERRA = { input: 2, cached: 0.2, output: 12 };
const GPT_5_6_TERRA_FAST = { input: 4, cached: 0.4, output: 24 };
const GPT_5_6_LUNA = { input: 0.2, cached: 0.02, output: 1.2 };
const GPT_5_6_LUNA_FAST = { input: 0.4, cached: 0.04, output: 2.4 };
/**
* Exact per-UID rates returned by Devin's authenticated live catalog on
* 2026-09-02. Rates are USD per one million tokens.
*/
export const DEVIN_MODEL_PRICING: Record<string, DevinTokenPricing> = {
...priced(variantIds("claude-fable-5-1", QUALITY_EFFORTS), CLAUDE_FABLE_5_1),
...priced(variantIds("claude-opus-5", QUALITY_EFFORTS), CLAUDE_OPUS),
...priced(fastVariantIds("claude-opus-5"), CLAUDE_OPUS_FAST),
...priced(variantIds("claude-opus-4-8", QUALITY_EFFORTS), CLAUDE_OPUS),
...priced(fastVariantIds("claude-opus-4-8"), CLAUDE_OPUS_FAST),
...priced(variantIds("claude-sonnet-5", QUALITY_EFFORTS), CLAUDE_SONNET_5),
...priced(
[
"claude-sonnet-4-6",
"claude-sonnet-4-6-thinking",
"claude-sonnet-4-6-1m",
"claude-sonnet-4-6-thinking-1m",
],
CLAUDE_SONNET_4_6
),
MODEL_PRIVATE_11: CLAUDE_HAIKU_4_5,
...priced(variantIds("gpt-5-6-sol", GPT_EFFORTS), GPT_5_6_SOL),
...priced(priorityVariantIds("gpt-5-6-sol"), GPT_5_6_SOL_FAST),
...priced(variantIds("gpt-5-6-terra", GPT_EFFORTS), GPT_5_6_TERRA),
...priced(priorityVariantIds("gpt-5-6-terra"), GPT_5_6_TERRA_FAST),
...priced(variantIds("gpt-5-6-luna", GPT_EFFORTS), GPT_5_6_LUNA),
...priced(priorityVariantIds("gpt-5-6-luna"), GPT_5_6_LUNA_FAST),
...priced(variantIds("kimi-k3", ["max", "high", "low"]), {
input: 3,
cached: 0.3,
output: 15,
}),
"kimi-k2-7": { input: 0.95, cached: 0.19, output: 4 },
...priced(variantIds("glm-5-3", ["max", "high", "low"]), {
input: 1.4,
cached: 0.26,
output: 4.4,
}),
...priced(variantIds("glm-5-3-flash", ["max", "high", "low"]), {
input: 0.15,
cached: 0.03,
output: 0.5,
}),
...priced(["swe-1-7", "swe-1-7-medium"], {
input: 0.5,
cached: 0.2,
output: 2.5,
}),
...priced(["swe-1-7-lightning", "swe-1-7-lightning-medium"], {
input: 2.5,
cached: 1,
output: 12.5,
}),
adaptive: { input: 0.5, cached: 0.1, output: 2 },
...priced(variantIds("grok-4-6", ["xhigh", "high", "medium", "low"]), {
input: 2,
cached: 0.3,
output: 6,
}),
...priced(variantIds("inkling", ["max", "xhigh", "high", "medium", "low", "none"]), {
input: 1.4,
cached: 0.26,
output: 4.4,
}),
...priced(variantIds("deepseek-v4-flash", ["max", "high", "low"]), {
input: 0.14,
cached: 0.03,
output: 0.28,
}),
...priced(variantIds("nemotron-3-ultra", ["high", "medium", "none"]), {
input: 0.6,
cached: 0.12,
output: 2.4,
}),
...priced(variantIds("gemini-3-7-flash", ["high", "medium", "low"]), {
input: 1.5,
cached: 0.15,
output: 7.5,
}),
...priced(variantIds("gemini-3-1-pro", ["high", "low"]), {
input: 2,
cached: 0.2,
output: 12,
}),
...priced(variantIds("deepseek-v4-pro", ["max", "high", "low"]), {
input: 1.32,
cached: 0.04,
output: 3.96,
}),
};
// Each transport gets its own provider namespace. They share today's upstream
// rate snapshot, but can diverge independently if Devin changes one channel.
export const DEFAULT_PRICING_DEVIN = {
"devin-cli": { ...DEVIN_MODEL_PRICING },
dv: { ...DEVIN_MODEL_PRICING },
"devin-desktop": { ...DEVIN_MODEL_PRICING },
"devin-cli-agentic": { ...DEVIN_MODEL_PRICING },
dva: { ...DEVIN_MODEL_PRICING },
};

View File

@@ -8,6 +8,7 @@ import {
GPT_5_6_LUNA_PRICING,
GPT_5_6_SOL_PRICING,
GPT_5_6_TERRA_PRICING,
CLAUDE_FABLE_5_1_PRICING,
CLAUDE_FABLE_5_PRICING,
CLAUDE_OPUS_5_PRICING,
CLAUDE_OPUS_4_PRICING,
@@ -213,6 +214,7 @@ export const DEFAULT_PRICING_FRONTIER = {
// Common model IDs (without dates) used across providers
// Intentional duplicates of dot-notation variants (e.g. claude-opus-4.6)
// to cover hyphen-notation IDs (claude-opus-4-6) used by some clients
"claude-fable-5-1": CLAUDE_FABLE_5_1_PRICING,
"claude-fable-5": CLAUDE_FABLE_5_PRICING,
"claude-opus-5": CLAUDE_OPUS_5_PRICING,
"claude-sonnet-5": CLAUDE_SONNET_5_PRICING,

View File

@@ -3,6 +3,7 @@
* Pure data; merged by default-pricing.ts via spread (god-file decomposition; semantic split).
*/
import {
CLAUDE_FABLE_5_1_PRICING,
CLAUDE_OPUS_5_PRICING,
GEMINI_3_7_FLASH_PROMO_PRICING,
GPT_5_3_CODEX_PRICING,
@@ -20,6 +21,7 @@ const ANTIGRAVITY_GEMINI_3_7_PRICING = {
export const DEFAULT_PRICING_OAUTH = {
cc: {
"claude-fable-5-1": CLAUDE_FABLE_5_1_PRICING,
"claude-fable-5": {
input: 10.0,
output: 50.0,

View File

@@ -60,6 +60,14 @@ export const CLAUDE_FABLE_5_PRICING = {
cache_creation: 15.0,
};
export const CLAUDE_FABLE_5_1_PRICING = {
input: 10.0,
output: 50.0,
cached: 0.25,
reasoning: 50.0,
cache_creation: 12.5,
};
export const CLAUDE_OPUS_5_PRICING = {
input: 5.0,
output: 25.0,