Skip to content

Commit 549ed0b

Browse files
committed
refactor(vertex): centralize Vertex pricing and 1M context in types helper and consume in webview
Implements feedback to move config logic into packages/types for reuse across providers.
1 parent 3329fca commit 549ed0b

File tree

2 files changed

+89
-79
lines changed

2 files changed

+89
-79
lines changed

packages/types/src/providers/vertex.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,85 @@ export const VERTEX_REGIONS = [
430430
{ value: "me-central1", label: "me-central1" },
431431
{ value: "africa-south1", label: "africa-south1" },
432432
]
433+
434+
// Regional pricing constants for Vertex Claude Sonnet 4.5
435+
export const VERTEX_SONNET_45_REGIONAL_PRICING_REGIONS = ["us-east5", "europe-west1", "asia-southeast1"] as const
436+
437+
export type VertexRegionalPricingRegion = (typeof VERTEX_SONNET_45_REGIONAL_PRICING_REGIONS)[number] | "global"
438+
439+
/**
440+
* getVertexAdjustedModelInfo
441+
*
442+
* Centralizes Vertex Claude Sonnet pricing and 1M context adjustments.
443+
* - Applies region-aware pricing for claude-sonnet-4-5@20250929
444+
* - Applies global pricing for claude-sonnet-4@20250514
445+
* - Enables 1M context window when either:
446+
* • Model id ends with "[1m]" OR
447+
* • largeInputTierEnabled is true
448+
*/
449+
export function getVertexAdjustedModelInfo(
450+
id: string,
451+
base: ModelInfo | undefined,
452+
opts?: { region?: string; largeInputTierEnabled?: boolean },
453+
): ModelInfo | undefined {
454+
if (!base) return undefined
455+
456+
const isSonnet45 = id.startsWith("claude-sonnet-4-5@20250929")
457+
const isSonnet4 = id.startsWith("claude-sonnet-4@20250514")
458+
459+
// If not a Sonnet 4/4.5 model, return base info as-is.
460+
if (!isSonnet45 && !isSonnet4) return base
461+
462+
const region = opts?.region ?? "global"
463+
const is1m = id.endsWith("[1m]") || opts?.largeInputTierEnabled === true
464+
465+
const regionalSet = new Set<string>(VERTEX_SONNET_45_REGIONAL_PRICING_REGIONS)
466+
const useRegionalPricing = regionalSet.has(region)
467+
468+
if (isSonnet45) {
469+
if (is1m) {
470+
// Over 200k (1M tier) with regional pricing
471+
return {
472+
...base,
473+
contextWindow: 1_000_000,
474+
inputPrice: useRegionalPricing ? 6.6 : 6.0,
475+
outputPrice: useRegionalPricing ? 24.75 : 22.5,
476+
cacheWritesPrice: useRegionalPricing ? 8.25 : 7.5,
477+
cacheReadsPrice: useRegionalPricing ? 0.66 : 0.6,
478+
}
479+
}
480+
481+
// Under 200k with regional pricing
482+
return {
483+
...base,
484+
contextWindow: 200_000,
485+
inputPrice: useRegionalPricing ? 3.3 : 3.0,
486+
outputPrice: useRegionalPricing ? 16.5 : 15.0,
487+
cacheWritesPrice: useRegionalPricing ? 4.13 : 3.75,
488+
cacheReadsPrice: useRegionalPricing ? 0.33 : 0.3,
489+
}
490+
}
491+
492+
// Sonnet 4 (global pricing only)
493+
if (is1m) {
494+
// Over 200k (1M tier)
495+
return {
496+
...base,
497+
contextWindow: 1_000_000,
498+
inputPrice: 6.0,
499+
outputPrice: 22.5,
500+
cacheWritesPrice: 7.5,
501+
cacheReadsPrice: 0.6,
502+
}
503+
}
504+
505+
// Under 200k
506+
return {
507+
...base,
508+
contextWindow: 200_000,
509+
inputPrice: 3.0,
510+
outputPrice: 15.0,
511+
cacheWritesPrice: 3.75,
512+
cacheReadsPrice: 0.3,
513+
}
514+
}

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

Lines changed: 7 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import {
5757
vercelAiGatewayDefaultModelId,
5858
BEDROCK_1M_CONTEXT_MODEL_IDS,
5959
deepInfraDefaultModelId,
60+
getVertexAdjustedModelInfo,
6061
} from "@roo-code/types"
6162

