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
Signed| @@ -484,9 +484,10 @@ const promptStorage = new localforage.createInstance({ name: 'SillyTavern_Prompt | |||
| 484 | export let itemizedPrompts = []; | 484 | export let itemizedPrompts = []; |
| 485 | 485 | ||
| 486 | export const systemUserName = 'SillyTavern System'; | 486 | export const systemUserName = 'SillyTavern System'; |
| 487 | export const neutralCharacterName = 'Assistant'; | ||
| 487 | let default_user_name = 'User'; | 488 | let default_user_name = 'User'; |
| 488 | export let name1 = default_user_name; | 489 | export let name1 = default_user_name; |
| 489 | export let name2 = 'SillyTavern System'; | 490 | export let name2 = systemUserName; |
| 490 | export let chat = []; | 491 | export let chat = []; |
| 491 | let chatSaveTimeout; | 492 | let chatSaveTimeout; |
| 492 | let importFlashTimeout; | 493 | let 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 | }; |
| 567 | 570 | ||
| 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 | } |
| 685 | 708 | ||
| @@ -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 | */ |
| 1852 | export function sendTextareaMessage() { | 1876 | export async function sendTextareaMessage() { |
| 1853 | if (is_send_press) return; | 1877 | if (is_send_press) return; |
| 1854 | if (isExecutingCommandsFromChatInput) return; | 1878 | if (isExecutingCommandsFromChatInput) return; |
| 1855 | 1879 | ||
| @@ -1867,6 +1891,10 @@ export function sendTextareaMessage() { | |||
| 1867 | generateType = 'continue'; | 1891 | generateType = 'continue'; |
| 1868 | } | 1892 | } |
| 1869 | 1893 | ||
| 1894 | if (textareaText && !selected_group && this_chid === undefined && name2 !== neutralCharacterName) { | ||
| 1895 | await newAssistantChat(); | ||
| 1896 | } | ||
| 1897 | |||
| 1870 | Generate(generateType); | 1898 | Generate(generateType); |
| 1871 | } | 1899 | } |
| 1872 | 1900 | ||
| @@ -1946,9 +1974,7 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId) { | |||
| 1946 | mes = mes.replaceAll('<', '<').replaceAll('>', '>'); | 1974 | mes = mes.replaceAll('<', '<').replaceAll('>', '>'); |
| 1947 | } | 1975 | } |
| 1948 | 1976 | ||
| 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 encoded | 1978 | // 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 | } |
| 1989 | 2015 | ||
| 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 | } |
| 3402 | 3421 | ||
| 3403 | const isChatValid = online_status !== 'no_connection' && this_chid !== undefined; | 3422 | const hasBackendConnection = online_status !== 'no_connection'; |
| 3404 | 3423 | ||
| 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 to | 3424 | // 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 | } |
| 3501 | 3517 | ||
| @@ -5905,11 +5921,16 @@ export function saveChatDebounced() { | |||
| 5905 | }, 1000); | 5921 | }, 1000); |
| 5906 | } | 5922 | } |
| 5907 | 5923 | ||
| 5908 | export async function saveChat(chat_name, withMetadata, mesId) { | 5924 | export 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; |
| 5911 | 5927 | ||
| 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) { | |||
| 6665 | function openMessageDelete(fromSlashCommand) { | 6686 | function 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 name | 8239 | * @returns {Promise<string>} Branch file name |
| 8219 | */ | 8240 | */ |
| 8220 | async function branchChat(mesId) { | 8241 | async 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); |
| 8223 | 8249 | ||
| @@ -8955,6 +8981,14 @@ async function removeCharacterFromUI() { | |||
| 8955 | saveSettingsDebounced(); | 8981 | saveSettingsDebounced(); |
| 8956 | } | 8982 | } |
| 8957 | 8983 | ||
| 8984 | async 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 | |||
| 8958 | function doTogglePanels() { | 8992 | function doTogglePanels() { |
| 8959 | $('#option_settings').trigger('click'); | 8993 | $('#option_settings').trigger('click'); |
| 8960 | return ''; | 8994 | return ''; |
| @@ -9800,6 +9834,9 @@ jQuery(async function () { | |||
| 9800 | 9834 | ||
| 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 | } |
| 9804 | 9841 | ||
| 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 edited | 10114 | // Previously system messages we're allowed to be edited |
| 10077 | /*const message = $(this).closest(".mes"); | 10115 | /*const message = $(this).closest(".mes"); |
| 10078 | 10116 | ||
| @@ -10664,7 +10702,7 @@ jQuery(async function () { | |||
| 10664 | }); | 10702 | }); |
| 10665 | } | 10703 | } |
| 10666 | 10704 | ||
| 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, () => { |
| @@ -168,6 +168,11 @@ export async function createBranch(mesId) { | |||
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | async function createNewBookmark(mesId) { | 170 | async 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; |
| @@ -125,8 +125,6 @@ function getConverter(type) { | |||
| 125 | * @returns {Promise<void>} | 125 | * @returns {Promise<void>} |
| 126 | */ | 126 | */ |
| 127 | export async function hideChatMessageRange(start, end, unhide) { | 127 | export 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; |
| @@ -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> | ||
| @@ -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> | ||