mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
fix: improve Claude Sonnet 4 1M context window beta header handling
- Fixed beta header array initialization to prevent mutation of model betas - Added proper deduplication of beta headers to avoid duplicates - Ensured context-1m-2025-08-07 beta is properly combined with prompt caching beta - Added comprehensive tests for 1M context window feature Fixes #7229
This commit is contained in:
parent
b06005d321
commit
5006092f08
2 changed files with 147 additions and 4 deletions
|
|
@ -264,5 +264,138 @@ describe("AnthropicHandler", () => {
|
|||
expect(result.reasoningBudget).toBeUndefined()
|
||||
expect(result.temperature).toBe(0)
|
||||
})
|
||||
|
||||
it("should enable 1M context window when anthropicBeta1MContext is true for Claude Sonnet 4", () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-sonnet-4-20250514",
|
||||
anthropicBeta1MContext: true,
|
||||
})
|
||||
|
||||
const model = handler.getModel()
|
||||
|
||||
// Should have 1M context window when enabled
|
||||
expect(model.info.contextWindow).toBe(1_000_000)
|
||||
// Should use tier pricing for >200K context
|
||||
expect(model.info.inputPrice).toBe(6.0)
|
||||
expect(model.info.outputPrice).toBe(22.5)
|
||||
expect(model.info.cacheWritesPrice).toBe(7.5)
|
||||
expect(model.info.cacheReadsPrice).toBe(0.6)
|
||||
})
|
||||
|
||||
it("should use default context window when anthropicBeta1MContext is false for Claude Sonnet 4", () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-sonnet-4-20250514",
|
||||
anthropicBeta1MContext: false,
|
||||
})
|
||||
|
||||
const model = handler.getModel()
|
||||
|
||||
// Should use default context window (200k)
|
||||
expect(model.info.contextWindow).toBe(200_000)
|
||||
// Should use default pricing for ≤200K context
|
||||
expect(model.info.inputPrice).toBe(3.0)
|
||||
expect(model.info.outputPrice).toBe(15.0)
|
||||
expect(model.info.cacheWritesPrice).toBe(3.75)
|
||||
expect(model.info.cacheReadsPrice).toBe(0.3)
|
||||
})
|
||||
|
||||
it("should not affect context window for non-Claude Sonnet 4 models", () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-3-5-sonnet-20241022",
|
||||
anthropicBeta1MContext: true,
|
||||
})
|
||||
|
||||
const model = handler.getModel()
|
||||
|
||||
// Should use default context window for non-Sonnet 4 models
|
||||
expect(model.info.contextWindow).toBe(200_000)
|
||||
})
|
||||
})
|
||||
|
||||
describe("createMessage with 1M context beta", () => {
|
||||
it("should include context-1m-2025-08-07 beta header when enabled for Claude Sonnet 4", async () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-sonnet-4-20250514",
|
||||
anthropicBeta1MContext: true,
|
||||
})
|
||||
|
||||
const systemPrompt = "You are a helpful assistant."
|
||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
|
||||
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
// Verify that the create method was called with the correct beta headers
|
||||
expect(mockCreate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
model: "claude-sonnet-4-20250514",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
headers: expect.objectContaining({
|
||||
"anthropic-beta": expect.stringContaining("context-1m-2025-08-07"),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
|
||||
// Verify that both betas are included
|
||||
const callArgs = mockCreate.mock.calls[0]
|
||||
const headers = callArgs[1]?.headers
|
||||
expect(headers?.["anthropic-beta"]).toContain("prompt-caching-2024-07-31")
|
||||
expect(headers?.["anthropic-beta"]).toContain("context-1m-2025-08-07")
|
||||
})
|
||||
|
||||
it("should not include context-1m beta header when disabled for Claude Sonnet 4", async () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-sonnet-4-20250514",
|
||||
anthropicBeta1MContext: false,
|
||||
})
|
||||
|
||||
const systemPrompt = "You are a helpful assistant."
|
||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
|
||||
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
// Verify that the create method was called without the 1M context beta
|
||||
const callArgs = mockCreate.mock.calls[0]
|
||||
const headers = callArgs[1]?.headers
|
||||
expect(headers?.["anthropic-beta"]).toContain("prompt-caching-2024-07-31")
|
||||
expect(headers?.["anthropic-beta"]).not.toContain("context-1m-2025-08-07")
|
||||
})
|
||||
|
||||
it("should handle thinking models with 1M context beta correctly", async () => {
|
||||
const handler = new AnthropicHandler({
|
||||
apiKey: "test-api-key",
|
||||
apiModelId: "claude-3-7-sonnet-20250219:thinking",
|
||||
anthropicBeta1MContext: true, // This shouldn't affect non-Sonnet 4 models
|
||||
})
|
||||
|
||||
const systemPrompt = "You are a helpful assistant."
|
||||
const messages: Anthropic.Messages.MessageParam[] = [{ role: "user", content: "Hello" }]
|
||||
|
||||
const stream = handler.createMessage(systemPrompt, messages)
|
||||
const chunks: any[] = []
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
}
|
||||
|
||||
// Verify that the 1M context beta is NOT included for non-Sonnet 4 models
|
||||
const callArgs = mockCreate.mock.calls[0]
|
||||
const headers = callArgs[1]?.headers
|
||||
expect(headers?.["anthropic-beta"]).toContain("output-128k-2025-02-19")
|
||||
expect(headers?.["anthropic-beta"]).toContain("prompt-caching-2024-07-31")
|
||||
expect(headers?.["anthropic-beta"]).not.toContain("context-1m-2025-08-07")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -43,11 +43,17 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
|
|||
): ApiStream {
|
||||
let stream: AnthropicStream<Anthropic.Messages.RawMessageStreamEvent>
|
||||
const cacheControl: CacheControlEphemeral = { type: "ephemeral" }
|
||||
let { id: modelId, betas = [], maxTokens, temperature, reasoning: thinking } = this.getModel()
|
||||
let { id: modelId, betas: modelBetas, maxTokens, temperature, reasoning: thinking } = this.getModel()
|
||||
|
||||
// Initialize betas array properly
|
||||
const betas: string[] = modelBetas ? [...modelBetas] : []
|
||||
|
||||
// Add 1M context beta flag if enabled for Claude Sonnet 4
|
||||
if (modelId === "claude-sonnet-4-20250514" && this.options.anthropicBeta1MContext) {
|
||||
betas.push("context-1m-2025-08-07")
|
||||
// Only add if not already present
|
||||
if (!betas.includes("context-1m-2025-08-07")) {
|
||||
betas.push("context-1m-2025-08-07")
|
||||
}
|
||||
}
|
||||
|
||||
switch (modelId) {
|
||||
|
|
@ -118,8 +124,12 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
|
|||
case "claude-3-5-haiku-20241022":
|
||||
case "claude-3-opus-20240229":
|
||||
case "claude-3-haiku-20240307":
|
||||
betas.push("prompt-caching-2024-07-31")
|
||||
return { headers: { "anthropic-beta": betas.join(",") } }
|
||||
// Only add prompt caching beta if not already present
|
||||
if (!betas.includes("prompt-caching-2024-07-31")) {
|
||||
betas.push("prompt-caching-2024-07-31")
|
||||
}
|
||||
// Only set headers if we have betas to include
|
||||
return betas.length > 0 ? { headers: { "anthropic-beta": betas.join(",") } } : undefined
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue