diff --git a/public/index.html b/public/index.html index 2a953f5c0..548d7db66 100644 --- a/public/index.html +++ b/public/index.html @@ -42,6 +42,9 @@ + + + Tavern.AI diff --git a/public/script.js b/public/script.js index 0f4b90550..bb65cb6b3 100644 --- a/public/script.js +++ b/public/script.js @@ -1,6 +1,32 @@ import { humanizedDateTime } from "./scripts/RossAscends-mods.js"; import { encode, decode } from "../scripts/gpt-2-3-tokenizer/mod.js"; +import { + world_info_budget, + world_info_data, + world_info_depth, + world_info, + checkWorldInfo, + selectImportedWorldInfo, + setWorldInfoSettings, + deleteWorldInfo, +} from "./scripts/worldinfo.js"; + +import { + groups, + selected_group, + saveGroupChat, + getGroups, + generateGroupWrapper, + deleteGroup, + is_group_generating, + printGroups, + resetSelectedGroup, + select_group_chats, +} from "./scripts/groupchats.js"; + +import { debounce, delay } from "./scripts/utils.js"; + //RossAscends: exporting functions and vars for RA mods. export { Generate, @@ -9,6 +35,18 @@ export { printMessages, clearChat, getChat, + getCharacters, + callPopup, + substituteParams, + sendSystemMessage, + addOneMessage, + resetChatState, + select_rm_info, + setCharacterId, + setCharacterName, + setEditedMessageId, + setSendButtonState, + chat, this_chid, settings, characters, @@ -16,9 +54,11 @@ export { main_api, api_server, api_key_novel, - getCharacters, + token, is_send_press, - world_info, + default_avatar, + system_message_types, + talkativeness_default, }; // API OBJECT FOR EXTERNAL WIRING @@ -49,9 +89,6 @@ let count_view_mes = 0; let mesStr = ""; let generatedPromtCache = ""; let characters = []; -let groups = []; -let selected_group = null; -let is_group_automode_enabled = false; let this_chid; let active_character; let backgrounds = []; @@ -62,6 +99,8 @@ let is_checked_colab = false; let is_mes_reload_avatar = false; let collapse_newlines = false; +const saveSettingsDebounced = debounce(() => saveSettings(), 500); + const system_message_types = { HELP: "help", WELCOME: "welcome", @@ -79,7 +118,7 @@ const system_messages = { is_name: true, mes: 'Hi there! The following chat formatting commands are supported:

Need more help? Visit our wiki – TavernAI Wiki!

