#1226 Add chat-bound lorebooks

This commit is contained in:
Cohee 2023-10-16 23:03:42 +03:00
parent 8a3547ecd0
commit 6a1b230c7e
6 changed files with 99 additions and 9 deletions

View File

@ -3428,6 +3428,7 @@
<input type="hidden" id="fav_checkbox" name="fav" />
<div id="advanced_div" class="menu_button fa-solid fa-book " title="Advanced Definitions" data-i18n="[title]Advanced Definition"></div>
<div id="world_button" class="menu_button fa-solid fa-globe" title="Character Lore" data-i18n="[title]Character Lore"></div>
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
<div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
<!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
<!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
@ -3543,8 +3544,11 @@
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
</div>
<div class="inline-drawer-content">
<div name="group-metadata-controls" class="marginTopBot5">
<input id="rm_group_chat_name" class="text_pole wide100p" type="text" name="chat_name" data-i18n="[placeholder]Chat Name (Optional)" placeholder="Chat Name (Optional)" maxlength="100" />
<div id="group-metadata-controls" class="marginTopBot5">
<div class="flex-container wide100p">
<input id="rm_group_chat_name" class="text_pole flex1" type="text" name="chat_name" data-i18n="[placeholder]Chat Name (Optional)" placeholder="Chat Name (Optional)" maxlength="100" />
<div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
</div>
<div id="group_tags_div" class="wide100p">
<div class="tag_controls">
<input id="groupTagInput" class="text_pole tag_input flex1 margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" maxlength="25" />
@ -3896,6 +3900,25 @@
</div>
</div>
<div id="chat_world_template" class="template_element">
<div class="chat_world range-block flexFlowColumn flex-container">
<div class="range-block-title">
<h4 data-i18n="Chat Lorebook">Chat Lorebook for <span class="chat_name"></span></h4>
</div>
<div class="range-block-counter justifyLeft flex-container flexFlowColumn margin-bot-10px">
<span data-i18n="A selected World Info will be bound to this chat.">
A selected World Info will be bound to this chat. When generating an AI reply,
it will be combined with the entries from global and character lorebooks.
</span>
</div>
<div class="range-block-range wide100p">
<select class="chat_world_info_selector wide100p">
<option value="">--- None ---</option>
</select>
</div>
</div>
</div>
<div id="character_world_template" class="template_element">
<div class="character_world range-block flexFlowColumn flex-container">
<div class="range-block-title">

View File

