feat: add Mistral AI as first-class alternative provider

This commit is contained in:
2026-03-01 14:41:42 +01:00
parent 886083ebc9
commit c911ec2354
22 changed files with 1425 additions and 167 deletions

View File

@@ -83,6 +83,13 @@ function setupChatApi() {
onA2UIMessage: vi.fn(() => vi.fn()),
onTokenUsage: vi.fn(() => vi.fn()),
dispatchA2UIAction: vi.fn(),
validateMistralApiKey: vi.fn().mockResolvedValue({ isValid: false, models: [] }),
setMistralApiKey: vi.fn().mockResolvedValue({ success: true }),
getMistralApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getTitleModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-haiku-4-5' }),
setTitleModel: vi.fn().mockResolvedValue({ success: true }),
getImageAnalysisModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-sonnet-4-5' }),
setImageAnalysisModel: vi.fn().mockResolvedValue({ success: true }),
} as never;
}

View File

@@ -39,6 +39,13 @@ describe('AssistantSidebar wiring', () => {
onA2UIMessage: vi.fn(() => vi.fn()),
onTokenUsage: vi.fn(() => vi.fn()),
dispatchA2UIAction: vi.fn(),
validateMistralApiKey: vi.fn().mockResolvedValue({ isValid: false, models: [] }),
setMistralApiKey: vi.fn().mockResolvedValue({ success: true }),
getMistralApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getTitleModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-haiku-4-5' }),
setTitleModel: vi.fn().mockResolvedValue({ success: true }),
getImageAnalysisModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-sonnet-4-5' }),
setImageAnalysisModel: vi.fn().mockResolvedValue({ success: true }),
} as never;
});

View File

@@ -45,6 +45,10 @@ describe('SettingsView i18n', () => {
getSystemPrompt: vi.fn().mockResolvedValue({ success: true, prompt: '' }),
getApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getAvailableModels: vi.fn().mockResolvedValue({ success: true, models: [], selectedModel: '' }),
getMistralApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getTitleModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-haiku-4-5' }),
getImageAnalysisModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-sonnet-4-5' }),
getModelCatalog: vi.fn().mockResolvedValue({ success: true, entries: [] }),
},
templates: {
...(window as Window & { electronAPI: any }).electronAPI?.templates,

View File

@@ -35,6 +35,10 @@ describe('MCPAgentButton uninstall', () => {
getSystemPrompt: vi.fn().mockResolvedValue({ success: true, prompt: '' }),
getApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getAvailableModels: vi.fn().mockResolvedValue({ success: true, models: [], selectedModel: '' }),
getMistralApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getTitleModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-haiku-4-5' }),
getImageAnalysisModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-sonnet-4-5' }),
getModelCatalog: vi.fn().mockResolvedValue({ success: true, entries: [] }),
},
templates: { getEnabledByKind: vi.fn().mockResolvedValue([]) },
projects: { update: vi.fn().mockResolvedValue({}) },

View File

@@ -37,6 +37,13 @@ describe('assistant sidebar guard rails', () => {
onA2UIMessage: vi.fn(() => vi.fn()),
onTokenUsage: vi.fn(() => vi.fn()),
dispatchA2UIAction: vi.fn(),
validateMistralApiKey: vi.fn().mockResolvedValue({ isValid: false, models: [] }),
setMistralApiKey: vi.fn().mockResolvedValue({ success: true }),
getMistralApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getTitleModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-haiku-4-5' }),
setTitleModel: vi.fn().mockResolvedValue({ success: true }),
getImageAnalysisModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-sonnet-4-5' }),
setImageAnalysisModel: vi.fn().mockResolvedValue({ success: true }),
} as never;
});

View File

@@ -48,6 +48,13 @@ describe('chat surface mode usage guards', () => {
onA2UIMessage: vi.fn(() => vi.fn()),
onTokenUsage: vi.fn(() => vi.fn()),
dispatchA2UIAction: vi.fn(),
validateMistralApiKey: vi.fn().mockResolvedValue({ isValid: false, models: [] }),
setMistralApiKey: vi.fn().mockResolvedValue({ success: true }),
getMistralApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getTitleModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-haiku-4-5' }),
setTitleModel: vi.fn().mockResolvedValue({ success: true }),
getImageAnalysisModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-sonnet-4-5' }),
setImageAnalysisModel: vi.fn().mockResolvedValue({ success: true }),
} as never;
});

View File

@@ -51,6 +51,13 @@ describe('chat surface shared usage guards', () => {
onA2UIMessage: vi.fn(() => vi.fn()),
onTokenUsage: vi.fn(() => vi.fn()),
dispatchA2UIAction: vi.fn(),
validateMistralApiKey: vi.fn().mockResolvedValue({ isValid: false, models: [] }),
setMistralApiKey: vi.fn().mockResolvedValue({ success: true }),
getMistralApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
getTitleModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-haiku-4-5' }),
setTitleModel: vi.fn().mockResolvedValue({ success: true }),
getImageAnalysisModel: vi.fn().mockResolvedValue({ success: true, modelId: 'claude-sonnet-4-5' }),
setImageAnalysisModel: vi.fn().mockResolvedValue({ success: true }),
} as never;
});