', }, - welcome: + welcome: { name: systemUserName, force_avatar: system_avatar, @@ -115,17 +154,12 @@ const system_messages = { }, }; -const world_info_position = { - before: 0, - after: 1, -}; const talkativeness_default = 0.5; const storage_keys = { collapse_newlines: "TavernAI_collapse_newlines", }; var is_advanced_char_open = false; -var is_world_edit_open = false; var menu_type = ""; //what is selected in the menu var selected_button = ""; //which button pressed @@ -139,9 +173,6 @@ var create_save_scenario = ""; var create_save_mes_example = ""; var create_save_talkativeness = talkativeness_default; -var timerSaveEdit; -var timerWorldSave; -var timerGroupSave; var durationSaveEdit = 200; //animation right menu var animation_rm_duration = 200; @@ -155,14 +186,12 @@ var api_server = ""; var api_server_textgenerationwebui = ""; //var interval_timer = setInterval(getStatus, 2000); var interval_timer_novel = setInterval(getStatusNovel, 3000); -const groupAutoModeInterval = setInterval(groupChatAutoModeWorker, 5000); var is_get_status = false; var is_get_status_novel = false; var is_api_button_press = false; var is_api_button_press_novel = false; var is_send_press = false; //Send generation -let is_group_generating = false; // Group generation flag var add_mes_without_animation = false; var this_del_mes = 0; @@ -171,7 +200,6 @@ var this_edit_mes_text = ""; var this_edit_mes_chname = ""; var this_edit_mes_id; -const delay = (ms) => new Promise((res) => setTimeout(res, ms)); //settings var settings; var koboldai_settings; @@ -179,12 +207,6 @@ var koboldai_setting_names; var preset_settings = "gui"; var user_avatar = "you.png"; var temp = 0.5; -var world_info = null; -var world_names; -var world_info_data = null; -var world_info_depth = 2; -var world_info_budget = 128; -var imported_world_name = ""; var amount_gen = 80; //default max length of AI generated responses var max_context = 2048; var rep_pen = 1; @@ -476,80 +498,6 @@ function printCharacters() { printGroups(); } -function printGroups() { - for (let group of groups) { - const template = $("#group_list_template .group_select").clone(); - template.data("id", group.id); - template.find(".ch_name").html(group.name); - $("#rm_print_characters_block").prepend(template); - updateGroupAvatar(group); - } -} - -function updateGroupAvatar(group) { - $("#rm_print_characters_block .group_select").each(function () { - if ($(this).data("id") == group.id) { - const avatar = getGroupAvatar(group); - if (avatar) { - $(this).find(".avatar").replaceWith(avatar); - } - } - }); -} - -function getGroupAvatar(group) { - const memberAvatars = []; - if (group && Array.isArray(group.members) && group.members.length) { - for (const member of group.members) { - const charIndex = characters.findIndex((x) => x.name === member); - if (charIndex !== -1 && characters[charIndex].avatar !== "none") { - const this_avatar = `characters/${characters[charIndex].avatar - }#${Date.now()}`; - memberAvatars.push(this_avatar); - } - if (memberAvatars.length === 4) { - break; - } - } - } - - // Cohee: there's probably a smarter way to do this.. - if (memberAvatars.length === 1) { - const groupAvatar = $("#group_avatars_template .collage_1").clone(); - groupAvatar.find(".img_1").attr("src", memberAvatars[0]); - return groupAvatar; - } - - if (memberAvatars.length === 2) { - const groupAvatar = $("#group_avatars_template .collage_2").clone(); - groupAvatar.find(".img_1").attr("src", memberAvatars[0]); - groupAvatar.find(".img_2").attr("src", memberAvatars[1]); - return groupAvatar; - } - - if (memberAvatars.length === 3) { - const groupAvatar = $("#group_avatars_template .collage_3").clone(); - groupAvatar.find(".img_1").attr("src", memberAvatars[0]); - groupAvatar.find(".img_2").attr("src", memberAvatars[1]); - groupAvatar.find(".img_3").attr("src", memberAvatars[2]); - return groupAvatar; - } - - if (memberAvatars.length === 4) { - const groupAvatar = $("#group_avatars_template .collage_4").clone(); - groupAvatar.find(".img_1").attr("src", memberAvatars[0]); - groupAvatar.find(".img_2").attr("src", memberAvatars[1]); - groupAvatar.find(".img_3").attr("src", memberAvatars[2]); - groupAvatar.find(".img_4").attr("src", memberAvatars[3]); - return groupAvatar; - } - - // default avatar - const groupAvatar = $("#group_avatars_template .collage_1").clone(); - groupAvatar.find(".img_1").attr("src", group.avatar_url); - return groupAvatar; -} - async function getCharacters() { await getGroups(); @@ -910,97 +858,6 @@ function substituteParams(content) { return content; } -function checkWorldInfo(chat) { - if (world_info_data.entries.length == 0) { - return ""; - } - - const messagesToLookBack = world_info_depth * 2; - let textToScan = chat.slice(0, messagesToLookBack).join("").toLowerCase(); - let worldInfoBefore = ""; - let worldInfoAfter = ""; - let needsToScan = true; - let allActivatedEntries = new Set(); - - const sortedEntries = Object.keys(world_info_data.entries) - .map((x) => world_info_data.entries[x]) - .sort((a, b) => b.order - a.order); - while (needsToScan) { - let activatedNow = new Set(); - - for (let entry of sortedEntries) { - if (allActivatedEntries.has(entry.uid)) { - continue; - } - - if (entry.constant) { - activatedNow.add(entry.uid); - } - - if (Array.isArray(entry.key) && entry.key.length) { - primary: for (let key of entry.key) { - if (key && textToScan.includes(key.trim().toLowerCase())) { - if ( - entry.selective && - Array.isArray(entry.keysecondary) && - entry.keysecondary.length - ) { - secondary: for (let keysecondary of entry.keysecondary) { - if ( - keysecondary && - textToScan.includes(keysecondary.trim().toLowerCase()) - ) { - activatedNow.add(entry.uid); - break secondary; - } - } - } else { - activatedNow.add(entry.uid); - break primary; - } - } - } - } - } - - needsToScan = activatedNow.size > 0; - const newEntries = [...activatedNow] - .map((x) => world_info_data.entries[x]) - .sort((a, b) => sortedEntries.indexOf(a) - sortedEntries.indexOf(b)); - - for (const entry of newEntries) { - if (entry.position === world_info_position.after) { - worldInfoAfter = `${substituteParams( - entry.content - )}\n${worldInfoAfter}`; - } else { - worldInfoBefore = `${substituteParams( - entry.content - )}\n${worldInfoBefore}`; - } - - if ( - encode(worldInfoBefore + worldInfoAfter).length >= world_info_budget - ) { - needsToScan = false; - break; - } - } - - if (needsToScan) { - textToScan = - newEntries - .map((x) => x.content) - .join("\n") - .toLowerCase() + textToScan; - } - - allActivatedEntries = new Set([...allActivatedEntries, ...activatedNow]); - } - - return { worldInfoBefore, worldInfoAfter }; -} - function isHelpRequest(message) { const helpTokens = ["/?", "/help"]; return helpTokens.includes(message.trim().toLowerCase()); @@ -1273,25 +1130,9 @@ async function Generate(type, automatic_trigger) { } } - let worldInfoString = "", - worldInfoBefore = "", - worldInfoAfter = ""; + let { worldInfoString, worldInfoBefore, worldInfoAfter } = getWorldInfoPrompt(chat2); - if (world_info && world_info_data) { - const activatedWorldInfo = checkWorldInfo(chat2); - worldInfoBefore = activatedWorldInfo.worldInfoBefore; - worldInfoAfter = activatedWorldInfo.worldInfoAfter; - worldInfoString = worldInfoBefore + worldInfoAfter; - } - - let extension_prompt = Object.keys(extension_prompts) - .sort() - .map((x) => extension_prompts[x]) - .filter((x) => x) - .join("\n"); - if (extension_prompt.length && !extension_prompt.endsWith("\n")) { - extension_prompt += "\n"; - } + let extension_prompt = getExtensionPrompt(); var i = 0; @@ -1754,21 +1595,7 @@ async function Generate(type, automatic_trigger) { } // clean-up group message from excessive generations if (type == "group_chat" && selected_group) { - const group = groups.find((x) => x.id == selected_group); - - if (group && Array.isArray(group.members) && group.members) { - for (let member of group.members) { - // Skip current speaker. - if (member === name2) { - continue; - } - - const indexOfMember = getMessage.indexOf(member + ":"); - if (indexOfMember != -1) { - getMessage = getMessage.substr(0, indexOfMember); - } - } - } + getMessage = cleanGroupMessage(getMessage); } let this_mes_is_name = true; if (getMessage.indexOf(name2 + ":") === 0) { @@ -1789,10 +1616,9 @@ async function Generate(type, automatic_trigger) { chat[chat.length - 1]["mes"] = getMessage; if (type === "group_chat") { - let avatarImg = default_avatars; + let avatarImg = default_avatar; if (characters[this_chid].avatar != "none") { - avatarImg = `characters/${characters[this_chid].avatar - }?${Date.now()}`; + avatarImg = `characters/${characters[this_chid].avatar}?${Date.now()}`; } chat[chat.length - 1]["is_name"] = true; chat[chat.length - 1]["force_avatar"] = avatarImg; @@ -1838,6 +1664,80 @@ async function Generate(type, automatic_trigger) { } } +function cleanGroupMessage(getMessage) { + const group = groups.find((x) => x.id == selected_group); + + if (group && Array.isArray(group.members) && group.members) { + for (let member of group.members) { + // Skip current speaker. + if (member === name2) { + continue; + } + + const indexOfMember = getMessage.indexOf(member + ":"); + if (indexOfMember != -1) { + getMessage = getMessage.substr(0, indexOfMember); + } + } + } + return getMessage; +} + +function getExtensionPrompt() { + let extension_prompt = Object.keys(extension_prompts) + .sort() + .map((x) => extension_prompts[x]) + .filter(x => x) + .join("\n"); + if (extension_prompt.length && !extension_prompt.endsWith("\n")) { + extension_prompt += "\n"; + } + return extension_prompt; +} + +function getWorldInfoPrompt(chat2) { + let worldInfoString = "", worldInfoBefore = "", worldInfoAfter = ""; + + if (world_info && world_info_data) { + const activatedWorldInfo = checkWorldInfo(chat2); + worldInfoBefore = activatedWorldInfo.worldInfoBefore; + worldInfoAfter = activatedWorldInfo.worldInfoAfter; + worldInfoString = worldInfoBefore + worldInfoAfter; + } + return { worldInfoString, worldInfoBefore, worldInfoAfter }; +} + +function resetChatState() { + active_character = "invalid-safety-id"; //unsets the chid in settings (this prevents AutoLoadChat from trying to load the wrong ChID + this_chid = "invalid-safety-id"; //unsets expected chid before reloading (related to getCharacters/printCharacters from using old arrays) + name2 = systemUserName; // replaces deleted charcter name with system user since it will be displayed next. + chat = [...safetychat]; // sets up system user to tell user about having deleted a character + characters.length = 0; // resets the characters array, forcing getcharacters to reset +} + +function setCharacterId(value) { + this_chid = value; +} + +function setCharacterName(value) { + name2 = value; +} + +function setEditedMessageId(value) { + this_edit_mes_id = value; +} + +function setSendButtonState(value) { + is_send_press = value; +} + +function resultCheckStatusNovel() { + is_api_button_press_novel = false; + checkOnlineStatus(); + $("#api_loading_novel").css("display", "none"); + $("#api_button_novel").css("display", "inline-block"); +} + async function saveChat() { chat.forEach(function (item, i) { if (item["is_user"]) { @@ -1877,6 +1777,29 @@ async function saveChat() { }); } + +function read_avatar_load(input) { + if (input.files && input.files[0]) { + const reader = new FileReader(); + if (selected_button == "create") { + create_save_avatar = input.files; + } + reader.onload = function (e) { + if (selected_button == "character_edit") { + setTimeout(() => { + $("#create_button").click(); + }, durationSaveEdit); + } + $("#avatar_load_preview").attr("src", e.target.result); + //.width(103) + //.height(83); + //console.log(e.target.result.name); + }; + + reader.readAsDataURL(input.files[0]); + } +} + async function getChat() { console.log("/getchat -- entered for -- " + characters[this_chid].name); jQuery.ajax({ @@ -1944,413 +1867,6 @@ function getChatResult() { printMessages(); select_selected_character(this_chid); } -async function generateGroupWrapper(by_auto_mode) { - if (online_status === "no_connection") { - is_group_generating = false; - is_send_press = false; - return; - } - - const group = groups.find((x) => x.id === selected_group); - - if (!group || !Array.isArray(group.members) || !group.members.length) { - sendSystemMessage(system_message_types.EMPTY); - return; - } - - try { - is_group_generating = true; - this_chid = undefined; - name2 = ""; - const userInput = $("#send_textarea").val(); - - let typingIndicator = $("#chat .typing_indicator"); - - if (typingIndicator.length === 0) { - typingIndicator = $( - "#typing_indicator_template .typing_indicator" - ).clone(); - typingIndicator.hide(); - $("#chat").append(typingIndicator); - } - - let messagesBefore = chat.length; - let activationText = ""; - if (userInput && userInput.length && !by_auto_mode) { - activationText = userInput; - messagesBefore++; - } else { - const lastMessage = chat[chat.length - 1]; - if (lastMessage && !lastMessage.is_system) { - activationText = lastMessage.mes; - } - } - - const activatedMembers = activateMembers(group.members, activationText); - // now the real generation begins: cycle through every character - for (const chId of activatedMembers) { - this_chid = chId; - name2 = characters[chId].name; - - await Generate("group_chat", by_auto_mode); - - // update indicator and scroll down - typingIndicator - .find(".typing_indicator_name") - .text(characters[chId].name); - $("#chat").append(typingIndicator); - typingIndicator.show(250, function () { - typingIndicator.get(0).scrollIntoView({ behavior: "smooth" }); - }); - - while (true) { - // check if message generated already - if (chat.length == messagesBefore) { - await delay(10); - } else { - messagesBefore++; - break; - } - } - - // hide and reapply the indicator to the bottom of the list - typingIndicator.hide(250); - $("#chat").append(typingIndicator); - } - } finally { - is_group_generating = false; - is_send_press = false; - this_chid = undefined; - } -} -function activateMembers(members, input) { - let activatedNames = []; - - // find mentions - if (input && input.length) { - for (let inputWord of extractAllWords(input)) { - for (let member of members) { - if (extractAllWords(member).includes(inputWord)) { - activatedNames.push(member); - break; - } - } - } - } - - // activation by talkativeness (in shuffled order) - const shuffledMembers = shuffle([...members]); - for (let member of shuffledMembers) { - const character = characters.find((x) => x.name === member); - - if (!character) { - continue; - } - - const rollValue = Math.random(); - let talkativeness = Number(character.talkativeness); - talkativeness = Number.isNaN(talkativeness) - ? talkativeness_default - : talkativeness; - if (talkativeness >= rollValue) { - activatedNames.push(member); - } - } - - // pick 1 at random if no one was activated - if (activatedNames.length === 0) { - const randomIndex = Math.floor(Math.random() * members.length); - activatedNames.push(members[randomIndex]); - } - - // de-duplicate array of names - activatedNames = activatedNames.filter(onlyUnique); - - // map to character ids - const memberIds = activatedNames - .map((x) => characters.findIndex((y) => y.name === x)) - .filter((x) => x !== -1); - return memberIds; -} -function extractAllWords(value) { - const words = []; - - if (!value) { - return words; - } - - const matches = value.matchAll(/\b\w+\b/gim); - for (let match of matches) { - words.push(match[0].toLowerCase()); - } - return words; -} - -// Group chats -async function getGroupChat(id) { - const response = await fetch("/getgroupchat", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({ id: id }), - }); - - if (response.ok) { - const data = await response.json(); - if (Array.isArray(data) && data.length) { - for (let key of data) { - chat.push(key); - } - printMessages(); - } else { - sendSystemMessage(system_message_types.GROUP); - const group = groups.find((x) => x.id === id); - if (group && Array.isArray(group.members)) { - for (let name of group.members) { - const character = characters.find((x) => x.name === name); - - if (!character) { - continue; - } - - const mes = {}; - mes["is_user"] = false; - mes["is_system"] = false; - mes["name"] = character.name; - mes["is_name"] = true; - mes["send_date"] = humanizedDateTime(); - mes["mes"] = character.first_mes - ? substituteParams(character.first_mes.trim()) - : default_ch_mes; - mes["force_avatar"] = - character.avatar != "none" - ? `characters/${character.avatar}?${Date.now()}` - : default_avatar; - chat.push(mes); - addOneMessage(mes); - } - } - } - - await saveGroupChat(id); - } -} - -async function saveGroupChat(id) { - const response = await fetch("/savegroupchat", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({ id: id, chat: [...chat] }), - }); -} - -async function getGroups() { - const response = await fetch("/getgroups", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - }); - - if (response.ok) { - const data = await response.json(); - groups = data.sort((a, b) => a.id - b.id); - } -} - -async function deleteGroup(id) { - const response = await fetch("/deletegroup", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({ id: id }), - }); - - if (response.ok) { - active_character = "invalid-safety-id"; //unsets the chid in settings (this prevents AutoLoadChat from trying to load the wrong ChID - this_chid = "invalid-safety-id"; //unsets expected chid before reloading (related to getCharacters/printCharacters from using old arrays) - selected_group = null; - characters.length = 0; // resets the characters array, forcing getcharacters to reset - name2 = systemUserName; // replaces deleted charcter name with system user since it will be displayed next. - chat = [...safetychat]; // sets up system user to tell user about having deleted a character - - /* QuickRefresh(); */ - $("#rm_info_avatar").html(""); - $("#rm_info_block").transition({ opacity: 0, duration: 0 }); - select_rm_info("Group deleted!"); - $("#rm_info_block").transition({ opacity: 1.0, duration: 2000 }); - } -} - -async function editGroup(id, immediately) { - const group = groups.find((x) => x.id == id); - - if (!group) { - return; - } - - async function _save() { - const response = await fetch("/editgroup", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify(group), - }); - } - - if (immediately) { - return await _save(); - } - - clearTimeout(timerGroupSave); - timerGroupSave = setTimeout(async () => await _save(), durationSaveEdit); -} - -async function groupChatAutoModeWorker() { - if (!is_group_automode_enabled || online_status === "no_connection") { - return; - } - - if (!selected_group || is_send_press || is_group_generating) { - return; - } - - const group = groups.find((x) => x.id === selected_group); - - if (!group || !Array.isArray(group.members) || !group.members.length) { - return; - } - - await generateGroupWrapper(true); -} - -function select_group_chats(chat_id) { - menu_type = "group_chats"; - const group = chat_id && groups.find((x) => x.id == chat_id); - const groupName = group?.name ?? ""; - - $("#rm_group_chat_name").val(groupName); - $("#rm_group_chat_name").off(); - $("#rm_group_chat_name").on("input", async function () { - if (chat_id) { - group.name = $(this).val(); - await editGroup(chat_id); - } - }); - $("#rm_group_filter").val("").trigger("input"); - $("#rm_group_chats_block").css("display", "flex"); - $("#rm_group_chats_block").css("opacity", 0.0); - $("#rm_group_chats_block").transition({ - opacity: 1.0, - duration: animation_rm_duration, - easing: animation_rm_easing, - complete: function () { }, - }); - - $("#rm_ch_create_block").css("display", "none"); - $("#rm_characters_block").css("display", "none"); - - async function memberClickHandler(event) { - event.stopPropagation(); - const id = $(this).data("id"); - const isDelete = !!$(this).closest("#rm_group_members").length; - const template = $(this).clone(); - template.data("id", id); - template.click(memberClickHandler); - - if (isDelete) { - template.find(".plus").show(); - template.find(".minus").hide(); - $("#rm_group_add_members").prepend(template); - } else { - template.find(".plus").hide(); - template.find(".minus").show(); - $("#rm_group_members").prepend(template); - } - - if (group) { - if (isDelete) { - const index = group.members.findIndex((x) => x === id); - if (index !== -1) { - group.members.splice(index, 1); - } - } else { - group.members.push(id); - } - await editGroup(chat_id); - updateGroupAvatar(group); - } - - $(this).remove(); - const groupHasMembers = !!$("#rm_group_members").children().length; - $("#rm_group_submit").prop("disabled", !groupHasMembers); - } - - // render characters list - $("#rm_group_add_members").empty(); - $("#rm_group_members").empty(); - for (let character of characters) { - const avatar = - character.avatar != "none" - ? `characters/${character.avatar}#${Date.now()}` - : default_avatar; - const template = $("#group_member_template .group_member").clone(); - template.data("id", character.name); - template.find(".avatar img").attr("src", avatar); - template.find(".ch_name").html(character.name); - template.click(memberClickHandler); - - if ( - group && - Array.isArray(group.members) && - group.members.includes(character.name) - ) { - template.find(".plus").hide(); - template.find(".minus").show(); - $("#rm_group_members").append(template); - } else { - template.find(".plus").show(); - template.find(".minus").hide(); - $("#rm_group_add_members").append(template); - } - } - - const groupHasMembers = !!$("#rm_group_members").children().length; - $("#rm_group_submit").prop("disabled", !groupHasMembers); - - // bottom buttons - if (chat_id) { - $("#rm_group_submit").hide(); - $("#rm_group_delete").show(); - } else { - $("#rm_group_submit").show(); - $("#rm_group_delete").hide(); - } - - $("#rm_group_delete").off(); - $("#rm_group_delete").on("click", function () { - popup_type = "del_group"; - $("#dialogue_popup").data("group_id", chat_id); - callPopup("

