diff --git a/package-lock.json b/package-lock.json index f5127fb3..8c614833 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +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.1", "@parcel/watcher": "2.5.1", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", @@ -2538,6 +2539,17 @@ "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz", "integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==" }, + "node_modules/@mistralai/mistralai": { + "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" + }, + "peerDependencies": { + "zod": ">= 3" + } + }, "node_modules/@next/env": { "version": "15.2.0", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.0.tgz", @@ -23145,6 +23157,15 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.3.tgz", + "integrity": "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } } } } diff --git a/package.json b/package.json index 921a4a1f..2a8f0a57 100644 --- a/package.json +++ b/package.json @@ -76,6 +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.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 d6fbd390..96c745a2 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -52,11 +52,7 @@ export const defaultModelsOfProvider = { // 'qwen-2.5-coder-32b', // preview mode (experimental) ], mistral: [ // https://docs.mistral.ai/getting-started/models/models_overview/ - 'codestral-latest', - 'mistral-large-latest', - 'ministral-3b-latest', - 'ministral-8b-latest', - '' + 'codestral-latest' ], openAICompatible: [], // fallback } as const satisfies Record @@ -135,12 +131,6 @@ const openSourceModelOptions_assumingOAICompat = { supportsTools: false, supportsReasoning: false, }, - 'mistral': { - 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('mistral')) return toFallback({ ...openSourceModelOptions_assumingOAICompat.mistral, 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.mistral, + ...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, 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 9b07807e..568e7244 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/llmMessageTypes.js'; @@ -143,7 +148,7 @@ const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError max_tokens: messages.maxTokens, }) .then(async response => { - const fullText = response.choices[0]?.text + const fullText = response.choices[0]?.text || ''; onFinalMessage({ fullText, }); }) .catch(error => { @@ -153,7 +158,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 { @@ -374,8 +421,8 @@ const sendAnthropicChat = ({ messages: messages_, providerName, onText, onFinalM // on done - (or when error/fail) - this is called AFTER last streamEvent stream.on('finalMessage', (response) => { - const toolCalls = toolCallsFrom_AnthropicContent(response.content) - onFinalMessage({ fullText, fullReasoning, toolCalls, rawAnthropicAssistantContent: response.content as any }) + const toolCalls = toolCallsFrom_AnthropicContent(response.value.choices) + onFinalMessage({ fullText, fullReasoning, toolCalls, rawAnthropicAssistantContent: response.value.choices as any }) }) // on error stream.on('error', (error) => { @@ -530,8 +577,8 @@ export const sendLLMMessageToProviderImplementation = { }, mistral: { sendChat: (params) => _sendOpenAICompatibleChat(params), - sendFIM: (params) => _sendOpenAICompatibleFIM(params), - list: (params) => _openaiCompatibleList(params), + sendFIM: (params) => _sendMistralFIM(params), + list: null }, } satisfies CallFnOfProvider @@ -545,10 +592,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