mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Rework of prepareOpenAIMessages
This commit is contained in:
@ -361,61 +361,71 @@ function formatWorldInfo(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldInfoBefore, worldInfoAfter, extensionPrompt, bias, type, quietPrompt, jailbreakPrompt, cyclePrompt } = {}) {
|
async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldInfoBefore, worldInfoAfter, extensionPrompt, bias, type, quietPrompt, jailbreakPrompt, cyclePrompt } = {}) {
|
||||||
const isImpersonate = type == "impersonate";
|
const chatCompletion = promptManager.getChatCompletion();
|
||||||
let this_max_context = oai_settings.openai_max_context;
|
|
||||||
let enhance_definitions_prompt = "";
|
|
||||||
|
|
||||||
// Experimental but kinda works
|
// Prepare messages
|
||||||
if (oai_settings.enhance_definitions) {
|
const enhanceDefinitionMessage = chatCompletion.makeSystemMessage(substituteParams('If you have more knowledge of {{char}}, add to the character\'s lore and personality to enhance them but keep the Character Sheet\'s definitions absolute.'));
|
||||||
enhance_definitions_prompt = "If you have more knowledge of " + name2 + ", add to the character's lore and personality to enhance them but keep the Character Sheet's definitions absolute.";
|
const worldInfoBeforeMessage = chatCompletion.makeSystemMessage(formatWorldInfo(worldInfoBefore));
|
||||||
}
|
const worldInfoAfterMessage = chatCompletion.makeSystemMessage(formatWorldInfo(worldInfoAfter));
|
||||||
|
const characterInfoMessages = chatCompletion.makeSystemMessage(substituteParams(storyString));
|
||||||
|
const newChatMessage = chatCompletion.makeSystemMessage('[Start new chat]');
|
||||||
|
const chatMessages = openai_msgs;
|
||||||
|
const biasMessage = chatCompletion.makeSystemMessage(bias.trim());
|
||||||
|
|
||||||
const characterInfo = storyString;
|
// Prepare context
|
||||||
|
chatCompletion
|
||||||
const wiBefore = formatWorldInfo(worldInfoBefore);
|
.replace('worldInfoBefore', worldInfoBeforeMessage)
|
||||||
const wiAfter = formatWorldInfo(worldInfoAfter);
|
.replace('worldInfoAfter', worldInfoAfterMessage)
|
||||||
|
.replace('characterInfo', characterInfoMessages)
|
||||||
let whole_prompt = getSystemPrompt(enhance_definitions_prompt, wiBefore, storyString, wiAfter, extensionPrompt, isImpersonate);
|
.replace('newExampleChat', newChatMessage)
|
||||||
|
.replace('newMainChat', newChatMessage)
|
||||||
// Join by a space and replace placeholders with real user/char names
|
.replace('chatHistory', chatMessages)
|
||||||
storyString = substituteParams(whole_prompt.join("\n"), name1, name2, oai_settings.main_prompt).replace(/\r/gm, '').trim();
|
|
||||||
|
|
||||||
let prompt_msg = { "role": "system", "content": storyString }
|
|
||||||
let examples_tosend = [];
|
|
||||||
let openai_msgs_tosend = [];
|
|
||||||
|
|
||||||
// todo: static value, maybe include in the initial context calculation
|
|
||||||
const handler_instance = new TokenHandler(countTokens);
|
|
||||||
|
|
||||||
let new_chat_msg = { "role": "system", "content": "[Start a new chat]" };
|
|
||||||
let start_chat_count = handler_instance.count([new_chat_msg], true, 'start_chat');
|
|
||||||
await delay(1);
|
|
||||||
let total_count = handler_instance.count([prompt_msg], true, 'prompt') + start_chat_count;
|
|
||||||
await delay(1);
|
|
||||||
|
|
||||||
if (bias && bias.trim().length) {
|
|
||||||
let bias_msg = { "role": "system", "content": bias.trim() };
|
|
||||||
openai_msgs.push(bias_msg);
|
|
||||||
total_count += handler_instance.count([bias_msg], true, 'bias');
|
|
||||||
await delay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Hande group chats
|
||||||
if (selected_group) {
|
if (selected_group) {
|
||||||
// set "special" group nudging messages
|
const names = getGroupMembers(groups);
|
||||||
const groupMembers = groups.find(x => x.id === selected_group)?.members;
|
const groupChatMessage = chatCompletion.makeSystemMessage(`[Start a new group chat. Group members: ${names}]`);
|
||||||
let names = '';
|
const groupNudgeMessage = chatCompletion.makeSystemMessage(`[Write the next reply only as ${name2}]`);
|
||||||
if (Array.isArray(groupMembers)) {
|
chatCompletion.replace('newMainChat', groupChatMessage)
|
||||||
names = groupMembers.map(member => characters.find(c => c.avatar === member)).filter(x => x).map(x => x.name);
|
chatCompletion.insertAfter('newMainChat', 'groupNudgeMessage', groupNudgeMessage);
|
||||||
names = names.join(', ')
|
}
|
||||||
}
|
|
||||||
new_chat_msg.content = `[Start a new group chat. Group members: ${names}]`;
|
|
||||||
let group_nudge = { "role": "system", "content": `[Write the next reply only as ${name2}]` };
|
|
||||||
openai_msgs.push(group_nudge);
|
|
||||||
|
|
||||||
// add a group nudge count
|
if (oai_settings.enhance_definitions) chatCompletion.insertAfter('characterInfo', 'enhancedDefinitions', enhanceDefinitionMessage);
|
||||||
let group_nudge_count = handler_instance.count([group_nudge], true, 'nudge');
|
if (extensionPrompt) chatCompletion.insertAfter('worldInfoAfter', 'extensionPrompt', extensionPrompt);
|
||||||
await delay(1);
|
if (bias && bias.trim().length) chatCompletion.add(biasMessage);
|
||||||
total_count += group_nudge_count;
|
|
||||||
|
const exampleMessages = prepareExampleMessages(openai_msgs ,openai_msgs_example, power_user.pin_examples);
|
||||||
|
if (exampleMessages.length) chatCompletion.replace('dialogueExamples', exampleMessages);
|
||||||
|
|
||||||
|
// Handle impersonation
|
||||||
|
if (type === "impersonate") {
|
||||||
|
chatCompletion.insertBefore('chatHistory', 'impersonate', substituteParams(oai_settings.impersonation_prompt));
|
||||||
|
chatCompletion.remove('main');
|
||||||
|
}
|
||||||
|
|
||||||
|
promptManager.updatePrompts(chatCompletion.getPromptsWithTokenCount());
|
||||||
|
|
||||||
|
// Save settings with updated token calculation and return context
|
||||||
|
return promptManager.saveServiceSettings().then(() => {
|
||||||
|
promptManager.render();
|
||||||
|
|
||||||
|
const openai_msgs_tosend = chatCompletion.getChat();
|
||||||
|
openai_messages_count = openai_msgs_tosend.filter(x => x.role === "user" || x.role === "assistant").length;
|
||||||
|
|
||||||
|
console.log("We're sending this:")
|
||||||
|
console.log(openai_msgs_tosend);
|
||||||
|
|
||||||
|
return openai_msgs_tosend;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGroupMembers(activeGroup) {
|
||||||
|
const groupMembers = activeGroup.find(x => x.id === selected_group)?.members;
|
||||||
|
let names = '';
|
||||||
|
if (Array.isArray(groupMembers)) {
|
||||||
|
names = groupMembers.map(member => characters.find(c => c.avatar === member)).filter(x => x).map(x => x.name);
|
||||||
|
names = names.join(', ')
|
||||||
|
}
|
||||||
|
|
||||||
// recount tokens for new start message
|
// recount tokens for new start message
|
||||||
total_count -= start_chat_count
|
total_count -= start_chat_count
|
||||||
@ -457,12 +467,17 @@ async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldIn
|
|||||||
await delay(1);
|
await delay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareExampleMessages(messages, exampleMessages, includeAll = false, ) {
|
||||||
// The user wants to always have all example messages in the context
|
// The user wants to always have all example messages in the context
|
||||||
if (power_user.pin_examples) {
|
let examples_tosend = [];
|
||||||
|
let total_count = 0;
|
||||||
|
const new_chat_msg = {role: 'system', content: '[Start new chat]'}
|
||||||
|
|
||||||
|
if (includeAll) {
|
||||||
// first we send *all* example messages
|
// first we send *all* example messages
|
||||||
// we don't check their token size since if it's bigger than the context, the user is fucked anyway
|
// we don't check their token size since if it's bigger than the context, the user is fucked anyway
|
||||||
// and should've have selected that option (maybe have some warning idk, too hard to add)
|
// and should've have selected that option (maybe have some warning idk, too hard to add)
|
||||||
for (const element of openai_msgs_example) {
|
for (const element of exampleMessages) {
|
||||||
// get the current example block with multiple user/bot messages
|
// get the current example block with multiple user/bot messages
|
||||||
let example_block = element;
|
let example_block = element;
|
||||||
// add the first message from the user to tell the model that it's a new dialogue
|
// add the first message from the user to tell the model that it's a new dialogue
|
||||||
@ -474,58 +489,48 @@ async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldIn
|
|||||||
examples_tosend.push(example);
|
examples_tosend.push(example);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
total_count += handler_instance.count(examples_tosend, true, 'examples');
|
|
||||||
await delay(1);
|
|
||||||
// go from newest message to oldest, because we want to delete the older ones from the context
|
// go from newest message to oldest, because we want to delete the older ones from the context
|
||||||
for (let j = openai_msgs.length - 1; j >= 0; j--) {
|
for (let j = messages.length - 1; j >= 0; j--) {
|
||||||
let item = openai_msgs[j];
|
let item = messages[j];
|
||||||
let item_count = handler_instance.count(item, true, 'conversation');
|
let item_count = handler_instance.count(item, true, 'conversation');
|
||||||
await delay(1);
|
|
||||||
// If we have enough space for this message, also account for the max assistant reply size
|
// If we have enough space for this message, also account for the max assistant reply size
|
||||||
if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) {
|
if ((total_count + item_count) < (oai_settings.openai_max_context - oai_settings.openai_max_tokens)) {
|
||||||
openai_msgs_tosend.push(item);
|
messages.push(item);
|
||||||
total_count += item_count;
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
// early break since if we still have more messages, they just won't fit anyway
|
// early break since if we still have more messages, they just won't fit anyway
|
||||||
handler_instance.uncount(item_count, 'conversation');
|
handler_instance.uncount(item_count, 'conversation');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let j = openai_msgs.length - 1; j >= 0; j--) {
|
for (let j = messages.length - 1; j >= 0; j--) {
|
||||||
let item = openai_msgs[j];
|
let item = messages[j];
|
||||||
let item_count = handler_instance.count(item, true, 'conversation');
|
let item_count = handler_instance.count(item, true, 'conversation');
|
||||||
await delay(1);
|
|
||||||
// If we have enough space for this message, also account for the max assistant reply size
|
// If we have enough space for this message, also account for the max assistant reply size
|
||||||
if ((total_count + item_count) < (this_max_context - oai_settings.openai_max_tokens)) {
|
if ((total_count + item_count) < (oai_settings.openai_max_context - oai_settings.openai_max_tokens)) {
|
||||||
openai_msgs_tosend.push(item);
|
messages.push(item);
|
||||||
total_count += item_count;
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
// early break since if we still have more messages, they just won't fit anyway
|
// early break since if we still have more messages, they just won't fit anyway
|
||||||
handler_instance.uncount(item_count, 'conversation');
|
handler_instance.uncount(item_count, 'conversation');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(total_count);
|
|
||||||
|
|
||||||
// each example block contains multiple user/bot messages
|
// each example block contains multiple user/bot messages
|
||||||
for (let example_block of openai_msgs_example) {
|
for (let example_block of exampleMessages) {
|
||||||
if (example_block.length == 0) { continue; }
|
if (example_block.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// include the heading
|
// include the heading
|
||||||
example_block = [new_chat_msg, ...example_block];
|
example_block = [new_chat_msg, ...example_block];
|
||||||
|
|
||||||
// add the block only if there is enough space for all its messages
|
// add the block only if there is enough space for all its messages
|
||||||
const example_count = handler_instance.count(example_block, true, 'examples');
|
const example_count = handler_instance.count(example_block, true, 'examples');
|
||||||
await delay(1);
|
if ((total_count + example_count) < (oai_settings.openai_max_context - oai_settings.openai_max_tokens)) {
|
||||||
if ((total_count + example_count) < (this_max_context - oai_settings.openai_max_tokens)) {
|
|
||||||
examples_tosend.push(...example_block)
|
examples_tosend.push(...example_block)
|
||||||
total_count += example_count;
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
// early break since more examples probably won't fit anyway
|
// early break since more examples probably won't fit anyway
|
||||||
handler_instance.uncount(example_count, 'examples');
|
handler_instance.uncount(example_count, 'examples');
|
||||||
break;
|
break;
|
||||||
@ -533,58 +538,7 @@ async function prepareOpenAIMessages({ systemPrompt, name2, storyString, worldIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatCompletion = promptManager.getChatCompletion();
|
return examples_tosend;
|
||||||
|
|
||||||
chatCompletion.replace('worldInfoBefore', {role: "system", content: wiBefore});
|
|
||||||
chatCompletion.replace('worldInfoAfter', {role: "system", content: wiAfter});
|
|
||||||
chatCompletion.replace('characterInfo', {role: 'system', content: characterInfo});
|
|
||||||
chatCompletion.replace('newExampleChat', new_chat_msg);
|
|
||||||
chatCompletion.replace('newMainChat', new_chat_msg);
|
|
||||||
chatCompletion.replace('chatHistory', openai_msgs);
|
|
||||||
|
|
||||||
const flattenedExampleMessages = 0 !== openai_msgs_example.length ? openai_msgs_example.reduce( (examples, obj) => [...examples, ...obj]) : [];
|
|
||||||
chatCompletion.replace('dialogueExamples', flattenedExampleMessages);
|
|
||||||
|
|
||||||
chatCompletion.insertAfter('characterInfo', 'enhancedDefinitions', {role: 'system', content: enhance_definitions_prompt});
|
|
||||||
chatCompletion.insertAfter('worldInfoAfter', 'extensionPrompt', extensionPrompt);
|
|
||||||
|
|
||||||
|
|
||||||
openai_messages_count = openai_msgs_tosend.filter(x => x.role == "user" || x.role == "assistant").length + openai_narrator_messages_count;
|
|
||||||
// reverse the messages array because we had the newest at the top to remove the oldest,
|
|
||||||
// now we want proper order
|
|
||||||
//openai_msgs_tosend.reverse();
|
|
||||||
//openai_msgs_tosend = [prompt_msg, ...examples_tosend, new_chat_msg, ...openai_msgs_tosend]
|
|
||||||
|
|
||||||
openai_msgs_tosend = chatCompletion.getChat();
|
|
||||||
|
|
||||||
//console.log("We're sending this:")
|
|
||||||
//console.log(openai_msgs_tosend);
|
|
||||||
//console.log(`Calculated the total context to be ${total_count} tokens`);
|
|
||||||
handler_instance.log();
|
|
||||||
return [
|
|
||||||
openai_msgs_tosend,
|
|
||||||
handler_instance.counts,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSystemPrompt(systemPrompt, nsfw_toggle_prompt, enhance_definitions_prompt, wiBefore, storyString, wiAfter, extensionPrompt, isImpersonate) {
|
|
||||||
// If the character has a custom system prompt AND user has it preferred, use that instead of the default
|
|
||||||
let prompt = power_user.prefer_character_prompt && systemPrompt ? systemPrompt : oai_settings.main_prompt;
|
|
||||||
let whole_prompt = [];
|
|
||||||
|
|
||||||
if (isImpersonate) {
|
|
||||||
whole_prompt = [nsfw_toggle_prompt, enhance_definitions_prompt + "\n\n" + wiBefore, storyString, wiAfter, extensionPrompt];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// If it's toggled, NSFW prompt goes first.
|
|
||||||
if (oai_settings.nsfw_first) {
|
|
||||||
whole_prompt = [nsfw_toggle_prompt, prompt, enhance_definitions_prompt + "\n\n" + wiBefore, storyString, wiAfter, extensionPrompt];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
whole_prompt = [prompt, nsfw_toggle_prompt, enhance_definitions_prompt, "\n", wiBefore, storyString, wiAfter, extensionPrompt].filter(elem => elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return whole_prompt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryParseStreamingError(response, decoded) {
|
function tryParseStreamingError(response, decoded) {
|
||||||
@ -1185,9 +1139,7 @@ function loadOpenAISettings(data, settings) {
|
|||||||
oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
|
oai_settings.proxy_password = settings.proxy_password ?? default_settings.proxy_password;
|
||||||
oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
|
oai_settings.assistant_prefill = settings.assistant_prefill ?? default_settings.assistant_prefill;
|
||||||
|
|
||||||
if (0 === settings.prompts.length) oai_settings.prompts = default_settings.prompts;
|
oai_settings.prompts = settings.prompts ?? []
|
||||||
else oai_settings.prompts = settings.prompts
|
|
||||||
|
|
||||||
oai_settings.prompt_lists = settings.prompt_lists ?? [];
|
oai_settings.prompt_lists = settings.prompt_lists ?? [];
|
||||||
oai_settings.prompt_manager_settings = settings.prompt_manager_settings ?? [];
|
oai_settings.prompt_manager_settings = settings.prompt_manager_settings ?? [];
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user