mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-27 01:10:14 +01:00
Add shallow character load mode
This commit is contained in:
parent
70f762c006
commit
383806325a
@ -4424,6 +4424,13 @@
|
|||||||
<option data-i18n="tag_import_existing" value="4">Existing</option>
|
<option data-i18n="tag_import_existing" value="4">Existing</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<label class="checkbox_label" for="shallow_characters" data-i18n="[title]Experimental feature. May behave unstable and have compatibility issues." title="Experimental feature. May behave unstable and have compatibility issues.">
|
||||||
|
<input id="shallow_characters" type="checkbox" />
|
||||||
|
<small data-i18n="Lazy Load Characters">
|
||||||
|
Lazy Load Characters
|
||||||
|
</small>
|
||||||
|
<i class="fa-solid fa-flask"></i>
|
||||||
|
</label>
|
||||||
<label class="checkbox_label" for="fuzzy_search_checkbox" title="Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring." data-i18n="[title]Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring">
|
<label class="checkbox_label" for="fuzzy_search_checkbox" title="Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring." data-i18n="[title]Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring">
|
||||||
<input id="fuzzy_search_checkbox" type="checkbox" />
|
<input id="fuzzy_search_checkbox" type="checkbox" />
|
||||||
<small data-i18n="Advanced Character Search">Advanced Character Search</small>
|
<small data-i18n="Advanced Character Search">Advanced Character Search</small>
|
||||||
|
@ -1781,7 +1781,7 @@ export async function getCharacters() {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: getRequestHeaders(),
|
headers: getRequestHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
'': '',
|
shallow: power_user.shallow_characters,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (response.ok === true) {
|
if (response.ok === true) {
|
||||||
@ -3658,6 +3658,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
|
|||||||
setGenerationProgress(0);
|
setGenerationProgress(0);
|
||||||
generation_started = new Date();
|
generation_started = new Date();
|
||||||
|
|
||||||
|
// Prevent generation from shallow characters
|
||||||
|
await unshallowCharacter(this_chid);
|
||||||
|
|
||||||
// Occurs every time, even if the generation is aborted due to slash commands execution
|
// Occurs every time, even if the generation is aborted due to slash commands execution
|
||||||
await eventSource.emit(event_types.GENERATION_STARTED, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
|
await eventSource.emit(event_types.GENERATION_STARTED, type, { automatic_trigger, force_name2, quiet_prompt, quietToLoud, skipWIAN, force_chid, signal, quietImage }, dryRun);
|
||||||
|
|
||||||
@ -6704,9 +6707,42 @@ export function buildAvatarList(block, entities, { templateId = 'inline_avatar_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all the data of a shallow character.
|
||||||
|
* @param {string|undefined} characterId Array index
|
||||||
|
* @returns {Promise<void>} Promise that resolves when the character is unshallowed
|
||||||
|
*/
|
||||||
|
export async function unshallowCharacter(characterId) {
|
||||||
|
if (characterId === undefined) {
|
||||||
|
console.warn('Undefined character cannot be unshallowed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const character = characters[characterId];
|
||||||
|
if (!character) {
|
||||||
|
console.warn('Character not found:', characterId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Character is not shallow
|
||||||
|
if (!character.shallow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const avatar = character.avatar;
|
||||||
|
if (!avatar) {
|
||||||
|
console.warn('Character has no avatar field:', characterId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await getOneCharacter(avatar);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getChat() {
|
export async function getChat() {
|
||||||
//console.log('/api/chats/get -- entered for -- ' + characters[this_chid].name);
|
//console.log('/api/chats/get -- entered for -- ' + characters[this_chid].name);
|
||||||
try {
|
try {
|
||||||
|
await unshallowCharacter(this_chid);
|
||||||
|
|
||||||
const response = await $.ajax({
|
const response = await $.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: '/api/chats/get',
|
url: '/api/chats/get',
|
||||||
|
@ -72,6 +72,7 @@ import {
|
|||||||
animation_duration,
|
animation_duration,
|
||||||
depth_prompt_role_default,
|
depth_prompt_role_default,
|
||||||
shouldAutoContinue,
|
shouldAutoContinue,
|
||||||
|
unshallowCharacter,
|
||||||
} from '../script.js';
|
} from '../script.js';
|
||||||
import { printTagList, createTagMapFromList, applyTagsOnCharacterSelect, tag_map, applyTagsOnGroupSelect } from './tags.js';
|
import { printTagList, createTagMapFromList, applyTagsOnCharacterSelect, tag_map, applyTagsOnGroupSelect } from './tags.js';
|
||||||
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
||||||
@ -216,6 +217,7 @@ export async function getGroupChat(groupId, reload = false) {
|
|||||||
|
|
||||||
// Run validation before any loading
|
// Run validation before any loading
|
||||||
validateGroup(group);
|
validateGroup(group);
|
||||||
|
await unshallowGroupMembers(groupId);
|
||||||
|
|
||||||
const chat_id = group.chat_id;
|
const chat_id = group.chat_id;
|
||||||
const data = await loadGroupChat(chat_id);
|
const data = await loadGroupChat(chat_id);
|
||||||
@ -824,6 +826,8 @@ async function generateGroupWrapper(by_auto_mode, type = null, params = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await unshallowGroupMembers(selected_group);
|
||||||
|
|
||||||
throwIfAborted();
|
throwIfAborted();
|
||||||
hideSwipeButtons();
|
hideSwipeButtons();
|
||||||
is_group_generating = true;
|
is_group_generating = true;
|
||||||
@ -1137,6 +1141,29 @@ export async function editGroup(id, immediately, reload = true) {
|
|||||||
saveGroupDebounced(group, reload);
|
saveGroupDebounced(group, reload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unshallows all definitions of group members.
|
||||||
|
* @param {string} groupId Id of the group
|
||||||
|
* @returns {Promise<void>} Promise that resolves when all group members are unshallowed
|
||||||
|
*/
|
||||||
|
export async function unshallowGroupMembers(groupId) {
|
||||||
|
const group = groups.find(x => x.id == groupId);
|
||||||
|
if (!group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const members = group.members;
|
||||||
|
if (!Array.isArray(members)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const member of members) {
|
||||||
|
const index = characters.findIndex(x => x.avatar === member);
|
||||||
|
if (index === -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await unshallowCharacter(String(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let groupAutoModeAbortController = null;
|
let groupAutoModeAbortController = null;
|
||||||
|
|
||||||
async function groupChatAutoModeWorker() {
|
async function groupChatAutoModeWorker() {
|
||||||
@ -1158,9 +1185,9 @@ async function groupChatAutoModeWorker() {
|
|||||||
await generateGroupWrapper(true, 'auto', { signal: groupAutoModeAbortController.signal });
|
await generateGroupWrapper(true, 'auto', { signal: groupAutoModeAbortController.signal });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function modifyGroupMember(chat_id, groupMember, isDelete) {
|
async function modifyGroupMember(groupId, groupMember, isDelete) {
|
||||||
const id = groupMember.data('id');
|
const id = groupMember.data('id');
|
||||||
const thisGroup = groups.find((x) => x.id == chat_id);
|
const thisGroup = groups.find((x) => x.id == groupId);
|
||||||
const membersArray = thisGroup?.members ?? newGroupMembers;
|
const membersArray = thisGroup?.members ?? newGroupMembers;
|
||||||
|
|
||||||
if (isDelete) {
|
if (isDelete) {
|
||||||
@ -1173,6 +1200,7 @@ async function modifyGroupMember(chat_id, groupMember, isDelete) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (openGroupId) {
|
if (openGroupId) {
|
||||||
|
await unshallowGroupMembers(openGroupId);
|
||||||
await editGroup(openGroupId, false, false);
|
await editGroup(openGroupId, false, false);
|
||||||
updateGroupAvatar(thisGroup);
|
updateGroupAvatar(thisGroup);
|
||||||
}
|
}
|
||||||
@ -1638,7 +1666,7 @@ async function onGroupActionClick(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'view') {
|
if (action === 'view') {
|
||||||
openCharacterDefinition(member);
|
await openCharacterDefinition(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'speak') {
|
if (action === 'speak') {
|
||||||
@ -1690,7 +1718,7 @@ export async function openGroupById(groupId) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCharacterDefinition(characterSelect) {
|
async function openCharacterDefinition(characterSelect) {
|
||||||
if (is_group_generating) {
|
if (is_group_generating) {
|
||||||
toastr.warning(t`Can't peek a character while group reply is being generated`);
|
toastr.warning(t`Can't peek a character while group reply is being generated`);
|
||||||
console.warn('Can\'t peek a character def while group reply is being generated');
|
console.warn('Can\'t peek a character def while group reply is being generated');
|
||||||
@ -1703,6 +1731,7 @@ function openCharacterDefinition(characterSelect) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await unshallowCharacter(chid);
|
||||||
setCharacterId(chid);
|
setCharacterId(chid);
|
||||||
select_selected_character(chid);
|
select_selected_character(chid);
|
||||||
// Gentle nudge to recalculate tokens
|
// Gentle nudge to recalculate tokens
|
||||||
|
@ -314,6 +314,7 @@ let power_user = {
|
|||||||
forbid_external_media: true,
|
forbid_external_media: true,
|
||||||
external_media_allowed_overrides: [],
|
external_media_allowed_overrides: [],
|
||||||
external_media_forbidden_overrides: [],
|
external_media_forbidden_overrides: [],
|
||||||
|
shallow_characters: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let themes = [];
|
let themes = [];
|
||||||
@ -1595,6 +1596,7 @@ async function loadPowerUserSettings(settings, data) {
|
|||||||
$('#auto-connect-checkbox').prop('checked', power_user.auto_connect);
|
$('#auto-connect-checkbox').prop('checked', power_user.auto_connect);
|
||||||
$('#auto-load-chat-checkbox').prop('checked', power_user.auto_load_chat);
|
$('#auto-load-chat-checkbox').prop('checked', power_user.auto_load_chat);
|
||||||
$('#forbid_external_media').prop('checked', power_user.forbid_external_media);
|
$('#forbid_external_media').prop('checked', power_user.forbid_external_media);
|
||||||
|
$('#shallow_characters').prop('checked', power_user.shallow_characters);
|
||||||
|
|
||||||
for (const theme of themes) {
|
for (const theme of themes) {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
@ -3888,6 +3890,12 @@ $(document).ready(() => {
|
|||||||
await exportTheme();
|
await exportTheme();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#shallow_characters').on('input', function () {
|
||||||
|
power_user.shallow_characters = !!$(this).prop('checked');
|
||||||
|
saveSettingsDebounced();
|
||||||
|
toastr.info('Reload the page for this setting to take effect');
|
||||||
|
});
|
||||||
|
|
||||||
$(document).on('click', '#debug_table [data-debug-function]', function () {
|
$(document).on('click', '#debug_table [data-debug-function]', function () {
|
||||||
const functionId = $(this).data('debug-function');
|
const functionId = $(this).data('debug-function');
|
||||||
const functionRecord = debug_functions.find(f => f.functionId === functionId);
|
const functionRecord = debug_functions.find(f => f.functionId === functionId);
|
||||||
|
@ -47,6 +47,7 @@ import {
|
|||||||
updateMessageBlock,
|
updateMessageBlock,
|
||||||
printMessages,
|
printMessages,
|
||||||
clearChat,
|
clearChat,
|
||||||
|
unshallowCharacter,
|
||||||
} from '../script.js';
|
} from '../script.js';
|
||||||
import {
|
import {
|
||||||
extension_settings,
|
extension_settings,
|
||||||
@ -55,7 +56,7 @@ import {
|
|||||||
renderExtensionTemplateAsync,
|
renderExtensionTemplateAsync,
|
||||||
writeExtensionField,
|
writeExtensionField,
|
||||||
} from './extensions.js';
|
} from './extensions.js';
|
||||||
import { groups, openGroupChat, selected_group } from './group-chats.js';
|
import { groups, openGroupChat, selected_group, unshallowGroupMembers } from './group-chats.js';
|
||||||
import { addLocaleData, getCurrentLocale, t, translate } from './i18n.js';
|
import { addLocaleData, getCurrentLocale, t, translate } from './i18n.js';
|
||||||
import { hideLoader, showLoader } from './loader.js';
|
import { hideLoader, showLoader } from './loader.js';
|
||||||
import { MacrosParser } from './macros.js';
|
import { MacrosParser } from './macros.js';
|
||||||
@ -210,6 +211,8 @@ export function getContext() {
|
|||||||
clearChat,
|
clearChat,
|
||||||
ChatCompletionService,
|
ChatCompletionService,
|
||||||
TextCompletionService,
|
TextCompletionService,
|
||||||
|
unshallowCharacter,
|
||||||
|
unshallowGroupMembers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,14 +200,45 @@ const calculateDataSize = (data) => {
|
|||||||
return typeof data === 'object' ? Object.values(data).reduce((acc, val) => acc + String(val).length, 0) : 0;
|
return typeof data === 'object' ? Object.values(data).reduce((acc, val) => acc + String(val).length, 0) : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only get fields that are used to display the character list.
|
||||||
|
* @param {object} character Character object
|
||||||
|
* @returns {{shallow: true, [key: string]: any}} Shallow character
|
||||||
|
*/
|
||||||
|
const toShallow = (character) => {
|
||||||
|
return {
|
||||||
|
name: character.name,
|
||||||
|
avatar: character.avatar,
|
||||||
|
chat: character.chat,
|
||||||
|
fav: character.fav,
|
||||||
|
date_added: character.date_added,
|
||||||
|
create_date: character.create_date,
|
||||||
|
date_last_chat: character.date_last_chat,
|
||||||
|
chat_size: character.chat_size,
|
||||||
|
data_size: character.data_size,
|
||||||
|
data: {
|
||||||
|
name: _.get(character, 'data.name', ''),
|
||||||
|
character_version: _.get(character, 'data.character_version', ''),
|
||||||
|
creator: _.get(character, 'data.creator', ''),
|
||||||
|
creator_notes: _.get(character, 'data.creator_notes', ''),
|
||||||
|
extensions: {
|
||||||
|
fav: _.get(character, 'data.extensions.fav', false),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shallow: true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* processCharacter - Process a given character, read its data and calculate its statistics.
|
* processCharacter - Process a given character, read its data and calculate its statistics.
|
||||||
*
|
*
|
||||||
* @param {string} item The name of the character.
|
* @param {string} item The name of the character.
|
||||||
* @param {import('../users.js').UserDirectoryList} directories User directories
|
* @param {import('../users.js').UserDirectoryList} directories User directories
|
||||||
|
* @param {object} options Options for the character processing
|
||||||
|
* @param {boolean} options.shallow If true, only return the core character's metadata
|
||||||
* @return {Promise<object>} A Promise that resolves when the character processing is done.
|
* @return {Promise<object>} A Promise that resolves when the character processing is done.
|
||||||
*/
|
*/
|
||||||
const processCharacter = async (item, directories) => {
|
const processCharacter = async (item, directories, { shallow }) => {
|
||||||
try {
|
try {
|
||||||
const imgFile = path.join(directories.characters, item);
|
const imgFile = path.join(directories.characters, item);
|
||||||
const imgData = await readCharacterData(imgFile);
|
const imgData = await readCharacterData(imgFile);
|
||||||
@ -226,7 +257,7 @@ const processCharacter = async (item, directories) => {
|
|||||||
character['chat_size'] = chatSize;
|
character['chat_size'] = chatSize;
|
||||||
character['date_last_chat'] = dateLastChat;
|
character['date_last_chat'] = dateLastChat;
|
||||||
character['data_size'] = calculateDataSize(jsonObject?.data);
|
character['data_size'] = calculateDataSize(jsonObject?.data);
|
||||||
return character;
|
return shallow ? toShallow(character) : character;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.error(`Could not process character: ${item}`);
|
console.error(`Could not process character: ${item}`);
|
||||||
@ -991,9 +1022,10 @@ router.post('/delete', jsonParser, validateAvatarUrlMiddleware, async function (
|
|||||||
*/
|
*/
|
||||||
router.post('/all', jsonParser, async function (request, response) {
|
router.post('/all', jsonParser, async function (request, response) {
|
||||||
try {
|
try {
|
||||||
|
const shallow = !!request.body.shallow;
|
||||||
const files = fs.readdirSync(request.user.directories.characters);
|
const files = fs.readdirSync(request.user.directories.characters);
|
||||||
const pngFiles = files.filter(file => file.endsWith('.png'));
|
const pngFiles = files.filter(file => file.endsWith('.png'));
|
||||||
const processingPromises = pngFiles.map(file => processCharacter(file, request.user.directories));
|
const processingPromises = pngFiles.map(file => processCharacter(file, request.user.directories, { shallow }));
|
||||||
const data = (await Promise.all(processingPromises)).filter(c => c.name);
|
const data = (await Promise.all(processingPromises)).filter(c => c.name);
|
||||||
return response.send(data);
|
return response.send(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -1012,7 +1044,7 @@ router.post('/get', jsonParser, validateAvatarUrlMiddleware, async function (req
|
|||||||
return response.sendStatus(404);
|
return response.sendStatus(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await processCharacter(item, request.user.directories);
|
const data = await processCharacter(item, request.user.directories, { shallow: false });
|
||||||
|
|
||||||
return response.send(data);
|
return response.send(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user