From 2a5f75ab8b6a0fd2116869a82844ef2ce80be522 Mon Sep 17 00:00:00 2001 From: George Goranov Date: Fri, 21 Nov 2025 18:04:33 +0200 Subject: [PATCH] Fix various issues that the original PR missed. --- packages/types/src/codebase-index.ts | 5 +- src/core/webview/ClineProvider.ts | 4 + src/core/webview/webviewMessageHandler.ts | 17 ++ .../__tests__/config-manager.spec.ts | 14 +- src/services/code-index/config-manager.ts | 10 +- .../embedders/__tests__/bedrock.spec.ts | 20 +- src/services/code-index/embedders/bedrock.ts | 16 +- src/services/code-index/service-factory.ts | 5 +- src/shared/WebviewMessage.ts | 3 + .../src/components/chat/CodeIndexPopover.tsx | 211 +++++++++++++++++- webview-ui/src/i18n/locales/en/settings.json | 8 + 11 files changed, 273 insertions(+), 40 deletions(-) diff --git a/packages/types/src/codebase-index.ts b/packages/types/src/codebase-index.ts index be7778f538..d56d4d0a2b 100644 --- a/packages/types/src/codebase-index.ts +++ b/packages/types/src/codebase-index.ts @@ -22,7 +22,7 @@ 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"]) + .enum(["openai", "ollama", "openai-compatible", "gemini", "mistral", "vercel-ai-gateway", "bedrock"]) .optional(), codebaseIndexEmbedderBaseUrl: z.string().optional(), codebaseIndexEmbedderModelId: z.string().optional(), @@ -36,6 +36,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 diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 91b8687966..9c71938f2a 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1946,6 +1946,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 @@ -2164,6 +2166,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 af5f9925c3..635129b6c7 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -2439,6 +2439,11 @@ export const webviewMessageHandler = async ( const settings = message.codeIndexSettings + // DEBUG: Log received settings + provider.log( + `[DEBUG] Received settings to save: provider=${settings.codebaseIndexEmbedderProvider}, bedrockRegion=${settings.codebaseIndexBedrockRegion}, bedrockProfile=${settings.codebaseIndexBedrockProfile}`, + ) + try { // Check if embedder provider has changed const currentConfig = getGlobalState("codebaseIndexConfig") || {} @@ -2455,10 +2460,17 @@ 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, } + // DEBUG: Log what we're saving to global state + provider.log( + `[DEBUG] Saving to global state: bedrockRegion=${globalStateConfig.codebaseIndexBedrockRegion}, bedrockProfile=${globalStateConfig.codebaseIndexBedrockProfile}`, + ) + // Save global state first await updateGlobalState("codebaseIndexConfig", globalStateConfig) @@ -2494,6 +2506,11 @@ export const webviewMessageHandler = async ( ) } + // DEBUG: Log what we're sending back to webview + provider.log( + `[DEBUG] Sending success response to webview: bedrockRegion=${globalStateConfig.codebaseIndexBedrockRegion}, bedrockProfile=${globalStateConfig.codebaseIndexBedrockProfile}`, + ) + // Send success response first - settings are saved regardless of validation await provider.postMessageToWebview({ type: "codeIndexSettingsSaved", diff --git a/src/services/code-index/__tests__/config-manager.spec.ts b/src/services/code-index/__tests__/config-manager.spec.ts index 9fc096ba74..83c6a58911 100644 --- a/src/services/code-index/__tests__/config-manager.spec.ts +++ b/src/services/code-index/__tests__/config-manager.spec.ts @@ -98,7 +98,7 @@ describe("CodeIndexConfigManager", () => { const result = await configManager.loadConfiguration() - expect(result.currentConfig).toEqual({ + expect(result.currentConfig).toMatchObject({ isConfigured: false, embedderProvider: "openai", modelId: undefined, @@ -129,7 +129,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 +162,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 +199,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 +237,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 +275,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 +1286,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 a1fb2f4a0e..a2eac70a69 100644 --- a/src/services/code-index/config-manager.ts +++ b/src/services/code-index/config-manager.ts @@ -139,9 +139,9 @@ export class CodeIndexConfigManager { this.geminiOptions = geminiApiKey ? { apiKey: geminiApiKey } : undefined this.mistralOptions = mistralApiKey ? { apiKey: mistralApiKey } : undefined this.vercelAiGatewayOptions = vercelAiGatewayApiKey ? { apiKey: vercelAiGatewayApiKey } : undefined - this.bedrockOptions = bedrockRegion - ? { region: bedrockRegion, profile: bedrockProfile || undefined } - : undefined + // Set bedrockOptions only if both region and profile are provided + this.bedrockOptions = + bedrockRegion && bedrockProfile ? { region: bedrockRegion, profile: bedrockProfile } : undefined } /** @@ -252,9 +252,11 @@ export class CodeIndexConfigManager { const isConfigured = !!(apiKey && qdrantUrl) return isConfigured } else if (this.embedderProvider === "bedrock") { + // Both region and profile are required for Bedrock const region = this.bedrockOptions?.region + const profile = this.bedrockOptions?.profile const qdrantUrl = this.qdrantUrl - const isConfigured = !!(region && qdrantUrl) + const isConfigured = !!(region && profile && qdrantUrl) return isConfigured } return false // Should not happen if embedderProvider is always set correctly diff --git a/src/services/code-index/embedders/__tests__/bedrock.spec.ts b/src/services/code-index/embedders/__tests__/bedrock.spec.ts index b88ce786fd..8af4e855ad 100644 --- a/src/services/code-index/embedders/__tests__/bedrock.spec.ts +++ b/src/services/code-index/embedders/__tests__/bedrock.spec.ts @@ -76,7 +76,7 @@ describe("BedrockEmbedder", () => { send: mockSend, })) - embedder = new BedrockEmbedder("us-east-1", "amazon.titan-embed-text-v2:0") + embedder = new BedrockEmbedder("us-east-1", "test-profile", "amazon.titan-embed-text-v2:0") }) afterEach(() => { @@ -84,17 +84,21 @@ describe("BedrockEmbedder", () => { }) describe("constructor", () => { - it("should initialize with provided region and model", () => { + it("should initialize with provided region, profile and model", () => { expect(embedder.embedderInfo.name).toBe("bedrock") }) - it("should use default region if not provided", () => { - const defaultEmbedder = new BedrockEmbedder() - expect(defaultEmbedder).toBeDefined() + it("should require both region and profile", () => { + expect(() => new BedrockEmbedder("", "profile", "model")).toThrow( + "Both region and profile are required for AWS Bedrock embedder", + ) + expect(() => new BedrockEmbedder("us-east-1", "", "model")).toThrow( + "Both region and profile are required for AWS Bedrock embedder", + ) }) - it("should use profile if provided", () => { - const profileEmbedder = new BedrockEmbedder("us-west-2", undefined, "dev-profile") + it("should use profile for credentials", () => { + const profileEmbedder = new BedrockEmbedder("us-west-2", "dev-profile") expect(profileEmbedder).toBeDefined() }) }) @@ -169,7 +173,7 @@ describe("BedrockEmbedder", () => { }) it("should handle Cohere model format", async () => { - const cohereEmbedder = new BedrockEmbedder("us-east-1", "cohere.embed-english-v3") + const cohereEmbedder = new BedrockEmbedder("us-east-1", "test-profile", "cohere.embed-english-v3") const testTexts = ["Hello world"] const mockResponse = { body: new TextEncoder().encode( diff --git a/src/services/code-index/embedders/bedrock.ts b/src/services/code-index/embedders/bedrock.ts index 9bdaf60226..48e6976c57 100644 --- a/src/services/code-index/embedders/bedrock.ts +++ b/src/services/code-index/embedders/bedrock.ts @@ -22,17 +22,21 @@ export class BedrockEmbedder implements IEmbedder { /** * Creates a new AWS Bedrock embedder - * @param region AWS region for Bedrock service + * @param region AWS region for Bedrock service (required) + * @param profile AWS profile name for credentials (required) * @param modelId Optional model ID override - * @param profile Optional AWS profile name for credentials */ constructor( - private readonly region: string = "us-east-1", + private readonly region: string, + private readonly profile: string, modelId?: string, - private readonly profile?: string, ) { - // Initialize the Bedrock client with appropriate credentials - const credentials = this.profile ? fromIni({ profile: this.profile }) : fromEnv() + if (!region || !profile) { + throw new Error("Both region and profile are required for AWS Bedrock embedder") + } + + // Initialize the Bedrock client with credentials from the specified profile + const credentials = fromIni({ profile: this.profile }) this.bedrockClient = new BedrockRuntimeClient({ region: this.region, diff --git a/src/services/code-index/service-factory.ts b/src/services/code-index/service-factory.ts index 54e54520f2..76cf54161f 100644 --- a/src/services/code-index/service-factory.ts +++ b/src/services/code-index/service-factory.ts @@ -81,10 +81,11 @@ export class CodeIndexServiceFactory { } return new VercelAiGatewayEmbedder(config.vercelAiGatewayOptions.apiKey, config.modelId) } else if (provider === "bedrock") { - if (!config.bedrockOptions?.region) { + // Both region and profile are required for Bedrock + if (!config.bedrockOptions?.region || !config.bedrockOptions?.profile) { throw new Error(t("embeddings:serviceFactory.bedrockConfigMissing")) } - return new BedrockEmbedder(config.bedrockOptions.region, config.modelId, config.bedrockOptions.profile) + return new BedrockEmbedder(config.bedrockOptions.region, config.bedrockOptions.profile, config.modelId) } throw new Error( diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index d43a2fce04..9c9913abb5 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -288,10 +288,13 @@ export interface WebviewMessage { | "gemini" | "mistral" | "vercel-ai-gateway" + | "bedrock" codebaseIndexEmbedderBaseUrl?: string codebaseIndexEmbedderModelId: string codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers codebaseIndexOpenAiCompatibleBaseUrl?: string + codebaseIndexBedrockRegion?: string + codebaseIndexBedrockProfile?: string codebaseIndexSearchMaxResults?: number codebaseIndexSearchMinScore?: number diff --git a/webview-ui/src/components/chat/CodeIndexPopover.tsx b/webview-ui/src/components/chat/CodeIndexPopover.tsx index 45bf4224a1..57e82e2fc9 100644 --- a/webview-ui/src/components/chat/CodeIndexPopover.tsx +++ b/webview-ui/src/components/chat/CodeIndexPopover.tsx @@ -65,6 +65,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 @@ -149,6 +153,17 @@ 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() + .min(1, t("settings:codeIndex.validation.bedrockProfileRequired")), + codebaseIndexEmbedderModelId: z + .string() + .min(1, t("settings:codeIndex.validation.modelSelectionRequired")), + }) + default: return baseSchema } @@ -187,6 +202,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: "", @@ -210,6 +227,14 @@ export const CodeIndexPopover: React.FC = ({ // Initialize settings from global state useEffect(() => { if (codebaseIndexConfig) { + // DEBUG: Log what we're loading from config + console.log("[CodeIndexPopover] Loading settings from codebaseIndexConfig:", { + bedrockRegion: codebaseIndexConfig.codebaseIndexBedrockRegion, + bedrockProfile: codebaseIndexConfig.codebaseIndexBedrockProfile, + provider: codebaseIndexConfig.codebaseIndexEmbedderProvider, + fullConfig: codebaseIndexConfig, + }) + const settings = { codebaseIndexEnabled: codebaseIndexConfig.codebaseIndexEnabled ?? true, codebaseIndexQdrantUrl: codebaseIndexConfig.codebaseIndexQdrantUrl || "", @@ -222,6 +247,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 || "", @@ -230,6 +257,12 @@ export const CodeIndexPopover: React.FC = ({ codebaseIndexMistralApiKey: "", codebaseIndexVercelAiGatewayApiKey: "", } + + console.log("[CodeIndexPopover] Setting initial/current settings to:", { + bedrockRegion: settings.codebaseIndexBedrockRegion, + bedrockProfile: settings.codebaseIndexBedrockProfile, + }) + setInitialSettings(settings) setCurrentSettings(settings) @@ -278,15 +311,64 @@ export const CodeIndexPopover: React.FC = ({ } else if (event.data.type === "codeIndexSettingsSaved") { if (event.data.success) { setSaveStatus("saved") - // Update initial settings to match current settings after successful save - // This ensures hasUnsavedChanges becomes false - const savedSettings = { ...currentSettingsRef.current } - setInitialSettings(savedSettings) - // Also update current settings to maintain consistency - setCurrentSettings(savedSettings) + + // DEBUG: Log what backend returned + console.log("[CodeIndexPopover] Received save success from backend:", { + bedrockRegion: event.data.settings?.codebaseIndexBedrockRegion, + bedrockProfile: event.data.settings?.codebaseIndexBedrockProfile, + allSettings: event.data.settings, + }) + + // Use the settings returned from the backend to update both initial and current settings + // This ensures we have the exact values that were saved + if (event.data.settings) { + const savedSettings = { + codebaseIndexEnabled: event.data.settings.codebaseIndexEnabled ?? true, + codebaseIndexQdrantUrl: event.data.settings.codebaseIndexQdrantUrl || "", + codebaseIndexEmbedderProvider: + event.data.settings.codebaseIndexEmbedderProvider || "openai", + codebaseIndexEmbedderBaseUrl: event.data.settings.codebaseIndexEmbedderBaseUrl || "", + codebaseIndexEmbedderModelId: event.data.settings.codebaseIndexEmbedderModelId || "", + codebaseIndexEmbedderModelDimension: + event.data.settings.codebaseIndexEmbedderModelDimension || undefined, + codebaseIndexSearchMaxResults: + event.data.settings.codebaseIndexSearchMaxResults ?? + CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_RESULTS, + codebaseIndexSearchMinScore: + event.data.settings.codebaseIndexSearchMinScore ?? + CODEBASE_INDEX_DEFAULTS.DEFAULT_SEARCH_MIN_SCORE, + codebaseIndexBedrockRegion: event.data.settings.codebaseIndexBedrockRegion || "", + codebaseIndexBedrockProfile: event.data.settings.codebaseIndexBedrockProfile || "", + codeIndexOpenAiKey: currentSettingsRef.current.codeIndexOpenAiKey, + codeIndexQdrantApiKey: currentSettingsRef.current.codeIndexQdrantApiKey, + codebaseIndexOpenAiCompatibleBaseUrl: + event.data.settings.codebaseIndexOpenAiCompatibleBaseUrl || "", + codebaseIndexOpenAiCompatibleApiKey: + currentSettingsRef.current.codebaseIndexOpenAiCompatibleApiKey, + codebaseIndexGeminiApiKey: currentSettingsRef.current.codebaseIndexGeminiApiKey, + codebaseIndexMistralApiKey: currentSettingsRef.current.codebaseIndexMistralApiKey, + codebaseIndexVercelAiGatewayApiKey: + currentSettingsRef.current.codebaseIndexVercelAiGatewayApiKey, + } + + console.log("[CodeIndexPopover] Updated settings after save:", { + bedrockRegion: savedSettings.codebaseIndexBedrockRegion, + bedrockProfile: savedSettings.codebaseIndexBedrockProfile, + }) + + console.log("[CodeIndexPopover] About to update state with saved settings:", { + bedrockRegion: savedSettings.codebaseIndexBedrockRegion, + bedrockProfile: savedSettings.codebaseIndexBedrockProfile, + }) + + setInitialSettings(savedSettings) + setCurrentSettings(savedSettings) + + console.log("[CodeIndexPopover] State updated with saved settings") + } + // Request secret status to ensure we have the latest state // This is important to maintain placeholder display after save - vscode.postMessage({ type: "requestCodeIndexSecretStatus" }) setSaveStatus("idle") @@ -440,6 +522,7 @@ export const CodeIndexPopover: React.FC = ({ errors[err.path[0] as string] = err.message } }) + console.log("[CodeIndexPopover] Validation errors:", errors) setFormErrors(errors) } return false @@ -483,6 +566,8 @@ export const CodeIndexPopover: React.FC = ({ useEscapeKey(open, handlePopoverClose) const handleSaveSettings = () => { + console.log("[CodeIndexPopover] handleSaveSettings called") + // Validate settings before saving if (!validateSettings()) { return @@ -511,6 +596,14 @@ export const CodeIndexPopover: React.FC = ({ // Always include codebaseIndexEnabled to ensure it's persisted settingsToSave.codebaseIndexEnabled = currentSettings.codebaseIndexEnabled + // DEBUG: Log Bedrock settings being saved + console.log("[CodeIndexPopover] Saving settings:", { + provider: settingsToSave.codebaseIndexEmbedderProvider, + bedrockRegion: settingsToSave.codebaseIndexBedrockRegion, + bedrockProfile: settingsToSave.codebaseIndexBedrockProfile, + allSettings: settingsToSave, + }) + // Save settings to backend vscode.postMessage({ type: "saveCodeIndexSettingsAtomic", @@ -531,7 +624,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 +763,9 @@ export const CodeIndexPopover: React.FC = ({ {t("settings:codeIndex.vercelAiGatewayProvider")} + + {t("settings:codeIndex.bedrockProvider")} + @@ -716,7 +813,7 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( @@ -973,7 +1070,7 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( @@ -1038,7 +1135,7 @@ export const CodeIndexPopover: React.FC = ({ {getAvailableModels().map((modelId) => { const model = codebaseIndexModels?.[ - currentSettings.codebaseIndexEmbedderProvider + currentSettings.codebaseIndexEmbedderProvider as keyof typeof codebaseIndexModels ]?.[modelId] return ( @@ -1108,7 +1205,97 @@ 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 ( diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index dfccc49cc4..f1b06b329c 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -69,6 +69,12 @@ "vercelAiGatewayProvider": "Vercel AI Gateway", "vercelAiGatewayApiKeyLabel": "API Key", "vercelAiGatewayApiKeyPlaceholder": "Enter your Vercel AI Gateway API key", + "bedrockProvider": "AWS Bedrock", + "bedrockRegionLabel": "AWS Region", + "bedrockRegionPlaceholder": "us-east-1", + "bedrockProfileLabel": "AWS Profile", + "bedrockProfilePlaceholder": "default", + "bedrockProfileDescription": "AWS profile name from ~/.aws/credentials (required).", "openaiCompatibleProvider": "OpenAI Compatible", "openAiKeyLabel": "OpenAI API Key", "openAiKeyPlaceholder": "Enter your OpenAI API key", @@ -135,6 +141,8 @@ "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", "ollamaBaseUrlRequired": "Ollama base URL is required", "baseUrlRequired": "Base URL is required", "modelDimensionMinValue": "Model dimension must be greater than 0"