From 4e9dc5ce4a7233e3093919d6eb50fa1fbb108122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Tue, 11 Mar 2025 16:12:14 +0100 Subject: [PATCH 01/10] defaultProviderSettings : mistral option --- .../contrib/void/common/voidSettingsTypes.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 5636790a..87efbaf5 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: 'Mistal.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, } } @@ -288,6 +298,12 @@ export const defaultSettingsOfProvider: SettingsOfProvider = { ...modelInfoOfDefaultModelNames(defaultModelsOfProvider.openAICompatible), _didFillInProviderSettings: undefined, }, + mistral: { // aggregator + ...defaultCustomSettings, + ...defaultProviderSettings.mistral, + ...modelInfoOfDefaultModelNames(defaultModelsOfProvider.mistral), + _didFillInProviderSettings: undefined, + }, ollama: { // aggregator ...defaultCustomSettings, ...defaultProviderSettings.ollama, From 7aca03de5cc92fc58689d771a792d868b2712d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Tue, 11 Mar 2025 16:19:34 +0100 Subject: [PATCH 02/10] ADD : defaultModelsOfProvider : mistral - codestral-latest --- .../contrib/void/common/modelCapabilities.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index 95de9604..c3692f05 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 @@ -192,6 +188,12 @@ const openSourceModelOptions_assumingOAICompat = { supportsTools: false, supportsReasoning: false, }, + 'codestral-latest': { + supportsFIM: true, + supportsSystemMessage: false, + supportsTools: false, + supportsReasoning: false, + }, } as const satisfies { [s: string]: Partial } From 89ac7738e4078a16a632514711f183597fc9ef97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Tue, 11 Mar 2025 16:21:49 +0100 Subject: [PATCH 03/10] sendLLMMessageToProviverImplementation : Mistral SendChat --- .../electron-main/llmMessage/sendLLMMessage.impl.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 abb0bc17..844a8a78 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 @@ -495,11 +495,11 @@ export const sendLLMMessageToProviderImplementation = { sendFIM: null, list: null, }, - // mistral: { - // sendChat: , // TODO - // sendFIM: , // TODO // https://docs.mistral.ai/api/#tag/fim - // list: null, - // }, + mistral: { + sendChat: (params) => _sendOpenAICompatibleChat(params), + sendFIM: null, // TODO // https://docs.mistral.ai/api/#tag/fim + list: null, + }, ollama: { sendChat: (params) => _sendOpenAICompatibleChat(params), sendFIM: sendOllamaFIM, From 6e39b225b2d1c7f9f9544a9e34e524f066f2d2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Wed, 12 Mar 2025 15:34:02 +0100 Subject: [PATCH 04/10] - FIM is seems ok but disabled (have to find where to change it) - Send Mistral chat to fix [error 422] --- .../contrib/void/common/modelCapabilities.ts | 72 ++++++++++++------- .../contrib/void/common/voidSettingsTypes.ts | 2 +- .../llmMessage/sendLLMMessage.impl.ts | 18 +++-- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/void/common/modelCapabilities.ts b/src/vs/workbench/contrib/void/common/modelCapabilities.ts index c3692f05..266b2d40 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -53,6 +53,11 @@ export const defaultModelsOfProvider = { ], mistral: [ // https://docs.mistral.ai/getting-started/models/models_overview/ 'codestral-latest', + 'open-codestral-mamba', + 'mistral-small-latest', + 'mistral-large-latest', + 'ministral-3b-latest', + 'ministral-8b-latest', ], openAICompatible: [], // fallback } as const satisfies Record @@ -117,6 +122,41 @@ const modelOptionsDefaults: ModelOptions = { supportsReasoning: false, } +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, + }, + 'open-codestral-mamba': { + 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 } + +const mistralSettings: ProviderSettings = { + ...mistralModelOptions, + modelOptions: {}, + modelOptionsFallback: (modelName) => extensiveModelFallback(modelName), +} const openSourceModelOptions_assumingOAICompat = { 'deepseekR1': { @@ -131,12 +171,6 @@ const openSourceModelOptions_assumingOAICompat = { supportsTools: false, supportsReasoning: false, }, - 'codestral': { - supportsFIM: true, - supportsSystemMessage: 'system-role', - supportsTools: 'openai-style', - supportsReasoning: false, - }, // llama 'llama3': { supportsFIM: false, @@ -188,17 +222,9 @@ const openSourceModelOptions_assumingOAICompat = { supportsTools: false, supportsReasoning: false, }, - 'codestral-latest': { - supportsFIM: true, - supportsSystemMessage: false, - supportsTools: false, - supportsReasoning: false, - }, + ...mistralModelOptions, } as const satisfies { [s: string]: Partial } - - - const extensiveModelFallback: ProviderSettings['modelOptionsFallback'] = (modelName) => { const toFallback = (opts: Omit): ModelOptions & { modelName: string } => { return { @@ -216,16 +242,15 @@ 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, }) + + /* Mistral Options fallback for Mistral and others providers keeping the code relatively clean for all models */ + if (modelName.includes('mistral')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['mistral-large-latest'] }) + if (modelName.includes('codestral')) return toFallback({ ...openSourceModelOptions_assumingOAICompat['codestral-latest'] }) + if (/\bo1\b/.test(modelName) || /\bo3\b/.test(modelName)) return toFallback(openAIModelOptions['o1']) return toFallback(modelOptionsDefaults) } - - - - - // ---------------- ANTHROPIC ---------------- const anthropicModelOptions = { 'claude-3-7-sonnet-20250219': { // https://docs.anthropic.com/en/docs/about-claude/models/all-models#model-comparison-table @@ -591,9 +616,6 @@ const openRouterSettings: ProviderSettings = { modelOptionsFallback: (modelName) => extensiveModelFallback(modelName), } - - - // ---------------- model settings of everything above ---------------- const modelSettingsOfProvider: { [providerName in ProviderName]: ProviderSettings } = { @@ -611,7 +633,7 @@ const modelSettingsOfProvider: { [providerName in ProviderName]: ProviderSetting vLLM: vLLMSettings, ollama: ollamaSettings, openAICompatible: openaiCompatible, - + mistral: mistralSettings, // googleVertex: {}, // microsoftAzure: {}, } as const diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts index 87efbaf5..79d2204b 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts +++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts @@ -149,7 +149,7 @@ export const displayInfoOfProviderName = (providerName: ProviderName): DisplayIn } else if (providerName === 'mistral') { return { - title: 'Mistal.ai API', + title: 'Mistral.ai API', } } 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 844a8a78..26af462a 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,10 @@ import Anthropic from '@anthropic-ai/sdk'; import { Ollama } from 'ollama'; import OpenAI, { ClientOptions } from 'openai'; + + + + import { Model as OpenAIModel } from 'openai/resources/models.js'; import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../common/helpers/extractCodeFromResult.js'; import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js'; @@ -112,6 +116,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}.`) } @@ -495,11 +503,6 @@ export const sendLLMMessageToProviderImplementation = { sendFIM: null, list: null, }, - mistral: { - sendChat: (params) => _sendOpenAICompatibleChat(params), - sendFIM: null, // TODO // https://docs.mistral.ai/api/#tag/fim - list: null, - }, ollama: { sendChat: (params) => _sendOpenAICompatibleChat(params), sendFIM: sendOllamaFIM, @@ -530,6 +533,11 @@ export const sendLLMMessageToProviderImplementation = { sendFIM: null, list: null, }, + mistral: { + sendChat: (params) => _sendMistralChat(params), + sendFIM: (params) => _sendMistralFIM(params), + list: null, + }, } satisfies CallFnOfProvider From b7f764799cc1819dba0eccf0082d2deabcf0ac86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Wed, 12 Mar 2025 15:34:35 +0100 Subject: [PATCH 05/10] stuff changed there also --- package-lock.json | 8 +-- package.json | 2 +- .../contrib/void/common/modelCapabilities.ts | 2 +- .../llmMessage/sendLLMMessage.impl.ts | 53 +++++++++++++++++-- 4 files changed, 56 insertions(+), 9 deletions(-) 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 266b2d40..7bffa921 100644 --- a/src/vs/workbench/contrib/void/common/modelCapabilities.ts +++ b/src/vs/workbench/contrib/void/common/modelCapabilities.ts @@ -582,7 +582,7 @@ 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 }, 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 26af462a..a4efefb0 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 @@ -8,7 +8,10 @@ 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'; @@ -118,7 +121,7 @@ const newOpenAICompatibleSDK = ({ settingsOfProvider, providerName, includeInPay } else if (providerName === 'mistral') { const thisConfig = settingsOfProvider[providerName] - return new OpenAI({ baseURL: 'https://api.mistral.ai/v1', apiKey: thisConfig.apiKey, ...commonPayloadOpts }) + return new OpenAI({ baseURL: 'https://api.mistral.ai/v1', apiKey: thisConfig.apiKey }) } else throw new Error(`Void providerName was invalid: ${providerName}.`) @@ -472,7 +475,51 @@ const sendOllamaFIM = ({ messages: messages_, onFinalMessage, onError, settingsO }) } +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 => { + let content = response?.ok ? response.value.choices?.[0]?.message?.content : ''; + const fullText = typeof content === 'string' ? content : + Array.isArray(content) ? content.map(chunk => chunk.type === 'text' ? chunk.text : '').join('') : ''; + onFinalMessage({ fullText, fullReasoning: '', anthropicReasoning: null }); + console.log('✅ Réponse FIM reçue:', fullText); + }) + .catch(error => { + onError({ message: error + '', fullError: error }); + }) +} type CallFnOfProvider = { [providerName in ProviderName]: { @@ -534,7 +581,7 @@ export const sendLLMMessageToProviderImplementation = { list: null, }, mistral: { - sendChat: (params) => _sendMistralChat(params), + sendChat: (params) => _sendOpenAICompatibleChat(params), sendFIM: (params) => _sendMistralFIM(params), list: null, }, From bc6f68e1051c0161c2a739be11e53c4f8aded26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Wed, 12 Mar 2025 18:52:54 +0100 Subject: [PATCH 06/10] Hope it works --- .../llmMessage/sendLLMMessage.impl.ts | 74 ++++++++++++++----- 1 file changed, 57 insertions(+), 17 deletions(-) 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 a4efefb0..a7e98938 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,11 +7,8 @@ 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 Mistral from '@mistralai/mistralai'; import { fimComplete } from "@mistralai/mistralai/funcs/fimComplete.js"; -/* End Mistral standalone Fim endpoint */ import { Model as OpenAIModel } from 'openai/resources/models.js'; @@ -121,7 +118,7 @@ const newOpenAICompatibleSDK = ({ settingsOfProvider, providerName, includeInPay } else if (providerName === 'mistral') { const thisConfig = settingsOfProvider[providerName] - return new OpenAI({ baseURL: 'https://api.mistral.ai/v1', apiKey: thisConfig.apiKey }) + return new Mistral({ apiKey: thisConfig.apiKey, ...commonPayloadOpts }) } else throw new Error(`Void providerName was invalid: ${providerName}.`) @@ -475,6 +472,59 @@ const sendOllamaFIM = ({ messages: messages_, onFinalMessage, onError, settingsO }) } +const _sendMistralChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, tools: tools_ }: SendChatParams_Internal) => { + const { + modelName, + supportsReasoning, + supportsSystemMessage, + supportsTools, + // maxOutputTokens, right now we are ignoring this + } = getModelCapabilities(providerName, modelName_) + + const { + canIOReasoning, + openSourceThinkTags, + } = supportsReasoning || {} + + const { providerReasoningIOSettings } = getProviderCapabilities(providerName) + + const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: false }) + + const thisConfig = settingsOfProvider[providerName] + const mistral = new Mistral({ apiKey: thisConfig.apiKey }) + + let fullTextSoFar = '' + let fullReasoningSoFar = '' + + const { needsManualParse: needsManualReasoningParse } = providerReasoningIOSettings?.output ?? {} + const manuallyParseReasoning = needsManualReasoningParse && canIOReasoning && openSourceThinkTags + if (manuallyParseReasoning) { + onText = extractReasoningOnTextWrapper(onText, openSourceThinkTags) + } + + mistral.chat.complete({ + model: modelName, + messages: messages, + stream: false, + }) + .then(response => { + const content = response.choices?.[0]?.message?.content + const fullText = typeof content === 'string' ? content : + Array.isArray(content) ? content.map(chunk => chunk.type === 'text' ? chunk.text : '').join('') : ''; + + onFinalMessage({ fullText, fullReasoning: '', toolCalls: [], anthropicReasoning: null }); + }) + .catch(error => { + if (error.status === 401) { + onError({ message: invalidApiKeyMessage(providerName), fullError: error }); + } else { + onError({ message: error + '', fullError: error }); + } + }) + +} + + const _sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendFIMParams_Internal) => { const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_) if (!supportsFIM) { @@ -486,16 +536,7 @@ const _sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, setting } 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 - }); + const mistral = new Mistral({ providerName, settingsOfProvider, includeInPayload }) fimComplete( mistral, { @@ -508,7 +549,6 @@ const _sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, setting stop: messages.stopTokens }, ) - .then(async response => { let content = response?.ok ? response.value.choices?.[0]?.message?.content : ''; const fullText = typeof content === 'string' ? content : @@ -581,7 +621,7 @@ export const sendLLMMessageToProviderImplementation = { list: null, }, mistral: { - sendChat: (params) => _sendOpenAICompatibleChat(params), + sendChat: (params) => _sendMistralChat(params), sendFIM: (params) => _sendMistralFIM(params), list: null, }, From 8dcc3bf187190f402e2aeb240cb850092de9428d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Thu, 13 Mar 2025 04:04:51 +0100 Subject: [PATCH 07/10] fix to do to types of Mistral connexion --- .../llmMessage/sendLLMMessage.impl.ts | 107 ++++++++++-------- 1 file changed, 60 insertions(+), 47 deletions(-) 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 a7e98938..0273e916 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,7 +7,8 @@ import Anthropic from '@anthropic-ai/sdk'; import { Ollama } from 'ollama'; import OpenAI, { ClientOptions } from 'openai'; -import Mistral from '@mistralai/mistralai'; +import Mistral from "@mistralai/mistralai"; +import { chatComplete } from "@mistralai/mistralai/funcs/chatComplete.js"; import { fimComplete } from "@mistralai/mistralai/funcs/fimComplete.js"; @@ -20,6 +21,7 @@ import { getModelSelectionState, getModelCapabilities, getProviderCapabilities } import { InternalToolInfo, ToolName, isAToolName } from '../../common/toolsServiceTypes.js'; + type InternalCommonMessageParams = { aiInstructions: string; onText: OnText; @@ -118,7 +120,7 @@ const newOpenAICompatibleSDK = ({ settingsOfProvider, providerName, includeInPay } else if (providerName === 'mistral') { const thisConfig = settingsOfProvider[providerName] - return new Mistral({ apiKey: thisConfig.apiKey, ...commonPayloadOpts }) + return new OpenAI({ apiKey: thisConfig.apiKey, ...commonPayloadOpts }) } else throw new Error(`Void providerName was invalid: ${providerName}.`) @@ -472,60 +474,70 @@ const sendOllamaFIM = ({ messages: messages_, onFinalMessage, onError, settingsO }) } -const _sendMistralChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, tools: tools_ }: SendChatParams_Internal) => { +const sendMistralChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendChatParams_Internal) => { const { modelName, - supportsReasoning, supportsSystemMessage, supportsTools, - // maxOutputTokens, right now we are ignoring this } = getModelCapabilities(providerName, modelName_) - const { - canIOReasoning, - openSourceThinkTags, - } = supportsReasoning || {} - - const { providerReasoningIOSettings } = getProviderCapabilities(providerName) - - const { messages } = prepareMessages({ messages: messages_, aiInstructions, supportsSystemMessage, supportsTools, supportsAnthropicReasoningSignature: false }) - - const thisConfig = settingsOfProvider[providerName] - const mistral = new Mistral({ apiKey: thisConfig.apiKey }) - - let fullTextSoFar = '' - let fullReasoningSoFar = '' - - const { needsManualParse: needsManualReasoningParse } = providerReasoningIOSettings?.output ?? {} - const manuallyParseReasoning = needsManualReasoningParse && canIOReasoning && openSourceThinkTags - if (manuallyParseReasoning) { - onText = extractReasoningOnTextWrapper(onText, openSourceThinkTags) - } - - mistral.chat.complete({ - model: modelName, - messages: messages, - stream: false, + const { messages } = prepareMessages({ + messages: messages_, + aiInstructions, + supportsSystemMessage, + supportsTools, + supportsAnthropicReasoningSignature: false }) - .then(response => { - const content = response.choices?.[0]?.message?.content - const fullText = typeof content === 'string' ? content : - Array.isArray(content) ? content.map(chunk => chunk.type === 'text' ? chunk.text : '').join('') : ''; - onFinalMessage({ fullText, fullReasoning: '', toolCalls: [], anthropicReasoning: null }); - }) - .catch(error => { - if (error.status === 401) { - onError({ message: invalidApiKeyMessage(providerName), fullError: error }); + const mistral = new Mistral({ apiKey: settingsOfProvider[providerName].apiKey }) + + let fullText = '' + + console.log('🔍 Debug - Messages envoyés:', messages) + + chatComplete( + mistral, { + model: modelName, + messages: messages.map((m: any) => ({ + role: m.role, + content: m.content + })), + stream: true, + } + ) + .then(response => { + console.log('🔍 Debug - Réponse initiale:', response) + + if (!response?.ok) { + throw new Error('Response not ok') + } + + // Traitement direct de la réponse + const content = response.value.choices?.[0]?.message?.content + if (content) { + fullText = typeof content === 'string' ? content : content.map(chunk => chunk.type === 'text' ? chunk.text : '').join('') + onText({ fullText, fullReasoning: '' }) + onFinalMessage({ + fullText, + fullReasoning: '', + toolCalls: [], + anthropicReasoning: null + }) } else { - onError({ message: error + '', fullError: error }); + onError({ message: 'Void: Response from model was empty.', fullError: null }) + } + }) + .catch(error => { + console.error('❌ Debug - Erreur capturée:', error) + if (error.status === 401) { + onError({ message: invalidApiKeyMessage(providerName), fullError: error }) + } else { + onError({ message: error + '', fullError: error }) } }) - } - -const _sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendFIMParams_Internal) => { +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_) @@ -536,8 +548,9 @@ const _sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, setting } const messages = prepareFIMMessage({ messages: messages_, aiInstructions }) - const mistral = new Mistral({ providerName, settingsOfProvider, includeInPayload }) + const mistral = new Mistral({ apiKey: settingsOfProvider[providerName].apiKey }) + console.log('messages FIM', messages) fimComplete( mistral, { model: modelName, @@ -621,8 +634,8 @@ export const sendLLMMessageToProviderImplementation = { list: null, }, mistral: { - sendChat: (params) => _sendMistralChat(params), - sendFIM: (params) => _sendMistralFIM(params), + sendChat: (params) => sendMistralChat(params), + sendFIM: (params) => sendMistralFIM(params), list: null, }, } satisfies CallFnOfProvider @@ -640,7 +653,7 @@ 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 From 654b577ce6ea04d651eb9afc0562f96188835fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Thu, 13 Mar 2025 18:23:05 +0100 Subject: [PATCH 08/10] Looks really hacky, but working ! YES ! --- .../llmMessage/sendLLMMessage.impl.ts | 121 +++++------------- 1 file changed, 30 insertions(+), 91 deletions(-) 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 0273e916..87507448 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 @@ -6,13 +6,14 @@ import Anthropic from '@anthropic-ai/sdk'; import { Ollama } from 'ollama'; import OpenAI, { ClientOptions } from 'openai'; - -import Mistral from "@mistralai/mistralai"; -import { chatComplete } from "@mistralai/mistralai/funcs/chatComplete.js"; -import { fimComplete } from "@mistralai/mistralai/funcs/fimComplete.js"; - - import { Model as OpenAIModel } from 'openai/resources/models.js'; + +// Mistral Core functions // +import { MistralCore } from "@mistralai/mistralai/core.js"; +import { fimComplete } from "@mistralai/mistralai/funcs/fimComplete.js"; +import { chatComplete } from "@mistralai/mistralai/funcs/chatComplete.js"; + + import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../common/helpers/extractCodeFromResult.js'; import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js'; import { defaultProviderSettings, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; @@ -21,7 +22,6 @@ import { getModelSelectionState, getModelCapabilities, getProviderCapabilities } import { InternalToolInfo, ToolName, isAToolName } from '../../common/toolsServiceTypes.js'; - type InternalCommonMessageParams = { aiInstructions: string; onText: OnText; @@ -120,7 +120,7 @@ const newOpenAICompatibleSDK = ({ settingsOfProvider, providerName, includeInPay } else if (providerName === 'mistral') { const thisConfig = settingsOfProvider[providerName] - return new OpenAI({ apiKey: thisConfig.apiKey, ...commonPayloadOpts }) + return new OpenAI({ baseURL: 'https://api.mistral.ai/v1', apiKey: thisConfig.apiKey, ...commonPayloadOpts }) } else throw new Error(`Void providerName was invalid: ${providerName}.`) @@ -474,67 +474,19 @@ const sendOllamaFIM = ({ messages: messages_, onFinalMessage, onError, settingsO }) } +//////// MISTRAL //////// const sendMistralChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendChatParams_Internal) => { - const { - modelName, - supportsSystemMessage, - supportsTools, - } = getModelCapabilities(providerName, modelName_) - - const { messages } = prepareMessages({ + _sendOpenAICompatibleChat({ messages: messages_, - aiInstructions, - supportsSystemMessage, - supportsTools, - supportsAnthropicReasoningSignature: false - }) - - const mistral = new Mistral({ apiKey: settingsOfProvider[providerName].apiKey }) - - let fullText = '' - - console.log('🔍 Debug - Messages envoyés:', messages) - - chatComplete( - mistral, { - model: modelName, - messages: messages.map((m: any) => ({ - role: m.role, - content: m.content - })), - stream: true, - } - ) - .then(response => { - console.log('🔍 Debug - Réponse initiale:', response) - - if (!response?.ok) { - throw new Error('Response not ok') - } - - // Traitement direct de la réponse - const content = response.value.choices?.[0]?.message?.content - if (content) { - fullText = typeof content === 'string' ? content : content.map(chunk => chunk.type === 'text' ? chunk.text : '').join('') - onText({ fullText, fullReasoning: '' }) - onFinalMessage({ - fullText, - fullReasoning: '', - toolCalls: [], - anthropicReasoning: null - }) - } else { - onError({ message: 'Void: Response from model was empty.', fullError: null }) - } - }) - .catch(error => { - console.error('❌ Debug - Erreur capturée:', error) - if (error.status === 401) { - onError({ message: invalidApiKeyMessage(providerName), fullError: error }) - } else { - onError({ message: error + '', fullError: error }) - } - }) + onText, + onFinalMessage, + onError, + settingsOfProvider, + modelName: modelName_, + _setAborter, + providerName, + aiInstructions + }); } const sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendFIMParams_Internal) => { @@ -548,32 +500,19 @@ const sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settings } const messages = prepareFIMMessage({ messages: messages_, aiInstructions }) - const mistral = new Mistral({ apiKey: settingsOfProvider[providerName].apiKey }) - - console.log('messages FIM', messages) - fimComplete( - mistral, { - model: modelName, - prompt: messages.prefix, - suffix: messages.suffix, - stream: false, - topP: 1, - maxTokens: messages.maxTokens, - stop: messages.stopTokens - }, - ) - .then(async response => { - let content = response?.ok ? response.value.choices?.[0]?.message?.content : ''; - const fullText = typeof content === 'string' ? content : - Array.isArray(content) ? content.map(chunk => chunk.type === 'text' ? chunk.text : '').join('') : ''; - onFinalMessage({ fullText, fullReasoning: '', anthropicReasoning: null }); - console.log('✅ Réponse FIM reçue:', fullText); - }) - .catch(error => { - onError({ message: error + '', fullError: error }); - }) + _sendOpenAICompatibleFIM({ + messages: messages_, + onFinalMessage, + onError, + settingsOfProvider, + modelName: modelName_, + _setAborter, + providerName, + aiInstructions + }); } + type CallFnOfProvider = { [providerName in ProviderName]: { sendChat: (params: SendChatParams_Internal) => void; From 27b782ff320c6f5bf103fde6e45ad4f99f13ef70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Thu, 13 Mar 2025 18:24:05 +0100 Subject: [PATCH 09/10] HOURRA ! --- .../void/electron-main/llmMessage/sendLLMMessage.impl.ts | 6 ------ 1 file changed, 6 deletions(-) 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 87507448..0c10e5ac 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 @@ -8,12 +8,6 @@ import { Ollama } from 'ollama'; import OpenAI, { ClientOptions } from 'openai'; import { Model as OpenAIModel } from 'openai/resources/models.js'; -// Mistral Core functions // -import { MistralCore } from "@mistralai/mistralai/core.js"; -import { fimComplete } from "@mistralai/mistralai/funcs/fimComplete.js"; -import { chatComplete } from "@mistralai/mistralai/funcs/chatComplete.js"; - - import { extractReasoningOnFinalMessage, extractReasoningOnTextWrapper } from '../../common/helpers/extractCodeFromResult.js'; import { LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText } from '../../common/sendLLMMessageTypes.js'; import { defaultProviderSettings, displayInfoOfProviderName, ModelSelectionOptions, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; From 136840cca4b65e1fc1ac7dcb152bfb0ee12ca357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Commaret?= Date: Fri, 14 Mar 2025 11:54:43 +0100 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=98=BB=20:=20Miaou=20!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../llmMessage/sendLLMMessage.impl.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) 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 0c10e5ac..8694795a 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 @@ -121,7 +121,7 @@ const newOpenAICompatibleSDK = ({ settingsOfProvider, providerName, includeInPay } -const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, }: SendFIMParams_Internal) => { +const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, modelSelectionOptions, }: SendFIMParams_Internal) => { const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_) if (!supportsFIM) { if (modelName === modelName_) @@ -155,7 +155,7 @@ const _sendOpenAICompatibleFIM = ({ messages: messages_, onFinalMessage, onError -const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, tools: tools_ }: SendChatParams_Internal) => { +const _sendOpenAICompatibleChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, modelSelectionOptions, tools: tools_ }: SendChatParams_Internal) => { const { modelName, supportsReasoning, @@ -469,7 +469,7 @@ const sendOllamaFIM = ({ messages: messages_, onFinalMessage, onError, settingsO } //////// MISTRAL //////// -const sendMistralChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendChatParams_Internal) => { +const sendMistralChat = ({ messages: messages_, onText, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, modelSelectionOptions }: SendChatParams_Internal) => { _sendOpenAICompatibleChat({ messages: messages_, onText, @@ -479,11 +479,12 @@ const sendMistralChat = ({ messages: messages_, onText, onFinalMessage, onError, modelName: modelName_, _setAborter, providerName, - aiInstructions + aiInstructions, + modelSelectionOptions }); } -const sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions }: SendFIMParams_Internal) => { +const sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settingsOfProvider, modelName: modelName_, _setAborter, providerName, aiInstructions, modelSelectionOptions }: SendFIMParams_Internal) => { const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_) if (!supportsFIM) { if (modelName === modelName_) @@ -492,7 +493,8 @@ const sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settings onError({ message: `Model ${modelName_} (${modelName}) does not support FIM.`, fullError: null }) return } - const messages = prepareFIMMessage({ messages: messages_, aiInstructions }) + + prepareFIMMessage({ messages: messages_, aiInstructions }) _sendOpenAICompatibleFIM({ messages: messages_, @@ -502,7 +504,9 @@ const sendMistralFIM = ({ messages: messages_, onFinalMessage, onError, settings modelName: modelName_, _setAborter, providerName, - aiInstructions + aiInstructions, + modelSelectionOptions, + onText: () => { } }); }