Neutral assistant chat space

This commit is contained in:
Cohee
2024-09-02 02:12:26 +03:00
parent 46a1ea6837
commit be2da4a629
4 changed files with 122 additions and 47 deletions

View File

@ -485,6 +485,7 @@ export let itemizedPrompts = [];
export const systemUserName = 'SillyTavern System'; export const systemUserName = 'SillyTavern System';
export const neutralCharacterName = 'Assistant'; export const neutralCharacterName = 'Assistant';
export const neutralCharacterAvatar = 'ST_your_ai_assistant.png';
let default_user_name = 'User'; let default_user_name = 'User';
export let name1 = default_user_name; export let name1 = default_user_name;
export let name2 = systemUserName; export let name2 = systemUserName;
@ -565,6 +566,7 @@ export const system_message_types = {
HOTKEYS: 'hotkeys', HOTKEYS: 'hotkeys',
MACROS: 'macros', MACROS: 'macros',
WELCOME_PROMPT: 'welcome_prompt', WELCOME_PROMPT: 'welcome_prompt',
PAST_CHAT_HINT: 'past_chat_hint',
}; };
/** /**
@ -692,6 +694,16 @@ async function getSystemMessages() {
isSmallSys: true, isSmallSys: true,
}, },
}, },
past_chat_hint: {
name: neutralCharacterName,
force_avatar: system_avatar,
is_user: false,
is_system: true,
mes: await renderTemplateAsync('pastChatHint'),
extra: {
isSmallSys: true,
},
},
}; };
} }
@ -1698,13 +1710,13 @@ async function delChat(chatfile) {
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ body: JSON.stringify({
chatfile: chatfile, chatfile: chatfile,
avatar_url: characters[this_chid].avatar, avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
}), }),
}); });
if (response.ok === true) { if (response.ok === true) {
// choose another chat if current was deleted // choose another chat if current was deleted
const name = chatfile.replace('.jsonl', ''); const name = chatfile.replace('.jsonl', '');
if (name === characters[this_chid].chat) { if (name === characters[this_chid]?.chat || name === chat_metadata?.chat_name) {
chat_metadata = {}; chat_metadata = {};
await replaceCurrentChat(); await replaceCurrentChat();
} }
@ -1719,15 +1731,20 @@ export async function replaceCurrentChat() {
const chatsResponse = await fetch('/api/characters/chats', { const chatsResponse = await fetch('/api/characters/chats', {
method: 'POST', method: 'POST',
headers: getRequestHeaders(), headers: getRequestHeaders(),
body: JSON.stringify({ avatar_url: characters[this_chid].avatar }), body: JSON.stringify({ avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar }),
}); });
if (chatsResponse.ok) { if (chatsResponse.ok) {
const chats = Object.values(await chatsResponse.json()); const chats = Object.values(await chatsResponse.json());
chats.sort((a, b) => sortMoments(timestampToMoment(a.last_mes), timestampToMoment(b.last_mes))); chats.sort((a, b) => sortMoments(timestampToMoment(a.last_mes), timestampToMoment(b.last_mes)));
// Create a new chat for assistant
if (this_chid === undefined && name2 === neutralCharacterName) {
await openAssistantChat();
}
// pick existing chat // pick existing chat
if (chats.length && typeof chats[0] === 'object') { else if (chats.length && typeof chats[0] === 'object') {
characters[this_chid].chat = chats[0].file_name.replace('.jsonl', ''); characters[this_chid].chat = chats[0].file_name.replace('.jsonl', '');
$('#selected_chat_pole').val(characters[this_chid].chat); $('#selected_chat_pole').val(characters[this_chid].chat);
saveCharacterDebounced(); saveCharacterDebounced();
@ -1845,7 +1862,7 @@ export async function reloadCurrentChat() {
if (selected_group) { if (selected_group) {
await getGroupChat(selected_group, true); await getGroupChat(selected_group, true);
} }
else if (this_chid !== undefined) { else if (this_chid !== undefined || (this_chid === undefined && name2 === neutralCharacterName)) {
await getChat(); await getChat();
} }
else { else {
@ -1862,7 +1879,7 @@ export async function reloadCurrentChat() {
/** /**
* Send the message currently typed into the chat box. * Send the message currently typed into the chat box.
*/ */
export function sendTextareaMessage() { export async function sendTextareaMessage() {
if (is_send_press) return; if (is_send_press) return;
if (isExecutingCommandsFromChatInput) return; if (isExecutingCommandsFromChatInput) return;
@ -1880,6 +1897,10 @@ export function sendTextareaMessage() {
generateType = 'continue'; generateType = 'continue';
} }
if (textareaText && this_chid === undefined) {
await openAssistantChat();
}
Generate(generateType); Generate(generateType);
} }
@ -5911,52 +5932,45 @@ export function saveChatDebounced() {
export async function saveChat(chat_name, withMetadata, mesId) { export async function saveChat(chat_name, withMetadata, mesId) {
const metadata = { ...chat_metadata, ...(withMetadata || {}) }; const metadata = { ...chat_metadata, ...(withMetadata || {}) };
let file_name = chat_name ?? characters[this_chid]?.chat; let fileName = chat_name ?? characters[this_chid]?.chat ?? chat_metadata?.chat_name;
if (!file_name) { if (!fileName) {
console.warn('saveChat called without chat_name and no chat file found'); console.warn('saveChat called without chat_name and no chat file found');
return; return;
} }
if (characters[this_chid]) {
characters[this_chid]['date_last_chat'] = Date.now(); characters[this_chid]['date_last_chat'] = Date.now();
}
chat.forEach(function (item, i) { chat.forEach(function (item, i) {
if (item['is_group']) { if (item['is_group']) {
toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.'); toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.');
throw new Error('Group chat saved from saveChat'); throw new Error('Group chat saved from saveChat');
} }
/*
if (item.is_user) {
//var str = item.mes.replace(`${name1}:`, `${name1}:`);
//chat[i].mes = str;
//chat[i].name = name1;
} else if (i !== chat.length - 1 && chat[i].swipe_id !== undefined) {
// delete chat[i].swipes;
// delete chat[i].swipe_id;
}
*/
}); });
const trimmed_chat = (mesId !== undefined && mesId >= 0 && mesId < chat.length) const trimmedChat = (mesId !== undefined && mesId >= 0 && mesId < chat.length)
? chat.slice(0, parseInt(mesId) + 1) ? chat.slice(0, parseInt(mesId) + 1)
: chat; : chat;
var save_chat = [ const saveChat = [
{ {
user_name: name1, user_name: name1,
character_name: name2, character_name: name2,
create_date: chat_create_date, create_date: chat_create_date,
chat_metadata: metadata, chat_metadata: metadata,
}, },
...trimmed_chat, ...trimmedChat,
]; ];
return jQuery.ajax({ return jQuery.ajax({
type: 'POST', type: 'POST',
url: '/api/chats/save', url: '/api/chats/save',
data: JSON.stringify({ data: JSON.stringify({
ch_name: characters[this_chid].name, ch_name: characters[this_chid]?.name ?? neutralCharacterName,
file_name: file_name, avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
chat: save_chat, file_name: fileName,
avatar_url: characters[this_chid].avatar, chat: saveChat,
}), }),
beforeSend: function () { beforeSend: function () {
@ -6091,9 +6105,9 @@ export async function getChat() {
type: 'POST', type: 'POST',
url: '/api/chats/get', url: '/api/chats/get',
data: JSON.stringify({ data: JSON.stringify({
ch_name: characters[this_chid].name, ch_name: characters[this_chid]?.name ?? neutralCharacterName,
file_name: characters[this_chid].chat, file_name: characters[this_chid]?.chat ?? chat_metadata?.chat_name,
avatar_url: characters[this_chid].avatar, avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
}), }),
dataType: 'json', dataType: 'json',
contentType: 'application/json', contentType: 'application/json',
@ -6124,7 +6138,7 @@ export async function getChat() {
} }
async function getChatResult() { async function getChatResult() {
name2 = characters[this_chid].name; name2 = characters[this_chid]?.name ?? neutralCharacterName;
let freshChat = false; let freshChat = false;
if (chat.length === 0) { if (chat.length === 0) {
const message = getFirstMessage(); const message = getFirstMessage();
@ -6150,7 +6164,7 @@ async function getChatResult() {
} }
function getFirstMessage() { function getFirstMessage() {
const firstMes = characters[this_chid].first_mes || ''; const firstMes = characters[this_chid]?.first_mes || '';
const alternateGreetings = characters[this_chid]?.data?.alternate_greetings; const alternateGreetings = characters[this_chid]?.data?.alternate_greetings;
const message = { const message = {
@ -6180,9 +6194,13 @@ function getFirstMessage() {
export async function openCharacterChat(file_name) { export async function openCharacterChat(file_name) {
await clearChat(); await clearChat();
characters[this_chid]['chat'] = file_name;
chat.length = 0; chat.length = 0;
chat_metadata = {}; chat_metadata = {};
if (characters[this_chid]) {
characters[this_chid]['chat'] = file_name;
} else {
chat_metadata['chat_name'] = file_name;
}
await getChat(); await getChat();
$('#selected_chat_pole').val(file_name); $('#selected_chat_pole').val(file_name);
await createOrEditCharacter(new CustomEvent('newChat')); await createOrEditCharacter(new CustomEvent('newChat'));
@ -6756,9 +6774,9 @@ export async function getChatsFromFiles(data, isGroupChat) {
const requestBody = isGroupChat const requestBody = isGroupChat
? JSON.stringify({ id: file_name }) ? JSON.stringify({ id: file_name })
: JSON.stringify({ : JSON.stringify({
ch_name: characters[context.characterId].name, ch_name: characters[context.characterId]?.name ?? neutralCharacterName,
file_name: file_name.replace('.jsonl', ''), file_name: file_name.replace('.jsonl', ''),
avatar_url: characters[context.characterId].avatar, avatar_url: characters[context.characterId]?.avatar ?? neutralCharacterAvatar,
}); });
const chatResponse = await fetch(endpoint, { const chatResponse = await fetch(endpoint, {
@ -6806,11 +6824,17 @@ export async function getChatsFromFiles(data, isGroupChat) {
*/ */
export async function getPastCharacterChats(characterId = null) { export async function getPastCharacterChats(characterId = null) {
characterId = characterId ?? this_chid; characterId = characterId ?? this_chid;
if (!characters[characterId]) return []; let avatar = characters[characterId]?.avatar;
if (!avatar && this_chid === undefined && name2 === neutralCharacterName) {
avatar = neutralCharacterAvatar;
}
if (!avatar) return [];
const response = await fetch('/api/characters/chats', { const response = await fetch('/api/characters/chats', {
method: 'POST', method: 'POST',
body: JSON.stringify({ avatar_url: characters[characterId].avatar }), body: JSON.stringify({ avatar_url: avatar }),
headers: getRequestHeaders(), headers: getRequestHeaders(),
}); });
@ -6831,6 +6855,15 @@ export async function getPastCharacterChats(characterId = null) {
* Helper for `displayPastChats`, to make the same info consistently available for other functions * Helper for `displayPastChats`, to make the same info consistently available for other functions
*/ */
function getCurrentChatDetails() { function getCurrentChatDetails() {
if (this_chid === undefined && name2 === neutralCharacterName) {
return {
sessionName: chat_metadata['chat_name'],
group: null,
characterName: neutralCharacterName,
avatarImgURL: neutralCharacterAvatar,
};
}
if (!characters[this_chid] && !selected_group) { if (!characters[this_chid] && !selected_group) {
return { sessionName: '', group: null, characterName: '', avatarImgURL: '' }; return { sessionName: '', group: null, characterName: '', avatarImgURL: '' };
} }
@ -7089,6 +7122,10 @@ export function select_rm_info(type, charId, previousCharId = null) {
} }
export function select_selected_character(chid) { export function select_selected_character(chid) {
if (chid === undefined) {
return;
}
//character select //character select
//console.log('select_selected_character() -- starting with input of -- ' + chid + ' (name:' + characters[chid].name + ')'); //console.log('select_selected_character() -- starting with input of -- ' + chid + ' (name:' + characters[chid].name + ')');
select_rm_create(); select_rm_create();
@ -7929,6 +7966,10 @@ async function createOrEditCharacter(e) {
toastr.error('Name is required'); toastr.error('Name is required');
} }
} else { } else {
if (this_chid === undefined) {
return;
}
let url = '/api/characters/edit'; let url = '/api/characters/edit';
if (crop_data != undefined) { if (crop_data != undefined) {
@ -8762,7 +8803,23 @@ export async function doNewChat({ deleteCurrentChat = false } = {}) {
await createOrEditCharacter(new CustomEvent('newChat')); await createOrEditCharacter(new CustomEvent('newChat'));
if (deleteCurrentChat) await delChat(chat_file_for_del + '.jsonl'); if (deleteCurrentChat) await delChat(chat_file_for_del + '.jsonl');
} }
}
async function openAssistantChat() {
resetSelectedGroup();
setCharacterId(undefined);
setCharacterName(neutralCharacterName);
setActiveCharacter(null);
setActiveGroup(null);
await clearChat();
chat.splice(0, chat.length);
this_edit_mes_id = undefined;
chat_metadata = { chat_name: humanizedDateTime() };
selected_button = 'characters';
$('#rm_button_selected_ch').children('h2').text('');
select_rm_characters();
sendSystemMessage(system_message_types.PAST_CHAT_HINT);
await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
} }
async function doDeleteChat() { async function doDeleteChat() {
@ -8800,7 +8857,7 @@ async function doRenameChat(_, chatName) {
export async function renameChat(oldFileName, newName) { export async function renameChat(oldFileName, newName) {
const body = { const body = {
is_group: !!selected_group, is_group: !!selected_group,
avatar_url: characters[this_chid]?.avatar, avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
original_file: `${oldFileName}.jsonl`, original_file: `${oldFileName}.jsonl`,
renamed_file: `${newName}.jsonl`, renamed_file: `${newName}.jsonl`,
}; };
@ -8827,7 +8884,7 @@ export async function renameChat(oldFileName, newName) {
await renameGroupChat(selected_group, oldFileName, newName); await renameGroupChat(selected_group, oldFileName, newName);
} }
else { else {
if (characters[this_chid].chat == oldFileName) { if (characters[this_chid]?.chat == oldFileName) {
characters[this_chid].chat = newName; characters[this_chid].chat = newName;
$('#selected_chat_pole').val(characters[this_chid].chat); $('#selected_chat_pole').val(characters[this_chid].chat);
await createOrEditCharacter(); await createOrEditCharacter();
@ -8835,7 +8892,8 @@ export async function renameChat(oldFileName, newName) {
} }
await reloadCurrentChat(); await reloadCurrentChat();
} catch { } catch (error) {
console.log('Error renaming chat:', error);
hideLoader(); hideLoader();
await delay(500); await delay(500);
await callPopup('An error has occurred. Chat was not renamed.', 'text'); await callPopup('An error has occurred. Chat was not renamed.', 'text');
@ -9612,7 +9670,7 @@ jQuery(async function () {
const filename = filenamefull.replace('.jsonl', ''); const filename = filenamefull.replace('.jsonl', '');
const body = { const body = {
is_group: !!selected_group, is_group: !!selected_group,
avatar_url: characters[this_chid]?.avatar, avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
file: `${filename}.jsonl`, file: `${filename}.jsonl`,
exportfilename: `${filename}.${format}`, exportfilename: `${filename}.${format}`,
format: format, format: format,
@ -9775,7 +9833,12 @@ jQuery(async function () {
}); });
if (id == 'option_select_chat') { if (id == 'option_select_chat') {
if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press) || fromSlashCommand) { if ((selected_group && !is_group_generating) || (!is_send_press) || fromSlashCommand) {
// Must be in assistant mode to view chats.
if (this_chid === undefined && name2 !== neutralCharacterName) {
return;
}
await displayPastChats(); await displayPastChats();
//this is just to avoid the shadow for past chat view when using /delchat //this is just to avoid the shadow for past chat view when using /delchat
//however, the dialog popup still gets one.. //however, the dialog popup still gets one..
@ -9793,7 +9856,7 @@ jQuery(async function () {
} }
else if (id == 'option_start_new_chat') { else if (id == 'option_start_new_chat') {
if ((selected_group || this_chid !== undefined) && !is_send_press) { if ((selected_group || this_chid !== undefined || name2 === neutralCharacterName) && !is_send_press) {
let deleteCurrentChat = false; let deleteCurrentChat = false;
const result = await Popup.show.confirm(t`Start new chat?`, await renderTemplateAsync('newChatConfirm'), { const result = await Popup.show.confirm(t`Start new chat?`, await renderTemplateAsync('newChatConfirm'), {
onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'), onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),
@ -9855,7 +9918,7 @@ jQuery(async function () {
select_rm_characters(); select_rm_characters();
sendSystemMessage(system_message_types.WELCOME); sendSystemMessage(system_message_types.WELCOME);
sendSystemMessage(system_message_types.WELCOME_PROMPT); sendSystemMessage(system_message_types.WELCOME_PROMPT);
eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId()); await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
await getClientVersion(); await getClientVersion();
} else { } else {
toastr.info('Please stop the message generation first.'); toastr.info('Please stop the message generation first.');
@ -9886,6 +9949,11 @@ jQuery(async function () {
} }
//} //}
} }
else if (id === 'option_ai_assistant') {
await openAssistantChat();
}
hideMenu(); hideMenu();
}); });

View File

@ -0,0 +1,8 @@
<div>
<b>Hint:</b>
<span>to view past conversations, choose</span>
<code><i class="fa-solid fa-address-book"></i>&nbsp;<span data-i18n="Manage chat files">Manage chat files</span></code>
<span>from the</span>
<code><i class="fa-solid fa-bars"></i>&nbsp;<span data-i18n="Options">Options</span></code>
<span>menu.</span>
</div>

View File

@ -1,6 +1,3 @@
<strong data-i18n="Connect to an API and ask me anything!"> <strong data-i18n="Connect to an API and ask me anything!">
Connect to an API and ask me anything! Connect to an API and ask me anything!
</strong> </strong>
<div>
The conversation history here is ephemeral and will be lost when you refresh the page or change characters.
</div>

View File

@ -145,7 +145,9 @@ router.post('/save', jsonParser, function (request, response) {
const chatData = request.body.chat; const chatData = request.body.chat;
const jsonlData = chatData.map(JSON.stringify).join('\n'); const jsonlData = chatData.map(JSON.stringify).join('\n');
const fileName = `${sanitize(String(request.body.file_name))}.jsonl`; const fileName = `${sanitize(String(request.body.file_name))}.jsonl`;
const filePath = path.join(request.user.directories.chats, directoryName, fileName); const directoryPath = path.join(request.user.directories.chats, directoryName);
if(!fs.existsSync(directoryPath)) fs.mkdirSync(directoryPath, { recursive: true });
const filePath = path.join(directoryPath, fileName);
writeFileAtomicSync(filePath, jsonlData, 'utf8'); writeFileAtomicSync(filePath, jsonlData, 'utf8');
backupChat(request.user.directories.backups, directoryName, jsonlData); backupChat(request.user.directories.backups, directoryName, jsonlData);
return response.send({ result: 'ok' }); return response.send({ result: 'ok' });