diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9e22303a350..34ac70a6cfd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -76,7 +76,7 @@ body: label: API Provider (optional) options: - Anthropic - - AWS Bedrock + - Amazon Bedrock - Chutes AI - DeepSeek - Featherless AI diff --git a/.roo/rules-translate/instructions-zh-cn.md b/.roo/rules-translate/instructions-zh-cn.md index 241ae338dc1..6141038728c 100644 --- a/.roo/rules-translate/instructions-zh-cn.md +++ b/.roo/rules-translate/instructions-zh-cn.md @@ -115,7 +115,7 @@ - 保留英文品牌名 - 技术术语保持一致性 - - 保留英文专有名词:如"AWS Bedrock ARN" + - 保留英文专有名词:如"Amazon Bedrock ARN" 4. **用户操作** - 操作动词统一: diff --git a/CHANGELOG.md b/CHANGELOG.md index 86e435b6ee2..198561e4ca0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -374,7 +374,7 @@ ## [3.28.11] - 2025-09-29 -- Fix: Correct AWS Bedrock Claude Sonnet 4.5 model identifier (#8371 by @sunhyung, PR by @app/roomote) +- Fix: Correct Amazon Bedrock Claude Sonnet 4.5 model identifier (#8371 by @sunhyung, PR by @app/roomote) - Fix: Correct Claude Sonnet 4.5 model ID format (thanks @daniel-lxs!) ## [3.28.10] - 2025-09-29 @@ -706,7 +706,7 @@ ## [3.25.14] - 2025-08-13 - Fix: Only include verbosity parameter for models that support it (#7054 by @eastonmeth, PR by @app/roomote) -- Fix: AWS Bedrock 1M context - Move anthropic_beta to additionalModelRequestFields (thanks @daniel-lxs!) +- Fix: Amazon Bedrock 1M context - Move anthropic_beta to additionalModelRequestFields (thanks @daniel-lxs!) - Fix: Make cancelling requests more responsive by reverting recent changes ## [3.25.13] - 2025-08-12 @@ -1071,7 +1071,7 @@ - Add user-configurable search score threshold slider for semantic search (thanks @hannesrudolph!) - Add default headers and testing for litellm fetcher (thanks @andrewshu2000!) - Fix consistent cancellation error messages for thinking vs streaming phases -- Fix AWS Bedrock cross-region inference profile mapping (thanks @KevinZhao!) +- Fix Amazon Bedrock cross-region inference profile mapping (thanks @KevinZhao!) - Fix URL loading timeout issues in @ mentions (thanks @MuriloFP!) - Fix API retry exponential backoff capped at 10 minutes (thanks @MuriloFP!) - Fix Qdrant URL field auto-filling with default value (thanks @SannidhyaSah!) @@ -1085,7 +1085,7 @@ - Suppress Mermaid error rendering - Improve Mermaid buttons with light background in light mode (thanks @chrarnoldus!) - Add .vscode/ to write-protected files/directories -- Update AWS Bedrock cross-region inference profile mapping (thanks @KevinZhao!) +- Update Amazon Bedrock cross-region inference profile mapping (thanks @KevinZhao!) ## [3.22.5] - 2025-06-28 @@ -1709,7 +1709,7 @@ - Improved display of diff errors + easy copying for investigation - Fixes to .vscodeignore (thanks @franekp!) - Fix a zh-CN translation for model capabilities (thanks @zhangtony239!) -- Rename AWS Bedrock to Amazon Bedrock (thanks @ronyblum!) +- Rename Amazon Bedrock to Amazon Bedrock (thanks @ronyblum!) - Update extension title and description (thanks @StevenTCramer!) ## [3.11.12] - 2025-04-09 @@ -1958,12 +1958,12 @@ - PowerShell-specific command handling (thanks @KJ7LNW!) - OpenAI-compatible DeepSeek/QwQ reasoning support (thanks @lightrabbit!) - Anthropic-style prompt caching in the OpenAI-compatible provider (thanks @dleen!) -- Add Deepseek R1 for AWS Bedrock (thanks @ATempsch!) +- Add Deepseek R1 for Amazon Bedrock (thanks @ATempsch!) - Fix MarkdownBlock text color for Dark High Contrast theme (thanks @cannuri!) - Add gemini-2.0-pro-exp-02-05 model to vertex (thanks @shohei-ihaya!) - Bring back progress status for multi-diff edits (thanks @qdaxb!) - Refactor alert dialog styles to use the correct vscode theme (thanks @cannuri!) -- Custom ARNs in AWS Bedrock (thanks @Smartsheet-JB-Brown!) +- Custom ARNs in Amazon Bedrock (thanks @Smartsheet-JB-Brown!) - Update MCP servers directory path for platform compatibility (thanks @hannesrudolph!) - Fix browser system prompt inclusion rules (thanks @cannuri!) - Publish git tags to github from CI (thanks @pdecat!) @@ -2101,7 +2101,7 @@ ## [3.7.1] - 2025-02-24 -- Add AWS Bedrock support for Sonnet 3.7 and update some defaults to Sonnet 3.7 instead of 3.5 +- Add Amazon Bedrock support for Sonnet 3.7 and update some defaults to Sonnet 3.7 instead of 3.5 ## [3.7.0] - 2025-02-24 @@ -2118,7 +2118,7 @@ ## [3.3.24] - 2025-02-20 -- Fixed a bug with region selection preventing AWS Bedrock profiles from being saved (thanks @oprstchn!) +- Fixed a bug with region selection preventing Amazon Bedrock profiles from being saved (thanks @oprstchn!) - Updated the price of gpt-4o (thanks @marvijo-code!) ## [3.3.23] - 2025-02-20 @@ -2302,7 +2302,7 @@ - Reverts provider key entry back to checking onInput instead of onChange to hopefully address issues entering API keys (thanks @samhvw8!) - Added explicit checkbox to use Azure for OpenAI compatible providers (thanks @samhvw8!) - Fixed Glama usage reporting (thanks @punkpeye!) -- Added Llama 3.3 70B Instruct model to the AWS Bedrock provider options (thanks @Premshay!) +- Added Llama 3.3 70B Instruct model to the Amazon Bedrock provider options (thanks @Premshay!) ## [3.2.7] diff --git a/package.json b/package.json index 793658b459f..0f3c3b7ba04 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "tar-fs": ">=3.1.1", "esbuild": ">=0.25.0", "undici": ">=5.29.0", - "brace-expansion": ">=2.0.2", + "brace-expansion": "^2.0.2", "form-data": ">=4.0.4", "bluebird": ">=3.7.2", "glob": ">=11.1.0" diff --git a/packages/types/src/codebase-index.ts b/packages/types/src/codebase-index.ts index 8ad66cbb68b..8673e98e7d6 100644 --- a/packages/types/src/codebase-index.ts +++ b/packages/types/src/codebase-index.ts @@ -22,7 +22,16 @@ export const codebaseIndexConfigSchema = z.object({ codebaseIndexEnabled: z.boolean().optional(), codebaseIndexQdrantUrl: z.string().optional(), codebaseIndexEmbedderProvider: z - .enum(["openai", "ollama", "openai-compatible", "gemini", "mistral", "vercel-ai-gateway", "openrouter"]) + .enum([ + "openai", + "ollama", + "openai-compatible", + "gemini", + "mistral", + "vercel-ai-gateway", + "bedrock", + "openrouter", + ]) .optional(), codebaseIndexEmbedderBaseUrl: z.string().optional(), codebaseIndexEmbedderModelId: z.string().optional(), @@ -36,6 +45,9 @@ export const codebaseIndexConfigSchema = z.object({ // OpenAI Compatible specific fields codebaseIndexOpenAiCompatibleBaseUrl: z.string().optional(), codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(), + // Bedrock specific fields + codebaseIndexBedrockRegion: z.string().optional(), + codebaseIndexBedrockProfile: z.string().optional(), }) export type CodebaseIndexConfig = z.infer @@ -52,6 +64,7 @@ export const codebaseIndexModelsSchema = z.object({ mistral: z.record(z.string(), z.object({ dimension: z.number() })).optional(), "vercel-ai-gateway": z.record(z.string(), z.object({ dimension: z.number() })).optional(), openrouter: z.record(z.string(), z.object({ dimension: z.number() })).optional(), + bedrock: z.record(z.string(), z.object({ dimension: z.number() })).optional(), }) export type CodebaseIndexModels = z.infer diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 971fcd4cc2a..6558ec50221 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,7 +8,7 @@ overrides: tar-fs: '>=3.1.1' esbuild: '>=0.25.0' undici: '>=5.29.0' - brace-expansion: '>=2.0.2' + brace-expansion: ^2.0.2 form-data: '>=4.0.4' bluebird: '>=3.7.2' glob: '>=11.1.0' @@ -4574,9 +4574,8 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@3.0.1: - resolution: {integrity: sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==} - engines: {node: '>= 16'} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} bare-events@2.5.4: resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} @@ -4659,9 +4658,8 @@ packages: bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} - brace-expansion@4.0.1: - resolution: {integrity: sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==} - engines: {node: '>= 18'} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -14054,7 +14052,7 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.17.50)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@20.17.57)(@vitest/ui@3.2.4)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(tsx@4.19.4)(yaml@2.8.0) '@vitest/utils@3.2.4': dependencies: @@ -14398,7 +14396,7 @@ snapshots: bail@2.0.2: {} - balanced-match@3.0.1: {} + balanced-match@1.0.2: {} bare-events@2.5.4: optional: true @@ -14485,9 +14483,9 @@ snapshots: bowser@2.11.0: {} - brace-expansion@4.0.1: + brace-expansion@2.0.2: dependencies: - balanced-match: 3.0.1 + balanced-match: 1.0.2 braces@3.0.3: dependencies: @@ -17968,7 +17966,7 @@ snapshots: minimatch@10.0.1: dependencies: - brace-expansion: 4.0.1 + brace-expansion: 2.0.2 minimatch@10.1.1: dependencies: @@ -17976,15 +17974,15 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 4.0.1 + brace-expansion: 2.0.2 minimatch@5.1.6: dependencies: - brace-expansion: 4.0.1 + brace-expansion: 2.0.2 minimatch@9.0.5: dependencies: - brace-expansion: 4.0.1 + brace-expansion: 2.0.2 minimist@1.2.8: {} diff --git a/src/core/tools/BrowserActionTool.ts b/src/core/tools/BrowserActionTool.ts index fa1cfeeb2fa..72212e926c0 100644 --- a/src/core/tools/BrowserActionTool.ts +++ b/src/core/tools/BrowserActionTool.ts @@ -109,12 +109,12 @@ export async function browserActionTool( // Do not close the browser on parameter validation errors return // can't be within an inner switch } - + // Get viewport dimensions from the browser session const viewportSize = cline.browserSession.getViewportSize() const viewportWidth = viewportSize.width || 900 // default to 900 if not available const viewportHeight = viewportSize.height || 600 // default to 600 if not available - + // Scale coordinate from image dimensions to viewport dimensions try { processedCoordinate = scaleCoordinate(coordinate, viewportWidth, viewportHeight) @@ -143,7 +143,7 @@ export async function browserActionTool( return } } - + if (action === "resize") { if (!size) { cline.consecutiveMistakeCount++ diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index dbfaeb16bd8..363e8b944a8 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2084,6 +2084,8 @@ export class ClineProvider codebaseIndexOpenAiCompatibleBaseUrl: codebaseIndexConfig?.codebaseIndexOpenAiCompatibleBaseUrl, codebaseIndexSearchMaxResults: codebaseIndexConfig?.codebaseIndexSearchMaxResults, codebaseIndexSearchMinScore: codebaseIndexConfig?.codebaseIndexSearchMinScore, + codebaseIndexBedrockRegion: codebaseIndexConfig?.codebaseIndexBedrockRegion, + codebaseIndexBedrockProfile: codebaseIndexConfig?.codebaseIndexBedrockProfile, }, // Only set mdmCompliant if there's an actual MDM policy // undefined means no MDM policy, true means compliant, false means non-compliant @@ -2312,6 +2314,8 @@ export class ClineProvider stateValues.codebaseIndexConfig?.codebaseIndexOpenAiCompatibleBaseUrl, codebaseIndexSearchMaxResults: stateValues.codebaseIndexConfig?.codebaseIndexSearchMaxResults, codebaseIndexSearchMinScore: stateValues.codebaseIndexConfig?.codebaseIndexSearchMinScore, + codebaseIndexBedrockRegion: stateValues.codebaseIndexConfig?.codebaseIndexBedrockRegion, + codebaseIndexBedrockProfile: stateValues.codebaseIndexConfig?.codebaseIndexBedrockProfile, }, profileThresholds: stateValues.profileThresholds ?? {}, includeDiagnosticMessages: stateValues.includeDiagnosticMessages ?? true, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 7ccee8c100d..1a0afa1fa3f 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -2378,6 +2378,8 @@ export const webviewMessageHandler = async ( codebaseIndexEmbedderModelId: settings.codebaseIndexEmbedderModelId, codebaseIndexEmbedderModelDimension: settings.codebaseIndexEmbedderModelDimension, // Generic dimension codebaseIndexOpenAiCompatibleBaseUrl: settings.codebaseIndexOpenAiCompatibleBaseUrl, + codebaseIndexBedrockRegion: settings.codebaseIndexBedrockRegion, + codebaseIndexBedrockProfile: settings.codebaseIndexBedrockProfile, codebaseIndexSearchMaxResults: settings.codebaseIndexSearchMaxResults, codebaseIndexSearchMinScore: settings.codebaseIndexSearchMinScore, } diff --git a/src/i18n/locales/ca/embeddings.json b/src/i18n/locales/ca/embeddings.json index c00e336ee55..21a4a27ab41 100644 --- a/src/i18n/locales/ca/embeddings.json +++ b/src/i18n/locales/ca/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "No s'han pogut crear les incrustacions després de {{attempts}} intents", "textExceedsTokenLimit": "El text a l'índex {{index}} supera el límit màxim de testimonis ({{itemTokens}} > {{maxTokens}}). S'està ometent.", "rateLimitRetry": "S'ha assolit el límit de velocitat, es torna a intentar en {{delayMs}}ms (intent {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Format de resposta no vàlid d'Amazon Bedrock", + "invalidCredentials": "Credencials d'AWS no vàlides. Si us plau, comprova la teva configuració d'AWS.", + "accessDenied": "Accés denegat al servei d'Amazon Bedrock. Si us plau, comprova els teus permisos d'IAM.", + "modelNotFound": "Model {{model}} no trobat a Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "No s'ha pogut llegir el cos de l'error", "requestFailed": "La sol·licitud de l'API d'Ollama ha fallat amb l'estat {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Falta la configuració de Mistral per crear l'embedder", "openRouterConfigMissing": "Falta la configuració d'OpenRouter per crear l'embedder", "vercelAiGatewayConfigMissing": "Falta la configuració de Vercel AI Gateway per crear l'embedder", + "bedrockConfigMissing": "Falta la configuració d'Amazon Bedrock per crear l'embedder", "invalidEmbedderType": "Tipus d'embedder configurat no vàlid: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "No s'ha pogut determinar la dimensió del vector per al model '{{modelId}}' amb el proveïdor '{{provider}}'. Assegura't que la 'Dimensió d'incrustació' estigui configurada correctament als paràmetres del proveïdor compatible amb OpenAI.", "vectorDimensionNotDetermined": "No s'ha pogut determinar la dimensió del vector per al model '{{modelId}}' amb el proveïdor '{{provider}}'. Comprova els perfils del model o la configuració.", diff --git a/src/i18n/locales/de/embeddings.json b/src/i18n/locales/de/embeddings.json index e0c50e0a3d6..0297ec03091 100644 --- a/src/i18n/locales/de/embeddings.json +++ b/src/i18n/locales/de/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Erstellung von Einbettungen nach {{attempts}} Versuchen fehlgeschlagen", "textExceedsTokenLimit": "Text bei Index {{index}} überschreitet das maximale Token-Limit ({{itemTokens}} > {{maxTokens}}). Wird übersprungen.", "rateLimitRetry": "Ratenlimit erreicht, Wiederholung in {{delayMs}}ms (Versuch {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Ungültiges Antwortformat von Amazon Bedrock", + "invalidCredentials": "Ungültige AWS-Anmeldedaten. Bitte überprüfe deine AWS-Konfiguration.", + "accessDenied": "Zugriff auf den Amazon Bedrock-Dienst verweigert. Bitte überprüfe deine IAM-Berechtigungen.", + "modelNotFound": "Modell {{model}} in Amazon Bedrock nicht gefunden" + }, "ollama": { "couldNotReadErrorBody": "Fehlerinhalt konnte nicht gelesen werden", "requestFailed": "Ollama API-Anfrage fehlgeschlagen mit Status {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Mistral-Konfiguration fehlt für die Erstellung des Embedders", "openRouterConfigMissing": "OpenRouter-Konfiguration fehlt für die Erstellung des Embedders", "vercelAiGatewayConfigMissing": "Vercel AI Gateway-Konfiguration fehlt für die Erstellung des Embedders", + "bedrockConfigMissing": "Amazon Bedrock-Konfiguration fehlt für die Erstellung des Embedders", "invalidEmbedderType": "Ungültiger Embedder-Typ konfiguriert: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Konnte die Vektordimension für Modell '{{modelId}}' mit Anbieter '{{provider}}' nicht bestimmen. Stelle sicher, dass die 'Embedding-Dimension' in den OpenAI-kompatiblen Anbietereinstellungen korrekt eingestellt ist.", "vectorDimensionNotDetermined": "Konnte die Vektordimension für Modell '{{modelId}}' mit Anbieter '{{provider}}' nicht bestimmen. Überprüfe die Modellprofile oder Konfiguration.", diff --git a/src/i18n/locales/en/embeddings.json b/src/i18n/locales/en/embeddings.json index 5cf0322584f..5819e45c1a8 100644 --- a/src/i18n/locales/en/embeddings.json +++ b/src/i18n/locales/en/embeddings.json @@ -17,6 +17,12 @@ "modelNotEmbeddingCapable": "Ollama model is not embedding capable: {{modelId}}", "hostNotFound": "Ollama host not found: {{baseUrl}}" }, + "bedrock": { + "invalidResponseFormat": "Invalid response format from Amazon Bedrock", + "invalidCredentials": "Invalid AWS credentials. Please check your AWS configuration.", + "accessDenied": "Access denied to Amazon Bedrock service. Please check your IAM permissions.", + "modelNotFound": "Model {{model}} not found in Amazon Bedrock" + }, "scanner": { "unknownErrorProcessingFile": "Unknown error processing file {{filePath}}", "unknownErrorDeletingPoints": "Unknown error deleting points for {{filePath}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Mistral configuration missing for embedder creation", "openRouterConfigMissing": "OpenRouter configuration missing for embedder creation", "vercelAiGatewayConfigMissing": "Vercel AI Gateway configuration missing for embedder creation", + "bedrockConfigMissing": "Amazon Bedrock configuration missing for embedder creation", "invalidEmbedderType": "Invalid embedder type configured: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Could not determine vector dimension for model '{{modelId}}' with provider '{{provider}}'. Please ensure the 'Embedding Dimension' is correctly set in the OpenAI-Compatible provider settings.", "vectorDimensionNotDetermined": "Could not determine vector dimension for model '{{modelId}}' with provider '{{provider}}'. Check model profiles or configuration.", diff --git a/src/i18n/locales/es/embeddings.json b/src/i18n/locales/es/embeddings.json index 76cd5cf53ad..eca9efcc075 100644 --- a/src/i18n/locales/es/embeddings.json +++ b/src/i18n/locales/es/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "No se pudieron crear las incrustaciones después de {{attempts}} intentos", "textExceedsTokenLimit": "El texto en el índice {{index}} supera el límite máximo de tokens ({{itemTokens}} > {{maxTokens}}). Omitiendo.", "rateLimitRetry": "Límite de velocidad alcanzado, reintentando en {{delayMs}}ms (intento {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Formato de respuesta no válido de Amazon Bedrock", + "invalidCredentials": "Credenciales de AWS no válidas. Por favor, verifica tu configuración de AWS.", + "accessDenied": "Acceso denegado al servicio de Amazon Bedrock. Por favor, verifica tus permisos de IAM.", + "modelNotFound": "Modelo {{model}} no encontrado en Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "No se pudo leer el cuerpo del error", "requestFailed": "La solicitud de la API de Ollama falló con estado {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Falta la configuración de Mistral para la creación del incrustador", "openRouterConfigMissing": "Falta la configuración de OpenRouter para la creación del incrustador", "vercelAiGatewayConfigMissing": "Falta la configuración de Vercel AI Gateway para la creación del incrustador", + "bedrockConfigMissing": "Falta la configuración de Amazon Bedrock para la creación del incrustador", "invalidEmbedderType": "Tipo de incrustador configurado inválido: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "No se pudo determinar la dimensión del vector para el modelo '{{modelId}}' con el proveedor '{{provider}}'. Asegúrate de que la 'Dimensión de incrustación' esté configurada correctamente en los ajustes del proveedor compatible con OpenAI.", "vectorDimensionNotDetermined": "No se pudo determinar la dimensión del vector para el modelo '{{modelId}}' con el proveedor '{{provider}}'. Verifica los perfiles del modelo o la configuración.", diff --git a/src/i18n/locales/fr/embeddings.json b/src/i18n/locales/fr/embeddings.json index 8bb97735a85..fa922179870 100644 --- a/src/i18n/locales/fr/embeddings.json +++ b/src/i18n/locales/fr/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Échec de la création des embeddings après {{attempts}} tentatives", "textExceedsTokenLimit": "Le texte à l'index {{index}} dépasse la limite maximale de tokens ({{itemTokens}} > {{maxTokens}}). Ignoré.", "rateLimitRetry": "Limite de débit atteinte, nouvelle tentative dans {{delayMs}}ms (tentative {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Format de réponse invalide d'Amazon Bedrock", + "invalidCredentials": "Identifiants AWS invalides. Veuillez vérifier votre configuration AWS.", + "accessDenied": "Accès refusé au service Amazon Bedrock. Veuillez vérifier vos permissions IAM.", + "modelNotFound": "Modèle {{model}} introuvable dans Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Impossible de lire le corps de l'erreur", "requestFailed": "Échec de la requête API Ollama avec le statut {{status}} {{statusText}} : {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Configuration Mistral manquante pour la création de l'embedder", "openRouterConfigMissing": "Configuration OpenRouter manquante pour la création de l'embedder", "vercelAiGatewayConfigMissing": "Configuration Vercel AI Gateway manquante pour la création de l'embedder", + "bedrockConfigMissing": "Configuration Amazon Bedrock manquante pour la création de l'embedder", "invalidEmbedderType": "Type d'embedder configuré invalide : {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Impossible de déterminer la dimension du vecteur pour le modèle '{{modelId}}' avec le fournisseur '{{provider}}'. Assure-toi que la 'Dimension d'embedding' est correctement définie dans les paramètres du fournisseur compatible OpenAI.", "vectorDimensionNotDetermined": "Impossible de déterminer la dimension du vecteur pour le modèle '{{modelId}}' avec le fournisseur '{{provider}}'. Vérifie les profils du modèle ou la configuration.", diff --git a/src/i18n/locales/hi/embeddings.json b/src/i18n/locales/hi/embeddings.json index 26f9326e302..eb7f066c56f 100644 --- a/src/i18n/locales/hi/embeddings.json +++ b/src/i18n/locales/hi/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "{{attempts}} प्रयासों के बाद एम्बेडिंग बनाने में विफल", "textExceedsTokenLimit": "अनुक्रमणिका {{index}} पर पाठ अधिकतम टोकन सीमा ({{itemTokens}} > {{maxTokens}}) से अधिक है। छोड़ा जा रहा है।", "rateLimitRetry": "दर सीमा समाप्त, {{delayMs}}ms में पुन: प्रयास किया जा रहा है (प्रयास {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Amazon Bedrock से अमान्य प्रतिसाद प्रारूप", + "invalidCredentials": "अमान्य AWS क्रेडेंशियल्स। कृपया अपनी AWS कॉन्फ़िगरेशन जांचें।", + "accessDenied": "Amazon Bedrock सेवा तक पहुंच अस्वीकृत। कृपया अपनी IAM अनुमतियां जांचें।", + "modelNotFound": "मॉडल {{model}} Amazon Bedrock में नहीं मिला" + }, "ollama": { "couldNotReadErrorBody": "त्रुटि सामग्री पढ़ नहीं सका", "requestFailed": "Ollama API अनुरोध स्थिति {{status}} {{statusText}} के साथ विफल: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "एम्बेडर निर्माण के लिए मिस्ट्रल कॉन्फ़िगरेशन गायब है", "openRouterConfigMissing": "एम्बेडर निर्माण के लिए OpenRouter कॉन्फ़िगरेशन गायब है", "vercelAiGatewayConfigMissing": "एम्बेडर निर्माण के लिए Vercel AI Gateway कॉन्फ़िगरेशन गायब है", + "bedrockConfigMissing": "एम्बेडर निर्माण के लिए Amazon Bedrock कॉन्फ़िगरेशन गायब है", "invalidEmbedderType": "अमान्य एम्बेडर प्रकार कॉन्फ़िगर किया गया: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "प्रदाता '{{provider}}' के साथ मॉडल '{{modelId}}' के लिए वेक्टर आयाम निर्धारित नहीं कर सका। कृपया सुनिश्चित करें कि OpenAI-संगत प्रदाता सेटिंग्स में 'एम्बेडिंग आयाम' सही तरीके से सेट है।", "vectorDimensionNotDetermined": "प्रदाता '{{provider}}' के साथ मॉडल '{{modelId}}' के लिए वेक्टर आयाम निर्धारित नहीं कर सका। मॉडल प्रोफ़ाइल या कॉन्फ़िगरेशन की जांच करें।", diff --git a/src/i18n/locales/id/embeddings.json b/src/i18n/locales/id/embeddings.json index b7cbf968514..cceb965430e 100644 --- a/src/i18n/locales/id/embeddings.json +++ b/src/i18n/locales/id/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Gagal membuat embeddings setelah {{attempts}} percobaan", "textExceedsTokenLimit": "Teks pada indeks {{index}} melebihi batas maksimum token ({{itemTokens}} > {{maxTokens}}). Dilewati.", "rateLimitRetry": "Batas rate tercapai, mencoba lagi dalam {{delayMs}}ms (percobaan {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Format respons tidak valid dari Amazon Bedrock", + "invalidCredentials": "Kredensial AWS tidak valid. Harap periksa konfigurasi AWS Anda.", + "accessDenied": "Akses ditolak ke layanan Amazon Bedrock. Harap periksa izin IAM Anda.", + "modelNotFound": "Model {{model}} tidak ditemukan di Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Tidak dapat membaca body error", "requestFailed": "Permintaan API Ollama gagal dengan status {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Konfigurasi Mistral hilang untuk pembuatan embedder", "openRouterConfigMissing": "Konfigurasi OpenRouter hilang untuk pembuatan embedder", "vercelAiGatewayConfigMissing": "Konfigurasi Vercel AI Gateway hilang untuk pembuatan embedder", + "bedrockConfigMissing": "Konfigurasi Amazon Bedrock hilang untuk pembuatan embedder", "invalidEmbedderType": "Tipe embedder yang dikonfigurasi tidak valid: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Tidak dapat menentukan dimensi vektor untuk model '{{modelId}}' dengan penyedia '{{provider}}'. Pastikan 'Dimensi Embedding' diatur dengan benar di pengaturan penyedia yang kompatibel dengan OpenAI.", "vectorDimensionNotDetermined": "Tidak dapat menentukan dimensi vektor untuk model '{{modelId}}' dengan penyedia '{{provider}}'. Periksa profil model atau konfigurasi.", diff --git a/src/i18n/locales/it/embeddings.json b/src/i18n/locales/it/embeddings.json index 220b902f2cb..2e339ef5d88 100644 --- a/src/i18n/locales/it/embeddings.json +++ b/src/i18n/locales/it/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Creazione degli embedding non riuscita dopo {{attempts}} tentativi", "textExceedsTokenLimit": "Il testo all'indice {{index}} supera il limite massimo di token ({{itemTokens}} > {{maxTokens}}). Saltato.", "rateLimitRetry": "Limite di velocità raggiunto, nuovo tentativo tra {{delayMs}}ms (tentativo {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Formato di risposta non valido da Amazon Bedrock", + "invalidCredentials": "Credenziali AWS non valide. Si prega di verificare la configurazione AWS.", + "accessDenied": "Accesso negato al servizio Amazon Bedrock. Si prega di verificare le autorizzazioni IAM.", + "modelNotFound": "Modello {{model}} non trovato in Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Impossibile leggere il corpo dell'errore", "requestFailed": "Richiesta API Ollama fallita con stato {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Configurazione di Mistral mancante per la creazione dell'embedder", "openRouterConfigMissing": "Configurazione di OpenRouter mancante per la creazione dell'embedder", "vercelAiGatewayConfigMissing": "Configurazione di Vercel AI Gateway mancante per la creazione dell'embedder", + "bedrockConfigMissing": "Configurazione di Amazon Bedrock mancante per la creazione dell'embedder", "invalidEmbedderType": "Tipo di embedder configurato non valido: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Impossibile determinare la dimensione del vettore per il modello '{{modelId}}' con il provider '{{provider}}'. Assicurati che la 'Dimensione di embedding' sia impostata correttamente nelle impostazioni del provider compatibile con OpenAI.", "vectorDimensionNotDetermined": "Impossibile determinare la dimensione del vettore per il modello '{{modelId}}' con il provider '{{provider}}'. Controlla i profili del modello o la configurazione.", diff --git a/src/i18n/locales/ja/embeddings.json b/src/i18n/locales/ja/embeddings.json index e74fef4138e..5223c204e0d 100644 --- a/src/i18n/locales/ja/embeddings.json +++ b/src/i18n/locales/ja/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "{{attempts}}回試行しましたが、埋め込みの作成に失敗しました", "textExceedsTokenLimit": "インデックス{{index}}のテキストが最大トークン制限を超えています({{itemTokens}}> {{maxTokens}})。スキップします。", "rateLimitRetry": "レート制限に達しました。{{delayMs}}ミリ秒後に再試行します(試行{{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Amazon Bedrockからの無効な応答形式", + "invalidCredentials": "無効なAWS認証情報です。AWSの設定を確認してください。", + "accessDenied": "Amazon Bedrockサービスへのアクセスが拒否されました。IAMの権限を確認してください。", + "modelNotFound": "モデル{{model}}がAmazon Bedrockに見つかりません" + }, "ollama": { "couldNotReadErrorBody": "エラー本文を読み取れませんでした", "requestFailed": "Ollama APIリクエストが失敗しました。ステータス {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "エンベッダー作成のためのMistral設定がありません", "openRouterConfigMissing": "エンベッダー作成のためのOpenRouter設定がありません", "vercelAiGatewayConfigMissing": "エンベッダー作成のためのVercel AI Gateway設定がありません", + "bedrockConfigMissing": "エンベッダー作成のためのAmazon Bedrock設定がありません", "invalidEmbedderType": "無効なエンベッダータイプが設定されています: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "プロバイダー '{{provider}}' のモデル '{{modelId}}' の埋め込み次元を決定できませんでした。OpenAI互換プロバイダー設定で「埋め込み次元」が正しく設定されていることを確認してください。", "vectorDimensionNotDetermined": "プロバイダー '{{provider}}' のモデル '{{modelId}}' の埋め込み次元を決定できませんでした。モデルプロファイルまたは設定を確認してください。", diff --git a/src/i18n/locales/ko/embeddings.json b/src/i18n/locales/ko/embeddings.json index 31c73fa5f26..236662eea22 100644 --- a/src/i18n/locales/ko/embeddings.json +++ b/src/i18n/locales/ko/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "{{attempts}}번 시도 후 임베딩 생성 실패", "textExceedsTokenLimit": "인덱스 {{index}}의 텍스트가 최대 토큰 제한({{itemTokens}} > {{maxTokens}})을 초과했습니다. 건너뜁니다.", "rateLimitRetry": "속도 제한에 도달했습니다. {{delayMs}}ms 후에 다시 시도합니다(시도 {{attempt}}/{{maxRetries}}).", + "bedrock": { + "invalidResponseFormat": "Amazon Bedrock에서 잘못된 응답 형식", + "invalidCredentials": "잘못된 AWS 자격증명입니다. AWS 구성을 확인하세요.", + "accessDenied": "Amazon Bedrock 서비스에 대한 액세스가 거부되었습니다. IAM 권한을 확인하세요.", + "modelNotFound": "Amazon Bedrock에서 모델 {{model}}을(를) 찾을 수 없습니다" + }, "ollama": { "couldNotReadErrorBody": "오류 본문을 읽을 수 없습니다", "requestFailed": "Ollama API 요청이 실패했습니다. 상태 {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "임베더 생성을 위한 Mistral 구성이 없습니다", "openRouterConfigMissing": "임베더 생성을 위한 OpenRouter 구성이 없습니다", "vercelAiGatewayConfigMissing": "임베더 생성을 위한 Vercel AI Gateway 구성이 없습니다", + "bedrockConfigMissing": "임베더 생성을 위한 Amazon Bedrock 구성이 없습니다", "invalidEmbedderType": "잘못된 임베더 유형이 구성되었습니다: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "프로바이더 '{{provider}}'의 모델 '{{modelId}}'에 대한 벡터 차원을 결정할 수 없습니다. OpenAI 호환 프로바이더 설정에서 '임베딩 차원'이 올바르게 설정되어 있는지 확인하세요.", "vectorDimensionNotDetermined": "프로바이더 '{{provider}}'의 모델 '{{modelId}}'에 대한 벡터 차원을 결정할 수 없습니다. 모델 프로필 또는 구성을 확인하세요.", diff --git a/src/i18n/locales/nl/embeddings.json b/src/i18n/locales/nl/embeddings.json index aa6d242f1f1..cce3f05c625 100644 --- a/src/i18n/locales/nl/embeddings.json +++ b/src/i18n/locales/nl/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Insluitingen maken mislukt na {{attempts}} pogingen", "textExceedsTokenLimit": "Tekst op index {{index}} overschrijdt de maximale tokenlimiet ({{itemTokens}} > {{maxTokens}}). Wordt overgeslagen.", "rateLimitRetry": "Snelheidslimiet bereikt, opnieuw proberen over {{delayMs}}ms (poging {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Ongeldig antwoordformaat van Amazon Bedrock", + "invalidCredentials": "Ongeldige AWS-referenties. Controleer uw AWS-configuratie.", + "accessDenied": "Toegang geweigerd tot Amazon Bedrock-service. Controleer uw IAM-machtigingen.", + "modelNotFound": "Model {{model}} niet gevonden in Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Kon foutinhoud niet lezen", "requestFailed": "Ollama API-verzoek mislukt met status {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Mistral-configuratie ontbreekt voor het maken van de embedder", "openRouterConfigMissing": "OpenRouter-configuratie ontbreekt voor het maken van de embedder", "vercelAiGatewayConfigMissing": "Vercel AI Gateway-configuratie ontbreekt voor het maken van de embedder", + "bedrockConfigMissing": "Amazon Bedrock-configuratie ontbreekt voor het maken van de embedder", "invalidEmbedderType": "Ongeldig embedder-type geconfigureerd: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Kan de vectordimensie voor model '{{modelId}}' met provider '{{provider}}' niet bepalen. Zorg ervoor dat de 'Embedding Dimensie' correct is ingesteld in de OpenAI-compatibele provider-instellingen.", "vectorDimensionNotDetermined": "Kan de vectordimensie voor model '{{modelId}}' met provider '{{provider}}' niet bepalen. Controleer modelprofielen of configuratie.", diff --git a/src/i18n/locales/pl/embeddings.json b/src/i18n/locales/pl/embeddings.json index 88543ede38c..133f9f40da2 100644 --- a/src/i18n/locales/pl/embeddings.json +++ b/src/i18n/locales/pl/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Nie udało się utworzyć osadzeń po {{attempts}} próbach", "textExceedsTokenLimit": "Tekst w indeksie {{index}} przekracza maksymalny limit tokenów ({{itemTokens}} > {{maxTokens}}). Pomijanie.", "rateLimitRetry": "Osiągnięto limit szybkości, ponawianie za {{delayMs}}ms (próba {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Nieprawidłowy format odpowiedzi z Amazon Bedrock", + "invalidCredentials": "Nieprawidłowe poświadczenia AWS. Sprawdź konfigurację AWS.", + "accessDenied": "Odmowa dostępu do usługi Amazon Bedrock. Sprawdź uprawnienia IAM.", + "modelNotFound": "Model {{model}} nie znaleziony w Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Nie można odczytać treści błędu", "requestFailed": "Żądanie API Ollama nie powiodło się ze statusem {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Brak konfiguracji Mistral do utworzenia embeddera", "openRouterConfigMissing": "Brak konfiguracji OpenRouter do utworzenia embeddera", "vercelAiGatewayConfigMissing": "Brak konfiguracji Vercel AI Gateway do utworzenia embeddera", + "bedrockConfigMissing": "Brak konfiguracji Amazon Bedrock do utworzenia embeddera", "invalidEmbedderType": "Skonfigurowano nieprawidłowy typ embeddera: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Nie można określić wymiaru wektora dla modelu '{{modelId}}' z dostawcą '{{provider}}'. Upewnij się, że 'Wymiar osadzania' jest poprawnie ustawiony w ustawieniach dostawcy kompatybilnego z OpenAI.", "vectorDimensionNotDetermined": "Nie można określić wymiaru wektora dla modelu '{{modelId}}' z dostawcą '{{provider}}'. Sprawdź profile modelu lub konfigurację.", diff --git a/src/i18n/locales/pt-BR/embeddings.json b/src/i18n/locales/pt-BR/embeddings.json index c67d0df686b..09f4a557874 100644 --- a/src/i18n/locales/pt-BR/embeddings.json +++ b/src/i18n/locales/pt-BR/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Falha ao criar embeddings após {{attempts}} tentativas", "textExceedsTokenLimit": "O texto no índice {{index}} excede o limite máximo de tokens ({{itemTokens}} > {{maxTokens}}). Ignorando.", "rateLimitRetry": "Limite de taxa atingido, tentando novamente em {{delayMs}}ms (tentativa {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Formato de resposta inválido do Amazon Bedrock", + "invalidCredentials": "Credenciais AWS inválidas. Verifique sua configuração AWS.", + "accessDenied": "Acesso negado ao serviço Amazon Bedrock. Verifique suas permissões IAM.", + "modelNotFound": "Modelo {{model}} não encontrado no Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Não foi possível ler o corpo do erro", "requestFailed": "Solicitação da API Ollama falhou com status {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Configuração do Mistral ausente para a criação do embedder", "openRouterConfigMissing": "Configuração do OpenRouter ausente para a criação do embedder", "vercelAiGatewayConfigMissing": "Configuração do Vercel AI Gateway ausente para a criação do embedder", + "bedrockConfigMissing": "Configuração do Amazon Bedrock ausente para a criação do embedder", "invalidEmbedderType": "Tipo de embedder configurado inválido: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Não foi possível determinar a dimensão do vetor para o modelo '{{modelId}}' com o provedor '{{provider}}'. Certifique-se de que a 'Dimensão de Embedding' esteja configurada corretamente nas configurações do provedor compatível com OpenAI.", "vectorDimensionNotDetermined": "Não foi possível determinar a dimensão do vetor para o modelo '{{modelId}}' com o provedor '{{provider}}'. Verifique os perfis do modelo ou a configuração.", diff --git a/src/i18n/locales/ru/embeddings.json b/src/i18n/locales/ru/embeddings.json index 7e48af3d59e..9e94082bbfb 100644 --- a/src/i18n/locales/ru/embeddings.json +++ b/src/i18n/locales/ru/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Не удалось создать вложения после {{attempts}} попыток", "textExceedsTokenLimit": "Текст в индексе {{index}} превышает максимальный лимит токенов ({{itemTokens}} > {{maxTokens}}). Пропускается.", "rateLimitRetry": "Достигнут лимит скорости, повторная попытка через {{delayMs}} мс (попытка {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Неверный формат ответа от Amazon Bedrock", + "invalidCredentials": "Неверные учетные данные AWS. Проверьте конфигурацию AWS.", + "accessDenied": "Доступ запрещен к сервису Amazon Bedrock. Проверьте разрешения IAM.", + "modelNotFound": "Модель {{model}} не найдена в Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Не удалось прочитать тело ошибки", "requestFailed": "Запрос к API Ollama не удался со статусом {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Конфигурация Mistral отсутствует для создания эмбеддера", "openRouterConfigMissing": "Конфигурация OpenRouter отсутствует для создания эмбеддера", "vercelAiGatewayConfigMissing": "Конфигурация Vercel AI Gateway отсутствует для создания эмбеддера", + "bedrockConfigMissing": "Конфигурация Amazon Bedrock отсутствует для создания эмбеддера", "invalidEmbedderType": "Настроен недопустимый тип эмбеддера: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Не удалось определить размерность вектора для модели '{{modelId}}' с провайдером '{{provider}}'. Убедитесь, что 'Размерность эмбеддинга' правильно установлена в настройках провайдера, совместимого с OpenAI.", "vectorDimensionNotDetermined": "Не удалось определить размерность вектора для модели '{{modelId}}' с провайдером '{{provider}}'. Проверьте профили модели или конфигурацию.", diff --git a/src/i18n/locales/tr/embeddings.json b/src/i18n/locales/tr/embeddings.json index 36efc466e3d..411ed7ab526 100644 --- a/src/i18n/locales/tr/embeddings.json +++ b/src/i18n/locales/tr/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "{{attempts}} denemeden sonra gömülmeler oluşturulamadı", "textExceedsTokenLimit": "{{index}} dizinindeki metin maksimum jeton sınırını aşıyor ({{itemTokens}} > {{maxTokens}}). Atlanıyor.", "rateLimitRetry": "Hız sınırına ulaşıldı, {{delayMs}}ms içinde yeniden deneniyor (deneme {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Amazon Bedrock'tan geçersiz yanıt formatı", + "invalidCredentials": "Geçersiz AWS kimlik bilgileri. Lütfen AWS yapılandırmanızı kontrol edin.", + "accessDenied": "Amazon Bedrock hizmetine erişim reddedildi. Lütfen IAM izinlerinizi kontrol edin.", + "modelNotFound": "Model {{model}} Amazon Bedrock'ta bulunamadı" + }, "ollama": { "couldNotReadErrorBody": "Hata gövdesi okunamadı", "requestFailed": "Ollama API isteği {{status}} {{statusText}} durumuyla başarısız oldu: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Gömücü oluşturmak için Mistral yapılandırması eksik", "openRouterConfigMissing": "Gömücü oluşturmak için OpenRouter yapılandırması eksik", "vercelAiGatewayConfigMissing": "Gömücü oluşturmak için Vercel AI Gateway yapılandırması eksik", + "bedrockConfigMissing": "Gömücü oluşturmak için Amazon Bedrock yapılandırması eksik", "invalidEmbedderType": "Geçersiz gömücü türü yapılandırıldı: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "'{{provider}}' sağlayıcısı ile '{{modelId}}' modeli için vektör boyutu belirlenemedi. OpenAI uyumlu sağlayıcı ayarlarında 'Gömme Boyutu'nun doğru ayarlandığından emin ol.", "vectorDimensionNotDetermined": "'{{provider}}' sağlayıcısı ile '{{modelId}}' modeli için vektör boyutu belirlenemedi. Model profillerini veya yapılandırmayı kontrol et.", diff --git a/src/i18n/locales/vi/embeddings.json b/src/i18n/locales/vi/embeddings.json index 96496083caa..c9f9880df08 100644 --- a/src/i18n/locales/vi/embeddings.json +++ b/src/i18n/locales/vi/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "Không thể tạo nhúng sau {{attempts}} lần thử", "textExceedsTokenLimit": "Văn bản tại chỉ mục {{index}} vượt quá giới hạn mã thông báo tối đa ({{itemTokens}} > {{maxTokens}}). Bỏ qua.", "rateLimitRetry": "Đã đạt đến giới hạn tốc độ, thử lại sau {{delayMs}}ms (lần thử {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Định dạng phản hồi không hợp lệ từ Amazon Bedrock", + "invalidCredentials": "Thông tin đăng nhập AWS không hợp lệ. Vui lòng kiểm tra cấu hình AWS của bạn.", + "accessDenied": "Bị từ chối truy cập dịch vụ Amazon Bedrock. Vui lòng kiểm tra quyền IAM của bạn.", + "modelNotFound": "Không tìm thấy mô hình {{model}} trong Amazon Bedrock" + }, "ollama": { "couldNotReadErrorBody": "Không thể đọc nội dung lỗi", "requestFailed": "Yêu cầu API Ollama thất bại với trạng thái {{status}} {{statusText}}: {{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "Thiếu cấu hình Mistral để tạo trình nhúng", "openRouterConfigMissing": "Thiếu cấu hình OpenRouter để tạo trình nhúng", "vercelAiGatewayConfigMissing": "Thiếu cấu hình Vercel AI Gateway để tạo trình nhúng", + "bedrockConfigMissing": "Thiếu cấu hình Amazon Bedrock để tạo trình nhúng", "invalidEmbedderType": "Loại embedder được cấu hình không hợp lệ: {{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "Không thể xác định kích thước vector cho mô hình '{{modelId}}' với nhà cung cấp '{{provider}}'. Hãy đảm bảo 'Kích thước Embedding' được cài đặt đúng trong cài đặt nhà cung cấp tương thích OpenAI.", "vectorDimensionNotDetermined": "Không thể xác định kích thước vector cho mô hình '{{modelId}}' với nhà cung cấp '{{provider}}'. Kiểm tra hồ sơ mô hình hoặc cấu hình.", diff --git a/src/i18n/locales/zh-CN/embeddings.json b/src/i18n/locales/zh-CN/embeddings.json index dfc591391e5..c27bc078015 100644 --- a/src/i18n/locales/zh-CN/embeddings.json +++ b/src/i18n/locales/zh-CN/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "尝试 {{attempts}} 次后创建嵌入失败", "textExceedsTokenLimit": "索引 {{index}} 处的文本超过最大令牌限制 ({{itemTokens}} > {{maxTokens}})。正在跳过。", "rateLimitRetry": "已达到速率限制,将在 {{delayMs}} 毫秒后重试(尝试次数 {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Amazon Bedrock 返回无效的响应格式", + "invalidCredentials": "AWS 凭证无效。请检查您的 AWS 配置。", + "accessDenied": "访问 Amazon Bedrock 服务被拒绝。请检查您的 IAM 权限。", + "modelNotFound": "在 Amazon Bedrock 中找不到模型 {{model}}" + }, "ollama": { "couldNotReadErrorBody": "无法读取错误内容", "requestFailed": "Ollama API 请求失败,状态码 {{status}} {{statusText}}:{{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "创建嵌入器时缺少 Mistral 配置", "openRouterConfigMissing": "创建嵌入器时缺少 OpenRouter 配置", "vercelAiGatewayConfigMissing": "创建嵌入器时缺少 Vercel AI Gateway 配置", + "bedrockConfigMissing": "创建嵌入器时缺少 Amazon Bedrock 配置", "invalidEmbedderType": "配置的嵌入器类型无效:{{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "无法确定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量维度。请确保在 OpenAI 兼容提供商设置中正确设置了「嵌入维度」。", "vectorDimensionNotDetermined": "无法确定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量维度。请检查模型配置文件或配置。", diff --git a/src/i18n/locales/zh-TW/embeddings.json b/src/i18n/locales/zh-TW/embeddings.json index 24ed5190967..744e7022ea1 100644 --- a/src/i18n/locales/zh-TW/embeddings.json +++ b/src/i18n/locales/zh-TW/embeddings.json @@ -6,6 +6,12 @@ "failedMaxAttempts": "嘗試 {{attempts}} 次後建立內嵌失敗", "textExceedsTokenLimit": "索引 {{index}} 處的文字超過最大權杖限制 ({{itemTokens}} > {{maxTokens}})。正在略過。", "rateLimitRetry": "已達到速率限制,將在 {{delayMs}} 毫秒後重試(嘗試次數 {{attempt}}/{{maxRetries}})", + "bedrock": { + "invalidResponseFormat": "Amazon Bedrock 傳回無效的回應格式", + "invalidCredentials": "AWS 認證無效。請檢查您的 AWS 設定。", + "accessDenied": "存取 Amazon Bedrock 服務遭拒。請檢查您的 IAM 權限。", + "modelNotFound": "在 Amazon Bedrock 中找不到模型 {{model}}" + }, "ollama": { "couldNotReadErrorBody": "無法讀取錯誤內容", "requestFailed": "Ollama API 請求失敗,狀態碼 {{status}} {{statusText}}:{{errorBody}}", @@ -49,6 +55,7 @@ "mistralConfigMissing": "建立嵌入器時缺少 Mistral 設定", "openRouterConfigMissing": "建立嵌入器時缺少 OpenRouter 設定", "vercelAiGatewayConfigMissing": "建立嵌入器時缺少 Vercel AI Gateway 設定", + "bedrockConfigMissing": "建立嵌入器時缺少 Amazon Bedrock 設定", "invalidEmbedderType": "設定的嵌入器類型無效:{{embedderProvider}}", "vectorDimensionNotDeterminedOpenAiCompatible": "無法確定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量維度。請確保在 OpenAI 相容提供商設定中正確設定了「嵌入維度」。", "vectorDimensionNotDetermined": "無法確定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量維度。請檢查模型設定檔或設定。", diff --git a/src/services/code-index/__tests__/config-manager.spec.ts b/src/services/code-index/__tests__/config-manager.spec.ts index 089e039ff8f..d3bec15920a 100644 --- a/src/services/code-index/__tests__/config-manager.spec.ts +++ b/src/services/code-index/__tests__/config-manager.spec.ts @@ -104,6 +104,7 @@ describe("CodeIndexConfigManager", () => { modelId: undefined, openAiOptions: { openAiNativeApiKey: "" }, ollamaOptions: { ollamaBaseUrl: "" }, + bedrockOptions: { region: "us-east-1", profile: undefined }, qdrantUrl: "http://localhost:6333", qdrantApiKey: "", searchMinScore: 0.4, @@ -129,7 +130,7 @@ describe("CodeIndexConfigManager", () => { const result = await configManager.loadConfiguration() - expect(result.currentConfig).toEqual({ + expect(result.currentConfig).toMatchObject({ isConfigured: true, embedderProvider: "openai", modelId: "text-embedding-3-large", @@ -162,7 +163,7 @@ describe("CodeIndexConfigManager", () => { const result = await configManager.loadConfiguration() - expect(result.currentConfig).toEqual({ + expect(result.currentConfig).toMatchObject({ isConfigured: true, embedderProvider: "openai-compatible", modelId: "text-embedding-3-large", @@ -199,7 +200,7 @@ describe("CodeIndexConfigManager", () => { const result = await configManager.loadConfiguration() - expect(result.currentConfig).toEqual({ + expect(result.currentConfig).toMatchObject({ isConfigured: true, embedderProvider: "openai-compatible", modelId: "custom-model", @@ -237,7 +238,7 @@ describe("CodeIndexConfigManager", () => { const result = await configManager.loadConfiguration() - expect(result.currentConfig).toEqual({ + expect(result.currentConfig).toMatchObject({ isConfigured: true, embedderProvider: "openai-compatible", modelId: "custom-model", @@ -275,7 +276,7 @@ describe("CodeIndexConfigManager", () => { const result = await configManager.loadConfiguration() - expect(result.currentConfig).toEqual({ + expect(result.currentConfig).toMatchObject({ isConfigured: true, embedderProvider: "openai-compatible", modelId: "custom-model", @@ -1286,7 +1287,7 @@ describe("CodeIndexConfigManager", () => { it("should return correct configuration via getConfig", () => { const config = configManager.getConfig() - expect(config).toEqual({ + expect(config).toMatchObject({ isConfigured: true, embedderProvider: "openai", modelId: "text-embedding-3-large", diff --git a/src/services/code-index/config-manager.ts b/src/services/code-index/config-manager.ts index 5bc00b6ce35..7de0d8d3de4 100644 --- a/src/services/code-index/config-manager.ts +++ b/src/services/code-index/config-manager.ts @@ -20,6 +20,7 @@ export class CodeIndexConfigManager { private geminiOptions?: { apiKey: string } private mistralOptions?: { apiKey: string } private vercelAiGatewayOptions?: { apiKey: string } + private bedrockOptions?: { region: string; profile?: string } private openRouterOptions?: { apiKey: string } private qdrantUrl?: string = "http://localhost:6333" private qdrantApiKey?: string @@ -52,6 +53,8 @@ export class CodeIndexConfigManager { codebaseIndexEmbedderModelId: "", codebaseIndexSearchMinScore: undefined, codebaseIndexSearchMaxResults: undefined, + codebaseIndexBedrockRegion: "us-east-1", + codebaseIndexBedrockProfile: "", } const { @@ -72,6 +75,8 @@ export class CodeIndexConfigManager { const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? "" const mistralApiKey = this.contextProxy?.getSecret("codebaseIndexMistralApiKey") ?? "" const vercelAiGatewayApiKey = this.contextProxy?.getSecret("codebaseIndexVercelAiGatewayApiKey") ?? "" + const bedrockRegion = codebaseIndexConfig.codebaseIndexBedrockRegion ?? "us-east-1" + const bedrockProfile = codebaseIndexConfig.codebaseIndexBedrockProfile ?? "" const openRouterApiKey = this.contextProxy?.getSecret("codebaseIndexOpenRouterApiKey") ?? "" // Update instance variables with configuration @@ -110,6 +115,8 @@ export class CodeIndexConfigManager { this.embedderProvider = "mistral" } else if (codebaseIndexEmbedderProvider === "vercel-ai-gateway") { this.embedderProvider = "vercel-ai-gateway" + } else if ((codebaseIndexEmbedderProvider as string) === "bedrock") { + this.embedderProvider = "bedrock" } else if (codebaseIndexEmbedderProvider === "openrouter") { this.embedderProvider = "openrouter" } else { @@ -134,6 +141,10 @@ export class CodeIndexConfigManager { this.mistralOptions = mistralApiKey ? { apiKey: mistralApiKey } : undefined this.vercelAiGatewayOptions = vercelAiGatewayApiKey ? { apiKey: vercelAiGatewayApiKey } : undefined this.openRouterOptions = openRouterApiKey ? { apiKey: openRouterApiKey } : undefined + // Set bedrockOptions if region is provided (profile is optional) + this.bedrockOptions = bedrockRegion + ? { region: bedrockRegion, profile: bedrockProfile || undefined } + : undefined } /** @@ -152,6 +163,7 @@ export class CodeIndexConfigManager { geminiOptions?: { apiKey: string } mistralOptions?: { apiKey: string } vercelAiGatewayOptions?: { apiKey: string } + bedrockOptions?: { region: string; profile?: string } openRouterOptions?: { apiKey: string } qdrantUrl?: string qdrantApiKey?: string @@ -173,6 +185,8 @@ export class CodeIndexConfigManager { geminiApiKey: this.geminiOptions?.apiKey ?? "", mistralApiKey: this.mistralOptions?.apiKey ?? "", vercelAiGatewayApiKey: this.vercelAiGatewayOptions?.apiKey ?? "", + bedrockRegion: this.bedrockOptions?.region ?? "", + bedrockProfile: this.bedrockOptions?.profile ?? "", openRouterApiKey: this.openRouterOptions?.apiKey ?? "", qdrantUrl: this.qdrantUrl ?? "", qdrantApiKey: this.qdrantApiKey ?? "", @@ -199,6 +213,7 @@ export class CodeIndexConfigManager { geminiOptions: this.geminiOptions, mistralOptions: this.mistralOptions, vercelAiGatewayOptions: this.vercelAiGatewayOptions, + bedrockOptions: this.bedrockOptions, openRouterOptions: this.openRouterOptions, qdrantUrl: this.qdrantUrl, qdrantApiKey: this.qdrantApiKey, @@ -242,6 +257,12 @@ export class CodeIndexConfigManager { const qdrantUrl = this.qdrantUrl const isConfigured = !!(apiKey && qdrantUrl) return isConfigured + } else if (this.embedderProvider === "bedrock") { + // Only region is required for Bedrock (profile is optional) + const region = this.bedrockOptions?.region + const qdrantUrl = this.qdrantUrl + const isConfigured = !!(region && qdrantUrl) + return isConfigured } else if (this.embedderProvider === "openrouter") { const apiKey = this.openRouterOptions?.apiKey const qdrantUrl = this.qdrantUrl @@ -282,6 +303,8 @@ export class CodeIndexConfigManager { const prevGeminiApiKey = prev?.geminiApiKey ?? "" const prevMistralApiKey = prev?.mistralApiKey ?? "" const prevVercelAiGatewayApiKey = prev?.vercelAiGatewayApiKey ?? "" + const prevBedrockRegion = prev?.bedrockRegion ?? "" + const prevBedrockProfile = prev?.bedrockProfile ?? "" const prevOpenRouterApiKey = prev?.openRouterApiKey ?? "" const prevQdrantUrl = prev?.qdrantUrl ?? "" const prevQdrantApiKey = prev?.qdrantApiKey ?? "" @@ -321,6 +344,8 @@ export class CodeIndexConfigManager { const currentGeminiApiKey = this.geminiOptions?.apiKey ?? "" const currentMistralApiKey = this.mistralOptions?.apiKey ?? "" const currentVercelAiGatewayApiKey = this.vercelAiGatewayOptions?.apiKey ?? "" + const currentBedrockRegion = this.bedrockOptions?.region ?? "" + const currentBedrockProfile = this.bedrockOptions?.profile ?? "" const currentOpenRouterApiKey = this.openRouterOptions?.apiKey ?? "" const currentQdrantUrl = this.qdrantUrl ?? "" const currentQdrantApiKey = this.qdrantApiKey ?? "" @@ -352,6 +377,10 @@ export class CodeIndexConfigManager { return true } + if (prevBedrockRegion !== currentBedrockRegion || prevBedrockProfile !== currentBedrockProfile) { + return true + } + if (prevOpenRouterApiKey !== currentOpenRouterApiKey) { return true } @@ -414,6 +443,7 @@ export class CodeIndexConfigManager { geminiOptions: this.geminiOptions, mistralOptions: this.mistralOptions, vercelAiGatewayOptions: this.vercelAiGatewayOptions, + bedrockOptions: this.bedrockOptions, openRouterOptions: this.openRouterOptions, qdrantUrl: this.qdrantUrl, qdrantApiKey: this.qdrantApiKey, diff --git a/src/services/code-index/embedders/__tests__/bedrock.spec.ts b/src/services/code-index/embedders/__tests__/bedrock.spec.ts new file mode 100644 index 00000000000..de4b3b15bb6 --- /dev/null +++ b/src/services/code-index/embedders/__tests__/bedrock.spec.ts @@ -0,0 +1,656 @@ +import type { MockedFunction } from "vitest" +import { BedrockRuntimeClient, InvokeModelCommand } from "@aws-sdk/client-bedrock-runtime" + +import { BedrockEmbedder } from "../bedrock" +import { MAX_ITEM_TOKENS, INITIAL_RETRY_DELAY_MS } from "../../constants" + +// Mock the AWS SDK +vitest.mock("@aws-sdk/client-bedrock-runtime", () => { + return { + BedrockRuntimeClient: vitest.fn().mockImplementation(() => ({ + send: vitest.fn(), + })), + InvokeModelCommand: vitest.fn().mockImplementation((input) => ({ + input, + })), + } +}) +vitest.mock("@aws-sdk/credential-providers", () => ({ + fromEnv: vitest.fn().mockReturnValue(Promise.resolve({})), + fromIni: vitest.fn().mockReturnValue(Promise.resolve({})), +})) + +// Mock TelemetryService +vitest.mock("@roo-code/telemetry", () => ({ + TelemetryService: { + instance: { + captureEvent: vitest.fn(), + }, + }, +})) + +// Mock i18n +vitest.mock("../../../../i18n", () => ({ + t: (key: string, params?: Record) => { + const translations: Record = { + "embeddings:authenticationFailed": + "Failed to create embeddings: Authentication failed. Please check your AWS credentials.", + "embeddings:failedWithStatus": `Failed to create embeddings after ${params?.attempts} attempts: HTTP ${params?.statusCode} - ${params?.errorMessage}`, + "embeddings:failedWithError": `Failed to create embeddings after ${params?.attempts} attempts: ${params?.errorMessage}`, + "embeddings:failedMaxAttempts": `Failed to create embeddings after ${params?.attempts} attempts`, + "embeddings:textExceedsTokenLimit": `Text at index ${params?.index} exceeds maximum token limit (${params?.itemTokens} > ${params?.maxTokens}). Skipping.`, + "embeddings:rateLimitRetry": `Rate limit hit, retrying in ${params?.delayMs}ms (attempt ${params?.attempt}/${params?.maxRetries})`, + "embeddings:bedrock.invalidResponseFormat": "Invalid response format from Bedrock", + "embeddings:bedrock.invalidCredentials": "Invalid AWS credentials", + "embeddings:bedrock.accessDenied": "Access denied to Bedrock service", + "embeddings:bedrock.modelNotFound": `Model ${params?.model} not found`, + "embeddings:validation.authenticationFailed": "Authentication failed", + "embeddings:validation.connectionFailed": "Connection failed", + "embeddings:validation.serviceUnavailable": "Service unavailable", + "embeddings:validation.configurationError": "Configuration error", + } + return translations[key] || key + }, +})) + +// Mock console methods +const consoleMocks = { + error: vitest.spyOn(console, "error").mockImplementation(() => {}), + warn: vitest.spyOn(console, "warn").mockImplementation(() => {}), +} + +describe("BedrockEmbedder", () => { + let embedder: BedrockEmbedder + let mockSend: MockedFunction + + beforeEach(() => { + vitest.clearAllMocks() + consoleMocks.error.mockClear() + consoleMocks.warn.mockClear() + + mockSend = vitest.fn() + + // Set up the mock implementation + const MockedBedrockRuntimeClient = BedrockRuntimeClient as any + MockedBedrockRuntimeClient.mockImplementation(() => ({ + send: mockSend, + })) + + embedder = new BedrockEmbedder("us-east-1", "test-profile", "amazon.titan-embed-text-v2:0") + }) + + afterEach(() => { + vitest.clearAllMocks() + }) + + describe("constructor", () => { + it("should initialize with provided region, profile and model", () => { + expect(embedder.embedderInfo.name).toBe("bedrock") + }) + + it("should require region", () => { + expect(() => new BedrockEmbedder("", "profile", "model")).toThrow( + "Region is required for AWS Bedrock embedder", + ) + }) + + it("should use profile for credentials", () => { + const profileEmbedder = new BedrockEmbedder("us-west-2", "dev-profile") + expect(profileEmbedder).toBeDefined() + }) + }) + + describe("createEmbeddings", () => { + const testModelId = "amazon.titan-embed-text-v2:0" + + it("should create embeddings for a single text with Titan model", async () => { + const testTexts = ["Hello world"] + const mockResponse = { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.1, 0.2, 0.3], + inputTextTokenCount: 2, + }), + ), + } + mockSend.mockResolvedValue(mockResponse) + + const result = await embedder.createEmbeddings(testTexts) + + expect(mockSend).toHaveBeenCalled() + const command = mockSend.mock.calls[0][0] as any + expect(command.input.modelId).toBe(testModelId) + const bodyStr = + typeof command.input.body === "string" + ? command.input.body + : new TextDecoder().decode(command.input.body as Uint8Array) + expect(JSON.parse(bodyStr || "{}")).toEqual({ + inputText: "Hello world", + }) + + expect(result).toEqual({ + embeddings: [[0.1, 0.2, 0.3]], + usage: { promptTokens: 2, totalTokens: 2 }, + }) + }) + + it("should create embeddings for multiple texts", async () => { + const testTexts = ["Hello world", "Another text"] + const mockResponses = [ + { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.1, 0.2, 0.3], + inputTextTokenCount: 2, + }), + ), + }, + { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.4, 0.5, 0.6], + inputTextTokenCount: 3, + }), + ), + }, + ] + + mockSend.mockResolvedValueOnce(mockResponses[0]).mockResolvedValueOnce(mockResponses[1]) + + const result = await embedder.createEmbeddings(testTexts) + + expect(mockSend).toHaveBeenCalledTimes(2) + expect(result).toEqual({ + embeddings: [ + [0.1, 0.2, 0.3], + [0.4, 0.5, 0.6], + ], + usage: { promptTokens: 5, totalTokens: 5 }, + }) + }) + + it("should handle Cohere model format", async () => { + const cohereEmbedder = new BedrockEmbedder("us-east-1", "test-profile", "cohere.embed-english-v3") + const testTexts = ["Hello world"] + const mockResponse = { + body: new TextEncoder().encode( + JSON.stringify({ + embeddings: [[0.1, 0.2, 0.3]], + }), + ), + } + mockSend.mockResolvedValue(mockResponse) + + const result = await cohereEmbedder.createEmbeddings(testTexts) + + const command = mockSend.mock.calls[0][0] as InvokeModelCommand + const bodyStr = + typeof command.input.body === "string" + ? command.input.body + : new TextDecoder().decode(command.input.body as Uint8Array) + expect(JSON.parse(bodyStr || "{}")).toEqual({ + texts: ["Hello world"], + input_type: "search_document", + }) + + expect(result).toEqual({ + embeddings: [[0.1, 0.2, 0.3]], + usage: { promptTokens: 0, totalTokens: 0 }, + }) + }) + + it("should create embeddings with Nova multimodal model", async () => { + const novaMultimodalEmbedder = new BedrockEmbedder( + "us-east-1", + "test-profile", + "amazon.nova-2-multimodal-embeddings-v1:0", + ) + const testTexts = ["Hello world"] + const mockResponse = { + body: new TextEncoder().encode( + JSON.stringify({ + embeddings: [ + { + embedding: [0.1, 0.2, 0.3], + }, + ], + inputTextTokenCount: 2, + }), + ), + } + mockSend.mockResolvedValue(mockResponse) + + const result = await novaMultimodalEmbedder.createEmbeddings(testTexts) + + expect(mockSend).toHaveBeenCalled() + const command = mockSend.mock.calls[0][0] as any + expect(command.input.modelId).toBe("amazon.nova-2-multimodal-embeddings-v1:0") + const bodyStr = + typeof command.input.body === "string" + ? command.input.body + : new TextDecoder().decode(command.input.body as Uint8Array) + // Nova multimodal embeddings use a task-based format with nested text object + expect(JSON.parse(bodyStr || "{}")).toEqual({ + taskType: "SINGLE_EMBEDDING", + singleEmbeddingParams: { + embeddingPurpose: "GENERIC_INDEX", + embeddingDimension: 1024, + text: { + truncationMode: "END", + value: "Hello world", + }, + }, + }) + + expect(result).toEqual({ + embeddings: [[0.1, 0.2, 0.3]], + usage: { promptTokens: 2, totalTokens: 2 }, + }) + }) + + it("should handle Nova multimodal model with multiple texts", async () => { + const novaMultimodalEmbedder = new BedrockEmbedder( + "us-east-1", + "test-profile", + "amazon.nova-2-multimodal-embeddings-v1:0", + ) + const testTexts = ["Hello world", "Another text"] + const mockResponses = [ + { + body: new TextEncoder().encode( + JSON.stringify({ + embeddings: [ + { + embedding: [0.1, 0.2, 0.3], + }, + ], + inputTextTokenCount: 2, + }), + ), + }, + { + body: new TextEncoder().encode( + JSON.stringify({ + embeddings: [ + { + embedding: [0.4, 0.5, 0.6], + }, + ], + inputTextTokenCount: 3, + }), + ), + }, + ] + + mockSend.mockResolvedValueOnce(mockResponses[0]).mockResolvedValueOnce(mockResponses[1]) + + const result = await novaMultimodalEmbedder.createEmbeddings(testTexts) + + expect(mockSend).toHaveBeenCalledTimes(2) + + // Verify the request format for both texts + const firstCommand = mockSend.mock.calls[0][0] as any + const firstBodyStr = + typeof firstCommand.input.body === "string" + ? firstCommand.input.body + : new TextDecoder().decode(firstCommand.input.body as Uint8Array) + // Nova multimodal embeddings use a task-based format with nested text object + expect(JSON.parse(firstBodyStr || "{}")).toEqual({ + taskType: "SINGLE_EMBEDDING", + singleEmbeddingParams: { + embeddingPurpose: "GENERIC_INDEX", + embeddingDimension: 1024, + text: { + truncationMode: "END", + value: "Hello world", + }, + }, + }) + + const secondCommand = mockSend.mock.calls[1][0] as any + const secondBodyStr = + typeof secondCommand.input.body === "string" + ? secondCommand.input.body + : new TextDecoder().decode(secondCommand.input.body as Uint8Array) + expect(JSON.parse(secondBodyStr || "{}")).toEqual({ + taskType: "SINGLE_EMBEDDING", + singleEmbeddingParams: { + embeddingPurpose: "GENERIC_INDEX", + embeddingDimension: 1024, + text: { + truncationMode: "END", + value: "Another text", + }, + }, + }) + + expect(result).toEqual({ + embeddings: [ + [0.1, 0.2, 0.3], + [0.4, 0.5, 0.6], + ], + usage: { promptTokens: 5, totalTokens: 5 }, + }) + }) + + it("should use custom model when provided", async () => { + const testTexts = ["Hello world"] + const customModel = "amazon.titan-embed-text-v1" + const mockResponse = { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.1, 0.2, 0.3], + inputTextTokenCount: 2, + }), + ), + } + mockSend.mockResolvedValue(mockResponse) + + await embedder.createEmbeddings(testTexts, customModel) + + const command = mockSend.mock.calls[0][0] as InvokeModelCommand + expect(command.input.modelId).toBe(customModel) + }) + + it("should handle missing token count data gracefully", async () => { + const testTexts = ["Hello world"] + const mockResponse = { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.1, 0.2, 0.3], + }), + ), + } + mockSend.mockResolvedValue(mockResponse) + + const result = await embedder.createEmbeddings(testTexts) + + expect(result).toEqual({ + embeddings: [[0.1, 0.2, 0.3]], + usage: { promptTokens: 0, totalTokens: 0 }, + }) + }) + + /** + * Test batching logic when texts exceed token limits + */ + describe("batching logic", () => { + it("should warn and skip texts exceeding maximum token limit", async () => { + // Create a text that exceeds MAX_ITEM_TOKENS (4 characters ≈ 1 token) + const oversizedText = "a".repeat(MAX_ITEM_TOKENS * 4 + 100) + const normalText = "normal text" + const testTexts = [normalText, oversizedText, "another normal"] + + const mockResponses = [ + { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.1, 0.2, 0.3], + inputTextTokenCount: 3, + }), + ), + }, + { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.4, 0.5, 0.6], + inputTextTokenCount: 3, + }), + ), + }, + ] + + mockSend.mockResolvedValueOnce(mockResponses[0]).mockResolvedValueOnce(mockResponses[1]) + + const result = await embedder.createEmbeddings(testTexts) + + // Verify warning was logged + expect(console.warn).toHaveBeenCalledWith(expect.stringContaining("exceeds maximum token limit")) + + // Verify only normal texts were processed + expect(mockSend).toHaveBeenCalledTimes(2) + expect(result.embeddings).toHaveLength(2) + }) + + it("should handle all texts being skipped due to size", async () => { + const oversizedText = "a".repeat(MAX_ITEM_TOKENS * 4 + 100) + const testTexts = [oversizedText, oversizedText] + + const result = await embedder.createEmbeddings(testTexts) + + expect(console.warn).toHaveBeenCalledTimes(2) + expect(mockSend).not.toHaveBeenCalled() + expect(result).toEqual({ + embeddings: [], + usage: { promptTokens: 0, totalTokens: 0 }, + }) + }) + }) + + /** + * Test retry logic for rate limiting and other errors + */ + describe("retry logic", () => { + beforeEach(() => { + vitest.useFakeTimers() + }) + + afterEach(() => { + vitest.useRealTimers() + }) + + it("should retry on throttling errors with exponential backoff", async () => { + const testTexts = ["Hello world"] + const throttlingError = new Error("Rate limit exceeded") + throttlingError.name = "ThrottlingException" + + mockSend + .mockRejectedValueOnce(throttlingError) + .mockRejectedValueOnce(throttlingError) + .mockResolvedValueOnce({ + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.1, 0.2, 0.3], + inputTextTokenCount: 2, + }), + ), + }) + + const resultPromise = embedder.createEmbeddings(testTexts) + + // Fast-forward through the delays + await vitest.advanceTimersByTimeAsync(INITIAL_RETRY_DELAY_MS) // First retry delay + await vitest.advanceTimersByTimeAsync(INITIAL_RETRY_DELAY_MS * 2) // Second retry delay + + const result = await resultPromise + + expect(mockSend).toHaveBeenCalledTimes(3) + expect(console.warn).toHaveBeenCalledWith(expect.stringContaining("Rate limit hit, retrying in")) + expect(result).toEqual({ + embeddings: [[0.1, 0.2, 0.3]], + usage: { promptTokens: 2, totalTokens: 2 }, + }) + }) + + it("should not retry on non-throttling errors", async () => { + const testTexts = ["Hello world"] + const authError = new Error("Unauthorized") + authError.name = "UnrecognizedClientException" + + mockSend.mockRejectedValue(authError) + + await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow( + "Failed to create embeddings after 3 attempts: Unauthorized", + ) + + expect(mockSend).toHaveBeenCalledTimes(1) + expect(console.warn).not.toHaveBeenCalledWith(expect.stringContaining("Rate limit hit")) + }) + }) + + /** + * Test error handling scenarios + */ + describe("error handling", () => { + it("should handle API errors gracefully", async () => { + const testTexts = ["Hello world"] + const apiError = new Error("API connection failed") + + mockSend.mockRejectedValue(apiError) + + await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow( + "Failed to create embeddings after 3 attempts: API connection failed", + ) + + expect(console.error).toHaveBeenCalledWith( + expect.stringContaining("Bedrock embedder error"), + expect.any(Error), + ) + }) + + it("should handle empty text arrays", async () => { + const testTexts: string[] = [] + + const result = await embedder.createEmbeddings(testTexts) + + expect(result).toEqual({ + embeddings: [], + usage: { promptTokens: 0, totalTokens: 0 }, + }) + expect(mockSend).not.toHaveBeenCalled() + }) + + it("should handle malformed API responses", async () => { + const testTexts = ["Hello world"] + const malformedResponse = { + body: new TextEncoder().encode("not json"), + } + + mockSend.mockResolvedValue(malformedResponse) + + await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow() + }) + + it("should handle AWS-specific errors", async () => { + const testTexts = ["Hello world"] + + // Test UnrecognizedClientException + const authError = new Error("Invalid credentials") + authError.name = "UnrecognizedClientException" + mockSend.mockRejectedValueOnce(authError) + + await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow( + "Failed to create embeddings after 3 attempts: Invalid credentials", + ) + + // Test AccessDeniedException + const accessError = new Error("Access denied") + accessError.name = "AccessDeniedException" + mockSend.mockRejectedValueOnce(accessError) + + await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow( + "Failed to create embeddings after 3 attempts: Access denied", + ) + + // Test ResourceNotFoundException + const notFoundError = new Error("Model not found") + notFoundError.name = "ResourceNotFoundException" + mockSend.mockRejectedValueOnce(notFoundError) + + await expect(embedder.createEmbeddings(testTexts)).rejects.toThrow( + "Failed to create embeddings after 3 attempts: Model not found", + ) + }) + }) + }) + + describe("validateConfiguration", () => { + it("should validate successfully with valid configuration", async () => { + const mockResponse = { + body: new TextEncoder().encode( + JSON.stringify({ + embedding: [0.1, 0.2, 0.3], + inputTextTokenCount: 1, + }), + ), + } + mockSend.mockResolvedValue(mockResponse) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(true) + expect(result.error).toBeUndefined() + expect(mockSend).toHaveBeenCalled() + }) + + it("should fail validation with authentication error", async () => { + const authError = new Error("Invalid credentials") + authError.name = "UnrecognizedClientException" + mockSend.mockRejectedValue(authError) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(false) + expect(result.error).toBe("Invalid AWS credentials") + }) + + it("should fail validation with access denied error", async () => { + const accessError = new Error("Access denied") + accessError.name = "AccessDeniedException" + mockSend.mockRejectedValue(accessError) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(false) + expect(result.error).toBe("Access denied to Bedrock service") + }) + + it("should fail validation with model not found error", async () => { + const notFoundError = new Error("Model not found") + notFoundError.name = "ResourceNotFoundException" + mockSend.mockRejectedValue(notFoundError) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(false) + expect(result.error).toContain("not found") + }) + + it("should fail validation with invalid response", async () => { + const mockResponse = { + body: new TextEncoder().encode( + JSON.stringify({ + // Missing embedding field + inputTextTokenCount: 1, + }), + ), + } + mockSend.mockResolvedValue(mockResponse) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(false) + expect(result.error).toBe("Invalid response format from Bedrock") + }) + + it("should fail validation with connection error", async () => { + const connectionError = new Error("ECONNREFUSED") + mockSend.mockRejectedValue(connectionError) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(false) + expect(result.error).toBe("Connection failed") + }) + + it("should fail validation with generic error", async () => { + const genericError = new Error("Unknown error") + mockSend.mockRejectedValue(genericError) + + const result = await embedder.validateConfiguration() + + expect(result.valid).toBe(false) + expect(result.error).toBe("Configuration error") + }) + }) +}) diff --git a/src/services/code-index/embedders/bedrock.ts b/src/services/code-index/embedders/bedrock.ts new file mode 100644 index 00000000000..2430b465dc8 --- /dev/null +++ b/src/services/code-index/embedders/bedrock.ts @@ -0,0 +1,321 @@ +import { BedrockRuntimeClient, InvokeModelCommand, InvokeModelCommandInput } from "@aws-sdk/client-bedrock-runtime" +import { fromEnv, fromIni } from "@aws-sdk/credential-providers" +import { IEmbedder, EmbeddingResponse, EmbedderInfo } from "../interfaces" +import { + MAX_BATCH_TOKENS, + MAX_ITEM_TOKENS, + MAX_BATCH_RETRIES as MAX_RETRIES, + INITIAL_RETRY_DELAY_MS as INITIAL_DELAY_MS, +} from "../constants" +import { getDefaultModelId } from "../../../shared/embeddingModels" +import { t } from "../../../i18n" +import { withValidationErrorHandling, formatEmbeddingError, HttpError } from "../shared/validation-helpers" +import { TelemetryEventName } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" + +/** + * Amazon Bedrock implementation of the embedder interface with batching and rate limiting + */ +export class BedrockEmbedder implements IEmbedder { + private bedrockClient: BedrockRuntimeClient + private readonly defaultModelId: string + + /** + * Creates a new Amazon Bedrock embedder + * @param region AWS region for Bedrock service (required) + * @param profile AWS profile name for credentials (optional - uses default credential chain if not provided) + * @param modelId Optional model ID override + */ + constructor( + private readonly region: string, + private readonly profile?: string, + modelId?: string, + ) { + if (!region) { + throw new Error("Region is required for AWS Bedrock embedder") + } + + // Initialize the Bedrock client with credentials + // If profile is specified, use it; otherwise use default credential chain + const credentials = this.profile ? fromIni({ profile: this.profile }) : fromEnv() + + this.bedrockClient = new BedrockRuntimeClient({ + region: this.region, + credentials, + }) + + this.defaultModelId = modelId || getDefaultModelId("bedrock") + } + + /** + * Creates embeddings for the given texts with batching and rate limiting + * @param texts Array of text strings to embed + * @param model Optional model identifier + * @returns Promise resolving to embedding response + */ + async createEmbeddings(texts: string[], model?: string): Promise { + const modelToUse = model || this.defaultModelId + + const allEmbeddings: number[][] = [] + const usage = { promptTokens: 0, totalTokens: 0 } + const remainingTexts = [...texts] + + while (remainingTexts.length > 0) { + const currentBatch: string[] = [] + let currentBatchTokens = 0 + const processedIndices: number[] = [] + + for (let i = 0; i < remainingTexts.length; i++) { + const text = remainingTexts[i] + const itemTokens = Math.ceil(text.length / 4) + + if (itemTokens > MAX_ITEM_TOKENS) { + console.warn( + t("embeddings:textExceedsTokenLimit", { + index: i, + itemTokens, + maxTokens: MAX_ITEM_TOKENS, + }), + ) + processedIndices.push(i) + continue + } + + if (currentBatchTokens + itemTokens <= MAX_BATCH_TOKENS) { + currentBatch.push(text) + currentBatchTokens += itemTokens + processedIndices.push(i) + } else { + break + } + } + + // Remove processed items from remainingTexts (in reverse order to maintain correct indices) + for (let i = processedIndices.length - 1; i >= 0; i--) { + remainingTexts.splice(processedIndices[i], 1) + } + + if (currentBatch.length > 0) { + const batchResult = await this._embedBatchWithRetries(currentBatch, modelToUse) + allEmbeddings.push(...batchResult.embeddings) + usage.promptTokens += batchResult.usage.promptTokens + usage.totalTokens += batchResult.usage.totalTokens + } + } + + return { embeddings: allEmbeddings, usage } + } + + /** + * Helper method to handle batch embedding with retries and exponential backoff + * @param batchTexts Array of texts to embed in this batch + * @param model Model identifier to use + * @returns Promise resolving to embeddings and usage statistics + */ + private async _embedBatchWithRetries( + batchTexts: string[], + model: string, + ): Promise<{ embeddings: number[][]; usage: { promptTokens: number; totalTokens: number } }> { + for (let attempts = 0; attempts < MAX_RETRIES; attempts++) { + try { + const embeddings: number[][] = [] + let totalPromptTokens = 0 + let totalTokens = 0 + + // Process each text in the batch + // Note: Amazon Titan models typically don't support batch embedding in a single request + // So we process them individually + for (const text of batchTexts) { + const embedding = await this._invokeEmbeddingModel(text, model) + embeddings.push(embedding.embedding) + totalPromptTokens += embedding.inputTextTokenCount || 0 + totalTokens += embedding.inputTextTokenCount || 0 + } + + return { + embeddings, + usage: { + promptTokens: totalPromptTokens, + totalTokens, + }, + } + } catch (error: any) { + const hasMoreAttempts = attempts < MAX_RETRIES - 1 + + // Check if it's a rate limit error + if (error.name === "ThrottlingException" && hasMoreAttempts) { + const delayMs = INITIAL_DELAY_MS * Math.pow(2, attempts) + console.warn( + t("embeddings:rateLimitRetry", { + delayMs, + attempt: attempts + 1, + maxRetries: MAX_RETRIES, + }), + ) + await new Promise((resolve) => setTimeout(resolve, delayMs)) + continue + } + + // Capture telemetry before reformatting the error + TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + location: "BedrockEmbedder:_embedBatchWithRetries", + attempt: attempts + 1, + }) + + // Log the error for debugging + console.error(`Bedrock embedder error (attempt ${attempts + 1}/${MAX_RETRIES}):`, error) + + // Format and throw the error + throw formatEmbeddingError(error, MAX_RETRIES) + } + } + + throw new Error(t("embeddings:failedMaxAttempts", { attempts: MAX_RETRIES })) + } + + /** + * Invokes the embedding model for a single text + * @param text The text to embed + * @param model The model identifier to use + * @returns Promise resolving to embedding and token count + */ + private async _invokeEmbeddingModel( + text: string, + model: string, + ): Promise<{ embedding: number[]; inputTextTokenCount?: number }> { + let requestBody: any + let modelId = model + + // Prepare the request body based on the model + if (model.startsWith("amazon.nova-2-multimodal")) { + // Nova multimodal embeddings use a task-based format with embeddingParams + // Reference: https://docs.aws.amazon.com/bedrock/latest/userguide/embeddings-nova.html + requestBody = { + taskType: "SINGLE_EMBEDDING", + singleEmbeddingParams: { + embeddingPurpose: "GENERIC_INDEX", + embeddingDimension: 1024, // Nova supports 1024 or 3072 + text: { + truncationMode: "END", + value: text, + }, + }, + } + } else if (model.startsWith("amazon.titan-embed")) { + requestBody = { + inputText: text, + } + } else if (model.startsWith("cohere.embed")) { + requestBody = { + texts: [text], + input_type: "search_document", // or "search_query" depending on use case + } + } else { + // Default to Titan format + requestBody = { + inputText: text, + } + } + + const params: InvokeModelCommandInput = { + modelId, + body: JSON.stringify(requestBody), + contentType: "application/json", + accept: "application/json", + } + + const command = new InvokeModelCommand(params) + + const response = await this.bedrockClient.send(command) + + // Parse the response + const responseBody = JSON.parse(new TextDecoder().decode(response.body)) + + // Extract embedding based on model type + if (model.startsWith("amazon.nova-2-multimodal")) { + // Nova multimodal returns { embeddings: [{ embedding: [...] }] } + // Reference: AWS Bedrock documentation + return { + embedding: responseBody.embeddings?.[0]?.embedding || responseBody.embedding, + inputTextTokenCount: responseBody.inputTextTokenCount, + } + } else if (model.startsWith("amazon.titan-embed")) { + return { + embedding: responseBody.embedding, + inputTextTokenCount: responseBody.inputTextTokenCount, + } + } else if (model.startsWith("cohere.embed")) { + return { + embedding: responseBody.embeddings[0], + // Cohere doesn't provide token count in response + } + } else { + // Default to Titan format + return { + embedding: responseBody.embedding, + inputTextTokenCount: responseBody.inputTextTokenCount, + } + } + } + + /** + * Validates the Bedrock embedder configuration by attempting a minimal embedding request + * @returns Promise resolving to validation result with success status and optional error message + */ + async validateConfiguration(): Promise<{ valid: boolean; error?: string }> { + return withValidationErrorHandling(async () => { + try { + // Test with a minimal embedding request + const result = await this._invokeEmbeddingModel("test", this.defaultModelId) + + // Check if we got a valid response + if (!result.embedding || result.embedding.length === 0) { + return { + valid: false, + error: t("embeddings:bedrock.invalidResponseFormat"), + } + } + + return { valid: true } + } catch (error: any) { + // Check for specific AWS errors + if (error.name === "UnrecognizedClientException") { + return { + valid: false, + error: t("embeddings:bedrock.invalidCredentials"), + } + } + + if (error.name === "AccessDeniedException") { + return { + valid: false, + error: t("embeddings:bedrock.accessDenied"), + } + } + + if (error.name === "ResourceNotFoundException") { + return { + valid: false, + error: t("embeddings:bedrock.modelNotFound", { model: this.defaultModelId }), + } + } + + // Capture telemetry for validation errors + TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + location: "BedrockEmbedder:validateConfiguration", + }) + throw error + } + }, "bedrock") + } + + get embedderInfo(): EmbedderInfo { + return { + name: "bedrock", + } + } +} diff --git a/src/services/code-index/interfaces/config.ts b/src/services/code-index/interfaces/config.ts index 9fe3df1f129..c5da4665fcd 100644 --- a/src/services/code-index/interfaces/config.ts +++ b/src/services/code-index/interfaces/config.ts @@ -15,6 +15,7 @@ export interface CodeIndexConfig { geminiOptions?: { apiKey: string } mistralOptions?: { apiKey: string } vercelAiGatewayOptions?: { apiKey: string } + bedrockOptions?: { region: string; profile?: string } openRouterOptions?: { apiKey: string } qdrantUrl?: string qdrantApiKey?: string @@ -38,6 +39,8 @@ export type PreviousConfigSnapshot = { geminiApiKey?: string mistralApiKey?: string vercelAiGatewayApiKey?: string + bedrockRegion?: string + bedrockProfile?: string openRouterApiKey?: string qdrantUrl?: string qdrantApiKey?: string diff --git a/src/services/code-index/interfaces/embedder.ts b/src/services/code-index/interfaces/embedder.ts index 7a3aa91ad9d..aa8515d6986 100644 --- a/src/services/code-index/interfaces/embedder.ts +++ b/src/services/code-index/interfaces/embedder.ts @@ -35,6 +35,7 @@ export type AvailableEmbedders = | "gemini" | "mistral" | "vercel-ai-gateway" + | "bedrock" | "openrouter" export interface EmbedderInfo { diff --git a/src/services/code-index/interfaces/manager.ts b/src/services/code-index/interfaces/manager.ts index 9a6e4031ab1..28ff5523277 100644 --- a/src/services/code-index/interfaces/manager.ts +++ b/src/services/code-index/interfaces/manager.ts @@ -77,6 +77,7 @@ export type EmbedderProvider = | "gemini" | "mistral" | "vercel-ai-gateway" + | "bedrock" | "openrouter" export interface IndexProgressUpdate { diff --git a/src/services/code-index/service-factory.ts b/src/services/code-index/service-factory.ts index 56ee1cff9f9..f7ef573c4fa 100644 --- a/src/services/code-index/service-factory.ts +++ b/src/services/code-index/service-factory.ts @@ -5,6 +5,7 @@ import { OpenAICompatibleEmbedder } from "./embedders/openai-compatible" import { GeminiEmbedder } from "./embedders/gemini" import { MistralEmbedder } from "./embedders/mistral" import { VercelAiGatewayEmbedder } from "./embedders/vercel-ai-gateway" +import { BedrockEmbedder } from "./embedders/bedrock" import { OpenRouterEmbedder } from "./embedders/openrouter" import { EmbedderProvider, getDefaultModelId, getModelDimension } from "../../shared/embeddingModels" import { QdrantVectorStore } from "./vector-store/qdrant-client" @@ -80,6 +81,12 @@ export class CodeIndexServiceFactory { throw new Error(t("embeddings:serviceFactory.vercelAiGatewayConfigMissing")) } return new VercelAiGatewayEmbedder(config.vercelAiGatewayOptions.apiKey, config.modelId) + } else if (provider === "bedrock") { + // Only region is required for Bedrock (profile is optional) + if (!config.bedrockOptions?.region) { + throw new Error(t("embeddings:serviceFactory.bedrockConfigMissing")) + } + return new BedrockEmbedder(config.bedrockOptions.region, config.bedrockOptions.profile, config.modelId) } else if (provider === "openrouter") { if (!config.openRouterOptions?.apiKey) { throw new Error(t("embeddings:serviceFactory.openRouterConfigMissing")) diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index b4c6580e1ba..d79a2ae504c 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -236,11 +236,14 @@ export interface WebviewMessage { | "gemini" | "mistral" | "vercel-ai-gateway" + | "bedrock" | "openrouter" codebaseIndexEmbedderBaseUrl?: string codebaseIndexEmbedderModelId: string codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers codebaseIndexOpenAiCompatibleBaseUrl?: string + codebaseIndexBedrockRegion?: string + codebaseIndexBedrockProfile?: string codebaseIndexSearchMaxResults?: number codebaseIndexSearchMinScore?: number diff --git a/src/shared/embeddingModels.ts b/src/shared/embeddingModels.ts index 9f83e6ae59e..781a4e46560 100644 --- a/src/shared/embeddingModels.ts +++ b/src/shared/embeddingModels.ts @@ -9,6 +9,7 @@ export type EmbedderProvider = | "gemini" | "mistral" | "vercel-ai-gateway" + | "bedrock" | "openrouter" // Add other providers as needed export interface EmbeddingModelProfile { @@ -77,6 +78,17 @@ export const EMBEDDING_MODEL_PROFILES: EmbeddingModelProfiles = { "mistral/codestral-embed": { dimension: 1536, scoreThreshold: 0.4 }, "mistral/mistral-embed": { dimension: 1024, scoreThreshold: 0.4 }, }, + bedrock: { + // Amazon Titan Embed models + "amazon.titan-embed-text-v1": { dimension: 1536, scoreThreshold: 0.4 }, + "amazon.titan-embed-text-v2:0": { dimension: 1024, scoreThreshold: 0.4 }, + "amazon.titan-embed-image-v1": { dimension: 1024, scoreThreshold: 0.4 }, + // Amazon Nova Embed models + "amazon.nova-2-multimodal-embeddings-v1:0": { dimension: 1024, scoreThreshold: 0.4 }, + // Cohere models available through Bedrock + "cohere.embed-english-v3": { dimension: 1024, scoreThreshold: 0.4 }, + "cohere.embed-multilingual-v3": { dimension: 1024, scoreThreshold: 0.4 }, + }, openrouter: { // OpenAI models via OpenRouter "openai/text-embedding-3-small": { dimension: 1536, scoreThreshold: 0.4 }, @@ -185,6 +197,8 @@ export function getDefaultModelId(provider: EmbedderProvider): string { case "vercel-ai-gateway": return "openai/text-embedding-3-large" + case "bedrock": + return "amazon.titan-embed-text-v2:0" case "openrouter": return "openai/text-embedding-3-large" diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 70a13377300..177b7a99e0e 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -66,6 +66,10 @@ interface LocalCodeIndexSettings { codebaseIndexSearchMaxResults?: number codebaseIndexSearchMinScore?: number + // Bedrock-specific settings + codebaseIndexBedrockRegion?: string + codebaseIndexBedrockProfile?: string + // Secret settings (start empty, will be loaded separately) codeIndexOpenAiKey?: string codeIndexQdrantApiKey?: string @@ -151,6 +155,15 @@ const createValidationSchema = (provider: EmbedderProvider, t: any) => { .min(1, t("settings:codeIndex.validation.modelSelectionRequired")), }) + case "bedrock": + return baseSchema.extend({ + codebaseIndexBedrockRegion: z.string().min(1, t("settings:codeIndex.validation.bedrockRegionRequired")), + codebaseIndexBedrockProfile: z.string().optional(), + codebaseIndexEmbedderModelId: z + .string() + .min(1, t("settings:codeIndex.validation.modelSelectionRequired")), + }) + case "openrouter": return baseSchema.extend({ codebaseIndexOpenRouterApiKey: z @@ -172,7 +185,7 @@ export const CodeIndexPopover: React.FC = ({ }) => { const SECRET_PLACEHOLDER = "••••••••••••••••" const { t } = useAppTranslation() - const { codebaseIndexConfig, codebaseIndexModels, cwd } = useExtensionState() + const { codebaseIndexConfig, codebaseIndexModels, cwd, apiConfiguration } = useExtensionState() const [open, setOpen] = useState(false) const [isAdvancedSettingsOpen, setIsAdvancedSettingsOpen] = useState(false) const [isSetupSettingsOpen, setIsSetupSettingsOpen] = useState(false) @@ -199,6 +212,8 @@ export const CodeIndexPopover: React.FC = ({ codebaseIndexEmbedderModelDimension: undefined, codebaseIndexSearchMaxResults: CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS, codebaseIndexSearchMinScore: CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE, + codebaseIndexBedrockRegion: "", + codebaseIndexBedrockProfile: "", codeIndexOpenAiKey: "", codeIndexQdrantApiKey: "", codebaseIndexOpenAiCompatibleBaseUrl: "", @@ -235,6 +250,8 @@ export const CodeIndexPopover: React.FC = ({ codebaseIndexConfig.codebaseIndexSearchMaxResults ?? CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS, codebaseIndexSearchMinScore: codebaseIndexConfig.codebaseIndexSearchMinScore ?? CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE, + codebaseIndexBedrockRegion: codebaseIndexConfig.codebaseIndexBedrockRegion || "", + codebaseIndexBedrockProfile: codebaseIndexConfig.codebaseIndexBedrockProfile || "", codeIndexOpenAiKey: "", codeIndexQdrantApiKey: "", codebaseIndexOpenAiCompatibleBaseUrl: codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl || "", @@ -554,7 +571,8 @@ export const CodeIndexPopover: React.FC = ({ const getAvailableModels = () => { if (!codebaseIndexModels) return [] - const models = codebaseIndexModels[currentSettings.codebaseIndexEmbedderProvider] + const models = + codebaseIndexModels[currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels] return models ? Object.keys(models) : [] } @@ -669,6 +687,33 @@ export const CodeIndexPopover: React.FC = ({ updateSetting("codebaseIndexEmbedderProvider", value) // Clear model selection when switching providers updateSetting("codebaseIndexEmbedderModelId", "") + + // Auto-populate Region and Profile when switching to Bedrock + // if the main API provider is also configured for Bedrock + if ( + value === "bedrock" && + apiConfiguration?.apiProvider === "bedrock" + ) { + // Only populate if currently empty + if ( + !currentSettings.codebaseIndexBedrockRegion && + apiConfiguration.awsRegion + ) { + updateSetting( + "codebaseIndexBedrockRegion", + apiConfiguration.awsRegion, + ) + } + if ( + !currentSettings.codebaseIndexBedrockProfile && + apiConfiguration.awsProfile + ) { + updateSetting( + "codebaseIndexBedrockProfile", + apiConfiguration.awsProfile, + ) + } + } }}> @@ -692,6 +737,9 @@ export const CodeIndexPopover: React.FC = ({ {t("settings:codeIndex.vercelAiGatewayProvider")} + + {t("settings:codeIndex.bedrockProvider")} + {t("settings:codeIndex.openRouterProvider")} @@ -742,7 +790,7 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( @@ -999,7 +1047,7 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( @@ -1064,7 +1112,7 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( @@ -1134,7 +1182,100 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels + ]?.[modelId] + return ( + + {modelId}{" "} + {model + ? t("settings:codeIndex.modelDimensions", { + dimension: model.dimension, + }) + : ""} + + ) + })} + + {formErrors.codebaseIndexEmbedderModelId && ( +

