From 125d2997dbd9d70a557ca3a92e2504f3e203a5e5 Mon Sep 17 00:00:00 2001 From: DonMoralez Date: Sat, 16 Dec 2023 14:12:06 +0200 Subject: [PATCH] Add prefixes sequence check for claude --- src/endpoints/backends/chat-completions.js | 28 ++++++++++++++++++++++ src/endpoints/prompt-converters.js | 18 +++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index 26a0e5d48..75531e766 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -40,6 +40,34 @@ async function sendClaudeRequest(request, response) { let requestPrompt = convertClaudePrompt(request.body.messages, !request.body.exclude_assistant, request.body.assistant_prefill, isSyspromptSupported, request.body.claude_use_sysprompt, request.body.human_sysprompt_message); console.log(chalk.green(`${divider}\nClaude request\n`) + chalk.cyan(`PROMPT\n${divider}\n${requestPrompt}\n${divider}`)); + + // Check Claude messages sequence and prefixes presence. + const sequence = requestPrompt.split('\n').filter(x => x.startsWith('Human:') || x.startsWith('Assistant:')); + let humanErrorCount = 0; + let assistantErrorCount = 0; + let humanFound = sequence.some(line => line.startsWith('Human:')); + let assistantFound = sequence.some(line => line.startsWith('Assistant:')); + + for (let i = 0; i < sequence.length - 1; i++) { + + if (sequence[i].startsWith(sequence[i + 1].split(':')[0])) { + if (sequence[i].startsWith('Human:')) { + humanErrorCount++; + } else if (sequence[i].startsWith('Assistant:')) { + assistantErrorCount++; + } + } + } + + if (!humanFound) { + console.log(chalk.red(`${divider}\nWarning: No Human prefix found in the prompt.\n${divider}`)); + } + if (!assistantFound) { + console.log(chalk.red(`${divider}\nWarning: No Assistant prefix found in the prompt.\n${divider}`)); + } + if (humanErrorCount > 0 || assistantErrorCount > 0) { + console.log(chalk.red(`${divider}\nWarning: Detected incorrect Prefix sequence(s).\nIncorrect 'Human:' prefix(es): ${humanErrorCount}.\nIncorrect 'Assistant:' prefix(es): ${assistantErrorCount}.\nCheck the prompt above and fix it in the sillytavern.\nThe correct sequence should look like this:\nSystem prompt message <--(for new sysprompt format only)\n <------------------(Every message start with Assistant:/Human:prefix should have one empty line above)\nHuman:\n\nAssistant:\n\...\n\nHuman:\n\nAssistant:\n${divider}`)); + } const stop_sequences = ['\n\nHuman:', '\n\nSystem:', '\n\nAssistant:']; // Add custom stop sequences diff --git a/src/endpoints/prompt-converters.js b/src/endpoints/prompt-converters.js index 7087ed77a..ce5be3a53 100644 --- a/src/endpoints/prompt-converters.js +++ b/src/endpoints/prompt-converters.js @@ -14,26 +14,31 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, console.log(JSON.stringify(messages, null, 2)); //Prepare messages for claude. if (messages.length > 0) { + messages[0].role = 'system'; //Add the assistant's message to the end of messages. if (addAssistantPostfix) { messages.push({ role: 'assistant', - content: addAssistantPrefill || '4', + content: addAssistantPrefill || '', }); } // Find the index of the first message with an assistant role and check for a "'user' role/Human:" before it. let hasUser = false; + let hasAssist = false; const firstAssistantIndex = messages.findIndex((message, i) => { - if (i > 0 && (message.role === 'user' || message.content.includes('Human:'))) { + if (i >= 0 && (message.role === 'user' || message.content.includes('\n\nHuman: '))) { + if (message.content.includes('\n\nAssistant: ')) { + hasAssist = true; + } hasUser = true; } return message.role === 'assistant' && i > 0; }); - // When 2.1 and 'Use system prompt" checked, switches to system prompt format by setting the first message's role to 'system'. - // Also, insert the human's message before the first the assistant one, in case there are no such message or prefix found. + // When 2.1 and 'Use system prompt" checked, switches to the system prompt format by setting the first message's role to the 'system'. + // Inserts the human's message before the first the assistant one, if there are no such message or prefix found. if (withSyspromptSupport && useSystemPrompt) { messages[0].role = 'system'; - if (firstAssistantIndex > 0 && !hasUser) { + if (firstAssistantIndex > 0 && (!hasUser || (hasUser && hasAssist))) { //addSysHumanMsg for test messages.splice(firstAssistantIndex, 0, { role: 'user', content: addSysHumanMsg || 'Let\'s get started.', @@ -50,6 +55,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, } console.log(JSON.stringify(messages, null, 2)); + // Convert messages to requestPrompt. let requestPrompt = messages.map((v, i) => { // Claude doesn't support message names, so we'll just add them to the message content. @@ -63,7 +69,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, 'user': '\n\nHuman: ', 'system': i === 0 ? '' : v.name === 'example_assistant' ? '\n\nA: ' : v.name === 'example_user' ? '\n\nH: ' : '\n\n', 'FixHumMsg': '\n\nFirst message: ', - }[v.role] ?? '\n\n4'; + }[v.role] ?? '\n\n'; return prefix + v.content; }).join('');