diff --git a/package-lock.json b/package-lock.json index 4a403917..8c614833 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@google/generative-ai": "^0.22.0", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", - "@mistralai/mistralai": "^1.5.0", + "@mistralai/mistralai": "^1.5.1", "@parcel/watcher": "2.5.1", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", @@ -2540,9 +2540,9 @@ "integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==" }, "node_modules/@mistralai/mistralai": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.5.0.tgz", - "integrity": "sha512-AIn8pwAwA/fDvEUvmkt+40zH1ZmfaG3Q7oUWl17GUEC1tU7ZPwYz8Cv9P59lyS1SisHdDSu81oknO7f1ywkz8Q==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.5.1.tgz", + "integrity": "sha512-Ie0EH4dAO11MEXR5N2kS2cgr+ycTWvqN/yP9bKrtmUEqjdcF4i7DLxtrFMUw5l2dOPhrkX93G4SziFiATPWu2w==", "dependencies": { "zod-to-json-schema": "^3.24.1" }, diff --git a/package.json b/package.json index 52387a3e..2a8f0a57 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@google/generative-ai": "^0.22.0", "@microsoft/1ds-core-js": "^3.2.13", "@microsoft/1ds-post-js": "^3.2.13", - "@mistralai/mistralai": "^1.5.0", + "@mistralai/mistralai": "^1.5.1", "@parcel/watcher": "2.5.1", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index 95de9604..54a5c422 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -51,13 +51,9 @@ export const defaultModelsOfProvider = { 'llama-3.1-8b-instant', // 'qwen-2.5-coder-32b', // preview mode (experimental) ], - // not supporting mistral right now- it's last on Void usage, and a huge pain to set up since it's nonstandard (it supports codestral FIM but it's on v1/fim/completions, etc) - // mistral: [ // https://docs.mistral.ai/getting-started/models/models_overview/ - // 'codestral-latest', - // 'mistral-large-latest', - // 'ministral-3b-latest', - // 'ministral-8b-latest', - // ], + mistral: [ // https://docs.mistral.ai/getting-started/models/models_overview/ + 'codestral-latest' + ], openAICompatible: [], // fallback } as const satisfies Record @@ -135,12 +131,6 @@ const openSourceModelOptions_assumingOAICompat = { supportsTools: false, supportsReasoning: false, }, - 'codestral': { - supportsFIM: true, - supportsSystemMessage: 'system-role', - supportsTools: 'openai-style', - supportsReasoning: false, - }, // llama 'llama3': { supportsFIM: false, @@ -179,6 +169,12 @@ const openSourceModelOptions_assumingOAICompat = { supportsTools: 'openai-style', supportsReasoning: { canToggleReasoning: false, canIOReasoning: true, openSourceThinkTags: ['', ''] }, }, + 'mistral-large-latest': { + supportsFIM: false, + supportsSystemMessage: 'system-role', + supportsTools: 'openai-style', + supportsReasoning: false, + }, // FIM only 'starcoder2': { supportsFIM: true, @@ -186,6 +182,13 @@ const openSourceModelOptions_assumingOAICompat = { supportsTools: false, supportsReasoning: false, }, + // Mistral + 'codestral-latest': { + supportsFIM: true, + supportsSystemMessage: 'system-role', + supportsTools: 'openai-style', + supportsReasoning: false, + }, 'codegemma:2b': { supportsFIM: true, supportsSystemMessage: false, @@ -214,7 +217,8 @@ const extensiveModelFallback: ProviderSettings['modelOptionsFallback'] = (modelN if (modelName.includes('deepseek')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.deepseekCoderV2, contextWindow: 32_000, maxOutputTokens: 4_096, }) if (modelName.includes('llama3')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.llama3, contextWindow: 32_000, maxOutputTokens: 4_096, }) if (modelName.includes('qwen') && modelName.includes('2.5') && modelName.includes('coder')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['qwen2.5coder'], contextWindow: 32_000, maxOutputTokens: 4_096, }) - if (modelName.includes('codestral')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.codestral, contextWindow: 32_000, maxOutputTokens: 4_096, }) + if (modelName.includes('mistral-large-latest')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['mistral-large-latest'], contextWindow: 32_000, maxOutputTokens: 4_096, }) + if (modelName.includes('codestral-latest')) return toFallback(mistralModelOptions['codestral-latest']) if (/\bo1\b/.test(modelName) || /\bo3\b/.test(modelName)) return toFallback(openAIModelOptions['o1']) return toFallback(modelOptionsDefaults) } @@ -354,6 +358,33 @@ const openAISettings: ProviderSettings = { } } +const mistralModelOptions = { + 'codestral-latest': { + contextWindow: 32_000, + maxOutputTokens: 4_096, + cost: { input: 0.00, output: 0.00 }, + supportsFIM: true, + supportsSystemMessage: 'system-role', + supportsTools: 'openai-style', + supportsReasoning: false, + }, + 'mistral-large-latest': { + contextWindow: 32_000, + maxOutputTokens: 4_096, + cost: { input: 0.00, output: 0.00 }, + supportsFIM: false, + supportsSystemMessage: 'system-role', + supportsTools: 'openai-style', + supportsReasoning: false, + } +} as const satisfies { [s: string]: ModelOptions } + + + + + + + // ---------------- XAI ---------------- const xAIModelOptions = { 'grok-2': { @@ -555,13 +586,20 @@ const openRouterModelOptions_assumingOpenAICompat = { supportsReasoning: false, }, 'mistralai/codestral-2501': { - ...openSourceModelOptions_assumingOAICompat.codestral, + ...openSourceModelOptions_assumingOAICompat['codestral-latest'], contextWindow: 256_000, maxOutputTokens: null, cost: { input: 0.3, output: 0.9 }, supportsTools: 'openai-style', supportsReasoning: false, }, + 'mistralai/mistral-large-latest': { + ...openSourceModelOptions_assumingOAICompat['mistral-large-latest'], + contextWindow: 256_000, + maxOutputTokens: null, + cost: { input: 0.3, output: 0.9 }, + }, + 'qwen/qwen-2.5-coder-32b-instruct': { ...openSourceModelOptions_assumingOAICompat['qwen2.5coder'], contextWindow: 33_000, @@ -590,6 +628,12 @@ const openRouterSettings: ProviderSettings = { } +const mistralSettings: ProviderSettings = { + ...openSourceModelOptions_assumingOAICompat.mistral, + modelOptions: {}, + modelOptionsFallback: (modelName) => extensiveModelFallback(modelName), +} + // ---------------- model settings of everything above ---------------- @@ -599,7 +643,7 @@ const modelSettingsOfProvider: { [providerName in ProviderName]: ProviderSetting anthropic: anthropicSettings, xAI: xAISettings, gemini: geminiSettings, - + mistral: mistralSettings, // open source models deepseek: deepseekSettings, groq: groqSettings, diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 5636790a..8041bbef 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -43,6 +43,9 @@ export const defaultProviderSettings = { xAI: { apiKey: '' }, + mistral: { + apiKey: '' + }, } as const @@ -144,6 +147,11 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn title: 'Grok (xAI)', } } + else if (providerName === 'mistral') { + return { + title: 'Mistral.ai API', + } + } throw new Error(`descOfProviderName: Unknown provider name: "${providerName}"`) @@ -170,7 +178,8 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName providerName === 'groq' ? 'gsk_key...' : providerName === 'openAICompatible' ? 'sk-key...' : providerName === 'xAI' ? 'xai-key...' : - '', + providerName === 'mistral' ? 'key...' : + '', subTextMd: providerName === 'anthropic' ? 'Get your [API Key here](https://console.anthropic.com/settings/keys).' : providerName === 'openAI' ? 'Get your [API Key here](https://platform.openai.com/api-keys).' : @@ -179,8 +188,9 @@ export const displayInfoOfSettingName = (providerName: ProviderName, settingName providerName === 'gemini' ? 'Get your [API Key here](https://aistudio.google.com/apikey).' : providerName === 'groq' ? 'Get your [API Key here](https://console.groq.com/keys).' : providerName === 'xAI' ? 'Get your [API Key here](https://console.x.ai).' : - providerName === 'openAICompatible' ? undefined : - '', + providerName === 'mistral' ? 'Get your [API Key here](https://console.mistral.ai/api-keys).' : + providerName === 'openAICompatible' ? undefined : + '', isPasswordField: true, } } @@ -300,6 +310,12 @@ export const defaultSettingsOfProvider: SettingsOfProvider = { ...modelInfoOfDefaultModelNames(defaultModelsOfProvider.vLLM), _didFillInProviderSettings: undefined, }, + mistral: { // aggregator + ...defaultCustomSettings, + ...defaultProviderSettings.mistral, + ...modelInfoOfDefaultModelNames(defaultModelsOfProvider.mistral), + _didFillInProviderSettings: undefined, + }, } diff --git a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts index dd75882e..ff19cbb2 100644 --- a/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts +++ b/src/vs/workbench/contrib/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -7,6 +7,11 @@ import Anthropic from '@anthropic-ai/sdk'; import { Ollama } from 'ollama'; import OpenAI, { ClientOptions } from 'openai'; +/* Mistral standalone Fim endpoint */ +import { MistralCore } from "@mistralai/mistralai/core.js"; +import { fimComplete } from "@mistralai/mistralai/funcs/fimComplete.js"; +/* End Mistral standalone Fim endpoint */ + import { Model as OpenAIModel } from 'openai/resources/models.js'; import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../browser/helpers/extractCodeFromResult.js'; import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js'; @@ -112,6 +117,10 @@ const newOpenAICompatibleSDK = ({ settingsOfProvider, providerName, includeInPay const thisConfig = settingsOfProvider[providerName] return new OpenAI({ baseURL: 'https://api.x.ai/v1', apiKey: thisConfig.apiKey, ...commonPayloadOpts }) } + else if (providerName === 'mistral') { + const thisConfig = settingsOfProvider[providerName] + return new OpenAI({ baseURL: 'https://api.mistral.ai/v1', apiKey: thisConfig.apiKey, ...commonPayloadOpts }) + } else throw new Error(`Void providerName was invalid: ${providerName}.`) } @@ -139,6 +148,7 @@ const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError max_tokens: messages.maxTokens, }) .then(async response => { + const fullText = response.choices[0]?.text onFinalMessage({ fullText, fullReasoning: '', anthropicReasoning: null }); }) @@ -149,7 +159,49 @@ const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError } +const _sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendFIMParams_Internal) => { + const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_) + if (!supportsFIM) { + if (modelName === modelName_) + onError({ message: `Model ${modelName} does not support FIM.`, fullError: null }) + else + onError({ message: `Model ${modelName_} (${modelName}) does not support FIM.`, fullError: null }) + return + } + const messages = prepareFIMMessage({ messages: messages_, aiInstructions }) + const mistral = new MistralCore({ apiKey: settingsOfProvider.mistral.apiKey }) + + // DEBUG : request params + // console.log('🔍 Sending FIM request with params:', { + // model: modelName, + // promptLength: messages.prefix.length, + // suffixLength: messages.suffix.length, + // stream: false, + // maxTokens: messages.maxTokens + //}); + + fimComplete( + mistral, { + model: modelName, + prompt: messages.prefix, + suffix: messages.suffix, + stream: false, + topP: 1, + maxTokens: messages.maxTokens, + stop: messages.stopTokens + }, + ) + .then(async response => { + const fullText = response.choices[0]?.text || ''; + onFinalMessage({ fullText, }); + // console.log('✅ Réponse FIM reçue:', fullText); + + }) + .catch(error => { + onError({ message: error + '', fullError: error }); + }) +} const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, tools: tools_ }: SendChatParams_Internal) => { const { @@ -530,6 +582,11 @@ export const sendLLMMessageToProviderImplementation = { sendFIM: null, list: null, }, + mistral: { + sendChat: (params) => _sendOpenAICompatibleChat(params), + sendFIM: (params) => _sendMistralFIM(params), + list: null + }, } satisfies CallFnOfProvider @@ -542,10 +599,7 @@ qwen2.5-coder https://ollama.com/library/qwen2.5-coder/blobs/e94a8ecb9327 <|fim_prefix|>{{ .Prompt }}<|fim_suffix|>{{ .Suffix }}<|fim_middle|> codestral https://ollama.com/library/codestral/blobs/51707752a87c -[SUFFIX]{{ .Suffix }}[PREFIX] {{ .Prompt }} - -deepseek-coder-v2 https://ollama.com/library/deepseek-coder-v2/blobs/22091531faf0 -<|fim▁begin|>{{ .Prompt }}<|fim▁hole|>{{ .Suffix }}<|fim▁end|> +{{ .Prompt }} starcoder2 https://ollama.com/library/starcoder2/blobs/3b190e68fefe