Delete the group?

"); - }); - - // top bar - if (group) { - var display_name = groupName; - $("#rm_button_selected_ch").children("h2").css(deselected_button_style); - $("#rm_button_selected_ch").children("h2").text(""); - } -} function openNavToggle() { if (!$("#nav-toggle").prop("checked")) { $("#nav-toggle").trigger("click"); @@ -2543,10 +2059,6 @@ async function getSettings(type) { style_anchor = !!settings.style_anchor; if (settings.character_anchor !== undefined) character_anchor = !!settings.character_anchor; - if (settings.world_info_depth !== undefined) - world_info_depth = Number(settings.world_info_depth); - if (settings.world_info_budget !== undefined) - world_info_budget = Number(settings.world_info_budget); //load poweruser options if (settings.auto_connect !== undefined) @@ -2578,12 +2090,6 @@ async function getSettings(type) { $("#amount_gen").val(amount_gen); $("#amount_gen_counter").html(amount_gen + " Tokens"); - $("#world_info_depth_counter").html(`${world_info_depth} Messages`); - $("#world_info_depth").val(world_info_depth); - - $("#world_info_budget_counter").html(`${world_info_budget} Tokens`); - $("#world_info_budget").val(world_info_budget); - addZeros = ""; if (isInt(rep_pen)) addZeros = ".00"; $("#rep_pen").val(rep_pen); @@ -2658,23 +2164,7 @@ async function getSettings(type) { api_server = settings.api_server; $("#api_url_text").val(api_server); - // world info settings - world_names = data.world_names?.length ? data.world_names : []; - - if (settings.world_info != undefined) { - if (world_names.includes(settings.world_info)) { - world_info = settings.world_info; - } - } - - world_names.forEach((item, i) => { - $("#world_info").append(``); - // preselect world if saved - if (item == world_info) { - $("#world_info").val(i).change(); - } - }); - // end world info settings + setWorldInfoSettings(settings, data); if (data.enable_extensions) { const src = "scripts/extensions.js"; @@ -3080,7 +2570,7 @@ function select_selected_character(chid) { $("#form_create").attr("actiontype", "editcharacter"); active_character = chid; //console.log('select_selected_character() -- active_character -- '+chid+'(ChID of '+display_name+')'); - saveSettings(); + saveSettingsDebounced(); //console.log('select_selected_character() -- called saveSettings() to save -- active_character -- '+active_character+'(ChID of '+display_name+')'); } @@ -3171,421 +2661,106 @@ function select_rm_characters() { $("#rm_button_selected_ch").css("class", "deselected-right-tab"); } -/// UTILS -function onlyUnique(value, index, array) { - return array.indexOf(value) === index; -} - -function shuffle(array) { - let currentIndex = array.length, - randomIndex; - - while (currentIndex != 0) { - randomIndex = Math.floor(Math.random() * currentIndex); - currentIndex--; - [array[currentIndex], array[randomIndex]] = [ - array[randomIndex], - array[currentIndex], - ]; - } - return array; -} - function setExtensionPrompt(key, value) { extension_prompts[key] = value; } -async function updateWorldInfoList(importedWorldName) { - var result = await fetch("/getsettings", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({}), - }); - - if (result.ok) { - var data = await result.json(); - world_names = data.world_names?.length ? data.world_names : []; - $("#world_info").find('option[value!="None"]').remove(); - - world_names.forEach((item, i) => { - $("#world_info").append(``); - }); - - if (importedWorldName) { - const indexOf = world_names.indexOf(world_info); - $("#world_info").val(indexOf); - - popup_type = "world_imported"; - callPopup("

World imported successfully! Select it now?

"); - } +function callPopup(text, type) { + if (type) { + popup_type = type; } + + $("#dialogue_popup_cancel").css("display", "inline-block"); + switch (popup_type) { + case "text": + case "char_not_selected": + $("#dialogue_popup_ok").text("Ok"); + $("#dialogue_popup_cancel").css("display", "none"); + break; + + case "world_imported": + case "new_chat": + $("#dialogue_popup_ok").text("Yes"); + break; + case "del_world": + case "del_group": + default: + $("#dialogue_popup_ok").text("Delete"); + } + $("#dialogue_popup_text").html(text); + $("#shadow_popup").css("display", "block"); + $("#shadow_popup").transition({ + opacity: 1.0, + duration: animation_rm_duration, + easing: animation_rm_easing, + }); } -function download(content, fileName, contentType) { - var a = document.createElement("a"); - var file = new Blob([content], { type: contentType }); - a.href = URL.createObjectURL(file); - a.download = fileName; - a.click(); -} +function read_bg_load(input) { + if (input.files && input.files[0]) { + var reader = new FileReader(); -// World Info Editor -async function showWorldEditor() { - if (!world_info) { - popup_type = "default"; - callPopup("

Select a world info first!

