Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ LLM_Z_AI_API_KEY=your_zai_key_here

# AWS Bedrock
LLM_AWS_BEDROCK_API_KEY=your_aws_bedrock_key_here
# AWS Bedrock Region (options: us-east-1, us-west-2, eu-west-1, eu-central-1, ap-northeast-1, ap-southeast-1)
# LLM_AWS_BEDROCK_REGION=us-east-1

# Azure
LLM_AZURE_API_KEY=your_azure_key_here
Expand Down
24 changes: 24 additions & 0 deletions apps/api/src/routes/keys-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ const providerKeySchema = z.object({
options: z
.object({
aws_bedrock_region_prefix: z.enum(["us.", "global.", "eu."]).optional(),
aws_bedrock_region: z
.enum([
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"eu-west-1",
"eu-central-1",
"ap-northeast-1",
"ap-southeast-1",
])
.optional(),
azure_resource: z.string().optional(),
azure_api_version: z.string().optional(),
azure_deployment_type: z.enum(["openai", "ai-foundry"]).optional(),
Expand Down Expand Up @@ -59,6 +71,18 @@ const createProviderKeySchema = z.object({
options: z
.object({
aws_bedrock_region_prefix: z.enum(["us.", "global.", "eu."]).optional(),
aws_bedrock_region: z
.enum([
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"eu-west-1",
"eu-central-1",
"ap-northeast-1",
"ap-southeast-1",
])
.optional(),
azure_resource: z.string().optional(),
azure_api_version: z.string().optional(),
azure_deployment_type: z.enum(["openai", "ai-foundry"]).optional(),
Expand Down
8 changes: 8 additions & 0 deletions apps/code/src/lib/api/v1.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4770,6 +4770,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4807,6 +4809,8 @@ export interface paths {
options?: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4837,6 +4841,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4955,6 +4961,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down
79 changes: 79 additions & 0 deletions apps/gateway/src/lib/costs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,85 @@ describe("calculateCosts", () => {
expect(result.imageInputCost).toBeCloseTo(1120 * (2 / 1e6) * 0.8);
});

describe("region-aware cost lookup", () => {
it("should calculate costs for a bedrock model with region suffix", async () => {
// anthropic.claude-sonnet-4-5-20250929-v1:0 on aws-bedrock with us-east-1 region
const result = await calculateCosts(
"anthropic.claude-sonnet-4-5-20250929-v1:0:us-east-1",
"aws-bedrock",
100,
50,
null,
);

// Should find the model via expanded region entries
expect(result.inputCost).not.toBeNull();
expect(result.outputCost).not.toBeNull();
expect(result.totalCost).not.toBeNull();
expect(result.promptTokens).toBe(100);
expect(result.completionTokens).toBe(50);
// aws-bedrock claude-sonnet-4-5-20250929 inputPrice: 3.0/1e6, outputPrice: 15.0/1e6, discount: 0.3
expect(result.inputCost).toBeCloseTo(100 * (3.0 / 1e6) * 0.7);
expect(result.outputCost).toBeCloseTo(50 * (15.0 / 1e6) * 0.7);
});

it("should calculate costs for base bedrock model without region suffix", async () => {
const result = await calculateCosts(
"anthropic.claude-sonnet-4-5-20250929-v1:0",
"aws-bedrock",
100,
50,
null,
);

expect(result.inputCost).not.toBeNull();
expect(result.outputCost).not.toBeNull();
expect(result.inputCost).toBeCloseTo(100 * (3.0 / 1e6) * 0.7);
expect(result.outputCost).toBeCloseTo(50 * (15.0 / 1e6) * 0.7);
});

it("should return null costs for non-existent region on bedrock model", async () => {
// Using a region that doesn't exist for this model.
// The baseModel extraction splits on ":" so "v1:0:sa-east-1" becomes
// "anthropic.claude-sonnet-4-5-20250929-v1" which doesn't match any model.
const result = await calculateCosts(
"anthropic.claude-sonnet-4-5-20250929-v1:0:sa-east-1",
"aws-bedrock",
100,
50,
null,
);

// Cost lookup returns null because the colon-based baseModel extraction
// can't handle Bedrock model names that already contain colons
expect(result.inputCost).toBeNull();
});

it("should use region-specific pricing when defined (alibaba)", async () => {
// qwen-max on alibaba: default inputPrice 1.6/1e6, cn-beijing inputPrice 0.345/1e6
const resultDefault = await calculateCosts(
"qwen-max:singapore",
"alibaba",
1000,
500,
null,
);

const resultBeijing = await calculateCosts(
"qwen-max:cn-beijing",
"alibaba",
1000,
500,
null,
);

expect(resultDefault.inputCost).not.toBeNull();
expect(resultBeijing.inputCost).not.toBeNull();
// Beijing pricing should be cheaper than Singapore
expect(resultBeijing.inputCost!).toBeLessThan(resultDefault.inputCost!);
});
});

it("should include image costs in totalCost sum", async () => {
// totalCost = inputCost + outputCost + cachedInputCost + requestCost + webSearchCost
// (inputCost already includes imageInputCost, outputCost already includes imageOutputCost)
Expand Down
10 changes: 10 additions & 0 deletions apps/gateway/src/lib/costs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ export async function calculateCosts(
) as ModelDefinition;
}

// Fallback: search expanded providers for region-suffixed model names
// (e.g., "anthropic.claude-3-5-haiku-20241022-v1:0:us-east-1")
if (!modelInfo) {
modelInfo = models.find((m) =>
expandAllProviderRegions(m.providers as ProviderModelMapping[]).some(
(p) => p.modelName === model,
),
) as ModelDefinition;
}

if (!modelInfo) {
return {
inputCost: null,
Expand Down
19 changes: 17 additions & 2 deletions apps/playground/src/components/model-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -462,11 +462,26 @@ export function ModelSelector({
const [selectedProviderId, selectedModelIdRaw] = raw.includes("/")
? (raw.split("/") as [string, string])
: ["", raw];
// Strip :region suffix for root model lookup, keep raw for mapping match
// Strip :region suffix for root model lookup, keep raw for mapping match.
// Model names can contain colons (e.g., "anthropic.claude-3-5-haiku-20241022-v1:0"),
// so we first try a direct match, then fall back to stripping the last colon segment.
const selectedModelId = selectedModelIdRaw.includes(":")
? selectedModelIdRaw.split(":")[0]
: selectedModelIdRaw;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
const selectedModel = models.find((m) => m.id === selectedModelId);
// Direct lookup by model.id, then fallback: search all models for a mapping
// that matches the raw provider model name (handles region-expanded names like
// "anthropic.claude-3-5-haiku-20241022-v1:0:us-east-1")
const selectedModel =
models.find((m) => m.id === selectedModelId) ??
(selectedProviderId
? models.find((m) =>
m.mappings.some(
(p) =>
p.providerId === selectedProviderId &&
p.modelName === selectedModelIdRaw,
),
)
: undefined);
const selectedProviderDef = providers.find(
(p) => p.id === selectedProviderId,
);
Expand Down
8 changes: 8 additions & 0 deletions apps/playground/src/lib/api/v1.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4770,6 +4770,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4807,6 +4809,8 @@ export interface paths {
options?: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4837,6 +4841,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4955,6 +4961,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"api": "workspace:*",
"babel-plugin-react-compiler": "1.0.0",
"better-auth": "1.5.5",
"canvas-confetti": "1.9.4",
"class-variance-authority": "0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
Expand Down Expand Up @@ -91,6 +92,7 @@
},
"devDependencies": {
"@tanstack/react-query-devtools": "5.90.2",
"@types/canvas-confetti": "1.9.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"autoprefixer": "^10.4.21",
Expand Down
3 changes: 2 additions & 1 deletion apps/ui/src/components/provider-keys/provider-keys-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ interface ProviderKeysListProps {

function formatOptionLabel(key: string, value: string): string {
const labels: Record<string, string> = {
aws_bedrock_region_prefix: "Region",
aws_bedrock_region_prefix: "Cross-Region Prefix",
aws_bedrock_region: "Region",
azure_resource: "Resource",
azure_api_version: "API Version",
azure_deployment_type: "Deployment",
Expand Down
8 changes: 8 additions & 0 deletions apps/ui/src/lib/api/v1.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4770,6 +4770,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4807,6 +4809,8 @@ export interface paths {
options?: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4837,6 +4841,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4955,6 +4961,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down
8 changes: 8 additions & 0 deletions ee/admin/src/lib/api/v1.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4770,6 +4770,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4807,6 +4809,8 @@ export interface paths {
options?: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4837,6 +4841,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down Expand Up @@ -4955,6 +4961,8 @@ export interface paths {
options: {
/** @enum {string} */
aws_bedrock_region_prefix?: "us." | "global." | "eu.";
/** @enum {string} */
aws_bedrock_region?: "us-east-1" | "us-east-2" | "us-west-1" | "us-west-2" | "eu-west-1" | "eu-central-1" | "ap-northeast-1" | "ap-southeast-1";
azure_resource?: string;
azure_api_version?: string;
/** @enum {string} */
Expand Down
Loading
Loading