Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/features/mistral-provider-support/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Mistral Provider Support Plan

## Runtime

- Add `mistral` to `DEFAULT_PROVIDERS` with `apiType: "mistral"`, default base URL `https://api.mistral.ai/v1`, Mistral website links, and disabled default state.
- Register `mistral` in `providerRegistry` as an OpenAI-compatible AI SDK provider.
- Use provider DB model metadata for model list refreshes and use `generate-text` verification with `mistral-small-latest`.

## Renderer And Deeplinks

- Add `mistral` to provider DB-backed refresh hints.
- Add `mistral` to provider install custom types and the manual deeplink playground.
- Wire `ModelIcon.vue` to the existing Mistral color SVG.
- Expose Mistral AI in the custom provider API type select.

## Compatibility

- Existing users keep their stored provider settings. The provider helper appends the new default if it is missing.
- Existing custom providers with id `mistral` are not overwritten by migration code.
20 changes: 20 additions & 0 deletions docs/features/mistral-provider-support/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Mistral Provider Support Spec

## User Story

Users can enable Mistral AI from the built-in Model Providers list, enter a Mistral API key, refresh models, verify the provider, and use Mistral chat and vision-capable models without editing provider files or creating a custom OpenAI-compatible provider.

## Acceptance Criteria

- A disabled built-in provider with id `mistral` appears in default provider settings.
- The provider uses `https://api.mistral.ai/v1` as its default base URL and Bearer API key authentication.
- Mistral uses the existing OpenAI-compatible runtime with no new SDK dependency.
- Refreshing models maps existing provider DB metadata for Mistral, including vision, tool call, reasoning, context, and output limits.
- Provider verification sends a small generate-text request to `mistral-small-latest`.
- Provider install deeplinks support built-in `id: "mistral"` and custom `type: "mistral"`.

## Non-Goals

- Add a dedicated Mistral SDK package.
- Add new IPC routes or renderer APIs.
- Change existing custom provider behavior beyond allowing `mistral` as a supported type.
8 changes: 8 additions & 0 deletions docs/features/mistral-provider-support/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Mistral Provider Support Tasks

- [x] Add SDD spec, plan, and task documents.
- [x] Add built-in Mistral provider metadata.
- [x] Register Mistral in the AI SDK provider registry.
- [x] Add renderer, deeplink, icon, and manual playground wiring.
- [x] Add targeted tests for provider metadata, runtime mapping, verification, and icon/deeplink support.
- [x] Run formatting, i18n, lint, typecheck, and targeted tests.
31 changes: 16 additions & 15 deletions src/main/presenter/configPresenter/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,22 @@ export const DEFAULT_PROVIDERS: LLM_PROVIDER_BASE[] = [
}
},

{
id: 'mistral',
name: 'Mistral',
apiType: 'mistral',
apiKey: '',
baseUrl: 'https://api.mistral.ai/v1',
enable: false,
websites: {
official: 'https://mistral.ai',
apiKey: 'https://console.mistral.ai/api-keys/',
docs: 'https://docs.mistral.ai/',
models: 'https://docs.mistral.ai/getting-started/models/',
defaultBaseUrl: 'https://api.mistral.ai/v1'
}
},

