Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
21 changes: 21 additions & 0 deletions packages/types/src/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,27 @@ export type AnthropicModelId = keyof typeof anthropicModels
export const anthropicDefaultModelId: AnthropicModelId = "claude-sonnet-4-5"

export const anthropicModels = {
"claude-sonnet-4-6": {
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,
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-5": {
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 Down
27 changes: 27 additions & 0 deletions packages/types/src/providers/bedrock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ export const bedrockModels = {
maxCachePoints: 4,
cachableFields: ["system", "messages", "tools"],
},
"anthropic.claude-sonnet-4-6-20260114-v1:0": {
maxTokens: 8192,
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
supportsImages: true,
supportsPromptCache: true,
supportsReasoningBudget: 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
minTokensPerCachePoint: 1024,
maxCachePoints: 4,
cachableFields: ["system", "messages", "tools"],
// 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)
},
],
},
"amazon.nova-pro-v1:0": {
maxTokens: 5000,
contextWindow: 300_000,
Expand Down Expand Up @@ -499,19 +523,22 @@ export const BEDROCK_REGIONS = [
export const BEDROCK_1M_CONTEXT_MODEL_IDS = [
"anthropic.claude-sonnet-4-20250514-v1:0",
"anthropic.claude-sonnet-4-5-20250929-v1:0",
"anthropic.claude-sonnet-4-6-20260114-v1:0",
"anthropic.claude-opus-4-6-v1",
] as const

// Amazon Bedrock models that support Global Inference profiles
// As of Nov 2025, AWS supports Global Inference for:
// - Claude Sonnet 4
// - Claude Sonnet 4.5
// - Claude Sonnet 4.6
// - Claude Haiku 4.5
// - Claude Opus 4.5
// - Claude Opus 4.6
export const BEDROCK_GLOBAL_INFERENCE_MODEL_IDS = [
"anthropic.claude-sonnet-4-20250514-v1:0",
"anthropic.claude-sonnet-4-5-20250929-v1:0",
"anthropic.claude-sonnet-4-6-20260114-v1:0",
"anthropic.claude-haiku-4-5-20251001-v1:0",
"anthropic.claude-opus-4-5-20251101-v1:0",
"anthropic.claude-opus-4-6-v1",
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/providers/openrouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const OPEN_ROUTER_PROMPT_CACHING_MODELS = new Set([
"anthropic/claude-3.7-sonnet:thinking",
"anthropic/claude-sonnet-4",
"anthropic/claude-sonnet-4.5",
"anthropic/claude-sonnet-4.6",
"anthropic/claude-opus-4",
"anthropic/claude-opus-4.1",
"anthropic/claude-opus-4.5",
Expand Down Expand Up @@ -75,6 +76,7 @@ export const OPEN_ROUTER_REASONING_BUDGET_MODELS = new Set([
"anthropic/claude-opus-4.6",
"anthropic/claude-sonnet-4",
"anthropic/claude-sonnet-4.5",
"anthropic/claude-sonnet-4.6",
"anthropic/claude-haiku-4.5",
"google/gemini-2.5-pro-preview",
"google/gemini-2.5-pro",
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/providers/vercel-ai-gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const VERCEL_AI_GATEWAY_PROMPT_CACHING_MODELS = new Set([
"anthropic/claude-opus-4.5",
"anthropic/claude-opus-4.6",
"anthropic/claude-sonnet-4",
"anthropic/claude-sonnet-4.6",
"openai/gpt-4.1",
"openai/gpt-4.1-mini",
"openai/gpt-4.1-nano",
Expand Down Expand Up @@ -55,6 +56,7 @@ export const VERCEL_AI_GATEWAY_VISION_AND_TOOLS_MODELS = new Set([
"anthropic/claude-opus-4.5",
"anthropic/claude-opus-4.6",
"anthropic/claude-sonnet-4",
"anthropic/claude-sonnet-4.6",
"google/gemini-1.5-flash",
"google/gemini-1.5-pro",
"google/gemini-2.0-flash",
Expand Down
22 changes: 22 additions & 0 deletions packages/types/src/providers/vertex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,27 @@ export const vertexModels = {
},
],
},
"claude-sonnet-4-6@20260114": {
maxTokens: 8192,
contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07'
supportsImages: 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-haiku-4-5@20251001": {
maxTokens: 8192,
contextWindow: 200_000,
Expand Down Expand Up @@ -491,6 +512,7 @@ export const vertexModels = {
export const VERTEX_1M_CONTEXT_MODEL_IDS = [
"claude-sonnet-4@20250514",
"claude-sonnet-4-5@20250929",
"claude-sonnet-4-6@20260114",
"claude-opus-4-6",
] as const

Expand Down
15 changes: 15 additions & 0 deletions src/api/providers/__tests__/anthropic-vertex.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,21 @@ describe("VertexHandler", () => {
expect(model.betas).toContain("context-1m-2025-08-07")
})

it("should enable 1M context for Claude Sonnet 4.6 when beta flag is set", () => {
const handler = new AnthropicVertexHandler({
apiModelId: "claude-sonnet-4-6@20260114",
vertexProjectId: "test-project",
vertexRegion: "us-central1",
vertex1MContext: true,
})

const model = handler.getModel()
expect(model.info.contextWindow).toBe(1_000_000)
expect(model.info.inputPrice).toBe(6.0)
expect(model.info.outputPrice).toBe(22.5)
expect(model.betas).toContain("context-1m-2025-08-07")
})

it("should not enable 1M context when flag is disabled", () => {
const handler = new AnthropicVertexHandler({
apiModelId: VERTEX_1M_CONTEXT_MODEL_IDS[0],
Expand Down
46 changes: 46 additions & 0 deletions src/api/providers/__tests__/anthropic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,28 @@ describe("AnthropicHandler", () => {
// Verify API
expect(mockCreate).toHaveBeenCalled()
})

it("should include 1M context beta header for Claude Sonnet 4.6 when enabled", async () => {
const sonnet46Handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
anthropicBeta1MContext: true,
})

const stream = sonnet46Handler.createMessage(systemPrompt, [
{
role: "user",
content: [{ type: "text" as const, text: "Hello" }],
},
])

for await (const _chunk of stream) {
// Consume stream
}

const requestOptions = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[1]
expect(requestOptions?.headers?.["anthropic-beta"]).toContain("context-1m-2025-08-07")
})
})

describe("completePrompt", () => {
Expand Down Expand Up @@ -286,6 +308,18 @@ describe("AnthropicHandler", () => {
expect(model.info.supportsReasoningBudget).toBe(true)
})

it("should handle Claude 4.6 Sonnet model correctly", () => {
const handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
})
const model = handler.getModel()
expect(model.id).toBe("claude-sonnet-4-6")
expect(model.info.maxTokens).toBe(64000)
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",
Expand All @@ -297,6 +331,18 @@ describe("AnthropicHandler", () => {
expect(model.info.inputPrice).toBe(6.0)
expect(model.info.outputPrice).toBe(22.5)
})

it("should enable 1M context for Claude 4.6 Sonnet when beta flag is set", () => {
const handler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
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)
})
})

describe("reasoning block filtering", () => {
Expand Down
15 changes: 15 additions & 0 deletions src/api/providers/__tests__/bedrock.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,21 @@ describe("AwsBedrockHandler", () => {
expect(model.info.contextWindow).toBe(1_000_000)
})

it("should apply 1M tier pricing when awsBedrock1MContext is true for Claude Sonnet 4.6", () => {
const handler = new AwsBedrockHandler({
apiModelId: "anthropic.claude-sonnet-4-6-20260114-v1:0",
awsAccessKey: "test",
awsSecretKey: "test",
awsRegion: "us-east-1",
awsBedrock1MContext: true,
})

const model = handler.getModel()
expect(model.info.contextWindow).toBe(1_000_000)
expect(model.info.inputPrice).toBe(6.0)
expect(model.info.outputPrice).toBe(22.5)
})

it("should use default context window when awsBedrock1MContext is false for Claude Sonnet 4", () => {
const handler = new AwsBedrockHandler({
apiModelId: BEDROCK_1M_CONTEXT_MODEL_IDS[0],
Expand Down
10 changes: 8 additions & 2 deletions src/api/providers/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
// Filter out non-Anthropic blocks (reasoning, thoughtSignature, etc.) before sending to the API
const sanitizedMessages = filterNonAnthropicBlocks(messages)

// Add 1M context beta flag if enabled for supported models (Claude Sonnet 4/4.5, Opus 4.6)
// Add 1M context beta flag if enabled for supported models (Claude Sonnet 4/4.5/4.6, Opus 4.6)
if (
(modelId === "claude-sonnet-4-20250514" ||
modelId === "claude-sonnet-4-5" ||
modelId === "claude-sonnet-4-6" ||
modelId === "claude-opus-4-6") &&
this.options.anthropicBeta1MContext
) {
Expand All @@ -80,6 +81,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
}

switch (modelId) {
case "claude-sonnet-4-6":
case "claude-sonnet-4-5":
case "claude-sonnet-4-20250514":
case "claude-opus-4-6":
Expand Down Expand Up @@ -145,6 +147,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa

// Then check for models that support prompt caching
switch (modelId) {
case "claude-sonnet-4-6":
case "claude-sonnet-4-5":
case "claude-sonnet-4-20250514":
case "claude-opus-4-6":
Expand Down Expand Up @@ -336,7 +339,10 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa

// If 1M context beta is enabled for supported models, update the model info
if (
(id === "claude-sonnet-4-20250514" || id === "claude-sonnet-4-5" || id === "claude-opus-4-6") &&
(id === "claude-sonnet-4-20250514" ||
id === "claude-sonnet-4-5" ||
id === "claude-sonnet-4-6" ||
id === "claude-opus-4-6") &&
this.options.anthropicBeta1MContext
) {
// Use the tier pricing for 1M context
Expand Down
24 changes: 24 additions & 0 deletions src/api/providers/fetchers/__tests__/openrouter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,30 @@ describe("OpenRouter API", () => {
})

describe("parseOpenRouterModel", () => {
it("sets claude-sonnet-4.6 model to Anthropic max tokens", () => {
const mockModel = {
name: "Claude Sonnet 4.6",
description: "Test model",
context_length: 200000,
max_completion_tokens: 8192,
pricing: {
prompt: "0.000003",
completion: "0.000015",
},
}

const result = parseOpenRouterModel({
id: "anthropic/claude-sonnet-4.6",
model: mockModel,
inputModality: ["text"],
outputModality: ["text"],
maxTokens: 8192,
})

expect(result.maxTokens).toBe(64000)
expect(result.contextWindow).toBe(200000)
})

it("sets horizon-alpha model to 32k max tokens", () => {
const mockModel = {
name: "Horizon Alpha",
Expand Down
5 changes: 5 additions & 0 deletions src/api/providers/fetchers/openrouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ export const parseOpenRouterModel = ({
modelInfo.maxTokens = anthropicModels["claude-3-7-sonnet-20250219:thinking"].maxTokens
}

// Set claude-sonnet-4.6 model to use the correct configuration
if (id === "anthropic/claude-sonnet-4.6") {
modelInfo.maxTokens = anthropicModels["claude-sonnet-4-6"].maxTokens
}

// Set claude-opus-4.1 model to use the correct configuration
if (id === "anthropic/claude-opus-4.1") {
modelInfo.maxTokens = anthropicModels["claude-opus-4-1-20250805"].maxTokens
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
const supports1MContextBeta =
selectedModel?.id === "claude-sonnet-4-20250514" ||
selectedModel?.id === "claude-sonnet-4-5" ||
selectedModel?.id === "claude-sonnet-4-6" ||
selectedModel?.id === "claude-opus-4-6"

const handleInputChange = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,38 @@ describe("useSelectedModel", () => {
})
})

describe("anthropic provider with 1M context", () => {
beforeEach(() => {
mockUseRouterModels.mockReturnValue({
data: undefined,
isLoading: false,
isError: false,
} as any)

mockUseOpenRouterModelProviders.mockReturnValue({
data: undefined,
isLoading: false,
isError: false,
} as any)
})

it("should apply 1M pricing tier for Claude Sonnet 4.6 when enabled", () => {
const apiConfiguration: ProviderSettings = {
apiProvider: "anthropic",
apiModelId: "claude-sonnet-4-6",
anthropicBeta1MContext: true,
}

const wrapper = createWrapper()
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })

expect(result.current.id).toBe("claude-sonnet-4-6")
expect(result.current.info?.contextWindow).toBe(1_000_000)
expect(result.current.info?.inputPrice).toBe(6.0)
expect(result.current.info?.outputPrice).toBe(22.5)
})
})

describe("bedrock provider with 1M context", () => {
beforeEach(() => {
mockUseRouterModels.mockReturnValue({
Expand Down
Loading
Loading