mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
mistral
This commit is contained in:
parent
018fd016c8
commit
3089bb31b5
7 changed files with 39 additions and 181 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -22,4 +22,3 @@ product.overrides.json
|
|||
*.snap.actual
|
||||
.vscode-test
|
||||
.tmp/
|
||||
.tool-versions
|
||||
|
|
|
|||
62
src/package-lock.json
generated
62
src/package-lock.json
generated
|
|
@ -1,62 +0,0 @@
|
|||
{
|
||||
"name": "src",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@mistralai/mistralai": "^1.3.5",
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@mistralai/mistralai": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.3.5.tgz",
|
||||
"integrity": "sha512-yC91oJ5ScEPqbXmv3mJTwTFgu/ZtsYoOPOhaVXSsy6x4zXTqTI57yEC1flC9uiA8GpG/yhpn2BBUXF95+U9Blw==",
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19",
|
||||
"react-dom": "^18 || ^19",
|
||||
"zod": ">= 3"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.25.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
|
||||
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.23.8",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
||||
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,9 +117,9 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
|
|||
// read and update the actual state immediately
|
||||
this._readState().then(readS => {
|
||||
|
||||
// THIS IS A HACK BECAUSE WE ADDED DEEPSEEK
|
||||
const deepseekAdd = { deepseek: defaultSettingsOfProvider['deepseek'] }
|
||||
readS = { ...readS, settingsOfProvider: { ...deepseekAdd, ...readS.settingsOfProvider, } }
|
||||
// THIS IS A HACK BECAUSE WE ADDED DEEPSEEK AND MISTRAL
|
||||
const additions = { deepseek: defaultSettingsOfProvider['deepseek'], mistral: defaultSettingsOfProvider['mistral'] }
|
||||
readS = { ...readS, settingsOfProvider: { ...additions, ...readS.settingsOfProvider, } }
|
||||
|
||||
this.state = readS
|
||||
resolver()
|
||||
|
|
|
|||
|
|
@ -363,16 +363,16 @@ export const voidInitModelOptions = {
|
|||
// used when waiting and for a type reference
|
||||
export const defaultSettingsOfProvider: SettingsOfProvider = {
|
||||
anthropic: {
|
||||
_enabled: undefined,
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.anthropic,
|
||||
...voidInitModelOptions.anthropic,
|
||||
_enabled: undefined,
|
||||
},
|
||||
openAI: {
|
||||
_enabled: undefined,
|
||||
...defaultCustomSettings,
|
||||
...defaultProviderSettings.openAI,
|
||||
...voidInitModelOptions.openAI,
|
||||
_enabled: undefined,
|
||||
},
|
||||
deepseek: {
|
||||
...defaultCustomSettings,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const sendGroqMsg: _InternalSendLLMMessageFnType = async ({ messages, onT
|
|||
messages: messages,
|
||||
model: modelName,
|
||||
stream: true,
|
||||
temperature: 0.7,
|
||||
// temperature: 0.7,
|
||||
// max_tokens: parseMaxTokensStr(thisConfig.maxTokens),
|
||||
})
|
||||
.then(async response => {
|
||||
|
|
@ -30,10 +30,8 @@ export const sendGroqMsg: _InternalSendLLMMessageFnType = async ({ messages, onT
|
|||
// when receive text
|
||||
for await (const chunk of response) {
|
||||
const newText = chunk.choices[0]?.delta?.content || '';
|
||||
if (newText) {
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
|
||||
onFinalMessage({ fullText });
|
||||
|
|
|
|||
|
|
@ -1,122 +1,46 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Glass Devtools, Inc. All rights reserved.
|
||||
* Mistral implementation by Jérôme Commaret (https://github.com/jcommaret)
|
||||
* Void Editor additions licensed under the AGPL 3.0 License.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
/*--------------------------------------------------------------------------------------
|
||||
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
|
||||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Mistral } from '@mistralai/mistralai';
|
||||
import { _InternalSendLLMMessageFnType } from '../../common/llmMessageTypes.js';
|
||||
|
||||
interface MistralMessage {
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface MistralChunk {
|
||||
data: {
|
||||
id: string;
|
||||
object: string;
|
||||
created: number;
|
||||
model: string;
|
||||
choices: Array<{
|
||||
index: number;
|
||||
delta: {
|
||||
content?: string;
|
||||
role?: string;
|
||||
};
|
||||
finishReason: string | null;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
// Mistral
|
||||
export const sendMistralMsg: _InternalSendLLMMessageFnType = async ({ messages, onText, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter }) => {
|
||||
let fullText = '';
|
||||
let aborted = false;
|
||||
|
||||
const thisConfig = settingsOfProvider.mistral;
|
||||
|
||||
if (!thisConfig.apiKey) {
|
||||
onError({ message: 'Mistral API key not configured.', fullError: new Error('No API key') });
|
||||
return;
|
||||
}
|
||||
|
||||
const mistral = new Mistral({
|
||||
apiKey: thisConfig.apiKey
|
||||
});
|
||||
apiKey: thisConfig.apiKey,
|
||||
})
|
||||
|
||||
// Define the aborter before staring the stream
|
||||
_setAborter(() => {
|
||||
aborted = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// Check if there are messages to process
|
||||
if (!messages || messages.length === 0) {
|
||||
onError({ message: 'No messages to process.', fullError: new Error('No messages provided') });
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert messages for Mistral
|
||||
const mistralMessages = messages
|
||||
.map(msg => ({
|
||||
role: msg.role === 'assistant' ? 'assistant' : 'user',
|
||||
content: msg.content.trim()
|
||||
})) as MistralMessage[];
|
||||
|
||||
// Ensure there is at least one message
|
||||
if (mistralMessages.length === 0) {
|
||||
onError({ message: 'No valid messages to send.', fullError: new Error('No valid messages') });
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the last message is from the user
|
||||
if (mistralMessages[mistralMessages.length - 1].role === 'assistant') {
|
||||
mistralMessages.push({
|
||||
role: 'user',
|
||||
content: 'Continue.'
|
||||
});
|
||||
}
|
||||
|
||||
const stream = await mistral.chat.stream({
|
||||
await mistral.chat
|
||||
.stream({
|
||||
messages: messages,
|
||||
model: modelName,
|
||||
messages: mistralMessages,
|
||||
temperature: 0.7,
|
||||
maxTokens: 2048
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
// Check if the request has been aborted
|
||||
if (aborted) {
|
||||
return;
|
||||
stream: true,
|
||||
// temperature: 0.7,
|
||||
// maxTokens: 2048,
|
||||
})
|
||||
.then(async response => {
|
||||
// Mistral has a really nonstandard API - no interrupt and weird stream types
|
||||
_setAborter(() => { console.log('Mistral does not support interrupts! Further messages will just be ignored.') });
|
||||
// when receive text
|
||||
for await (const chunk of response) {
|
||||
const c = chunk.data.choices[0].delta.content || ''
|
||||
const newText = (
|
||||
typeof c === 'string' ? c
|
||||
: c?.map(c => c.type === 'text' ? c.text : c.type).join('\n')
|
||||
)
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
|
||||
if (typeof chunk === 'object' && chunk && 'data' in chunk) {
|
||||
const { data } = chunk as MistralChunk;
|
||||
if (data.choices?.[0]?.delta?.content) {
|
||||
const newText = data.choices[0].delta.content;
|
||||
fullText += newText;
|
||||
onText({ newText, fullText });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check one last time if the request has been aborted
|
||||
if (aborted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fullText) {
|
||||
onError({ message: 'No response received from Mistral.', fullError: new Error('No response content') });
|
||||
return;
|
||||
}
|
||||
|
||||
onFinalMessage({ fullText });
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.message || JSON.stringify(error);
|
||||
onError({
|
||||
message: `Mistral Error: ${errorMessage}`,
|
||||
fullError: error
|
||||
});
|
||||
}
|
||||
};
|
||||
onFinalMessage({ fullText });
|
||||
})
|
||||
.catch(error => {
|
||||
onError({ message: error + '', fullError: error });
|
||||
})
|
||||
}
|
||||
|
|
|
|||
1
void
1
void
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 5083b8e971e48ae1001e5c8ceee7b010bcea3640
|
||||
Loading…
Reference in a new issue