{
id: 'grok',
name: 'Grok',
Expand Down Expand Up @@ -611,21 +627,6 @@ export const DEFAULT_PROVIDERS: LLM_PROVIDER_BASE[] = [
// }
// },
// {
// id: 'mistral',
// name: 'Mistral',
// apiType: 'mistral',
// apiKey: '',
// baseUrl: 'https://api.mistral.ai',
// enable: false,
// websites: {
// official: 'https://mistral.ai',
// apiKey: 'https://console.mistral.ai/api-keys/',
// docs: 'https://docs.mistral.ai',
// models: 'https://docs.mistral.ai/getting-started/models/models_overview',
// defaultBaseUrl: 'https://api.mistral.ai'
// }
// },
// {
// id: 'jina',
// name: 'Jina',
// apiType: 'jina',
Expand Down
16 changes: 16 additions & 0 deletions src/main/presenter/llmProviderPresenter/providerRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,21 @@ const PROVIDER_ID_REGISTRY = new Map<string, AiSdkProviderDefinition>([
...OPENAI_BASE
})
],
[
'mistral',
createDefinition({
...OPENAI_BASE,
modelSource: 'provider-db',
providerDbSourceId: 'mistral',
providerDbGroup: 'default',
checkStrategy: 'generate-text',
credentialStrategy: 'api-key',
checkModelId: 'mistral-small-latest',
checkPrompt: 'Hello',
checkTemperature: 0.2,
checkMaxTokens: 16
})
],
[
'minimax',
createDefinition({
Expand Down Expand Up @@ -434,6 +449,7 @@ const PROVIDER_API_TYPE_REGISTRY = new Map<string, AiSdkProviderDefinition>([
['gemini', PROVIDER_ID_REGISTRY.get('gemini')!],
['grok', PROVIDER_ID_REGISTRY.get('grok')!],
['groq', PROVIDER_ID_REGISTRY.get('groq')!],
['mistral', PROVIDER_ID_REGISTRY.get('mistral')!],
['new-api', PROVIDER_ID_REGISTRY.get('new-api')!],
['o3fan', PROVIDER_ID_REGISTRY.get('o3fan')!],
['openai', PROVIDER_ID_REGISTRY.get('openai')!],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,8 @@ export class AiSdkProvider extends BaseLLMProvider {
}

private mapProviderDbModels(group: string): MODEL_META[] {
const resolvedId = modelCapabilities.resolveProviderId(this.provider.id) || this.provider.id
const sourceId = this.definition.providerDbSourceId || this.provider.id
const resolvedId = modelCapabilities.resolveProviderId(sourceId) || sourceId
const provider = providerDbLoader.getProvider(resolvedId)
if (!provider || !Array.isArray(provider.models)) {
return []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
<SelectItem value="gemini">Gemini</SelectItem>
<SelectItem value="anthropic">Anthropic</SelectItem>
<SelectItem value="ollama">Ollama</SelectItem>
<!-- <SelectItem value="groq">Groq</SelectItem>
<SelectItem value="mistral">Mistral AI</SelectItem>
<!-- <SelectItem value="groq">Groq</SelectItem>
<SelectItem value="cohere">Cohere</SelectItem>
<SelectItem value="zhinao">智脑</SelectItem>
<SelectItem value="custom">自定义</SelectItem> -->
Expand Down
20 changes: 19 additions & 1 deletion src/renderer/src/assets/llm-icons/mistral-color.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/renderer/src/components/icons/ModelIcon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import ollamaColorIcon from '@/assets/llm-icons/ollama.svg?url'
import doubaoColorIcon from '@/assets/llm-icons/doubao-color.svg?url'
import dimcodeColorIcon from '@/assets/llm-icons/dimcode.svg?url'
import minimaxColorIcon from '@/assets/llm-icons/minimax-color.svg?url'
import mistralColorIcon from '@/assets/llm-icons/mistral-color.svg?url'
import fireworksColorIcon from '@/assets/llm-icons/fireworks-color.svg?url'
import zerooneColorIcon from '@/assets/llm-icons/zeroone.svg?url'
import xaiColorIcon from '@/assets/llm-icons/xai.svg?url'
Expand Down Expand Up @@ -106,6 +107,7 @@ const icons = {
ollama: ollamaColorIcon,
doubao: doubaoColorIcon,
minimax: minimaxColorIcon,
mistral: mistralColorIcon,
fireworks: fireworksColorIcon,
zeabur: zeaburColorIcon,
zeroone: zerooneColorIcon,
Expand Down
2 changes: 1 addition & 1 deletion src/shared/providerDbCatalog.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const PROVIDER_DB_BACKED_PROVIDER_IDS = new Set(['doubao', 'zhipu', 'minimax', 'o3fan'])
const PROVIDER_DB_BACKED_PROVIDER_IDS = new Set(['doubao', 'zhipu', 'minimax', 'mistral', 'o3fan'])

export const isProviderDbBackedProvider = (providerId: string | undefined | null): boolean => {
if (!providerId) {
Expand Down
1 change: 1 addition & 0 deletions src/shared/providerDeeplink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const SUPPORTED_PROVIDER_INSTALL_CUSTOM_TYPES = [
'openai-compatible',
'openai-responses',
'lmstudio',
'mistral',
'together',
'groq',
'grok',
Expand Down
20 changes: 20 additions & 0 deletions test/main/presenter/configPresenter/defaultProviders.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { describe, expect, it } from 'vitest'
import { DEFAULT_PROVIDERS } from '../../../../src/main/presenter/configPresenter/providers'

describe('DEFAULT_PROVIDERS', () => {
it('includes Mistral as a disabled built-in OpenAI-compatible provider', () => {
expect(DEFAULT_PROVIDERS).toContainEqual(
expect.objectContaining({
id: 'mistral',
name: 'Mistral',
apiType: 'mistral',
baseUrl: 'https://api.mistral.ai/v1',
enable: false,
websites: expect.objectContaining({
apiKey: 'https://console.mistral.ai/api-keys/',
defaultBaseUrl: 'https://api.mistral.ai/v1'
})
})
)
})
})
Loading