"); - return; - } + reader.onload = function (e) { + $("#bg_load_preview") + .attr("src", e.target.result) + .width(103) + .height(83); - is_world_edit_open = true; - $("#world_popup_name").val(world_info); - $("#world_popup").css("display", "flex"); - await loadWorldInfoData(); - displayWorldEntries(world_info_data); -} + var formData = new FormData($("#form_bg_download").get(0)); -async function loadWorldInfoData() { - if (!world_info) { - return; - } + //console.log(formData); + jQuery.ajax({ + type: "POST", + url: "/downloadbackground", + data: formData, + beforeSend: function () { + //$('#create_button').attr('value','Creating...'); + }, + cache: false, + contentType: false, + processData: false, + success: function (html) { + setBackground(html); + if (bg1_toggle == true) { + // this is a repeat of the background setting function for when user uploads a new BG image + bg1_toggle = false; // should make the Bg setting a modular function to be called in both cases + var number_bg = 2; + var target_opacity = 1.0; + } else { + bg1_toggle = true; + var number_bg = 1; + var target_opacity = 0.0; + } + $("#bg2").transition({ + opacity: target_opacity, + duration: 1300, //animation_rm_duration, + easing: "linear", + complete: function () { + $("#options").css("display", "none"); + }, + }); + $("#bg" + number_bg).css( + "background-image", + "url(" + e.target.result + ")" + ); + $("#form_bg_download").after( + "
" + ); + }, + error: function (jqXHR, exception) { + console.log(exception); + console.log(jqXHR); + }, + }); + }; - const response = await fetch("/getworldinfo", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({ name: world_info }), - }); - - if (response.ok) { - world_info_data = await response.json(); - } -} - -function hideWorldEditor() { - is_world_edit_open = false; - $("#world_popup").css("display", "none"); -} - -function displayWorldEntries(data) { - $("#world_popup_entries_list").empty(); - - if (!data || !("entries" in data)) { - return; - } - - for (const entryUid in data.entries) { - const entry = data.entries[entryUid]; - appendWorldEntry(entry); - } -} - -function appendWorldEntry(entry) { - const template = $("#entry_edit_template .world_entry").clone(); - template.data("uid", entry.uid); - - // key - const keyInput = template.find('textarea[name="key"]'); - keyInput.data("uid", entry.uid); - keyInput.on("input", function () { - const uid = $(this).data("uid"); - const value = $(this).val(); - $(this).css("height", ""); //reset the height - $(this).css("height", $(this).prop("scrollHeight") + "px"); - world_info_data.entries[uid].key = value - .split(",") - .map((x) => x.trim()) - .filter((x) => x); - saveWorldInfo(); - }); - keyInput.val(entry.key.join(",")).trigger("input"); - keyInput.css("height", ""); //reset the height - keyInput.css("height", $(this).prop("scrollHeight") + "px"); - - // keysecondary - const keySecondaryInput = template.find('textarea[name="keysecondary"]'); - keySecondaryInput.data("uid", entry.uid); - keySecondaryInput.on("input", function () { - const uid = $(this).data("uid"); - const value = $(this).val(); - $(this).css("height", ""); //reset the height - $(this).css("height", $(this).prop("scrollHeight") + "px"); - world_info_data.entries[uid].keysecondary = value - .split(",") - .map((x) => x.trim()) - .filter((x) => x); - saveWorldInfo(); - }); - keySecondaryInput.val(entry.keysecondary.join(",")).trigger("input"); - keySecondaryInput.css("height", ""); //reset the height - keySecondaryInput.css("height", $(this).prop("scrollHeight") + "px"); - - // comment - const commentInput = template.find('textarea[name="comment"]'); - commentInput.data("uid", entry.uid); - commentInput.on("input", function () { - const uid = $(this).data("uid"); - const value = $(this).val(); - $(this).css("height", ""); //reset the height - $(this).css("height", $(this).prop("scrollHeight") + "px"); - world_info_data.entries[uid].comment = value; - saveWorldInfo(); - }); - commentInput.val(entry.comment).trigger("input"); - commentInput.css("height", ""); //reset the height - commentInput.css("height", $(this).prop("scrollHeight") + "px"); - - // content - const contentInput = template.find('textarea[name="content"]'); - contentInput.data("uid", entry.uid); - contentInput.on("input", function () { - const uid = $(this).data("uid"); - const value = $(this).val(); - world_info_data.entries[uid].content = value; - $(this).css("height", ""); //reset the height - $(this).css("height", $(this).prop("scrollHeight") + "px"); - saveWorldInfo(); - - // count tokens - const numberOfTokens = encode(value).length; - $(this) - .closest(".world_entry") - .find(".world_entry_form_token_counter") - .html(numberOfTokens); - }); - contentInput.val(entry.content).trigger("input"); - contentInput.css("height", ""); //reset the height - contentInput.css("height", $(this).prop("scrollHeight") + "px"); - - // selective - const selectiveInput = template.find('input[name="selective"]'); - selectiveInput.data("uid", entry.uid); - selectiveInput.on("input", function () { - const uid = $(this).data("uid"); - const value = $(this).prop("checked"); - world_info_data.entries[uid].selective = value; - saveWorldInfo(); - - const keysecondary = $(this) - .closest(".world_entry") - .find(".keysecondary"); - value ? keysecondary.show() : keysecondary.hide(); - }); - selectiveInput.prop("checked", entry.selective).trigger("input"); - selectiveInput.siblings(".checkbox_fancy").click(function () { - $(this).siblings("input").click(); - }); - - // constant - const constantInput = template.find('input[name="constant"]'); - constantInput.data("uid", entry.uid); - constantInput.on("input", function () { - const uid = $(this).data("uid"); - const value = $(this).prop("checked"); - world_info_data.entries[uid].constant = value; - saveWorldInfo(); - }); - constantInput.prop("checked", entry.constant).trigger("input"); - constantInput.siblings(".checkbox_fancy").click(function () { - $(this).siblings("input").click(); - }); - - // order - const orderInput = template.find('input[name="order"]'); - orderInput.data("uid", entry.uid); - orderInput.on("input", function () { - const uid = $(this).data("uid"); - const value = Number($(this).val()); - - world_info_data.entries[uid].order = !isNaN(value) ? value : 0; - saveWorldInfo(); - }); - orderInput.val(entry.order).trigger("input"); - - // position - if (entry.position === undefined) { - entry.position = 0; - } - - const positionInput = template.find('input[name="position"]'); - positionInput.data("uid", entry.uid); - positionInput.on("input", function () { - const uid = $(this).data("uid"); - const value = Number($(this).val()); - world_info_data.entries[uid].position = !isNaN(value) ? value : 0; - saveWorldInfo(); - }); - template - .find(`input[name="position"][value=${entry.position}]`) - .prop("checked", true) - .trigger("input"); - - // display uid - template.find(".world_entry_form_uid_value").html(entry.uid); - - // delete button - const deleteButton = template.find("input.delete_entry_button"); - deleteButton.data("uid", entry.uid); - deleteButton.on("click", function () { - const uid = $(this).data("uid"); - deleteWorldInfoEntry(uid); - $(this).closest(".world_entry").remove(); - saveWorldInfo(); - }); - - template.appendTo("#world_popup_entries_list"); - return template; -} - -async function deleteWorldInfoEntry(uid) { - if (!world_info_data || !("entries" in world_info_data)) { - return; - } - - delete world_info_data.entries[uid]; -} - -function createWorldInfoEntry() { - const newEntryTemplate = { - key: [], - keysecondary: [], - comment: "", - content: "", - constant: false, - selective: false, - order: 100, - position: 0, - }; - const newUid = getFreeWorldEntryUid(); - - if (!Number.isInteger(newUid)) { - console.error("Couldn't assign UID to a new entry"); - return; - } - - const newEntry = { uid: newUid, ...newEntryTemplate }; - world_info_data.entries[newUid] = newEntry; - - const entryTemplate = appendWorldEntry(newEntry); - entryTemplate.get(0).scrollIntoView({ behavior: "smooth" }); -} - -async function saveWorldInfo(immediately) { - if (!world_info || !world_info_data) { - return; - } - - async function _save() { - const response = await fetch("/editworldinfo", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({ name: world_info, data: world_info_data }), - }); - } - - if (immediately) { - return await _save(); - } - - clearTimeout(timerWorldSave); - timerWorldSave = setTimeout(async () => await _save(), durationSaveEdit); -} - -async function renameWorldInfo() { - const oldName = world_info; - const newName = $("#world_popup_name").val(); - - if (oldName === newName) { - return; - } - - world_info = newName; - await saveWorldInfo(true); - await deleteWorldInfo(oldName, newName); -} - -async function deleteWorldInfo(worldInfoName, selectWorldName) { - if (!world_names.includes(worldInfoName)) { - return; - } - - const response = await fetch("/deleteworldinfo", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({ name: worldInfoName }), - }); - - if (response.ok) { - await updateWorldInfoList(); - - const selectedIndex = world_names.indexOf(selectWorldName); - if (selectedIndex !== -1) { - $("#world_info").val(selectedIndex).change(); - } else { - $("#world_info").val("None").change(); - } - - hideWorldEditor(); - } -} - -function getFreeWorldEntryUid() { - if (!world_info_data || !("entries" in world_info_data)) { - return null; - } - - const MAX_UID = 1_000_000; // <- should be safe enough :) - for (let uid = 0; uid < MAX_UID; uid++) { - if (uid in world_info_data.entries) { - continue; - } - return uid; - } - - return null; -} - -function getFreeWorldName() { - const MAX_FREE_NAME = 100_000; - for (let index = 1; index < MAX_FREE_NAME; index++) { - const newName = `New World (${index})`; - if (world_names.includes(newName)) { - continue; - } - return newName; - } - - return undefined; -} - -async function createNewWorldInfo() { - const worldInfoTemplate = { entries: {} }; - const worldInfoName = getFreeWorldName(); - - if (!worldInfoName) { - return; - } - - world_info = worldInfoName; - world_info_data = { ...worldInfoTemplate }; - await saveWorldInfo(true); - await updateWorldInfoList(); - - const selectedIndex = world_names.indexOf(worldInfoName); - if (selectedIndex !== -1) { - $("#world_info").val(selectedIndex).change(); - } else { - $("#world_info").val("None").change(); + reader.readAsDataURL(input.files[0]); } } @@ -3718,96 +2893,12 @@ $(document).ready(function () { selected_button = "character_edit"; select_selected_character(this_chid); }); - $(document).on("click", ".group_select", async function () { - const id = $(this).data("id"); - selected_button = "group_chats"; - - if (!is_send_press && !is_group_generating) { - if (selected_group !== id) { - selected_group = id; - this_chid = undefined; - this_edit_mes_id = undefined; - clearChat(); - chat.length = 0; - await getGroupChat(id); - } - - select_group_chats(id); - } - }); - $("#rm_button_group_chats").click(function () { - selected_button = "group_chats"; - select_group_chats(); - }); - $("#rm_button_back_from_group").click(function () { - selected_button = "characters"; - select_rm_characters(); - }); - $("#rm_group_filter").on("input", function () { - const searchValue = $(this).val().trim().toLowerCase(); - - if (!searchValue) { - $("#rm_group_add_members .group_member").show(); - } else { - $("#rm_group_add_members .group_member").each(function () { - $(this).children(".ch_name").text().toLowerCase().includes(searchValue) - ? $(this).show() - : $(this).hide(); - }); - } - }); - $("#rm_group_submit").click(async function () { - let name = $("#rm_group_chat_name").val(); - const members = $("#rm_group_members .group_member") - .map((_, x) => $(x).data("id")) - .toArray(); - - if (!name) { - name = `Chat with ${members.join(", ")}`; - } - - // placeholder - const avatar_url = "/img/five.png"; - - const createGroupResponse = await fetch("/creategroup", { - method: "POST", - headers: { - "Content-Type": "application/json", - "X-CSRF-Token": token, - }, - body: JSON.stringify({ - name: name, - members: members, - avatar_url: avatar_url, - }), - }); - - if (createGroupResponse.ok) { - const createGroupData = await createGroupResponse.json(); - const id = createGroupData.id; - - await getCharacters(); - $("#rm_info_avatar").html(""); - var avatar = $("#avatar_div_div").clone(); - avatar.find("img").attr("src", avatar_url); - $("#rm_info_avatar").append(avatar); - $("#rm_info_block").transition({ opacity: 0, duration: 0 }); - select_rm_info("Group chat created"); - $("#rm_info_block").transition({ opacity: 1.0, duration: 2000 }); - } - }); - - $("#rm_group_automode").on("input", function () { - const value = $(this).prop("checked"); - is_group_automode_enabled = value; - }); $(document).on("click", ".character_select", function () { if (this_chid !== $(this).attr("chid")) { //if clicked on a different character from what was currently selected if (!is_send_press) { - selected_group = null; - is_group_generating = false; + resetSelectedGroup(); this_edit_mes_id = undefined; selected_button = "character_edit"; this_chid = $(this).attr("chid"); @@ -3867,7 +2958,7 @@ $(document).ready(function () { .attr("src", "User Avatars/" + user_avatar); } }); - saveSettings(); + saveSettingsDebounced(); highlightSelectedAvatar(); }); $(document).on("click", "#user_avatar_block .avatar_upload", function () { @@ -4041,19 +3132,14 @@ $(document).ready(function () { getCharacters(); // gets the new list of characters (that doesn't include the deleted one) printMessages(); // prints out system user's 'deleted character' message //console.log("#dialogue_popup_ok(del-char) >>>> saving"); - saveSettings(); // saving settings to keep changes to variables + saveSettingsDebounced(); // saving settings to keep changes to variables //getCharacters(); //$('#create_button_div').html(html); }, }); } - if (popup_type === "world_imported" && imported_world_name) { - world_names.forEach((item, i) => { - if (item === imported_world_name) { - $("#world_info").val(i).change(); - } - }); - imported_world_name = ""; + if (popup_type === "world_imported") { + selectImportedWorldInfo(); } if (popup_type === "del_world" && world_info) { deleteWorldInfo(world_info); @@ -4076,7 +3162,7 @@ $(document).ready(function () { chat.length = 0; characters[this_chid].chat = name2 + " - " + humanizedDateTime(); //RossAscends: added character name to new chat filenames and replaced Date.now() with humanizedDateTime; $("#selected_chat_pole").val(characters[this_chid].chat); - timerSaveEdit = setTimeout(() => { + setTimeout(() => { $("#create_button").click(); }, durationSaveEdit); getChat(); @@ -4087,127 +3173,16 @@ $(document).ready(function () { $("#shadow_popup").css("opacity:", 0.0); popup_type = ""; }); - function callPopup(text) { - $("#dialogue_popup_cancel").css("display", "inline-block"); - switch (popup_type) { - case "text": - case "char_not_selected": - $("#dialogue_popup_ok").text("Ok"); - $("#dialogue_popup_cancel").css("display", "none"); - break; - case "world_imported": - case "new_chat": - $("#dialogue_popup_ok").text("Yes"); - break; - case "del_world": - case "del_group": - default: - $("#dialogue_popup_ok").text("Delete"); - } - $("#dialogue_popup_text").html(text); - $("#shadow_popup").css("display", "block"); - $("#shadow_popup").transition({ - opacity: 1.0, - duration: animation_rm_duration, - easing: animation_rm_easing, - }); - } - function read_bg_load(input) { - if (input.files && input.files[0]) { - var reader = new FileReader(); - - reader.onload = function (e) { - $("#bg_load_preview") - .attr("src", e.target.result) - .width(103) - .height(83); - - var formData = new FormData($("#form_bg_download").get(0)); - - //console.log(formData); - jQuery.ajax({ - type: "POST", - url: "/downloadbackground", - data: formData, - beforeSend: function () { - //$('#create_button').attr('value','Creating...'); - }, - cache: false, - contentType: false, - processData: false, - success: function (html) { - setBackground(html); - if (bg1_toggle == true) { - // this is a repeat of the background setting function for when user uploads a new BG image - bg1_toggle = false; // should make the Bg setting a modular function to be called in both cases - var number_bg = 2; - var target_opacity = 1.0; - } else { - bg1_toggle = true; - var number_bg = 1; - var target_opacity = 0.0; - } - $("#bg2").transition({ - opacity: target_opacity, - duration: 1300, //animation_rm_duration, - easing: "linear", - complete: function () { - $("#options").css("display", "none"); - }, - }); - $("#bg" + number_bg).css( - "background-image", - "url(" + e.target.result + ")" - ); - $("#form_bg_download").after( - "
" - ); - }, - error: function (jqXHR, exception) { - console.log(exception); - console.log(jqXHR); - }, - }); - }; - - reader.readAsDataURL(input.files[0]); - } - } $("#add_bg_button").change(function () { read_bg_load(this); }); - function read_avatar_load(input) { - if (input.files && input.files[0]) { - var reader = new FileReader(); - if (selected_button == "create") { - create_save_avatar = input.files; - } - reader.onload = function (e) { - if (selected_button == "character_edit") { - timerSaveEdit = setTimeout(() => { - $("#create_button").click(); - }, durationSaveEdit); - } - $("#avatar_load_preview").attr("src", e.target.result); - //.width(103) - //.height(83); - //console.log(e.target.result.name); - }; - reader.readAsDataURL(input.files[0]); - } - } $("#add_avatar_button").change(function () { is_mes_reload_avatar = Date.now(); read_avatar_load(this); }); + $("#form_create").submit(function (e) { $("#rm_info_avatar").html(""); var formData = new FormData($("#form_create").get(0)); @@ -4348,16 +3323,19 @@ $(document).ready(function () { }); } }); + $("#delete_button").click(function () { popup_type = "del_ch"; callPopup( "

