Skip to content

Commit 9123357

Browse files
committed
🤖 chore: update models.json and fix Opus 4.5 specs
- Update models.json from LiteLLM - Fix claude-opus-4-5 max_output_tokens: 64k (matches AI SDK) - Update claude-opus-4-5 pricing: $5/$25 per million tokens (price drop) - This fixes the SDK warning about maxOutputTokens + thinkingBudget > max _Generated with `mux`_
1 parent 375a80f commit 9123357

File tree

7 files changed

+26775
-26683
lines changed

7 files changed

+26775
-26683
lines changed

bun.lock

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
{
22
"lockfileVersion": 1,
3-
"configVersion": 0,
43
"workspaces": {
54
"": {
65
"name": "@coder/cmux",
76
"dependencies": {
8-
"@ai-sdk/anthropic": "^2.0.44",
9-
"@ai-sdk/google": "^2.0.38",
10-
"@ai-sdk/openai": "^2.0.66",
11-
"@ai-sdk/xai": "^2.0.33",
7+
"@ai-sdk/anthropic": "^2.0.47",
8+
"@ai-sdk/google": "^2.0.43",
9+
"@ai-sdk/openai": "^2.0.72",
10+
"@ai-sdk/xai": "^2.0.36",
1211
"@lydell/node-pty": "1.1.0",
13-
"@openrouter/ai-sdk-provider": "^1.2.2",
12+
"@openrouter/ai-sdk-provider": "^1.2.5",
1413
"@radix-ui/react-checkbox": "^1.3.3",
1514
"@radix-ui/react-dialog": "^1.1.15",
1615
"@radix-ui/react-dropdown-menu": "^2.1.16",
@@ -22,7 +21,7 @@
2221
"@radix-ui/react-tabs": "^1.1.13",
2322
"@radix-ui/react-toggle-group": "^1.1.11",
2423
"@radix-ui/react-tooltip": "^1.2.8",
25-
"ai": "^5.0.93",
24+
"ai": "^5.0.101",
2625
"ai-tokenizer": "^1.0.4",
2726
"chalk": "^5.6.2",
2827
"cors": "^2.8.5",
@@ -137,21 +136,21 @@
137136

138137
"@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="],
139138

140-
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-o8TfNXRzO/KZkBrcx+CL9LQsPhx7PHyqzUGjza3TJaF9WxfH1S5UQLAmEw8F7lQoHNLU0IX03WT8o8R/4JbUxQ=="],
139+
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YioBDTTQ6z2fijcOByG6Gj7me0ITqaJACprHROis7fXFzYIBzyAwxhsCnOrXO+oXv+9Ixddgy/Cahdmu84uRvQ=="],
141140

142-
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.10", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.3" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c++qOKfjKokTPAJ+vP9UXXNuTQ819yEDCZVXBhpZbgRly1P4fHTJbIAwuh+Qxxe9Bmtu8PEta0JGYZxc+hm7/Q=="],
141+
"@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.15", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-i1YVKzC1dg9LGvt+GthhD7NlRhz9J4+ZRj3KELU14IZ/MHPsOBiFeEoCCIDLR+3tqT8/+5nIsK3eZ7DFRfMfdw=="],
143142

144-
"@ai-sdk/google": ["@ai-sdk/google@2.0.38", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z+RFCxRA/dSd3eCkGBlnk79nz3jv8vwaW42gVc+qDuMofNfvjRz19rjnkFNuYQ6cEUcPKCo0P1rD/JLeTN2Z5A=="],
143+
"@ai-sdk/google": ["@ai-sdk/google@2.0.43", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-qO6giuoYCX/SdZScP/3VO5Xnbd392zm3HrTkhab/efocZU8J/VVEAcAUE1KJh0qOIAYllofRtpJIUGkRK8Q5rw=="],
145144

146-
"@ai-sdk/openai": ["@ai-sdk/openai@2.0.68", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-qUSLFkqgUoFArzBwttu0KWVAZYjbsdZGOklSJXpfZ2nDC61yseHxtcnuG8u6tqKnGXDh4eakEgREDWU2sRht7A=="],
145+
"@ai-sdk/openai": ["@ai-sdk/openai@2.0.72", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9j8Gdt9gFiUGFdQIjjynbC7+w8YQxkXje6dwAq1v2Pj17wmB3U0Td3lnEe/a+EnEysY3mdkc8dHPYc5BNev9NQ=="],
147146

148147
"@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bpYruxVLhrTbVH6CCq48zMJNeHu6FmHtEedl9FXckEgcIEAi036idFhJlcRwC1jNCwlacbzb8dPD7OAH1EKJaQ=="],
149148

150149
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="],
151150

152151
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="],
153152

154-
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.27", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-0+S+hxbAj8dA8/3dYQsmgkVkPcs8yptO1ueLWtJpa6PYjrdyliDcPSCZREL8aE76vHGvFsYlRABFfH9Ps2M8tg=="],
153+
"@ai-sdk/xai": ["@ai-sdk/xai@2.0.36", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.27", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tQuCDVNK4W4fiom59r2UnU7u9SAz58fpl5yKYoS9IbMOrDRO3fzQGWmj2p8MUvz9LzXf6hiyUkVNFGzzx+uZcw=="],
155154

156155
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
157156

@@ -523,7 +522,7 @@
523522

524523
"@npmcli/move-file": ["@npmcli/move-file@2.0.1", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ=="],
525524

526-
"@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@1.2.3", "", { "dependencies": { "@openrouter/sdk": "^0.1.8" }, "peerDependencies": { "ai": "^5.0.0", "zod": "^3.24.1 || ^v4" } }, "sha512-a6Nc8dPRHakRH9966YJ/HZJhLOds7DuPTscNZDoAr+Aw+tEFUlacSJMvb/b3gukn74mgbuaJRji9YOn62ipfVg=="],
525+
"@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@1.2.5", "", { "dependencies": { "@openrouter/sdk": "^0.1.8" }, "peerDependencies": { "ai": "^5.0.0", "zod": "^3.24.1 || ^v4" } }, "sha512-NrvJFPvdEUo6DYUQIVWPGfhafuZ2PAIX7+CUMKGknv8TcTNVo0TyP1y5SU7Bgjf/Wup9/74UFKUB07icOhVZjQ=="],
527526

528527
"@openrouter/sdk": ["@openrouter/sdk@0.1.11", "", { "dependencies": { "zod": "^3.25.0 || ^4.0.0" }, "peerDependencies": { "@tanstack/react-query": "^5", "react": "^18 || ^19", "react-dom": "^18 || ^19" }, "optionalPeers": ["@tanstack/react-query", "react", "react-dom"] }, "sha512-OuPc8qqidL/PUM8+9WgrOfSR9+b6rKIWiezGcUJ54iPTdh+Gye5Qjut6hrLWlOCMZE7Z853gN90r1ft4iChj7Q=="],
529528

@@ -1085,7 +1084,7 @@
10851084

10861085
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
10871086

1088-
"@vercel/oidc": ["@vercel/oidc@3.0.3", "", {}, "sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg=="],
1087+
"@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="],
10891088

10901089
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
10911090

@@ -1115,7 +1114,7 @@
11151114

11161115
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
11171116

1118-
"ai": ["ai@5.0.94", "", { "dependencies": { "@ai-sdk/gateway": "2.0.10", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-PQs3H8xDhORG/L6eyR+SxAKUsa0ORO4ENvRovzGgPmPGCIlwle6UD5VIMQFtj1gvZk+BKBUVEFFwtkTeJTAURw=="],
1117+
"ai": ["ai@5.0.101", "", { "dependencies": { "@ai-sdk/gateway": "2.0.15", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/P4fgs2PGYTBaZi192YkPikOudsl9vccA65F7J7LvoNTOoP5kh1yAsJPsKAy6FXU32bAngai7ft1UDyC3u7z5g=="],
11191118

11201119
"ai-tokenizer": ["ai-tokenizer@1.0.4", "", { "peerDependencies": { "ai": "^5.0.0" }, "optionalPeers": ["ai"] }, "sha512-BHOUljsmH0SEO9bULQL3sz6pJ4jv00r+NHxX3kR6tn1suAAj6DDN4njSk+sqCOI5Cm6FqizUhDfoYZ0R+5/WVQ=="],
11211120

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@
4545
"postinstall": "sh scripts/postinstall.sh"
4646
},
4747
"dependencies": {
48-
"@ai-sdk/anthropic": "^2.0.44",
49-
"@ai-sdk/google": "^2.0.38",
50-
"@ai-sdk/openai": "^2.0.66",
51-
"@ai-sdk/xai": "^2.0.33",
48+
"@ai-sdk/anthropic": "^2.0.47",
49+
"@ai-sdk/google": "^2.0.43",
50+
"@ai-sdk/openai": "^2.0.72",
51+
"@ai-sdk/xai": "^2.0.36",
5252
"@lydell/node-pty": "1.1.0",
53-
"@openrouter/ai-sdk-provider": "^1.2.2",
53+
"@openrouter/ai-sdk-provider": "^1.2.5",
5454
"@radix-ui/react-checkbox": "^1.3.3",
5555
"@radix-ui/react-dialog": "^1.1.15",
5656
"@radix-ui/react-dropdown-menu": "^2.1.16",
@@ -62,7 +62,7 @@
6262
"@radix-ui/react-tabs": "^1.1.13",
6363
"@radix-ui/react-toggle-group": "^1.1.11",
6464
"@radix-ui/react-tooltip": "^1.2.8",
65-
"ai": "^5.0.93",
65+
"ai": "^5.0.101",
6666
"ai-tokenizer": "^1.0.4",
6767
"chalk": "^5.6.2",
6868
"cors": "^2.8.5",

src/common/utils/ai/providerOptions.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { log } from "@/node/services/log";
2020
import type { MuxMessage } from "@/common/types/message";
2121
import { enforceThinkingPolicy } from "@/browser/utils/thinking/policy";
22+
import { getModelStats } from "@/common/utils/tokens/modelStats";
2223

2324
/**
2425
* OpenRouter reasoning options
@@ -277,3 +278,77 @@ export function buildProviderOptions(
277278
log.debug("buildProviderOptions: Unsupported provider", provider);
278279
return {};
279280
}
281+
282+
/**
283+
* Calculate the effective maxOutputTokens for a model based on its limits and thinking budget
284+
*
285+
* For Anthropic models with extended thinking, the AI SDK adds thinkingBudget to maxOutputTokens
286+
* internally. We need to ensure the sum doesn't exceed the model's max_output_tokens limit.
287+
*
288+
* For example, Claude Opus 4 has max_output_tokens=32000. If we use:
289+
* - thinkingBudget=20000 (high)
290+
* - maxOutputTokens=32000
291+
* Then total=52000 which exceeds 32000 → SDK shows warning and caps output
292+
*
293+
* Solution: Reduce maxOutputTokens so that maxOutputTokens + thinkingBudget <= model limit
294+
*
295+
* @param modelString - Full model string (e.g., "anthropic:claude-opus-4-1")
296+
* @param thinkingLevel - Current thinking level
297+
* @param requestedMaxOutputTokens - Optional user-requested maxOutputTokens
298+
* @returns Effective maxOutputTokens that respects model limits with thinking budget
299+
*/
300+
export function calculateEffectiveMaxOutputTokens(
301+
modelString: string,
302+
thinkingLevel: ThinkingLevel,
303+
requestedMaxOutputTokens?: number
304+
): number | undefined {
305+
const [provider] = modelString.split(":");
306+
307+
// Only apply this adjustment for Anthropic models
308+
if (provider !== "anthropic") {
309+
return requestedMaxOutputTokens;
310+
}
311+
312+
// Get the actual thinking level after policy enforcement
313+
const effectiveThinking = enforceThinkingPolicy(modelString, thinkingLevel);
314+
const thinkingBudget = ANTHROPIC_THINKING_BUDGETS[effectiveThinking];
315+
316+
// Get model's max output tokens from models.json
317+
const modelStats = getModelStats(modelString);
318+
const modelMaxOutput = modelStats?.max_output_tokens;
319+
320+
// If we don't know the model's max output, return requested value
321+
if (!modelMaxOutput) {
322+
log.debug("calculateEffectiveMaxOutputTokens: Unknown model max output, using requested", {
323+
modelString,
324+
requestedMaxOutputTokens,
325+
});
326+
return requestedMaxOutputTokens;
327+
}
328+
329+
// Calculate the maximum safe maxOutputTokens
330+
// The SDK will add thinkingBudget to maxOutputTokens, so we need room for both
331+
const maxSafeOutput = modelMaxOutput - thinkingBudget;
332+
333+
// If user didn't request specific tokens, use the max safe value
334+
const targetOutput = requestedMaxOutputTokens ?? modelMaxOutput;
335+
336+
// Cap at the safe maximum
337+
const effectiveOutput = Math.min(targetOutput, maxSafeOutput);
338+
339+
// Ensure we don't go below a reasonable minimum (1000 tokens)
340+
const finalOutput = Math.max(effectiveOutput, 1000);
341+
342+
log.debug("calculateEffectiveMaxOutputTokens", {
343+
modelString,
344+
thinkingLevel,
345+
effectiveThinking,
346+
thinkingBudget,
347+
modelMaxOutput,
348+
requestedMaxOutputTokens,
349+
maxSafeOutput,
350+
finalOutput,
351+
});
352+
353+
return finalOutput;
354+
}

src/common/utils/tokens/modelStats.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { modelsExtra } from "./models-extra";
33

44
export interface ModelStats {
55
max_input_tokens: number;
6+
max_output_tokens?: number;
67
input_cost_per_token: number;
78
output_cost_per_token: number;
89
cache_creation_input_token_cost?: number;
@@ -11,6 +12,7 @@ export interface ModelStats {
1112

1213
interface RawModelData {
1314
max_input_tokens?: number | string;
15+
max_output_tokens?: number | string;
1416
input_cost_per_token?: number;
1517
output_cost_per_token?: number;
1618
cache_creation_input_token_cost?: number;
@@ -37,6 +39,8 @@ function extractModelStats(data: RawModelData): ModelStats {
3739
/* eslint-disable @typescript-eslint/non-nullable-type-assertion-style */
3840
return {
3941
max_input_tokens: data.max_input_tokens as number,
42+
max_output_tokens:
43+
typeof data.max_output_tokens === "number" ? data.max_output_tokens : undefined,
4044
input_cost_per_token: data.input_cost_per_token as number,
4145
output_cost_per_token: data.output_cost_per_token as number,
4246
cache_creation_input_token_cost:

src/common/utils/tokens/models-extra.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,16 @@ interface ModelData {
2222
}
2323

2424
export const modelsExtra: Record<string, ModelData> = {
25-
// Claude Opus 4.5 - Released November 2025
26-
// $15/M input, $75/M output (same pricing as Opus 4.1)
25+
// Claude Opus 4.5 - Released November 24, 2025
26+
// $5/M input, $25/M output (price drop from Opus 4.1's $15/$75)
27+
// 64K max output tokens (matches Sonnet 4.5)
2728
"claude-opus-4-5": {
2829
max_input_tokens: 200000,
29-
max_output_tokens: 32000,
30-
input_cost_per_token: 0.000015, // $15 per million input tokens
31-
output_cost_per_token: 0.000075, // $75 per million output tokens
32-
cache_creation_input_token_cost: 0.00001875, // $18.75 per million tokens
33-
cache_read_input_token_cost: 0.0000015, // $1.50 per million tokens
30+
max_output_tokens: 64000,
31+
input_cost_per_token: 0.000005, // $5 per million input tokens
32+
output_cost_per_token: 0.000025, // $25 per million output tokens
33+
cache_creation_input_token_cost: 0.00000625, // $6.25 per million tokens (estimated)
34+
cache_read_input_token_cost: 0.0000005, // $0.50 per million tokens (estimated)
3435
litellm_provider: "anthropic",
3536
mode: "chat",
3637
supports_function_calling: true,

0 commit comments

Comments
 (0)