Merge branch 'staging' of https://github.com/DonMoralez/SillyTavern into staging

This commit is contained in:
DonMoralez 2023-12-21 13:39:30 +02:00
commit 1456ebd4bb
3 changed files with 39 additions and 31 deletions

View File

@ -1537,29 +1537,38 @@
</div> </div>
<div data-newbie-hidden class="range-block" data-source="claude"> <div data-newbie-hidden class="range-block" data-source="claude">
<label for="exclude_assistant" title="Exclude Assistant suffix" class="checkbox_label widthFreeExpand"> <label for="exclude_assistant" title="Exclude Assistant suffix" class="checkbox_label widthFreeExpand">
<input id="exclude_assistant" type="checkbox" /><span data-i18n="Exclude Assistant suffix">Exclude Assistant suffix</span> <input id="exclude_assistant" type="checkbox" />
<span data-i18n="Exclude Assistant suffix">Exclude Assistant suffix</span>
</label> </label>
<div class="toggle-description justifyLeft"> <div class="toggle-description justifyLeft">
<span data-i18n="Exclude the assistant suffix from being added to the end of prompt.">Exclude the assistant suffix from being added to the end of prompt (Requires jailbreak with 'Assistant:' in it).</span> <span data-i18n="Exclude the assistant suffix from being added to the end of prompt.">
Exclude the assistant suffix from being added to the end of prompt (Requires jailbreak with 'Assistant:' in it).
</span>
</div> </div>
<div id="claude_assistant_prefill_block" data-source="claude" class="wide100p"> <div id="claude_assistant_prefill_block" class="wide100p">
<span id="claude_assistant_prefill_text" data-i18n="Assistant Prefill">Assistant Prefill</span> <span id="claude_assistant_prefill_text" data-i18n="Assistant Prefill">Assistant Prefill</span>
<textarea id="claude_assistant_prefill" class="text_pole textarea_compact" name="assistant_prefill autoSetHeight" rows="3" maxlength="10000" data-i18n="[placeholder]Start Claude's answer with..." placeholder="Start Claude's answer with..."></textarea> <textarea id="claude_assistant_prefill" class="text_pole textarea_compact" name="assistant_prefill autoSetHeight" rows="3" maxlength="10000" data-i18n="[placeholder]Start Claude's answer with..." placeholder="Start Claude's answer with..."></textarea>
</div> </div>
<label for="claude_use_sysprompt" title="Use system prompt(claude 2.1+ only)" class="checkbox_label widthFreeExpand"> <label for="claude_use_sysprompt" class="checkbox_label widthFreeExpand">
<input id="claude_use_sysprompt" type="checkbox" /><span data-i18n="Use system prompt(claude 2.1+ only)">Use system prompt(claude 2.1+ only)</span> <input id="claude_use_sysprompt" type="checkbox" />
<span data-i18n="Use system prompt (Claude 2.1+ only)">
Use system prompt (Claude 2.1+ only)
</span>
</label> </label>
<div class="toggle-description justifyLeft"> <div class="toggle-description justifyLeft">
<span data-i18n="Exclude the 'Human: ' prefix from being added to the beginning of the prompt.">Exclude the 'Human: ' prefix from being added to the beginning of the prompt. Instead, place it between the sysprompt and the first message with the role 'assistant'(right before 'Chat History', by default).</span> <span data-i18n="Exclude the 'Human: ' prefix from being added to the beginning of the prompt.">
Exclude the 'Human: ' prefix from being added to the beginning of the prompt.
Instead, place it between the system prompt and the first message with the role 'assistant' (right before 'Chat History' by default).
</span>
</div> </div>
<div id="claude_human_sysprompt_message_block" data-source="claude" class="wide100p"> <div id="claude_human_sysprompt_message_block" class="wide100p">
<div class="range-block-title openai_restorable"> <div class="range-block-title openai_restorable">
<span id="claude_human_sysprompt_textarea_text" data-i18n="Human: first message">Human: first message</span> <span data-i18n="Human: first message">Human: first message</span>
<div id="claude_human_sysprompt_message_restore" title="Restore Human: first message" class="right_menu_button"> <div id="claude_human_sysprompt_message_restore" title="Restore Human: first message" class="right_menu_button">
<div class="fa-solid fa-clock-rotate-left"></div> <div class="fa-solid fa-clock-rotate-left"></div>
</div> </div>
</div> </div>
<textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact" name="human_sysprompt_message autoSetHeight" rows="3" maxlength="10000" data-i18n="[placeholder]Your(Human: ) message, instruction, etc... !!WHEN EMPTY, ADDS NOTHING!!I.e., requires a new prompt with the role 'user' or manually adding the 'Human: ' prefix." placeholder="Your(Human: ) message, instruction, etc... !!WHEN EMPTY, ADDS NOTHING!! I.e., requires a new prompt with the role 'user' or manually adding the 'Human: ' prefix."></textarea> <textarea id="claude_human_sysprompt_textarea" class="text_pole textarea_compact" rows="4" maxlength="10000" data-i18n="[placeholder]Human message" placeholder="Human message, instruction, etc.&#10;Adds nothing when empty, i.e. requires a new prompt with the role 'user' or manually adding the 'Human: ' prefix."></textarea>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,7 +4,7 @@ const { Readable } = require('stream');
const { jsonParser } = require('../../express-common'); const { jsonParser } = require('../../express-common');
const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY } = require('../../constants'); const { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, BISON_SAFETY } = require('../../constants');
const { forwardFetchResponse, getConfigValue, tryParse, uuidv4 } = require('../../util'); const { forwardFetchResponse, getConfigValue, tryParse, uuidv4, color } = require('../../util');
const { convertClaudePrompt, convertGooglePrompt, convertTextCompletionPrompt } = require('../prompt-converters'); const { convertClaudePrompt, convertGooglePrompt, convertTextCompletionPrompt } = require('../prompt-converters');
const { readSecret, SECRET_KEYS } = require('../secrets'); const { readSecret, SECRET_KEYS } = require('../secrets');
@ -21,11 +21,10 @@ const API_CLAUDE = 'https://api.anthropic.com/v1';
async function sendClaudeRequest(request, response) { async function sendClaudeRequest(request, response) {
const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString(); const apiUrl = new URL(request.body.reverse_proxy || API_CLAUDE).toString();
const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.CLAUDE); const apiKey = request.body.reverse_proxy ? request.body.proxy_password : readSecret(SECRET_KEYS.CLAUDE);
const chalk = require('chalk');
const divider = '-'.repeat(process.stdout.columns); const divider = '-'.repeat(process.stdout.columns);
if (!apiKey) { if (!apiKey) {
console.log(chalk.red(`Claude API key is missing.\n${divider}`)); console.log(color.red(`Claude API key is missing.\n${divider}`));
return response.status(400).send({ error: true }); return response.status(400).send({ error: true });
} }
@ -36,17 +35,17 @@ async function sendClaudeRequest(request, response) {
controller.abort(); controller.abort();
}); });
let isSyspromptSupported = request.body.model === 'claude-2' || request.body.model === 'claude-2.1'; const isSysPromptSupported = request.body.model === 'claude-2' || request.body.model === 'claude-2.1';
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); const 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}`)); console.log(color.green(`${divider}\nClaude request\n`) + color.cyan(`PROMPT\n${divider}\n${requestPrompt}\n${divider}`));
// Check Claude messages sequence and prefixes presence. // Check Claude messages sequence and prefixes presence.
const sequence = requestPrompt.split('\n').filter(x => x.startsWith('Human:') || x.startsWith('Assistant:')); const sequence = requestPrompt.split('\n').filter(x => x.startsWith('Human:') || x.startsWith('Assistant:'));
const humanFound = sequence.some(line => line.startsWith('Human:'));
const assistantFound = sequence.some(line => line.startsWith('Assistant:'));
let humanErrorCount = 0; let humanErrorCount = 0;
let assistantErrorCount = 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++) { for (let i = 0; i < sequence.length - 1; i++) {
if (sequence[i].startsWith(sequence[i + 1].split(':')[0])) { if (sequence[i].startsWith(sequence[i + 1].split(':')[0])) {
@ -59,20 +58,20 @@ async function sendClaudeRequest(request, response) {
} }
if (!humanFound) { if (!humanFound) {
console.log(chalk.red(`${divider}\nWarning: No 'Human:' prefix found in the prompt.\n${divider}`)); console.log(color.red(`${divider}\nWarning: No 'Human:' prefix found in the prompt.\n${divider}`));
} }
if (!assistantFound) { if (!assistantFound) {
console.log(chalk.red(`${divider}\nWarning: No 'Assistant: ' prefix found in the prompt.\n${divider}`)); console.log(color.red(`${divider}\nWarning: No 'Assistant: ' prefix found in the prompt.\n${divider}`));
} }
if (!sequence[0].startsWith('Human:')) { if (!sequence[0].startsWith('Human:')) {
console.log(chalk.red(`${divider}\nWarning: The messages sequence should start with 'Human:' prefix.\nMake sure you have 'Human:' prefix at the very beggining of the prompt, or after the system prompt.\n${divider}`)); console.log(color.red(`${divider}\nWarning: The messages sequence should start with 'Human:' prefix.\nMake sure you have 'Human:' prefix at the very beggining of the prompt, or after the system prompt.\n${divider}`));
} }
if (humanErrorCount > 0 || assistantErrorCount > 0) { if (humanErrorCount > 0 || assistantErrorCount > 0) {
console.log(chalk.red(`${divider}\nWarning: Detected incorrect Prefix sequence(s).`)); console.log(color.red(`${divider}\nWarning: Detected incorrect Prefix sequence(s).`));
console.log(chalk.red(`Incorrect "Human:" prefix(es): ${humanErrorCount}.\nIncorrect "Assistant: " prefix(es): ${assistantErrorCount}.`)); console.log(color.red(`Incorrect "Human:" prefix(es): ${humanErrorCount}.\nIncorrect "Assistant: " prefix(es): ${assistantErrorCount}.`));
console.log(chalk.red('Check the prompt above and fix it in the sillytavern.')); console.log(color.red('Check the prompt above and fix it in the sillytavern.'));
console.log(chalk.red('\nThe correct sequence should look like this:\nSystem prompt <-(for the sysprompt format only, else have 2 empty lines above the first human\'s message.)')); console.log(color.red('\nThe correct sequence should look like this:\nSystem prompt <-(for the sysprompt format only, else have 2 empty lines above the first human\'s message.)'));
console.log(chalk.red(` <-----(Each message beginning with the "Assistant:/Human:" prefix must have one empty line above.)\nHuman:\n\nAssistant:\n...\n\nHuman:\n\nAssistant:\n${divider}`)); console.log(color.red(` <-----(Each message beginning with the "Assistant:/Human:" prefix must have one empty line above.)\nHuman:\n\nAssistant:\n...\n\nHuman:\n\nAssistant:\n${divider}`));
} }
const stop_sequences = ['\n\nHuman:', '\n\nSystem:', '\n\nAssistant:']; const stop_sequences = ['\n\nHuman:', '\n\nSystem:', '\n\nAssistant:'];
@ -107,20 +106,20 @@ async function sendClaudeRequest(request, response) {
forwardFetchResponse(generateResponse, response); forwardFetchResponse(generateResponse, response);
} else { } else {
if (!generateResponse.ok) { if (!generateResponse.ok) {
console.log(chalk.red(`Claude API returned error: ${generateResponse.status} ${generateResponse.statusText}\n${await generateResponse.text()}\n${divider}`)); console.log(color.red(`Claude API returned error: ${generateResponse.status} ${generateResponse.statusText}\n${await generateResponse.text()}\n${divider}`));
return response.status(generateResponse.status).send({ error: true }); return response.status(generateResponse.status).send({ error: true });
} }
const generateResponseJson = await generateResponse.json(); const generateResponseJson = await generateResponse.json();
const responseText = generateResponseJson.completion; const responseText = generateResponseJson.completion;
console.log(chalk.green(`Claude response\n${divider}\n${responseText}\n${divider}`)); console.log(color.green(`Claude response\n${divider}\n${responseText}\n${divider}`));
// Wrap it back to OAI format // Wrap it back to OAI format
const reply = { choices: [{ 'message': { 'content': responseText } }] }; const reply = { choices: [{ 'message': { 'content': responseText } }] };
return response.send(reply); return response.send(reply);
} }
} catch (error) { } catch (error) {
console.log(chalk.red(`Error communicating with Claude: ${error}\n${divider}`)); console.log(color.red(`Error communicating with Claude: ${error}\n${divider}`));
if (!response.headersSent) { if (!response.headersSent) {
return response.status(500).send({ error: true }); return response.status(500).send({ error: true });
} }

View File

@ -3,13 +3,13 @@
* @param {object[]} messages Array of messages * @param {object[]} messages Array of messages
* @param {boolean} addAssistantPostfix Add Assistant postfix. * @param {boolean} addAssistantPostfix Add Assistant postfix.
* @param {string} addAssistantPrefill Add Assistant prefill after the assistant postfix. * @param {string} addAssistantPrefill Add Assistant prefill after the assistant postfix.
* @param {boolean} withSyspromptSupport Indicates if the Claude model supports the system prompt format. * @param {boolean} withSysPromptSupport Indicates if the Claude model supports the system prompt format.
* @param {boolean} useSystemPrompt Indicates if the system prompt format should be used. * @param {boolean} useSystemPrompt Indicates if the system prompt format should be used.
* @param {string} addSysHumanMsg Add Human message between system prompt and assistant. * @param {string} addSysHumanMsg Add Human message between system prompt and assistant.
* @returns {string} Prompt for Claude * @returns {string} Prompt for Claude
* @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3). * @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3).
*/ */
function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSyspromptSupport, useSystemPrompt, addSysHumanMsg) { function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg) {
//Prepare messages for claude. //Prepare messages for claude.
if (messages.length > 0) { if (messages.length > 0) {
@ -31,7 +31,7 @@ function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill,
}); });
// When 2.1+ and 'Use system prompt" checked, switches to the system prompt format by setting the first message's role to the 'system'. // 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. // Inserts the human's message before the first the assistant one, if there are no such message or prefix found.
if (withSyspromptSupport && useSystemPrompt) { if (withSysPromptSupport && useSystemPrompt) {
messages[0].role = 'system'; messages[0].role = 'system';
if (firstAssistantIndex > 0 && addSysHumanMsg && !hasUser) { if (firstAssistantIndex > 0 && addSysHumanMsg && !hasUser) {
messages.splice(firstAssistantIndex, 0, { messages.splice(firstAssistantIndex, 0, {