Browse Source

feat: Add Mistral embedding provider (#5932) (#5946)

* feat: add Mistral embedding provider with OpenAI Compatible Wrapper

- Implement MistralEmbedder class using OpenAI-compatible API
- Add comprehensive unit tests with 100% coverage
- Update type definitions for Mistral provider support
- Integrate Mistral option in UI components and configuration
- Add internationalization support for Mistral provider
- Fix API key storage and retrieval for embedding providers
- Update service factory to support Mistral embeddings
- Add proper error handling and validation

This implementation allows users to use Mistral's embedding models
through the existing OpenAI-compatible wrapper approach, providing
a seamless integration experience.

* feat: add Mistral embedding provider support

- Implement MistralEmbedder class with API integration
- Add Mistral models to embedding model configurations
- Update UI to include Mistral provider option
- Add comprehensive unit tests for Mistral embedder
- Update type definitions and interfaces
- Add internationalization support for Mistral provider

* fix: add missing translations for Mistral embedding provider

* fix: address PR review feedback - improve translations and add clarifying comment
SannidhyaSah 5 months ago
parent
commit
7ddc4e64f3
49 changed files with 513 additions and 6 deletions
  1. 3 1
      packages/types/src/codebase-index.ts
  2. 1 0
      packages/types/src/global-settings.ts
  3. 8 0
      src/core/webview/webviewMessageHandler.ts
  4. 1 0
      src/i18n/locales/ca/embeddings.json
  5. 1 0
      src/i18n/locales/de/embeddings.json
  6. 1 0
      src/i18n/locales/en/embeddings.json
  7. 1 0
      src/i18n/locales/es/embeddings.json
  8. 1 0
      src/i18n/locales/fr/embeddings.json
  9. 1 0
      src/i18n/locales/hi/embeddings.json
  10. 1 0
      src/i18n/locales/id/embeddings.json
  11. 1 0
      src/i18n/locales/it/embeddings.json
  12. 1 0
      src/i18n/locales/ja/embeddings.json
  13. 1 0
      src/i18n/locales/ko/embeddings.json
  14. 1 0
      src/i18n/locales/nl/embeddings.json
  15. 1 0
      src/i18n/locales/pl/embeddings.json
  16. 1 0
      src/i18n/locales/pt-BR/embeddings.json
  17. 1 0
      src/i18n/locales/ru/embeddings.json
  18. 1 0
      src/i18n/locales/tr/embeddings.json
  19. 1 0
      src/i18n/locales/vi/embeddings.json
  20. 1 0
      src/i18n/locales/zh-CN/embeddings.json
  21. 1 0
      src/i18n/locales/zh-TW/embeddings.json
  22. 24 0
      src/services/code-index/config-manager.ts
  23. 193 0
      src/services/code-index/embedders/__tests__/mistral.spec.ts
  24. 91 0
      src/services/code-index/embedders/mistral.ts
  25. 2 0
      src/services/code-index/interfaces/config.ts
  26. 1 1
      src/services/code-index/interfaces/embedder.ts
  27. 1 1
      src/services/code-index/interfaces/manager.ts
  28. 6 0
      src/services/code-index/service-factory.ts
  29. 2 1
      src/shared/WebviewMessage.ts
  30. 7 1
      src/shared/embeddingModels.ts
  31. 84 1
      webview-ui/src/components/chat/CodeIndexPopover.tsx
  32. 4 0
      webview-ui/src/i18n/locales/ca/settings.json
  33. 4 0
      webview-ui/src/i18n/locales/de/settings.json
  34. 4 0
      webview-ui/src/i18n/locales/en/settings.json
  35. 4 0
      webview-ui/src/i18n/locales/es/settings.json
  36. 4 0
      webview-ui/src/i18n/locales/fr/settings.json
  37. 4 0
      webview-ui/src/i18n/locales/hi/settings.json
  38. 4 0
      webview-ui/src/i18n/locales/id/settings.json
  39. 4 0
      webview-ui/src/i18n/locales/it/settings.json
  40. 4 0
      webview-ui/src/i18n/locales/ja/settings.json
  41. 4 0
      webview-ui/src/i18n/locales/ko/settings.json
  42. 4 0
      webview-ui/src/i18n/locales/nl/settings.json
  43. 4 0
      webview-ui/src/i18n/locales/pl/settings.json
  44. 4 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  45. 4 0
      webview-ui/src/i18n/locales/ru/settings.json
  46. 4 0
      webview-ui/src/i18n/locales/tr/settings.json
  47. 4 0
      webview-ui/src/i18n/locales/vi/settings.json
  48. 4 0
      webview-ui/src/i18n/locales/zh-CN/settings.json
  49. 4 0
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 3 - 1
packages/types/src/codebase-index.ts

@@ -21,7 +21,7 @@ export const CODEBASE_INDEX_DEFAULTS = {
 export const codebaseIndexConfigSchema = z.object({
 	codebaseIndexEnabled: z.boolean().optional(),
 	codebaseIndexQdrantUrl: z.string().optional(),
-	codebaseIndexEmbedderProvider: z.enum(["openai", "ollama", "openai-compatible", "gemini"]).optional(),
+	codebaseIndexEmbedderProvider: z.enum(["openai", "ollama", "openai-compatible", "gemini", "mistral"]).optional(),
 	codebaseIndexEmbedderBaseUrl: z.string().optional(),
 	codebaseIndexEmbedderModelId: z.string().optional(),
 	codebaseIndexEmbedderModelDimension: z.number().optional(),
@@ -47,6 +47,7 @@ export const codebaseIndexModelsSchema = z.object({
 	ollama: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
 	"openai-compatible": z.record(z.string(), z.object({ dimension: z.number() })).optional(),
 	gemini: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
+	mistral: z.record(z.string(), z.object({ dimension: z.number() })).optional(),
 })
 
 export type CodebaseIndexModels = z.infer<typeof codebaseIndexModelsSchema>
@@ -62,6 +63,7 @@ export const codebaseIndexProviderSchema = z.object({
 	codebaseIndexOpenAiCompatibleApiKey: z.string().optional(),
 	codebaseIndexOpenAiCompatibleModelDimension: z.number().optional(),
 	codebaseIndexGeminiApiKey: z.string().optional(),
+	codebaseIndexMistralApiKey: z.string().optional(),
 })
 
 export type CodebaseIndexProvider = z.infer<typeof codebaseIndexProviderSchema>

+ 1 - 0
packages/types/src/global-settings.ts

@@ -162,6 +162,7 @@ export const SECRET_STATE_KEYS = [
 	"codeIndexQdrantApiKey",
 	"codebaseIndexOpenAiCompatibleApiKey",
 	"codebaseIndexGeminiApiKey",
+	"codebaseIndexMistralApiKey",
 ] as const satisfies readonly (keyof ProviderSettings)[]
 export type SecretState = Pick<ProviderSettings, (typeof SECRET_STATE_KEYS)[number]>
 

+ 8 - 0
src/core/webview/webviewMessageHandler.ts

@@ -1970,6 +1970,12 @@ export const webviewMessageHandler = async (
 						settings.codebaseIndexGeminiApiKey,
 					)
 				}
+				if (settings.codebaseIndexMistralApiKey !== undefined) {
+					await provider.contextProxy.storeSecret(
+						"codebaseIndexMistralApiKey",
+						settings.codebaseIndexMistralApiKey,
+					)
+				}
 
 				// Send success response first - settings are saved regardless of validation
 				await provider.postMessageToWebview({
@@ -2062,6 +2068,7 @@ export const webviewMessageHandler = async (
 				"codebaseIndexOpenAiCompatibleApiKey",
 			))
 			const hasGeminiApiKey = !!(await provider.context.secrets.get("codebaseIndexGeminiApiKey"))
+			const hasMistralApiKey = !!(await provider.context.secrets.get("codebaseIndexMistralApiKey"))
 
 			provider.postMessageToWebview({
 				type: "codeIndexSecretStatus",
@@ -2070,6 +2077,7 @@ export const webviewMessageHandler = async (
 					hasQdrantApiKey,
 					hasOpenAiCompatibleApiKey,
 					hasGeminiApiKey,
+					hasMistralApiKey,
 				},
 			})
 			break

+ 1 - 0
src/i18n/locales/ca/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Falta la configuració d'Ollama per crear l'embedder",
 		"openAiCompatibleConfigMissing": "Falta la configuració compatible amb OpenAI per crear l'embedder",
 		"geminiConfigMissing": "Falta la configuració de Gemini per crear l'embedder",
+		"mistralConfigMissing": "Falta la configuració de Mistral 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ó.",

+ 1 - 0
src/i18n/locales/de/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Ollama-Konfiguration fehlt für die Erstellung des Embedders",
 		"openAiCompatibleConfigMissing": "OpenAI-kompatible Konfiguration fehlt für die Erstellung des Embedders",
 		"geminiConfigMissing": "Gemini-Konfiguration fehlt für die Erstellung des Embedders",
+		"mistralConfigMissing": "Mistral-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.",

+ 1 - 0
src/i18n/locales/en/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Ollama configuration missing for embedder creation",
 		"openAiCompatibleConfigMissing": "OpenAI Compatible configuration missing for embedder creation",
 		"geminiConfigMissing": "Gemini configuration missing for embedder creation",
+		"mistralConfigMissing": "Mistral 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.",

+ 1 - 0
src/i18n/locales/es/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Falta la configuración de Ollama para crear el incrustador",
 		"openAiCompatibleConfigMissing": "Falta la configuración compatible con OpenAI para crear el incrustador",
 		"geminiConfigMissing": "Falta la configuración de Gemini para crear el incrustador",
+		"mistralConfigMissing": "Falta la configuración de Mistral 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.",

+ 1 - 0
src/i18n/locales/fr/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Configuration Ollama manquante pour la création de l'embedder",
 		"openAiCompatibleConfigMissing": "Configuration compatible OpenAI manquante pour la création de l'embedder",
 		"geminiConfigMissing": "Configuration Gemini manquante pour la création de l'embedder",
+		"mistralConfigMissing": "Configuration Mistral 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.",

+ 1 - 0
src/i18n/locales/hi/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "एम्बेडर बनाने के लिए Ollama कॉन्फ़िगरेशन गायब है",
 		"openAiCompatibleConfigMissing": "एम्बेडर बनाने के लिए OpenAI संगत कॉन्फ़िगरेशन गायब है",
 		"geminiConfigMissing": "एम्बेडर बनाने के लिए Gemini कॉन्फ़िगरेशन गायब है",
+		"mistralConfigMissing": "एम्बेडर निर्माण के लिए मिस्ट्रल कॉन्फ़िगरेशन गायब है",
 		"invalidEmbedderType": "अमान्य एम्बेडर प्रकार कॉन्फ़िगर किया गया: {{embedderProvider}}",
 		"vectorDimensionNotDeterminedOpenAiCompatible": "प्रदाता '{{provider}}' के साथ मॉडल '{{modelId}}' के लिए वेक्टर आयाम निर्धारित नहीं कर सका। कृपया सुनिश्चित करें कि OpenAI-संगत प्रदाता सेटिंग्स में 'एम्बेडिंग आयाम' सही तरीके से सेट है।",
 		"vectorDimensionNotDetermined": "प्रदाता '{{provider}}' के साथ मॉडल '{{modelId}}' के लिए वेक्टर आयाम निर्धारित नहीं कर सका। मॉडल प्रोफ़ाइल या कॉन्फ़िगरेशन की जांच करें।",

+ 1 - 0
src/i18n/locales/id/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Konfigurasi Ollama tidak ada untuk membuat embedder",
 		"openAiCompatibleConfigMissing": "Konfigurasi yang kompatibel dengan OpenAI tidak ada untuk membuat embedder",
 		"geminiConfigMissing": "Konfigurasi Gemini tidak ada untuk membuat embedder",
+		"mistralConfigMissing": "Konfigurasi Mistral 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.",

+ 1 - 0
src/i18n/locales/it/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Configurazione Ollama mancante per la creazione dell'embedder",
 		"openAiCompatibleConfigMissing": "Configurazione compatibile con OpenAI mancante per la creazione dell'embedder",
 		"geminiConfigMissing": "Configurazione Gemini mancante per la creazione dell'embedder",
+		"mistralConfigMissing": "Configurazione di Mistral 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.",

+ 1 - 0
src/i18n/locales/ja/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "エンベッダー作成のためのOllama設定がありません",
 		"openAiCompatibleConfigMissing": "エンベッダー作成のためのOpenAI互換設定がありません",
 		"geminiConfigMissing": "エンベッダー作成のためのGemini設定がありません",
+		"mistralConfigMissing": "エンベッダー作成のためのMistral設定がありません",
 		"invalidEmbedderType": "無効なエンベッダータイプが設定されています: {{embedderProvider}}",
 		"vectorDimensionNotDeterminedOpenAiCompatible": "プロバイダー '{{provider}}' のモデル '{{modelId}}' の埋め込み次元を決定できませんでした。OpenAI互換プロバイダー設定で「埋め込み次元」が正しく設定されていることを確認してください。",
 		"vectorDimensionNotDetermined": "プロバイダー '{{provider}}' のモデル '{{modelId}}' の埋め込み次元を決定できませんでした。モデルプロファイルまたは設定を確認してください。",

+ 1 - 0
src/i18n/locales/ko/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "임베더 생성을 위한 Ollama 구성이 누락되었습니다",
 		"openAiCompatibleConfigMissing": "임베더 생성을 위한 OpenAI 호환 구성이 누락되었습니다",
 		"geminiConfigMissing": "임베더 생성을 위한 Gemini 구성이 누락되었습니다",
+		"mistralConfigMissing": "임베더 생성을 위한 Mistral 구성이 없습니다",
 		"invalidEmbedderType": "잘못된 임베더 유형이 구성되었습니다: {{embedderProvider}}",
 		"vectorDimensionNotDeterminedOpenAiCompatible": "프로바이더 '{{provider}}'의 모델 '{{modelId}}'에 대한 벡터 차원을 결정할 수 없습니다. OpenAI 호환 프로바이더 설정에서 '임베딩 차원'이 올바르게 설정되어 있는지 확인하세요.",
 		"vectorDimensionNotDetermined": "프로바이더 '{{provider}}'의 모델 '{{modelId}}'에 대한 벡터 차원을 결정할 수 없습니다. 모델 프로필 또는 구성을 확인하세요.",

+ 1 - 0
src/i18n/locales/nl/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Ollama-configuratie ontbreekt voor het maken van embedder",
 		"openAiCompatibleConfigMissing": "OpenAI-compatibele configuratie ontbreekt voor het maken van embedder",
 		"geminiConfigMissing": "Gemini-configuratie ontbreekt voor het maken van embedder",
+		"mistralConfigMissing": "Mistral-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.",

+ 1 - 0
src/i18n/locales/pl/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Brak konfiguracji Ollama do utworzenia embeddera",
 		"openAiCompatibleConfigMissing": "Brak konfiguracji kompatybilnej z OpenAI do utworzenia embeddera",
 		"geminiConfigMissing": "Brak konfiguracji Gemini do utworzenia embeddera",
+		"mistralConfigMissing": "Brak konfiguracji Mistral 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ę.",

+ 1 - 0
src/i18n/locales/pt-BR/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Configuração do Ollama ausente para criação do embedder",
 		"openAiCompatibleConfigMissing": "Configuração compatível com OpenAI ausente para criação do embedder",
 		"geminiConfigMissing": "Configuração do Gemini ausente para criação do embedder",
+		"mistralConfigMissing": "Configuração do Mistral 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.",

+ 1 - 0
src/i18n/locales/ru/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Отсутствует конфигурация Ollama для создания эмбеддера",
 		"openAiCompatibleConfigMissing": "Отсутствует конфигурация, совместимая с OpenAI, для создания эмбеддера",
 		"geminiConfigMissing": "Отсутствует конфигурация Gemini для создания эмбеддера",
+		"mistralConfigMissing": "Конфигурация Mistral отсутствует для создания эмбеддера",
 		"invalidEmbedderType": "Настроен недопустимый тип эмбеддера: {{embedderProvider}}",
 		"vectorDimensionNotDeterminedOpenAiCompatible": "Не удалось определить размерность вектора для модели '{{modelId}}' с провайдером '{{provider}}'. Убедитесь, что 'Размерность эмбеддинга' правильно установлена в настройках провайдера, совместимого с OpenAI.",
 		"vectorDimensionNotDetermined": "Не удалось определить размерность вектора для модели '{{modelId}}' с провайдером '{{provider}}'. Проверьте профили модели или конфигурацию.",

+ 1 - 0
src/i18n/locales/tr/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Gömücü oluşturmak için Ollama yapılandırması eksik",
 		"openAiCompatibleConfigMissing": "Gömücü oluşturmak için OpenAI uyumlu yapılandırması eksik",
 		"geminiConfigMissing": "Gömücü oluşturmak için Gemini yapılandırması eksik",
+		"mistralConfigMissing": "Gömücü oluşturmak için Mistral 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.",

+ 1 - 0
src/i18n/locales/vi/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "Thiếu cấu hình Ollama để tạo embedder",
 		"openAiCompatibleConfigMissing": "Thiếu cấu hình tương thích OpenAI để tạo embedder",
 		"geminiConfigMissing": "Thiếu cấu hình Gemini để tạo embedder",
+		"mistralConfigMissing": "Thiếu cấu hình Mistral để 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.",

+ 1 - 0
src/i18n/locales/zh-CN/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "创建嵌入器缺少 Ollama 配置",
 		"openAiCompatibleConfigMissing": "创建嵌入器缺少 OpenAI 兼容配置",
 		"geminiConfigMissing": "创建嵌入器缺少 Gemini 配置",
+		"mistralConfigMissing": "创建嵌入器时缺少 Mistral 配置",
 		"invalidEmbedderType": "配置的嵌入器类型无效:{{embedderProvider}}",
 		"vectorDimensionNotDeterminedOpenAiCompatible": "无法确定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量维度。请确保在 OpenAI 兼容提供商设置中正确设置了「嵌入维度」。",
 		"vectorDimensionNotDetermined": "无法确定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量维度。请检查模型配置文件或配置。",

+ 1 - 0
src/i18n/locales/zh-TW/embeddings.json

@@ -46,6 +46,7 @@
 		"ollamaConfigMissing": "建立嵌入器缺少 Ollama 設定",
 		"openAiCompatibleConfigMissing": "建立嵌入器缺少 OpenAI 相容設定",
 		"geminiConfigMissing": "建立嵌入器缺少 Gemini 設定",
+		"mistralConfigMissing": "建立嵌入器時缺少 Mistral 設定",
 		"invalidEmbedderType": "設定的嵌入器類型無效:{{embedderProvider}}",
 		"vectorDimensionNotDeterminedOpenAiCompatible": "無法確定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量維度。請確保在 OpenAI 相容提供商設定中正確設定了「嵌入維度」。",
 		"vectorDimensionNotDetermined": "無法確定提供商 '{{provider}}' 的模型 '{{modelId}}' 的向量維度。請檢查模型設定檔或設定。",

+ 24 - 0
src/services/code-index/config-manager.ts

@@ -18,6 +18,7 @@ export class CodeIndexConfigManager {
 	private ollamaOptions?: ApiHandlerOptions
 	private openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
 	private geminiOptions?: { apiKey: string }
+	private mistralOptions?: { apiKey: string }
 	private qdrantUrl?: string = "http://localhost:6333"
 	private qdrantApiKey?: string
 	private searchMinScore?: number
@@ -67,6 +68,7 @@ export class CodeIndexConfigManager {
 		const openAiCompatibleBaseUrl = codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl ?? ""
 		const openAiCompatibleApiKey = this.contextProxy?.getSecret("codebaseIndexOpenAiCompatibleApiKey") ?? ""
 		const geminiApiKey = this.contextProxy?.getSecret("codebaseIndexGeminiApiKey") ?? ""
+		const mistralApiKey = this.contextProxy?.getSecret("codebaseIndexMistralApiKey") ?? ""
 
 		// Update instance variables with configuration
 		this.codebaseIndexEnabled = codebaseIndexEnabled ?? true
@@ -100,6 +102,8 @@ export class CodeIndexConfigManager {
 			this.embedderProvider = "openai-compatible"
 		} else if (codebaseIndexEmbedderProvider === "gemini") {
 			this.embedderProvider = "gemini"
+		} else if (codebaseIndexEmbedderProvider === "mistral") {
+			this.embedderProvider = "mistral"
 		} else {
 			this.embedderProvider = "openai"
 		}
@@ -119,6 +123,7 @@ export class CodeIndexConfigManager {
 				: undefined
 
 		this.geminiOptions = geminiApiKey ? { apiKey: geminiApiKey } : undefined
+		this.mistralOptions = mistralApiKey ? { apiKey: mistralApiKey } : undefined
 	}
 
 	/**
@@ -135,6 +140,7 @@ export class CodeIndexConfigManager {
 			ollamaOptions?: ApiHandlerOptions
 			openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
 			geminiOptions?: { apiKey: string }
+			mistralOptions?: { apiKey: string }
 			qdrantUrl?: string
 			qdrantApiKey?: string
 			searchMinScore?: number
@@ -153,6 +159,7 @@ export class CodeIndexConfigManager {
 			openAiCompatibleBaseUrl: this.openAiCompatibleOptions?.baseUrl ?? "",
 			openAiCompatibleApiKey: this.openAiCompatibleOptions?.apiKey ?? "",
 			geminiApiKey: this.geminiOptions?.apiKey ?? "",
+			mistralApiKey: this.mistralOptions?.apiKey ?? "",
 			qdrantUrl: this.qdrantUrl ?? "",
 			qdrantApiKey: this.qdrantApiKey ?? "",
 		}
@@ -176,6 +183,7 @@ export class CodeIndexConfigManager {
 				ollamaOptions: this.ollamaOptions,
 				openAiCompatibleOptions: this.openAiCompatibleOptions,
 				geminiOptions: this.geminiOptions,
+				mistralOptions: this.mistralOptions,
 				qdrantUrl: this.qdrantUrl,
 				qdrantApiKey: this.qdrantApiKey,
 				searchMinScore: this.currentSearchMinScore,
@@ -208,6 +216,11 @@ export class CodeIndexConfigManager {
 			const qdrantUrl = this.qdrantUrl
 			const isConfigured = !!(apiKey && qdrantUrl)
 			return isConfigured
+		} else if (this.embedderProvider === "mistral") {
+			const apiKey = this.mistralOptions?.apiKey
+			const qdrantUrl = this.qdrantUrl
+			const isConfigured = !!(apiKey && qdrantUrl)
+			return isConfigured
 		}
 		return false // Should not happen if embedderProvider is always set correctly
 	}
@@ -241,6 +254,7 @@ export class CodeIndexConfigManager {
 		const prevOpenAiCompatibleApiKey = prev?.openAiCompatibleApiKey ?? ""
 		const prevModelDimension = prev?.modelDimension
 		const prevGeminiApiKey = prev?.geminiApiKey ?? ""
+		const prevMistralApiKey = prev?.mistralApiKey ?? ""
 		const prevQdrantUrl = prev?.qdrantUrl ?? ""
 		const prevQdrantApiKey = prev?.qdrantApiKey ?? ""
 
@@ -277,6 +291,7 @@ export class CodeIndexConfigManager {
 		const currentOpenAiCompatibleApiKey = this.openAiCompatibleOptions?.apiKey ?? ""
 		const currentModelDimension = this.modelDimension
 		const currentGeminiApiKey = this.geminiOptions?.apiKey ?? ""
+		const currentMistralApiKey = this.mistralOptions?.apiKey ?? ""
 		const currentQdrantUrl = this.qdrantUrl ?? ""
 		const currentQdrantApiKey = this.qdrantApiKey ?? ""
 
@@ -295,6 +310,14 @@ export class CodeIndexConfigManager {
 			return true
 		}
 
+		if (prevGeminiApiKey !== currentGeminiApiKey) {
+			return true
+		}
+
+		if (prevMistralApiKey !== currentMistralApiKey) {
+			return true
+		}
+
 		// Check for model dimension changes (generic for all providers)
 		if (prevModelDimension !== currentModelDimension) {
 			return true
@@ -351,6 +374,7 @@ export class CodeIndexConfigManager {
 			ollamaOptions: this.ollamaOptions,
 			openAiCompatibleOptions: this.openAiCompatibleOptions,
 			geminiOptions: this.geminiOptions,
+			mistralOptions: this.mistralOptions,
 			qdrantUrl: this.qdrantUrl,
 			qdrantApiKey: this.qdrantApiKey,
 			searchMinScore: this.currentSearchMinScore,

+ 193 - 0
src/services/code-index/embedders/__tests__/mistral.spec.ts

@@ -0,0 +1,193 @@
+import { vitest, describe, it, expect, beforeEach } from "vitest"
+import type { MockedClass } from "vitest"
+import { MistralEmbedder } from "../mistral"
+import { OpenAICompatibleEmbedder } from "../openai-compatible"
+
+// Mock the OpenAICompatibleEmbedder
+vitest.mock("../openai-compatible")
+
+// Mock TelemetryService
+vitest.mock("@roo-code/telemetry", () => ({
+	TelemetryService: {
+		instance: {
+			captureEvent: vitest.fn(),
+		},
+	},
+}))
+
+const MockedOpenAICompatibleEmbedder = OpenAICompatibleEmbedder as MockedClass<typeof OpenAICompatibleEmbedder>
+
+describe("MistralEmbedder", () => {
+	let embedder: MistralEmbedder
+
+	beforeEach(() => {
+		vitest.clearAllMocks()
+	})
+
+	describe("constructor", () => {
+		it("should create an instance with default model when no model specified", () => {
+			// Arrange
+			const apiKey = "test-mistral-api-key"
+
+			// Act
+			embedder = new MistralEmbedder(apiKey)
+
+			// Assert
+			expect(MockedOpenAICompatibleEmbedder).toHaveBeenCalledWith(
+				"https://api.mistral.ai/v1",
+				apiKey,
+				"codestral-embed-2505",
+				8191,
+			)
+		})
+
+		it("should create an instance with specified model", () => {
+			// Arrange
+			const apiKey = "test-mistral-api-key"
+			const modelId = "custom-embed-model"
+
+			// Act
+			embedder = new MistralEmbedder(apiKey, modelId)
+
+			// Assert
+			expect(MockedOpenAICompatibleEmbedder).toHaveBeenCalledWith(
+				"https://api.mistral.ai/v1",
+				apiKey,
+				"custom-embed-model",
+				8191,
+			)
+		})
+
+		it("should throw error when API key is not provided", () => {
+			// Act & Assert
+			expect(() => new MistralEmbedder("")).toThrow("validation.apiKeyRequired")
+			expect(() => new MistralEmbedder(null as any)).toThrow("validation.apiKeyRequired")
+			expect(() => new MistralEmbedder(undefined as any)).toThrow("validation.apiKeyRequired")
+		})
+	})
+
+	describe("embedderInfo", () => {
+		it("should return correct embedder info", () => {
+			// Arrange
+			embedder = new MistralEmbedder("test-api-key")
+
+			// Act
+			const info = embedder.embedderInfo
+
+			// Assert
+			expect(info).toEqual({
+				name: "mistral",
+			})
+		})
+
+		describe("createEmbeddings", () => {
+			let mockCreateEmbeddings: any
+
+			beforeEach(() => {
+				mockCreateEmbeddings = vitest.fn()
+				MockedOpenAICompatibleEmbedder.prototype.createEmbeddings = mockCreateEmbeddings
+			})
+
+			it("should use instance model when no model parameter provided", async () => {
+				// Arrange
+				embedder = new MistralEmbedder("test-api-key")
+				const texts = ["test text 1", "test text 2"]
+				const mockResponse = {
+					embeddings: [
+						[0.1, 0.2],
+						[0.3, 0.4],
+					],
+				}
+				mockCreateEmbeddings.mockResolvedValue(mockResponse)
+
+				// Act
+				const result = await embedder.createEmbeddings(texts)
+
+				// Assert
+				expect(mockCreateEmbeddings).toHaveBeenCalledWith(texts, "codestral-embed-2505")
+				expect(result).toEqual(mockResponse)
+			})
+
+			it("should use provided model parameter when specified", async () => {
+				// Arrange
+				embedder = new MistralEmbedder("test-api-key", "custom-embed-model")
+				const texts = ["test text 1", "test text 2"]
+				const mockResponse = {
+					embeddings: [
+						[0.1, 0.2],
+						[0.3, 0.4],
+					],
+				}
+				mockCreateEmbeddings.mockResolvedValue(mockResponse)
+
+				// Act
+				const result = await embedder.createEmbeddings(texts, "codestral-embed-2505")
+
+				// Assert
+				expect(mockCreateEmbeddings).toHaveBeenCalledWith(texts, "codestral-embed-2505")
+				expect(result).toEqual(mockResponse)
+			})
+
+			it("should handle errors from OpenAICompatibleEmbedder", async () => {
+				// Arrange
+				embedder = new MistralEmbedder("test-api-key")
+				const texts = ["test text"]
+				const error = new Error("Embedding failed")
+				mockCreateEmbeddings.mockRejectedValue(error)
+
+				// Act & Assert
+				await expect(embedder.createEmbeddings(texts)).rejects.toThrow("Embedding failed")
+			})
+		})
+	})
+
+	describe("validateConfiguration", () => {
+		let mockValidateConfiguration: any
+
+		beforeEach(() => {
+			mockValidateConfiguration = vitest.fn()
+			MockedOpenAICompatibleEmbedder.prototype.validateConfiguration = mockValidateConfiguration
+		})
+
+		it("should delegate validation to OpenAICompatibleEmbedder", async () => {
+			// Arrange
+			embedder = new MistralEmbedder("test-api-key")
+			mockValidateConfiguration.mockResolvedValue({ valid: true })
+
+			// Act
+			const result = await embedder.validateConfiguration()
+
+			// Assert
+			expect(mockValidateConfiguration).toHaveBeenCalled()
+			expect(result).toEqual({ valid: true })
+		})
+
+		it("should pass through validation errors from OpenAICompatibleEmbedder", async () => {
+			// Arrange
+			embedder = new MistralEmbedder("test-api-key")
+			mockValidateConfiguration.mockResolvedValue({
+				valid: false,
+				error: "embeddings:validation.authenticationFailed",
+			})
+
+			// Act
+			const result = await embedder.validateConfiguration()
+
+			// Assert
+			expect(mockValidateConfiguration).toHaveBeenCalled()
+			expect(result).toEqual({
+				valid: false,
+				error: "embeddings:validation.authenticationFailed",
+			})
+		})
+
+		it("should handle validation exceptions", async () => {
+			// Arrange
+			embedder = new MistralEmbedder("test-api-key")
+			mockValidateConfiguration.mockRejectedValue(new Error("Validation failed"))
+
+			// Act & Assert
+			await expect(embedder.validateConfiguration()).rejects.toThrow("Validation failed")
+		})
+	})
+})

+ 91 - 0
src/services/code-index/embedders/mistral.ts

@@ -0,0 +1,91 @@
+import { OpenAICompatibleEmbedder } from "./openai-compatible"
+import { IEmbedder, EmbeddingResponse, EmbedderInfo } from "../interfaces/embedder"
+import { MAX_ITEM_TOKENS } from "../constants"
+import { t } from "../../../i18n"
+import { TelemetryEventName } from "@roo-code/types"
+import { TelemetryService } from "@roo-code/telemetry"
+
+/**
+ * Mistral embedder implementation that wraps the OpenAI Compatible embedder
+ * with configuration for Mistral's embedding API.
+ *
+ * Supported models:
+ * - codestral-embed-2505 (dimension: 1536)
+ */
+export class MistralEmbedder implements IEmbedder {
+	private readonly openAICompatibleEmbedder: OpenAICompatibleEmbedder
+	private static readonly MISTRAL_BASE_URL = "https://api.mistral.ai/v1"
+	private static readonly DEFAULT_MODEL = "codestral-embed-2505"
+	private readonly modelId: string
+
+	/**
+	 * Creates a new Mistral embedder
+	 * @param apiKey The Mistral API key for authentication
+	 * @param modelId The model ID to use (defaults to codestral-embed-2505)
+	 */
+	constructor(apiKey: string, modelId?: string) {
+		if (!apiKey) {
+			throw new Error(t("embeddings:validation.apiKeyRequired"))
+		}
+
+		// Use provided model or default
+		this.modelId = modelId || MistralEmbedder.DEFAULT_MODEL
+
+		// Create an OpenAI Compatible embedder with Mistral's configuration
+		this.openAICompatibleEmbedder = new OpenAICompatibleEmbedder(
+			MistralEmbedder.MISTRAL_BASE_URL,
+			apiKey,
+			this.modelId,
+			MAX_ITEM_TOKENS, // This is the max token limit (8191), not the embedding dimension
+		)
+	}
+
+	/**
+	 * Creates embeddings for the given texts using Mistral's embedding API
+	 * @param texts Array of text strings to embed
+	 * @param model Optional model identifier (uses constructor model if not provided)
+	 * @returns Promise resolving to embedding response
+	 */
+	async createEmbeddings(texts: string[], model?: string): Promise<EmbeddingResponse> {
+		try {
+			// Use the provided model or fall back to the instance's model
+			const modelToUse = model || this.modelId
+			return await this.openAICompatibleEmbedder.createEmbeddings(texts, modelToUse)
+		} catch (error) {
+			TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
+				error: error instanceof Error ? error.message : String(error),
+				stack: error instanceof Error ? error.stack : undefined,
+				location: "MistralEmbedder:createEmbeddings",
+			})
+			throw error
+		}
+	}
+
+	/**
+	 * Validates the Mistral embedder configuration by delegating to the underlying OpenAI-compatible embedder
+	 * @returns Promise resolving to validation result with success status and optional error message
+	 */
+	async validateConfiguration(): Promise<{ valid: boolean; error?: string }> {
+		try {
+			// Delegate validation to the OpenAI-compatible embedder
+			// The error messages will be specific to Mistral since we're using Mistral's base URL
+			return await this.openAICompatibleEmbedder.validateConfiguration()
+		} catch (error) {
+			TelemetryService.instance.captureEvent(TelemetryEventName.CODE_INDEX_ERROR, {
+				error: error instanceof Error ? error.message : String(error),
+				stack: error instanceof Error ? error.stack : undefined,
+				location: "MistralEmbedder:validateConfiguration",
+			})
+			throw error
+		}
+	}
+
+	/**
+	 * Returns information about this embedder
+	 */
+	get embedderInfo(): EmbedderInfo {
+		return {
+			name: "mistral",
+		}
+	}
+}

+ 2 - 0
src/services/code-index/interfaces/config.ts

@@ -13,6 +13,7 @@ export interface CodeIndexConfig {
 	ollamaOptions?: ApiHandlerOptions
 	openAiCompatibleOptions?: { baseUrl: string; apiKey: string }
 	geminiOptions?: { apiKey: string }
+	mistralOptions?: { apiKey: string }
 	qdrantUrl?: string
 	qdrantApiKey?: string
 	searchMinScore?: number
@@ -33,6 +34,7 @@ export type PreviousConfigSnapshot = {
 	openAiCompatibleBaseUrl?: string
 	openAiCompatibleApiKey?: string
 	geminiApiKey?: string
+	mistralApiKey?: string
 	qdrantUrl?: string
 	qdrantApiKey?: string
 }

+ 1 - 1
src/services/code-index/interfaces/embedder.ts

@@ -28,7 +28,7 @@ export interface EmbeddingResponse {
 	}
 }
 
-export type AvailableEmbedders = "openai" | "ollama" | "openai-compatible" | "gemini"
+export type AvailableEmbedders = "openai" | "ollama" | "openai-compatible" | "gemini" | "mistral"
 
 export interface EmbedderInfo {
 	name: AvailableEmbedders

+ 1 - 1
src/services/code-index/interfaces/manager.ts

@@ -70,7 +70,7 @@ export interface ICodeIndexManager {
 }
 
 export type IndexingState = "Standby" | "Indexing" | "Indexed" | "Error"
-export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini"
+export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini" | "mistral"
 
 export interface IndexProgressUpdate {
 	systemStatus: IndexingState

+ 6 - 0
src/services/code-index/service-factory.ts

@@ -3,6 +3,7 @@ import { OpenAiEmbedder } from "./embedders/openai"
 import { CodeIndexOllamaEmbedder } from "./embedders/ollama"
 import { OpenAICompatibleEmbedder } from "./embedders/openai-compatible"
 import { GeminiEmbedder } from "./embedders/gemini"
+import { MistralEmbedder } from "./embedders/mistral"
 import { EmbedderProvider, getDefaultModelId, getModelDimension } from "../../shared/embeddingModels"
 import { QdrantVectorStore } from "./vector-store/qdrant-client"
 import { codeParser, DirectoryScanner, FileWatcher } from "./processors"
@@ -64,6 +65,11 @@ export class CodeIndexServiceFactory {
 				throw new Error(t("embeddings:serviceFactory.geminiConfigMissing"))
 			}
 			return new GeminiEmbedder(config.geminiOptions.apiKey, config.modelId)
+		} else if (provider === "mistral") {
+			if (!config.mistralOptions?.apiKey) {
+				throw new Error(t("embeddings:serviceFactory.mistralConfigMissing"))
+			}
+			return new MistralEmbedder(config.mistralOptions.apiKey, config.modelId)
 		}
 
 		throw new Error(

+ 2 - 1
src/shared/WebviewMessage.ts

@@ -244,7 +244,7 @@ export interface WebviewMessage {
 		// Global state settings
 		codebaseIndexEnabled: boolean
 		codebaseIndexQdrantUrl: string
-		codebaseIndexEmbedderProvider: "openai" | "ollama" | "openai-compatible" | "gemini"
+		codebaseIndexEmbedderProvider: "openai" | "ollama" | "openai-compatible" | "gemini" | "mistral"
 		codebaseIndexEmbedderBaseUrl?: string
 		codebaseIndexEmbedderModelId: string
 		codebaseIndexEmbedderModelDimension?: number // Generic dimension for all providers
@@ -257,6 +257,7 @@ export interface WebviewMessage {
 		codeIndexQdrantApiKey?: string
 		codebaseIndexOpenAiCompatibleApiKey?: string
 		codebaseIndexGeminiApiKey?: string
+		codebaseIndexMistralApiKey?: string
 	}
 }
 

+ 7 - 1
src/shared/embeddingModels.ts

@@ -2,7 +2,7 @@
  * Defines profiles for different embedding models, including their dimensions.
  */
 
-export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini" // Add other providers as needed
+export type EmbedderProvider = "openai" | "ollama" | "openai-compatible" | "gemini" | "mistral" // Add other providers as needed
 
 export interface EmbeddingModelProfile {
 	dimension: number
@@ -50,6 +50,9 @@ export const EMBEDDING_MODEL_PROFILES: EmbeddingModelProfiles = {
 		"text-embedding-004": { dimension: 768 },
 		"gemini-embedding-001": { dimension: 3072, scoreThreshold: 0.4 },
 	},
+	mistral: {
+		"codestral-embed-2505": { dimension: 1536, scoreThreshold: 0.4 },
+	},
 }
 
 /**
@@ -137,6 +140,9 @@ export function getDefaultModelId(provider: EmbedderProvider): string {
 		case "gemini":
 			return "gemini-embedding-001"
 
+		case "mistral":
+			return "codestral-embed-2505"
+
 		default:
 			// Fallback for unknown providers
 			console.warn(`Unknown provider for default model ID: ${provider}. Falling back to OpenAI default.`)

+ 84 - 1
webview-ui/src/components/chat/CodeIndexPopover.tsx

@@ -68,6 +68,7 @@ interface LocalCodeIndexSettings {
 	codebaseIndexOpenAiCompatibleBaseUrl?: string
 	codebaseIndexOpenAiCompatibleApiKey?: string
 	codebaseIndexGeminiApiKey?: string
+	codebaseIndexMistralApiKey?: string
 }
 
 // Validation schema for codebase index settings
@@ -126,6 +127,14 @@ const createValidationSchema = (provider: EmbedderProvider, t: any) => {
 					.min(1, t("settings:codeIndex.validation.modelSelectionRequired")),
 			})
 
+		case "mistral":
+			return baseSchema.extend({
+				codebaseIndexMistralApiKey: z.string().min(1, t("settings:codeIndex.validation.mistralApiKeyRequired")),
+				codebaseIndexEmbedderModelId: z
+					.string()
+					.min(1, t("settings:codeIndex.validation.modelSelectionRequired")),
+			})
+
 		default:
 			return baseSchema
 	}
@@ -169,6 +178,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 		codebaseIndexOpenAiCompatibleBaseUrl: "",
 		codebaseIndexOpenAiCompatibleApiKey: "",
 		codebaseIndexGeminiApiKey: "",
+		codebaseIndexMistralApiKey: "",
 	})
 
 	// Initial settings state - stores the settings when popover opens
@@ -202,6 +212,7 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 				codebaseIndexOpenAiCompatibleBaseUrl: codebaseIndexConfig.codebaseIndexOpenAiCompatibleBaseUrl || "",
 				codebaseIndexOpenAiCompatibleApiKey: "",
 				codebaseIndexGeminiApiKey: "",
+				codebaseIndexMistralApiKey: "",
 			}
 			setInitialSettings(settings)
 			setCurrentSettings(settings)
@@ -293,6 +304,9 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 					if (!prev.codebaseIndexGeminiApiKey || prev.codebaseIndexGeminiApiKey === SECRET_PLACEHOLDER) {
 						updated.codebaseIndexGeminiApiKey = secretStatus.hasGeminiApiKey ? SECRET_PLACEHOLDER : ""
 					}
+					if (!prev.codebaseIndexMistralApiKey || prev.codebaseIndexMistralApiKey === SECRET_PLACEHOLDER) {
+						updated.codebaseIndexMistralApiKey = secretStatus.hasMistralApiKey ? SECRET_PLACEHOLDER : ""
+					}
 
 					return updated
 				}
@@ -364,7 +378,8 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 				if (
 					key === "codeIndexOpenAiKey" ||
 					key === "codebaseIndexOpenAiCompatibleApiKey" ||
-					key === "codebaseIndexGeminiApiKey"
+					key === "codebaseIndexGeminiApiKey" ||
+					key === "codebaseIndexMistralApiKey"
 				) {
 					dataToValidate[key] = "placeholder-valid"
 				}
@@ -606,6 +621,9 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 												<SelectItem value="gemini">
 													{t("settings:codeIndex.geminiProvider")}
 												</SelectItem>
+												<SelectItem value="mistral">
+													{t("settings:codeIndex.mistralProvider")}
+												</SelectItem>
 											</SelectContent>
 										</Select>
 									</div>
@@ -933,6 +951,71 @@ export const CodeIndexPopover: React.FC<CodeIndexPopoverProps> = ({
 										</>
 									)}
 
+									{currentSettings.codebaseIndexEmbedderProvider === "mistral" && (
+										<>
+											<div className="space-y-2">
+												<label className="text-sm font-medium">
+													{t("settings:codeIndex.mistralApiKeyLabel")}
+												</label>
+												<VSCodeTextField
+													type="password"
+													value={currentSettings.codebaseIndexMistralApiKey || ""}
+													onInput={(e: any) =>
+														updateSetting("codebaseIndexMistralApiKey", e.target.value)
+													}
+													placeholder={t("settings:codeIndex.mistralApiKeyPlaceholder")}
+													className={cn("w-full", {
+														"border-red-500": formErrors.codebaseIndexMistralApiKey,
+													})}
+												/>
+												{formErrors.codebaseIndexMistralApiKey && (
+													<p className="text-xs text-vscode-errorForeground mt-1 mb-0">
+														{formErrors.codebaseIndexMistralApiKey}
+													</p>
+												)}
+											</div>
+
+											<div className="space-y-2">
+												<label className="text-sm font-medium">
+													{t("settings:codeIndex.modelLabel")}
+												</label>
+												<VSCodeDropdown
+													value={currentSettings.codebaseIndexEmbedderModelId}
+													onChange={(e: any) =>
+														updateSetting("codebaseIndexEmbedderModelId", e.target.value)
+													}
+													className={cn("w-full", {
+														"border-red-500": formErrors.codebaseIndexEmbedderModelId,
+													})}>
+													<VSCodeOption value="" className="p-2">
+														{t("settings:codeIndex.selectModel")}
+													</VSCodeOption>
+													{getAvailableModels().map((modelId) => {
+														const model =
+															codebaseIndexModels?.[
+																currentSettings.codebaseIndexEmbedderProvider
+															]?.[modelId]
+														return (
+															<VSCodeOption key={modelId} value={modelId} className="p-2">
+																{modelId}{" "}
+																{model
+																	? t("settings:codeIndex.modelDimensions", {
+																			dimension: model.dimension,
+																		})
+																	: ""}
+															</VSCodeOption>
+														)
+													})}
+												</VSCodeDropdown>
+												{formErrors.codebaseIndexEmbedderModelId && (
+													<p className="text-xs text-vscode-errorForeground mt-1 mb-0">
+														{formErrors.codebaseIndexEmbedderModelId}
+													</p>
+												)}
+											</div>
+										</>
+									)}
+
 									{/* Qdrant Settings */}
 									<div className="space-y-2">
 										<label className="text-sm font-medium">

+ 4 - 0
webview-ui/src/i18n/locales/ca/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Clau API:",
 		"geminiApiKeyPlaceholder": "Introduïu la vostra clau d'API de Gemini",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Clau de l'API:",
+		"mistralApiKeyPlaceholder": "Introduïu la vostra clau de l'API de Mistral",
 		"openaiCompatibleProvider": "Compatible amb OpenAI",
 		"openAiKeyLabel": "Clau API OpenAI",
 		"openAiKeyPlaceholder": "Introduïu la vostra clau API OpenAI",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Cal un ID de model",
 			"modelDimensionRequired": "Cal una dimensió de model",
 			"geminiApiKeyRequired": "Cal una clau d'API de Gemini",
+			"mistralApiKeyRequired": "La clau de l'API de Mistral és requerida",
 			"ollamaBaseUrlRequired": "Cal una URL base d'Ollama",
 			"baseUrlRequired": "Cal una URL base",
 			"modelDimensionMinValue": "La dimensió del model ha de ser superior a 0"

+ 4 - 0
webview-ui/src/i18n/locales/de/settings.json

@@ -52,6 +52,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API-Schlüssel:",
 		"geminiApiKeyPlaceholder": "Geben Sie Ihren Gemini-API-Schlüssel ein",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API-Schlüssel:",
+		"mistralApiKeyPlaceholder": "Gib deinen Mistral-API-Schlüssel ein",
 		"openaiCompatibleProvider": "OpenAI-kompatibel",
 		"openAiKeyLabel": "OpenAI API-Schlüssel",
 		"openAiKeyPlaceholder": "Gib deinen OpenAI API-Schlüssel ein",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Modell-ID ist erforderlich",
 			"modelDimensionRequired": "Modellabmessung ist erforderlich",
 			"geminiApiKeyRequired": "Gemini-API-Schlüssel ist erforderlich",
+			"mistralApiKeyRequired": "Mistral-API-Schlüssel ist erforderlich",
 			"ollamaBaseUrlRequired": "Ollama-Basis-URL ist erforderlich",
 			"baseUrlRequired": "Basis-URL ist erforderlich",
 			"modelDimensionMinValue": "Modellabmessung muss größer als 0 sein"

+ 4 - 0
webview-ui/src/i18n/locales/en/settings.json

@@ -52,6 +52,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API Key:",
 		"geminiApiKeyPlaceholder": "Enter your Gemini API key",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API Key:",
+		"mistralApiKeyPlaceholder": "Enter your Mistral API key",
 		"openaiCompatibleProvider": "OpenAI Compatible",
 		"openAiKeyLabel": "OpenAI API Key",
 		"openAiKeyPlaceholder": "Enter your OpenAI API key",
@@ -116,6 +119,7 @@
 			"modelIdRequired": "Model ID is required",
 			"modelDimensionRequired": "Model dimension is required",
 			"geminiApiKeyRequired": "Gemini API key is required",
+			"mistralApiKeyRequired": "Mistral API key is required",
 			"ollamaBaseUrlRequired": "Ollama base URL is required",
 			"baseUrlRequired": "Base URL is required",
 			"modelDimensionMinValue": "Model dimension must be greater than 0"

+ 4 - 0
webview-ui/src/i18n/locales/es/settings.json

@@ -52,6 +52,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Clave API:",
 		"geminiApiKeyPlaceholder": "Introduce tu clave de API de Gemini",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Clave API:",
+		"mistralApiKeyPlaceholder": "Introduce tu clave de API de Mistral",
 		"openaiCompatibleProvider": "Compatible con OpenAI",
 		"openAiKeyLabel": "Clave API de OpenAI",
 		"openAiKeyPlaceholder": "Introduce tu clave API de OpenAI",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Se requiere el ID del modelo",
 			"modelDimensionRequired": "Se requiere la dimensión del modelo",
 			"geminiApiKeyRequired": "Se requiere la clave API de Gemini",
+			"mistralApiKeyRequired": "Se requiere la clave de API de Mistral",
 			"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"

+ 4 - 0
webview-ui/src/i18n/locales/fr/settings.json

@@ -52,6 +52,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Clé API :",
 		"geminiApiKeyPlaceholder": "Entrez votre clé API Gemini",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Clé d'API:",
+		"mistralApiKeyPlaceholder": "Entrez votre clé d'API Mistral",
 		"openaiCompatibleProvider": "Compatible OpenAI",
 		"openAiKeyLabel": "Clé API OpenAI",
 		"openAiKeyPlaceholder": "Entrez votre clé API OpenAI",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "L'ID du modèle est requis",
 			"modelDimensionRequired": "La dimension du modèle est requise",
 			"geminiApiKeyRequired": "La clé API Gemini est requise",
+			"mistralApiKeyRequired": "La clé API Mistral est requise",
 			"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"

+ 4 - 0
webview-ui/src/i18n/locales/hi/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API कुंजी:",
 		"geminiApiKeyPlaceholder": "अपना जेमिनी एपीआई कुंजी दर्ज करें",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API कुंजी:",
+		"mistralApiKeyPlaceholder": "अपनी मिस्ट्रल एपीआई कुंजी दर्ज करें",
 		"openaiCompatibleProvider": "OpenAI संगत",
 		"openAiKeyLabel": "OpenAI API कुंजी",
 		"openAiKeyPlaceholder": "अपना OpenAI API कुंजी दर्ज करें",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "मॉडल आईडी आवश्यक है",
 			"modelDimensionRequired": "मॉडल आयाम आवश्यक है",
 			"geminiApiKeyRequired": "Gemini API कुंजी आवश्यक है",
+			"mistralApiKeyRequired": "मिस्ट्रल एपीआई कुंजी आवश्यक है",
 			"ollamaBaseUrlRequired": "Ollama आधार URL आवश्यक है",
 			"baseUrlRequired": "आधार URL आवश्यक है",
 			"modelDimensionMinValue": "मॉडल आयाम 0 से बड़ा होना चाहिए"

+ 4 - 0
webview-ui/src/i18n/locales/id/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API Key:",
 		"geminiApiKeyPlaceholder": "Masukkan kunci API Gemini Anda",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Kunci API:",
+		"mistralApiKeyPlaceholder": "Masukkan kunci API Mistral Anda",
 		"openaiCompatibleProvider": "OpenAI Compatible",
 		"openAiKeyLabel": "OpenAI API Key",
 		"openAiKeyPlaceholder": "Masukkan kunci API OpenAI kamu",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "ID Model diperlukan",
 			"modelDimensionRequired": "Dimensi model diperlukan",
 			"geminiApiKeyRequired": "Kunci API Gemini diperlukan",
+			"mistralApiKeyRequired": "Kunci API Mistral diperlukan",
 			"ollamaBaseUrlRequired": "URL dasar Ollama diperlukan",
 			"baseUrlRequired": "URL dasar diperlukan",
 			"modelDimensionMinValue": "Dimensi model harus lebih besar dari 0"

+ 4 - 0
webview-ui/src/i18n/locales/it/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Chiave API:",
 		"geminiApiKeyPlaceholder": "Inserisci la tua chiave API Gemini",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Chiave API:",
+		"mistralApiKeyPlaceholder": "Inserisci la tua chiave API Mistral",
 		"openaiCompatibleProvider": "Compatibile con OpenAI",
 		"openAiKeyLabel": "Chiave API OpenAI",
 		"openAiKeyPlaceholder": "Inserisci la tua chiave API OpenAI",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "È richiesto l'ID del modello",
 			"modelDimensionRequired": "È richiesta la dimensione del modello",
 			"geminiApiKeyRequired": "È richiesta la chiave API Gemini",
+			"mistralApiKeyRequired": "La chiave API di Mistral è richiesta",
 			"ollamaBaseUrlRequired": "È richiesto l'URL di base di Ollama",
 			"baseUrlRequired": "È richiesto l'URL di base",
 			"modelDimensionMinValue": "La dimensione del modello deve essere maggiore di 0"

+ 4 - 0
webview-ui/src/i18n/locales/ja/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "APIキー:",
 		"geminiApiKeyPlaceholder": "Gemini APIキーを入力してください",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "APIキー:",
+		"mistralApiKeyPlaceholder": "Mistral APIキーを入力してください",
 		"openaiCompatibleProvider": "OpenAI互換",
 		"openAiKeyLabel": "OpenAI APIキー",
 		"openAiKeyPlaceholder": "OpenAI APIキーを入力してください",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "モデルIDが必要です",
 			"modelDimensionRequired": "モデルの次元が必要です",
 			"geminiApiKeyRequired": "Gemini APIキーが必要です",
+			"mistralApiKeyRequired": "Mistral APIキーが必要です",
 			"ollamaBaseUrlRequired": "OllamaのベースURLが必要です",
 			"baseUrlRequired": "ベースURLが必要です",
 			"modelDimensionMinValue": "モデルの次元は0より大きくなければなりません"

+ 4 - 0
webview-ui/src/i18n/locales/ko/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API 키:",
 		"geminiApiKeyPlaceholder": "Gemini API 키를 입력하세요",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API 키:",
+		"mistralApiKeyPlaceholder": "Mistral API 키를 입력하세요",
 		"openaiCompatibleProvider": "OpenAI 호환",
 		"openAiKeyLabel": "OpenAI API 키",
 		"openAiKeyPlaceholder": "OpenAI API 키를 입력하세요",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "모델 ID가 필요합니다",
 			"modelDimensionRequired": "모델 차원이 필요합니다",
 			"geminiApiKeyRequired": "Gemini API 키가 필요합니다",
+			"mistralApiKeyRequired": "Mistral API 키가 필요합니다",
 			"ollamaBaseUrlRequired": "Ollama 기본 URL이 필요합니다",
 			"baseUrlRequired": "기본 URL이 필요합니다",
 			"modelDimensionMinValue": "모델 차원은 0보다 커야 합니다"

+ 4 - 0
webview-ui/src/i18n/locales/nl/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API-sleutel:",
 		"geminiApiKeyPlaceholder": "Voer uw Gemini API-sleutel in",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API-sleutel:",
+		"mistralApiKeyPlaceholder": "Voer uw Mistral API-sleutel in",
 		"openaiCompatibleProvider": "OpenAI-compatibel",
 		"openAiKeyLabel": "OpenAI API-sleutel",
 		"openAiKeyPlaceholder": "Voer uw OpenAI API-sleutel in",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Model-ID is vereist",
 			"modelDimensionRequired": "Modelafmeting is vereist",
 			"geminiApiKeyRequired": "Gemini API-sleutel is vereist",
+			"mistralApiKeyRequired": "Mistral API-sleutel is vereist",
 			"ollamaBaseUrlRequired": "Ollama basis-URL is vereist",
 			"baseUrlRequired": "Basis-URL is vereist",
 			"modelDimensionMinValue": "Modelafmeting moet groter zijn dan 0"

+ 4 - 0
webview-ui/src/i18n/locales/pl/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Klucz API:",
 		"geminiApiKeyPlaceholder": "Wprowadź swój klucz API Gemini",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Klucz API:",
+		"mistralApiKeyPlaceholder": "Wprowadź swój klucz API Mistral",
 		"openaiCompatibleProvider": "Kompatybilny z OpenAI",
 		"openAiKeyLabel": "Klucz API OpenAI",
 		"openAiKeyPlaceholder": "Wprowadź swój klucz API OpenAI",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Wymagane jest ID modelu",
 			"modelDimensionRequired": "Wymagany jest wymiar modelu",
 			"geminiApiKeyRequired": "Wymagany jest klucz API Gemini",
+			"mistralApiKeyRequired": "Klucz API Mistral jest wymagany",
 			"ollamaBaseUrlRequired": "Wymagany jest bazowy adres URL Ollama",
 			"baseUrlRequired": "Wymagany jest bazowy adres URL",
 			"modelDimensionMinValue": "Wymiar modelu musi być większy niż 0"

+ 4 - 0
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Chave de API:",
 		"geminiApiKeyPlaceholder": "Digite sua chave de API do Gemini",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Chave de API:",
+		"mistralApiKeyPlaceholder": "Digite sua chave de API da Mistral",
 		"openaiCompatibleProvider": "Compatível com OpenAI",
 		"openAiKeyLabel": "Chave de API OpenAI",
 		"openAiKeyPlaceholder": "Digite sua chave de API OpenAI",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "O ID do modelo é obrigatório",
 			"modelDimensionRequired": "A dimensão do modelo é obrigatória",
 			"geminiApiKeyRequired": "A chave de API do Gemini é obrigatória",
+			"mistralApiKeyRequired": "A chave de API Mistral é obrigatória",
 			"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"

+ 4 - 0
webview-ui/src/i18n/locales/ru/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Ключ API:",
 		"geminiApiKeyPlaceholder": "Введите свой API-ключ Gemini",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Ключ API:",
+		"mistralApiKeyPlaceholder": "Введите свой API-ключ Mistral",
 		"openaiCompatibleProvider": "OpenAI-совместимый",
 		"openAiKeyLabel": "Ключ API OpenAI",
 		"openAiKeyPlaceholder": "Введите ваш ключ API OpenAI",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Требуется идентификатор модели",
 			"modelDimensionRequired": "Требуется размерность модели",
 			"geminiApiKeyRequired": "Требуется ключ API Gemini",
+			"mistralApiKeyRequired": "Требуется API-ключ Mistral",
 			"ollamaBaseUrlRequired": "Требуется базовый URL Ollama",
 			"baseUrlRequired": "Требуется базовый URL",
 			"modelDimensionMinValue": "Размерность модели должна быть больше 0"

+ 4 - 0
webview-ui/src/i18n/locales/tr/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API Anahtarı:",
 		"geminiApiKeyPlaceholder": "Gemini API anahtarınızı girin",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API Anahtarı:",
+		"mistralApiKeyPlaceholder": "Mistral API anahtarınızı girin",
 		"openaiCompatibleProvider": "OpenAI Uyumlu",
 		"openAiKeyLabel": "OpenAI API Anahtarı",
 		"openAiKeyPlaceholder": "OpenAI API anahtarınızı girin",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Model kimliği gereklidir",
 			"modelDimensionRequired": "Model boyutu gereklidir",
 			"geminiApiKeyRequired": "Gemini API anahtarı gereklidir",
+			"mistralApiKeyRequired": "Mistral API anahtarı gereklidir",
 			"ollamaBaseUrlRequired": "Ollama temel URL'si gereklidir",
 			"baseUrlRequired": "Temel URL'si gereklidir",
 			"modelDimensionMinValue": "Model boyutu 0'dan büyük olmalıdır"

+ 4 - 0
webview-ui/src/i18n/locales/vi/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "Khóa API:",
 		"geminiApiKeyPlaceholder": "Nhập khóa API Gemini của bạn",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "Khóa API:",
+		"mistralApiKeyPlaceholder": "Nhập khóa API Mistral của bạn",
 		"openaiCompatibleProvider": "Tương thích OpenAI",
 		"openAiKeyLabel": "Khóa API OpenAI",
 		"openAiKeyPlaceholder": "Nhập khóa API OpenAI của bạn",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "Yêu cầu ID mô hình",
 			"modelDimensionRequired": "Yêu cầu kích thước mô hình",
 			"geminiApiKeyRequired": "Yêu cầu khóa API Gemini",
+			"mistralApiKeyRequired": "Cần có khóa API của Mistral",
 			"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"

+ 4 - 0
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -52,6 +52,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API 密钥:",
 		"geminiApiKeyPlaceholder": "输入您的Gemini API密钥",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API 密钥:",
+		"mistralApiKeyPlaceholder": "输入您的 Mistral API 密钥",
 		"openaiCompatibleProvider": "OpenAI 兼容",
 		"openAiKeyLabel": "OpenAI API 密钥",
 		"openAiKeyPlaceholder": "输入你的 OpenAI API 密钥",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "需要模型 ID",
 			"modelDimensionRequired": "需要模型维度",
 			"geminiApiKeyRequired": "需要 Gemini API 密钥",
+			"mistralApiKeyRequired": "需要 Mistral API 密钥",
 			"ollamaBaseUrlRequired": "需要 Ollama 基础 URL",
 			"baseUrlRequired": "需要基础 URL",
 			"modelDimensionMinValue": "模型维度必须大于 0"

+ 4 - 0
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -47,6 +47,9 @@
 		"geminiProvider": "Gemini",
 		"geminiApiKeyLabel": "API 金鑰:",
 		"geminiApiKeyPlaceholder": "輸入您的Gemini API金鑰",
+		"mistralProvider": "Mistral",
+		"mistralApiKeyLabel": "API 金鑰:",
+		"mistralApiKeyPlaceholder": "輸入您的 Mistral API 金鑰",
 		"openaiCompatibleProvider": "OpenAI 相容",
 		"openAiKeyLabel": "OpenAI API 金鑰",
 		"openAiKeyPlaceholder": "輸入您的 OpenAI API 金鑰",
@@ -109,6 +112,7 @@
 			"modelIdRequired": "需要模型 ID",
 			"modelDimensionRequired": "需要模型維度",
 			"geminiApiKeyRequired": "需要 Gemini API 金鑰",
+			"mistralApiKeyRequired": "需要 Mistral API 金鑰",
 			"ollamaBaseUrlRequired": "需要 Ollama 基礎 URL",
 			"baseUrlRequired": "需要基礎 URL",
 			"modelDimensionMinValue": "模型維度必須大於 0"