Delete the character?

Your chat will be closed." ); }); + $("#rm_info_button").click(function () { $("#rm_info_avatar").html(""); select_rm_characters(); }); + //@@@@@@@@@@@@@@@@@@@@@@@@ //character text poles creating and editing save $("#character_name_pole").on("change keyup paste", function () { @@ -4365,6 +3343,7 @@ $(document).ready(function () { create_save_name = $("#character_name_pole").val(); } }); + $("#description_textarea").on("keyup paste cut", function () { //change keyup paste cut @@ -4372,60 +3351,66 @@ $(document).ready(function () { create_save_description = $("#description_textarea").val(); CountCharTokens(); } else { - timerSaveEdit = setTimeout(() => { + setTimeout(() => { $("#create_button").click(); }, durationSaveEdit); } }); + $("#personality_textarea").on("keyup paste cut", function () { if (menu_type == "create") { create_save_personality = $("#personality_textarea").val(); CountCharTokens(); } else { - timerSaveEdit = setTimeout(() => { + setTimeout(() => { $("#create_button").click(); }, durationSaveEdit); } }); + $("#scenario_pole").on("keyup paste cut", function () { if (menu_type == "create") { create_save_scenario = $("#scenario_pole").val(); CountCharTokens(); } else { - timerSaveEdit = setTimeout(() => { + setTimeout(() => { $("#create_button").click(); }, durationSaveEdit); } }); + $("#mes_example_textarea").on("keyup paste cut", function () { if (menu_type == "create") { create_save_mes_example = $("#mes_example_textarea").val(); CountCharTokens(); } else { - timerSaveEdit = setTimeout(() => { + setTimeout(() => { $("#create_button").click(); }, durationSaveEdit); } }); + $("#firstmessage_textarea").on("keyup paste cut", function () { if (menu_type == "create") { create_save_first_message = $("#firstmessage_textarea").val(); CountCharTokens(); } else { - timerSaveEdit = setTimeout(() => { + setTimeout(() => { $("#create_button").click(); }, durationSaveEdit); } }); + $("#talkativeness_slider").on("input", function () { if (menu_type == "create") { create_save_talkativeness = $("#talkativeness_slider").val(); } else { - timerSaveEdit = setTimeout(() => { + setTimeout(() => { $("#create_button").click(); }, durationSaveEdit); } }); + $("#api_button").click(function () { if ($("#api_url_text").val() != "") { $("#api_loading").css("display", "inline-block"); @@ -4446,7 +3431,7 @@ $(document).ready(function () { } //console.log("2: "+api_server); main_api = "kobold"; - saveSettings(); + saveSettingsDebounced(); is_get_status = true; is_api_button_press = true; getStatus(); @@ -4476,7 +3461,7 @@ $(document).ready(function () { } //console.log("2: "+api_server_textgenerationwebui); main_api = "textgenerationwebui"; - saveSettings(); + saveSettingsDebounced(); is_get_status = true; is_api_button_press = true; getStatus(); @@ -4530,6 +3515,7 @@ $(document).ready(function () { }); } }); + $("#option_start_new_chat").click(function () { if (selected_group) { // will open a group creation screen @@ -4542,12 +3528,14 @@ $(document).ready(function () { callPopup("

Start new chat?

"); } }); + $("#option_regenerate").click(function () { if (is_send_press == false) { is_send_press = true; Generate("regenerate"); } }); + // this function hides the input form, and shows the delete/cancel buttons for deleting messages from chat $("#option_delete_mes").click(function () { if ( @@ -4564,6 +3552,7 @@ $(document).ready(function () { }); } }); + //functionality for the cancel delete messages button, reverts to normal display of input form $("#dialogue_del_mes_cancel").click(function () { $("#dialogue_del_mes").css("display", "none"); @@ -4576,6 +3565,7 @@ $(document).ready(function () { }); this_del_mes = 0; }); + //confirms message delation with the "ok" button $("#dialogue_del_mes_ok").click(function () { $("#dialogue_del_mes").css("display", "none"); @@ -4604,21 +3594,6 @@ $(document).ready(function () { this_del_mes = 0; }); - $("#world_info").change(async function () { - const selectedWorld = $("#world_info").find(":selected").val(); - world_info = null; - world_info_data = null; - - if (selectedWorld !== "None") { - const worldIndex = Number(selectedWorld); - world_info = !isNaN(worldIndex) ? world_names[worldIndex] : null; - await loadWorldInfoData(); - } - - hideWorldEditor(); - saveSettings(); - }); - $("#settings_perset").change(function () { if ($("#settings_perset").find(":selected").val() != "gui") { preset_settings = $("#settings_perset").find(":selected").text(); @@ -4659,8 +3634,9 @@ $(document).ready(function () { $("#amount_gen_block").children().prop("disabled", true); $("#amount_gen_block").css("opacity", 0.45); } - saveSettings(); + saveSettingsDebounced(); }); + $("#settings_perset_novel").change(function () { preset_settings_novel = $("#settings_perset_novel") .find(":selected") @@ -4689,8 +3665,9 @@ $(document).ready(function () { //$("#range_block").children().prop("disabled", false); //$("#range_block").css('opacity',1.0); - saveSettings(); + saveSettingsDebounced(); }); + $("#main_api").change(function () { is_pygmalion = false; is_get_status = false; @@ -4699,8 +3676,9 @@ $(document).ready(function () { clearSoftPromptsList(); checkOnlineStatus(); changeMainAPI(); - saveSettings(); + saveSettingsDebounced(); }); + $("#softprompt").change(async function () { if (!api_server) { return; @@ -4740,7 +3718,7 @@ $(document).ready(function () { $("#" + i + "_counter_textgenerationwebui").html($(this).val()); } textgenerationwebui_settings[i] = parseFloat(val); - setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); } @@ -4751,56 +3729,44 @@ $(document).ready(function () { } else { $("#temp_counter").html($(this).val()); } - var tempTimer = setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); $(document).on("input", "#amount_gen", function () { amount_gen = $(this).val(); $("#amount_gen_counter").html($(this).val()); - var amountTimer = setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); $(document).on("input", "#max_context", function () { max_context = parseInt($(this).val()); $("#max_context_counter").html($(this).val() + " Tokens"); - var max_contextTimer = setTimeout(saveSettings, 500); - }); - - $(document).on("input", "#world_info_depth", function () { - world_info_depth = Number($(this).val()); - $("#world_info_depth_counter").html(`${$(this).val()} Messages`); - setTimeout(saveSettings, 500); - }); - - $(document).on("input", "#world_info_budget", function () { - world_info_budget = Number($(this).val()); - $("#world_info_budget_counter").html(`${$(this).val()} Tokens`); - setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); $("#style_anchor").change(function () { style_anchor = !!$("#style_anchor").prop("checked"); - saveSettings(); + saveSettingsDebounced(); }); $("#character_anchor").change(function () { character_anchor = !!$("#character_anchor").prop("checked"); - saveSettings(); + saveSettingsDebounced(); }); $("#auto-connect-checkbox").change(function () { auto_connect = !!$("#auto-connect-checkbox").prop("checked"); - saveSettings(); + saveSettingsDebounced(); }); $("#auto-load-chat-checkbox").change(function () { auto_load_chat = !!$("#auto-load-chat-checkbox").prop("checked"); - saveSettings(); + saveSettingsDebounced(); }); $("#collapse-newlines-checkbox").change(function () { collapse_newlines = !!$("#collapse-newlines-checkbox").prop("checked"); - saveSettings(); + saveSettingsDebounced(); }); $(document).on("input", "#rep_pen", function () { @@ -4810,13 +3776,13 @@ $(document).ready(function () { } else { $("#rep_pen_counter").html($(this).val()); } - var repPenTimer = setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); $(document).on("input", "#rep_pen_size", function () { rep_pen_size = $(this).val(); $("#rep_pen_size_counter").html($(this).val() + " Tokens"); - var repPenSizeTimer = setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); //Novel @@ -4828,7 +3794,7 @@ $(document).ready(function () { } else { $("#temp_counter_novel").html($(this).val()); } - var tempTimer_novel = setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); $(document).on("input", "#rep_pen_novel", function () { @@ -4838,13 +3804,13 @@ $(document).ready(function () { } else { $("#rep_pen_counter_novel").html($(this).val()); } - var repPenTimer_novel = setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); $(document).on("input", "#rep_pen_size_novel", function () { rep_pen_size_novel = $(this).val(); $("#rep_pen_size_counter_novel").html($(this).val() + " Tokens"); - var repPenSizeTimer_novel = setTimeout(saveSettings, 500); + saveSettingsDebounced(); }); $("#donation").click(function () { @@ -5005,17 +3971,11 @@ $(document).ready(function () { api_key_novel = $("#api_key_novel").val(); api_key_novel = $.trim(api_key_novel); //console.log("1: "+api_server); - saveSettings(); + saveSettingsDebounced(); is_get_status_novel = true; is_api_button_press_novel = true; } }); - function resultCheckStatusNovel() { - is_api_button_press_novel = false; - checkOnlineStatus(); - $("#api_loading_novel").css("display", "none"); - $("#api_button_novel").css("display", "inline-block"); - } $("#model_novel_select").change(function () { model_novel = $("#model_novel_select").find(":selected").val(); saveSettings(); @@ -5095,6 +4055,7 @@ $(document).ready(function () { $("#chat_import_button").click(function () { $("#chat_import_file").click(); }); + $("#chat_import_file").on("change", function (e) { var file = e.target.files[0]; //console.log(1); @@ -5138,6 +4099,16 @@ $(document).ready(function () { }); }); + $("#rm_button_group_chats").click(function () { + selected_button = "group_chats"; + select_group_chats(); + }); + + $("#rm_button_back_from_group").click(function () { + selected_button = "characters"; + select_rm_characters(); + }); + $(document).on("click", ".select_chat_block", function () { let file_name = $(this).attr("file_name").replace(".jsonl", ""); //console.log(characters[this_chid]['chat']); @@ -5150,77 +4121,4 @@ $(document).ready(function () { $("#shadow_select_chat_popup").css("display", "none"); $("#load_select_chat_div").css("display", "block"); }); - - //**************************WORLD INFO IMPORT EXPORT*************************// - $("#world_import_button").click(function () { - $("#world_import_file").click(); - }); - - $("#world_import_file").on("change", function (e) { - var file = e.target.files[0]; - - if (!file) { - return; - } - - const ext = file.name.match(/\.(\w+)$/); - if (!ext || ext[1].toLowerCase() !== "json") { - return; - } - - var formData = new FormData($("#form_world_import").get(0)); - - jQuery.ajax({ - type: "POST", - url: "/importworldinfo", - data: formData, - beforeSend: () => { }, - cache: false, - contentType: false, - processData: false, - success: function (data) { - if (data.name) { - imported_world_name = data.name; - updateWorldInfoList(imported_world_name); - } - }, - error: (jqXHR, exception) => { }, - }); - - // Will allow to select the same file twice in a row - $("#form_world_import").trigger("reset"); - }); - - $("#world_info_edit_button").click(() => { - is_world_edit_open ? hideWorldEditor() : showWorldEditor(); - }); - - $("#world_popup_export").click(() => { - if (world_info && world_info_data) { - const jsonValue = JSON.stringify(world_info_data); - const fileName = `${world_info}.json`; - download(jsonValue, fileName, "application/json"); - } - }); - - $("#world_popup_delete").click(() => { - popup_type = "del_world"; - callPopup("

