@@ -60,6 +60,9 @@ import {
6060 bedrockModels ,
6161 SERVICE_TIER_KEY ,
6262 ApiProviderError ,
63+ BEDROCK_DEFAULT_CONTEXT ,
64+ BEDROCK_MAX_TOKENS ,
65+ type ProviderSettings ,
6366} from "@roo-code/types"
6467
6568import type { Anthropic } from "@anthropic-ai/sdk"
@@ -1948,6 +1951,139 @@ describe("AwsBedrockHandler", () => {
19481951 expect ( commandArg . inferenceConfig ?. temperature ) . toBeDefined ( )
19491952 } )
19501953
1954+ describe ( "custom ARN base model" , ( ) => {
1955+ const appProfileArn = "arn:aws:bedrock:us-west-2:123456789012:application-inference-profile/abcd1234efgh"
1956+ const createCustomArnHandler = ( options : Partial < ProviderSettings > = { } ) =>
1957+ new AwsBedrockHandler ( {
1958+ apiModelId : "custom-arn" ,
1959+ awsCustomArn : appProfileArn ,
1960+ awsAccessKey : "test-access-key" ,
1961+ awsSecretKey : "test-secret-key" ,
1962+ awsRegion : "us-west-2" ,
1963+ ...options ,
1964+ } )
1965+ const sendFirstRequest = async ( handler : AwsBedrockHandler ) => {
1966+ await handler . createMessage ( "System prompt" , messages ) . next ( )
1967+ return mockConverseStreamCommand . mock . calls [ 0 ] [ 0 ]
1968+ }
1969+
1970+ it ( "uses Opus 5.5 capabilities: native 1M context, adaptive thinking, no temperature" , async ( ) => {
1971+ const handler = createCustomArnHandler ( {
1972+ awsCustomArnBaseModelId : "anthropic.claude-opus-5-5" ,
1973+ enableReasoningEffort : true ,
1974+ } )
1975+
1976+ expect ( handler . getModel ( ) . info . contextWindow ) . toBe ( 1_000_000 )
1977+ expect ( handler . getModel ( ) . info . maxTokens ) . toBe ( bedrockModels [ "anthropic.claude-opus-5-5" ] . maxTokens )
1978+
1979+ const input = await sendFirstRequest ( handler )
1980+ expect ( input ) . toMatchObject ( {
1981+ modelId : appProfileArn ,
1982+ additionalModelRequestFields : {
1983+ thinking : { type : "adaptive" , display : "summarized" } ,
1984+ output_config : { effort : "xhigh" } ,
1985+ } ,
1986+ } )
1987+ expect ( input . additionalModelRequestFields ) . not . toMatchObject ( {
1988+ anthropic_beta : expect . arrayContaining ( [ "context-1m-2025-08-07" ] ) ,
1989+ } )
1990+ expect ( input . inferenceConfig ?. temperature ) . toBeUndefined ( )
1991+ } )
1992+
1993+ it ( "sends the 1M context beta for an Opus 4.8 base model when enabled" , async ( ) => {
1994+ const handler = createCustomArnHandler ( {
1995+ awsCustomArnBaseModelId : "anthropic.claude-opus-4-8" ,
1996+ awsBedrock1MContext : true ,
1997+ } )
1998+
1999+ expect ( handler . getModel ( ) . info . contextWindow ) . toBe ( 1_000_000 )
2000+ const input = await sendFirstRequest ( handler )
2001+ expect ( input ) . toMatchObject ( {
2002+ modelId : appProfileArn ,
2003+ additionalModelRequestFields : {
2004+ anthropic_beta : expect . arrayContaining ( [ "context-1m-2025-08-07" ] ) ,
2005+ } ,
2006+ } )
2007+ } )
2008+
2009+ it ( "keeps budget thinking and temperature for a Sonnet 4.5 base model" , async ( ) => {
2010+ const input = await sendFirstRequest (
2011+ createCustomArnHandler ( {
2012+ awsCustomArnBaseModelId : "anthropic.claude-sonnet-4-5-20250929-v1:0" ,
2013+ enableReasoningEffort : true ,
2014+ } ) ,
2015+ )
2016+
2017+ expect ( input ) . toMatchObject ( { additionalModelRequestFields : { thinking : { type : "enabled" } } } )
2018+ expect ( input . inferenceConfig ?. temperature ) . toBeDefined ( )
2019+ } )
2020+
2021+ it ( "sends no Anthropic fields for a non-Anthropic base model" , async ( ) => {
2022+ const handler = createCustomArnHandler ( { awsCustomArnBaseModelId : "meta.llama3-3-70b-instruct-v1:0" } )
2023+
2024+ const llama = bedrockModels [ "meta.llama3-3-70b-instruct-v1:0" ]
2025+ expect ( handler . getModel ( ) . info ) . toMatchObject ( {
2026+ contextWindow : llama . contextWindow ,
2027+ maxTokens : llama . maxTokens ,
2028+ inputPrice : llama . inputPrice ,
2029+ } )
2030+ const input = await sendFirstRequest ( handler )
2031+ expect ( input . modelId ) . toBe ( appProfileArn )
2032+ expect ( input . additionalModelRequestFields ) . toBeUndefined ( )
2033+ expect ( input . inferenceConfig ?. temperature ) . toBeDefined ( )
2034+ } )
2035+
2036+ it ( "uses the generic fallback with user limits when the model is not listed" , ( ) => {
2037+ expect ( createCustomArnHandler ( ) . getModel ( ) . info ) . toMatchObject ( {
2038+ contextWindow : BEDROCK_DEFAULT_CONTEXT ,
2039+ maxTokens : BEDROCK_MAX_TOKENS ,
2040+ } )
2041+ expect (
2042+ createCustomArnHandler ( { awsModelContextWindow : 32_000 , modelMaxTokens : 2048 } ) . getModel ( ) . info ,
2043+ ) . toMatchObject ( { contextWindow : 32_000 , maxTokens : 2048 } )
2044+ } )
2045+
2046+ it ( "applies capabilities of a model named in an inference profile ARN" , async ( ) => {
2047+ const input = await sendFirstRequest (
2048+ createCustomArnHandler ( {
2049+ awsCustomArn :
2050+ "arn:aws:bedrock:us-east-1:123456789012:inference-profile/us.anthropic.claude-opus-4-8" ,
2051+ awsRegion : "us-east-1" ,
2052+ awsBedrock1MContext : true ,
2053+ } ) ,
2054+ )
2055+
2056+ expect ( input ) . toMatchObject ( {
2057+ additionalModelRequestFields : { anthropic_beta : expect . arrayContaining ( [ "context-1m-2025-08-07" ] ) } ,
2058+ } )
2059+ expect ( input . inferenceConfig ?. temperature ) . toBeUndefined ( )
2060+ } )
2061+
2062+ it ( "completePrompt omits temperature for an adaptive base model (non-stream path)" , async ( ) => {
2063+ const mockConverseCommand = vi . mocked ( ConverseCommand )
2064+ mockConverseCommand . mockClear ( )
2065+
2066+ await createCustomArnHandler ( { awsCustomArnBaseModelId : "anthropic.claude-opus-5-5" } ) . completePrompt (
2067+ "Test prompt" ,
2068+ )
2069+
2070+ const input = mockConverseCommand . mock . calls [ 0 ] [ 0 ]
2071+ expect ( input . modelId ) . toBe ( appProfileArn )
2072+ expect ( input . inferenceConfig ?. temperature ) . toBeUndefined ( )
2073+ } )
2074+
2075+ it ( "ignores the base model for foundation-model ARNs, which already name the invoked model" , ( ) => {
2076+ const handler = createCustomArnHandler ( {
2077+ awsCustomArn :
2078+ "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0" ,
2079+ awsRegion : "us-east-1" ,
2080+ awsCustomArnBaseModelId : "anthropic.claude-opus-5-5" ,
2081+ } )
2082+
2083+ expect ( handler . getModel ( ) . id ) . toBe ( "anthropic.claude-3-5-sonnet-20241022-v2:0" )
2084+ } )
2085+ } )
2086+
19512087 describe ( "isAdaptiveThinkingModel detection" , ( ) => {
19522088 // Unit-cover the private guard directly (same pattern the suite uses for
19532089 // parseBaseModelId / getPrefixForRegion). This exercises all four model
0 commit comments