mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-02 19:07:40 +01:00
Neutral assistant chat space
This commit is contained in:
parent
46a1ea6837
commit
be2da4a629
154
public/script.js
154
public/script.js
@ -485,6 +485,7 @@ export let itemizedPrompts = [];
|
||||
|
||||
export const systemUserName = 'SillyTavern System';
|
||||
export const neutralCharacterName = 'Assistant';
|
||||
export const neutralCharacterAvatar = 'ST_your_ai_assistant.png';
|
||||
let default_user_name = 'User';
|
||||
export let name1 = default_user_name;
|
||||
export let name2 = systemUserName;
|
||||
@ -565,6 +566,7 @@ export const system_message_types = {
|
||||
HOTKEYS: 'hotkeys',
|
||||
MACROS: 'macros',
|
||||
WELCOME_PROMPT: 'welcome_prompt',
|
||||
PAST_CHAT_HINT: 'past_chat_hint',
|
||||
};
|
||||
|
||||
/**
|
||||
@ -692,6 +694,16 @@ async function getSystemMessages() {
|
||||
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(),
|
||||
body: JSON.stringify({
|
||||
chatfile: chatfile,
|
||||
avatar_url: characters[this_chid].avatar,
|
||||
avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
|
||||
}),
|
||||
});
|
||||
if (response.ok === true) {
|
||||
// choose another chat if current was deleted
|
||||
const name = chatfile.replace('.jsonl', '');
|
||||
if (name === characters[this_chid].chat) {
|
||||
if (name === characters[this_chid]?.chat || name === chat_metadata?.chat_name) {
|
||||
chat_metadata = {};
|
||||
await replaceCurrentChat();
|
||||
}
|
||||
@ -1719,15 +1731,20 @@ export async function replaceCurrentChat() {
|
||||
const chatsResponse = await fetch('/api/characters/chats', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ avatar_url: characters[this_chid].avatar }),
|
||||
body: JSON.stringify({ avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar }),
|
||||
});
|
||||
|
||||
if (chatsResponse.ok) {
|
||||
const chats = Object.values(await chatsResponse.json());
|
||||
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
|
||||
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', '');
|
||||
$('#selected_chat_pole').val(characters[this_chid].chat);
|
||||
saveCharacterDebounced();
|
||||
@ -1845,7 +1862,7 @@ export async function reloadCurrentChat() {
|
||||
if (selected_group) {
|
||||
await getGroupChat(selected_group, true);
|
||||
}
|
||||
else if (this_chid !== undefined) {
|
||||
else if (this_chid !== undefined || (this_chid === undefined && name2 === neutralCharacterName)) {
|
||||
await getChat();
|
||||
}
|
||||
else {
|
||||
@ -1862,7 +1879,7 @@ export async function reloadCurrentChat() {
|
||||
/**
|
||||
* Send the message currently typed into the chat box.
|
||||
*/
|
||||
export function sendTextareaMessage() {
|
||||
export async function sendTextareaMessage() {
|
||||
if (is_send_press) return;
|
||||
if (isExecutingCommandsFromChatInput) return;
|
||||
|
||||
@ -1880,6 +1897,10 @@ export function sendTextareaMessage() {
|
||||
generateType = 'continue';
|
||||
}
|
||||
|
||||
if (textareaText && this_chid === undefined) {
|
||||
await openAssistantChat();
|
||||
}
|
||||
|
||||
Generate(generateType);
|
||||
}
|
||||
|
||||
@ -5911,52 +5932,45 @@ export function saveChatDebounced() {
|
||||
|
||||
export async function saveChat(chat_name, withMetadata, mesId) {
|
||||
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');
|
||||
return;
|
||||
}
|
||||
|
||||
characters[this_chid]['date_last_chat'] = Date.now();
|
||||
if (characters[this_chid]) {
|
||||
characters[this_chid]['date_last_chat'] = Date.now();
|
||||
}
|
||||
|
||||
chat.forEach(function (item, i) {
|
||||
if (item['is_group']) {
|
||||
toastr.error('Trying to save group chat with regular saveChat function. Aborting to prevent corruption.');
|
||||
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;
|
||||
|
||||
var save_chat = [
|
||||
const saveChat = [
|
||||
{
|
||||
user_name: name1,
|
||||
character_name: name2,
|
||||
create_date: chat_create_date,
|
||||
chat_metadata: metadata,
|
||||
},
|
||||
...trimmed_chat,
|
||||
...trimmedChat,
|
||||
];
|
||||
return jQuery.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/chats/save',
|
||||
data: JSON.stringify({
|
||||
ch_name: characters[this_chid].name,
|
||||
file_name: file_name,
|
||||
chat: save_chat,
|
||||
avatar_url: characters[this_chid].avatar,
|
||||
ch_name: characters[this_chid]?.name ?? neutralCharacterName,
|
||||
avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
|
||||
file_name: fileName,
|
||||
chat: saveChat,
|
||||
}),
|
||||
beforeSend: function () {
|
||||
|
||||
@ -6091,9 +6105,9 @@ export async function getChat() {
|
||||
type: 'POST',
|
||||
url: '/api/chats/get',
|
||||
data: JSON.stringify({
|
||||
ch_name: characters[this_chid].name,
|
||||
file_name: characters[this_chid].chat,
|
||||
avatar_url: characters[this_chid].avatar,
|
||||
ch_name: characters[this_chid]?.name ?? neutralCharacterName,
|
||||
file_name: characters[this_chid]?.chat ?? chat_metadata?.chat_name,
|
||||
avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
|
||||
}),
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
@ -6124,7 +6138,7 @@ export async function getChat() {
|
||||
}
|
||||
|
||||
async function getChatResult() {
|
||||
name2 = characters[this_chid].name;
|
||||
name2 = characters[this_chid]?.name ?? neutralCharacterName;
|
||||
let freshChat = false;
|
||||
if (chat.length === 0) {
|
||||
const message = getFirstMessage();
|
||||
@ -6150,7 +6164,7 @@ async function getChatResult() {
|
||||
}
|
||||
|
||||
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 message = {
|
||||
@ -6180,9 +6194,13 @@ function getFirstMessage() {
|
||||
|
||||
export async function openCharacterChat(file_name) {
|
||||
await clearChat();
|
||||
characters[this_chid]['chat'] = file_name;
|
||||
chat.length = 0;
|
||||
chat_metadata = {};
|
||||
if (characters[this_chid]) {
|
||||
characters[this_chid]['chat'] = file_name;
|
||||
} else {
|
||||
chat_metadata['chat_name'] = file_name;
|
||||
}
|
||||
await getChat();
|
||||
$('#selected_chat_pole').val(file_name);
|
||||
await createOrEditCharacter(new CustomEvent('newChat'));
|
||||
@ -6756,9 +6774,9 @@ export async function getChatsFromFiles(data, isGroupChat) {
|
||||
const requestBody = isGroupChat
|
||||
? JSON.stringify({ id: file_name })
|
||||
: JSON.stringify({
|
||||
ch_name: characters[context.characterId].name,
|
||||
ch_name: characters[context.characterId]?.name ?? neutralCharacterName,
|
||||
file_name: file_name.replace('.jsonl', ''),
|
||||
avatar_url: characters[context.characterId].avatar,
|
||||
avatar_url: characters[context.characterId]?.avatar ?? neutralCharacterAvatar,
|
||||
});
|
||||
|
||||
const chatResponse = await fetch(endpoint, {
|
||||
@ -6806,11 +6824,17 @@ export async function getChatsFromFiles(data, isGroupChat) {
|
||||
*/
|
||||
export async function getPastCharacterChats(characterId = null) {
|
||||
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', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ avatar_url: characters[characterId].avatar }),
|
||||
body: JSON.stringify({ avatar_url: avatar }),
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
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) {
|
||||
if (chid === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
//character select
|
||||
//console.log('select_selected_character() -- starting with input of -- ' + chid + ' (name:' + characters[chid].name + ')');
|
||||
select_rm_create();
|
||||
@ -7929,6 +7966,10 @@ async function createOrEditCharacter(e) {
|
||||
toastr.error('Name is required');
|
||||
}
|
||||
} else {
|
||||
if (this_chid === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
let url = '/api/characters/edit';
|
||||
|
||||
if (crop_data != undefined) {
|
||||
@ -8762,7 +8803,23 @@ export async function doNewChat({ deleteCurrentChat = false } = {}) {
|
||||
await createOrEditCharacter(new CustomEvent('newChat'));
|
||||
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() {
|
||||
@ -8800,7 +8857,7 @@ async function doRenameChat(_, chatName) {
|
||||
export async function renameChat(oldFileName, newName) {
|
||||
const body = {
|
||||
is_group: !!selected_group,
|
||||
avatar_url: characters[this_chid]?.avatar,
|
||||
avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
|
||||
original_file: `${oldFileName}.jsonl`,
|
||||
renamed_file: `${newName}.jsonl`,
|
||||
};
|
||||
@ -8827,7 +8884,7 @@ export async function renameChat(oldFileName, newName) {
|
||||
await renameGroupChat(selected_group, oldFileName, newName);
|
||||
}
|
||||
else {
|
||||
if (characters[this_chid].chat == oldFileName) {
|
||||
if (characters[this_chid]?.chat == oldFileName) {
|
||||
characters[this_chid].chat = newName;
|
||||
$('#selected_chat_pole').val(characters[this_chid].chat);
|
||||
await createOrEditCharacter();
|
||||
@ -8835,7 +8892,8 @@ export async function renameChat(oldFileName, newName) {
|
||||
}
|
||||
|
||||
await reloadCurrentChat();
|
||||
} catch {
|
||||
} catch (error) {
|
||||
console.log('Error renaming chat:', error);
|
||||
hideLoader();
|
||||
await delay(500);
|
||||
await callPopup('An error has occurred. Chat was not renamed.', 'text');
|
||||
@ -9612,7 +9670,7 @@ jQuery(async function () {
|
||||
const filename = filenamefull.replace('.jsonl', '');
|
||||
const body = {
|
||||
is_group: !!selected_group,
|
||||
avatar_url: characters[this_chid]?.avatar,
|
||||
avatar_url: characters[this_chid]?.avatar ?? neutralCharacterAvatar,
|
||||
file: `${filename}.jsonl`,
|
||||
exportfilename: `${filename}.${format}`,
|
||||
format: format,
|
||||
@ -9775,7 +9833,12 @@ jQuery(async function () {
|
||||
});
|
||||
|
||||
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();
|
||||
//this is just to avoid the shadow for past chat view when using /delchat
|
||||
//however, the dialog popup still gets one..
|
||||
@ -9793,7 +9856,7 @@ jQuery(async function () {
|
||||
}
|
||||
|
||||
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;
|
||||
const result = await Popup.show.confirm(t`Start new chat?`, await renderTemplateAsync('newChatConfirm'), {
|
||||
onClose: () => deleteCurrentChat = !!$('#del_chat_checkbox').prop('checked'),
|
||||
@ -9855,7 +9918,7 @@ jQuery(async function () {
|
||||
select_rm_characters();
|
||||
sendSystemMessage(system_message_types.WELCOME);
|
||||
sendSystemMessage(system_message_types.WELCOME_PROMPT);
|
||||
eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
|
||||
await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
|
||||
await getClientVersion();
|
||||
} else {
|
||||
toastr.info('Please stop the message generation first.');
|
||||
@ -9886,6 +9949,11 @@ jQuery(async function () {
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
else if (id === 'option_ai_assistant') {
|
||||
await openAssistantChat();
|
||||
}
|
||||
|
||||
hideMenu();
|
||||
});
|
||||
|
||||
|
8
public/scripts/templates/pastChatHint.html
Normal file
8
public/scripts/templates/pastChatHint.html
Normal 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> <span data-i18n="Manage chat files">Manage chat files</span></code>
|
||||
<span>from the</span>
|
||||
<code><i class="fa-solid fa-bars"></i> <span data-i18n="Options">Options</span></code>
|
||||
<span>menu.</span>
|
||||
</div>
|
@ -1,6 +1,3 @@
|
||||
<strong data-i18n="Connect to an API and ask me anything!">
|
||||
Connect to an API and ask me anything!
|
||||
</strong>
|
||||
<div>
|
||||
The conversation history here is ephemeral and will be lost when you refresh the page or change characters.
|
||||
</div>
|
||||
|
@ -145,7 +145,9 @@ router.post('/save', jsonParser, function (request, response) {
|
||||
const chatData = request.body.chat;
|
||||
const jsonlData = chatData.map(JSON.stringify).join('\n');
|
||||
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');
|
||||
backupChat(request.user.directories.backups, directoryName, jsonlData);
|
||||
return response.send({ result: 'ok' });
|
||||
|
Loading…
x
Reference in New Issue
Block a user