Delete the World Info?

"); - }); - - $("#world_popup_new").click(() => { - createWorldInfoEntry(); - }); - - $("#world_cross").click(() => { - hideWorldEditor(); - }); - - $("#world_popup_name_button").click(() => { - renameWorldInfo(); - }); - - $("#world_create_button").click(() => { - createNewWorldInfo(); - }); }); diff --git a/public/scripts/groupchats.js b/public/scripts/groupchats.js new file mode 100644 index 000000000..c6eef3a2d --- /dev/null +++ b/public/scripts/groupchats.js @@ -0,0 +1,622 @@ +import { + shuffle, + onlyUnique, + debounce, + delay, +} from './utils.js'; +import { humanizedDateTime } from "./RossAscends-mods.js"; + +import { + chat, + sendSystemMessage, + printMessages, + substituteParams, + characters, + default_avatar, + token, + addOneMessage, + callPopup, + clearChat, + Generate, + select_rm_info, + setCharacterId, + setCharacterName, + setEditedMessageId, + is_send_press, + resetChatState, + setSendButtonState, + getCharacters, + system_message_types, + online_status, + talkativeness_default, +} from "../script.js"; + +export { + selected_group, + is_group_automode_enabled, + is_group_generating, + groups, + saveGroupChat, + generateGroupWrapper, + deleteGroup, + getGroupAvatar, + getGroups, + printGroups, + resetSelectedGroup, + select_group_chats, +} + +let is_group_generating = false; // Group generation flag +let is_group_automode_enabled = false; +let groups = []; +let selected_group = null; + +const groupAutoModeInterval = setInterval(groupChatAutoModeWorker, 5000); +const saveGroupDebounced = debounce(async (group) => await _save(group), 500); + +async function _save(group) { + await fetch("/editgroup", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify(group), + }); +} + + +// Group chats +async function getGroupChat(id) { + const response = await fetch("/getgroupchat", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({ id: id }), + }); + + if (response.ok) { + const data = await response.json(); + if (Array.isArray(data) && data.length) { + for (let key of data) { + chat.push(key); + } + printMessages(); + } else { + sendSystemMessage(system_message_types.GROUP); + const group = groups.find((x) => x.id === id); + if (group && Array.isArray(group.members)) { + for (let name of group.members) { + const character = characters.find((x) => x.name === name); + + if (!character) { + continue; + } + + const mes = {}; + mes["is_user"] = false; + mes["is_system"] = false; + mes["name"] = character.name; + mes["is_name"] = true; + mes["send_date"] = humanizedDateTime(); + mes["mes"] = character.first_mes + ? substituteParams(character.first_mes.trim()) + : default_ch_mes; + mes["force_avatar"] = + character.avatar != "none" + ? `characters/${character.avatar}?${Date.now()}` + : default_avatar; + chat.push(mes); + addOneMessage(mes); + } + } + } + + await saveGroupChat(id); + } +} + +function resetSelectedGroup() { + selected_group = null; + is_group_generating = false; +} + +async function saveGroupChat(id) { + const response = await fetch("/savegroupchat", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({ id: id, chat: [...chat] }), + }); + + if (response.ok) { + // response ok + } +} + +async function getGroups() { + const response = await fetch("/getgroups", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + }); + + if (response.ok) { + const data = await response.json(); + groups = data.sort((a, b) => a.id - b.id); + } +} + + +function printGroups() { + for (let group of groups) { + const template = $("#group_list_template .group_select").clone(); + template.data("id", group.id); + template.find(".ch_name").html(group.name); + $("#rm_print_characters_block").prepend(template); + updateGroupAvatar(group); + } +} + +function updateGroupAvatar(group) { + $("#rm_print_characters_block .group_select").each(function () { + if ($(this).data("id") == group.id) { + const avatar = getGroupAvatar(group); + if (avatar) { + $(this).find(".avatar").replaceWith(avatar); + } + } + }); +} + +function getGroupAvatar(group) { + const memberAvatars = []; + if (group && Array.isArray(group.members) && group.members.length) { + for (const member of group.members) { + const charIndex = characters.findIndex((x) => x.name === member); + if (charIndex !== -1 && characters[charIndex].avatar !== "none") { + const avatar = `characters/${characters[charIndex].avatar}#${Date.now()}`; + memberAvatars.push(avatar); + } + if (memberAvatars.length === 4) { + break; + } + } + } + + // Cohee: there's probably a smarter way to do this.. + if (memberAvatars.length === 1) { + const groupAvatar = $("#group_avatars_template .collage_1").clone(); + groupAvatar.find(".img_1").attr("src", memberAvatars[0]); + return groupAvatar; + } + + if (memberAvatars.length === 2) { + const groupAvatar = $("#group_avatars_template .collage_2").clone(); + groupAvatar.find(".img_1").attr("src", memberAvatars[0]); + groupAvatar.find(".img_2").attr("src", memberAvatars[1]); + return groupAvatar; + } + + if (memberAvatars.length === 3) { + const groupAvatar = $("#group_avatars_template .collage_3").clone(); + groupAvatar.find(".img_1").attr("src", memberAvatars[0]); + groupAvatar.find(".img_2").attr("src", memberAvatars[1]); + groupAvatar.find(".img_3").attr("src", memberAvatars[2]); + return groupAvatar; + } + + if (memberAvatars.length === 4) { + const groupAvatar = $("#group_avatars_template .collage_4").clone(); + groupAvatar.find(".img_1").attr("src", memberAvatars[0]); + groupAvatar.find(".img_2").attr("src", memberAvatars[1]); + groupAvatar.find(".img_3").attr("src", memberAvatars[2]); + groupAvatar.find(".img_4").attr("src", memberAvatars[3]); + return groupAvatar; + } + + // default avatar + const groupAvatar = $("#group_avatars_template .collage_1").clone(); + groupAvatar.find(".img_1").attr("src", group.avatar_url); + return groupAvatar; +} + + +async function generateGroupWrapper(by_auto_mode) { + if (online_status === "no_connection") { + is_group_generating = false; + setSendButtonState(false); + return; + } + + const group = groups.find((x) => x.id === selected_group); + + if (!group || !Array.isArray(group.members) || !group.members.length) { + sendSystemMessage(system_message_types.EMPTY); + return; + } + + try { + is_group_generating = true; + setCharacterName(''); + setCharacterId(undefined); + const userInput = $("#send_textarea").val(); + + let typingIndicator = $("#chat .typing_indicator"); + + if (typingIndicator.length === 0) { + typingIndicator = $( + "#typing_indicator_template .typing_indicator" + ).clone(); + typingIndicator.hide(); + $("#chat").append(typingIndicator); + } + + let messagesBefore = chat.length; + let activationText = ""; + if (userInput && userInput.length && !by_auto_mode) { + activationText = userInput; + messagesBefore++; + } else { + const lastMessage = chat[chat.length - 1]; + if (lastMessage && !lastMessage.is_system) { + activationText = lastMessage.mes; + } + } + + const activatedMembers = activateMembers(group.members, activationText); + // now the real generation begins: cycle through every character + for (const chId of activatedMembers) { + setCharacterId(chId); + setCharacterName(characters[chId].name) + + await Generate("group_chat", by_auto_mode); + + // update indicator and scroll down + typingIndicator + .find(".typing_indicator_name") + .text(characters[chId].name); + $("#chat").append(typingIndicator); + typingIndicator.show(250, function () { + typingIndicator.get(0).scrollIntoView({ behavior: "smooth" }); + }); + + while (true) { + // check if message generated already + if (chat.length == messagesBefore) { + await delay(10); + } else { + messagesBefore++; + break; + } + } + + // hide and reapply the indicator to the bottom of the list + typingIndicator.hide(250); + $("#chat").append(typingIndicator); + } + } finally { + is_group_generating = false; + setSendButtonState(false); + setCharacterId(undefined); + } +} + +function activateMembers(members, input) { + let activatedNames = []; + + // find mentions + if (input && input.length) { + for (let inputWord of extractAllWords(input)) { + for (let member of members) { + if (extractAllWords(member).includes(inputWord)) { + activatedNames.push(member); + break; + } + } + } + } + + // activation by talkativeness (in shuffled order) + const shuffledMembers = shuffle([...members]); + for (let member of shuffledMembers) { + const character = characters.find((x) => x.name === member); + + if (!character) { + continue; + } + + const rollValue = Math.random(); + let talkativeness = Number(character.talkativeness); + talkativeness = Number.isNaN(talkativeness) + ? talkativeness_default + : talkativeness; + if (talkativeness >= rollValue) { + activatedNames.push(member); + } + } + + // pick 1 at random if no one was activated + if (activatedNames.length === 0) { + const randomIndex = Math.floor(Math.random() * members.length); + activatedNames.push(members[randomIndex]); + } + + // de-duplicate array of names + activatedNames = activatedNames.filter(onlyUnique); + + // map to character ids + const memberIds = activatedNames + .map((x) => characters.findIndex((y) => y.name === x)) + .filter((x) => x !== -1); + return memberIds; +} + +function extractAllWords(value) { + const words = []; + + if (!value) { + return words; + } + + const matches = value.matchAll(/\b\w+\b/gim); + for (let match of matches) { + words.push(match[0].toLowerCase()); + } + return words; +} + + +async function deleteGroup(id) { + const response = await fetch("/deletegroup", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({ id: id }), + }); + + if (response.ok) { + selected_group = null; + resetChatState(); + clearChat(); + printMessages(); + await getCharacters(); + + $("#rm_info_avatar").html(""); + $("#rm_info_block").transition({ opacity: 0, duration: 0 }); + select_rm_info("Group deleted!"); + $("#rm_info_block").transition({ opacity: 1.0, duration: 2000 }); + } +} + +async function editGroup(id, immediately) { + const group = groups.find((x) => x.id == id); + + if (!group) { + return; + } + + if (immediately) { + return await _save(); + } + + saveGroupDebounced(group); +} + +async function groupChatAutoModeWorker() { + if (!is_group_automode_enabled || online_status === "no_connection") { + return; + } + + if (!selected_group || is_send_press || is_group_generating) { + return; + } + + const group = groups.find((x) => x.id === selected_group); + + if (!group || !Array.isArray(group.members) || !group.members.length) { + return; + } + + await generateGroupWrapper(true); +} + +function select_group_chats(chat_id) { + const group = chat_id && groups.find((x) => x.id == chat_id); + const groupName = group?.name ?? ""; + + $("#rm_group_chat_name").val(groupName); + $("#rm_group_chat_name").off(); + $("#rm_group_chat_name").on("input", async function () { + if (chat_id) { + group.name = $(this).val(); + await editGroup(chat_id); + } + }); + $("#rm_group_filter").val("").trigger("input"); + $("#rm_group_chats_block").css("display", "flex"); + $("#rm_group_chats_block").css("opacity", 0.0); + $("#rm_group_chats_block").transition({ + opacity: 1.0, + duration: 200, + easing: '', + complete: function () { }, + }); + + $("#rm_ch_create_block").css("display", "none"); + $("#rm_characters_block").css("display", "none"); + + async function memberClickHandler(event) { + event.stopPropagation(); + const id = $(this).data("id"); + const isDelete = !!$(this).closest("#rm_group_members").length; + const template = $(this).clone(); + template.data("id", id); + template.click(memberClickHandler); + + if (isDelete) { + template.find(".plus").show(); + template.find(".minus").hide(); + $("#rm_group_add_members").prepend(template); + } else { + template.find(".plus").hide(); + template.find(".minus").show(); + $("#rm_group_members").prepend(template); + } + + if (group) { + if (isDelete) { + const index = group.members.findIndex((x) => x === id); + if (index !== -1) { + group.members.splice(index, 1); + } + } else { + group.members.push(id); + } + await editGroup(chat_id); + updateGroupAvatar(group); + } + + $(this).remove(); + const groupHasMembers = !!$("#rm_group_members").children().length; + $("#rm_group_submit").prop("disabled", !groupHasMembers); + } + + // render characters list + $("#rm_group_add_members").empty(); + $("#rm_group_members").empty(); + for (let character of characters) { + const avatar = + character.avatar != "none" + ? `characters/${character.avatar}#${Date.now()}` + : default_avatar; + const template = $("#group_member_template .group_member").clone(); + template.data("id", character.name); + template.find(".avatar img").attr("src", avatar); + template.find(".ch_name").html(character.name); + template.click(memberClickHandler); + + if ( + group && + Array.isArray(group.members) && + group.members.includes(character.name) + ) { + template.find(".plus").hide(); + template.find(".minus").show(); + $("#rm_group_members").append(template); + } else { + template.find(".plus").show(); + template.find(".minus").hide(); + $("#rm_group_add_members").append(template); + } + } + + const groupHasMembers = !!$("#rm_group_members").children().length; + $("#rm_group_submit").prop("disabled", !groupHasMembers); + + // bottom buttons + if (chat_id) { + $("#rm_group_submit").hide(); + $("#rm_group_delete").show(); + } else { + $("#rm_group_submit").show(); + $("#rm_group_delete").hide(); + } + + $("#rm_group_delete").off(); + $("#rm_group_delete").on("click", function () { + $("#dialogue_popup").data("group_id", chat_id); + callPopup("

