fix: add Mistral provider support to webview UI

- Add Mistral option to provider dropdown
- Add Mistral API key configuration UI
- Add Mistral model selection dropdown
- Add validation schema for Mistral provider
- Handle Mistral API key in secret management
This commit is contained in:
Roo Code 2025-07-18 23:47:47 +00:00
parent 8ba958123b
commit 49c884b080

View file

@ -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">