Skip to content

Commit 13d1a5b

Browse files
authored
Add Sonnet 1M context checkbox (#7032)
1 parent 4c01866 commit 13d1a5b

File tree

24 files changed

+343
-4
lines changed

24 files changed

+343
-4
lines changed

packages/types/src/provider-settings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { z } from "zod"
33
import { reasoningEffortsSchema, verbosityLevelsSchema, modelInfoSchema } from "./model.js"
44
import { codebaseIndexProviderSchema } from "./codebase-index.js"
55

6+
// Bedrock Claude Sonnet 4 model ID that supports 1M context
7+
export const BEDROCK_CLAUDE_SONNET_4_MODEL_ID = "anthropic.claude-sonnet-4-20250514-v1:0"
8+
69
// Extended schema that includes "minimal" for GPT-5 models
710
export const extendedReasoningEffortsSchema = z.union([reasoningEffortsSchema, z.literal("minimal")])
811

@@ -135,6 +138,7 @@ const bedrockSchema = apiModelIdProviderModelSchema.extend({
135138
awsModelContextWindow: z.number().optional(),
136139
awsBedrockEndpointEnabled: z.boolean().optional(),
137140
awsBedrockEndpoint: z.string().optional(),
141+
awsBedrock1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window
138142
})
139143

140144
const vertexSchema = apiModelIdProviderModelSchema.extend({

src/api/providers/__tests__/bedrock.spec.ts

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ vi.mock("@aws-sdk/client-bedrock-runtime", () => {
2525

2626
import { AwsBedrockHandler } from "../bedrock"
2727
import { ConverseStreamCommand, BedrockRuntimeClient } from "@aws-sdk/client-bedrock-runtime"
28+
import { BEDROCK_CLAUDE_SONNET_4_MODEL_ID } from "@roo-code/types"
2829

2930
import type { Anthropic } from "@anthropic-ai/sdk"
3031

@@ -564,4 +565,184 @@ describe("AwsBedrockHandler", () => {
564565
expect(typeof model.info.supportsPromptCache).toBe("boolean")
565566
})
566567
})
568+
569+
describe("1M context beta feature", () => {
570+
it("should enable 1M context window when awsBedrock1MContext is true for Claude Sonnet 4", () => {
571+
const handler = new AwsBedrockHandler({
572+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
573+
awsAccessKey: "test",
574+
awsSecretKey: "test",
575+
awsRegion: "us-east-1",
576+
awsBedrock1MContext: true,
577+
})
578+
579+
const model = handler.getModel()
580+
581+
// Should have 1M context window when enabled
582+
expect(model.info.contextWindow).toBe(1_000_000)
583+
})
584+
585+
it("should use default context window when awsBedrock1MContext is false for Claude Sonnet 4", () => {
586+
const handler = new AwsBedrockHandler({
587+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
588+
awsAccessKey: "test",
589+
awsSecretKey: "test",
590+
awsRegion: "us-east-1",
591+
awsBedrock1MContext: false,
592+
})
593+
594+
const model = handler.getModel()
595+
596+
// Should use default context window (200k)
597+
expect(model.info.contextWindow).toBe(200_000)
598+
})
599+
600+
it("should not affect context window for non-Claude Sonnet 4 models", () => {
601+
const handler = new AwsBedrockHandler({
602+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
603+
awsAccessKey: "test",
604+
awsSecretKey: "test",
605+
awsRegion: "us-east-1",
606+
awsBedrock1MContext: true,
607+
})
608+
609+
const model = handler.getModel()
610+
611+
// Should use default context window for non-Sonnet 4 models
612+
expect(model.info.contextWindow).toBe(200_000)
613+
})
614+
615+
it("should include anthropic_beta parameter when 1M context is enabled", async () => {
616+
const handler = new AwsBedrockHandler({
617+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
618+
awsAccessKey: "test",
619+
awsSecretKey: "test",
620+
awsRegion: "us-east-1",
621+
awsBedrock1MContext: true,
622+
})
623+
624+
const messages: Anthropic.Messages.MessageParam[] = [
625+
{
626+
role: "user",
627+
content: "Test message",
628+
},
629+
]
630+
631+
const generator = handler.createMessage("", messages)
632+
await generator.next() // Start the generator
633+
634+
// Verify the command was created with the right payload
635+
expect(mockConverseStreamCommand).toHaveBeenCalled()
636+
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
637+
638+
// Should include anthropic_beta parameter but NOT anthropic_version (only for thinking)
639+
expect(commandArg.anthropic_beta).toEqual(["context-1m-2025-08-07"])
640+
expect(commandArg.anthropic_version).toBeUndefined()
641+
})
642+
643+
it("should not include anthropic_beta parameter when 1M context is disabled", async () => {
644+
const handler = new AwsBedrockHandler({
645+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
646+
awsAccessKey: "test",
647+
awsSecretKey: "test",
648+
awsRegion: "us-east-1",
649+
awsBedrock1MContext: false,
650+
})
651+
652+
const messages: Anthropic.Messages.MessageParam[] = [
653+
{
654+
role: "user",
655+
content: "Test message",
656+
},
657+
]
658+
659+
const generator = handler.createMessage("", messages)
660+
await generator.next() // Start the generator
661+
662+
// Verify the command was created with the right payload
663+
expect(mockConverseStreamCommand).toHaveBeenCalled()
664+
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
665+
666+
// Should not include anthropic_beta parameter
667+
expect(commandArg.anthropic_beta).toBeUndefined()
668+
})
669+
670+
it("should not include anthropic_beta parameter for non-Claude Sonnet 4 models", async () => {
671+
const handler = new AwsBedrockHandler({
672+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
673+
awsAccessKey: "test",
674+
awsSecretKey: "test",
675+
awsRegion: "us-east-1",
676+
awsBedrock1MContext: true,
677+
})
678+
679+
const messages: Anthropic.Messages.MessageParam[] = [
680+
{
681+
role: "user",
682+
content: "Test message",
683+
},
684+
]
685+
686+
const generator = handler.createMessage("", messages)
687+
await generator.next() // Start the generator
688+
689+
// Verify the command was created with the right payload
690+
expect(mockConverseStreamCommand).toHaveBeenCalled()
691+
const commandArg = mockConverseStreamCommand.mock.calls[0][0] as any
692+
693+
// Should not include anthropic_beta parameter for non-Sonnet 4 models
694+
expect(commandArg.anthropic_beta).toBeUndefined()
695+
})
696+
697+
it("should enable 1M context window with cross-region inference for Claude Sonnet 4", () => {
698+
const handler = new AwsBedrockHandler({
699+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
700+
awsAccessKey: "test",
701+
awsSecretKey: "test",
702+
awsRegion: "us-east-1",
703+
awsUseCrossRegionInference: true,
704+
awsBedrock1MContext: true,
705+
})
706+
707+
const model = handler.getModel()
708+
709+
// Should have 1M context window even with cross-region prefix
710+
expect(model.info.contextWindow).toBe(1_000_000)
711+
// Model ID should have cross-region prefix
712+
expect(model.id).toBe(`us.${BEDROCK_CLAUDE_SONNET_4_MODEL_ID}`)
713+
})
714+
715+
it("should include anthropic_beta parameter with cross-region inference for Claude Sonnet 4", async () => {
716+
const handler = new AwsBedrockHandler({
717+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
718+
awsAccessKey: "test",
719+
awsSecretKey: "test",
720+
awsRegion: "us-east-1",
721+
awsUseCrossRegionInference: true,
722+
awsBedrock1MContext: true,
723+
})
724+
725+
const messages: Anthropic.Messages.MessageParam[] = [
726+
{
727+
role: "user",
728+
content: "Test message",
729+
},
730+
]
731+
732+
const generator = handler.createMessage("", messages)
733+
await generator.next() // Start the generator
734+
735+
// Verify the command was created with the right payload
736+
expect(mockConverseStreamCommand).toHaveBeenCalled()
737+
const commandArg = mockConverseStreamCommand.mock.calls[
738+
mockConverseStreamCommand.mock.calls.length - 1
739+
][0] as any
740+
741+
// Should include anthropic_beta parameter but NOT anthropic_version (only for thinking)
742+
expect(commandArg.anthropic_beta).toEqual(["context-1m-2025-08-07"])
743+
expect(commandArg.anthropic_version).toBeUndefined()
744+
// Model ID should have cross-region prefix
745+
expect(commandArg.modelId).toBe(`us.${BEDROCK_CLAUDE_SONNET_4_MODEL_ID}`)
746+
})
747+
})
567748
})

src/api/providers/bedrock.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
BEDROCK_MAX_TOKENS,
2222
BEDROCK_DEFAULT_CONTEXT,
2323
AWS_INFERENCE_PROFILE_MAPPING,
24+
BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
2425
} from "@roo-code/types"
2526

2627
import { ApiStream } from "../transform/stream"
@@ -62,6 +63,7 @@ interface BedrockPayload {
6263
system?: SystemContentBlock[]
6364
inferenceConfig: BedrockInferenceConfig
6465
anthropic_version?: string
66+
anthropic_beta?: string[]
6567
additionalModelRequestFields?: BedrockThinkingConfig
6668
}
6769

@@ -375,6 +377,11 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
375377
inferenceConfig.topP = 0.1
376378
}
377379

380+
// Check if 1M context is enabled for Claude Sonnet 4
381+
// Use parseBaseModelId to handle cross-region inference prefixes
382+
const baseModelId = this.parseBaseModelId(modelConfig.id)
383+
const is1MContextEnabled = baseModelId === BEDROCK_CLAUDE_SONNET_4_MODEL_ID && this.options.awsBedrock1MContext
384+
378385
const payload: BedrockPayload = {
379386
modelId: modelConfig.id,
380387
messages: formatted.messages,
@@ -383,6 +390,8 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
383390
...(additionalModelRequestFields && { additionalModelRequestFields }),
384391
// Add anthropic_version when using thinking features
385392
...(thinkingEnabled && { anthropic_version: "bedrock-2023-05-31" }),
393+
// Add anthropic_beta when 1M context is enabled
394+
...(is1MContextEnabled && { anthropic_beta: ["context-1m-2025-08-07"] }),
386395
}
387396