Delete the group?

", "del_group"); + }); + + // top bar + if (group) { + $("#rm_button_selected_ch").children("h2").css({}); + $("#rm_button_selected_ch").children("h2").text(""); + } +} + +$(document).ready(() => { + $(document).on("click", ".group_select", async function () { + const id = $(this).data("id"); + + if (!is_send_press && !is_group_generating) { + if (selected_group !== id) { + selected_group = id; + setCharacterId(undefined); + setCharacterName(''); + setEditedMessageId(undefined); + clearChat(); + chat.length = 0; + await getGroupChat(id); + } + + select_group_chats(id); + } + }); + + $("#rm_group_filter").on("input", function () { + const searchValue = $(this).val().trim().toLowerCase(); + + if (!searchValue) { + $("#rm_group_add_members .group_member").show(); + } else { + $("#rm_group_add_members .group_member").each(function () { + $(this).children(".ch_name").text().toLowerCase().includes(searchValue) + ? $(this).show() + : $(this).hide(); + }); + } + }); + + $("#rm_group_submit").click(async function () { + let name = $("#rm_group_chat_name").val(); + const members = $("#rm_group_members .group_member") + .map((_, x) => $(x).data("id")) + .toArray(); + + if (!name) { + name = `Chat with ${members.join(", ")}`; + } + + // placeholder + const avatar_url = 'img/five.png'; + + const createGroupResponse = await fetch("/creategroup", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({ + name: name, + members: members, + avatar_url: avatar_url, + }), + }); + + if (createGroupResponse.ok) { + await getCharacters(); + $("#rm_info_avatar").html(""); + const avatar = $("#avatar_div_div").clone(); + avatar.find("img").attr("src", avatar_url); + $("#rm_info_avatar").append(avatar); + $("#rm_info_block").transition({ opacity: 0, duration: 0 }); + select_rm_info("Group chat created"); + $("#rm_info_block").transition({ opacity: 1.0, duration: 2000 }); + } + }); + + $("#rm_group_automode").on("input", function () { + const value = $(this).prop("checked"); + is_group_automode_enabled = value; + }); +}); \ No newline at end of file diff --git a/public/scripts/utils.js b/public/scripts/utils.js new file mode 100644 index 000000000..fb06eaaa6 --- /dev/null +++ b/public/scripts/utils.js @@ -0,0 +1,86 @@ +export { + onlyUnique, + shuffle, + download, + urlContentToDataUri, + getBase64Async, + getStringHash, + debounce, + delay, +}; + +/// UTILS +function onlyUnique(value, index, array) { + return array.indexOf(value) === index; +} + +function shuffle(array) { + let currentIndex = array.length, + randomIndex; + + while (currentIndex != 0) { + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], + array[currentIndex], + ]; + } + return array; +} + +function download(content, fileName, contentType) { + const a = document.createElement("a"); + const file = new Blob([content], { type: contentType }); + a.href = URL.createObjectURL(file); + a.download = fileName; + a.click(); +} + +async function urlContentToDataUri(url, params) { + const response = await fetch(url, params); + const blob = await response.blob(); + return await new Promise(callback => { + let reader = new FileReader(); + reader.onload = function () { callback(this.result); }; + reader.readAsDataURL(blob); + }); +} + +function getBase64Async(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function () { + resolve(reader.result); + }; + reader.onerror = function (error) { + reject(error); + }; + }); +} + +function getStringHash(str, seed = 0) { + let h1 = 0xdeadbeef ^ seed, + h2 = 0x41c6ce57 ^ seed; + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); + } + + h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909); + h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909); + + return 4294967296 * (2097151 & h2) + (h1 >>> 0); +}; + +function debounce(func, timeout = 300) { + let timer; + return (...args) => { + clearTimeout(timer); + timer = setTimeout(() => { func.apply(this, args); }, timeout); + }; +} + +const delay = (ms) => new Promise((res) => setTimeout(res, ms)); \ No newline at end of file diff --git a/public/scripts/worldinfo.js b/public/scripts/worldinfo.js new file mode 100644 index 000000000..9f51dac89 --- /dev/null +++ b/public/scripts/worldinfo.js @@ -0,0 +1,649 @@ +import { saveSettings, callPopup, token, substituteParams } from "../script.js"; +import { download, debounce } from "./utils.js"; +import { encode } from "../scripts/gpt-2-3-tokenizer/mod.js"; + +export { + world_info, + world_info_data, + world_info_budget, + world_info_depth, + world_names, + imported_world_name, + checkWorldInfo, + deleteWorldInfo, + selectImportedWorldInfo, + setWorldInfoSettings, +} + +let world_info = null; +let world_names; +let world_info_data = null; +let world_info_depth = 2; +let world_info_budget = 128; +let is_world_edit_open = false; +let imported_world_name = ""; +const saveWorldDebounced = debounce(async () => await _save(), 500); +const saveSettingsDebounced = debounce(() => saveSettings(), 500); + +const world_info_position = { + before: 0, + after: 1, +}; + +function setWorldInfoSettings(settings, data) { + if (settings.world_info_depth !== undefined) + world_info_depth = Number(settings.world_info_depth); + if (settings.world_info_budget !== undefined) + world_info_budget = Number(settings.world_info_budget); + + $("#world_info_depth_counter").html(`${world_info_depth} Messages`); + $("#world_info_depth").val(world_info_depth); + + $("#world_info_budget_counter").html(`${world_info_budget} Tokens`); + $("#world_info_budget").val(world_info_budget); + + world_names = data.world_names?.length ? data.world_names : []; + + if (settings.world_info != undefined) { + if (world_names.includes(settings.world_info)) { + world_info = settings.world_info; + } + } + + world_names.forEach((item, i) => { + $("#world_info").append(``); + // preselect world if saved + if (item == world_info) { + $("#world_info").val(i).change(); + } + }); +} + +// World Info Editor +async function showWorldEditor() { + if (!world_info) { + callPopup("

Select a world info first!

", "default"); + return; + } + + is_world_edit_open = true; + $("#world_popup_name").val(world_info); + $("#world_popup").css("display", "flex"); + await loadWorldInfoData(); + displayWorldEntries(world_info_data); +} + +async function loadWorldInfoData() { + if (!world_info) { + return; + } + + const response = await fetch("/getworldinfo", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({ name: world_info }), + }); + + if (response.ok) { + world_info_data = await response.json(); + } +} + +async function updateWorldInfoList(importedWorldName) { + var result = await fetch("/getsettings", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({}), + }); + + if (result.ok) { + var data = await result.json(); + world_names = data.world_names?.length ? data.world_names : []; + $("#world_info").find('option[value!="None"]').remove(); + + world_names.forEach((item, i) => { + $("#world_info").append(``); + }); + + if (importedWorldName) { + const indexOf = world_names.indexOf(world_info); + $("#world_info").val(indexOf); + + callPopup("

World imported successfully! Select it now?

