Mistral implementation : Working

This commit is contained in:
Jérôme Commaret 2024-12-27 16:37:27 +01:00
parent 4729e4dac3
commit 4065c5b504
3 changed files with 95 additions and 19 deletions

15
package-lock.json generated
View file

@ -14,6 +14,7 @@
"@google/generative-ai": "^0.21.0",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@mistralai/mistralai": "^1.3.5",
"@parcel/watcher": "2.1.0",
"@rrweb/record": "^2.0.0-alpha.17",
"@rrweb/types": "^2.0.0-alpha.17",
@ -64,10 +65,10 @@
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.1.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
"yazl": "^2.4.3",
"zod": "^3.24.1"
},
"devDependencies": {
"@mistralai/mistralai": "^1.3.5",
"@playwright/test": "^1.46.1",
"@swc/core": "1.3.62",
"@types/cookie": "^0.3.3",
@ -1954,7 +1955,6 @@
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.3.5.tgz",
"integrity": "sha512-yC91oJ5ScEPqbXmv3mJTwTFgu/ZtsYoOPOhaVXSsy6x4zXTqTI57yEC1flC9uiA8GpG/yhpn2BBUXF95+U9Blw==",
"dev": true,
"peerDependencies": {
"react": "^18 || ^19",
"react-dom": "^18 || ^19",
@ -23241,6 +23241,15 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zod": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View file

@ -78,6 +78,7 @@
"@google/generative-ai": "^0.21.0",
"@microsoft/1ds-core-js": "^3.2.13",
"@microsoft/1ds-post-js": "^3.2.13",
"@mistralai/mistralai": "^1.3.5",
"@parcel/watcher": "2.1.0",
"@rrweb/record": "^2.0.0-alpha.17",
"@rrweb/types": "^2.0.0-alpha.17",
@ -128,10 +129,10 @@
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "9.1.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
"yazl": "^2.4.3",
"zod": "^3.24.1"
},
"devDependencies": {
"@mistralai/mistralai": "^1.3.5",
"@playwright/test": "^1.46.1",
"@swc/core": "1.3.62",
"@types/cookie": "^0.3.3",

View file

@ -1,43 +1,109 @@
/*---------------------------------------------------------------------------------------------
* 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.
*--------------------------------------------------------------------------------------------*/
import MistralClient from '@mistralai/mistralai';
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 = '';
const thisConfig = settingsOfProvider.mistral;
const mistral = new MistralClient({
if (!thisConfig.apiKey) {
onError({ message: 'Mistral API key not configured.', fullError: new Error('No API key') });
return;
}
const mistral = new Mistral({
apiKey: thisConfig.apiKey
});
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
.filter(msg => msg.role !== 'system') // Ignore system 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({
model: modelName,
messages: messages,
messages: mistralMessages,
temperature: 0.7,
maxTokens: 2048
});
_setAborter(() => stream.controller.abort());
_setAborter(() => { }); // Mistral does not provide an abort method
for await (const chunk of stream) {
const newText = chunk.choices[0]?.delta?.content || '';
if (newText) {
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 });
}
}
}
onFinalMessage({ fullText });
} catch (error) {
if (error.status === 401) {
onError({ message: 'Invalid API key.', fullError: error });
} else {
onError({ message: error + '', fullError: error });
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
});
}
};