388397
// Create AbortController with 10 minute timeout
@@ -960,6 +969,17 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
960969
}
961970
}
962971

972+
// Check if 1M context is enabled for Claude Sonnet 4
973+
// Use parseBaseModelId to handle cross-region inference prefixes
974+
const baseModelId = this.parseBaseModelId(modelConfig.id)
975+
if (baseModelId === BEDROCK_CLAUDE_SONNET_4_MODEL_ID && this.options.awsBedrock1MContext) {
976+
// Update context window to 1M tokens when 1M context beta is enabled
977+
modelConfig.info = {
978+
...modelConfig.info,
979+
contextWindow: 1_000_000,
980+
}
981+
}
982+
963983
// Get model params including reasoning configuration
964984
const params = getModelParams({
965985
format: "anthropic",

webview-ui/src/components/settings/providers/Bedrock.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ import { useCallback, useState, useEffect } from "react"
22
import { Checkbox } from "vscrui"
33
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
44

5-
import { type ProviderSettings, type ModelInfo, BEDROCK_REGIONS } from "@roo-code/types"
5+
import {
6+
type ProviderSettings,
7+
type ModelInfo,
8+
BEDROCK_REGIONS,
9+
BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
10+
} from "@roo-code/types"
611

712
import { useAppTranslation } from "@src/i18n/TranslationContext"
813
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StandardTooltip } from "@src/components/ui"
@@ -19,6 +24,9 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
1924
const { t } = useAppTranslation()
2025
const [awsEndpointSelected, setAwsEndpointSelected] = useState(!!apiConfiguration?.awsBedrockEndpointEnabled)
2126

27+
// Check if the selected model supports 1M context (Claude Sonnet 4)
28+
const supports1MContextBeta = apiConfiguration?.apiModelId === BEDROCK_CLAUDE_SONNET_4_MODEL_ID
29+
2230
// Update the endpoint enabled state when the configuration changes
2331
useEffect(() => {
2432
setAwsEndpointSelected(!!apiConfiguration?.awsBedrockEndpointEnabled)
@@ -159,6 +167,20 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
159167
</div>
160168
</>
161169
)}
170+
{supports1MContextBeta && (
171+
<div>
172+
<Checkbox
173+
checked={apiConfiguration?.awsBedrock1MContext ?? false}
174+
onChange={(checked: boolean) => {
175+
setApiConfigurationField("awsBedrock1MContext", checked)
176+
}}>
177+
{t("settings:providers.awsBedrock1MContextBetaLabel")}
178+
</Checkbox>
179+
<div className="text-sm text-vscode-descriptionForeground mt-1 ml-6">
180+
{t("settings:providers.awsBedrock1MContextBetaDescription")}
181+
</div>
182+
</div>
183+
)}
162184
<Checkbox
163185
checked={awsEndpointSelected}
164186
onChange={(isChecked) => {

webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
55
import { renderHook } from "@testing-library/react"
66
import type { Mock } from "vitest"
77

8-
import { ProviderSettings, ModelInfo } from "@roo-code/types"
8+
import { ProviderSettings, ModelInfo, BEDROCK_CLAUDE_SONNET_4_MODEL_ID } from "@roo-code/types"
99

1010
import { useSelectedModel } from "../useSelectedModel"
1111
import { useRouterModels } from "../useRouterModels"
@@ -448,4 +448,69 @@ describe("useSelectedModel", () => {
448448
expect(result.current.info?.supportsImages).toBe(false)
449449
})
450450
})
451+
452+
describe("bedrock provider with 1M context", () => {
453+
beforeEach(() => {
454+
mockUseRouterModels.mockReturnValue({
455+
data: {
456+
openrouter: {},
457+
requesty: {},
458+
glama: {},
459+
unbound: {},
460+
litellm: {},
461+
"io-intelligence": {},
462+
},
463+
isLoading: false,
464+
isError: false,
465+
} as any)
466+
467+
mockUseOpenRouterModelProviders.mockReturnValue({
468+
data: {},
469+
isLoading: false,
470+
isError: false,
471+
} as any)
472+
})
473+
474+
it("should enable 1M context window for Bedrock Claude Sonnet 4 when awsBedrock1MContext is true", () => {
475+
const apiConfiguration: ProviderSettings = {
476+
apiProvider: "bedrock",
477+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
478+
awsBedrock1MContext: true,
479+
}
480+
481+
const wrapper = createWrapper()
482+
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
483+
484+
expect(result.current.id).toBe(BEDROCK_CLAUDE_SONNET_4_MODEL_ID)
485+
expect(result.current.info?.contextWindow).toBe(1_000_000)
486+
})
487+
488+
it("should use default context window for Bedrock Claude Sonnet 4 when awsBedrock1MContext is false", () => {
489+
const apiConfiguration: ProviderSettings = {
490+
apiProvider: "bedrock",
491+
apiModelId: BEDROCK_CLAUDE_SONNET_4_MODEL_ID,
492+
awsBedrock1MContext: false,
493+
}
494+
495+
const wrapper = createWrapper()
496+
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
497+
498+
expect(result.current.id).toBe(BEDROCK_CLAUDE_SONNET_4_MODEL_ID)
499+
expect(result.current.info?.contextWindow).toBe(200_000)
500+
})
501+
502+
it("should not affect context window for non-Claude Sonnet 4 Bedrock models", () => {
503+
const apiConfiguration: ProviderSettings = {
504+
apiProvider: "bedrock",
505+
apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
506+
awsBedrock1MContext: true,
507+
}
508+
509+
const wrapper = createWrapper()
510+
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
511+
512+
expect(result.current.id).toBe("anthropic.claude-3-5-sonnet-20241022-v2:0")
513+
expect(result.current.info?.contextWindow).toBe(200_000)
514+
})
515+
})
451516
})

0 commit comments

Comments
 (0)