diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/public/KoboldAI Worlds/Sample.json b/public/KoboldAI Worlds/Sample.json new file mode 100644 index 000000000..082684e84 --- /dev/null +++ b/public/KoboldAI Worlds/Sample.json @@ -0,0 +1,30 @@ +{ + "folders": { + "Sample Folder": [ + 0, + 1 + ] + }, + "entries": { + "0": { + "uid": 0, + "title": "AAA", + "key": [ "AAA" ], + "keysecondary": [ ], + "constant": false, + "content": "AAA is a city where BBB lives.", + "comment": "AAA definition", + "selective": true + }, + "1": { + "uid": 1, + "title": "BBB", + "key": [ "BBB" ], + "keysecondary": [ ], + "constant": false, + "content": "BBB is a 21-year old female student of CCC academy.", + "comment": "BBB definition", + "selective": true + } + } +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index e19a7131c..e5d7c6c46 100644 --- a/public/index.html +++ b/public/index.html @@ -54,6 +54,7 @@ var is_mes_reload_avatar = false; 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 @@ -67,6 +68,8 @@ var create_save_mes_example = ''; var timerSaveEdit; + var timerKoboldSync; + var timerWorldSave; var durationSaveEdit = 200; //animation right menu var animation_rm_duration = 200; @@ -102,6 +105,13 @@ var user_avatar = 'you.png'; var temp = 0.5; var amount_gen = 80; + var kobold_world = null; + var koboldai_world_names; + var kobold_world_synced = false; + var kobold_sync_failed = false; + var kobold_is_united = false; + var kobold_world_data = null; + var imported_world_name = ''; var max_context = 2048;//2048; var rep_pen = 1; var rep_pen_size = 100; @@ -191,7 +201,6 @@ $("#online_status_indicator3").css("background-color", "green"); $("#online_status_text3").html(online_status); } - } async function getLastVersion(){ @@ -251,6 +260,9 @@ online_status = data.result; if(online_status == undefined){ online_status = 'no_connection'; + + kobold_world_synced = false; + updateWorldStatus(); } if(online_status.toLowerCase().indexOf('pygmalion') != -1){ is_pygmalion = true; @@ -263,6 +275,7 @@ resultCheckStatus(); if(online_status !== 'no_connection'){ var checkStatusNow = setTimeout(getStatus, 3000);//getStatus(); + syncKoboldWorldInfo(false); } }, error: function (jqXHR, exception) { @@ -270,12 +283,18 @@ console.log(jqXHR); online_status = 'no_connection'; + // invalidate world info when losing connection to kobold + kobold_world_synced = false; + updateWorldStatus(); + resultCheckStatus(); } }); }else{ if(is_get_status_novel != true){ online_status = 'no_connection'; + kobold_world_synced = false; + updateWorldStatus(); } } } @@ -896,7 +915,8 @@ var generate_data; if(main_api == 'kobold'){ - var generate_data = {prompt: finalPromt, gui_settings: true,max_length: amount_gen,temperature: temp, max_context_length: max_context}; + const use_world_info = Boolean(kobold_world && kobold_world_synced); + var generate_data = {prompt: finalPromt, gui_settings: true,max_length: amount_gen,temperature: temp, max_context_length: max_context, use_world_info}; if(preset_settings != 'gui'){ var this_settings = koboldai_settings[koboldai_setting_names[preset_settings]]; @@ -937,7 +957,8 @@ s4:this_settings.sampler_order[3], s5:this_settings.sampler_order[4], s6:this_settings.sampler_order[5], - s7:this_settings.sampler_order[6] + s7:this_settings.sampler_order[6], + use_world_info: use_world_info, }; } } @@ -1199,6 +1220,9 @@ $( "#rm_button_characters" ).children("h2").css(deselected_button_style); $( "#rm_button_settings" ).children("h2").css(seleced_button_style); $( "#rm_button_selected_ch" ).children("h2").css(deselected_button_style); + + // Dumb call, but won't need an interval + updateWorldStatus(); }); $( "#rm_button_characters" ).click(function() { selected_button = 'characters'; @@ -1504,6 +1528,17 @@ } }); } + if (popup_type === 'world_imported' && imported_world_name) { + koboldai_world_names.forEach((item, i) => { + if (item === imported_world_name) { + $('#world_info').val(i).change(); + } + }) + imported_world_name = ''; + } + if (popup_type === 'del_world' && kobold_world) { + deleteWorldInfo(kobold_world); + } if(popup_type == 'new_chat' && this_chid != undefined && menu_type != "create"){//Fix it; New chat doesn't create while open create character menu clearChat(); chat.length = 0; @@ -1529,11 +1564,13 @@ $("#dialogue_popup_cancel").css("display", "none"); break; + case 'world_imported': case 'new_chat': $("#dialogue_popup_ok").css("background-color", "#191b31CC"); $("#dialogue_popup_ok").text("Yes"); break; + case 'del_world': default: $("#dialogue_popup_ok").css("background-color", "#791b31"); $("#dialogue_popup_ok").text("Delete"); @@ -1829,6 +1866,7 @@ is_get_status = true; is_api_button_press = true; getStatus(); + detectUnitedKobold(); } }); @@ -1927,6 +1965,23 @@ }); + $("#world_info").change(function() { + const selectedWorld = $('#world_info').find(":selected").val(); + kobold_world_synced = false; + kobold_sync_failed = false; + kobold_world = null; + + if (selectedWorld !== 'None') { + const worldIndex = Number(selectedWorld); + kobold_world = !isNaN(worldIndex) ? koboldai_world_names[worldIndex] : null; + } + + hideWorldEditor(); + syncKoboldWorldInfo(true); + saveSettings(); + updateWorldStatus(); + }); + $( "#settings_perset" ).change(function() { if($('#settings_perset').find(":selected").val() != 'gui'){ @@ -2005,6 +2060,7 @@ main_api = 'kobold'; $('#max_context_block').css('display', 'block'); $('#amount_gen_block').css('display', 'block'); + $('#world_info_block').css('display', 'flex'); } if($('#main_api').find(":selected").val() == 'novel'){ $('#kobold_api').css("display", "none"); @@ -2012,7 +2068,10 @@ main_api = 'novel'; $('#max_context_block').css('display', 'none'); $('#amount_gen_block').css('display', 'none'); + $('#world_info_block').css('display', 'none'); } + + updateWorldStatus(); } async function getUserAvatars(){ const response = await fetch("/getuseravatars", { @@ -2037,7 +2096,100 @@ } } - + + function updateWorldStatus() { + if($('#world_info_block').is(':visible') && kobold_world) { + $('#world_info_edit_button').show(); + $('#world_status').show(); + + if (kobold_world_synced) { + $("#world_status_indicator").css("background-color", "green"); + $("#world_status_text").html("Synchronized with KoboldAI") + } + else { + let statusText = online_status === 'no_connection' + ? "Waiting for connection" + : "Synchronizing..."; + + if (kobold_sync_failed) { + statusText = "Synchronization failed (see console)"; + } + + $("#world_status_text").html(statusText); + $("#world_status_indicator").css("background-color", "red"); + } + + if (kobold_is_united) { + $("#world_status_text").html('KoboldAI United detected. WI may not work as intended.
If experiencing issues, please select "None".
'); + } + } else { + $('#world_status').hide(); + $('#world_info_edit_button').hide(); + } + } + + async function detectUnitedKobold() { + if (!api_server || main_api !== 'kobold') { + return; + } + + // If we can reach Kobold's new ui, then it should be United branch + kobold_is_united = false; + try { + const kobold_united_ui2 = api_server.replace('/api', '/new_ui'); + const response = await fetch(kobold_united_ui2, { method: 'HEAD'}); + + if (response.ok && response.status == 200) { + kobold_is_united = true; + } + } + catch { + // empty catch + } + } + + async function syncKoboldWorldInfo(force) { + // Don't sync if no world selected or if synced and not forcing + if (online_status === 'no_connection' || (!kobold_world && !force) || (kobold_world_synced && !force)) { + updateWorldStatus(); + return; + } + + const response = await fetch("/synckoboldworld", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ "name": kobold_world }) + }); + + if (response.ok) { + const syncData = await response.json(); + + if (syncData.ok) { + kobold_world_synced = true; + kobold_sync_failed = false; + } + + if (syncData.busy) { + // console.log('Sync API is busy. Retrying in 3sec'); + clearTimeout(timerKoboldSync); + timerKoboldSync = setTimeout(() => syncKoboldWorldInfo(force), 3000); + return; + } + } else { + kobold_sync_failed = true; + let responseLog = response.statusText; + + try { + var responseBody = await response.text(); + responseLog += ('\n' + responseBody); + } catch { + // empty catch + } + console.error(`Sync API response: ${responseLog}`); + } + + updateWorldStatus(); + } $(document).on('input', '#temp', function() { temp = $(this).val(); @@ -2181,6 +2333,26 @@ koboldai_setting_names = {}; koboldai_setting_names = arr_holder; + // world info settings + koboldai_world_names = data.koboldai_world_names?.length ? data.koboldai_world_names : []; + + if(settings.kobold_world != undefined) { + if (koboldai_world_names.includes(settings.kobold_world)) { + kobold_world = settings.kobold_world; + kobold_world_synced = false; + kobold_sync_failed = false; + } + } + + koboldai_world_names.forEach((item, i) => { + $('#world_info').append(``); + // preselect world if saved + if (item == kobold_world){ + $('#world_info').val(i).change(); + } + }); + // end world info settings + preset_settings = settings.preset_settings; temp = settings.temp; @@ -2256,7 +2428,6 @@ preset_settings = 'gui'; $("#settings_perset option[value=gui]").attr('selected', 'true'); } - } //User @@ -2306,7 +2477,8 @@ model_novel: model_novel, temp_novel: temp_novel, rep_pen_novel: rep_pen_novel, - rep_pen_size_novel: rep_pen_size_novel + rep_pen_size_novel: rep_pen_size_novel, + kobold_world: kobold_world,                  }), beforeSend: function(){ @@ -2321,7 +2493,8 @@ if(type === 'change_name'){ location.reload(); } - + + syncKoboldWorldInfo(false); }, error: function (jqXHR, exception) { console.log(exception); @@ -2739,6 +2912,376 @@ $('#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"); + }); + + async function updateWorldInfoList(importedWorldName) { + var result = await fetch('/getsettings', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({}) + }); + + if (result.ok) { + var data = await result.json(); + koboldai_world_names = data.koboldai_world_names?.length ? data.koboldai_world_names : []; + $('#world_info').find('option[value!="None"]').remove(); + + koboldai_world_names.forEach((item, i) => { + $('#world_info').append(``); + }); + + if (importedWorldName) { + const indexOf = koboldai_world_names.indexOf(kobold_world); + $('#world_info').val(indexOf); + + popup_type = 'world_imported'; + callPopup('

World imported successfully! Select it now?

'); + } + } + } + + 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(); + } + + // World Info Editor + async function showWorldEditor() { + is_world_edit_open = true; + $('#world_popup_name').val(kobold_world); + $('#world_popup').css('display', 'flex'); + + if (kobold_world) { + const response = await fetch("/getworldinfo", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name: kobold_world }) + }); + + if (response.ok) { + kobold_world_data = await response.json(); + displayWorldEntries(kobold_world_data); + } + } + } + + function hideWorldEditor() { + is_world_edit_open = false; + $('#world_popup').css('display', 'none'); + syncKoboldWorldInfo(true); + } + + 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('input[name="key"]'); + keyInput.data('uid', entry.uid); + keyInput.on('input', function () { + const uid = $(this).data('uid'); + const value = $(this).val(); + kobold_world_data.entries[uid].key = value.split(',').map(x => x.trim()).filter(x => x); + saveWorldInfo(); + }); + keyInput.val(entry.key.join(',')).trigger('input'); + + // keysecondary + const keySecondaryInput = template.find('input[name="keysecondary"]'); + keySecondaryInput.data('uid', entry.uid); + keySecondaryInput.on('input', function() { + const uid = $(this).data('uid'); + const value = $(this).val(); + kobold_world_data.entries[uid].keysecondary = value.split(',').map(x => x.trim()).filter(x => x); + saveWorldInfo(); + }); + keySecondaryInput.val(entry.keysecondary.join(',')).trigger('input'); + + // comment + const commentInput = template.find('input[name="comment"]'); + commentInput.data('uid', entry.uid); + commentInput.on('input', function() { + const uid = $(this).data('uid'); + const value = $(this).val(); + kobold_world_data.entries[uid].comment = value; + saveWorldInfo(); + }); + commentInput.val(entry.comment).trigger('input'); + + // 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(); + kobold_world_data.entries[uid].content = value; + 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'); + + // 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'); + kobold_world_data.entries[uid].selective = value; + saveWorldInfo(); + }); + 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'); + kobold_world_data.entries[uid].constant = value; + saveWorldInfo(); + }); + constantInput.prop('checked', entry.constant).trigger('input'); + constantInput.siblings('.checkbox_fancy').click(function() { + $(this).siblings('input').click(); + }); + + // 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 (!kobold_world_data || !('entries' in kobold_world_data)) { + return; + } + + delete kobold_world_data.entries[uid]; + + if ('folders' in kobold_world_data) { + for (const folderName in kobold_world_data.folders) { + const folder = kobold_world_data.folders[folderName] + const index = folder.indexOf(Number(uid)); + + if (index !== -1) { + folder.splice(index, 1); + } + } + } + } + + function createWorldInfoEntry() { + const newEntryTemplate = { + key: [], + keysecondary: [], + comment: '', + content: '', + constant: false, + selective: false, + }; + const newUid = getFreeWorldEntryUid(); + + if (!Number.isInteger(newUid)) { + console.error("Couldn't assign UID to a new entry"); + return; + } + + const newEntry = { uid: newUid, ...newEntryTemplate }; + kobold_world_data.entries[newUid] = newEntry; + + if ('folders' in kobold_world_data) { + if (kobold_world in kobold_world_data.folders && Array.isArray(kobold_world_data.folders)) { + kobold_world_data.folders[kobold_world].push(newUid); + } else { + kobold_world_data.folders[kobold_world] = [newUid]; + } + } + + const entryTemplate = appendWorldEntry(newEntry); + entryTemplate.get(0).scrollIntoView({behavior: 'smooth'}); + } + + async function saveWorldInfo(immediately) { + if (!kobold_world || !kobold_world_data) { + return; + } + + async function _save() { + const response = await fetch("/editworldinfo", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name: kobold_world, data: kobold_world_data }) + }); + + if (response.ok) { + kobold_world_synced = false; + } + } + + if (immediately) { + return await _save(); + } + + clearTimeout(timerWorldSave); + timerWorldSave = setTimeout(async () => await _save(), durationSaveEdit); + } + + async function renameWorldInfo() { + const oldName = kobold_world; + const newName = $('#world_popup_name').val(); + + if (oldName === newName) { + return; + } + + kobold_world = newName; + await saveWorldInfo(true); + await deleteWorldInfo(oldName, newName); + } + + async function deleteWorldInfo(worldInfoName, selectWorldName) { + if (!koboldai_world_names.includes(worldInfoName)) { + return; + } + + const response = await fetch("/deleteworldinfo", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name: worldInfoName }) + }); + + if (response.ok) { + await updateWorldInfoList(); + + const selectedIndex = koboldai_world_names.indexOf(selectWorldName); + if (selectedIndex !== -1) { + $('#world_info').val(selectedIndex).change(); + } + else { + $('#world_info').val('None').change(); + } + + hideWorldEditor(); + } + } + + function getFreeWorldEntryUid() { + if (!kobold_world_data || !('entries' in kobold_world_data)) { + return null; + } + + const MAX_UID = 1_000_000; // <- should be safe enough :) + for (let uid = 0; uid < MAX_UID; uid++) { + if (uid in kobold_world_data.entries) { + continue; + } + return uid; + } + + return null; + } + + $('#world_info_edit_button').click(() => { + is_world_edit_open ? hideWorldEditor() : showWorldEditor(); + }); + + $('#world_popup_export').click(() => { + if (kobold_world && kobold_world_data) { + const jsonValue = JSON.stringify(kobold_world_data); + const fileName = `${kobold_world}.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(); + }); });      Tavern.AI @@ -2796,6 +3339,95 @@
+ +
+
+ +
+ + +

+ World Info Editor + (?) +

+
 
+
+ + +
+
+
+ +
+
+ +
+ +
 
+ + +
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +   +
+ UID: +   + +
+
+ Tokens used: +   + 0 +
+ +
+
+
+
+
+
@@ -2946,6 +3578,25 @@

Repetition Penalty Range

select
+
+

+ World Info +

+Import

+

+
How to use (?)
+ + + +
+
+
+