| 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 messages | 366 | * @param {object[]} messages Array of messages |
| 367 | * @param {string} model Model name | | |
| 368 | * @param {string} charName Character name | 367 | * @param {string} charName Character name |
| 369 | * @param {string} userName User name | 368 | * @param {string} userName User name |
| 370 | */ | 369 | */ |
| 371 | function convertMistralMessages(messages, model, charName = '', userName = '') { | 370 | function convertMistralMessages(messages, charName = '', userName = '') { |
| 372 | if (!Array.isArray(messages)) { | 371 | if (!Array.isArray(messages)) { |
| 373 | return []; | 372 | return []; |
| 374 | } | 373 | } |
| 375 | | 374 | |
| 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 this | 375 | // 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 | } |
| 391 | | 380 | |
| 392 | //system prompts can be stacked at the start, but any futher sys prompts after the first user/assistant message will break the model | 381 | // 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 | } |
| 401 | | 389 | |
| 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 | } |
| 408 | | 396 | |
| 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 | }); |
| 413 | | 402 | |
| 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++) { |
| | 405 | if (messages[i].role === 'assistant' && messages[i + 1].role === 'system') { |
| | 406 | messages[i + 1].role = 'user'; |
| 416 | } | 407 | } |
| 417 | | | |
| 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 | } | 408 | } |
| 424 | }); | | |
| 425 | | 409 | |
| 426 | return messages; | 410 | return messages; |
| 427 | } | 411 | } |