Replace multigen with auto-continue

This commit is contained in:
Cohee
2023-09-15 21:34:41 +03:00
parent eaca6ddaf0
commit d34f7d3e1a
8 changed files with 120 additions and 241 deletions

View File

@ -287,7 +287,7 @@ export const event_types = {
USER_MESSAGE_RENDERED: 'user_message_rendered',
CHARACTER_MESSAGE_RENDERED: 'character_message_rendered',
FORCE_SET_BACKGROUND: 'force_set_background',
CHAT_DELETED : 'chat_deleted',
CHAT_DELETED: 'chat_deleted',
GROUP_CHAT_DELETED: 'group_chat_deleted',
}
@ -662,9 +662,7 @@ export let user_avatar = "you.png";
export var amount_gen = 80; //default max length of AI generated responses
var max_context = 2048;
var tokens_already_generated = 0;
var message_already_generated = "";
var cycle_count_generation = 0;
var swipes = true;
let extension_prompts = {};
@ -1158,7 +1156,7 @@ function showMoreMessages() {
console.debug('Inserting messages before', messageId, 'count', count, 'chat length', chat.length);
const prevHeight = $('#chat').prop('scrollHeight');
while(messageId > 0 && count > 0) {
while (messageId > 0 && count > 0) {
count--;
messageId--;
addOneMessage(chat[messageId], { insertBefore: messageId + 1, scroll: false, forceId: messageId });
@ -2060,8 +2058,7 @@ function isStreamingEnabled() {
return ((main_api == 'openai' && oai_settings.stream_openai && oai_settings.chat_completion_source !== chat_completion_sources.SCALE && oai_settings.chat_completion_source !== chat_completion_sources.AI21)
|| (main_api == 'kobold' && kai_settings.streaming_kobold && kai_flags.can_use_streaming)
|| (main_api == 'novel' && nai_settings.streaming_novel)
|| (main_api == 'textgenerationwebui' && textgenerationwebui_settings.streaming))
&& !isMultigenEnabled(); // Multigen has a quasi-streaming mode which breaks the real streaming
|| (main_api == 'textgenerationwebui' && textgenerationwebui_settings.streaming));
}
function showStopButton() {
@ -2311,7 +2308,6 @@ class StreamingProcessor {
async function Generate(type, { automatic_trigger, force_name2, resolve, reject, quiet_prompt, force_chid, signal } = {}, dryRun = false) {
//console.log('Generate entered');
setGenerationProgress(0);
tokens_already_generated = 0;
generation_started = new Date();
// Don't recreate abort controller if signal is passed
@ -2324,17 +2320,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
const isImpersonate = type == "impersonate";
message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `;
// Name for the multigen prefix
const magName = isImpersonate ? name1 : name2;
if (isInstruct) {
message_already_generated = formatInstructModePrompt(magName, isImpersonate, '', name1, name2);
} else {
message_already_generated = `${magName}: `;
}
// To trim after multigen ended
const magFirst = message_already_generated;
const interruptedByCommand = processCommands($("#send_textarea").val(), type);
@ -2356,12 +2341,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
return;
}
if (main_api == 'kobold' && kai_settings.streaming_kobold && power_user.multigen) {
toastr.error('Multigen is not supported with Kobold streaming enabled. Disable streaming in "AI Response Configuration" or multigen in "Advanced Formatting" to proceed.', undefined, { timeOut: 10000, preventDuplicates: true, });
is_send_press = false;
return;
}
if (isHordeGenerationNotAllowed()) {
is_send_press = false;
return;
@ -2605,7 +2584,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
const storyString = renderStoryString(storyStringParams);
if (main_api === 'openai') {
message_already_generated = ''; // OpenAI doesn't have multigen
message_already_generated = '';
setOpenAIMessages(coreChat);
setOpenAIMessageExamples(mesExamplesArray);
}
@ -2699,10 +2678,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
// Save reply does add cycle text to the prompt, so it's not needed here
streamingProcessor && (streamingProcessor.firstMessageText = '');
message_already_generated = continue_mag;
tokens_already_generated = 1; // Multigen copium
}
// Multigen rewrites the type and I don't know why
const originalType = type;
runGenerate(cyclePrompt);
@ -2761,13 +2738,13 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
}
// Get instruct mode line
if (isInstruct && tokens_already_generated === 0) {
if (isInstruct && !isContinue) {
const name = isImpersonate ? name1 : name2;
lastMesString += formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2);
}
// Get non-instruct impersonation line
if (!isInstruct && isImpersonate && tokens_already_generated === 0) {
if (!isInstruct && isImpersonate && !isContinue) {
const name = name1;
if (!lastMesString.endsWith('\n')) {
lastMesString += '\n';
@ -2777,7 +2754,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
// Add character's name
// Force name append on continue
if (!isInstruct && force_name2 && (tokens_already_generated === 0 || isContinue)) {
if (!isInstruct && force_name2) {
if (!lastMesString.endsWith('\n')) {
lastMesString += '\n';
}
@ -2890,14 +2867,12 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
});
// TODO: Move zero-depth anchor append to work like CFG and bias appends
if (zeroDepthAnchor && zeroDepthAnchor.length) {
if (!isMultigenEnabled() || tokens_already_generated == 0) {
console.log(/\s/.test(finalMesSend[finalMesSend.length - 1].message.slice(-1)))
finalMesSend[finalMesSend.length - 1].message +=
/\s/.test(finalMesSend[finalMesSend.length - 1].message.slice(-1))
? zeroDepthAnchor
: `${zeroDepthAnchor}`;
}
if (zeroDepthAnchor?.length && !isContinue) {
console.log(/\s/.test(finalMesSend[finalMesSend.length - 1].message.slice(-1)))
finalMesSend[finalMesSend.length - 1].message +=
/\s/.test(finalMesSend[finalMesSend.length - 1].message.slice(-1))
? zeroDepthAnchor
: `${zeroDepthAnchor}`;
}
let cfgPrompt = {};
@ -2919,7 +2894,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
// Add prompt bias after everything else
// Always run with continue
if (!isInstruct && !isImpersonate && (tokens_already_generated === 0 || isContinue)) {
if (!isInstruct && !isImpersonate) {
if (promptBias.trim().length !== 0) {
finalMesSend[finalMesSend.length - 1].message +=
/\s/.test(finalMesSend[finalMesSend.length - 1].message.slice(-1))
@ -2968,11 +2943,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
let this_amount_gen = Number(amount_gen); // how many tokens the AI will be requested to generate
let this_settings = koboldai_settings[koboldai_setting_names[preset_settings]];
if (isMultigenEnabled() && type !== 'quiet') {
// if nothing has been generated yet..
this_amount_gen = getMultigenAmount();
}
let thisPromptBits = [];
// TODO: Make this a switch
@ -3123,6 +3093,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
if (isStreamingEnabled() && type !== 'quiet') {
hideSwipeButtons();
let getMessage = await streamingProcessor.generate();
let messageChunk = extractNameFromMessage(cleanUpMessage(getMessage, isImpersonate, isContinue, false), force_name2, isImpersonate).getMessage;
if (isContinue) {
getMessage = continue_mag + getMessage;
@ -3131,10 +3102,13 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
if (streamingProcessor && !streamingProcessor.isStopped && streamingProcessor.isFinished) {
await streamingProcessor.onFinishStreaming(streamingProcessor.messageId, getMessage);
streamingProcessor = null;
triggerAutoContinue(messageChunk, isImpersonate);
}
}
async function onSuccess(data) {
let messageChunk = '';
if (data.error == 'dryRun') {
generatedPromptCache = '';
resolve();
@ -3147,49 +3121,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
let title = extractTitleFromData(data);
kobold_horde_model = title;
// to make it continue generating so long as it's under max_amount and hasn't signaled
// an end to the character's response via typing "You:" or adding "<endoftext>"
if (isMultigenEnabled() && type !== 'quiet') {
message_already_generated += getMessage;
promptBias = '';
let this_mes_is_name;
({ this_mes_is_name, getMessage } = extractNameFromMessage(getMessage, force_name2, isImpersonate));
if (!isImpersonate) {
if (tokens_already_generated == 0) {
console.debug("New message");
({ type, getMessage } = await saveReply(type, getMessage, this_mes_is_name, title));
}
else {
console.debug("Should append message");
({ type, getMessage } = await saveReply('append', getMessage, this_mes_is_name, title));
}
} else {
let chunk = cleanUpMessage(message_already_generated, true, isContinue, true);
let extract = extractNameFromMessage(chunk, force_name2, isImpersonate);
$('#send_textarea').val(extract.getMessage).trigger('input');
}
if (shouldContinueMultigen(getMessage, isImpersonate, isInstruct)) {
hideSwipeButtons();
tokens_already_generated += this_amount_gen; // add new gen amt to any prev gen counter..
getMessage = message_already_generated;
// if any tokens left to generate
if (getMultigenAmount() > 0) {
runGenerate(getMessage);
console.debug('returning to make generate again');
return;
}
}
tokens_already_generated = 0;
generatedPromptCache = "";
const substringStart = originalType !== 'continue' ? magFirst.length : 0;
getMessage = message_already_generated.substring(substringStart);
}
if (isContinue) {
getMessage = continue_mag + getMessage;
}
@ -3210,8 +3141,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
resolve(getMessage);
}
else {
// Without streaming we'll be having a full message on continuation. Treat it as a multigen last chunk.
if (!isMultigenEnabled() && originalType !== 'continue') {
// Without streaming we'll be having a full message on continuation. Treat it as a last chunk.
if (originalType !== 'continue') {
({ type, getMessage } = await saveReply(type, getMessage, this_mes_is_name, title));
}
else {
@ -3265,6 +3196,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
return
}
}
messageChunk = getMessage;
} else {
generatedPromptCache = '';
activateSendButtons();
@ -3290,6 +3223,8 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
if (type !== 'quiet') {
resolve();
}
triggerAutoContinue(messageChunk, isImpersonate);
};
function onError(exception) {
@ -3320,6 +3255,55 @@ function getNextMessageId(type) {
return type == 'swipe' ? Number(count_view_mes - 1) : Number(count_view_mes);
}
/**
*
* @param {string} messageChunk
* @param {boolean} isImpersonate
* @returns {void}
*/
export function triggerAutoContinue(messageChunk, isImpersonate) {
if (selected_group) {
console.debug('Auto-continue is disabled for group chat');
return;
}
if (power_user.auto_continue.enabled && !is_send_press) {
if (power_user.auto_continue.target_length <= 0) {
console.debug('Auto-continue target length is 0, not triggering auto-continue');
return;
}
if (main_api === 'openai' && !power_user.auto_continue.allow_chat_completions){
console.debug('Auto-continue for OpenAI is disabled by user.');
return;
}
if (isImpersonate) {
console.debug('Continue for impersonation is not implemented yet');
return;
}
const USABLE_LENGTH = 5;
if (messageChunk.trim().length > USABLE_LENGTH && chat.length) {
const lastMessage = chat[chat.length - 1];
const messageLength = getTokenCount(lastMessage.mes);
const shouldAutoContinue = messageLength < power_user.auto_continue.target_length;
if (shouldAutoContinue) {
console.debug(`Triggering auto-continue. Message tokens: ${messageLength}. Target tokens: ${power_user.auto_continue.target_length}`);
$("#option_continue").trigger('click');
} else {
console.debug(`Not triggering auto-continue. Message tokens: ${messageLength}. Target tokens: ${power_user.auto_continue.target_length}`);
return;
}
} else {
console.debug('Last generated chunk was empty, not triggering auto-continue');
return;
}
}
}
export function getBiasStrings(textareaText, type) {
if (type == 'impersonate' || type == 'continue') {
return { messageBias: '', promptBias: '', isUserPromptBias: false };
@ -3497,35 +3481,6 @@ function appendZeroDepthAnchor(force_name2, zeroDepthAnchor, finalPrompt) {
return finalPrompt;
}
function getMultigenAmount() {
let this_amount_gen = Number(amount_gen);
if (tokens_already_generated === 0) {
// if the max gen setting is > 50...(
if (Number(amount_gen) >= power_user.multigen_first_chunk) {
// then only try to make 50 this cycle..
this_amount_gen = power_user.multigen_first_chunk;
}
else {
// otherwise, make as much as the max amount request.
this_amount_gen = Number(amount_gen);
}
}
// if we already received some generated text...
else {
// if the remaining tokens to be made is less than next potential cycle count
if (Number(amount_gen) - tokens_already_generated < power_user.multigen_next_chunks) {
// subtract already generated amount from the desired max gen amount
this_amount_gen = Number(amount_gen) - tokens_already_generated;
}
else {
// otherwise make the standard cycle amount (first 50, and 30 after that)
this_amount_gen = power_user.multigen_next_chunks;
}
}
return this_amount_gen;
}
async function DupeChar() {
if (!this_chid) {
toastr.warning('You must first select a character to duplicate!')
@ -3749,26 +3704,6 @@ function getGenerateUrl() {
return generate_url;
}
function shouldContinueMultigen(getMessage, isImpersonate, isInstruct) {
if (isInstruct && power_user.instruct.stop_sequence) {
if (message_already_generated.indexOf(power_user.instruct.stop_sequence) !== -1) {
return false;
}
}
// stopping name string
const nameString = isImpersonate ? `${name2}:` : `${name1}:`;
// if there is no 'You:' in the response msg
const doesNotContainName = message_already_generated.indexOf(nameString) === -1;
//if there is no <endoftext> stamp in the response msg
const isNotEndOfText = message_already_generated.indexOf('<|endoftext|>') === -1;
//if the gen'd msg is less than the max response length..
const notReachedMax = tokens_already_generated < Number(amount_gen);
//if we actually have gen'd text at all...
const msgHasText = getMessage.length > 0;
return doesNotContainName && isNotEndOfText && notReachedMax && msgHasText;
}
function extractNameFromMessage(getMessage, force_name2, isImpersonate) {
const nameToTrim = isImpersonate ? name1 : name2;
let this_mes_is_name = true;
@ -3866,7 +3801,7 @@ function cleanUpMessage(getMessage, isImpersonate, isContinue, displayIncomplete
if (nameToTrim && getMessage.indexOf(`${nameToTrim}:`) == 0) {
getMessage = getMessage.substr(0, getMessage.indexOf(`${nameToTrim}:`));
}
if (nameToTrim && getMessage.indexOf(`\n${nameToTrim}:`) > 0) {
if (nameToTrim && getMessage.indexOf(`\n${nameToTrim}:`) >= 0) {
getMessage = getMessage.substr(0, getMessage.indexOf(`\n${nameToTrim}:`));
}
if (getMessage.indexOf('<|endoftext|>') != -1) {
@ -4125,10 +4060,6 @@ function extractImageFromMessage(getMessage) {
return { getMessage, image, title };
}
export function isMultigenEnabled() {
return power_user.multigen && (main_api == 'textgenerationwebui' || main_api == 'kobold' || main_api == 'koboldhorde' || main_api == 'novel');
}
export function activateSendButtons() {
is_send_press = false;
$("#send_but").removeClass("displayNone");
@ -6563,13 +6494,6 @@ const swipe_right = () => {
return;
}
// if (chat.length == 1) {
// if (chat[0]['swipe_id'] !== undefined && chat[0]['swipe_id'] == chat[0]['swipes'].length - 1) {
// toastr.info('Add more alternative greetings to swipe through', 'That\'s all for now');
// return;
// }
// }
const swipe_duration = 200;
const swipe_range = 700;
//console.log(swipe_range);