Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,9 +308,13 @@ const sambaNovaSchema = apiModelIdProviderModelSchema.extend({
sambaNovaApiKey: z.string().optional(),
})

export const zaiApiLineSchema = z.enum(["international_coding", "international", "china_coding", "china"])

export type ZaiApiLine = z.infer<typeof zaiApiLineSchema>

const zaiSchema = apiModelIdProviderModelSchema.extend({
zaiApiKey: z.string().optional(),
zaiApiLine: z.union([z.literal("china"), z.literal("international")]).optional(),
zaiApiLine: zaiApiLineSchema.optional(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains unrelated changes to the ZAi provider that should be in a separate PR. The ZaiApiLine enum and related changes are not related to adding Claude 4.5 models. Could we move these changes to a separate PR to keep this one focused?

})

const fireworksSchema = apiModelIdProviderModelSchema.extend({
Expand Down
34 changes: 34 additions & 0 deletions packages/types/src/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ export type AnthropicModelId = keyof typeof anthropicModels
export const anthropicDefaultModelId: AnthropicModelId = "claude-sonnet-4-20250514"

export const anthropicModels = {
"claude-sonnet-4-5-20250514": {
maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false.
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
supportsImages: true,
supportsComputerUse: true,
supportsPromptCache: true,
inputPrice: 3.0, // $3 per million input tokens (≤200K context)
outputPrice: 15.0, // $15 per million output tokens (≤200K context)
cacheWritesPrice: 3.75, // $3.75 per million tokens
cacheReadsPrice: 0.3, // $0.30 per million tokens
supportsReasoningBudget: true,
// Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07')
tiers: [
{
contextWindow: 1_000_000, // 1M tokens with beta flag
inputPrice: 6.0, // $6 per million input tokens (>200K context)
outputPrice: 22.5, // $22.50 per million output tokens (>200K context)
cacheWritesPrice: 7.5, // $7.50 per million tokens (>200K context)
cacheReadsPrice: 0.6, // $0.60 per million tokens (>200K context)
},
],
},
"claude-sonnet-4-20250514": {
maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false.
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
Expand All @@ -28,6 +50,18 @@ export const anthropicModels = {
},
],
},
"claude-opus-4-5-20250514": {
maxTokens: 32_000, // Overridden to 8k if `enableReasoningEffort` is false.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude 4.5 Opus has maxTokens of 32,000 here, but in bedrock.ts and vertex.ts it's only 8,192. Could we document why these values differ across providers, or is this a configuration error that needs to be fixed?

contextWindow: 200_000,
supportsImages: true,
supportsComputerUse: true,
supportsPromptCache: true,
inputPrice: 15.0, // $15 per million input tokens
outputPrice: 75.0, // $75 per million output tokens
cacheWritesPrice: 18.75, // $18.75 per million tokens
cacheReadsPrice: 1.5, // $1.50 per million tokens
supportsReasoningBudget: true,
},
"claude-opus-4-1-20250805": {
maxTokens: 8192,
contextWindow: 200_000,
Expand Down
30 changes: 30 additions & 0 deletions packages/types/src/providers/bedrock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ export const bedrockModels = {
maxCachePoints: 1,
cachableFields: ["system"],
},
"anthropic.claude-sonnet-4-5-20250514-v1:0": {
maxTokens: 8192,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The maxTokens value for Claude 4.5 models is 8,192 here but 64,000/32,000 in the anthropic.ts file. Is this intentional due to Bedrock limitations, or should these values be aligned?

contextWindow: 200_000,
supportsImages: true,
supportsComputerUse: true,
supportsPromptCache: true,
supportsReasoningBudget: true,
inputPrice: 3.0,
outputPrice: 15.0,
cacheWritesPrice: 3.75,
cacheReadsPrice: 0.3,
minTokensPerCachePoint: 1024,
maxCachePoints: 4,
cachableFields: ["system", "messages", "tools"],
},
"anthropic.claude-sonnet-4-20250514-v1:0": {
maxTokens: 8192,
contextWindow: 200_000,
Expand All @@ -82,6 +97,21 @@ export const bedrockModels = {
maxCachePoints: 4,
cachableFields: ["system", "messages", "tools"],
},
"anthropic.claude-opus-4-5-20250514-v1:0": {
maxTokens: 8192,
contextWindow: 200_000,
supportsImages: true,
supportsComputerUse: true,
supportsPromptCache: true,
supportsReasoningBudget: true,
inputPrice: 15.0,
outputPrice: 75.0,
cacheWritesPrice: 18.75,
cacheReadsPrice: 1.5,
minTokensPerCachePoint: 1024,
maxCachePoints: 4,
cachableFields: ["system", "messages", "tools"],
},
"anthropic.claude-opus-4-1-20250805-v1:0": {
maxTokens: 8192,
contextWindow: 200_000,
Expand Down
16 changes: 16 additions & 0 deletions packages/types/src/providers/claude-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ export function getClaudeCodeModelId(baseModelId: ClaudeCodeModelId, useVertex =
}

export const claudeCodeModels = {
"claude-sonnet-4-5-20250514": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the Claude 4.5 model configurations are duplicated across multiple providers with mostly identical settings, have you considered creating a shared configuration object? This could reduce duplication and make future updates easier to maintain.

...anthropicModels["claude-sonnet-4-5-20250514"],
supportsImages: false,
supportsPromptCache: true, // Claude Code does report cache tokens
supportsReasoningEffort: false,
supportsReasoningBudget: false,
requiredReasoningBudget: false,
},
"claude-sonnet-4-20250514": {
...anthropicModels["claude-sonnet-4-20250514"],
supportsImages: false,
Expand All @@ -48,6 +56,14 @@ export const claudeCodeModels = {
supportsReasoningBudget: false,
requiredReasoningBudget: false,
},
"claude-opus-4-5-20250514": {
...anthropicModels["claude-opus-4-5-20250514"],
supportsImages: false,
supportsPromptCache: true, // Claude Code does report cache tokens
supportsReasoningEffort: false,
supportsReasoningBudget: false,
requiredReasoningBudget: false,
},
"claude-opus-4-1-20250805": {
...anthropicModels["claude-opus-4-1-20250805"],
supportsImages: false,
Expand Down
24 changes: 24 additions & 0 deletions packages/types/src/providers/vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,18 @@ export const vertexModels = {
inputPrice: 1.25,
outputPrice: 5,
},
"claude-sonnet-4-5@20250514": {
maxTokens: 8192,
contextWindow: 200_000,
supportsImages: true,
supportsComputerUse: true,
supportsPromptCache: true,
inputPrice: 3.0,
outputPrice: 15.0,
cacheWritesPrice: 3.75,
cacheReadsPrice: 0.3,
supportsReasoningBudget: true,
},
"claude-sonnet-4@20250514": {
maxTokens: 8192,
contextWindow: 200_000,
Expand All @@ -175,6 +187,18 @@ export const vertexModels = {
cacheReadsPrice: 0.3,
supportsReasoningBudget: true,
},
"claude-opus-4-5@20250514": {
maxTokens: 8192,
contextWindow: 200_000,
supportsImages: true,
supportsComputerUse: true,
supportsPromptCache: true,
inputPrice: 15.0,
outputPrice: 75.0,
cacheWritesPrice: 18.75,
cacheReadsPrice: 1.5,
supportsReasoningBudget: true,
},
"claude-opus-4-1@20250805": {
maxTokens: 8192,
contextWindow: 200_000,
Expand Down
12 changes: 12 additions & 0 deletions packages/types/src/providers/zai.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ModelInfo } from "../model.js"
import { ZaiApiLine } from "../provider-settings.js"

// Z AI
// https://docs.z.ai/guides/llm/glm-4.5
Expand Down Expand Up @@ -103,3 +104,14 @@ export const mainlandZAiModels = {
} as const satisfies Record<string, ModelInfo>

export const ZAI_DEFAULT_TEMPERATURE = 0

export const zaiApiLineConfigs = {
international_coding: {
name: "International Coding Plan",
baseUrl: "https://api.z.ai/api/coding/paas/v4",
isChina: false,
},
international: { name: "International Standard", baseUrl: "https://api.z.ai/api/paas/v4", isChina: false },
china_coding: { name: "China Coding Plan", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4", isChina: true },
china: { name: "China Standard", baseUrl: "https://open.bigmodel.cn/api/paas/v4", isChina: true },
} satisfies Record<ZaiApiLine, { name: string; baseUrl: string; isChina: boolean }>
36 changes: 36 additions & 0 deletions src/api/providers/__tests__/anthropic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,5 +264,41 @@ describe("AnthropicHandler", () => {
expect(result.reasoningBudget).toBeUndefined()
expect(result.temperature).toBe(0)
})

it("should handle Claude 4.5 Sonnet model correctly", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good test coverage for the Anthropic provider! However, could we also add test coverage for the other providers (Bedrock, Claude Code, Vertex) that received the new Claude 4.5 models? This would ensure all integrations work correctly.

const handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-5-20250514",
})
const model = handler.getModel()
expect(model.id).toBe("claude-sonnet-4-5-20250514")
expect(model.info.maxTokens).toBe(64000)
expect(model.info.contextWindow).toBe(200000)
expect(model.info.supportsReasoningBudget).toBe(true)
})

it("should handle Claude 4.5 Opus model correctly", () => {
const handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-opus-4-5-20250514",
})
const model = handler.getModel()
expect(model.id).toBe("claude-opus-4-5-20250514")
expect(model.info.maxTokens).toBe(32000)
expect(model.info.contextWindow).toBe(200000)
expect(model.info.supportsReasoningBudget).toBe(true)
})

it("should enable 1M context for Claude 4.5 Sonnet when beta flag is set", () => {
const handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-5-20250514",
anthropicBeta1MContext: true,
})
const model = handler.getModel()
expect(model.info.contextWindow).toBe(1000000)
expect(model.info.inputPrice).toBe(6.0)
expect(model.info.outputPrice).toBe(22.5)
})
})
})
12 changes: 10 additions & 2 deletions src/api/providers/__tests__/zai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ describe("ZAiHandler", () => {

it("should use the correct international Z AI base URL", () => {
new ZAiHandler({ zaiApiKey: "test-zai-api-key", zaiApiLine: "international" })
expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ baseURL: "https://api.z.ai/api/paas/v4" }))
expect(OpenAI).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: "https://api.z.ai/api/paas/v4",
}),
)
})

