#2557 Improve Mistral prompt conversion

ef59e8abae74486de590690c5628593f03b764c6

Cohee <18619528+Cohee1207@users.noreply.github.com>

2 files changed, +15 -31Ignore whitespace
src/endpoints/backends/chat-completions.js+1 -1
@@ -483,7 +483,7 @@ async function sendMistralAIRequest(request, response) {
483 }483 }
484484
485 try {485 try {
486 const messages = convertMistralMessages(request.body.messages, request.body.model, request.body.char_name, request.body.user_name);486 const messages = convertMistralMessages(request.body.messages, request.body.char_name, request.body.user_name);
487 const controller = new AbortController();487 const controller = new AbortController();
488 request.socket.removeAllListeners('close');488 request.socket.removeAllListeners('close');
489 request.socket.on('close', function () {489 request.socket.on('close', function () {
src/prompt-converters.js+14 -30
@@ -364,64 +364,48 @@ function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '
364/**364/**
365 * Convert a prompt from the ChatML objects to the format used by MistralAI.365 * Convert a prompt from the ChatML objects to the format used by MistralAI.
366 * @param {object[]} messages Array of messages366 * @param {object[]} messages Array of messages
367 * @param {string} model Model name
368 * @param {string} charName Character name367 * @param {string} charName Character name
369 * @param {string} userName User name368 * @param {string} userName User name
370 */369 */
371function convertMistralMessages(messages, model, charName = '', userName = '') {370function convertMistralMessages(messages, charName = '', userName = '') {
372 if (!Array.isArray(messages)) {371 if (!Array.isArray(messages)) {
373 return [];372 return [];
374 }373 }
375374
376 //large seems to be throwing a 500 error if we don't make the first message a user role, most likely a bug since the other models won't do this375 // Make the last assistant message a prefill
377 if (model.includes('large')) {
378 messages[0].role = 'user';
379 }
380
381 //must send a user role as last message
382 const lastMsg = messages[messages.length - 1];376 const lastMsg = messages[messages.length - 1];
383 if (messages.length > 0 && lastMsg && (lastMsg.role === 'system' || lastMsg.role === 'assistant')) {377 if (messages.length > 0 && lastMsg && (lastMsg.role === 'assistant')) {
384 if (lastMsg.role === 'assistant' && lastMsg.name) {378 lastMsg.prefix = true;
385 lastMsg.content = lastMsg.name + ': ' + lastMsg.content;
386 } else if (lastMsg.role === 'system') {
387 lastMsg.content = '[INST] ' + lastMsg.content + ' [/INST]';
388 }
389 lastMsg.role = 'user';
390 }379 }
391380
392 //system prompts can be stacked at the start, but any futher sys prompts after the first user/assistant message will break the model381 // Doesn't support completion names, so prepend if not already done by the frontend (e.g. for group chats).
393 let encounteredNonSystemMessage = false;
394 messages.forEach(msg => {382 messages.forEach(msg => {
395 if (msg.role === 'system' && msg.name === 'example_assistant') {383 if (msg.role === 'system' && msg.name === 'example_assistant') {
396 if (charName) {384 if (charName && !msg.content.startsWith(`${charName}: `)) {
397 msg.content = `${charName}: ${msg.content}`;385 msg.content = `${charName}: ${msg.content}`;
398 }386 }
399 delete msg.name;387 delete msg.name;
400 }388 }
401389
402 if (msg.role === 'system' && msg.name === 'example_user') {390 if (msg.role === 'system' && msg.name === 'example_user') {
403 if (userName) {391 if (userName && !msg.content.startsWith(`${userName}: `)) {
404 msg.content = `${userName}: ${msg.content}`;392 msg.content = `${userName}: ${msg.content}`;
405 }393 }
406 delete msg.name;394 delete msg.name;
407 }395 }
408396
409 if (msg.name) {397 if (msg.name && msg.role !== 'system' && !msg.content.startsWith(`${msg.name}: `)) {
410 msg.content = `${msg.name}: ${msg.content}`;398 msg.content = `${msg.name}: ${msg.content}`;
411 delete msg.name;399 delete msg.name;
412 }400 }
401 });
413402
414 if ((msg.role === 'user' || msg.role === 'assistant') && !encounteredNonSystemMessage) {403 // If system role message immediately follows an assistant message, change its role to user
415 encounteredNonSystemMessage = true;404 for (let i = 0; i < messages.length - 1; i++) {
416 }405 if (messages[i].role === 'assistant' && messages[i + 1].role === 'system') {
417406 messages[i + 1].role = 'user';
418 if (encounteredNonSystemMessage && msg.role === 'system') {
419 msg.role = 'user';
420 //unsure if the instruct version is what they've deployed on their endpoints and if this will make a difference or not.
421 //it should be better than just sending the message as a user role without context though
422 msg.content = '[INST] ' + msg.content + ' [/INST]';
423 }407 }
424 });408 }
425409
426 return messages;410 return messages;
427}411}