| 364 | 364 | /** |
| 365 | 365 | * Convert a prompt from the ChatML objects to the format used by MistralAI. |
| 366 | 366 | * @param {object[]} messages Array of messages |
| 367 | | - * @param {string} model Model name |
| 368 | 367 | * @param {string} charName Character name |
| 369 | 368 | * @param {string} userName User name |
| 370 | 369 | */ |
| 371 | 370 | function convertMistralMessages(messages, model, charName = '', userName = '') { |
| 372 | 371 | if (!Array.isArray(messages)) { |
| 373 | 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 | 376 | const lastMsg = messages[messages.length - 1]; |
| 383 | 377 | if (messages.length > 0 && lastMsg && (lastMsg.role === 'system' || 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 | 382 | messages.forEach(msg => { |
| 395 | 383 | if (msg.role === 'system' && msg.name === 'example_assistant') { |
| 396 | | - if (charName) { |
| 384 | + if (charName && !msg.content.startsWith(`${charName}: `)) { |
| 397 | 385 | msg.content = `${charName}: ${msg.content}`; |
| 398 | 386 | } |
| 399 | 387 | delete msg.name; |
| 400 | 388 | } |
| 401 | 389 | |
| 402 | 390 | if (msg.role === 'system' && msg.name === 'example_user') { |
| 403 | | - if (userName) { |
| 391 | + if (userName && !msg.content.startsWith(`${userName}: `)) { |
| 404 | 392 | msg.content = `${userName}: ${msg.content}`; |
| 405 | 393 | } |
| 406 | 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 | 398 | msg.content = `${msg.name}: ${msg.content}`; |
| 411 | 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 | 410 | return messages; |
| 427 | 411 | } |