6263
import type { ModelRecord, RouterModels } from "@roo/api"
@@ -225,86 +226,13 @@ function getSelectedModel({
225226
return { id, info: undefined }
226227
}
227228

228-
// Region-aware, tiered pricing for Vertex Claude Sonnet models
229-
// Source: https://cloud.google.com/vertex-ai/generative-ai/pricing
230-
//
231-
// Rules implemented:
232-
// - Sonnet 4 pricing is the same globally in all regions
233-
// • Under 200k Input Tokens:
234-
// Input: $3, Output: $15, Cache Write: $3.75, Cache Hit: $0.30
235-
// • Over 200k Input Tokens ([1m] variants or largeInputTierEnabled):
236-
// Input: $6, Output: $22.50, Cache Write: $7.50, Cache Hit: $0.60
237-
//
238-
// - Sonnet 4.5 has different pricing per region and per input context size
239-
// • Global region (all regions except us-east5, europe-west1, asia-southeast1)
240-
// - Under 200k Input Tokens:
241-
// Input: $3.00, Output: $15.00, Cache Write: $3.75, Cache Hit: $0.30
242-
// - Over 200k Input Tokens ([1m] variants or largeInputTierEnabled):
243-
// Input: $6.00, Output: $22.50, Cache Write: $7.50, Cache Hit: $0.60
244-
// • Regional (us-east5, europe-west1, asia-southeast1)
245-
// - Under 200k Input Tokens:
246-
// Input: $3.30, Output: $16.50, Cache Write: $4.13, Cache Hit: $0.33
247-
// - Over 200k Input Tokens ([1m] variants or largeInputTierEnabled):
248-
// Input: $6.60, Output: $24.75, Cache Write: $8.25, Cache Hit: $0.66
249-
//
250-
// We derive "over 200k" from Roo's explicit [1m] model variants, the selected Vertex region,
251-
// or the generic largeInputTierEnabled setting.
252-
const region = apiConfiguration.vertexRegion ?? "global"
253-
const is1m = id.endsWith("[1m]") || apiConfiguration.largeInputTierEnabled === true
254-
const isSonnet45 = id.startsWith("claude-sonnet-4-5@20250929")
255-
const isSonnet4 = id.startsWith("claude-sonnet-4@20250514")
256-
const regionalPricingRegions = new Set(["us-east5", "europe-west1", "asia-southeast1"])
257-
const useRegionalPricing = regionalPricingRegions.has(region)
229+
// Delegate pricing and 1M context adjustments to shared types helper
230+
const info = getVertexAdjustedModelInfo(id, baseInfo, {
231+
region: apiConfiguration.vertexRegion ?? "global",
232+
largeInputTierEnabled: apiConfiguration.largeInputTierEnabled === true,
233+
})
258234

259-
let adjustedInfo: ModelInfo = baseInfo as ModelInfo
260-
261-
if (isSonnet45) {
262-
if (is1m) {
263-
// Over 200k (1M beta)
264-
adjustedInfo = {
265-
...baseInfo,
266-
contextWindow: 1_000_000,
267-
inputPrice: useRegionalPricing ? (6.6 as number) : (6.0 as number),
268-
outputPrice: useRegionalPricing ? (24.75 as number) : (22.5 as number),
269-
cacheWritesPrice: useRegionalPricing ? (8.25 as number) : (7.5 as number),
270-
cacheReadsPrice: useRegionalPricing ? (0.66 as number) : (0.6 as number),
271-
} as ModelInfo
272-
} else {
273-
// Under 200k
274-
adjustedInfo = {
275-
...baseInfo,
276-
contextWindow: 200_000,
277-
inputPrice: useRegionalPricing ? (3.3 as number) : (3.0 as number),
278-
outputPrice: useRegionalPricing ? (16.5 as number) : (15.0 as number),
279-
cacheWritesPrice: useRegionalPricing ? (4.13 as number) : (3.75 as number),
280-
cacheReadsPrice: useRegionalPricing ? (0.33 as number) : (0.3 as number),
281-
} as ModelInfo
282-
}
283-
} else if (isSonnet4) {
284-
if (is1m) {
285-
// Over 200k (1M beta) - global pricing
286-
adjustedInfo = {
287-
...baseInfo,
288-
contextWindow: 1_000_000,
289-
inputPrice: 6.0 as number,
290-
outputPrice: 22.5 as number,
291-
cacheWritesPrice: 7.5 as number,
292-
cacheReadsPrice: 0.6 as number,
293-
} as ModelInfo
294-
} else {
295-
// Under 200k - global pricing
296-
adjustedInfo = {
297-
...baseInfo,
298-
contextWindow: 200_000,
299-
inputPrice: 3.0 as number,
300-
outputPrice: 15.0 as number,
301-
cacheWritesPrice: 3.75 as number,
302-
cacheReadsPrice: 0.3 as number,
303-
} as ModelInfo
304-
}
305-
}
306-
307-
return { id, info: adjustedInfo }
235+
return { id, info }
308236
}
309237
case "gemini": {
310238
const id = apiConfiguration.apiModelId ?? geminiDefaultModelId

0 commit comments

Comments
 (0)