+ {formErrors.codebaseIndexEmbedderModelId} +

+ )} + + + )} + + {currentSettings.codebaseIndexEmbedderProvider === "bedrock" && ( + <> +
+ + + updateSetting("codebaseIndexBedrockRegion", e.target.value) + } + placeholder={t("settings:codeIndex.bedrockRegionPlaceholder")} + className={cn("w-full", { + "border-red-500": formErrors.codebaseIndexBedrockRegion, + })} + /> + {formErrors.codebaseIndexBedrockRegion && ( +

+ {formErrors.codebaseIndexBedrockRegion} +

+ )} +
+ +
+ + + updateSetting("codebaseIndexBedrockProfile", e.target.value) + } + placeholder={t("settings:codeIndex.bedrockProfilePlaceholder")} + className={cn("w-full", { + "border-red-500": formErrors.codebaseIndexBedrockProfile, + })} + /> + {formErrors.codebaseIndexBedrockProfile && ( +

+ {formErrors.codebaseIndexBedrockProfile} +

+ )} + {!formErrors.codebaseIndexBedrockProfile && ( +

+ {t("settings:codeIndex.bedrockProfileDescription")} +

+ )} +
+ +
+ + + updateSetting("codebaseIndexEmbedderModelId", e.target.value) + } + className={cn("w-full", { + "border-red-500": formErrors.codebaseIndexEmbedderModelId, + })}> + + {t("settings:codeIndex.selectModel")} + + {getAvailableModels().map((modelId) => { + const model = + codebaseIndexModels?.[ + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( @@ -1199,7 +1340,7 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( diff --git a/webview-ui/src/components/chat/__tests__/CodeIndexPopover.auto-populate.spec.tsx b/webview-ui/src/components/chat/__tests__/CodeIndexPopover.auto-populate.spec.tsx new file mode 100644 index 00000000000..818a7712815 --- /dev/null +++ b/webview-ui/src/components/chat/__tests__/CodeIndexPopover.auto-populate.spec.tsx @@ -0,0 +1,332 @@ +/** + * Tests for the auto-population feature in CodeIndexPopover + * + * Feature: When switching to Bedrock provider in code indexing configuration, + * automatically populate Region and Profile fields from main API configuration + * if the main API is also configured for Bedrock. + * + * Implementation location: CodeIndexPopover.tsx lines 737-752 + * + * These tests verify the core logic of the auto-population feature by directly + * testing the onValueChange handler behavior. + */ + +// Type for API configuration used in tests +type TestApiConfiguration = { + apiProvider: string + apiKey?: string + awsRegion?: string + awsProfile?: string +} + +describe("CodeIndexPopover - Auto-population Feature Logic", () => { + /** + * Test 1: Happy Path - Auto-population works + * Main API provider is Bedrock with region "us-west-2" and profile "my-profile" + * Code indexing fields are empty + * User switches provider to "bedrock" + * Expected: updateSetting is called to populate Region and Profile + */ + test("auto-populates Region and Profile when switching to Bedrock and main API is Bedrock", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "", + codebaseIndexBedrockProfile: "", + } + const apiConfiguration = { + apiProvider: "bedrock", + awsRegion: "us-west-2", + awsProfile: "my-profile", + } + + // Simulate the onValueChange logic from lines 737-752 + const value = "bedrock" + + // Clear model selection + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + // Auto-populate Region and Profile when switching to Bedrock + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify updateSetting was called correctly + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexBedrockRegion", "us-west-2") + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexBedrockProfile", "my-profile") + expect(mockUpdateSetting).toHaveBeenCalledTimes(3) + }) + + /** + * Test 2: Main API is not Bedrock + * Main API provider is "openai" (not Bedrock) + * User switches code indexing provider to "bedrock" + * Expected: Only model is cleared, no auto-population + */ + test("does not auto-populate when main API provider is not Bedrock", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "", + codebaseIndexBedrockProfile: "", + } + const apiConfiguration: TestApiConfiguration = { + apiProvider: "openai", + apiKey: "test-key", + } + + // Simulate the onValueChange logic + const value = "bedrock" + + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify only model was cleared, no auto-population + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).toHaveBeenCalledTimes(1) + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockRegion", expect.anything()) + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockProfile", expect.anything()) + }) + + /** + * Test 3: Existing values not overwritten + * Code indexing already has Region "eu-west-1" configured + * Main API has Region "us-west-2" + * User switches provider to "bedrock" + * Expected: Region is NOT updated (existing value preserved) + */ + test("does not overwrite existing Region value when switching to Bedrock", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "eu-west-1", + codebaseIndexBedrockProfile: "", + } + const apiConfiguration = { + apiProvider: "bedrock", + awsRegion: "us-west-2", + awsProfile: "default", + } + + // Simulate the onValueChange logic + const value = "bedrock" + + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify Region was NOT updated (it already had a value) + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexBedrockProfile", "default") + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockRegion", expect.anything()) + expect(mockUpdateSetting).toHaveBeenCalledTimes(2) + }) + + /** + * Test 4: Partial population + * Main API has Region but no Profile + * Code indexing fields are empty + * User switches to "bedrock" + * Expected: Only Region is populated, Profile is not + */ + test("only populates Region when Profile is not configured in main API", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "", + codebaseIndexBedrockProfile: "", + } + const apiConfiguration: TestApiConfiguration = { + apiProvider: "bedrock", + awsRegion: "ap-southeast-1", + // No awsProfile configured + } + + // Simulate the onValueChange logic + const value = "bedrock" + + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify only Region was populated + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexBedrockRegion", "ap-southeast-1") + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockProfile", expect.anything()) + expect(mockUpdateSetting).toHaveBeenCalledTimes(2) + }) + + /** + * Test 5: Empty main API config + * Main API provider is Bedrock but has no region/profile configured + * User switches code indexing to "bedrock" + * Expected: No auto-population (nothing to populate from) + */ + test("does not populate when main API Bedrock config is empty", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "", + codebaseIndexBedrockProfile: "", + } + const apiConfiguration: TestApiConfiguration = { + apiProvider: "bedrock", + // No awsRegion or awsProfile configured + } + + // Simulate the onValueChange logic + const value = "bedrock" + + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify only model was cleared, no auto-population + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).toHaveBeenCalledTimes(1) + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockRegion", expect.anything()) + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockProfile", expect.anything()) + }) + + /** + * Test 6: Verify Profile can be empty while Region is populated + * This tests that auto-population handles undefined/null Profile correctly + */ + test("handles undefined Profile in main API config gracefully", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "", + codebaseIndexBedrockProfile: "", + } + const apiConfiguration = { + apiProvider: "bedrock", + awsRegion: "us-east-1", + awsProfile: undefined, // Explicitly undefined + } + + // Simulate the onValueChange logic + const value = "bedrock" + + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify only Region was populated (Profile is undefined) + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexBedrockRegion", "us-east-1") + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockProfile", expect.anything()) + expect(mockUpdateSetting).toHaveBeenCalledTimes(2) + }) + + /** + * Test 7: Does not populate when switching TO other providers + * This ensures the feature only works when switching TO Bedrock specifically + */ + test("does not trigger auto-population when switching to non-Bedrock provider", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "", + codebaseIndexBedrockProfile: "", + } + const apiConfiguration = { + apiProvider: "bedrock", + awsRegion: "us-west-2", + awsProfile: "my-profile", + } + + // Simulate switching to openai instead of bedrock + const value: string = "openai" + + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + // The condition intentionally won't match since value is "openai" + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify only model was cleared, no auto-population + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).toHaveBeenCalledTimes(1) + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockRegion", expect.anything()) + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockProfile", expect.anything()) + }) + + /** + * Test 8: Both fields have existing values + * Neither field should be auto-populated if both already have values + */ + test("does not overwrite when both Region and Profile already have values", () => { + const mockUpdateSetting = vi.fn() + const currentSettings = { + codebaseIndexBedrockRegion: "eu-central-1", + codebaseIndexBedrockProfile: "production", + } + const apiConfiguration = { + apiProvider: "bedrock", + awsRegion: "us-west-2", + awsProfile: "default", + } + + // Simulate the onValueChange logic + const value = "bedrock" + + mockUpdateSetting("codebaseIndexEmbedderModelId", "") + + if (value === "bedrock" && apiConfiguration?.apiProvider === "bedrock") { + if (!currentSettings.codebaseIndexBedrockRegion && apiConfiguration.awsRegion) { + mockUpdateSetting("codebaseIndexBedrockRegion", apiConfiguration.awsRegion) + } + if (!currentSettings.codebaseIndexBedrockProfile && apiConfiguration.awsProfile) { + mockUpdateSetting("codebaseIndexBedrockProfile", apiConfiguration.awsProfile) + } + } + + // Verify neither field was updated (both already had values) + expect(mockUpdateSetting).toHaveBeenCalledWith("codebaseIndexEmbedderModelId", "") + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockRegion", expect.anything()) + expect(mockUpdateSetting).not.toHaveBeenCalledWith("codebaseIndexBedrockProfile", expect.anything()) + expect(mockUpdateSetting).toHaveBeenCalledTimes(1) + }) +}) diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index b54bd5a3ac2..75feccd2ddb 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -78,6 +78,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Clau API", "vercelAiGatewayApiKeyPlaceholder": "Introduïu la vostra clau API de Vercel AI Gateway", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Regió d'AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Perfil d'AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Nom del perfil d'AWS de ~/.aws/credentials (obligatori).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Clau de l'API d'OpenRouter", "openRouterApiKeyPlaceholder": "Introduïu la vostra clau de l'API d'OpenRouter", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Cal una clau d'API de Gemini", "mistralApiKeyRequired": "La clau de l'API de Mistral és requerida", "vercelAiGatewayApiKeyRequired": "Es requereix la clau API de Vercel AI Gateway", + "bedrockRegionRequired": "Es requereix la regió d'AWS", + "bedrockProfileRequired": "Es requereix el perfil d'AWS", "ollamaBaseUrlRequired": "Cal una URL base d'Ollama", "baseUrlRequired": "Cal una URL base", "modelDimensionMinValue": "La dimensió del model ha de ser superior a 0", "openRouterApiKeyRequired": "Clau API d'OpenRouter és requerida" }, + "optional": "opcional", "advancedConfigLabel": "Configuració avançada", "searchMinScoreLabel": "Llindar de puntuació de cerca", "searchMinScoreDescription": "Puntuació mínima de similitud (0.0-1.0) requerida per als resultats de la cerca. Valors més baixos retornen més resultats però poden ser menys rellevants. Valors més alts retornen menys resultats però més rellevants.", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 632757f71ac..2b380115780 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -80,6 +80,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API-Schlüssel", "vercelAiGatewayApiKeyPlaceholder": "Gib deinen Vercel AI Gateway API-Schlüssel ein", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS-Region", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS-Profil", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "AWS-Profilname aus ~/.aws/credentials (erforderlich).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter API-Schlüssel", "openRouterApiKeyPlaceholder": "Gib deinen OpenRouter API-Schlüssel ein", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Gemini-API-Schlüssel ist erforderlich", "mistralApiKeyRequired": "Mistral-API-Schlüssel ist erforderlich", "vercelAiGatewayApiKeyRequired": "Vercel AI Gateway API-Schlüssel ist erforderlich", + "bedrockRegionRequired": "AWS-Region erforderlich", + "bedrockProfileRequired": "AWS-Profil erforderlich", "ollamaBaseUrlRequired": "Ollama-Basis-URL ist erforderlich", "baseUrlRequired": "Basis-URL ist erforderlich", "modelDimensionMinValue": "Modellabmessung muss größer als 0 sein", "openRouterApiKeyRequired": "OpenRouter API-Schlüssel ist erforderlich" }, + "optional": "optional", "advancedConfigLabel": "Erweiterte Konfiguration", "searchMinScoreLabel": "Suchergebnis-Schwellenwert", "searchMinScoreDescription": "Mindestähnlichkeitswert (0.0-1.0), der für Suchergebnisse erforderlich ist. Niedrigere Werte liefern mehr Ergebnisse, die jedoch möglicherweise weniger relevant sind. Höhere Werte liefern weniger, aber relevantere Ergebnisse.", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 5a956c1bfed..7548d44be19 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -89,6 +89,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API Key", "vercelAiGatewayApiKeyPlaceholder": "Enter your Vercel AI Gateway API key", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS Region", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS Profile", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "AWS profile name from ~/.aws/credentials (required).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter API Key", "openRouterApiKeyPlaceholder": "Enter your OpenRouter API key", @@ -158,11 +164,14 @@ "geminiApiKeyRequired": "Gemini API key is required", "mistralApiKeyRequired": "Mistral API key is required", "vercelAiGatewayApiKeyRequired": "Vercel AI Gateway API key is required", + "bedrockRegionRequired": "AWS region is required", + "bedrockProfileRequired": "AWS profile is required", "openRouterApiKeyRequired": "OpenRouter API key is required", "ollamaBaseUrlRequired": "Ollama base URL is required", "baseUrlRequired": "Base URL is required", "modelDimensionMinValue": "Model dimension must be greater than 0" - } + }, + "optional": "optional" }, "autoApprove": { "description": "Run these actions without asking for permission. Only enable for actions you fully trust and if you understand the security risks.", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 59327d69eae..8519938d4af 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -80,6 +80,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Clave API", "vercelAiGatewayApiKeyPlaceholder": "Introduce tu clave API de Vercel AI Gateway", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Región de AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Perfil de AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Nombre del perfil de AWS desde ~/.aws/credentials (requerido).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Clave de API de OpenRouter", "openRouterApiKeyPlaceholder": "Introduce tu clave de API de OpenRouter", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Se requiere la clave API de Gemini", "mistralApiKeyRequired": "Se requiere la clave de API de Mistral", "vercelAiGatewayApiKeyRequired": "Se requiere la clave API de Vercel AI Gateway", + "bedrockRegionRequired": "Se requiere la región de AWS", + "bedrockProfileRequired": "Se requiere el perfil de AWS", "ollamaBaseUrlRequired": "Se requiere la URL base de Ollama", "baseUrlRequired": "Se requiere la URL base", "modelDimensionMinValue": "La dimensión del modelo debe ser mayor que 0", "openRouterApiKeyRequired": "Se requiere la clave API de OpenRouter" }, + "optional": "opcional", "advancedConfigLabel": "Configuración avanzada", "searchMinScoreLabel": "Umbral de puntuación de búsqueda", "searchMinScoreDescription": "Puntuación mínima de similitud (0.0-1.0) requerida para los resultados de búsqueda. Valores más bajos devuelven más resultados pero pueden ser menos relevantes. Valores más altos devuelven menos resultados pero más relevantes.", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 40ed87ed874..13f54eb59fd 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -80,6 +80,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Clé API", "vercelAiGatewayApiKeyPlaceholder": "Entrez votre clé API Vercel AI Gateway", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Région AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Profil AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Nom du profil AWS depuis ~/.aws/credentials (requis).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Clé d'API OpenRouter", "openRouterApiKeyPlaceholder": "Entrez votre clé d'API OpenRouter", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "La clé API Gemini est requise", "mistralApiKeyRequired": "La clé API Mistral est requise", "vercelAiGatewayApiKeyRequired": "La clé API Vercel AI Gateway est requise", + "bedrockRegionRequired": "La région AWS est requise", + "bedrockProfileRequired": "Le profil AWS est requis", "ollamaBaseUrlRequired": "L'URL de base Ollama est requise", "baseUrlRequired": "L'URL de base est requise", "modelDimensionMinValue": "La dimension du modèle doit être supérieure à 0", "openRouterApiKeyRequired": "Clé API OpenRouter est requise" }, + "optional": "optionnel", "advancedConfigLabel": "Configuration avancée", "searchMinScoreLabel": "Seuil de score de recherche", "searchMinScoreDescription": "Score de similarité minimum (0.0-1.0) requis pour les résultats de recherche. Des valeurs plus faibles renvoient plus de résultats mais peuvent être moins pertinents. Des valeurs plus élevées renvoient moins de résultats mais plus pertinents.", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 6bc61918cba..b9ceadc55bf 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API कुंजी", "vercelAiGatewayApiKeyPlaceholder": "अपनी Vercel AI Gateway API कुंजी दर्ज करें", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS क्षेत्र", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS प्रोफ़ाइल", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "~/.aws/credentials से AWS प्रोफ़ाइल नाम (आवश्यक)।", "openRouterProvider": "ओपनराउटर", "openRouterApiKeyLabel": "ओपनराउटर एपीआई कुंजी", "openRouterApiKeyPlaceholder": "अपनी ओपनराउटर एपीआई कुंजी दर्ज करें", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Gemini API कुंजी आवश्यक है", "mistralApiKeyRequired": "मिस्ट्रल एपीआई कुंजी आवश्यक है", "vercelAiGatewayApiKeyRequired": "Vercel AI Gateway API कुंजी आवश्यक है", + "bedrockRegionRequired": "AWS क्षेत्र आवश्यक है", + "bedrockProfileRequired": "AWS प्रोफ़ाइल आवश्यक है", "ollamaBaseUrlRequired": "Ollama आधार URL आवश्यक है", "baseUrlRequired": "आधार URL आवश्यक है", "modelDimensionMinValue": "मॉडल आयाम 0 से बड़ा होना चाहिए", "openRouterApiKeyRequired": "OpenRouter API कुंजी आवश्यक है" }, + "optional": "वैकल्पिक", "advancedConfigLabel": "उन्नत कॉन्फ़िगरेशन", "searchMinScoreLabel": "खोज स्कोर थ्रेसहोल्ड", "searchMinScoreDescription": "खोज परिणामों के लिए आवश्यक न्यूनतम समानता स्कोर (0.0-1.0)। कम मान अधिक परिणाम लौटाते हैं लेकिन कम प्रासंगिक हो सकते हैं। उच्च मान कम लेकिन अधिक प्रासंगिक परिणाम लौटाते हैं।", diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index d33287f6548..2426f8be3cf 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API Key", "vercelAiGatewayApiKeyPlaceholder": "Masukkan kunci API Vercel AI Gateway Anda", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Wilayah AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Profil AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Nama profil AWS dari ~/.aws/credentials (wajib).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Kunci API OpenRouter", "openRouterApiKeyPlaceholder": "Masukkan kunci API OpenRouter Anda", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Kunci API Gemini diperlukan", "mistralApiKeyRequired": "Kunci API Mistral diperlukan", "vercelAiGatewayApiKeyRequired": "Kunci API Vercel AI Gateway diperlukan", + "bedrockRegionRequired": "Wilayah AWS diperlukan", + "bedrockProfileRequired": "Profil AWS diperlukan", "ollamaBaseUrlRequired": "URL dasar Ollama diperlukan", "baseUrlRequired": "URL dasar diperlukan", "modelDimensionMinValue": "Dimensi model harus lebih besar dari 0", "openRouterApiKeyRequired": "Kunci API OpenRouter diperlukan" }, + "optional": "opsional", "advancedConfigLabel": "Konfigurasi Lanjutan", "searchMinScoreLabel": "Ambang Batas Skor Pencarian", "searchMinScoreDescription": "Skor kesamaan minimum (0.0-1.0) yang diperlukan untuk hasil pencarian. Nilai yang lebih rendah mengembalikan lebih banyak hasil tetapi mungkin kurang relevan. Nilai yang lebih tinggi mengembalikan lebih sedikit hasil tetapi lebih relevan.", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 7f88a1f6a72..752b8a6dd20 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Chiave API", "vercelAiGatewayApiKeyPlaceholder": "Inserisci la tua chiave API Vercel AI Gateway", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Regione AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Profilo AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Nome del profilo AWS da ~/.aws/credentials (richiesto).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Chiave API OpenRouter", "openRouterApiKeyPlaceholder": "Inserisci la tua chiave API OpenRouter", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "È richiesta la chiave API Gemini", "mistralApiKeyRequired": "La chiave API di Mistral è richiesta", "vercelAiGatewayApiKeyRequired": "È richiesta la chiave API Vercel AI Gateway", + "bedrockRegionRequired": "La regione AWS è richiesta", + "bedrockProfileRequired": "Il profilo AWS è richiesto", "ollamaBaseUrlRequired": "È richiesto l'URL di base di Ollama", "baseUrlRequired": "È richiesto l'URL di base", "modelDimensionMinValue": "La dimensione del modello deve essere maggiore di 0", "openRouterApiKeyRequired": "Chiave API OpenRouter è richiesta" }, + "optional": "opzionale", "advancedConfigLabel": "Configurazione avanzata", "searchMinScoreLabel": "Soglia punteggio di ricerca", "searchMinScoreDescription": "Punteggio minimo di somiglianza (0.0-1.0) richiesto per i risultati della ricerca. Valori più bassi restituiscono più risultati ma potrebbero essere meno pertinenti. Valori più alti restituiscono meno risultati ma più pertinenti.", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index cefe8ca20dc..9281f3be15a 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "APIキー", "vercelAiGatewayApiKeyPlaceholder": "Vercel AI GatewayのAPIキーを入力してください", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS リージョン", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS プロファイル", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "~/.aws/credentials の AWS プロファイル名(必須)。", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter APIキー", "openRouterApiKeyPlaceholder": "OpenRouter APIキーを入力してください", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Gemini APIキーが必要です", "mistralApiKeyRequired": "Mistral APIキーが必要です", "vercelAiGatewayApiKeyRequired": "Vercel AI Gateway APIキーが必要です", + "bedrockRegionRequired": "AWS リージョンは必須です", + "bedrockProfileRequired": "AWS プロファイルは必須です", "ollamaBaseUrlRequired": "OllamaのベースURLが必要です", "baseUrlRequired": "ベースURLが必要です", "modelDimensionMinValue": "モデルの次元は0より大きくなければなりません", "openRouterApiKeyRequired": "OpenRouter APIキーが必要です" }, + "optional": "オプション", "advancedConfigLabel": "詳細設定", "searchMinScoreLabel": "検索スコアのしきい値", "searchMinScoreDescription": "検索結果に必要な最小類似度スコア(0.0-1.0)。値を低くするとより多くの結果が返されますが、関連性が低くなる可能性があります。値を高くすると返される結果は少なくなりますが、より関連性が高くなります。", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index d7b0359cf2e..c555b81f243 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -78,6 +78,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API 키", "vercelAiGatewayApiKeyPlaceholder": "Vercel AI Gateway API 키를 입력하세요", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS 리전", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS 프로필", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "~/.aws/credentials의 AWS 프로필 이름(필수).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter API 키", "openRouterApiKeyPlaceholder": "OpenRouter API 키를 입력하세요", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Gemini API 키가 필요합니다", "mistralApiKeyRequired": "Mistral API 키가 필요합니다", "vercelAiGatewayApiKeyRequired": "Vercel AI Gateway API 키가 필요합니다", + "bedrockRegionRequired": "AWS 리전이 필요합니다", + "bedrockProfileRequired": "AWS 프로필이 필요합니다", "ollamaBaseUrlRequired": "Ollama 기본 URL이 필요합니다", "baseUrlRequired": "기본 URL이 필요합니다", "modelDimensionMinValue": "모델 차원은 0보다 커야 합니다", "openRouterApiKeyRequired": "OpenRouter API 키가 필요합니다" }, + "optional": "선택 사항", "advancedConfigLabel": "고급 구성", "searchMinScoreLabel": "검색 점수 임계값", "searchMinScoreDescription": "검색 결과에 필요한 최소 유사도 점수(0.0-1.0). 값이 낮을수록 더 많은 결과가 반환되지만 관련성이 떨어질 수 있습니다. 값이 높을수록 결과는 적지만 관련성이 높은 결과가 반환됩니다.", diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index ad0f2264b71..01da609400c 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API-sleutel", "vercelAiGatewayApiKeyPlaceholder": "Voer uw Vercel AI Gateway API-sleutel in", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS-regio", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS-profiel", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "AWS-profielnaam uit ~/.aws/credentials (vereist).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter API-sleutel", "openRouterApiKeyPlaceholder": "Voer uw OpenRouter API-sleutel in", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Gemini API-sleutel is vereist", "mistralApiKeyRequired": "Mistral API-sleutel is vereist", "vercelAiGatewayApiKeyRequired": "Vercel AI Gateway API-sleutel is vereist", + "bedrockRegionRequired": "AWS-regio is vereist", + "bedrockProfileRequired": "AWS-profiel is vereist", "ollamaBaseUrlRequired": "Ollama basis-URL is vereist", "baseUrlRequired": "Basis-URL is vereist", "modelDimensionMinValue": "Modelafmeting moet groter zijn dan 0", "openRouterApiKeyRequired": "OpenRouter API-sleutel is vereist" }, + "optional": "optioneel", "advancedConfigLabel": "Geavanceerde configuratie", "searchMinScoreLabel": "Zoekscore drempel", "searchMinScoreDescription": "Minimale overeenkomstscore (0.0-1.0) vereist voor zoekresultaten. Lagere waarden leveren meer resultaten op, maar zijn mogelijk minder relevant. Hogere waarden leveren minder, maar relevantere resultaten op.", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 37053fb4bf8..a8cf0eca78e 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -78,6 +78,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Klucz API", "vercelAiGatewayApiKeyPlaceholder": "Wprowadź swój klucz API Vercel AI Gateway", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Region AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Profil AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Nazwa profilu AWS z ~/.aws/credentials (wymagane).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Klucz API OpenRouter", "openRouterApiKeyPlaceholder": "Wprowadź swój klucz API OpenRouter", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Wymagany jest klucz API Gemini", "mistralApiKeyRequired": "Klucz API Mistral jest wymagany", "vercelAiGatewayApiKeyRequired": "Klucz API Vercel AI Gateway jest wymagany", + "bedrockRegionRequired": "Region AWS jest wymagany", + "bedrockProfileRequired": "Profil AWS jest wymagany", "ollamaBaseUrlRequired": "Wymagany jest bazowy adres URL Ollama", "baseUrlRequired": "Wymagany jest bazowy adres URL", "modelDimensionMinValue": "Wymiar modelu musi być większy niż 0", "openRouterApiKeyRequired": "Wymagany jest klucz API OpenRouter" }, + "optional": "opcjonalny", "advancedConfigLabel": "Konfiguracja zaawansowana", "searchMinScoreLabel": "Próg wyniku wyszukiwania", "searchMinScoreDescription": "Minimalny wynik podobieństwa (0.0-1.0) wymagany dla wyników wyszukiwania. Niższe wartości zwracają więcej wyników, ale mogą być mniej trafne. Wyższe wartości zwracają mniej wyników, ale bardziej trafnych.", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index d8206816354..0cb0c48afd5 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Chave de API", "vercelAiGatewayApiKeyPlaceholder": "Digite sua chave de API do Vercel AI Gateway", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Região da AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Perfil da AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Nome do perfil da AWS em ~/.aws/credentials (obrigatório).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Chave de API do OpenRouter", "openRouterApiKeyPlaceholder": "Digite sua chave de API do OpenRouter", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "A chave de API do Gemini é obrigatória", "mistralApiKeyRequired": "A chave de API Mistral é obrigatória", "vercelAiGatewayApiKeyRequired": "A chave de API do Vercel AI Gateway é obrigatória", + "bedrockRegionRequired": "A região da AWS é obrigatória", + "bedrockProfileRequired": "O perfil da AWS é obrigatório", "ollamaBaseUrlRequired": "A URL base do Ollama é obrigatória", "baseUrlRequired": "A URL base é obrigatória", "modelDimensionMinValue": "A dimensão do modelo deve ser maior que 0", "openRouterApiKeyRequired": "Chave API do OpenRouter é obrigatória" }, + "optional": "opcional", "advancedConfigLabel": "Configuração Avançada", "searchMinScoreLabel": "Limite de pontuação de busca", "searchMinScoreDescription": "Pontuação mínima de similaridade (0.0-1.0) necessária para os resultados da busca. Valores mais baixos retornam mais resultados, mas podem ser menos relevantes. Valores mais altos retornam menos resultados, mas mais relevantes.", diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 4ebb8c4b0dc..69cca8038f5 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Ключ API", "vercelAiGatewayApiKeyPlaceholder": "Введите свой API-ключ Vercel AI Gateway", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Регион AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Профиль AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Имя профиля AWS из ~/.aws/credentials (обязательно).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Ключ API OpenRouter", "openRouterApiKeyPlaceholder": "Введите свой ключ API OpenRouter", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Требуется ключ API Gemini", "mistralApiKeyRequired": "Требуется API-ключ Mistral", "vercelAiGatewayApiKeyRequired": "Требуется API-ключ Vercel AI Gateway", + "bedrockRegionRequired": "Требуется регион AWS", + "bedrockProfileRequired": "Требуется профиль AWS", "ollamaBaseUrlRequired": "Требуется базовый URL Ollama", "baseUrlRequired": "Требуется базовый URL", "modelDimensionMinValue": "Размерность модели должна быть больше 0", "openRouterApiKeyRequired": "Требуется ключ API OpenRouter" }, + "optional": "необязательно", "advancedConfigLabel": "Расширенная конфигурация", "searchMinScoreLabel": "Порог оценки поиска", "searchMinScoreDescription": "Минимальный балл сходства (0.0-1.0), необходимый для результатов поиска. Более низкие значения возвращают больше результатов, но они могут быть менее релевантными. Более высокие значения возвращают меньше результатов, но более релевантных.", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 71d21cbe586..c4491f618c2 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -78,6 +78,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API Anahtarı", "vercelAiGatewayApiKeyPlaceholder": "Vercel AI Gateway API anahtarınızı girin", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS Bölgesi", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS Profili", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "~/.aws/credentials dosyasından AWS profil adı (gerekli).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter API Anahtarı", "openRouterApiKeyPlaceholder": "OpenRouter API anahtarınızı girin", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Gemini API anahtarı gereklidir", "mistralApiKeyRequired": "Mistral API anahtarı gereklidir", "vercelAiGatewayApiKeyRequired": "Vercel AI Gateway API anahtarı gereklidir", + "bedrockRegionRequired": "AWS bölgesi gereklidir", + "bedrockProfileRequired": "AWS profili gereklidir", "ollamaBaseUrlRequired": "Ollama temel URL'si gereklidir", "baseUrlRequired": "Temel URL'si gereklidir", "modelDimensionMinValue": "Model boyutu 0'dan büyük olmalıdır", "openRouterApiKeyRequired": "OpenRouter API anahtarı gereklidir" }, + "optional": "isteğe bağlı", "advancedConfigLabel": "Gelişmiş Yapılandırma", "searchMinScoreLabel": "Arama Skoru Eşiği", "searchMinScoreDescription": "Arama sonuçları için gereken minimum benzerlik puanı (0.0-1.0). Düşük değerler daha fazla sonuç döndürür ancak daha az alakalı olabilir. Yüksek değerler daha az ancak daha alakalı sonuçlar döndürür.", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 23268def152..8691d3e7db2 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -78,6 +78,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "Khóa API", "vercelAiGatewayApiKeyPlaceholder": "Nhập khóa API Vercel AI Gateway của bạn", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "Vùng AWS", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "Hồ sơ AWS", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "Tên hồ sơ AWS từ ~/.aws/credentials (bắt buộc).", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "Khóa API OpenRouter", "openRouterApiKeyPlaceholder": "Nhập khóa API OpenRouter của bạn", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "Yêu cầu khóa API Gemini", "mistralApiKeyRequired": "Cần có khóa API của Mistral", "vercelAiGatewayApiKeyRequired": "Cần có khóa API Vercel AI Gateway", + "bedrockRegionRequired": "Vùng AWS là bắt buộc", + "bedrockProfileRequired": "Hồ sơ AWS là bắt buộc", "ollamaBaseUrlRequired": "Yêu cầu URL cơ sở Ollama", "baseUrlRequired": "Yêu cầu URL cơ sở", "modelDimensionMinValue": "Kích thước mô hình phải lớn hơn 0", "openRouterApiKeyRequired": "Yêu cầu khóa API OpenRouter" }, + "optional": "tùy chọn", "advancedConfigLabel": "Cấu hình nâng cao", "searchMinScoreLabel": "Ngưỡng điểm tìm kiếm", "searchMinScoreDescription": "Điểm tương đồng tối thiểu (0.0-1.0) cần thiết cho kết quả tìm kiếm. Giá trị thấp hơn trả về nhiều kết quả hơn nhưng có thể kém liên quan hơn. Giá trị cao hơn trả về ít kết quả hơn nhưng có liên quan hơn.", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index e4fcd5663a5..39684435896 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -80,6 +80,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API 密钥", "vercelAiGatewayApiKeyPlaceholder": "输入您的 Vercel AI Gateway API 密钥", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS 区域", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS 配置文件", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "来自 ~/.aws/credentials 的 AWS 配置文件名称(必需)。", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter API 密钥", "openRouterApiKeyPlaceholder": "输入您的 OpenRouter API 密钥", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "需要 Gemini API 密钥", "mistralApiKeyRequired": "需要 Mistral API 密钥", "vercelAiGatewayApiKeyRequired": "需要 Vercel AI Gateway API 密钥", + "bedrockRegionRequired": "AWS 区域为必填项", + "bedrockProfileRequired": "AWS 配置文件为必填项", "ollamaBaseUrlRequired": "需要 Ollama 基础 URL", "baseUrlRequired": "需要基础 URL", "modelDimensionMinValue": "模型维度必须大于 0", "openRouterApiKeyRequired": "OpenRouter API 密钥是必需的" }, + "optional": "可选", "advancedConfigLabel": "高级配置", "searchMinScoreLabel": "搜索分数阈值", "searchMinScoreDescription": "搜索结果所需的最低相似度分数(0.0-1.0)。较低的值返回更多结果,但可能不太相关。较高的值返回较少但更相关的结果。", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 0928791014b..6dc3eada7f9 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -75,6 +75,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API 金鑰", "vercelAiGatewayApiKeyPlaceholder": "輸入您的 Vercel AI Gateway API 金鑰", + "bedrockProvider": "Amazon Bedrock", + "bedrockRegionLabel": "AWS 區域", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS 設定檔", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "來自 ~/.aws/credentials 的 AWS 設定檔名稱(必需)。", "openRouterProvider": "OpenRouter", "openRouterApiKeyLabel": "OpenRouter API 金鑰", "openRouterApiKeyPlaceholder": "輸入您的 OpenRouter API 金鑰", @@ -145,11 +151,14 @@ "geminiApiKeyRequired": "需要 Gemini API 金鑰", "mistralApiKeyRequired": "需要 Mistral API 金鑰", "vercelAiGatewayApiKeyRequired": "需要 Vercel AI Gateway API 金鑰", + "bedrockRegionRequired": "AWS 區域為必填", + "bedrockProfileRequired": "AWS 設定檔為必填", "ollamaBaseUrlRequired": "需要 Ollama 基礎 URL", "baseUrlRequired": "需要基礎 URL", "modelDimensionMinValue": "模型維度必須大於 0", "openRouterApiKeyRequired": "OpenRouter API 密鑰是必需的" }, + "optional": "可選", "advancedConfigLabel": "進階設定", "searchMinScoreLabel": "搜尋分數閾值", "searchMinScoreDescription": "搜尋結果所需的最低相似度分數(0.0-1.0)。較低的值會傳回更多結果,但可能較不相關。較高的值會傳回較少但更相關的結果。",