", "world_imported"); + } + } +} + +function hideWorldEditor() { + is_world_edit_open = false; + $("#world_popup").css("display", "none"); +} + +function displayWorldEntries(data) { + $("#world_popup_entries_list").empty(); + + if (!data || !("entries" in data)) { + return; + } + + for (const entryUid in data.entries) { + const entry = data.entries[entryUid]; + appendWorldEntry(entry); + } +} + +function appendWorldEntry(entry) { + const template = $("#entry_edit_template .world_entry").clone(); + template.data("uid", entry.uid); + + // key + const keyInput = template.find('textarea[name="key"]'); + keyInput.data("uid", entry.uid); + keyInput.on("input", function () { + const uid = $(this).data("uid"); + const value = $(this).val(); + $(this).css("height", ""); //reset the height + $(this).css("height", $(this).prop("scrollHeight") + "px"); + world_info_data.entries[uid].key = value + .split(",") + .map((x) => x.trim()) + .filter((x) => x); + saveWorldInfo(); + }); + keyInput.val(entry.key.join(",")).trigger("input"); + keyInput.css("height", ""); //reset the height + keyInput.css("height", $(this).prop("scrollHeight") + "px"); + + // keysecondary + const keySecondaryInput = template.find('textarea[name="keysecondary"]'); + keySecondaryInput.data("uid", entry.uid); + keySecondaryInput.on("input", function () { + const uid = $(this).data("uid"); + const value = $(this).val(); + $(this).css("height", ""); //reset the height + $(this).css("height", $(this).prop("scrollHeight") + "px"); + world_info_data.entries[uid].keysecondary = value + .split(",") + .map((x) => x.trim()) + .filter((x) => x); + saveWorldInfo(); + }); + keySecondaryInput.val(entry.keysecondary.join(",")).trigger("input"); + keySecondaryInput.css("height", ""); //reset the height + keySecondaryInput.css("height", $(this).prop("scrollHeight") + "px"); + + // comment + const commentInput = template.find('textarea[name="comment"]'); + commentInput.data("uid", entry.uid); + commentInput.on("input", function () { + const uid = $(this).data("uid"); + const value = $(this).val(); + $(this).css("height", ""); //reset the height + $(this).css("height", $(this).prop("scrollHeight") + "px"); + world_info_data.entries[uid].comment = value; + saveWorldInfo(); + }); + commentInput.val(entry.comment).trigger("input"); + commentInput.css("height", ""); //reset the height + commentInput.css("height", $(this).prop("scrollHeight") + "px"); + + // content + const contentInput = template.find('textarea[name="content"]'); + contentInput.data("uid", entry.uid); + contentInput.on("input", function () { + const uid = $(this).data("uid"); + const value = $(this).val(); + world_info_data.entries[uid].content = value; + $(this).css("height", ""); //reset the height + $(this).css("height", $(this).prop("scrollHeight") + "px"); + saveWorldInfo(); + + // count tokens + const numberOfTokens = encode(value).length; + $(this) + .closest(".world_entry") + .find(".world_entry_form_token_counter") + .html(numberOfTokens); + }); + contentInput.val(entry.content).trigger("input"); + contentInput.css("height", ""); //reset the height + contentInput.css("height", $(this).prop("scrollHeight") + "px"); + + // selective + const selectiveInput = template.find('input[name="selective"]'); + selectiveInput.data("uid", entry.uid); + selectiveInput.on("input", function () { + const uid = $(this).data("uid"); + const value = $(this).prop("checked"); + world_info_data.entries[uid].selective = value; + saveWorldInfo(); + + const keysecondary = $(this) + .closest(".world_entry") + .find(".keysecondary"); + value ? keysecondary.show() : keysecondary.hide(); + }); + selectiveInput.prop("checked", entry.selective).trigger("input"); + selectiveInput.siblings(".checkbox_fancy").click(function () { + $(this).siblings("input").click(); + }); + + // constant + const constantInput = template.find('input[name="constant"]'); + constantInput.data("uid", entry.uid); + constantInput.on("input", function () { + const uid = $(this).data("uid"); + const value = $(this).prop("checked"); + world_info_data.entries[uid].constant = value; + saveWorldInfo(); + }); + constantInput.prop("checked", entry.constant).trigger("input"); + constantInput.siblings(".checkbox_fancy").click(function () { + $(this).siblings("input").click(); + }); + + // order + const orderInput = template.find('input[name="order"]'); + orderInput.data("uid", entry.uid); + orderInput.on("input", function () { + const uid = $(this).data("uid"); + const value = Number($(this).val()); + + world_info_data.entries[uid].order = !isNaN(value) ? value : 0; + saveWorldInfo(); + }); + orderInput.val(entry.order).trigger("input"); + + // position + if (entry.position === undefined) { + entry.position = 0; + } + + const positionInput = template.find('input[name="position"]'); + positionInput.data("uid", entry.uid); + positionInput.on("input", function () { + const uid = $(this).data("uid"); + const value = Number($(this).val()); + world_info_data.entries[uid].position = !isNaN(value) ? value : 0; + saveWorldInfo(); + }); + template + .find(`input[name="position"][value=${entry.position}]`) + .prop("checked", true) + .trigger("input"); + + // display uid + template.find(".world_entry_form_uid_value").html(entry.uid); + + // delete button + const deleteButton = template.find("input.delete_entry_button"); + deleteButton.data("uid", entry.uid); + deleteButton.on("click", function () { + const uid = $(this).data("uid"); + deleteWorldInfoEntry(uid); + $(this).closest(".world_entry").remove(); + saveWorldInfo(); + }); + + template.appendTo("#world_popup_entries_list"); + return template; +} + +async function deleteWorldInfoEntry(uid) { + if (!world_info_data || !("entries" in world_info_data)) { + return; + } + + delete world_info_data.entries[uid]; +} + +function createWorldInfoEntry() { + const newEntryTemplate = { + key: [], + keysecondary: [], + comment: "", + content: "", + constant: false, + selective: false, + order: 100, + position: 0, + }; + const newUid = getFreeWorldEntryUid(); + + if (!Number.isInteger(newUid)) { + console.error("Couldn't assign UID to a new entry"); + return; + } + + const newEntry = { uid: newUid, ...newEntryTemplate }; + world_info_data.entries[newUid] = newEntry; + + const entryTemplate = appendWorldEntry(newEntry); + entryTemplate.get(0).scrollIntoView({ behavior: "smooth" }); +} + +async function _save() { + const response = await fetch("/editworldinfo", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({ name: world_info, data: world_info_data }), + }); +} + +async function saveWorldInfo(immediately) { + if (!world_info || !world_info_data) { + return; + } + + + if (immediately) { + return await _save(); + } + + saveWorldDebounced(); +} + +async function renameWorldInfo() { + const oldName = world_info; + const newName = $("#world_popup_name").val(); + + if (oldName === newName) { + return; + } + + world_info = newName; + await saveWorldInfo(true); + await deleteWorldInfo(oldName, newName); +} + +async function deleteWorldInfo(worldInfoName, selectWorldName) { + if (!world_names.includes(worldInfoName)) { + return; + } + + const response = await fetch("/deleteworldinfo", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": token, + }, + body: JSON.stringify({ name: worldInfoName }), + }); + + if (response.ok) { + await updateWorldInfoList(); + + const selectedIndex = world_names.indexOf(selectWorldName); + if (selectedIndex !== -1) { + $("#world_info").val(selectedIndex).change(); + } else { + $("#world_info").val("None").change(); + } + + hideWorldEditor(); + } +} + +function getFreeWorldEntryUid() { + if (!world_info_data || !("entries" in world_info_data)) { + return null; + } + + const MAX_UID = 1_000_000; // <- should be safe enough :) + for (let uid = 0; uid < MAX_UID; uid++) { + if (uid in world_info_data.entries) { + continue; + } + return uid; + } + + return null; +} + +function getFreeWorldName() { + const MAX_FREE_NAME = 100_000; + for (let index = 1; index < MAX_FREE_NAME; index++) { + const newName = `New World (${index})`; + if (world_names.includes(newName)) { + continue; + } + return newName; + } + + return undefined; +} + +async function createNewWorldInfo() { + const worldInfoTemplate = { entries: {} }; + const worldInfoName = getFreeWorldName(); + + if (!worldInfoName) { + return; + } + + world_info = worldInfoName; + world_info_data = { ...worldInfoTemplate }; + await saveWorldInfo(true); + await updateWorldInfoList(); + + const selectedIndex = world_names.indexOf(worldInfoName); + if (selectedIndex !== -1) { + $("#world_info").val(selectedIndex).change(); + } else { + $("#world_info").val("None").change(); + } +} + +function checkWorldInfo(chat) { + if (world_info_data.entries.length == 0) { + return ""; + } + + const messagesToLookBack = world_info_depth * 2; + let textToScan = chat.slice(0, messagesToLookBack).join("").toLowerCase(); + let worldInfoBefore = ""; + let worldInfoAfter = ""; + let needsToScan = true; + let allActivatedEntries = new Set(); + + const sortedEntries = Object.keys(world_info_data.entries) + .map((x) => world_info_data.entries[x]) + .sort((a, b) => b.order - a.order); + while (needsToScan) { + let activatedNow = new Set(); + + for (let entry of sortedEntries) { + if (allActivatedEntries.has(entry.uid)) { + continue; + } + + if (entry.constant) { + activatedNow.add(entry.uid); + } + + if (Array.isArray(entry.key) && entry.key.length) { + primary: for (let key of entry.key) { + if (key && textToScan.includes(key.trim().toLowerCase())) { + if ( + entry.selective && + Array.isArray(entry.keysecondary) && + entry.keysecondary.length + ) { + secondary: for (let keysecondary of entry.keysecondary) { + if ( + keysecondary && + textToScan.includes(keysecondary.trim().toLowerCase()) + ) { + activatedNow.add(entry.uid); + break secondary; + } + } + } else { + activatedNow.add(entry.uid); + break primary; + } + } + } + } + } + + needsToScan = activatedNow.size > 0; + const newEntries = [...activatedNow] + .map((x) => world_info_data.entries[x]) + .sort((a, b) => sortedEntries.indexOf(a) - sortedEntries.indexOf(b)); + + for (const entry of newEntries) { + if (entry.position === world_info_position.after) { + worldInfoAfter = `${substituteParams( + entry.content + )}\n${worldInfoAfter}`; + } else { + worldInfoBefore = `${substituteParams( + entry.content + )}\n${worldInfoBefore}`; + } + + if ( + encode(worldInfoBefore + worldInfoAfter).length >= world_info_budget + ) { + needsToScan = false; + break; + } + } + + if (needsToScan) { + textToScan = + newEntries + .map((x) => x.content) + .join("\n") + .toLowerCase() + textToScan; + } + + allActivatedEntries = new Set([...allActivatedEntries, ...activatedNow]); + } + + return { worldInfoBefore, worldInfoAfter }; +} + +function selectImportedWorldInfo() { + if (!imported_world_name) { + return; + } + + world_names.forEach((item, i) => { + if (item === imported_world_name) { + $("#world_info").val(i).change(); + } + }); + imported_world_name = ""; +} + +$(document).ready(() => { + $("#world_info").change(async function () { + const selectedWorld = $("#world_info").find(":selected").val(); + world_info = null; + world_info_data = null; + + if (selectedWorld !== "None") { + const worldIndex = Number(selectedWorld); + world_info = !isNaN(worldIndex) ? world_names[worldIndex] : null; + await loadWorldInfoData(); + } + + hideWorldEditor(); + saveSettingsDebounced(); + }); + + //**************************WORLD INFO IMPORT EXPORT*************************// + $("#world_import_button").click(function () { + $("#world_import_file").click(); + }); + + $("#world_import_file").on("change", function (e) { + var file = e.target.files[0]; + + if (!file) { + return; + } + + const ext = file.name.match(/\.(\w+)$/); + if (!ext || ext[1].toLowerCase() !== "json") { + return; + } + + const formData = new FormData($("#form_world_import").get(0)); + + jQuery.ajax({ + type: "POST", + url: "/importworldinfo", + data: formData, + beforeSend: () => { }, + cache: false, + contentType: false, + processData: false, + success: function (data) { + if (data.name) { + imported_world_name = data.name; + updateWorldInfoList(imported_world_name); + } + }, + error: (jqXHR, exception) => { }, + }); + + // Will allow to select the same file twice in a row + $("#form_world_import").trigger("reset"); + }); + + $("#world_info_edit_button").click(() => { + is_world_edit_open ? hideWorldEditor() : showWorldEditor(); + }); + + $("#world_popup_export").click(() => { + if (world_info && world_info_data) { + const jsonValue = JSON.stringify(world_info_data); + const fileName = `${world_info}.json`; + download(jsonValue, fileName, "application/json"); + } + }); + + $("#world_popup_delete").click(() => { + callPopup("

Delete the World Info?

", "del_world"); + }); + + $("#world_popup_new").click(() => { + createWorldInfoEntry(); + }); + + $("#world_cross").click(() => { + hideWorldEditor(); + }); + + $("#world_popup_name_button").click(() => { + renameWorldInfo(); + }); + + $("#world_create_button").click(() => { + createNewWorldInfo(); + }); + + $(document).on("input", "#world_info_depth", function () { + world_info_depth = Number($(this).val()); + $("#world_info_depth_counter").html(`${$(this).val()} Messages`); + saveSettingsDebounced(); + }); + + $(document).on("input", "#world_info_budget", function () { + world_info_budget = Number($(this).val()); + $("#world_info_budget_counter").html(`${$(this).val()} Tokens`); + saveSettingsDebounced(); + }); +}); \ No newline at end of file diff --git a/public/style.css b/public/style.css index 0e9387666..d3bdec9f3 100644 --- a/public/style.css +++ b/public/style.css @@ -2490,9 +2490,10 @@ a { max-width: 600px; max-height: 300px; border-radius: 10px; + display: block; } -.mes img.img_extra~* { +.mes img.img_extra ~ * { display: none; }