From 176c0eecc20f3e133631eeb34f2da520a48d6825 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Fri, 5 Dec 2025 21:45:08 +0000 Subject: [PATCH] feat: add AWS Bedrock service tier support - Add awsBedrockServiceTier field to provider settings schema - Add tier-specific pricing for Claude models (FLEX ~20% discount, PRIORITY ~20% premium) - Update Bedrock provider to include service_tier in API requests - Add service tier selector UI in Bedrock settings - Apply tier-specific pricing in cost calculations Implements #9874 --- packages/types/src/provider-settings.ts | 2 + packages/types/src/providers/bedrock.ts | 53 +++++++++++++++ src/api/providers/bedrock.ts | 66 +++++++++++++++++-- .../components/settings/providers/Bedrock.tsx | 23 +++++++ 4 files changed, 139 insertions(+), 5 deletions(-) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index d713a47d6b..14fee13b92 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -236,6 +236,8 @@ const bedrockSchema = apiModelIdProviderModelSchema.extend({ awsBedrockEndpointEnabled: z.boolean().optional(), awsBedrockEndpoint: z.string().optional(), awsBedrock1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window. + // AWS Bedrock service tier for models that support it (STANDARD, FLEX, PRIORITY) + awsBedrockServiceTier: serviceTierSchema.optional(), }) const vertexSchema = apiModelIdProviderModelSchema.extend({ diff --git a/packages/types/src/providers/bedrock.ts b/packages/types/src/providers/bedrock.ts index de44e71560..b69eac489a 100644 --- a/packages/types/src/providers/bedrock.ts +++ b/packages/types/src/providers/bedrock.ts @@ -27,6 +27,25 @@ export const bedrockModels = { minTokensPerCachePoint: 1024, maxCachePoints: 4, cachableFields: ["system", "messages", "tools"], + // Service tier pricing (FLEX ~20% discount, PRIORITY ~20% premium) + tiers: [ + { + name: "flex" as const, + contextWindow: 200_000, + inputPrice: 2.4, // 20% discount + outputPrice: 12.0, // 20% discount + cacheWritesPrice: 3.0, // 20% discount + cacheReadsPrice: 0.24, // 20% discount + }, + { + name: "priority" as const, + contextWindow: 200_000, + inputPrice: 3.6, // 20% premium + outputPrice: 18.0, // 20% premium + cacheWritesPrice: 4.5, // 20% premium + cacheReadsPrice: 0.36, // 20% premium + }, + ], }, "amazon.nova-pro-v1:0": { maxTokens: 5000, @@ -184,6 +203,25 @@ export const bedrockModels = { minTokensPerCachePoint: 2048, maxCachePoints: 4, cachableFields: ["system", "messages", "tools"], + // Service tier pricing (FLEX ~20% discount, PRIORITY ~20% premium) + tiers: [ + { + name: "flex" as const, + contextWindow: 200_000, + inputPrice: 0.64, // 20% discount + outputPrice: 3.2, // 20% discount + cacheWritesPrice: 0.8, // 20% discount + cacheReadsPrice: 0.064, // 20% discount + }, + { + name: "priority" as const, + contextWindow: 200_000, + inputPrice: 0.96, // 20% premium + outputPrice: 4.8, // 20% premium + cacheWritesPrice: 1.2, // 20% premium + cacheReadsPrice: 0.096, // 20% premium + }, + ], }, "anthropic.claude-haiku-4-5-20251001-v1:0": { maxTokens: 8192, @@ -235,6 +273,21 @@ export const bedrockModels = { supportsNativeTools: true, inputPrice: 0.25, outputPrice: 1.25, + // Service tier pricing (FLEX ~20% discount, PRIORITY ~20% premium) + tiers: [ + { + name: "flex" as const, + contextWindow: 200_000, + inputPrice: 0.2, // 20% discount + outputPrice: 1.0, // 20% discount + }, + { + name: "priority" as const, + contextWindow: 200_000, + inputPrice: 0.3, // 20% premium + outputPrice: 1.5, // 20% premium + }, + ], }, "anthropic.claude-2-1-v1:0": { maxTokens: 4096, diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index 4a4adfc0f4..431045138c 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -53,13 +53,14 @@ interface BedrockInferenceConfig { } // Define interface for Bedrock additional model request fields -// This includes thinking configuration, 1M context beta, and other model-specific parameters +// This includes thinking configuration, 1M context beta, service tier, and other model-specific parameters interface BedrockAdditionalModelFields { thinking?: { type: "enabled" budget_tokens: number } anthropic_beta?: string[] + service_tier?: "STANDARD" | "FLEX" | "PRIORITY" [key: string]: any // Add index signature to be compatible with DocumentType } @@ -433,6 +434,28 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH additionalModelRequestFields.anthropic_beta = anthropicBetas } + // Add service tier if specified and model supports it + if (this.options.awsBedrockServiceTier && modelConfig.info.tiers && modelConfig.info.tiers.length > 0) { + if (!additionalModelRequestFields) { + additionalModelRequestFields = {} as BedrockAdditionalModelFields + } + // Convert from lowercase to uppercase for API + const tierMap: Record = { + default: "STANDARD", + flex: "FLEX", + priority: "PRIORITY", + } + const mappedTier = tierMap[this.options.awsBedrockServiceTier as string] + if (mappedTier) { + additionalModelRequestFields.service_tier = mappedTier + logger.info("Service tier specified for Bedrock request", { + ctx: "bedrock", + modelId: modelConfig.id, + serviceTier: mappedTier, + }) + } + } + // Build tool configuration if native tools are enabled let toolConfig: ToolConfiguration | undefined if (useNativeTools && metadata?.tools) { @@ -1027,15 +1050,18 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH reasoningBudget?: number } { if (this.costModelConfig?.id?.trim().length > 0) { + // Apply service tier pricing if specified + const tierAdjustedInfo = this.applyServiceTierPricing(this.costModelConfig.info) + // Get model params for cost model config const params = getModelParams({ format: "anthropic", modelId: this.costModelConfig.id, - model: this.costModelConfig.info, + model: tierAdjustedInfo, settings: this.options, defaultTemperature: BEDROCK_DEFAULT_TEMPERATURE, }) - return { ...this.costModelConfig, ...params } + return { ...this.costModelConfig, info: tierAdjustedInfo, ...params } } let modelConfig = undefined @@ -1080,17 +1106,20 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH } } + // Apply service tier pricing if specified + const tierAdjustedInfo = this.applyServiceTierPricing(modelConfig.info) + // Get model params including reasoning configuration const params = getModelParams({ format: "anthropic", modelId: modelConfig.id, - model: modelConfig.info, + model: tierAdjustedInfo, settings: this.options, defaultTemperature: BEDROCK_DEFAULT_TEMPERATURE, }) // Don't override maxTokens/contextWindow here; handled in getModelById (and includes user overrides) - return { ...modelConfig, ...params } as { + return { ...modelConfig, info: tierAdjustedInfo, ...params } as { id: BedrockModelId | string info: ModelInfo maxTokens?: number @@ -1230,6 +1259,33 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH return false } + /************************************************************************************ + * + * SERVICE TIER PRICING + * + *************************************************************************************/ + + /** + * Returns a shallow-cloned ModelInfo with pricing overridden for the given tier, if available. + * If no tier or no overrides exist, the original ModelInfo is returned. + */ + private applyServiceTierPricing(info: ModelInfo): ModelInfo { + const tier = this.options.awsBedrockServiceTier + if (!tier || tier === "default") return info + + // Find the tier with matching name in the tiers array + const tierInfo = info.tiers?.find((t) => t.name === tier) + if (!tierInfo) return info + + return { + ...info, + inputPrice: tierInfo.inputPrice ?? info.inputPrice, + outputPrice: tierInfo.outputPrice ?? info.outputPrice, + cacheReadsPrice: tierInfo.cacheReadsPrice ?? info.cacheReadsPrice, + cacheWritesPrice: tierInfo.cacheWritesPrice ?? info.cacheWritesPrice, + } + } + /************************************************************************************ * * ERROR HANDLING diff --git a/webview-ui/src/components/settings/providers/Bedrock.tsx b/webview-ui/src/components/settings/providers/Bedrock.tsx index fac75170e9..095a6df54f 100644 --- a/webview-ui/src/components/settings/providers/Bedrock.tsx +++ b/webview-ui/src/components/settings/providers/Bedrock.tsx @@ -35,6 +35,9 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo !!apiConfiguration?.apiModelId && BEDROCK_GLOBAL_INFERENCE_MODEL_IDS.includes(apiConfiguration.apiModelId as any) + // Check if the selected model supports service tiers + const supportsServiceTiers = !!(selectedModelInfo?.tiers && selectedModelInfo.tiers.length > 0) + // Update the endpoint enabled state when the configuration changes useEffect(() => { setAwsEndpointSelected(!!apiConfiguration?.awsBedrockEndpointEnabled) @@ -150,6 +153,26 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo + {supportsServiceTiers && ( +
+ + +
+ Choose the service tier based on your cost and latency requirements +
+
+ )} {supportsGlobalInference && (