diff --git a/packages/types/src/providers/bedrock.ts b/packages/types/src/providers/bedrock.ts index 492089be5b..e341a32f48 100644 --- a/packages/types/src/providers/bedrock.ts +++ b/packages/types/src/providers/bedrock.ts @@ -4,6 +4,18 @@ import type { ModelInfo } from "../model.js" export type BedrockModelId = keyof typeof bedrockModels +export const BedrockModelsMessageType = { + requestBedrockModels: "requestBedrockModels", + cancelBedrockModels: "cancelBedrockModels", + bedrockModels: "bedrockModels", +} as const + +export interface BedrockCatalogEntry { + arn: string + name: string + kind: "regional" | "geographic" | "global" | "application" +} + export const bedrockDefaultModelId: BedrockModelId = "anthropic.claude-sonnet-4-5-20250929-v1:0" export const bedrockDefaultPromptRouterModelId: BedrockModelId = "anthropic.claude-3-sonnet-20240229-v1:0" @@ -738,3 +750,24 @@ export const BEDROCK_SERVICE_TIER_PRICING = { FLEX: 0.5, // 50% discount from standard PRIORITY: 1.75, // 75% premium over standard } as const + +/** Resolve the bundled model selection without silently opting into a routing geography. */ +export function getBedrockInferenceModelId( + modelId: string, + region?: string, + crossRegion?: boolean, + global?: boolean, +): string { + const existingPrefix = ["global.", ...AWS_INFERENCE_PROFILE_MAPPING.map(([, prefix]) => prefix)].find((prefix) => + modelId.startsWith(prefix), + ) + if (existingPrefix) modelId = modelId.slice(existingPrefix.length) + if (global && BEDROCK_GLOBAL_INFERENCE_MODEL_IDS.some((id) => id === modelId)) { + return `global.${modelId}` + } + const prefix = + crossRegion && region + ? AWS_INFERENCE_PROFILE_MAPPING.find(([pattern]) => region.startsWith(pattern))?.[1] + : undefined + return prefix ? `${prefix}${modelId}` : modelId +} diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index 5f6b579779..bbad655ed8 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -16,6 +16,7 @@ import { RouterModelsMessageType, type ModelRecord, type RouterModels } from "./ import { LmStudioModelsMessageType } from "./providers/lm-studio.js" import { OllamaModelsMessageType } from "./providers/ollama.js" import { OpenAiModelsMessageType } from "./providers/openai.js" +import { BedrockModelsMessageType, type BedrockCatalogEntry } from "./providers/bedrock.js" import { VsCodeLmModelsMessageType } from "./providers/vscode-llm.js" import type { OpenAiCodexRateLimitInfo } from "./providers/openai-codex-rate-limits.js" import type { SkillMetadata } from "./skills.js" @@ -45,6 +46,7 @@ export interface ExtensionMessage { | typeof RouterModelsMessageType.routerModels | "zooGatewayCredentialsReady" | typeof OpenAiModelsMessageType.openAiModels + | typeof BedrockModelsMessageType.bedrockModels | typeof OllamaModelsMessageType.ollamaModels | typeof LmStudioModelsMessageType.lmStudioModels | typeof VsCodeLmModelsMessageType.vsCodeLmModels @@ -141,6 +143,7 @@ export interface ExtensionMessage { clineMessage?: ClineMessage routerModels?: RouterModels openAiModels?: string[] + bedrockModels?: BedrockCatalogEntry[] ollamaModels?: ModelRecord lmStudioModels?: ModelRecord vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[] @@ -490,6 +493,8 @@ export interface WebviewMessage { | typeof RouterModelsMessageType.flushRouterModels | typeof RouterModelsMessageType.requestRouterModels | typeof OpenAiModelsMessageType.requestOpenAiModels + | typeof BedrockModelsMessageType.requestBedrockModels + | typeof BedrockModelsMessageType.cancelBedrockModels | typeof OllamaModelsMessageType.requestOllamaModels | typeof LmStudioModelsMessageType.requestLmStudioModels | "requestRooModels" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b93c1cfe5..a9a6a2df57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -424,6 +424,9 @@ importers: '@anthropic-ai/vertex-sdk': specifier: ^0.19.0 version: 0.19.0(zod@3.25.76) + '@aws-sdk/client-bedrock': + specifier: ^3.922.0 + version: 3.1136.0 '@aws-sdk/client-bedrock-runtime': specifier: ^3.922.0 version: 3.1078.0 @@ -1036,6 +1039,10 @@ packages: resolution: {integrity: sha512-GGIpsHOk+zMRQMgxd+5D7Kfhpe6qzyGP4shGzb7NwYqAEleCW9PgX5xXuVQEESkhGX5kqMkDPfT+gg6PN2gczg==} engines: {node: '>=20.0.0'} + '@aws-sdk/client-bedrock@3.1136.0': + resolution: {integrity: sha512-7WJt7duGYkrjdfv/AuRupzK0DJ0S1C/JzDNdwCFPv3ljGqpIu3N1OGrcUrTRJhnkUQQ0F7LMt0Ubw04ZLS3LVA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/client-cognito-identity@3.1078.0': resolution: {integrity: sha512-BYy0X/+GMXlitKShxkdTsCexWwDrn8usY2Y2Z06M5MSi4aRT3Ce5ilyA6OubQUqOWfsmDMYrm8oBNaTIcQFyrg==} engines: {node: '>=20.0.0'} @@ -1044,6 +1051,10 @@ packages: resolution: {integrity: sha512-WRWEgIq6vx+NU6ot3VrRu4Jovj9MIObitSi6of/GV5THDDPccBhivCRNkWJutMM+m3GvdeI3l/UbGNcoOobxOA==} engines: {node: '>=20.0.0'} + '@aws-sdk/core@3.978.0': + resolution: {integrity: sha512-2yX9LUmxPklVjSGTb8dfnWRJSiFQ3TeH2nn7G1mdKHTfnabzF0+gfrS8rYfLWmZrQ8A3mEcxMJjRc51dL5KWaA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-cognito-identity@3.972.52': resolution: {integrity: sha512-m+akZFJsghShferf2xsMw0Hogl1jNIJl2zUoZBNTFyWvlaOj1aK5sMTzcnw8m1dICvlQ+lC4T1OPGGsmZ+ezXA==} engines: {node: '>=20.0.0'} @@ -1052,34 +1063,66 @@ packages: resolution: {integrity: sha512-+KDA3uc/HZ1vIneGu5QMQb0gAXDYrm2vOE60+BJ7lS0YinMQ5i2oV4PR1A16XkF6K1IbSwjEHd1hQIIgMsK48w==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-env@3.972.71': + resolution: {integrity: sha512-JN+JHruYZw3GUZB8YGAlDk4wTDPOEAEEdEzj5nS0xodWR4smzHsN7PnK2j6IeOsDIj2aqua5DSbhXl9Gtf90FQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.972.55': resolution: {integrity: sha512-1gBfkWY3RWeBlCoB9lIJjXMx45/54wxcgfzv6BY9otTmMrZPcNPi1v+MwZxxaCUg441NV3jsr1efnFNCXiW70g==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-http@3.972.73': + resolution: {integrity: sha512-uyYYnJOnlis8uQzaYGPd7N1JoioCoNpXgnkXYixsWJXHXgXyYi8WXJSDfofxJeWfQIGWLe2Nwyq60Uc7MZdVOg==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.972.60': resolution: {integrity: sha512-CV2md+PXvABwRjApWGhQ0wACy9WSFIhnUGrovLcjnjBCd/46TbuivLADtkF8IWNjtCQmQ+2IagSaxqBYqXBNAQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-ini@3.973.16': + resolution: {integrity: sha512-i++ly+0Uxa+u3ebSSyr0S/3CFhFJDxCXT3+Zj+mW2bXenEx5bKGCdTIKFu39SgXBNhWDjex/8cXUx9MUTMCrTw==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.972.59': resolution: {integrity: sha512-JG4S9yyA1GFzJdJXqLKrUzZbyK+VDp2QIsJD7YOicJHAhqymfHpDJIok2dLnhOdVB0I37RjdC53uOwCMVS00gw==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-login@3.972.78': + resolution: {integrity: sha512-eUtswnXu0+Ii9ieRK+0L7aPFV3Z/dnW2VntJzjBP9xs8s+8p5nBNuymIXtXwZ+5r5+XJP3e32nMkuZ/r0HozEA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.972.62': resolution: {integrity: sha512-S6Slq3Tx7bvFk5yc34XNADyZYTX2HUXvaFAnowGRQnhjBO8J/mP62Fn7lxvJwjaDyYm/7gh9h6HEHaltRyMFXw==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-node@3.972.83': + resolution: {integrity: sha512-jdso7ejzfRnatxMUZK4S/U6KbaDPCvfIV4XL+IQAPFDBt5rj5Fq595euqlK8Le4lNCMFR9oUpt+1l0aMgaayOQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-process@3.972.53': resolution: {integrity: sha512-EhfH+MQlqOMCkXIVa8MMObPzAQqwTTtxA7KhEJiyPeuNVA8PLOOUpgK7nBrgaDaGiIDLN/9LpGdaHuDjomeRTw==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-process@3.972.71': + resolution: {integrity: sha512-lYmXJa4gvq4xN1lrT5NiP5vIYYKcGWAdj8y+8o6dlcateB5eF3Dn8DtmjjHKfMBrTPAMr2pebIiX/UOj8c1/UA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.972.59': resolution: {integrity: sha512-h8793pOjcImx0SB+VcLONcaQQ57VAvKVuqyewQMRKqqH+CSXsG2dwOeLMUJPMxLdNvL7dXOM0ueTukyNUnu5mA==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-sso@3.973.15': + resolution: {integrity: sha512-6Jhcf4v0pSFdjk1EW2kvzuEBKD+UZ2uNcHUIglKKLndD20YhvkL2kdmDOV5/j4mYuWWwe/a1FQ1aomU86/Cg5Q==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.59': resolution: {integrity: sha512-VoyO9+vl3XVmpZwn4obskrWIkrA/Jf3lSe1E3ZERlaN9u0D4YZ6+HywC3+L98QOXqZesEfedk67gRER8tK8+8w==} engines: {node: '>=20.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.77': + resolution: {integrity: sha512-uylIQSUWpfLuH2LovxEEfwzJGM/SabLOfLMg6YXu/E8jJEKUdpdILCVCQCdFvHyu/7dLJOHPMfrSwduxO56NkQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/credential-providers@3.1078.0': resolution: {integrity: sha512-V9Tr3MrNWUfTGgTMIr+WJaMC/VDbXY57BzrGDuyDZn7+vgZjAEG6nI5nMdfnTGdvV9wq1n0zZPYW2RDfcsWNCw==} engines: {node: '>=20.0.0'} @@ -1100,10 +1143,18 @@ packages: resolution: {integrity: sha512-A8PIePF9NIIOJ/4Lg1rl9xm/+QaKkHGetq+Z9wb5B+3Da31YYXRo8n7IDMh5C+HQI5eyEmjrwkGWVdYtnLtbXQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/nested-clients@3.997.45': + resolution: {integrity: sha512-mooq9Q+jLa18VoM7HouczmslZU60iiB0aKc/Ztnq/luIL1ud0z4DnYprLR/ZO1gp331S9tJctM1HZr7u6YKBXQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/signature-v4-multi-region@3.996.38': resolution: {integrity: sha512-C379Sk+MiFZCfWZphKlMyLHKxV22OjoGM5KJjj5IJNJcOCWL4IGIpnEGzv1FQiRwhYXfq55SJMfxlqPE08JJ9g==} engines: {node: '>=20.0.0'} + '@aws-sdk/signature-v4-multi-region@3.996.46': + resolution: {integrity: sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/token-providers@3.1078.0': resolution: {integrity: sha512-/uyXLBGu3Lw1GbBA2X66hcOMnKtMcqAIF+3/eHfxBQmUeXF2sdqozDPrTfEr/TnSd0D6deZar+eVyhEqqWu29w==} engines: {node: '>=20.0.0'} @@ -1112,14 +1163,30 @@ packages: resolution: {integrity: sha512-cbietrLlHPhhmbnMPTuDS4Zj/KNGhY+3vVhn6dwjO6Dqzrwothzg2srtcY34T9mlICsTXn34avDoWLHSntP54A==} engines: {node: '>=20.0.0'} + '@aws-sdk/token-providers@3.1129.0': + resolution: {integrity: sha512-Sbl3rpzQdsG4ZK2zh0JWUYyZPKKorJlVOddA2T0DVbKJFrsW8J6wgnslxxUH04+WaBMr4A1HzJZvZX0xUvkniA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1136.0': + resolution: {integrity: sha512-CHjdpFszuNTUEQ2z1F85pOiHxpLe0fDyvTUoNAZ7YczdTMEfU6TSA7FVZelAM67L/g6xcS31U64HuBl42dSKCA==} + engines: {node: '>=20.0.0'} + '@aws-sdk/types@3.973.15': resolution: {integrity: sha512-IULn8uBV/SMtmOIANsm4WHXIOtVPBWfOWs3WGL0j/sI+KhaYehvOw0ET+9urnn8MBpiijuU/0JOpuwKOE451PQ==} engines: {node: '>=20.0.0'} + '@aws-sdk/types@3.974.5': + resolution: {integrity: sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==} + engines: {node: '>=20.0.0'} + '@aws-sdk/xml-builder@3.972.33': resolution: {integrity: sha512-ezbwz9WpuLctm6o7P2t2naDhVVPI5jFGrVefVybhcKGjU57VIyT46pQVO0RI2RYkUdhdj2Z9uSIlAzGZE9NW9A==} engines: {node: '>=20.0.0'} + '@aws-sdk/xml-builder@3.972.40': + resolution: {integrity: sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==} + engines: {node: '>=20.0.0'} + '@aws/lambda-invoke-store@0.3.0': resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} engines: {node: '>=18.0.0'} @@ -3079,14 +3146,30 @@ packages: resolution: {integrity: sha512-qoiY4nrk5OCu1+eIR1VB8l5DmON/oKiqrd5zZFAhXJXjJlLWQusKEW/SkBDAtGDcPaz86m9kfcE1lngU0GlM6A==} engines: {node: '>=18.0.0'} + '@smithy/core@3.34.1': + resolution: {integrity: sha512-dLcOUxz8YCv1RZUMKq6GbyUf95pLbrqh34bPvpCZ1+CByFF31BEAFewZjsGCnVsZTKdThNENfGyAgk2TJqVwSw==} + engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.4.6': resolution: {integrity: sha512-B2WQ/PV/H6Jeg3lrIq6bKUfa6Hy01mtK7CGs6lhjzHA6k4aagldH6T6eEjnzKl4HI0cJnAsxfJ19pgb5PV+CVQ==} engines: {node: '>=18.0.0'} + '@smithy/credential-provider-imds@4.5.2': + resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} + engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.6.3': resolution: {integrity: sha512-CwCc/7SMTj45y97MUnDTbTaxvtAsiNNRm81z3abROIuMbMsC2Iy5EKfkkVdsKrz8WExQAAMx1EJapq+9j4fFTQ==} engines: {node: '>=18.0.0'} + '@smithy/fetch-http-handler@5.8.0': + resolution: {integrity: sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.12.1': + resolution: {integrity: sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==} + engines: {node: '>=18.0.0'} + '@smithy/node-http-handler@4.9.3': resolution: {integrity: sha512-qZTa4gQFUo8RM02rk6q5UVTDLNrQ1oS20LsepBzqq1QBVc/EHJ03OOUADcqMZiXHArW+Y7+OGY0BpdTwZRq/Yg==} engines: {node: '>=18.0.0'} @@ -3095,10 +3178,18 @@ packages: resolution: {integrity: sha512-QgHflghMoPxCJ9axiCVh8KZfbC9fuP6vkXXyK//E3cq7nLaSSyyLj0GAoqVWezYeDQmXIZhmlRvLE16jsqDK6g==} engines: {node: '>=18.0.0'} + '@smithy/signature-v4@5.7.3': + resolution: {integrity: sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==} + engines: {node: '>=18.0.0'} + '@smithy/types@4.15.1': resolution: {integrity: sha512-x3L0XSACF6UYzKpa9biqiRMgvH5+wnFFew9Tm/grFYqgaupPwx/+ojDPpPJM8dZON3S9tjz5U+PQYsCBd1Mw5Q==} engines: {node: '>=18.0.0'} + '@smithy/types@4.18.0': + resolution: {integrity: sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A==} + engines: {node: '>=18.0.0'} + '@stablelib/base64@1.0.1': resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} @@ -9035,6 +9126,18 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/client-bedrock@3.1136.0': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/credential-provider-node': 3.972.83 + '@aws-sdk/token-providers': 3.1136.0 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.12.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/client-cognito-identity@3.1078.0': dependencies: '@aws-sdk/core': 3.974.27 @@ -9057,6 +9160,17 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 + '@aws-sdk/core@3.978.0': + dependencies: + '@aws-sdk/types': 3.974.5 + '@aws-sdk/xml-builder': 3.972.40 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.34.1 + '@smithy/signature-v4': 5.7.3 + '@smithy/types': 4.18.0 + bowser: 2.14.1 + tslib: 2.8.1 + '@aws-sdk/credential-provider-cognito-identity@3.972.52': dependencies: '@aws-sdk/nested-clients': 3.997.27 @@ -9073,6 +9187,14 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-env@3.972.71': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.972.55': dependencies: '@aws-sdk/core': 3.974.27 @@ -9083,6 +9205,16 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-http@3.972.73': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.12.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-ini@3.972.60': dependencies: '@aws-sdk/core': 3.974.27 @@ -9099,6 +9231,22 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-ini@3.973.16': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/credential-provider-env': 3.972.71 + '@aws-sdk/credential-provider-http': 3.972.73 + '@aws-sdk/credential-provider-login': 3.972.78 + '@aws-sdk/credential-provider-process': 3.972.71 + '@aws-sdk/credential-provider-sso': 3.973.15 + '@aws-sdk/credential-provider-web-identity': 3.972.77 + '@aws-sdk/nested-clients': 3.997.45 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-login@3.972.59': dependencies: '@aws-sdk/core': 3.974.27 @@ -9108,6 +9256,15 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-login@3.972.78': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/nested-clients': 3.997.45 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-node@3.972.62': dependencies: '@aws-sdk/credential-provider-env': 3.972.53 @@ -9122,6 +9279,20 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-node@3.972.83': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.71 + '@aws-sdk/credential-provider-http': 3.972.73 + '@aws-sdk/credential-provider-ini': 3.973.16 + '@aws-sdk/credential-provider-process': 3.972.71 + '@aws-sdk/credential-provider-sso': 3.973.15 + '@aws-sdk/credential-provider-web-identity': 3.972.77 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/credential-provider-imds': 4.5.2 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-process@3.972.53': dependencies: '@aws-sdk/core': 3.974.27 @@ -9130,6 +9301,14 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-process@3.972.71': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-sso@3.972.59': dependencies: '@aws-sdk/core': 3.974.27 @@ -9140,6 +9319,16 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-sso@3.973.15': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/nested-clients': 3.997.45 + '@aws-sdk/token-providers': 3.1129.0 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-provider-web-identity@3.972.59': dependencies: '@aws-sdk/core': 3.974.27 @@ -9149,6 +9338,15 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/credential-provider-web-identity@3.972.77': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/nested-clients': 3.997.45 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/credential-providers@3.1078.0': dependencies: '@aws-sdk/client-cognito-identity': 3.1078.0 @@ -9204,6 +9402,17 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/nested-clients@3.997.45': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/signature-v4-multi-region': 3.996.46 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/fetch-http-handler': 5.8.0 + '@smithy/node-http-handler': 4.12.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/signature-v4-multi-region@3.996.38': dependencies: '@aws-sdk/types': 3.973.15 @@ -9211,6 +9420,13 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/signature-v4-multi-region@3.996.46': + dependencies: + '@aws-sdk/types': 3.974.5 + '@smithy/signature-v4': 5.7.3 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/token-providers@3.1078.0': dependencies: '@aws-sdk/core': 3.974.27 @@ -9229,16 +9445,44 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/token-providers@3.1129.0': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/nested-clients': 3.997.45 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1136.0': + dependencies: + '@aws-sdk/core': 3.978.0 + '@aws-sdk/nested-clients': 3.997.45 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/types@3.973.15': dependencies: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/types@3.974.5': + dependencies: + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws-sdk/xml-builder@3.972.33': dependencies: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@aws-sdk/xml-builder@3.972.40': + dependencies: + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@aws/lambda-invoke-store@0.3.0': {} '@azu/format-text@1.0.2': {} @@ -11275,18 +11519,41 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@smithy/core@3.34.1': + dependencies: + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@smithy/credential-provider-imds@4.4.6': dependencies: '@smithy/core': 3.29.1 '@smithy/types': 4.15.1 tslib: 2.8.1 + '@smithy/credential-provider-imds@4.5.2': + dependencies: + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@smithy/fetch-http-handler@5.6.3': dependencies: '@smithy/core': 3.29.1 '@smithy/types': 4.15.1 tslib: 2.8.1 + '@smithy/fetch-http-handler@5.8.0': + dependencies: + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.12.1': + dependencies: + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@smithy/node-http-handler@4.9.3': dependencies: '@smithy/core': 3.29.1 @@ -11299,10 +11566,20 @@ snapshots: '@smithy/types': 4.15.1 tslib: 2.8.1 + '@smithy/signature-v4@5.7.3': + dependencies: + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 + tslib: 2.8.1 + '@smithy/types@4.15.1': dependencies: tslib: 2.8.1 + '@smithy/types@4.18.0': + dependencies: + tslib: 2.8.1 + '@stablelib/base64@1.0.1': {} '@standard-schema/spec@1.1.0': {} diff --git a/src/api/providers/__tests__/bedrock.spec.ts b/src/api/providers/__tests__/bedrock.spec.ts index fd6da53a9b..a08ba5f461 100644 --- a/src/api/providers/__tests__/bedrock.spec.ts +++ b/src/api/providers/__tests__/bedrock.spec.ts @@ -97,6 +97,42 @@ describe("AwsBedrockHandler", () => { }) }) + it.each(["isn't supported", "isn’t supported.", "is not supported"])( + "explains inference-profile errors with wording %s", + async (wording) => { + handler["client"].send = vi + .fn() + .mockRejectedValue(new Error(`Invocation with on-demand throughput ${wording}`)) + const consume = async () => { + for await (const chunk of handler.createMessage("system", [{ role: "user", content: "hello" }])) { + void chunk + } + } + await expect(consume()).rejects.toThrow("Try enabling cross-region inference in settings") + }, + ) + + it("preserves a newly discovered foundation-model ID rather than invoking the default Claude model", () => { + const provider = new AwsBedrockHandler({ + apiModelId: "custom-arn", + awsRegion: "eu-west-3", + awsCustomArn: "arn:aws:bedrock:eu-west-3::foundation-model/mistral.new-model-v1:0", + }) + expect(provider.getModel().id).toBe("mistral.new-model-v1:0") + expect(provider.getModel().info.supportsReasoningBudget).not.toBe(true) + }) + + it("rejects the custom-arn sentinel before sending a request when no ARN is entered", async () => { + const provider = new AwsBedrockHandler({ apiModelId: "custom-arn", awsRegion: "eu-west-3" }) + const send = vi.fn() + provider["client"].send = send + await expect( + collectStream(provider.createMessage("system", [{ role: "user", content: "hello" }])), + ).rejects.toThrow("Enter a custom ARN") + await expect(provider.completePrompt("hello")).rejects.toThrow("Enter a custom ARN") + expect(send).not.toHaveBeenCalled() + }) + it.each(["max_tokens", "end_turn", "tool_use", "stop_sequence"])( "reports truncation only for %s, after preserving usage", async (stopReason) => { diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index 2e71ce0e12..9c7d45dd02 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -30,8 +30,8 @@ import { BEDROCK_DEFAULT_CONTEXT, AWS_INFERENCE_PROFILE_MAPPING, BEDROCK_1M_CONTEXT_MODEL_IDS, + getBedrockInferenceModelId, BEDROCK_THINKING_DISABLE_MODEL_IDS, - BEDROCK_GLOBAL_INFERENCE_MODEL_IDS, BEDROCK_SERVICE_TIER_MODEL_IDS, BEDROCK_SERVICE_TIER_PRICING, SERVICE_TIER_KEY, @@ -355,9 +355,9 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH } // Helper to guess model info from custom modelId string if not in bedrockModels - private guessModelInfoFromId(modelId: string): Partial { + private guessModelInfoFromId(modelId: string): ModelInfo { // Define a mapping for model ID patterns and their configurations - const modelConfigMap: Record> = { + const modelConfigMap: Record = { "claude-4": { maxTokens: 8192, contextWindow: 200_000, @@ -424,6 +424,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH } }, ): ApiStream { + this.assertCustomArnProvided() const modelConfig = this.getModel() const usePromptCache = Boolean( (this.options.awsUsePromptCache ?? true) && this.supportsAwsPromptCache(modelConfig), @@ -876,6 +877,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH } async completePrompt(prompt: string, options?: CompletePromptOptions): Promise { + this.assertCustomArnProvided() try { const modelConfig = this.getModel() @@ -1162,6 +1164,13 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH return modelId } + // "custom-arn" is a picker sentinel, not a Bedrock model ID; never send it as ConverseCommand.modelId. + private assertCustomArnProvided(): void { + if (this.options.apiModelId === "custom-arn" && !this.options.awsCustomArn?.trim()) { + throw new Error("Enter a custom ARN in the Amazon Bedrock settings, or select a model from the list.") + } + } + //Prompt Router responses come back in a different sequence and the model used is in the response and must be fetched by name getModelById(modelId: string, modelType?: string): { id: BedrockModelId | string; info: ModelInfo } { // Try to find the model in bedrockModels @@ -1182,9 +1191,8 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH // Use heuristics for model info, then allow overrides from ProviderSettings const guessed = this.guessModelInfoFromId(modelId) model = { - id: bedrockDefaultModelId, + id: modelId || bedrockDefaultModelId, info: { - ...JSON.parse(JSON.stringify(bedrockModels[bedrockDefaultModelId])), ...guessed, }, } @@ -1235,21 +1243,12 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH //a model was selected from the drop down modelConfig = this.getModelById(this.options.apiModelId as string) - // Apply Global Inference prefix if enabled and supported (takes precedence over cross-region) - const baseIdForGlobal = this.parseBaseModelId(modelConfig.id) - if ( - this.options.awsUseGlobalInference && - BEDROCK_GLOBAL_INFERENCE_MODEL_IDS.includes(baseIdForGlobal as any) - ) { - modelConfig.id = `global.${baseIdForGlobal}` - } - // Otherwise, add cross-region inference prefix if enabled - else if (this.options.awsUseCrossRegionInference && this.options.awsRegion) { - const prefix = AwsBedrockHandler.getPrefixForRegion(this.options.awsRegion) - if (prefix) { - modelConfig.id = `${prefix}${modelConfig.id}` - } - } + modelConfig.id = getBedrockInferenceModelId( + modelConfig.id, + this.options.awsRegion, + this.options.awsUseCrossRegionInference, + this.options.awsUseGlobalInference, + ) } // Check if 1M context is enabled for supported Claude 4 models @@ -1566,7 +1565,7 @@ Please try: logLevel: "error", }, ON_DEMAND_NOT_SUPPORTED: { - patterns: ["with on-demand throughput isn’t supported."], + patterns: ["on-demand throughput isn't supported", "on-demand throughput is not supported"], messageTemplate: ` 1. Try enabling cross-region inference in settings. 2. Or, create an inference profile and then leverage the "Use custom ARN..." option of the model selector in settings.`, @@ -1631,7 +1630,7 @@ Please check: return "THROTTLING" } - const errorMessage = error.message.toLowerCase() + const errorMessage = error.message.toLowerCase().replace(/[‘’]/g, "'") const errorName = error.name.toLowerCase() // Check each error type's patterns in order of specificity (most specific first) diff --git a/src/api/providers/fetchers/__tests__/bedrock.spec.ts b/src/api/providers/fetchers/__tests__/bedrock.spec.ts new file mode 100644 index 0000000000..d5256a63ef --- /dev/null +++ b/src/api/providers/fetchers/__tests__/bedrock.spec.ts @@ -0,0 +1,218 @@ +import { BedrockClient, ListFoundationModelsCommand } from "@aws-sdk/client-bedrock" +import { fromIni } from "@aws-sdk/credential-providers" +import { getBedrockCatalog } from "../bedrock" + +const { send, destroy, pages } = vi.hoisted(() => ({ send: vi.fn(), destroy: vi.fn(), pages: vi.fn() })) +vi.mock("@aws-sdk/client-bedrock", () => ({ + BedrockClient: vi.fn().mockImplementation(function () { + return { send, destroy } + }), + ListFoundationModelsCommand: vi.fn(), + paginateListInferenceProfiles: (...args: unknown[]) => pages(...args), +})) +vi.mock("@aws-sdk/credential-providers", () => ({ fromIni: vi.fn(() => "profile-provider") })) +vi.mock("../../../../utils/networkProxy", () => ({ getSystemProxyUrl: () => undefined })) + +beforeEach(() => { + vi.clearAllMocks() +}) + +it("uses the selected region and credentials, and includes every profile page with explicit routing labels", async () => { + send.mockResolvedValue({ + modelSummaries: [ + { modelArn: "foundation-new", modelName: "New model", responseStreamingSupported: true }, + { modelArn: "no-stream", responseStreamingSupported: false }, + ], + }) + pages.mockImplementation(async function* () { + yield { + inferenceProfileSummaries: [ + { + inferenceProfileArn: "geo", + inferenceProfileId: "eu.model", + status: "ACTIVE", + type: "SYSTEM_DEFINED", + }, + ], + } + yield { + inferenceProfileSummaries: [ + { + inferenceProfileArn: "world", + inferenceProfileId: "global.model", + status: "ACTIVE", + type: "SYSTEM_DEFINED", + }, + { + inferenceProfileArn: "app", + inferenceProfileId: "application", + status: "ACTIVE", + type: "APPLICATION", + }, + { inferenceProfileArn: "inactive", status: "CREATING" }, + ], + } + }) + const result = await getBedrockCatalog({ + awsRegion: "eu-west-3", + awsAccessKey: "key", + awsSecretKey: "secret", + awsSessionToken: "session", + }) + expect(BedrockClient).toHaveBeenCalledWith( + expect.objectContaining({ + region: "eu-west-3", + credentials: { accessKeyId: "key", secretAccessKey: "secret", sessionToken: "session" }, + }), + ) + expect(ListFoundationModelsCommand).toHaveBeenCalledWith({ byOutputModality: "TEXT", byInferenceType: "ON_DEMAND" }) + expect(result).toHaveLength(4) + expect(result).toEqual( + expect.arrayContaining([ + expect.objectContaining({ arn: "foundation-new", kind: "regional" }), + expect.objectContaining({ arn: "geo", kind: "geographic" }), + expect.objectContaining({ arn: "world", kind: "global" }), + expect.objectContaining({ arn: "app", kind: "application" }), + ]), + ) + expect(destroy).toHaveBeenCalledOnce() +}) + +it("uses a profile and releases the client on permission failure without fabricating availability", async () => { + send.mockRejectedValue(new Error("AccessDenied")) + await expect( + getBedrockCatalog({ + awsRegion: "us-east-1", + awsUseProfile: true, + awsProfile: "work", + awsAccessKey: "retained-key", + awsSecretKey: "retained-secret", + }), + ).rejects.toThrow("AccessDenied") + expect(fromIni).toHaveBeenCalledWith({ profile: "work", ignoreCache: true }) + expect(BedrockClient).toHaveBeenCalledWith(expect.objectContaining({ credentials: "profile-provider" })) + expect(destroy).toHaveBeenCalledOnce() +}) + +it.each([undefined, "", " "])( + "rejects incomplete profile mode (%j) without using retained keys", + async (awsProfile) => { + await expect( + getBedrockCatalog({ + awsRegion: "eu-west-3", + awsUseProfile: true, + awsProfile, + awsAccessKey: "retained-key", + awsSecretKey: "retained-secret", + }), + ).rejects.toThrow("Select an AWS profile") + expect(BedrockClient).not.toHaveBeenCalled() + }, +) + +it("does not silently use the default credential chain when API-key authentication is selected", async () => { + await expect(getBedrockCatalog({ awsRegion: "eu-west-3", awsUseApiKey: true })).rejects.toThrow("IAM credentials") + expect(BedrockClient).not.toHaveBeenCalled() +}) + +it("rejects a missing region before creating a client", async () => { + await expect(getBedrockCatalog({})).rejects.toThrow("Select an AWS region") + expect(BedrockClient).not.toHaveBeenCalled() +}) + +it("filters legacy and incomplete entries and sorts display names with ID/ARN fallbacks", async () => { + send.mockResolvedValue({ + modelSummaries: [ + { modelArn: "legacy", responseStreamingSupported: true, modelLifecycle: { status: "LEGACY" } }, + { modelName: "Missing ARN", responseStreamingSupported: true }, + { modelArn: "z-arn", responseStreamingSupported: true }, + { modelArn: "foundation", modelId: "b-id", responseStreamingSupported: true }, + ], + }) + pages.mockImplementation(async function* () { + yield { + inferenceProfileSummaries: [ + { status: "ACTIVE", inferenceProfileName: "Missing ARN" }, + { + inferenceProfileArn: "profile", + inferenceProfileId: "global.id", + inferenceProfileName: "a-name", + status: "ACTIVE", + type: "APPLICATION", + }, + { inferenceProfileArn: "c-arn", status: "ACTIVE" }, + ], + } + }) + await expect(getBedrockCatalog({ awsRegion: "eu-west-3" })).resolves.toEqual([ + { arn: "profile", name: "a-name", kind: "application" }, + { arn: "foundation", name: "b-id", kind: "regional" }, + { arn: "c-arn", name: "c-arn", kind: "geographic" }, + { arn: "z-arn", name: "z-arn", kind: "regional" }, + ]) + expect(BedrockClient).toHaveBeenCalledWith(expect.objectContaining({ credentials: undefined })) + expect(destroy).toHaveBeenCalledOnce() +}) + +it("accepts empty AWS responses without inventing models", async () => { + send.mockResolvedValue({}) + pages.mockImplementation(async function* () { + yield {} + }) + await expect(getBedrockCatalog({ awsRegion: "eu-west-3" })).resolves.toEqual([]) + expect(destroy).toHaveBeenCalledOnce() +}) + +it("releases the client if a later profile page fails instead of returning partial availability", async () => { + send.mockResolvedValue({ modelSummaries: [] }) + pages.mockImplementation(async function* () { + yield { inferenceProfileSummaries: [{ inferenceProfileArn: "first", status: "ACTIVE" }] } + throw new Error("Pagination denied") + }) + await expect(getBedrockCatalog({ awsRegion: "eu-west-3" })).rejects.toThrow("Pagination denied") + expect(destroy).toHaveBeenCalledOnce() +}) + +it.each([ + ["foundations", "external"], + ["profiles", "external"], + ["foundations", "timeout"], + ["profiles", "timeout"], +])("aborts pending %s discovery on %s cancellation and releases the client", async (operation, cancellation) => { + const external = new AbortController() + const timeout = new AbortController() + const timeoutSpy = vi.spyOn(AbortSignal, "timeout").mockReturnValue(timeout.signal) + const pending = vi.fn( + (signal: AbortSignal) => + new Promise((_resolve, reject) => { + signal.addEventListener("abort", () => reject(signal.reason), { once: true }) + }), + ) + send.mockImplementation((_command: unknown, { abortSignal }: { abortSignal: AbortSignal }) => + operation === "foundations" ? pending(abortSignal) : Promise.resolve({}), + ) + pages.mockImplementation(async function* (_config, _input, { abortSignal }: { abortSignal: AbortSignal }) { + yield {} + await pending(abortSignal) + }) + try { + const request = getBedrockCatalog({ awsRegion: "eu-west-3" }, external.signal) + const rejected = expect(request).rejects.toThrow("cancelled discovery") + await vi.waitFor(() => expect(pending).toHaveBeenCalledOnce()) + const signal = pending.mock.calls[0][0] + expect(signal.aborted).toBe(false) + expect(destroy).not.toHaveBeenCalled() + ;(cancellation === "external" ? external : timeout).abort(new Error("cancelled discovery")) + expect(signal.aborted).toBe(true) + await rejected + expect(timeoutSpy).toHaveBeenCalledWith(30_000) + expect(destroy).toHaveBeenCalledOnce() + if (operation === "profiles") { + expect(send.mock.calls[0][1].abortSignal).toBe(signal) + } else { + expect(pages).not.toHaveBeenCalled() + } + } finally { + timeoutSpy.mockRestore() + } +}) diff --git a/src/api/providers/fetchers/bedrock.ts b/src/api/providers/fetchers/bedrock.ts new file mode 100644 index 0000000000..ad1e80694b --- /dev/null +++ b/src/api/providers/fetchers/bedrock.ts @@ -0,0 +1,75 @@ +import { BedrockClient, ListFoundationModelsCommand, paginateListInferenceProfiles } from "@aws-sdk/client-bedrock" +import { fromIni } from "@aws-sdk/credential-providers" +import { NodeHttpHandler } from "@smithy/node-http-handler" +import { HttpsProxyAgent } from "https-proxy-agent" +import { HttpProxyAgent } from "http-proxy-agent" +import type { BedrockCatalogEntry, ProviderSettings } from "@roo-code/types" +import { getSystemProxyUrl } from "../../../utils/networkProxy" + +/** Query the regional control plane, not a custom inference/runtime endpoint. */ +export async function getBedrockCatalog( + options: ProviderSettings, + abortSignal?: AbortSignal, +): Promise { + if (!options.awsRegion) throw new Error("Select an AWS region before refreshing the catalogue.") + if (options.awsUseApiKey) + throw new Error("Catalogue discovery requires AWS IAM credentials or an AWS profile, not a Bedrock API key.") + if (options.awsUseProfile && !options.awsProfile?.trim()) + throw new Error("Select an AWS profile before refreshing the catalogue.") + const proxy = getSystemProxyUrl() + const client = new BedrockClient({ + region: options.awsRegion, + maxAttempts: 2, + credentials: + options.awsUseProfile && options.awsProfile + ? fromIni({ profile: options.awsProfile, ignoreCache: true }) + : options.awsAccessKey && options.awsSecretKey + ? { + accessKeyId: options.awsAccessKey, + secretAccessKey: options.awsSecretKey, + sessionToken: options.awsSessionToken, + } + : undefined, + requestHandler: new NodeHttpHandler({ + requestTimeout: 30_000, + ...(proxy ? { httpAgent: new HttpProxyAgent(proxy), httpsAgent: new HttpsProxyAgent(proxy) } : {}), + }), + }) + const timeout = AbortSignal.timeout(30_000) + const signal = abortSignal ? AbortSignal.any([abortSignal, timeout]) : timeout + try { + signal.throwIfAborted() + const result: BedrockCatalogEntry[] = [] + const foundations = await client.send( + new ListFoundationModelsCommand({ byOutputModality: "TEXT", byInferenceType: "ON_DEMAND" }), + { abortSignal: signal }, + ) + for (const model of foundations.modelSummaries ?? []) { + if (model.modelArn && model.responseStreamingSupported && model.modelLifecycle?.status !== "LEGACY") { + result.push({ + arn: model.modelArn, + name: model.modelName ?? model.modelId ?? model.modelArn, + kind: "regional", + }) + } + } + for await (const page of paginateListInferenceProfiles({ client }, {}, { abortSignal: signal })) { + for (const profile of page.inferenceProfileSummaries ?? []) { + if (!profile.inferenceProfileArn || profile.status !== "ACTIVE") continue + result.push({ + arn: profile.inferenceProfileArn, + name: profile.inferenceProfileName ?? profile.inferenceProfileId ?? profile.inferenceProfileArn, + kind: + profile.type === "APPLICATION" + ? "application" + : profile.inferenceProfileId?.startsWith("global.") + ? "global" + : "geographic", + }) + } + } + return result.sort((a, b) => a.name.localeCompare(b.name)) + } finally { + client.destroy() + } +} diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 185cfbb427..31f7bd4264 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -198,6 +198,7 @@ export class ClineProvider private static activeInstances: Set = new Set() private disposables: vscode.Disposable[] = [] private webviewDisposables: vscode.Disposable[] = [] + public readonly bedrockCatalogRequests = new Map() private pendingThemeFixtureProbes = new Map< string, { @@ -779,6 +780,10 @@ export class ClineProvider - https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts */ private clearWebviewResources() { + for (const controller of this.bedrockCatalogRequests.values()) { + controller.abort() + } + this.bedrockCatalogRequests.clear() this.rejectPendingThemeFixtureProbes(new Error("Webview was disposed before the theme fixture probe completed")) while (this.webviewDisposables.length) { const x = this.webviewDisposables.pop() diff --git a/src/core/webview/__tests__/ClineProvider.spec.ts b/src/core/webview/__tests__/ClineProvider.spec.ts index 72aa1bcb76..41312a838c 100644 --- a/src/core/webview/__tests__/ClineProvider.spec.ts +++ b/src/core/webview/__tests__/ClineProvider.spec.ts @@ -839,6 +839,19 @@ describe("ClineProvider", () => { }) }) + test("aborts and forgets pending Bedrock discovery when webview resources are cleared", () => { + const first = new AbortController() + const second = new AbortController() + provider.bedrockCatalogRequests.set("first", first) + provider.bedrockCatalogRequests.set("second", second) + + provider["clearWebviewResources"]() + + expect(first.signal.aborted).toBe(true) + expect(second.signal.aborted).toBe(true) + expect(provider.bedrockCatalogRequests.size).toBe(0) + }) + test("postStateToWebview does not force action navigation for non-compliant MDM state", async () => { const mdmService = { requiresCloudAuth: vi.fn().mockReturnValue(true), diff --git a/src/core/webview/__tests__/webviewMessageHandler.routerModels.spec.ts b/src/core/webview/__tests__/webviewMessageHandler.routerModels.spec.ts index 5a4b3e7be3..7c8c1140e7 100644 --- a/src/core/webview/__tests__/webviewMessageHandler.routerModels.spec.ts +++ b/src/core/webview/__tests__/webviewMessageHandler.routerModels.spec.ts @@ -5,6 +5,7 @@ import { providerIdentifiers, retiredProviderIdentifiers, RouterModelsMessageType, + BedrockModelsMessageType, } from "@roo-code/types" import { webviewMessageHandler } from "../webviewMessageHandler" @@ -57,6 +58,10 @@ vi.mock("vscode", () => ({ // Mock modelCache getModels/flushModels used by the handler const getModelsMock = vi.fn() const flushModelsMock = vi.fn() +const getBedrockCatalogMock = vi.fn() +vi.mock("../../../api/providers/fetchers/bedrock", () => ({ + getBedrockCatalog: (...args: unknown[]) => getBedrockCatalogMock(...args), +})) vi.mock("../../../api/providers/fetchers/modelCache", () => ({ getModels: (...args: any[]) => getModelsMock(...args), flushModels: (...args: any[]) => flushModelsMock(...args), @@ -75,6 +80,7 @@ describe("webviewMessageHandler - requestRouterModels provider filter", () => { mockProvider = { // Only methods used by this code path + bedrockCatalogRequests: new Map(), postMessageToWebview: vi.fn(), getState: vi.fn().mockResolvedValue({ apiConfiguration: {} }), contextProxy: { @@ -102,6 +108,88 @@ describe("webviewMessageHandler - requestRouterModels provider filter", () => { }) }) + it("discovers Bedrock models using unsaved settings and correlates the response without persisting credentials", async () => { + const apiConfiguration = { awsRegion: "eu-west-3", awsAccessKey: "draft-key", awsSecretKey: "draft-secret" } + const models = [{ arn: "profile", name: "EU model", kind: "geographic" }] + getBedrockCatalogMock.mockResolvedValue(models) + await webviewMessageHandler(mockProvider, { + type: BedrockModelsMessageType.requestBedrockModels, + requestId: "draft", + apiConfiguration, + }) + expect(getBedrockCatalogMock).toHaveBeenCalledWith(apiConfiguration, expect.any(AbortSignal)) + expect(mockProvider.bedrockCatalogRequests.size).toBe(0) + expect(mockProvider.contextProxy.setValue).not.toHaveBeenCalled() + expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith({ + type: BedrockModelsMessageType.bedrockModels, + requestId: "draft", + bedrockModels: models, + }) + }) + + it("logs the underlying Bedrock discovery failure for diagnosis", async () => { + const error = new Error("User is not authorized to perform: bedrock:ListInferenceProfiles") + error.name = "AccessDeniedException" + getBedrockCatalogMock.mockRejectedValue(error) + await webviewMessageHandler(mockProvider, { + type: BedrockModelsMessageType.requestBedrockModels, + requestId: "denied", + }) + expect(mockProvider.log).toHaveBeenCalledWith( + expect.stringContaining( + "AccessDeniedException: User is not authorized to perform: bedrock:ListInferenceProfiles", + ), + ) + }) + + it("returns a correlated, sanitized Bedrock discovery failure", async () => { + getBedrockCatalogMock.mockRejectedValue(new Error("secret credential detail")) + await webviewMessageHandler(mockProvider, { + type: BedrockModelsMessageType.requestBedrockModels, + requestId: "failed", + }) + expect(mockProvider.postMessageToWebview).toHaveBeenCalledWith( + expect.objectContaining({ + type: BedrockModelsMessageType.bedrockModels, + requestId: "failed", + error: expect.stringContaining("listing"), + }), + ) + expect(JSON.stringify(mockProvider.postMessageToWebview.mock.calls)).not.toContain("secret credential detail") + expect(mockProvider.bedrockCatalogRequests.size).toBe(0) + }) + + it.each(["success", "failure"])("cancels only the correlated discovery and suppresses late %s", async (outcome) => { + let finish!: () => void + getBedrockCatalogMock.mockImplementationOnce( + () => + new Promise((resolve, reject) => { + finish = () => (outcome === "success" ? resolve([]) : reject(new Error("aborted discovery"))) + }), + ) + const other = new AbortController() + mockProvider.bedrockCatalogRequests.set("other", other) + const pending = webviewMessageHandler(mockProvider, { + type: BedrockModelsMessageType.requestBedrockModels, + requestId: "cancelled", + }) + await vi.waitFor(() => expect(getBedrockCatalogMock).toHaveBeenCalledOnce()) + const signal: AbortSignal = getBedrockCatalogMock.mock.calls[0][1] + expect(signal.aborted).toBe(false) + await webviewMessageHandler(mockProvider, { + type: BedrockModelsMessageType.cancelBedrockModels, + requestId: "cancelled", + }) + expect(signal.aborted).toBe(true) + expect(other.signal.aborted).toBe(false) + expect(mockProvider.bedrockCatalogRequests.has("cancelled")).toBe(false) + finish() + await pending + expect(mockProvider.postMessageToWebview).not.toHaveBeenCalled() + expect(mockProvider.log).not.toHaveBeenCalled() + expect(mockProvider.bedrockCatalogRequests.get("other")).toBe(other) + }) + it("returns explicit removal error for requestRooModels", async () => { await webviewMessageHandler(mockProvider as any, { type: "requestRooModels" } as any) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index a41fda1ce1..bebf24b24f 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -27,6 +27,7 @@ import { LmStudioModelsMessageType, OllamaModelsMessageType, OpenAiModelsMessageType, + BedrockModelsMessageType, RouterModelsMessageType, VsCodeLmModelsMessageType, isTelemetryOptedIn, @@ -40,6 +41,7 @@ import { saveTaskMessages } from "../task-persistence" import { importRooTaskHistory } from "../task-persistence/importRooTaskHistory" import { ClineProvider } from "./ClineProvider" +import { getBedrockCatalog } from "../../api/providers/fetchers/bedrock" import { handleCheckpointRestoreOperation } from "./checkpointRestoreHandler" import { generateErrorDiagnostics } from "./diagnosticsHandler" import { @@ -1106,6 +1108,44 @@ export const webviewMessageHandler = async ( // For providers that need credentials, use their specific handlers await flushModels({ provider: routerNameFlush } as GetModelsOptions, true) break + case BedrockModelsMessageType.cancelBedrockModels: + if (message.requestId) { + provider.bedrockCatalogRequests.get(message.requestId)?.abort() + provider.bedrockCatalogRequests.delete(message.requestId) + } + break + case BedrockModelsMessageType.requestBedrockModels: { + const { requestId } = message + if (!requestId) break + provider.bedrockCatalogRequests.get(requestId)?.abort() + const controller = new AbortController() + provider.bedrockCatalogRequests.set(requestId, controller) + try { + const bedrockModels = await getBedrockCatalog(message.apiConfiguration ?? {}, controller.signal) + if (controller.signal.aborted) break + await provider.postMessageToWebview({ + type: BedrockModelsMessageType.bedrockModels, + requestId: message.requestId, + bedrockModels, + }) + } catch (error) { + if (controller.signal.aborted) break + // Keep the underlying cause (IAM denial, network, wrong region) diagnosable in the output channel. + provider.log( + `Bedrock catalogue discovery failed: ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`, + ) + await provider.postMessageToWebview({ + type: BedrockModelsMessageType.bedrockModels, + requestId: message.requestId, + error: "Could not load the regional catalogue. Check IAM credentials/profile, network access, and listing permissions (bedrock:ListFoundationModels / bedrock:ListInferenceProfiles). This discovery flow requires IAM authentication.", + }) + } finally { + if (provider.bedrockCatalogRequests.get(requestId) === controller) { + provider.bedrockCatalogRequests.delete(requestId) + } + } + break + } case RouterModelsMessageType.requestRouterModels: { const { apiConfiguration } = await provider.getState() diff --git a/src/eslint-suppressions.json b/src/eslint-suppressions.json index 24db0bf433..479fd50e3c 100644 --- a/src/eslint-suppressions.json +++ b/src/eslint-suppressions.json @@ -296,7 +296,7 @@ }, "api/providers/bedrock.ts": { "@typescript-eslint/no-explicit-any": { - "count": 34 + "count": 33 } }, "api/providers/deepseek.ts": { diff --git a/src/package.json b/src/package.json index c142e02abb..eb7ff2068b 100644 --- a/src/package.json +++ b/src/package.json @@ -475,9 +475,9 @@ "dependencies": { "@anthropic-ai/sdk": "^0.109.0", "@anthropic-ai/vertex-sdk": "^0.19.0", + "@aws-sdk/client-bedrock": "^3.922.0", "@aws-sdk/client-bedrock-runtime": "^3.922.0", "@aws-sdk/credential-providers": "^3.922.0", - "@smithy/node-http-handler": "^4.0.0", "@google/genai": "^1.29.1", "@lmstudio/sdk": "^1.1.1", "@mistralai/mistralai": "^1.9.18", @@ -488,6 +488,7 @@ "@roo-code/ipc": "workspace:^", "@roo-code/telemetry": "workspace:^", "@roo-code/types": "workspace:^", + "@smithy/node-http-handler": "^4.0.0", "ai-sdk-provider-poe": "2.0.18", "async-mutex": "^0.5.0", "axios": "^1.18.1", @@ -503,9 +504,9 @@ "get-folder-size": "^5.0.0", "global-agent": "^3.0.0", "google-auth-library": "^10.2.0", + "gray-matter": "^4.0.3", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", - "gray-matter": "^4.0.3", "i18next": "^25.0.0", "ignore": "^7.0.3", "isbinaryfile": "^5.0.7", diff --git a/webview-ui/playwright/gallery/stories.tsx b/webview-ui/playwright/gallery/stories.tsx index 7b42e9780d..4f5c976a53 100644 --- a/webview-ui/playwright/gallery/stories.tsx +++ b/webview-ui/playwright/gallery/stories.tsx @@ -12,6 +12,39 @@ const mermaidGantt = `gantt Ship release :active, release, after scope, 3d` export const stories: Record = { + "bedrock-routing": async ({ global = false }) => { + const [{ AppProviders }, { Bedrock }, { providerIdentifiers }] = await Promise.all([ + import("../AppProviders"), + import("@/components/settings/providers/Bedrock"), + import("@roo-code/types"), + ]) + function Routing() { + const [config, setConfig] = useState({ + apiProvider: providerIdentifiers.bedrock, + apiModelId: "anthropic.claude-sonnet-4-5-20250929-v1:0", + awsRegion: "eu-west-3", + awsUseProfile: true, + awsProfile: "default", + awsUseCrossRegionInference: true, + awsUseGlobalInference: global === true, + }) + return ( +
+ + setConfig((previous) => ({ ...previous, [field]: value })) + } + /> +
+ ) + } + return ( + + + + ) + }, "bedrock-output-budget": async ({ model = "anthropic.claude-opus-5" }) => { const [{ AppProviders }, { ThinkingBudget }, { bedrockModels, providerIdentifiers }] = await Promise.all([ import("../AppProviders"), diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 714b507774..4b39f9a14b 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -542,6 +542,7 @@ const ApiOptions = ({ apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} selectedModelInfo={selectedModelInfo} + organizationAllowList={organizationAllowList} simplifySettings={fromWelcomeView} /> )} diff --git a/webview-ui/src/components/settings/providers/Bedrock.tsx b/webview-ui/src/components/settings/providers/Bedrock.tsx index ed554f126d..d0c52b9373 100644 --- a/webview-ui/src/components/settings/providers/Bedrock.tsx +++ b/webview-ui/src/components/settings/providers/Bedrock.tsx @@ -6,6 +6,10 @@ import { type ProviderSettings, type ModelInfo, type BedrockServiceTier, + type OrganizationAllowList, + providerIdentifiers, + getBedrockInferenceModelId, + bedrockDefaultModelId, BEDROCK_REGIONS, BEDROCK_1M_CONTEXT_MODEL_IDS, BEDROCK_GLOBAL_INFERENCE_MODEL_IDS, @@ -16,17 +20,43 @@ import { useAppTranslation } from "@src/i18n/TranslationContext" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StandardTooltip } from "@src/components/ui" import { inputEventTransform, noTransform } from "../transforms" +import { BedrockCatalog } from "./BedrockCatalog" type BedrockProps = { apiConfiguration: ProviderSettings setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void selectedModelInfo?: ModelInfo + organizationAllowList?: OrganizationAllowList simplifySettings?: boolean } -export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedModelInfo }: BedrockProps) => { +export const Bedrock = ({ + apiConfiguration, + setApiConfigurationField, + selectedModelInfo, + organizationAllowList, +}: BedrockProps) => { const { t } = useAppTranslation() const [awsEndpointSelected, setAwsEndpointSelected] = useState(!!apiConfiguration?.awsBedrockEndpointEnabled) + const allowCatalog = + !organizationAllowList || + organizationAllowList.allowAll || + organizationAllowList.providers[providerIdentifiers.bedrock]?.allowAll + const customArn = apiConfiguration.awsCustomArn + // The "custom-arn" sentinel is not a request model ID; show a placeholder until an ARN is entered. + const awaitingArn = !customArn && apiConfiguration.apiModelId === "custom-arn" + const requestModelId = awaitingArn + ? "—" + : customArn + ? customArn.includes(":foundation-model/") + ? getBedrockInferenceModelId(customArn.split(":foundation-model/")[1]) + : customArn + : getBedrockInferenceModelId( + apiConfiguration.apiModelId || bedrockDefaultModelId, + apiConfiguration.awsRegion, + apiConfiguration.awsUseCrossRegionInference, + apiConfiguration.awsUseGlobalInference, + ) // Check if the selected model supports 1M context (supported Claude 4 models) const supports1MContextBeta = @@ -179,22 +209,51 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo )} {supportsGlobalInference && ( +
+ { + // Global Inference takes priority over cross-region when both are enabled + setApiConfigurationField("awsUseGlobalInference", checked) + }}> + {t("settings:providers.awsGlobalInference")} + +
+ {t("settings:providers.awsGlobalInferenceDescription")} +
+
+ )} +
{ - // Global Inference takes priority over cross-region when both are enabled - setApiConfigurationField("awsUseGlobalInference", checked) + setApiConfigurationField("awsUseCrossRegionInference", checked) }}> - {t("settings:providers.awsGlobalInference")} + {t("settings:providers.awsCrossRegion")} +
+ {t("settings:providers.awsCrossRegionDescription")} +
+
+
+ {t("settings:providers.awsRequestModelId")} {requestModelId} +
+
+ {t( + customArn || awaitingArn + ? "settings:providers.awsArnRouting" + : "settings:providers.awsBundledModels", + )} +
+ {allowCatalog && ( + { + setApiConfigurationField("apiModelId", "custom-arn") + setApiConfigurationField("awsCustomArn", arn) + }} + /> )} - { - setApiConfigurationField("awsUseCrossRegionInference", checked) - }}> - {t("settings:providers.awsCrossRegion")} - {selectedModelInfo?.supportsPromptCache && ( <> void +}) { + const { t } = useAppTranslation() + const [models, setModels] = useState() + const [error, setError] = useState(false) + const [loading, setLoading] = useState(false) + const requestId = useRef() + const { awsRegion, awsAccessKey, awsSecretKey, awsSessionToken, awsProfile, awsUseProfile, awsUseApiKey } = + apiConfiguration + useEffect(() => { + requestId.current = undefined + setModels(undefined) + setLoading(false) + setError(false) + const listener = ({ data }: MessageEvent) => { + if ( + data.type !== BedrockModelsMessageType.bedrockModels || + !requestId.current || + data.requestId !== requestId.current + ) + return + requestId.current = undefined + setLoading(false) + setError(!!data.error) + setModels(data.error ? undefined : (data.bedrockModels ?? [])) + } + window.addEventListener("message", listener) + return () => { + if (requestId.current) { + vscode.postMessage({ + type: BedrockModelsMessageType.cancelBedrockModels, + requestId: requestId.current, + }) + } + requestId.current = undefined + window.removeEventListener("message", listener) + } + }, [awsRegion, awsAccessKey, awsSecretKey, awsSessionToken, awsProfile, awsUseProfile, awsUseApiKey]) + return ( +
+ +
+ {t("settings:providers.awsCatalogDescription")} +
+ {error && ( +
+ {t("settings:providers.awsCatalogError")} +
+ )} + {models && ( + <> + + {/* Empty value keeps the placeholder visible: picking an entry fills the custom ARN field. */} + + + )} +
+ ) +} diff --git a/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx index b827024859..0e04724a71 100644 --- a/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx +++ b/webview-ui/src/components/settings/providers/__tests__/Bedrock.spec.tsx @@ -1,7 +1,10 @@ import React from "react" -import { render, screen, fireEvent } from "@/utils/test-utils" +import { act, render, screen, fireEvent } from "@/utils/test-utils" import { Bedrock } from "../Bedrock" -import { ProviderSettings } from "@roo-code/types" +import { BedrockModelsMessageType, type ProviderSettings, type OrganizationAllowList } from "@roo-code/types" +import { vscode } from "@/utils/vscode" + +vi.mock("@/utils/vscode", () => ({ vscode: { postMessage: vi.fn() } })) // Mock the vscrui Checkbox component vi.mock("vscrui", () => ({ @@ -85,6 +88,130 @@ describe("Bedrock Component", () => { vi.clearAllMocks() }) + it.each([ + ["arn:aws:bedrock:eu-west-3::foundation-model/amazon.nova-pro-v1:0", "amazon.nova-pro-v1:0"], + [ + "arn:aws:bedrock:eu-west-3:123456789012:application-inference-profile/custom", + "arn:aws:bedrock:eu-west-3:123456789012:application-inference-profile/custom", + ], + ])("previews custom ARN routing without applying checkbox prefixes: %s", (awsCustomArn, expected) => { + render( + , + ) + expect(screen.getByTestId("bedrock-request-model").querySelector("code")?.textContent).toBe(expected) + expect(screen.getByText("settings:providers.awsArnRouting")).toBeInTheDocument() + expect(mockSetApiConfigurationField).not.toHaveBeenCalled() + }) + + it("shows a placeholder and ARN guidance while the custom ARN is still empty", () => { + render( + , + ) + expect(screen.getByTestId("bedrock-request-model").querySelector("code")?.textContent).toBe("—") + expect(screen.getByText("settings:providers.awsArnRouting")).toBeInTheDocument() + }) + + it.each<[OrganizationAllowList, boolean]>([ + [{ allowAll: true, providers: {} }, true], + [{ allowAll: false, providers: { bedrock: { allowAll: true } } }, true], + [{ allowAll: false, providers: { bedrock: { allowAll: false, models: ["amazon.nova-pro-v1:0"] } } }, false], + [{ allowAll: false, providers: {} }, false], + ])("respects organization catalogue restrictions: %j", (organizationAllowList, visible) => { + render( + , + ) + expect(!!screen.queryByRole("button", { name: "settings:providers.awsCatalogRefresh" })).toBe(visible) + }) + + it("selects a discovered ARN into the edit buffer without saving or enabling routing", () => { + render( + , + ) + fireEvent.click(screen.getByRole("button", { name: "settings:providers.awsCatalogRefresh" })) + const request = vi.mocked(vscode.postMessage).mock.calls[0][0] + const arn = "arn:aws:bedrock:eu-west-3:123456789012:inference-profile/eu.model" + act(() => + window.dispatchEvent( + new MessageEvent("message", { + data: { + type: BedrockModelsMessageType.bedrockModels, + requestId: request.requestId, + bedrockModels: [{ arn, name: "Discovered", kind: "geographic" }], + }, + }), + ), + ) + const catalogSelect = screen.getByRole("option", { name: /Discovered/ }).closest("select")! + fireEvent.change(catalogSelect, { target: { value: arn } }) + expect(mockSetApiConfigurationField.mock.calls).toEqual([ + ["apiModelId", "custom-arn"], + ["awsCustomArn", arn], + ]) + expect(vscode.postMessage).toHaveBeenCalledTimes(1) + }) + + it.each(["awsGlobalInference", "awsCrossRegion"])("buffers explicit routing opt-in: %s", (label) => { + render( + , + ) + fireEvent.click(screen.getByLabelText(`settings:providers.${label}`)) + expect(mockSetApiConfigurationField).toHaveBeenCalledWith( + label === "awsGlobalInference" ? "awsUseGlobalInference" : "awsUseCrossRegionInference", + true, + ) + expect(vscode.postMessage).not.toHaveBeenCalled() + }) + + it.each([ + [false, false, "anthropic.claude-sonnet-4-5-20250929-v1:0"], + [true, false, "eu.anthropic.claude-sonnet-4-5-20250929-v1:0"], + [true, true, "global.anthropic.claude-sonnet-4-5-20250929-v1:0"], + ] as const)( + "previews the effective model for cross-region=%s global=%s", + (awsUseCrossRegionInference, awsUseGlobalInference, expected) => { + render( + , + ) + expect(screen.getByTestId("bedrock-request-model")).toHaveTextContent(expected) + expect(screen.getByText("settings:providers.awsGlobalInferenceDescription")).toBeInTheDocument() + expect(screen.getByText("settings:providers.awsCrossRegionDescription")).toBeInTheDocument() + expect(mockSetApiConfigurationField).not.toHaveBeenCalled() + }, + ) + it("should show text field when VPC endpoint checkbox is checked", () => { // Initial render with checkbox unchecked const apiConfiguration: Partial = { diff --git a/webview-ui/src/components/settings/providers/__tests__/Bedrock.visual.tsx b/webview-ui/src/components/settings/providers/__tests__/Bedrock.visual.tsx new file mode 100644 index 0000000000..e9697c7620 --- /dev/null +++ b/webview-ui/src/components/settings/providers/__tests__/Bedrock.visual.tsx @@ -0,0 +1,32 @@ +import { expect, test } from "../../../../../playwright/coverage-fixture" +import { getCapturedVscodeMessages } from "../../../../../playwright/vscode-messages" +import { BedrockModelsMessageType } from "@roo-code/types" + +for (const global of [false, true]) { + test(`shows Bedrock routing scope and effective ID (global=${global})`, async ({ page }) => { + await page.goto("/") + await page.waitForFunction(() => typeof window.mount === "function") + await page.evaluate((global) => window.mount({ story: "bedrock-routing", props: { global } }), global) + await expect(page.locator("[data-playwright-mounted]")).toHaveScreenshot( + `bedrock-routing-${global ? "global" : "geo"}.png`, + ) + }) +} + +test("shows the inference profile caveat for discovered foundation models", async ({ page }) => { + await page.setViewportSize({ width: 520, height: 900 }) + await page.goto("/") + await page.waitForFunction(() => typeof window.mount === "function") + await page.evaluate(() => window.mount({ story: "bedrock-routing" })) + await page.getByRole("button", { name: "Refresh regional AWS catalogue" }).click() + const request = (await getCapturedVscodeMessages(page)).find( + (message) => message.type === BedrockModelsMessageType.requestBedrockModels, + ) + await page.evaluate((data) => window.dispatchEvent(new MessageEvent("message", { data })), { + type: BedrockModelsMessageType.bedrockModels, + requestId: request?.requestId, + bedrockModels: [{ arn: "foundation", name: "Example model", kind: "regional" }], + }) + await page.locator("#bedrock-catalog").click() + await expect(page.getByRole("listbox")).toHaveScreenshot("bedrock-catalog-regional.png") +}) diff --git a/webview-ui/src/components/settings/providers/__tests__/BedrockCatalog.spec.tsx b/webview-ui/src/components/settings/providers/__tests__/BedrockCatalog.spec.tsx new file mode 100644 index 0000000000..ce08ef250b --- /dev/null +++ b/webview-ui/src/components/settings/providers/__tests__/BedrockCatalog.spec.tsx @@ -0,0 +1,179 @@ +import { act, fireEvent, render, screen } from "@/utils/test-utils" +import { BedrockModelsMessageType, type ProviderSettings } from "@roo-code/types" +import { vscode } from "@/utils/vscode" +import { BedrockCatalog } from "../BedrockCatalog" + +vi.mock("@/utils/vscode", () => ({ vscode: { postMessage: vi.fn() } })) +// Radix Select needs pointer APIs JSDOM lacks; a native select keeps the value wiring testable. +vi.mock("@src/components/ui", () => ({ + Select: ({ + children, + value, + onValueChange, + disabled, + }: { + children: React.ReactNode + value: string + onValueChange: (value: string) => void + disabled?: boolean + }) => ( + + ), + SelectContent: ({ children }: { children: React.ReactNode }) => <>{children}, + SelectItem: ({ children, value }: { children: React.ReactNode; value: string }) => ( + + ), + SelectTrigger: ({ children }: { children: React.ReactNode }) => , + SelectValue: ({ placeholder }: { placeholder: string }) => <>{placeholder}, +})) + +beforeEach(() => vi.clearAllMocks()) + +it.each([{}, { awsRegion: "eu-west-3", awsUseApiKey: true }])( + "does not request discovery for unavailable authentication or region: %j", + (apiConfiguration) => { + render() + expect(screen.getByRole("button")).toBeDisabled() + fireEvent.click(screen.getByRole("button")) + expect(vscode.postMessage).not.toHaveBeenCalled() + }, +) + +it("disables refresh while loading and renders an unselectable empty successful result", () => { + const { unmount } = render() + fireEvent.click(screen.getByRole("button")) + const request = vi.mocked(vscode.postMessage).mock.calls[0][0] + expect(screen.getByRole("button")).toBeDisabled() + expect(screen.getByRole("button")).toHaveTextContent("settings:providers.awsCatalogLoading") + fireEvent.click(screen.getByRole("button")) + expect(vscode.postMessage).toHaveBeenCalledTimes(1) + act(() => + window.dispatchEvent( + new MessageEvent("message", { + data: { type: BedrockModelsMessageType.bedrockModels, requestId: request.requestId, bedrockModels: [] }, + }), + ), + ) + expect(screen.getByRole("button")).toBeEnabled() + expect(screen.getByRole("combobox")).toBeDisabled() + expect(screen.getByRole("combobox")).toHaveTextContent("settings:providers.awsCatalogEmpty") + expect(screen.queryByRole("alert")).not.toBeInTheDocument() + unmount() + expect(vscode.postMessage).toHaveBeenCalledTimes(1) +}) + +it("cancels pending discovery on unmount", () => { + const { unmount } = render() + fireEvent.click(screen.getByRole("button")) + const request = vi.mocked(vscode.postMessage).mock.calls[0][0] + unmount() + expect(vscode.postMessage).toHaveBeenLastCalledWith({ + type: BedrockModelsMessageType.cancelBedrockModels, + requestId: request.requestId, + }) +}) + +it.each([undefined, "", " "])("disables incomplete profile discovery (%j)", (awsProfile) => { + render( + , + ) + expect(screen.getByRole("button")).toBeDisabled() + fireEvent.click(screen.getByRole("button")) + expect(vscode.postMessage).not.toHaveBeenCalled() +}) + +it.each([true, false])("sends only credentials for the selected mode (profile=%s)", (awsUseProfile) => { + render( + , + ) + fireEvent.click(screen.getByRole("button")) + expect(vi.mocked(vscode.postMessage).mock.calls[0][0].apiConfiguration).toEqual({ + awsRegion: "eu-west-3", + awsUseProfile, + ...(awsUseProfile + ? { awsProfile: "work" } + : { awsAccessKey: "key", awsSecretKey: "secret", awsSessionToken: "session" }), + }) +}) + +it.each([{ awsRegion: "us-east-1" }, { awsProfile: "other" }])( + "ignores stale catalogue replies after connection settings change: %j", + (change) => { + const config: ProviderSettings = { awsRegion: "eu-west-3", awsUseProfile: true, awsProfile: "work" } + const onSelect = vi.fn() + const { rerender } = render() + fireEvent.click(screen.getByRole("button")) + const first = vi.mocked(vscode.postMessage).mock.calls[0][0] + expect(first.apiConfiguration).toEqual(expect.objectContaining(config)) + rerender() + expect(vscode.postMessage).toHaveBeenLastCalledWith({ + type: BedrockModelsMessageType.cancelBedrockModels, + requestId: first.requestId, + }) + act(() => + window.dispatchEvent( + new MessageEvent("message", { + data: { + type: BedrockModelsMessageType.bedrockModels, + requestId: first.requestId, + bedrockModels: [{ arn: "old", name: "Stale", kind: "global" }], + }, + }), + ), + ) + expect(screen.queryByRole("combobox")).not.toBeInTheDocument() + fireEvent.click(screen.getByRole("button")) + const second = vi.mocked(vscode.postMessage).mock.calls[2][0] + act(() => + window.dispatchEvent( + new MessageEvent("message", { + data: { + type: BedrockModelsMessageType.bedrockModels, + requestId: second.requestId, + bedrockModels: [{ arn: "new", name: "Current", kind: "geographic" }], + }, + }), + ), + ) + fireEvent.change(screen.getByRole("combobox"), { target: { value: "new" } }) + expect(onSelect).toHaveBeenCalledWith("new") + expect(vscode.postMessage).toHaveBeenCalledTimes(3) + }, +) + +it("shows discovery failures and allows retry without claiming availability", () => { + render() + fireEvent.click(screen.getByRole("button")) + const request = vi.mocked(vscode.postMessage).mock.calls[0][0] + act(() => + window.dispatchEvent( + new MessageEvent("message", { + data: { type: BedrockModelsMessageType.bedrockModels, requestId: request.requestId, error: "denied" }, + }), + ), + ) + expect(screen.getByRole("alert")).toBeInTheDocument() + expect(screen.getByRole("button")).toBeEnabled() + expect(screen.queryByRole("combobox")).not.toBeInTheDocument() +}) diff --git a/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-catalog-regional.png b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-catalog-regional.png new file mode 100644 index 0000000000..a5a0fc4109 Binary files /dev/null and b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-catalog-regional.png differ diff --git a/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-routing-geo.png b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-routing-geo.png new file mode 100644 index 0000000000..6b65f287e8 Binary files /dev/null and b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-routing-geo.png differ diff --git a/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-routing-global.png b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-routing-global.png new file mode 100644 index 0000000000..90d52959ab Binary files /dev/null and b/webview-ui/src/components/settings/providers/__tests__/__screenshots__/bedrock-routing-global.png differ diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 762a1290af..845109efc3 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "Clau secreta d'AWS", "awsSessionToken": "Token de sessió d'AWS", "awsRegion": "Regió d'AWS", - "awsCrossRegion": "Utilitzar inferència entre regions", - "awsGlobalInference": "Utilitzar la inferència global (selecció automàtica de la regió òptima d'AWS)", + "awsCrossRegion": "Utilitzar inferència geogràfica entre regions", + "awsGlobalInference": "Utilitzar inferència global (encaminament mundial)", + "awsCrossRegionDescription": "Utilitza un perfil d'inferència geogràfic (per exemple eu. o us.). El processament es manté dins la zona geogràfica del perfil, no necessàriament a la regió seleccionada. Alguns models, com Claude Sonnet 4.5, requereixen un perfil d'inferència. Comprova les regions d'origen i destinació admeses a AWS; un prefix generat no garanteix la disponibilitat.", + "awsGlobalInferenceDescription": "Permet el processament en regions comercials d'AWS de tot el món. Si les dues opcions estan activades, l'encaminament global té prioritat per als models compatibles. Desactiva aquesta opció si el processament ha de romandre en una zona geogràfica concreta.", + "awsRequestModelId": "ID del model / ARN de la sol·licitud:", + "awsArnRouting": "L'ARN seleccionat controla l'encaminament; aquestes caselles no el substitueixen. Els perfils d'aplicació hereten l'encaminament del model/perfil subjacent. Comprova les seves destinacions a AWS abans d'utilitzar-lo.", + "awsBundledModels": "El selector inferior és una llista inclosa sense verificar, no una comprovació de disponibilitat del compte/regió. Actualitza el catàleg regional per seleccionar un model AWS o perfil d'inferència per ARN. Cap opció d'encaminament s'activa automàticament.", + "awsCatalogRefresh": "Actualitzar el catàleg regional d'AWS", + "awsCatalogLoading": "S'està carregant el catàleg regional d'AWS…", + "awsCatalogDescription": "Consulta el pla de control d'AWS de la regió seleccionada amb les credencials/perfil IAM actuals (no un punt d'accés d'execució personalitzat). Requereix bedrock:ListFoundationModels i bedrock:ListInferenceProfiles; no admet claus API de Bedrock. Les entrades no garanteixen permís d'invocació ni compatibilitat amb eines. Les capacitats dels models desconeguts són estimades; verifica-les abans d'utilitzar-los. Seleccionar una entrada omple l'ARN personalitzat sense desar la configuració.", + "awsCatalogError": "No s'ha pogut carregar el catàleg. Comprova les credencials/perfil IAM, l'accés a la xarxa i els permisos de llistat. La llista inclosa i l'ARN personalitzat continuen disponibles.", + "awsCatalogSelect": "Models regionals i perfils d'inferència", + "awsCatalogEmpty": "No s'han retornat models de text en streaming ni perfils actius", + "awsCatalogKinds": { + "regional": "Model fundacional — sota demanda en aquesta regió; la invocació directa pot requerir un perfil d'inferència", + "geographic": "Perfil d'inferència geogràfic", + "global": "Perfil d'inferència global — mundial", + "application": "Perfil d'aplicació — comprova les destinacions a AWS" + }, "awsServiceTier": "Nivell de servei", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Rendiment i cost equilibrats", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index f56984bc73..366eb57d0b 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS Geheimschlüssel", "awsSessionToken": "AWS Sitzungstoken", "awsRegion": "AWS Region", - "awsCrossRegion": "Regionsübergreifende Inferenz verwenden", - "awsGlobalInference": "Globale Inferenz verwenden (optimale AWS-Region automatisch auswählen)", + "awsCrossRegion": "Geografische regionsübergreifende Inferenz verwenden", + "awsGlobalInference": "Globale Inferenz verwenden (weltweites Routing)", + "awsCrossRegionDescription": "Verwendet ein geografisches Inferenzprofil (z. B. eu. oder us.). Die Verarbeitung bleibt innerhalb dessen geografischen Gebiets, nicht unbedingt in der ausgewählten Region. Einige Modelle, darunter Claude Sonnet 4.5, benötigen ein Inferenzprofil. Prüfe die unterstützten Quell- und Zielregionen in AWS; ein erzeugtes Präfix garantiert keine Verfügbarkeit.", + "awsGlobalInferenceDescription": "Erlaubt die Verarbeitung in kommerziellen AWS-Regionen weltweit. Sind beide Optionen aktiviert, hat globales Routing für unterstützte Modelle Vorrang. Deaktiviere dies, wenn die Verarbeitung auf ein bestimmtes geografisches Gebiet beschränkt bleiben muss.", + "awsRequestModelId": "Modell-ID / ARN der Anfrage:", + "awsArnRouting": "Die ausgewählte ARN bestimmt das Routing; diese Kontrollkästchen überschreiben es nicht. Anwendungsprofile übernehmen das Routing ihres zugrunde liegenden Modells/Profils. Prüfe dessen Zielregionen vor der Verwendung in AWS.", + "awsBundledModels": "Die Modellauswahl unten ist eine mitgelieferte, ungeprüfte Liste, keine Verfügbarkeitsprüfung für Konto/Region. Aktualisiere den regionalen Katalog, um ein AWS-Modell oder Inferenzprofil per ARN auszuwählen. Keine Routingoption wird automatisch aktiviert.", + "awsCatalogRefresh": "Regionalen AWS-Katalog aktualisieren", + "awsCatalogLoading": "Regionaler AWS-Katalog wird geladen…", + "awsCatalogDescription": "Fragt die AWS-Steuerungsebene der ausgewählten Region mit deinen aktuellen IAM-Zugangsdaten/deinem Profil ab (nicht über einen benutzerdefinierten Laufzeitendpunkt). Erfordert bedrock:ListFoundationModels und bedrock:ListInferenceProfiles; Bedrock-API-Schlüssel werden nicht unterstützt. Einträge garantieren weder Aufrufberechtigung noch Werkzeugunterstützung. Unbekannte Modelle verwenden geschätzte Fähigkeiten; prüfe diese vor der Verwendung. Die Auswahl füllt die benutzerdefinierte ARN aus, ohne Einstellungen zu speichern.", + "awsCatalogError": "Der Katalog konnte nicht geladen werden. Prüfe IAM-Zugangsdaten/Profil, Netzwerkzugriff und Auflistungsberechtigungen. Die mitgelieferte Liste und die benutzerdefinierte ARN bleiben verfügbar.", + "awsCatalogSelect": "Regionale Modelle und Inferenzprofile", + "awsCatalogEmpty": "Keine Textmodelle mit Streaming oder aktive Profile zurückgegeben", + "awsCatalogKinds": { + "regional": "Basismodell — On-Demand in dieser Region; direkte Aufrufe können ein Inferenzprofil erfordern", + "geographic": "Geografisches Inferenzprofil", + "global": "Globales Inferenzprofil — weltweit", + "application": "Anwendungsprofil — Zielregionen in AWS prüfen" + }, "awsServiceTier": "Service-Stufe", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Ausgewogene Leistung und Kosten", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 77f8a4f86d..12525a8e79 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -630,8 +630,25 @@ "awsSecretKey": "AWS Secret Key", "awsSessionToken": "AWS Session Token", "awsRegion": "AWS Region", - "awsCrossRegion": "Use cross-region inference", - "awsGlobalInference": "Use Global inference (auto-select optimal AWS Region)", + "awsCrossRegion": "Use geographic cross-region inference", + "awsGlobalInference": "Use global inference (worldwide routing)", + "awsCrossRegionDescription": "Uses a geographic inference profile (for example eu. or us.). Processing stays within the profile’s geography, not necessarily the selected region. Some models, including Claude Sonnet 4.5, require an inference profile. Check the profile’s supported source and destination regions in AWS; a generated prefix does not guarantee availability.", + "awsGlobalInferenceDescription": "Allows processing in commercial AWS regions worldwide. When both options are enabled, global routing takes precedence for supported models. Leave this off if processing must stay within a specific geography.", + "awsRequestModelId": "Request model ID / ARN:", + "awsArnRouting": "The selected ARN controls routing; these checkboxes do not override it. Application profiles inherit the routing of their underlying model/profile. Check its destinations in AWS before use.", + "awsBundledModels": "The model picker below is a bundled, unverified list, not an account/region availability check. Refresh the regional catalogue to select an AWS model or inference profile by ARN. No routing option is enabled automatically.", + "awsCatalogRefresh": "Refresh regional AWS catalogue", + "awsCatalogLoading": "Loading regional AWS catalogue…", + "awsCatalogDescription": "Queries the selected region’s AWS control plane using your current IAM credentials/profile (not a custom runtime endpoint). Requires bedrock:ListFoundationModels and bedrock:ListInferenceProfiles; Bedrock API keys are not supported. Listings do not guarantee invocation permission or tool support. Unknown models use estimated capabilities; verify them before use. Selecting an entry fills the custom ARN without saving settings.", + "awsCatalogError": "Could not load the catalogue. Check IAM credentials/profile, network access, and listing permissions. The bundled list and custom ARN remain available.", + "awsCatalogSelect": "Regional models and inference profiles", + "awsCatalogEmpty": "No streaming text models or active profiles returned", + "awsCatalogKinds": { + "regional": "Foundation model — on-demand in this region; direct invocation may require an inference profile", + "geographic": "Geographic inference profile", + "global": "Global inference profile — worldwide", + "application": "Application profile — check destinations in AWS" + }, "awsServiceTier": "Service Tier", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Balanced performance and cost", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index e730483fa0..a76a92fdd4 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "Clave secreta de AWS", "awsSessionToken": "Token de sesión de AWS", "awsRegion": "Región de AWS", - "awsCrossRegion": "Usar inferencia entre regiones", - "awsGlobalInference": "Usar inferencia global (selección automática de la región óptima de AWS)", + "awsCrossRegion": "Usar inferencia geográfica entre regiones", + "awsGlobalInference": "Usar inferencia global (enrutamiento mundial)", + "awsCrossRegionDescription": "Utiliza un perfil de inferencia geográfico (por ejemplo, eu. o us.). El procesamiento permanece en la zona geográfica del perfil, no necesariamente en la región seleccionada. Algunos modelos, como Claude Sonnet 4.5, requieren un perfil de inferencia. Comprueba sus regiones de origen y destino admitidas en AWS; un prefijo generado no garantiza la disponibilidad.", + "awsGlobalInferenceDescription": "Permite el procesamiento en regiones comerciales de AWS de todo el mundo. Si ambas opciones están activadas, el enrutamiento global tiene prioridad para los modelos compatibles. Desactiva esta opción si el procesamiento debe permanecer en una zona geográfica específica.", + "awsRequestModelId": "ID del modelo / ARN de la solicitud:", + "awsArnRouting": "El ARN seleccionado controla el enrutamiento; estas casillas no lo anulan. Los perfiles de aplicación heredan el enrutamiento de su modelo/perfil subyacente. Comprueba sus destinos en AWS antes de usarlo.", + "awsBundledModels": "El selector de modelos inferior es una lista incluida sin verificar, no una comprobación de disponibilidad de la cuenta/región. Actualiza el catálogo regional para seleccionar un modelo de AWS o perfil de inferencia por ARN. Ninguna opción de enrutamiento se activa automáticamente.", + "awsCatalogRefresh": "Actualizar catálogo regional de AWS", + "awsCatalogLoading": "Cargando catálogo regional de AWS…", + "awsCatalogDescription": "Consulta el plano de control de AWS de la región seleccionada con tus credenciales/perfil IAM actuales (no un punto de conexión de ejecución personalizado). Requiere bedrock:ListFoundationModels y bedrock:ListInferenceProfiles; no admite claves API de Bedrock. Las entradas no garantizan permisos de invocación ni compatibilidad con herramientas. Las capacidades de modelos desconocidos son estimadas; verifícalas antes de usarlos. Seleccionar una entrada rellena el ARN personalizado sin guardar los ajustes.", + "awsCatalogError": "No se pudo cargar el catálogo. Comprueba las credenciales/perfil IAM, el acceso a la red y los permisos de listado. La lista incluida y el ARN personalizado siguen disponibles.", + "awsCatalogSelect": "Modelos regionales y perfiles de inferencia", + "awsCatalogEmpty": "No se devolvieron modelos de texto en streaming ni perfiles activos", + "awsCatalogKinds": { + "regional": "Modelo fundacional — bajo demanda en esta región; la invocación directa puede requerir un perfil de inferencia", + "geographic": "Perfil de inferencia geográfico", + "global": "Perfil de inferencia global — mundial", + "application": "Perfil de aplicación — comprobar destinos en AWS" + }, "awsServiceTier": "Nivel de servicio", "awsServiceTierStandard": "Estándar", "awsServiceTierStandardDesc": "Rendimiento y costo equilibrados", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 78770da21d..83da4f4d98 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "Clé secrète AWS", "awsSessionToken": "Jeton de session AWS", "awsRegion": "Région AWS", - "awsCrossRegion": "Utiliser l'inférence inter-régions", - "awsGlobalInference": "Utiliser l'inférence globale (sélection automatique de la région AWS optimale)", + "awsCrossRegion": "Utiliser l'inférence inter-régions géographique", + "awsGlobalInference": "Utiliser l'inférence globale (routage mondial)", + "awsCrossRegionDescription": "Utilise un profil d'inférence géographique (par exemple eu. ou us.). Le traitement reste dans la zone géographique du profil, pas nécessairement dans la région sélectionnée. Certains modèles, dont Claude Sonnet 4.5, nécessitent un profil d'inférence. Vérifiez ses régions source et destination prises en charge dans AWS ; un préfixe généré ne garantit pas la disponibilité.", + "awsGlobalInferenceDescription": "Autorise le traitement dans les régions AWS commerciales du monde entier. Si les deux options sont activées, le routage global est prioritaire pour les modèles compatibles. Désactivez cette option si le traitement doit rester dans une zone géographique précise.", + "awsRequestModelId": "ID du modèle / ARN de la requête :", + "awsArnRouting": "L'ARN sélectionné détermine le routage ; ces cases ne le remplacent pas. Les profils d'application héritent du routage du modèle/profil sous-jacent. Vérifiez ses destinations dans AWS avant utilisation.", + "awsBundledModels": "Le sélecteur ci-dessous est une liste intégrée non vérifiée, pas un contrôle de disponibilité pour le compte/la région. Actualisez le catalogue régional pour sélectionner un modèle AWS ou un profil d'inférence par ARN. Aucune option de routage n'est activée automatiquement.", + "awsCatalogRefresh": "Actualiser le catalogue AWS régional", + "awsCatalogLoading": "Chargement du catalogue AWS régional…", + "awsCatalogDescription": "Interroge le plan de contrôle AWS de la région sélectionnée avec vos identifiants/profil IAM actuels (pas un point de terminaison d'exécution personnalisé). Nécessite bedrock:ListFoundationModels et bedrock:ListInferenceProfiles ; les clés API Bedrock ne sont pas prises en charge. La présence dans la liste ne garantit ni l'autorisation d'appel ni la prise en charge des outils. Les capacités des modèles inconnus sont estimées ; vérifiez-les avant utilisation. Sélectionner une entrée renseigne l'ARN personnalisé sans enregistrer les paramètres.", + "awsCatalogError": "Impossible de charger le catalogue. Vérifiez les identifiants/profil IAM, l'accès réseau et les autorisations de consultation. La liste intégrée et l'ARN personnalisé restent disponibles.", + "awsCatalogSelect": "Modèles régionaux et profils d'inférence", + "awsCatalogEmpty": "Aucun modèle de texte en streaming ni profil actif renvoyé", + "awsCatalogKinds": { + "regional": "Modèle de fondation — à la demande dans cette région ; un appel direct peut nécessiter un profil d'inférence", + "geographic": "Profil d'inférence géographique", + "global": "Profil d'inférence global — mondial", + "application": "Profil d'application — vérifier les destinations dans AWS" + }, "awsServiceTier": "Niveau de service", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Performances et coûts équilibrés", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 413d3515bc..342b9cc403 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS सीक्रेट कुंजी", "awsSessionToken": "AWS सत्र टोकन", "awsRegion": "AWS क्षेत्र", - "awsCrossRegion": "क्रॉस-क्षेत्र अनुमान का उपयोग करें", - "awsGlobalInference": "वैश्विक अनुमान का उपयोग करें (स्वचालित रूप से श्रेष्ठ AWS क्षेत्र चुनें)", + "awsCrossRegion": "भौगोलिक क्रॉस-क्षेत्र अनुमान का उपयोग करें", + "awsGlobalInference": "वैश्विक अनुमान का उपयोग करें (दुनिया भर में रूटिंग)", + "awsCrossRegionDescription": "भौगोलिक अनुमान प्रोफ़ाइल (जैसे eu. या us.) का उपयोग करता है। प्रोसेसिंग प्रोफ़ाइल के भौगोलिक दायरे में रहती है, ज़रूरी नहीं कि चुने गए क्षेत्र में हो। Claude Sonnet 4.5 सहित कुछ मॉडलों को अनुमान प्रोफ़ाइल की आवश्यकता होती है। AWS में प्रोफ़ाइल के समर्थित स्रोत और गंतव्य क्षेत्रों की जाँच करें; बनाया गया उपसर्ग उपलब्धता की गारंटी नहीं देता।", + "awsGlobalInferenceDescription": "दुनिया भर के वाणिज्यिक AWS क्षेत्रों में प्रोसेसिंग की अनुमति देता है। दोनों विकल्प चालू होने पर समर्थित मॉडलों के लिए वैश्विक रूटिंग को प्राथमिकता मिलती है। यदि प्रोसेसिंग किसी विशेष भौगोलिक दायरे में रहनी चाहिए, तो इसे बंद रखें।", + "awsRequestModelId": "अनुरोध मॉडल ID / ARN:", + "awsArnRouting": "चुना गया ARN रूटिंग नियंत्रित करता है; ये चेकबॉक्स इसे ओवरराइड नहीं करते। एप्लिकेशन प्रोफ़ाइल अपने मूल मॉडल/प्रोफ़ाइल की रूटिंग अपनाती हैं। उपयोग से पहले AWS में उसके गंतव्यों की जाँच करें।", + "awsBundledModels": "नीचे का मॉडल चयनकर्ता एक शामिल, असत्यापित सूची है, खाते/क्षेत्र की उपलब्धता की जाँच नहीं। ARN द्वारा AWS मॉडल या अनुमान प्रोफ़ाइल चुनने के लिए क्षेत्रीय कैटलॉग रीफ़्रेश करें। कोई रूटिंग विकल्प अपने आप चालू नहीं होता।", + "awsCatalogRefresh": "क्षेत्रीय AWS कैटलॉग रीफ़्रेश करें", + "awsCatalogLoading": "क्षेत्रीय AWS कैटलॉग लोड हो रहा है…", + "awsCatalogDescription": "आपके वर्तमान IAM क्रेडेंशियल/प्रोफ़ाइल से चुने गए क्षेत्र के AWS कंट्रोल प्लेन को क्वेरी करता है (कस्टम रनटाइम एंडपॉइंट को नहीं)। bedrock:ListFoundationModels और bedrock:ListInferenceProfiles आवश्यक हैं; Bedrock API कुंजियाँ समर्थित नहीं हैं। सूची में होना कॉल करने की अनुमति या टूल समर्थन की गारंटी नहीं देता। अज्ञात मॉडलों की क्षमताएँ अनुमानित हैं; उपयोग से पहले सत्यापित करें। कोई प्रविष्टि चुनने पर सेटिंग्स सहेजे बिना कस्टम ARN भर जाता है।", + "awsCatalogError": "कैटलॉग लोड नहीं हो सका। IAM क्रेडेंशियल/प्रोफ़ाइल, नेटवर्क पहुँच और सूची देखने की अनुमतियाँ जाँचें। शामिल सूची और कस्टम ARN उपलब्ध रहते हैं।", + "awsCatalogSelect": "क्षेत्रीय मॉडल और अनुमान प्रोफ़ाइल", + "awsCatalogEmpty": "कोई स्ट्रीमिंग टेक्स्ट मॉडल या सक्रिय प्रोफ़ाइल नहीं मिली", + "awsCatalogKinds": { + "regional": "आधार मॉडल — इस क्षेत्र में ऑन-डिमांड; सीधे कॉल के लिए अनुमान प्रोफ़ाइल की आवश्यकता हो सकती है", + "geographic": "भौगोलिक अनुमान प्रोफ़ाइल", + "global": "वैश्विक अनुमान प्रोफ़ाइल — दुनिया भर में", + "application": "एप्लिकेशन प्रोफ़ाइल — AWS में गंतव्य जाँचें" + }, "awsServiceTier": "सेवा स्तर", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "संतुलित प्रदर्शन और लागत", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index 9b9928da64..da374d3519 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS Secret Key", "awsSessionToken": "AWS Session Token", "awsRegion": "AWS Region", - "awsCrossRegion": "Gunakan cross-region inference", - "awsGlobalInference": "Gunakan inferensi Global (pilih Wilayah AWS optimal secara otomatis)", + "awsCrossRegion": "Gunakan inferensi lintas wilayah geografis", + "awsGlobalInference": "Gunakan inferensi global (perutean seluruh dunia)", + "awsCrossRegionDescription": "Menggunakan profil inferensi geografis (misalnya eu. atau us.). Pemrosesan tetap dalam cakupan geografis profil, belum tentu di wilayah yang dipilih. Beberapa model, termasuk Claude Sonnet 4.5, memerlukan profil inferensi. Periksa wilayah sumber dan tujuan yang didukung profil di AWS; awalan yang dihasilkan tidak menjamin ketersediaan.", + "awsGlobalInferenceDescription": "Mengizinkan pemrosesan di wilayah AWS komersial di seluruh dunia. Jika kedua opsi diaktifkan, perutean global diutamakan untuk model yang didukung. Nonaktifkan ini jika pemrosesan harus tetap dalam cakupan geografis tertentu.", + "awsRequestModelId": "ID model / ARN permintaan:", + "awsArnRouting": "ARN yang dipilih mengendalikan perutean; kotak centang ini tidak menimpanya. Profil aplikasi mewarisi perutean model/profil dasarnya. Periksa tujuannya di AWS sebelum digunakan.", + "awsBundledModels": "Pemilih model di bawah adalah daftar bawaan yang belum diverifikasi, bukan pemeriksaan ketersediaan akun/wilayah. Segarkan katalog regional untuk memilih model AWS atau profil inferensi berdasarkan ARN. Tidak ada opsi perutean yang diaktifkan secara otomatis.", + "awsCatalogRefresh": "Segarkan katalog AWS regional", + "awsCatalogLoading": "Memuat katalog AWS regional…", + "awsCatalogDescription": "Mengkueri bidang kontrol AWS wilayah yang dipilih menggunakan kredensial/profil IAM saat ini (bukan endpoint runtime khusus). Memerlukan bedrock:ListFoundationModels dan bedrock:ListInferenceProfiles; kunci API Bedrock tidak didukung. Daftar tidak menjamin izin pemanggilan atau dukungan alat. Kemampuan model yang tidak dikenal merupakan perkiraan; verifikasi sebelum digunakan. Memilih entri mengisi ARN khusus tanpa menyimpan pengaturan.", + "awsCatalogError": "Tidak dapat memuat katalog. Periksa kredensial/profil IAM, akses jaringan, dan izin pencantuman. Daftar bawaan dan ARN khusus tetap tersedia.", + "awsCatalogSelect": "Model regional dan profil inferensi", + "awsCatalogEmpty": "Tidak ada model teks streaming atau profil aktif yang dikembalikan", + "awsCatalogKinds": { + "regional": "Model dasar — on-demand di wilayah ini; pemanggilan langsung mungkin memerlukan profil inferensi", + "geographic": "Profil inferensi geografis", + "global": "Profil inferensi global — seluruh dunia", + "application": "Profil aplikasi — periksa tujuan di AWS" + }, "awsServiceTier": "Tingkat Layanan", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Performa dan biaya yang seimbang", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 8a43ec3d35..b3cdbd3c9d 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "Chiave segreta AWS", "awsSessionToken": "Token di sessione AWS", "awsRegion": "Regione AWS", - "awsCrossRegion": "Usa inferenza cross-regione", - "awsGlobalInference": "Usa l'inferenza globale (selezione automatica della regione AWS ottimale)", + "awsCrossRegion": "Usa inferenza geografica tra regioni", + "awsGlobalInference": "Usa inferenza globale (instradamento mondiale)", + "awsCrossRegionDescription": "Usa un profilo di inferenza geografico (ad esempio eu. o us.). L'elaborazione rimane nell'area geografica del profilo, non necessariamente nella regione selezionata. Alcuni modelli, incluso Claude Sonnet 4.5, richiedono un profilo di inferenza. Verifica le regioni di origine e destinazione supportate in AWS; un prefisso generato non garantisce la disponibilità.", + "awsGlobalInferenceDescription": "Consente l'elaborazione nelle regioni commerciali AWS di tutto il mondo. Se entrambe le opzioni sono attive, l'instradamento globale ha la precedenza per i modelli supportati. Disattiva questa opzione se l'elaborazione deve rimanere in una specifica area geografica.", + "awsRequestModelId": "ID modello / ARN della richiesta:", + "awsArnRouting": "L'ARN selezionato controlla l'instradamento; queste caselle non lo sostituiscono. I profili applicativi ereditano l'instradamento del modello/profilo sottostante. Verifica le destinazioni in AWS prima dell'uso.", + "awsBundledModels": "Il selettore sottostante è un elenco incluso non verificato, non un controllo di disponibilità per account/regione. Aggiorna il catalogo regionale per selezionare un modello AWS o un profilo di inferenza tramite ARN. Nessuna opzione di instradamento viene attivata automaticamente.", + "awsCatalogRefresh": "Aggiorna catalogo regionale AWS", + "awsCatalogLoading": "Caricamento del catalogo regionale AWS…", + "awsCatalogDescription": "Interroga il piano di controllo AWS della regione selezionata con le credenziali/profilo IAM correnti (non un endpoint di esecuzione personalizzato). Richiede bedrock:ListFoundationModels e bedrock:ListInferenceProfiles; le chiavi API Bedrock non sono supportate. Le voci non garantiscono autorizzazioni di invocazione né supporto degli strumenti. Le capacità dei modelli sconosciuti sono stimate; verificale prima dell'uso. Selezionare una voce compila l'ARN personalizzato senza salvare le impostazioni.", + "awsCatalogError": "Impossibile caricare il catalogo. Controlla credenziali/profilo IAM, accesso alla rete e autorizzazioni di elenco. L'elenco incluso e l'ARN personalizzato restano disponibili.", + "awsCatalogSelect": "Modelli regionali e profili di inferenza", + "awsCatalogEmpty": "Nessun modello di testo in streaming o profilo attivo restituito", + "awsCatalogKinds": { + "regional": "Modello di base — on demand in questa regione; l'invocazione diretta può richiedere un profilo di inferenza", + "geographic": "Profilo di inferenza geografico", + "global": "Profilo di inferenza globale — mondiale", + "application": "Profilo applicativo — verifica le destinazioni in AWS" + }, "awsServiceTier": "Livello di servizio", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Prestazioni e costo equilibrati", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index b2cfbe977e..c299d4cdf1 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWSシークレットキー", "awsSessionToken": "AWSセッショントークン", "awsRegion": "AWSリージョン", - "awsCrossRegion": "クロスリージョン推論を使用", - "awsGlobalInference": "グローバル推論を使用する(最適なAWSリージョンを自動選択)", + "awsCrossRegion": "地理的クロスリージョン推論を使用", + "awsGlobalInference": "グローバル推論を使用(世界規模のルーティング)", + "awsCrossRegionDescription": "地理的推論プロファイル(例:eu.、us.)を使用します。処理はプロファイルの地理的範囲内に留まりますが、選択したリージョン内とは限りません。Claude Sonnet 4.5 など一部のモデルには推論プロファイルが必要です。AWS でプロファイルが対応する送信元と宛先リージョンを確認してください。生成されたプレフィックスは利用可能性を保証しません。", + "awsGlobalInferenceDescription": "世界中の商用 AWS リージョンでの処理を許可します。両方のオプションが有効な場合、対応モデルではグローバルルーティングが優先されます。処理を特定の地理的範囲内に留める必要がある場合は無効にしてください。", + "awsRequestModelId": "リクエストのモデル ID / ARN:", + "awsArnRouting": "選択した ARN がルーティングを制御し、これらのチェックボックスでは上書きされません。アプリケーションプロファイルは基になるモデル/プロファイルのルーティングを継承します。使用前に AWS で宛先を確認してください。", + "awsBundledModels": "以下のモデル選択は同梱の未検証リストであり、アカウント/リージョンの利用可能性を確認するものではありません。リージョン別カタログを更新して、ARN で AWS モデルまたは推論プロファイルを選択してください。ルーティングオプションが自動で有効になることはありません。", + "awsCatalogRefresh": "リージョン別 AWS カタログを更新", + "awsCatalogLoading": "リージョン別 AWS カタログを読み込み中…", + "awsCatalogDescription": "現在の IAM 認証情報/プロファイルで、選択したリージョンの AWS コントロールプレーンに問い合わせます(カスタムランタイムエンドポイントではありません)。bedrock:ListFoundationModels と bedrock:ListInferenceProfiles が必要です。Bedrock API キーは非対応です。掲載は呼び出し権限やツール対応を保証しません。未知のモデルの機能は推定のため、使用前に確認してください。項目を選択すると、設定を保存せずにカスタム ARN が入力されます。", + "awsCatalogError": "カタログを読み込めませんでした。IAM 認証情報/プロファイル、ネットワーク接続、一覧取得権限を確認してください。同梱リストとカスタム ARN は引き続き使用できます。", + "awsCatalogSelect": "リージョン別モデルと推論プロファイル", + "awsCatalogEmpty": "ストリーミング対応のテキストモデルまたはアクティブなプロファイルが返されませんでした", + "awsCatalogKinds": { + "regional": "基盤モデル — このリージョンでオンデマンド。直接呼び出すには推論プロファイルが必要な場合があります", + "geographic": "地理的推論プロファイル", + "global": "グローバル推論プロファイル — 世界規模", + "application": "アプリケーションプロファイル — AWS で宛先を確認" + }, "awsServiceTier": "サービスティア", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "バランスの取れたパフォーマンスとコスト", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 1ff9addeb4..fdd2eaebde 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS 시크릿 키", "awsSessionToken": "AWS 세션 토큰", "awsRegion": "AWS 리전", - "awsCrossRegion": "교차 리전 추론 사용", - "awsGlobalInference": "글로벌 추론 사용(최적의 AWS 리전 자동 선택)", + "awsCrossRegion": "지리적 교차 리전 추론 사용", + "awsGlobalInference": "글로벌 추론 사용(전 세계 라우팅)", + "awsCrossRegionDescription": "지리적 추론 프로필(예: eu. 또는 us.)을 사용합니다. 처리는 프로필의 지리적 범위 안에 머무르지만 반드시 선택한 리전에서 이루어지는 것은 아닙니다. Claude Sonnet 4.5 등 일부 모델에는 추론 프로필이 필요합니다. AWS에서 프로필이 지원하는 소스 및 대상 리전을 확인하세요. 생성된 접두사는 가용성을 보장하지 않습니다.", + "awsGlobalInferenceDescription": "전 세계 상용 AWS 리전에서 처리를 허용합니다. 두 옵션이 모두 켜져 있으면 지원되는 모델에 글로벌 라우팅이 우선 적용됩니다. 처리가 특정 지리적 범위 내에 머물러야 한다면 이 옵션을 끄세요.", + "awsRequestModelId": "요청 모델 ID / ARN:", + "awsArnRouting": "선택한 ARN이 라우팅을 제어하며 이 체크박스는 이를 재정의하지 않습니다. 애플리케이션 프로필은 기본 모델/프로필의 라우팅을 상속합니다. 사용 전에 AWS에서 대상을 확인하세요.", + "awsBundledModels": "아래 모델 선택기는 포함된 미검증 목록이며 계정/리전 가용성 확인이 아닙니다. 리전별 카탈로그를 새로고침하여 ARN으로 AWS 모델 또는 추론 프로필을 선택하세요. 라우팅 옵션은 자동으로 켜지지 않습니다.", + "awsCatalogRefresh": "리전별 AWS 카탈로그 새로고침", + "awsCatalogLoading": "리전별 AWS 카탈로그 로딩 중…", + "awsCatalogDescription": "현재 IAM 자격 증명/프로필로 선택한 리전의 AWS 제어 영역을 조회합니다(사용자 지정 런타임 엔드포인트가 아님). bedrock:ListFoundationModels 및 bedrock:ListInferenceProfiles가 필요하며 Bedrock API 키는 지원되지 않습니다. 목록에 있어도 호출 권한이나 도구 지원이 보장되지 않습니다. 알 수 없는 모델의 기능은 추정치이므로 사용 전에 확인하세요. 항목을 선택하면 설정을 저장하지 않고 사용자 지정 ARN을 채웁니다.", + "awsCatalogError": "카탈로그를 불러올 수 없습니다. IAM 자격 증명/프로필, 네트워크 접근 및 목록 조회 권한을 확인하세요. 포함된 목록과 사용자 지정 ARN은 계속 사용할 수 있습니다.", + "awsCatalogSelect": "리전별 모델 및 추론 프로필", + "awsCatalogEmpty": "스트리밍 텍스트 모델이나 활성 프로필이 반환되지 않았습니다", + "awsCatalogKinds": { + "regional": "기반 모델 — 이 리전에서 온디맨드; 직접 호출 시 추론 프로필이 필요할 수 있습니다", + "geographic": "지리적 추론 프로필", + "global": "글로벌 추론 프로필 — 전 세계", + "application": "애플리케이션 프로필 — AWS에서 대상 확인" + }, "awsServiceTier": "서비스 계층", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "균형 잡힌 성능 및 비용", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 4361d091a1..d6567483f7 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS-geheime sleutel", "awsSessionToken": "AWS-sessietoken", "awsRegion": "AWS-regio", - "awsCrossRegion": "Gebruik cross-region inference", - "awsGlobalInference": "Gebruik wereldwijde inferentie (automatische selectie van optimale AWS-regio)", + "awsCrossRegion": "Gebruik geografische inferentie tussen regio's", + "awsGlobalInference": "Gebruik globale inferentie (wereldwijde routering)", + "awsCrossRegionDescription": "Gebruikt een geografisch inferentieprofiel (bijvoorbeeld eu. of us.). Verwerking blijft binnen het geografische gebied van het profiel, niet noodzakelijk in de geselecteerde regio. Sommige modellen, waaronder Claude Sonnet 4.5, vereisen een inferentieprofiel. Controleer de ondersteunde bron- en doelregio's in AWS; een gegenereerd voorvoegsel garandeert geen beschikbaarheid.", + "awsGlobalInferenceDescription": "Staat verwerking toe in commerciële AWS-regio's wereldwijd. Als beide opties zijn ingeschakeld, heeft globale routering voorrang voor ondersteunde modellen. Schakel dit uit als verwerking binnen een specifiek geografisch gebied moet blijven.", + "awsRequestModelId": "Model-ID / ARN van de aanvraag:", + "awsArnRouting": "De geselecteerde ARN bepaalt de routering; deze selectievakjes overschrijven die niet. Applicatieprofielen erven de routering van hun onderliggende model/profiel. Controleer de bestemmingen in AWS vóór gebruik.", + "awsBundledModels": "De modelkiezer hieronder is een meegeleverde, ongecontroleerde lijst, geen beschikbaarheidscontrole voor het account/de regio. Vernieuw de regionale catalogus om een AWS-model of inferentieprofiel via ARN te selecteren. Geen enkele routeringsoptie wordt automatisch ingeschakeld.", + "awsCatalogRefresh": "Regionale AWS-catalogus vernieuwen", + "awsCatalogLoading": "Regionale AWS-catalogus laden…", + "awsCatalogDescription": "Bevraagt het AWS-besturingsvlak van de geselecteerde regio met je huidige IAM-inloggegevens/profiel (niet een aangepast runtime-eindpunt). Vereist bedrock:ListFoundationModels en bedrock:ListInferenceProfiles; Bedrock-API-sleutels worden niet ondersteund. Vermeldingen garanderen geen aanroeprechten of ondersteuning voor tools. Mogelijkheden van onbekende modellen zijn geschat; controleer ze vóór gebruik. Een selectie vult de aangepaste ARN in zonder instellingen op te slaan.", + "awsCatalogError": "Kan de catalogus niet laden. Controleer IAM-inloggegevens/profiel, netwerktoegang en lijstbevoegdheden. De meegeleverde lijst en aangepaste ARN blijven beschikbaar.", + "awsCatalogSelect": "Regionale modellen en inferentieprofielen", + "awsCatalogEmpty": "Geen streamingtekstmodellen of actieve profielen teruggegeven", + "awsCatalogKinds": { + "regional": "Basismodel — on-demand in deze regio; directe aanroepen kunnen een inferentieprofiel vereisen", + "geographic": "Geografisch inferentieprofiel", + "global": "Globaal inferentieprofiel — wereldwijd", + "application": "Applicatieprofiel — controleer bestemmingen in AWS" + }, "awsServiceTier": "Servicelaag", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Gebalanceerde prestaties en kosten", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 277bcaa470..a26ba4ee1c 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "Klucz tajny AWS", "awsSessionToken": "Token sesji AWS", "awsRegion": "Region AWS", - "awsCrossRegion": "Użyj wnioskowania międzyregionalnego", - "awsGlobalInference": "Użyj globalnej inferencji (automatyczny wybór optymalnego regionu AWS)", + "awsCrossRegion": "Użyj geograficznego wnioskowania międzyregionalnego", + "awsGlobalInference": "Użyj wnioskowania globalnego (routing na całym świecie)", + "awsCrossRegionDescription": "Używa geograficznego profilu wnioskowania (np. eu. lub us.). Przetwarzanie pozostaje w obszarze geograficznym profilu, niekoniecznie w wybranym regionie. Niektóre modele, w tym Claude Sonnet 4.5, wymagają profilu wnioskowania. Sprawdź obsługiwane regiony źródłowe i docelowe w AWS; wygenerowany prefiks nie gwarantuje dostępności.", + "awsGlobalInferenceDescription": "Zezwala na przetwarzanie w komercyjnych regionach AWS na całym świecie. Gdy obie opcje są włączone, routing globalny ma pierwszeństwo dla obsługiwanych modeli. Wyłącz tę opcję, jeśli przetwarzanie musi pozostać w określonym obszarze geograficznym.", + "awsRequestModelId": "ID modelu / ARN żądania:", + "awsArnRouting": "Wybrany ARN steruje routingiem; te pola wyboru go nie zastępują. Profile aplikacji dziedziczą routing modelu/profilu bazowego. Przed użyciem sprawdź jego regiony docelowe w AWS.", + "awsBundledModels": "Poniższy selektor to dołączona, niezweryfikowana lista, a nie kontrola dostępności dla konta/regionu. Odśwież katalog regionalny, aby wybrać model AWS lub profil wnioskowania przez ARN. Żadna opcja routingu nie jest włączana automatycznie.", + "awsCatalogRefresh": "Odśwież regionalny katalog AWS", + "awsCatalogLoading": "Ładowanie regionalnego katalogu AWS…", + "awsCatalogDescription": "Odpytuje płaszczyznę sterowania AWS wybranego regionu przy użyciu bieżących danych uwierzytelniających/profilu IAM (nie niestandardowego punktu końcowego wykonania). Wymaga bedrock:ListFoundationModels i bedrock:ListInferenceProfiles; klucze API Bedrock nie są obsługiwane. Wpisy nie gwarantują uprawnień do wywołań ani obsługi narzędzi. Możliwości nieznanych modeli są szacowane; sprawdź je przed użyciem. Wybranie wpisu wypełnia niestandardowy ARN bez zapisywania ustawień.", + "awsCatalogError": "Nie udało się załadować katalogu. Sprawdź dane uwierzytelniające/profil IAM, dostęp do sieci i uprawnienia do wyświetlania list. Dołączona lista i niestandardowy ARN pozostają dostępne.", + "awsCatalogSelect": "Modele regionalne i profile wnioskowania", + "awsCatalogEmpty": "Nie zwrócono modeli tekstowych ze strumieniowaniem ani aktywnych profili", + "awsCatalogKinds": { + "regional": "Model bazowy — na żądanie w tym regionie; bezpośrednie wywołanie może wymagać profilu wnioskowania", + "geographic": "Geograficzny profil wnioskowania", + "global": "Globalny profil wnioskowania — cały świat", + "application": "Profil aplikacji — sprawdź regiony docelowe w AWS" + }, "awsServiceTier": "Warstwa usługi", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "Zrównoważona wydajność i koszt", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 8ce67bcd48..79a4ff2001 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "Chave Secreta AWS", "awsSessionToken": "Token de Sessão AWS", "awsRegion": "Região AWS", - "awsCrossRegion": "Usar inferência entre regiões", - "awsGlobalInference": "Usar inferência global (selecionar automaticamente a região ideal da AWS)", + "awsCrossRegion": "Usar inferência geográfica entre regiões", + "awsGlobalInference": "Usar inferência global (roteamento mundial)", + "awsCrossRegionDescription": "Usa um perfil de inferência geográfico (por exemplo, eu. ou us.). O processamento permanece na área geográfica do perfil, não necessariamente na região selecionada. Alguns modelos, incluindo Claude Sonnet 4.5, exigem um perfil de inferência. Confira as regiões de origem e destino compatíveis na AWS; um prefixo gerado não garante disponibilidade.", + "awsGlobalInferenceDescription": "Permite processamento em regiões comerciais da AWS no mundo todo. Quando ambas as opções estão ativadas, o roteamento global tem prioridade para modelos compatíveis. Desative esta opção se o processamento precisar permanecer em uma área geográfica específica.", + "awsRequestModelId": "ID do modelo / ARN da solicitação:", + "awsArnRouting": "O ARN selecionado controla o roteamento; estas caixas não o substituem. Perfis de aplicação herdam o roteamento do modelo/perfil subjacente. Confira seus destinos na AWS antes de usar.", + "awsBundledModels": "O seletor abaixo é uma lista incluída não verificada, não uma verificação de disponibilidade da conta/região. Atualize o catálogo regional para selecionar um modelo AWS ou perfil de inferência por ARN. Nenhuma opção de roteamento é ativada automaticamente.", + "awsCatalogRefresh": "Atualizar catálogo regional da AWS", + "awsCatalogLoading": "Carregando catálogo regional da AWS…", + "awsCatalogDescription": "Consulta o plano de controle da AWS da região selecionada com suas credenciais/perfil IAM atuais (não um endpoint de execução personalizado). Requer bedrock:ListFoundationModels e bedrock:ListInferenceProfiles; chaves de API do Bedrock não são compatíveis. As entradas não garantem permissão de invocação nem suporte a ferramentas. As capacidades de modelos desconhecidos são estimadas; verifique-as antes de usar. Selecionar uma entrada preenche o ARN personalizado sem salvar as configurações.", + "awsCatalogError": "Não foi possível carregar o catálogo. Confira credenciais/perfil IAM, acesso à rede e permissões de listagem. A lista incluída e o ARN personalizado continuam disponíveis.", + "awsCatalogSelect": "Modelos regionais e perfis de inferência", + "awsCatalogEmpty": "Nenhum modelo de texto com streaming ou perfil ativo retornado", + "awsCatalogKinds": { + "regional": "Modelo de base — sob demanda nesta região; a invocação direta pode exigir um perfil de inferência", + "geographic": "Perfil de inferência geográfico", + "global": "Perfil de inferência global — mundial", + "application": "Perfil de aplicação — confira os destinos na AWS" + }, "awsServiceTier": "Nível de serviço", "awsServiceTierStandard": "Padrão", "awsServiceTierStandardDesc": "Desempenho e custo equilibrados", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 0ac516c190..e426e6d047 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS Secret Key", "awsSessionToken": "AWS Session Token", "awsRegion": "Регион AWS", - "awsCrossRegion": "Использовать кросс-региональный вывод", - "awsGlobalInference": "Использовать глобальный вывод (автоматический выбор оптимального региона AWS)", + "awsCrossRegion": "Использовать географический межрегиональный вывод", + "awsGlobalInference": "Использовать глобальный вывод (маршрутизация по всему миру)", + "awsCrossRegionDescription": "Использует географический профиль вывода (например, eu. или us.). Обработка остаётся в географической области профиля, но не обязательно в выбранном регионе. Некоторые модели, включая Claude Sonnet 4.5, требуют профиль вывода. Проверьте поддерживаемые исходные и целевые регионы профиля в AWS; созданный префикс не гарантирует доступность.", + "awsGlobalInferenceDescription": "Разрешает обработку в коммерческих регионах AWS по всему миру. Если включены обе опции, глобальная маршрутизация имеет приоритет для поддерживаемых моделей. Отключите эту опцию, если обработка должна оставаться в определённой географической области.", + "awsRequestModelId": "ID модели / ARN запроса:", + "awsArnRouting": "Выбранный ARN определяет маршрутизацию; эти флажки её не переопределяют. Профили приложений наследуют маршрутизацию базовой модели/профиля. Перед использованием проверьте целевые регионы в AWS.", + "awsBundledModels": "Список моделей ниже встроенный и непроверенный; он не проверяет доступность для аккаунта/региона. Обновите региональный каталог, чтобы выбрать модель AWS или профиль вывода по ARN. Ни одна опция маршрутизации не включается автоматически.", + "awsCatalogRefresh": "Обновить региональный каталог AWS", + "awsCatalogLoading": "Загрузка регионального каталога AWS…", + "awsCatalogDescription": "Запрашивает плоскость управления AWS выбранного региона с текущими учётными данными/профилем IAM (не пользовательскую конечную точку выполнения). Требуются bedrock:ListFoundationModels и bedrock:ListInferenceProfiles; ключи API Bedrock не поддерживаются. Наличие в списке не гарантирует права вызова или поддержку инструментов. Возможности неизвестных моделей оценочные; проверьте их перед использованием. Выбор записи заполняет пользовательский ARN без сохранения настроек.", + "awsCatalogError": "Не удалось загрузить каталог. Проверьте учётные данные/профиль IAM, доступ к сети и разрешения на получение списков. Встроенный список и пользовательский ARN остаются доступны.", + "awsCatalogSelect": "Региональные модели и профили вывода", + "awsCatalogEmpty": "Потоковые текстовые модели и активные профили не найдены", + "awsCatalogKinds": { + "regional": "Базовая модель — по запросу в этом регионе; для прямого вызова может потребоваться профиль вывода", + "geographic": "Географический профиль вывода", + "global": "Глобальный профиль вывода — весь мир", + "application": "Профиль приложения — проверьте целевые регионы в AWS" + }, "awsServiceTier": "Уровень обслуживания", "awsServiceTierStandard": "Стандартный", "awsServiceTierStandardDesc": "Сбалансированная производительность и стоимость", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 5d3a5cb89a..9f9c186bf2 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS Gizli Anahtarı", "awsSessionToken": "AWS Oturum Belirteci", "awsRegion": "AWS Bölgesi", - "awsCrossRegion": "Bölgeler arası çıkarım kullan", - "awsGlobalInference": "Genel çıkarımı kullan (en uygun AWS Bölgesini otomatik seç)", + "awsCrossRegion": "Coğrafi bölgeler arası çıkarım kullan", + "awsGlobalInference": "Küresel çıkarım kullan (dünya çapında yönlendirme)", + "awsCrossRegionDescription": "Coğrafi bir çıkarım profili kullanır (örneğin eu. veya us.). İşleme, seçilen bölgede olmak zorunda değildir ancak profilin coğrafi alanı içinde kalır. Claude Sonnet 4.5 dahil bazı modeller çıkarım profili gerektirir. AWS'de profilin desteklenen kaynak ve hedef bölgelerini kontrol edin; oluşturulan önek kullanılabilirliği garanti etmez.", + "awsGlobalInferenceDescription": "Dünya çapındaki ticari AWS bölgelerinde işlemeye izin verir. Her iki seçenek de etkinse desteklenen modellerde küresel yönlendirme önceliklidir. İşlemenin belirli bir coğrafi alanda kalması gerekiyorsa bunu kapalı tutun.", + "awsRequestModelId": "İstek model kimliği / ARN:", + "awsArnRouting": "Seçilen ARN yönlendirmeyi belirler; bu onay kutuları onu geçersiz kılmaz. Uygulama profilleri temel model/profilin yönlendirmesini devralır. Kullanmadan önce AWS'de hedeflerini kontrol edin.", + "awsBundledModels": "Aşağıdaki model seçici, hesap/bölge kullanılabilirlik kontrolü değil, paketlenmiş ve doğrulanmamış bir listedir. ARN ile bir AWS modeli veya çıkarım profili seçmek için bölgesel kataloğu yenileyin. Hiçbir yönlendirme seçeneği otomatik etkinleştirilmez.", + "awsCatalogRefresh": "Bölgesel AWS kataloğunu yenile", + "awsCatalogLoading": "Bölgesel AWS kataloğu yükleniyor…", + "awsCatalogDescription": "Geçerli IAM kimlik bilgileriniz/profilinizle seçilen bölgenin AWS kontrol düzlemini sorgular (özel çalışma zamanı uç noktasını değil). bedrock:ListFoundationModels ve bedrock:ListInferenceProfiles gerektirir; Bedrock API anahtarları desteklenmez. Listeler çağırma iznini veya araç desteğini garanti etmez. Bilinmeyen modellerin yetenekleri tahminidir; kullanmadan önce doğrulayın. Bir giriş seçmek ayarları kaydetmeden özel ARN'yi doldurur.", + "awsCatalogError": "Katalog yüklenemedi. IAM kimlik bilgilerini/profilini, ağ erişimini ve listeleme izinlerini kontrol edin. Paketlenmiş liste ve özel ARN kullanılabilir durumda kalır.", + "awsCatalogSelect": "Bölgesel modeller ve çıkarım profilleri", + "awsCatalogEmpty": "Akış destekli metin modeli veya etkin profil döndürülmedi", + "awsCatalogKinds": { + "regional": "Temel model — bu bölgede isteğe bağlı; doğrudan çağrı bir çıkarım profili gerektirebilir", + "geographic": "Coğrafi çıkarım profili", + "global": "Küresel çıkarım profili — dünya çapında", + "application": "Uygulama profili — AWS'de hedefleri kontrol edin" + }, "awsServiceTier": "Hizmet seviyesi", "awsServiceTierStandard": "Standart", "awsServiceTierStandardDesc": "Dengeli performans ve maliyet", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index adb1be64e3..52b5303cb9 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "Khóa bí mật AWS", "awsSessionToken": "Token phiên AWS", "awsRegion": "Vùng AWS", - "awsCrossRegion": "Sử dụng suy luận liên vùng", - "awsGlobalInference": "Sử dụng suy luận toàn cầu (tự động chọn Khu vực AWS tối ưu)", + "awsCrossRegion": "Sử dụng suy luận liên vùng theo địa lý", + "awsGlobalInference": "Sử dụng suy luận toàn cầu (định tuyến toàn thế giới)", + "awsCrossRegionDescription": "Sử dụng hồ sơ suy luận theo địa lý (ví dụ eu. hoặc us.). Việc xử lý nằm trong phạm vi địa lý của hồ sơ, không nhất thiết ở vùng đã chọn. Một số mô hình, bao gồm Claude Sonnet 4.5, yêu cầu hồ sơ suy luận. Kiểm tra các vùng nguồn và đích được hồ sơ hỗ trợ trong AWS; tiền tố được tạo không đảm bảo tính khả dụng.", + "awsGlobalInferenceDescription": "Cho phép xử lý tại các vùng AWS thương mại trên toàn thế giới. Khi bật cả hai tùy chọn, định tuyến toàn cầu được ưu tiên cho các mô hình được hỗ trợ. Tắt tùy chọn này nếu việc xử lý phải nằm trong một phạm vi địa lý cụ thể.", + "awsRequestModelId": "ID mô hình / ARN của yêu cầu:", + "awsArnRouting": "ARN đã chọn điều khiển định tuyến; các hộp kiểm này không ghi đè định tuyến đó. Hồ sơ ứng dụng kế thừa định tuyến của mô hình/hồ sơ cơ sở. Kiểm tra các đích trong AWS trước khi sử dụng.", + "awsBundledModels": "Bộ chọn mô hình bên dưới là danh sách đi kèm chưa được xác minh, không phải kiểm tra tính khả dụng của tài khoản/vùng. Làm mới danh mục theo vùng để chọn mô hình AWS hoặc hồ sơ suy luận bằng ARN. Không tùy chọn định tuyến nào được bật tự động.", + "awsCatalogRefresh": "Làm mới danh mục AWS theo vùng", + "awsCatalogLoading": "Đang tải danh mục AWS theo vùng…", + "awsCatalogDescription": "Truy vấn mặt phẳng điều khiển AWS của vùng đã chọn bằng thông tin xác thực/hồ sơ IAM hiện tại (không phải điểm cuối thời gian chạy tùy chỉnh). Yêu cầu bedrock:ListFoundationModels và bedrock:ListInferenceProfiles; không hỗ trợ khóa API Bedrock. Danh sách không đảm bảo quyền gọi hoặc hỗ trợ công cụ. Khả năng của mô hình chưa biết là ước tính; hãy xác minh trước khi dùng. Chọn một mục sẽ điền ARN tùy chỉnh mà không lưu cài đặt.", + "awsCatalogError": "Không thể tải danh mục. Kiểm tra thông tin xác thực/hồ sơ IAM, quyền truy cập mạng và quyền liệt kê. Danh sách đi kèm và ARN tùy chỉnh vẫn khả dụng.", + "awsCatalogSelect": "Mô hình theo vùng và hồ sơ suy luận", + "awsCatalogEmpty": "Không có mô hình văn bản phát trực tuyến hoặc hồ sơ đang hoạt động được trả về", + "awsCatalogKinds": { + "regional": "Mô hình nền tảng — theo yêu cầu trong vùng này; gọi trực tiếp có thể cần hồ sơ suy luận", + "geographic": "Hồ sơ suy luận theo địa lý", + "global": "Hồ sơ suy luận toàn cầu — toàn thế giới", + "application": "Hồ sơ ứng dụng — kiểm tra các đích trong AWS" + }, "awsServiceTier": "Cấp độ dịch vụ", "awsServiceTierStandard": "Tiêu chuẩn", "awsServiceTierStandardDesc": "Hiệu suất và chi phí cân bằng", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 916f629efe..b710826a7e 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -550,8 +550,25 @@ "awsSecretKey": "AWS 密钥", "awsSessionToken": "AWS 会话Token", "awsRegion": "AWS 区域", - "awsCrossRegion": "使用跨区域推理", - "awsGlobalInference": "使用全局推理(自动选择最佳 AWS 区域)", + "awsCrossRegion": "使用地理范围内的跨区域推理", + "awsGlobalInference": "使用全局推理(全球路由)", + "awsCrossRegionDescription": "使用地理推理配置文件(例如 eu. 或 us.)。处理会保持在配置文件的地理范围内,但不一定在所选区域内。部分模型(包括 Claude Sonnet 4.5)需要推理配置文件。请在 AWS 中检查配置文件支持的源区域和目标区域;生成的前缀不保证可用性。", + "awsGlobalInferenceDescription": "允许在全球的商业 AWS 区域中处理。当两个选项都启用时,支持的模型优先使用全局路由。如果处理必须保持在特定地理范围内,请关闭此选项。", + "awsRequestModelId": "请求模型 ID / ARN:", + "awsArnRouting": "所选 ARN 控制路由;这些复选框不会覆盖它。应用程序配置文件继承其底层模型/配置文件的路由。使用前请在 AWS 中检查其目标区域。", + "awsBundledModels": "下方的模型选择器是内置的未经验证的列表,并非账户/区域可用性检查。刷新区域目录可通过 ARN 选择 AWS 模型或推理配置文件。不会自动启用任何路由选项。", + "awsCatalogRefresh": "刷新区域 AWS 目录", + "awsCatalogLoading": "正在加载区域 AWS 目录…", + "awsCatalogDescription": "使用当前 IAM 凭证/配置文件查询所选区域的 AWS 控制平面(而非自定义运行时端点)。需要 bedrock:ListFoundationModels 和 bedrock:ListInferenceProfiles;不支持 Bedrock API 密钥。列出条目不保证调用权限或工具支持。未知模型的能力为估计值,请在使用前核实。选择条目会填入自定义 ARN,但不会保存设置。", + "awsCatalogError": "无法加载目录。请检查 IAM 凭证/配置文件、网络访问和列表查询权限。内置列表和自定义 ARN 仍然可用。", + "awsCatalogSelect": "区域模型和推理配置文件", + "awsCatalogEmpty": "未返回流式文本模型或活动配置文件", + "awsCatalogKinds": { + "regional": "基础模型 — 在此区域按需可用;直接调用可能需要推理配置文件", + "geographic": "地理推理配置文件", + "global": "全局推理配置文件 — 全球", + "application": "应用程序配置文件 — 请在 AWS 中检查目标区域" + }, "awsServiceTier": "服务层级", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "性能和成本均衡", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index d1f9258dcf..5943e2685e 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -577,8 +577,25 @@ "awsSecretKey": "AWS 秘密金鑰", "awsSessionToken": "AWS 工作階段權杖", "awsRegion": "AWS 區域", - "awsCrossRegion": "使用跨區域推論", - "awsGlobalInference": "使用全域推論(自動選取最佳 AWS 區域)", + "awsCrossRegion": "使用地理範圍內的跨區域推論", + "awsGlobalInference": "使用全域推論(全球路由)", + "awsCrossRegionDescription": "使用地理推論設定檔(例如 eu. 或 us.)。處理會維持在設定檔的地理範圍內,但不一定在所選區域內。部分模型(包括 Claude Sonnet 4.5)需要推論設定檔。請在 AWS 中檢查設定檔支援的來源區域和目的地區域;產生的前綴不保證可用性。", + "awsGlobalInferenceDescription": "允許在全球的商業 AWS 區域中處理。當兩個選項都啟用時,支援的模型優先使用全域路由。如果處理必須維持在特定地理範圍內,請關閉此選項。", + "awsRequestModelId": "請求模型 ID / ARN:", + "awsArnRouting": "所選 ARN 控制路由;這些核取方塊不會覆寫它。應用程式設定檔繼承其底層模型/設定檔的路由。使用前請在 AWS 中檢查其目的地。", + "awsBundledModels": "下方的模型選擇器是內建的未驗證清單,並非帳戶/區域可用性檢查。重新整理區域目錄可透過 ARN 選擇 AWS 模型或推論設定檔。不會自動啟用任何路由選項。", + "awsCatalogRefresh": "重新整理區域 AWS 目錄", + "awsCatalogLoading": "正在載入區域 AWS 目錄…", + "awsCatalogDescription": "使用目前的 IAM 憑證/設定檔查詢所選區域的 AWS 控制平面(而非自訂執行階段端點)。需要 bedrock:ListFoundationModels 和 bedrock:ListInferenceProfiles;不支援 Bedrock API 金鑰。列出項目不保證呼叫權限或工具支援。未知模型的能力為估計值,請在使用前確認。選擇項目會填入自訂 ARN,但不會儲存設定。", + "awsCatalogError": "無法載入目錄。請檢查 IAM 憑證/設定檔、網路存取和清單查詢權限。內建清單和自訂 ARN 仍然可用。", + "awsCatalogSelect": "區域模型和推論設定檔", + "awsCatalogEmpty": "未傳回串流文字模型或使用中的設定檔", + "awsCatalogKinds": { + "regional": "基礎模型 — 在此區域隨選可用;直接呼叫可能需要推論設定檔", + "geographic": "地理推論設定檔", + "global": "全域推論設定檔 — 全球", + "application": "應用程式設定檔 — 請在 AWS 中檢查目的地" + }, "awsServiceTier": "服務層級", "awsServiceTierStandard": "Standard", "awsServiceTierStandardDesc": "效能和成本均衡",