Ability to specify multiple messages and roles in generateRaw() (#4264) * adding capability for generateRaw() to specify roles of messages for chat completion * linting * additional comments * Apply review comments, fix type errors * Fix joiner for non-instruct prompts * Format joint prompt for novel, fix prefix for non-instruct * Fix role for string prompts * Do not trim sysprompt for instruct * Do not prefix names for CC * Append newline to systemPrompt in instruct --------- Co-authored-by: qvink <qvink@users.noreply.github.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

db318cd75c76b1f6d50c25a71a579863361aa49b

qvink <191186569+qvink@users.noreply.github.com>

Signed
1 files changed, +62 -19Ignore whitespace
public/script.js+62 -19
@@ -3053,8 +3053,64 @@ class StreamingProcessor {
3053}3053}
30543054
3055/**3055/**
3056 * Constructs a prompt to be used for either Text Completion or Chat Completion. Input is format-agnostic.
3057 * @param {string | object[]} prompt Input prompt. Can be a string or an array of chat-style messages, i.e. [{role: '', content: ''}, ...]
3058 * @param {string} api API to use.
3059 * @param {boolean} instructOverride true to override instruct mode, false to use the default value
3060 * @param {boolean} quietToLoud true to generate a message in system mode, false to generate a message in character mode
3061 * @param {string} [systemPrompt] System prompt to use. Only Instruct mode or OpenAI.
3062 * @returns {string | object[]} Prompt ready for use in generation. If using TC, this will be a string. If using CC, this will be an array of chat-style messages.
3063 */
3064export function createRawPrompt(prompt, api, instructOverride, quietToLoud, systemPrompt) {
3065 const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride;
3066
3067 // If the prompt was given as a string, convert to a message-style object assuming user role
3068 if (typeof prompt === 'string') {
3069 const message = api === 'openai'
3070 ? { role: 'user', content: prompt.trim() }
3071 : { role: 'system', content: prompt };
3072 prompt = [message];
3073 } else { // checks for message-style object
3074 if (prompt.length === 0 && !systemPrompt) throw Error('No messages provided');
3075 }
3076
3077 // Format each message in the prompt, accounting for the provided roles
3078 for (const message of prompt) {
3079 let name = '';
3080 if (message.role === 'user') name = message.name ?? name1;
3081 if (message.role === 'assistant') name = message.name ?? name2;
3082 if (message.role === 'system') name = message.name ?? '';
3083 const prefix = isInstruct || api === 'openai' ? '' : (name ? `${name}: ` : '');
3084 message.content = prefix + substituteParams(message.content ?? '');
3085 if (isInstruct) { // instruct formatting for text completion
3086 const isUser = message.role === 'user';
3087 const isNarrator = message.role === 'system';
3088 message.content = formatInstructModeChat(name, message.content, isUser, isNarrator, '', name1, name2, false);
3089 }
3090 }
3091
3092 // prepend system prompt, if provided
3093 if (systemPrompt) {
3094 systemPrompt = substituteParams(systemPrompt);
3095 systemPrompt = isInstruct ? (formatInstructModeSystemPrompt(systemPrompt) + '\n') : systemPrompt.trim();
3096 prompt.unshift({ role: 'system', content: systemPrompt });
3097 }
3098
3099 // If text completion, convert to text prompt by concatenating all message contents
3100 if (api !== 'openai') {
3101 const joiner = isInstruct ? '' : '\n';
3102 prompt = prompt.map(message => message.content).join(joiner);
3103 prompt = api === 'novel' ? adjustNovelInstructionPrompt(prompt) : prompt;
3104 prompt = prompt + (isInstruct ? formatInstructModePrompt(name2, false, '', name1, name2, true, quietToLoud) : '\n'); // add last line
3105 }
3106
3107 return prompt;
3108}
3109
3110/**
3056 * Generates a message using the provided prompt.3111 * Generates a message using the provided prompt.
3057 * @param {string} prompt Prompt to generate a message from3112 * If the prompt is an array of chat-style messages and not using chat completion, it will be converted to a text prompt.
3113 * @param {string | object[]} prompt Prompt to generate a message from. Can be a string or an array of chat-style messages, i.e. [{role: '', content: ''}, ...]
3058 * @param {string} api API to use. Main API is used if not specified.3114 * @param {string} api API to use. Main API is used if not specified.
3059 * @param {boolean} instructOverride true to override instruct mode, false to use the default value3115 * @param {boolean} instructOverride true to override instruct mode, false to use the default value
3060 * @param {boolean} quietToLoud true to generate a message in system mode, false to generate a message in character mode3116 * @param {boolean} quietToLoud true to generate a message in system mode, false to generate a message in character mode
@@ -3070,20 +3126,10 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
30703126
3071 const abortController = new AbortController();3127 const abortController = new AbortController();
3072 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;3128 const responseLengthCustomized = typeof responseLength === 'number' && responseLength > 0;
3073 const isInstruct = power_user.instruct.enabled && api !== 'openai' && api !== 'novel' && !instructOverride;
3074 const isQuiet = true;
3075 let eventHook = () => { };3129 let eventHook = () => { };
30763130
3077 if (systemPrompt) {3131 // construct final prompt from the input. Can either be a string or an array of chat-style messages.
3078 systemPrompt = substituteParams(systemPrompt);3132 prompt = createRawPrompt(prompt, api, instructOverride, quietToLoud, systemPrompt);
3079 systemPrompt = isInstruct ? formatInstructModeSystemPrompt(systemPrompt) : systemPrompt;
3080 prompt = api === 'openai' ? prompt : `${systemPrompt}\n${prompt}`;
3081 }
3082
3083 prompt = substituteParams(prompt);
3084 prompt = api == 'novel' ? adjustNovelInstructionPrompt(prompt) : prompt;
3085 prompt = isInstruct ? formatInstructModeChat(name1, prompt, false, true, '', name1, name2, false) : prompt;
3086 prompt = isInstruct ? (prompt + formatInstructModePrompt(name2, false, '', name1, name2, isQuiet, quietToLoud)) : (prompt + '\n');
30873133
3088 try {3134 try {
3089 if (responseLengthCustomized) {3135 if (responseLengthCustomized) {
@@ -3100,7 +3146,7 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
3100 } else {3146 } else {
3101 const isHorde = api === 'koboldhorde';3147 const isHorde = api === 'koboldhorde';
3102 const koboldSettings = koboldai_settings[koboldai_setting_names[kai_settings.preset_settings]];3148 const koboldSettings = koboldai_settings[koboldai_setting_names[kai_settings.preset_settings]];
3103 generateData = getKoboldGenerationData(prompt, koboldSettings, amount_gen, max_context, isHorde, 'quiet');3149 generateData = getKoboldGenerationData(prompt.toString(), koboldSettings, amount_gen, max_context, isHorde, 'quiet');
3104 }3150 }
3105 TempResponseLength.restore(api);3151 TempResponseLength.restore(api);
3106 break;3152 break;
@@ -3115,10 +3161,7 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
3115 TempResponseLength.restore(api);3161 TempResponseLength.restore(api);
3116 break;3162 break;
3117 case 'openai': {3163 case 'openai': {
3118 generateData = [{ role: 'user', content: prompt.trim() }];3164 generateData = prompt; // generateData is just the chat message object
3119 if (systemPrompt) {
3120 generateData.unshift({ role: 'system', content: systemPrompt.trim() });
3121 }
3122 eventHook = TempResponseLength.setupEventHook(api);3165 eventHook = TempResponseLength.setupEventHook(api);
3123 } break;3166 } break;
3124 }3167 }
@@ -3126,7 +3169,7 @@ export async function generateRaw(prompt, api, instructOverride, quietToLoud, sy
3126 let data = {};3169 let data = {};
31273170
3128 if (api === 'koboldhorde') {3171 if (api === 'koboldhorde') {
3129 data = await generateHorde(prompt, generateData, abortController.signal, false);3172 data = await generateHorde(prompt.toString(), generateData, abortController.signal, false);
3130 } else if (api === 'openai') {3173 } else if (api === 'openai') {
3131 data = await sendOpenAIRequest('quiet', generateData, abortController.signal);3174 data = await sendOpenAIRequest('quiet', generateData, abortController.signal);
3132 } else {3175 } else {