Chat without character selected (#2757) * WIP: Enable chatting without character selected * Add burger menu item * Neutral assistant chat space * Revert "Neutral assistant chat space" This reverts commit be2da4a629fb8cfef2f7387272eec9d88b9c4328. * Revert "Add burger menu item" This reverts commit d4e8d990c1ae847a4bc18ee0d4b886cbcd065b7f. * Use simple ephemeral chats * Only start a new assistant chat if not already in it * Don't emit event not to clear pending files * Allow edits, disable checkpoints and branches * Exclude from tags, allow copy * Allow hide/unhide * Allow zooming avatar * Suppress warning * Rename neutral character * Rename neutral back to Assistant

f7de4953210bc611ca272f6ba64631d629c7cc53

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
5 files changed, +77 -30Showing whitespace changes
public/script.js+66 -28
@@ -484,9 +484,10 @@ const promptStorage = new localforage.createInstance({ name: 'SillyTavern_Prompt
484export let itemizedPrompts = [];484export let itemizedPrompts = [];
485485
486export const systemUserName = 'SillyTavern System';486export const systemUserName = 'SillyTavern System';
487export const neutralCharacterName = 'Assistant';
487let default_user_name = 'User';488let default_user_name = 'User';
488export let name1 = default_user_name;489export let name1 = default_user_name;
489export let name2 = 'SillyTavern System';490export let name2 = systemUserName;
490export let chat = [];491export let chat = [];
491let chatSaveTimeout;492let chatSaveTimeout;
492let importFlashTimeout;493let importFlashTimeout;
@@ -563,6 +564,8 @@ export const system_message_types = {
563 FORMATTING: 'formatting',564 FORMATTING: 'formatting',
564 HOTKEYS: 'hotkeys',565 HOTKEYS: 'hotkeys',
565 MACROS: 'macros',566 MACROS: 'macros',
567 WELCOME_PROMPT: 'welcome_prompt',
568 ASSISTANT_NOTE: 'assistant_note',
566};569};
567570
568/**571/**
@@ -680,6 +683,26 @@ async function getSystemMessages() {
680 is_system: true,683 is_system: true,
681 mes: 'Click here to return to the previous chat: <a class="bookmark_link" file_name="{0}" href="javascript:void(null);">Return</a>',684 mes: 'Click here to return to the previous chat: <a class="bookmark_link" file_name="{0}" href="javascript:void(null);">Return</a>',
682 },685 },
686 welcome_prompt: {
687 name: systemUserName,
688 force_avatar: system_avatar,
689 is_user: false,
690 is_system: true,
691 mes: await renderTemplateAsync('welcomePrompt'),
692 extra: {
693 isSmallSys: true,
694 },
695 },
696 assistant_note: {
697 name: systemUserName,
698 force_avatar: system_avatar,
699 is_user: false,
700 is_system: true,
701 mes: await renderTemplateAsync('assistantNote'),
702 extra: {
703 isSmallSys: true,
704 },
705 },
683 };706 };
684}707}
685708
@@ -924,6 +947,7 @@ async function firstLoadInit() {
924 initTextGenModels();947 initTextGenModels();
925 await getSystemMessages();948 await getSystemMessages();
926 sendSystemMessage(system_message_types.WELCOME);949 sendSystemMessage(system_message_types.WELCOME);
950 sendSystemMessage(system_message_types.WELCOME_PROMPT);
927 await getSettings();951 await getSettings();
928 initKeyboard();952 initKeyboard();
929 initDynamicStyles();953 initDynamicStyles();
@@ -1849,7 +1873,7 @@ export async function reloadCurrentChat() {
1849/**1873/**
1850 * Send the message currently typed into the chat box.1874 * Send the message currently typed into the chat box.
1851 */1875 */
1852export function sendTextareaMessage() {1876export async function sendTextareaMessage() {
1853 if (is_send_press) return;1877 if (is_send_press) return;
1854 if (isExecutingCommandsFromChatInput) return;1878 if (isExecutingCommandsFromChatInput) return;
18551879
@@ -1867,6 +1891,10 @@ export function sendTextareaMessage() {
1867 generateType = 'continue';1891 generateType = 'continue';
1868 }1892 }
18691893
1894 if (textareaText && !selected_group && this_chid === undefined && name2 !== neutralCharacterName) {
1895 await newAssistantChat();
1896 }
1897
1870 Generate(generateType);1898 Generate(generateType);
1871}1899}
18721900
@@ -1946,9 +1974,7 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
1946 mes = mes.replaceAll('<', '&lt;').replaceAll('>', '&gt;');1974 mes = mes.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
1947 }1975 }
19481976
1949 if (this_chid === undefined && !selected_group) {1977 if (!isSystem) {
1950 mes = mes.replace(/\*\*(.+?)\*\*/g, '<b>$1</b>');
1951 } else if (!isSystem) {
1952 // Save double quotes in tags as a special character to prevent them from being encoded1978 // Save double quotes in tags as a special character to prevent them from being encoded
1953 if (!power_user.encode_tags) {1979 if (!power_user.encode_tags) {
1954 mes = mes.replace(/<([^>]+)>/g, function (_, contents) {1980 mes = mes.replace(/<([^>]+)>/g, function (_, contents) {
@@ -1987,13 +2013,6 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) {
1987 });2013 });
1988 }2014 }
19892015
1990 /*
1991 // Hides bias from empty messages send with slash commands
1992 if (isSystem) {
1993 mes = mes.replace(/\{\{[\s\S]*?\}\}/gm, "");
1994 }
1995 */
1996
1997 if (!power_user.allow_name2_display && ch_name && !isUser && !isSystem) {2016 if (!power_user.allow_name2_display && ch_name && !isUser && !isSystem) {
1998 mes = mes.replace(new RegExp(`(^|\n)${escapeRegex(ch_name)}:`, 'g'), '$1');2017 mes = mes.replace(new RegExp(`(^|\n)${escapeRegex(ch_name)}:`, 'g'), '$1');
1999 }2018 }
@@ -3400,14 +3419,11 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3400 quiet_prompt = main_api == 'novel' && !quietToLoud ? adjustNovelInstructionPrompt(quiet_prompt) : quiet_prompt;3419 quiet_prompt = main_api == 'novel' && !quietToLoud ? adjustNovelInstructionPrompt(quiet_prompt) : quiet_prompt;
3401 }3420 }
34023421
3403 const isChatValid = online_status !== 'no_connection' && this_chid !== undefined;3422 const hasBackendConnection = online_status !== 'no_connection';
34043423
3405 // We can't do anything because we're not in a chat right now. (Unless it's a dry run, in which case we need to3424 // We can't do anything because we're not in a chat right now. (Unless it's a dry run, in which case we need to
3406 // assemble the prompt so we can count its tokens regardless of whether a chat is active.)3425 // assemble the prompt so we can count its tokens regardless of whether a chat is active.)
3407 if (!dryRun && !isChatValid) {3426 if (!dryRun && !hasBackendConnection) {
3408 if (this_chid === undefined) {
3409 toastr.warning('Сharacter is not selected');
3410 }
3411 is_send_press = false;3427 is_send_press = false;
3412 return Promise.resolve();3428 return Promise.resolve();
3413 }3429 }
@@ -3493,9 +3509,9 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
3493 setExtensionPrompt('DEPTH_PROMPT_' + index, value.text, extension_prompt_types.IN_CHAT, value.depth, extension_settings.note.allowWIScan, role);3509 setExtensionPrompt('DEPTH_PROMPT_' + index, value.text, extension_prompt_types.IN_CHAT, value.depth, extension_settings.note.allowWIScan, role);
3494 });3510 });
3495 } else {3511 } else {
3496 const depthPromptText = baseChatReplace(characters[this_chid].data?.extensions?.depth_prompt?.prompt?.trim(), name1, name2) || '';3512 const depthPromptText = baseChatReplace(characters[this_chid]?.data?.extensions?.depth_prompt?.prompt?.trim(), name1, name2) || '';
3497 const depthPromptDepth = characters[this_chid].data?.extensions?.depth_prompt?.depth ?? depth_prompt_depth_default;3513 const depthPromptDepth = characters[this_chid]?.data?.extensions?.depth_prompt?.depth ?? depth_prompt_depth_default;
3498 const depthPromptRole = getExtensionPromptRoleByName(characters[this_chid].data?.extensions?.depth_prompt?.role ?? depth_prompt_role_default);3514 const depthPromptRole = getExtensionPromptRoleByName(characters[this_chid]?.data?.extensions?.depth_prompt?.role ?? depth_prompt_role_default);
3499 setExtensionPrompt('DEPTH_PROMPT', depthPromptText, extension_prompt_types.IN_CHAT, depthPromptDepth, extension_settings.note.allowWIScan, depthPromptRole);3515 setExtensionPrompt('DEPTH_PROMPT', depthPromptText, extension_prompt_types.IN_CHAT, depthPromptDepth, extension_settings.note.allowWIScan, depthPromptRole);
3500 }3516 }
35013517
@@ -5905,11 +5921,16 @@ export function saveChatDebounced() {
5905 }, 1000);5921 }, 1000);
5906}5922}
59075923
5908export async function saveChat(chat_name, withMetadata, mesId) {5924export async function saveChat(chatName, withMetadata, mesId) {
5909 const metadata = { ...chat_metadata, ...(withMetadata || {}) };5925 const metadata = { ...chat_metadata, ...(withMetadata || {}) };
5910 let file_name = chat_name ?? characters[this_chid]?.chat;5926 const fileName = chatName ?? characters[this_chid]?.chat;
59115927
5912 if (!file_name) {5928 if (!fileName && name2 === neutralCharacterName) {
5929 // TODO: Do something for a temporary chat with no character.
5930 return;
5931 }
5932
5933 if (!fileName) {
5913 console.warn('saveChat called without chat_name and no chat file found');5934 console.warn('saveChat called without chat_name and no chat file found');
5914 return;5935 return;
5915 }5936 }
@@ -5950,7 +5971,7 @@ export async function saveChat(chat_name, withMetadata, mesId) {
5950 url: '/api/chats/save',5971 url: '/api/chats/save',
5951 data: JSON.stringify({5972 data: JSON.stringify({
5952 ch_name: characters[this_chid].name,5973 ch_name: characters[this_chid].name,
5953 file_name: file_name,5974 file_name: fileName,
5954 chat: save_chat,5975 chat: save_chat,
5955 avatar_url: characters[this_chid].avatar,5976 avatar_url: characters[this_chid].avatar,
5956 }),5977 }),
@@ -6665,7 +6686,7 @@ function updateMessage(div) {
6665function openMessageDelete(fromSlashCommand) {6686function openMessageDelete(fromSlashCommand) {
6666 closeMessageEditor();6687 closeMessageEditor();
6667 hideSwipeButtons();6688 hideSwipeButtons();
6668 if (fromSlashCommand || (this_chid != undefined && !is_send_press) || (selected_group && !is_group_generating)) {6689 if (fromSlashCommand || (!is_send_press) || (selected_group && !is_group_generating)) {
6669 $('#dialogue_del_mes').css('display', 'block');6690 $('#dialogue_del_mes').css('display', 'block');
6670 $('#send_form').css('display', 'none');6691 $('#send_form').css('display', 'none');
6671 $('.del_checkbox').each(function () {6692 $('.del_checkbox').each(function () {
@@ -8218,6 +8239,11 @@ function swipe_left() { // when we swipe left..but no generation.
8218 * @returns {Promise<string>} Branch file name8239 * @returns {Promise<string>} Branch file name
8219 */8240 */
8220async function branchChat(mesId) {8241async function branchChat(mesId) {
8242 if (this_chid === undefined && !selected_group) {
8243 toastr.info('No character selected.', 'Branch creation aborted');
8244 return;
8245 }
8246
8221 const fileName = await createBranch(mesId);8247 const fileName = await createBranch(mesId);
8222 await saveItemizedPrompts(fileName);8248 await saveItemizedPrompts(fileName);
82238249
@@ -8955,6 +8981,14 @@ async function removeCharacterFromUI() {
8955 saveSettingsDebounced();8981 saveSettingsDebounced();
8956}8982}
89578983
8984async function newAssistantChat() {
8985 await clearChat();
8986 chat.splice(0, chat.length);
8987 chat_metadata = {};
8988 setCharacterName(neutralCharacterName);
8989 sendSystemMessage(system_message_types.ASSISTANT_NOTE);
8990}
8991
8958function doTogglePanels() {8992function doTogglePanels() {
8959 $('#option_settings').trigger('click');8993 $('#option_settings').trigger('click');
8960 return '';8994 return '';
@@ -9800,6 +9834,9 @@ jQuery(async function () {
98009834
9801 await doNewChat({ deleteCurrentChat: deleteCurrentChat });9835 await doNewChat({ deleteCurrentChat: deleteCurrentChat });
9802 }9836 }
9837 if (!selected_group && this_chid === undefined && !is_send_press) {
9838 await newAssistantChat();
9839 }
9803 }9840 }
98049841
9805 else if (id == 'option_regenerate') {9842 else if (id == 'option_regenerate') {
@@ -9850,6 +9887,7 @@ jQuery(async function () {
9850 $('#rm_button_selected_ch').children('h2').text('');9887 $('#rm_button_selected_ch').children('h2').text('');
9851 select_rm_characters();9888 select_rm_characters();
9852 sendSystemMessage(system_message_types.WELCOME);9889 sendSystemMessage(system_message_types.WELCOME);
9890 sendSystemMessage(system_message_types.WELCOME_PROMPT);
9853 eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());9891 eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
9854 await getClientVersion();9892 await getClientVersion();
9855 } else {9893 } else {
@@ -10048,7 +10086,7 @@ jQuery(async function () {
10048 }10086 }
10049 else {10087 else {
10050 $(document).on('pointerup', '.mes_copy', function () {10088 $(document).on('pointerup', '.mes_copy', function () {
10051 if (this_chid !== undefined || selected_group) {10089 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {
10052 try {10090 try {
10053 const messageId = $(this).closest('.mes').attr('mesid');10091 const messageId = $(this).closest('.mes').attr('mesid');
10054 const text = chat[messageId]['mes'];10092 const text = chat[messageId]['mes'];
@@ -10072,7 +10110,7 @@ jQuery(async function () {
10072 //********************10110 //********************
10073 //***Message Editor***10111 //***Message Editor***
10074 $(document).on('click', '.mes_edit', async function () {10112 $(document).on('click', '.mes_edit', async function () {
10075 if (this_chid !== undefined || selected_group) {10113 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {
10076 // Previously system messages we're allowed to be edited10114 // Previously system messages we're allowed to be edited
10077 /*const message = $(this).closest(".mes");10115 /*const message = $(this).closest(".mes");
1007810116
@@ -10664,7 +10702,7 @@ jQuery(async function () {
10664 });10702 });
10665 }10703 }
1066610704
10667 const avatarSrc = isDataURL(thumbURL) ? thumbURL : charsPath + targetAvatarImg;10705 const avatarSrc = (isDataURL(thumbURL) || /^\/?img\/(?:.+)/.test(thumbURL)) ? thumbURL : charsPath + targetAvatarImg;
10668 if ($(`.zoomed_avatar[forChar="${charname}"]`).length) {10706 if ($(`.zoomed_avatar[forChar="${charname}"]`).length) {
10669 console.debug('removing container as it already existed');10707 console.debug('removing container as it already existed');
10670 $(`.zoomed_avatar[forChar="${charname}"]`).fadeOut(animation_duration, () => {10708 $(`.zoomed_avatar[forChar="${charname}"]`).fadeOut(animation_duration, () => {
public/scripts/bookmarks.js+5 -0
@@ -168,6 +168,11 @@ export async function createBranch(mesId) {
168}168}
169169
170async function createNewBookmark(mesId) {170async function createNewBookmark(mesId) {
171 if (this_chid === undefined && !selected_group) {
172 toastr.info('No character selected.', 'Checkpoint creation aborted');
173 return;
174 }
175
171 if (!chat.length) {176 if (!chat.length) {
172 toastr.warning('The chat is empty.', 'Checkpoint creation failed');177 toastr.warning('The chat is empty.', 'Checkpoint creation failed');
173 return;178 return;
public/scripts/chats.js+0 -2
@@ -125,8 +125,6 @@ function getConverter(type) {
125 * @returns {Promise<void>}125 * @returns {Promise<void>}
126 */126 */
127export async function hideChatMessageRange(start, end, unhide) {127export async function hideChatMessageRange(start, end, unhide) {
128 if (!getCurrentChatId()) return;
129
130 if (isNaN(start)) return;128 if (isNaN(start)) return;
131 if (!end) end = start;129 if (!end) end = start;
132 const hide = !unhide;130 const hide = !unhide;
public/scripts/templates/assistantNote.html+3 -0
@@ -0,0 +1,3 @@
1<div>
2 <b>Note:</b> this chat is temporary and will be deleted as soon as you leave it.
3</div>
public/scripts/templates/welcomePrompt.html+3 -0
@@ -0,0 +1,3 @@
1<strong data-i18n="Connect to an API and ask me anything!">
2 Connect to an API and ask me anything!
3</strong>