@ -5729,6 +5729,7 @@ export function select_selected_character(chid) {
checkEmbeddedWorld(chid);
$("#form_create").attr("actiontype", "editcharacter");
$('.form_create_bottom_buttons_block .chat_lorebook_button').show();
saveSettingsDebounced();
}
@ -5787,6 +5788,7 @@ function select_rm_create() {
checkEmbeddedWorld();
$("#form_create").attr("actiontype", "createcharacter");
$('.form_create_bottom_buttons_block .chat_lorebook_button').hide();
}
function select_rm_characters() {

View File

@ -1110,6 +1110,7 @@ function select_group_chats(groupId, skipAnimation) {
$("#rm_group_submit").hide();
$("#rm_group_delete").show();
$("#rm_group_scenario").show();
$('#group-metadata-controls .chat_lorebook_button').removeClass('disabled').prop('disabled', false);
} else {
$("#rm_group_submit").show();
if ($("#groupAddMemberListToggle .inline-drawer-content").css('display') !== 'block') {
@ -1117,6 +1118,7 @@ function select_group_chats(groupId, skipAnimation) {
}
$("#rm_group_delete").hide();
$("#rm_group_scenario").hide();
$('#group-metadata-controls .chat_lorebook_button').addClass('disabled').prop('disabled', true);
}
updateFavButtonState(group?.fav ?? false);

View File

@ -1,4 +1,4 @@
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPrompt, MAX_INJECTION_DEPTH, extension_prompt_types, getExtensionPromptByName } from "../script.js";
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPrompt, MAX_INJECTION_DEPTH, extension_prompt_types, getExtensionPromptByName, saveMetadata, getCurrentChatId } from "../script.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition } from "./utils.js";
import { extension_settings, getContext } from "./extensions.js";
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js";
@ -53,6 +53,7 @@ let updateEditor = (navigation) => { navigation; };
// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data.
const worldInfoFilter = new FilterHelper(() => updateEditor());
const SORT_ORDER_KEY = 'world_info_sort_order';
const METADATA_KEY = 'world_info';
const InputWidthReference = $("#WIInputWidthReference");
@ -167,6 +168,11 @@ function setWorldInfoSettings(settings, data) {
$('#world_info_sort_order').val(localStorage.getItem(SORT_ORDER_KEY) || '0');
$("#world_editor_select").trigger("change");
eventSource.on(event_types.CHAT_CHANGED, () => {
const hasWorldInfo = !!chat_metadata[METADATA_KEY] && world_names.includes(chat_metadata[METADATA_KEY]);
$('.chat_lorebook_button').toggleClass('world_set', hasWorldInfo);
});
}
// World Info Editor
@ -1301,10 +1307,26 @@ async function getGlobalLore() {
return entries;
}
async function getChatLore() {
const chatWorld = chat_metadata[METADATA_KEY];
if (!chatWorld) {
return [];
}
const data = await loadWorldInfoData(chatWorld);
const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]) : [];
console.debug(`Chat lore has ${entries.length} entries`);
return entries;
}
async function getSortedEntries() {
try {
const globalLore = await getGlobalLore();
const characterLore = await getCharacterLore();
const chatLore = await getChatLore();
let entries;
@ -1327,6 +1349,9 @@ async function getSortedEntries() {
break;
}
// Chat lore always goes first
entries = [...chatLore.sort(sortFn), ...entries];
console.debug(`Sorted ${entries.length} world lore entries using strategy ${world_info_character_strategy}`);
// Need to deep clone the entries to avoid modifying the cached data
@ -1911,6 +1936,39 @@ export async function importWorldInfo(file) {
});
}
function assignLorebookToChat() {
const selectedName = chat_metadata[METADATA_KEY];
const template = $('#chat_world_template .chat_world').clone();
const worldSelect = template.find('select');
const chatName = template.find('.chat_name');
chatName.text(getCurrentChatId());
for (const worldName of world_names) {
const option = document.createElement('option');
option.value = worldName;
option.innerText = worldName;
option.selected = selectedName === worldName;
worldSelect.append(option);
}
worldSelect.on('change', function () {
const worldName = $(this).val();
if (worldName) {
chat_metadata[METADATA_KEY] = worldName;
$('.chat_lorebook_button').addClass('world_set');
} else {
delete chat_metadata[METADATA_KEY];
$('.chat_lorebook_button').removeClass('world_set');
}
saveMetadata();
});
callPopup(template, 'text');
}
jQuery(() => {
$(document).ready(function () {
@ -2051,6 +2109,8 @@ jQuery(() => {
updateEditor(navigation_option.none);
})
$(document).on('click', '.chat_lorebook_button', assignLorebookToChat);
// Not needed on mobile
const deviceInfo = getDeviceInfo();
if (deviceInfo && deviceInfo.device.type === 'desktop') {

View File

@ -1316,7 +1316,7 @@ select option:not(:checked) {
}
.menu_button.disabled {
filter: brightness(50%);
filter: brightness(75%) grayscale(1);
cursor: not-allowed;
}
@ -1911,10 +1911,10 @@ grammarly-extension {
font-weight: bold;
padding: 5px;
margin: 0;
height: 32px;
height: 26px;
filter: grayscale(0.5);
text-align: center;
font-size: 20px;
font-size: 17px;
aspect-ratio: 1 / 1;
}
@ -2307,7 +2307,7 @@ input[type="range"]::-webkit-slider-thumb {
#char-management-dropdown,
#tagInput {
height: 32px;
height: 26px;
margin-bottom: 0;
}

View File

@ -1811,15 +1811,18 @@ function convertWorldInfoToCharacterBook(name, entries) {
}
function readWorldInfoFile(worldInfoName) {
const dummyObject = { entries: {} };
if (!worldInfoName) {
return { entries: {} };
return dummyObject;
}
const filename = `${worldInfoName}.json`;
const pathToWorldInfo = path.join(DIRECTORIES.worlds, filename);
if (!fs.existsSync(pathToWorldInfo)) {
throw new Error(`World info file ${filename} doesn't exist.`);
console.log(`World info file ${filename} doesn't exist.`);
return dummyObject;
}
const worldInfoText = fs.readFileSync(pathToWorldInfo, 'utf8');