it("should use the provided API key for international", () => {
Expand Down Expand Up @@ -109,7 +113,11 @@ describe("ZAiHandler", () => {
describe("Default behavior", () => {
it("should default to international when no zaiApiLine is specified", () => {
const handlerDefault = new ZAiHandler({ zaiApiKey: "test-zai-api-key" })
expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ baseURL: "https://api.z.ai/api/paas/v4" }))
expect(OpenAI).toHaveBeenCalledWith(
expect.objectContaining({
baseURL: "https://api.z.ai/api/coding/paas/v4",
}),
)

const model = handlerDefault.getModel()
expect(model.id).toBe(internationalZAiDefaultModelId)
Expand Down
18 changes: 14 additions & 4 deletions src/api/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,18 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
const cacheControl: CacheControlEphemeral = { type: "ephemeral" }
let { id: modelId, betas = [], maxTokens, temperature, reasoning: thinking } = this.getModel()

// Add 1M context beta flag if enabled for Claude Sonnet 4
if (modelId === "claude-sonnet-4-20250514" && this.options.anthropicBeta1MContext) {
// Add 1M context beta flag if enabled for Claude Sonnet 4 and 4.5
if (
(modelId === "claude-sonnet-4-20250514" || modelId === "claude-sonnet-4-5-20250514") &&
this.options.anthropicBeta1MContext
) {
betas.push("context-1m-2025-08-07")
}

switch (modelId) {
case "claude-sonnet-4-5-20250514":
case "claude-sonnet-4-20250514":
case "claude-opus-4-5-20250514":
case "claude-opus-4-1-20250805":
case "claude-opus-4-20250514":
case "claude-3-7-sonnet-20250219":
Expand Down Expand Up @@ -110,7 +115,9 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa

// Then check for models that support prompt caching
switch (modelId) {
case "claude-sonnet-4-5-20250514":
case "claude-sonnet-4-20250514":
case "claude-opus-4-5-20250514":
case "claude-opus-4-1-20250805":
case "claude-opus-4-20250514":
case "claude-3-7-sonnet-20250219":
Expand Down Expand Up @@ -243,8 +250,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
let id = modelId && modelId in anthropicModels ? (modelId as AnthropicModelId) : anthropicDefaultModelId
let info: ModelInfo = anthropicModels[id]

// If 1M context beta is enabled for Claude Sonnet 4, update the model info
if (id === "claude-sonnet-4-20250514" && this.options.anthropicBeta1MContext) {
// If 1M context beta is enabled for Claude Sonnet 4 or 4.5, update the model info
if (
(id === "claude-sonnet-4-20250514" || id === "claude-sonnet-4-5-20250514") &&
this.options.anthropicBeta1MContext
) {
// Use the tier pricing for 1M context
const tier = info.tiers?.[0]
if (tier) {
Expand Down
5 changes: 3 additions & 2 deletions src/api/providers/zai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
type InternationalZAiModelId,
type MainlandZAiModelId,
ZAI_DEFAULT_TEMPERATURE,
zaiApiLineConfigs,
} from "@roo-code/types"

import type { ApiHandlerOptions } from "../../shared/api"
Expand All @@ -14,14 +15,14 @@ import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"

export class ZAiHandler extends BaseOpenAiCompatibleProvider<InternationalZAiModelId | MainlandZAiModelId> {
constructor(options: ApiHandlerOptions) {
const isChina = options.zaiApiLine === "china"
const isChina = zaiApiLineConfigs[options.zaiApiLine ?? "international_coding"].isChina
const models = isChina ? mainlandZAiModels : internationalZAiModels
const defaultModelId = isChina ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId

super({
...options,
providerName: "Z AI",
baseURL: isChina ? "https://open.bigmodel.cn/api/paas/v4" : "https://api.z.ai/api/paas/v4",
baseURL: zaiApiLineConfigs[options.zaiApiLine ?? "international_coding"].baseUrl,
apiKey: options.zaiApiKey ?? "not-provided",
defaultProviderModelId: defaultModelId,
providerModels: models,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)

// Check if the current model supports 1M context beta
const supports1MContextBeta = selectedModel?.id === "claude-sonnet-4-20250514"
const supports1MContextBeta =
selectedModel?.id === "claude-sonnet-4-20250514" || selectedModel?.id === "claude-sonnet-4-5-20250514"

const handleInputChange = useCallback(
<K extends keyof ProviderSettings, E>(
Expand Down
20 changes: 11 additions & 9 deletions webview-ui/src/components/settings/providers/ZAi.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback } from "react"
import { VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"

import type { ProviderSettings } from "@roo-code/types"
import { zaiApiLineConfigs, zaiApiLineSchema, type ProviderSettings } from "@roo-code/types"

import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
Expand Down Expand Up @@ -33,15 +33,17 @@ export const ZAi = ({ apiConfiguration, setApiConfigurationField }: ZAiProps) =>
<div>
<label className="block font-medium mb-1">{t("settings:providers.zaiEntrypoint")}</label>
<VSCodeDropdown
value={apiConfiguration.zaiApiLine || "international"}
value={apiConfiguration.zaiApiLine || zaiApiLineSchema.enum.international_coding}
onChange={handleInputChange("zaiApiLine")}
className={cn("w-full")}>
<VSCodeOption value="international" className="p-2">
api.z.ai
</VSCodeOption>
<VSCodeOption value="china" className="p-2">
open.bigmodel.cn
</VSCodeOption>
{zaiApiLineSchema.options.map((zaiApiLine) => {
const config = zaiApiLineConfigs[zaiApiLine]
return (
<VSCodeOption key={zaiApiLine} value={zaiApiLine} className="p-2">
{config.name} ({config.baseUrl})
</VSCodeOption>
)
})}
</VSCodeDropdown>
<div className="text-xs text-vscode-descriptionForeground mt-1">
{t("settings:providers.zaiEntrypointDescription")}
Expand All @@ -62,7 +64,7 @@ export const ZAi = ({ apiConfiguration, setApiConfigurationField }: ZAiProps) =>
{!apiConfiguration?.zaiApiKey && (
<VSCodeButtonLink
href={
apiConfiguration.zaiApiLine === "china"
zaiApiLineConfigs[apiConfiguration.zaiApiLine ?? "international_coding"].isChina
? "https://open.bigmodel.cn/console/overview"
: "https://z.ai/manage-apikey/apikey-list"
}
Expand Down
Loading