Support multiple chats per group. Fix A/N metadata not saving for groups.

This commit is contained in:
SillyLossy
2023-05-02 16:01:23 +03:00
parent dce309f0d9
commit 28627e7e51
6 changed files with 296 additions and 174 deletions

View File

@ -1786,6 +1786,19 @@
<!-- templates for JS to reuse when needed -->
<div id="past_chat_template" class="template_element">
<div class="select_chat_block_wrapper">
<div class="select_chat_block" file_name="">
<div class="avatar">
<img src="">
</div>
<div class="select_chat_block_filename"></div>
<div class="select_chat_block_mes"></div>
</div>
<div file_name="" class="PastChat_cross fa-solid fa-circle-xmark"></div>
</div>
</div>
<div id="tag_view_template" class="template_element">
<div class="tag_view_item">
<div class="tag_view_name" contenteditable="true"></div>

View File

@ -42,6 +42,11 @@ import {
getGroupChat,
renameGroupMember,
createNewGroupChat,
getGroupPastChats,
getGroupAvatar,
openGroupChat,
editGroup,
deleteGroupChat,
} from "./scripts/group-chats.js";
import {
@ -852,20 +857,10 @@ async function delChat(chatfile) {
}),
});
if (response.ok === true) {
//close past chat popup
$("#select_chat_cross").click();
// choose another chat if current was deleted
if (chatfile.replace('.jsonl', '') === characters[this_chid].chat) {
await replaceCurrentChat();
}
//open the history view again after 100ms
//hide option popup menu
setTimeout(function () {
$("#option_select_chat").click();
$("#options").hide();
}, 100);
}
}
@ -2707,8 +2702,6 @@ async function openCharacterChat(file_name) {
await getChat();
$("#selected_chat_pole").val(file_name);
$("#create_button").click();
$("#shadow_select_chat_popup").css("display", "none");
$("#load_select_chat_div").css("display", "block");
}
////////// OPTIMZED MAIN API CHANGE FUNCTION ////////////
@ -3164,67 +3157,63 @@ function messageEditDone(div) {
saveChatConditional();
}
async function getAllCharaChats() {
//console.log('getAllCharaChats() pinging server for character chat history.');
$("#select_chat_div").html("");
//console.log(characters[this_chid].chat);
jQuery.ajax({
type: "POST",
url: "/getallchatsofcharacter",
data: JSON.stringify({ avatar_url: characters[this_chid].avatar }),
beforeSend: function () {
async function getPastCharacterChats() {
const response = await fetch("/getallchatsofcharacter", {
method: 'POST',
body: JSON.stringify({ avatar_url: characters[this_chid].avatar }),
headers: getRequestHeaders(),
});
if (!response.ok) {
return;
}
let data = await response.json();
data = Object.values(data);
data = data.sort((a, b) => a["file_name"].localeCompare(b["file_name"])).reverse();
return data;
}
async function displayPastChats() {
$("#select_chat_div").empty();
const group = selected_group ? groups.find(x => x.id === selected_group) : null;
const data = await (selected_group ? getGroupPastChats(selected_group) : getPastCharacterChats());
const currentChat = selected_group ? group?.chat_id : characters[this_chid]["chat"];
const displayName = selected_group ? group?.name : characters[this_chid].name;
const avatarImg = selected_group ? group?.avatar_url : getThumbnailUrl('avatar', characters[this_chid]['avatar']);
},
cache: false,
dataType: "json",
contentType: "application/json",
success: function (data) {
$("#load_select_chat_div").css("display", "none");
let dataArr = Object.values(data);
data = dataArr.sort((a, b) =>
a["file_name"].localeCompare(b["file_name"])
);
data = data.reverse();
$("#ChatHistoryCharName").html(characters[this_chid].name);
$("#ChatHistoryCharName").text(displayName);
for (const key in data) {
let strlen = 300;
let mes = data[key]["mes"];
if (mes !== undefined) {
if (mes.length > strlen) {
mes = "..." + mes.substring(mes.length - strlen);
}
$("#select_chat_div").append(
'<div class="select_chat_block_wrapper">' +
'<div class="select_chat_block" file_name="' + data[key]["file_name"] + '">' +
'<div class=avatar><img src="characters/' + characters[this_chid]["avatar"] + '""></div >' +
'<div class="select_chat_block_filename">' + data[key]["file_name"] + '</div>' +
'<div class="select_chat_block_mes">' +
mes +
"</div>" +
"</div >" +
'<div file_name="' + data[key]["file_name"] + '" class="PastChat_cross fa-solid fa-circle-xmark"></div>' +
'</div>'
const fileName = data[key]['file_name'];
const template = $('#past_chat_template .select_chat_block_wrapper').clone();
template.find('.select_chat_block').attr('file_name', fileName);
template.find('.avatar img').attr('src', avatarImg);
template.find('.select_chat_block_filename').text(fileName);
template.find('.select_chat_block_mes').text(mes);
template.find('.PastChat_cross').attr('file_name', fileName);
);
if (
characters[this_chid]["chat"] ==
data[key]["file_name"].replace(".jsonl", "")
) {
//children().last()
$("#select_chat_div")
.find(".select_chat_block:last")
.attr("highlight", true);
if (selected_group) {
template.find('.avatar img').replaceWith(getGroupAvatar(group));
}
$("#select_chat_div").append(template);
if (currentChat === fileName.replace(".jsonl", "")) {
$("#select_chat_div").find(".select_chat_block:last").attr("highlight", true);
}
}
}
},
error: function (jqXHR, exception) {
console.log(exception);
console.log(jqXHR);
},
});
}
//************************************************************
@ -3542,6 +3531,10 @@ function read_bg_load(input) {
}
function showSwipeButtons() {
if (chat.length === 0) {
return;
}
if (
chat[chat.length - 1].is_system ||
!swipes ||
@ -3592,12 +3585,21 @@ function hideSwipeButtons() {
$("#chat").children().filter(`[mesid="${count_view_mes - 1}"]`).children('.swipe_left').css('display', 'none');
}
function saveChatConditional() {
async function saveMetadata() {
if (selected_group) {
saveGroupChat(selected_group);
await editGroup(selected_group, true, false);
}
else {
saveChat();
await saveChat();
}
}
async function saveChatConditional() {
if (selected_group) {
await saveGroupChat(selected_group, true);
}
else {
await saveChat();
}
}
@ -3681,6 +3683,7 @@ window["SillyTavern"].getContext = function () {
setExtensionPrompt: setExtensionPrompt,
updateChatMetadata: updateChatMetadata,
saveChat: saveChatConditional,
saveMetadata: saveMetadata,
sendSystemMessage: sendSystemMessage,
activateSendButtons,
deactivateSendButtons,
@ -4226,7 +4229,7 @@ $(document).ready(function () {
is_advanced_char_open = false;
$("#character_popup").css("display", "none");
});
$("#dialogue_popup_ok").click(function (e) {
$("#dialogue_popup_ok").click(async function (e) {
$("#shadow_popup").transition({
opacity: 0,
duration: 200,
@ -4239,9 +4242,21 @@ $(document).ready(function () {
bg_file_for_del.parent().remove();
}
if (popup_type == "del_chat") {
//close past chat popup
$("#select_chat_cross").click();
delChat(chat_file_for_del);
if (selected_group) {
await deleteGroupChat(selected_group, chat_file_for_del);
} else {
await delChat(chat_file_for_del);
}
//open the history view again after 100ms
//hide option popup menu
setTimeout(function () {
$("#option_select_chat").click();
$("#options").hide();
}, 100);
}
if (popup_type == "del_ch") {
console.log(
@ -4312,7 +4327,7 @@ $(document).ready(function () {
chat.length = 0;
if (selected_group) {
createNewGroupChat();
createNewGroupChat(selected_group);
}
else {
//RossAscends: added character name to new chat filenames and replaced Date.now() with humanizedDateTime;
@ -4652,14 +4667,8 @@ $(document).ready(function () {
$("#options [id]").on("click", function () {
var id = $(this).attr("id");
if (id == "option_select_chat") {
if (selected_group) {
// will open a chat selection screen
/* openNavToggle(); */
$("#rm_button_characters").trigger("click");
return;
}
if (this_chid != undefined && !is_send_press) {
getAllCharaChats();
if ((selected_group && !is_group_generating) || (this_chid !== undefined && !is_send_press)) {
displayPastChats();
$("#shadow_select_chat_popup").css("display", "block");
$("#shadow_select_chat_popup").css("opacity", 0.0);
$("#shadow_select_chat_popup").transition({
@ -5266,7 +5275,7 @@ $(document).ready(function () {
success: function (data) {
//console.log(data);
if (data.res) {
getAllCharaChats();
displayPastChats();
}
},
error: function (jqXHR, exception) {
@ -5287,7 +5296,15 @@ $(document).ready(function () {
$(document).on("click", ".select_chat_block, .bookmark_link", async function () {
let file_name = $(this).attr("file_name").replace(".jsonl", "");
openCharacterChat(file_name);
if (selected_group) {
await openGroupChat(selected_group, file_name);
} else {
await openCharacterChat(file_name);
}
$("#shadow_select_chat_popup").css("display", "none");
$("#load_select_chat_div").css("display", "block");
});
$(document).on("click", ".mes_stop", function () {

View File

@ -51,13 +51,13 @@ function hasCustomBackground() {
function saveBackgroundMetadata(file) {
const context = getContext();
context.chatMetadata[METADATA_KEY] = file;
context.saveChat();
context.saveMetadata();
}
function removeBackgroundMetadata() {
const context = getContext();
delete context.chatMetadata[METADATA_KEY];
context.saveChat();
context.saveMetadata();
}
function setCustomBackground() {

View File

@ -3,7 +3,7 @@ import { extension_settings, getContext } from "../../extensions.js";
import { debounce } from "../../utils.js";
export { MODULE_NAME };
const saveChatDebounced = debounce(async () => await getContext().saveChat(), 1000);
const saveMetadataDebounced = debounce(async () => await getContext().saveMetadata(), 1000);
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
const UPDATE_INTERVAL = 1000;
@ -21,12 +21,12 @@ const metadata_keys = {
async function onExtensionFloatingPromptInput() {
chat_metadata[metadata_keys.prompt] = $(this).val();
saveChatDebounced();
saveMetadataDebounced();
}
async function onExtensionFloatingIntervalInput() {
chat_metadata[metadata_keys.interval] = Number($(this).val());
saveChatDebounced();
saveMetadataDebounced();
}
async function onExtensionFloatingDepthInput() {
@ -38,12 +38,12 @@ async function onExtensionFloatingDepthInput() {
}
chat_metadata[metadata_keys.depth] = value;
saveChatDebounced();
saveMetadataDebounced();
}
async function onExtensionFloatingPositionInput(e) {
chat_metadata[metadata_keys.position] = e.target.value;
saveChatDebounced();
saveMetadataDebounced();
}
function onExtensionFloatingDefaultInput() {

View File

@ -81,15 +81,16 @@ export const group_activation_strategy = {
const groupAutoModeInterval = setInterval(groupChatAutoModeWorker, 5000);
const saveGroupDebounced = debounce(async (group) => await _save(group), 500);
async function _save(group) {
async function _save(group, reload = true) {
await fetch("/editgroup", {
method: "POST",
headers: getRequestHeaders(),
body: JSON.stringify(group),
});
if (reload) {
await getCharacters();
}
}
// Group chats
async function regenerateGroup() {
@ -114,17 +115,26 @@ async function regenerateGroup() {
generateGroupWrapper();
}
export async function getGroupChat(groupId) {
const group = groups.find((x) => x.id === groupId);
const chat_id = group.chat_id;
async function loadGroupChat(chatId) {
const response = await fetch("/getgroupchat", {
method: "POST",
headers: getRequestHeaders(),
body: JSON.stringify({ id: chat_id }),
body: JSON.stringify({ id: chatId }),
});
if (response.ok) {
const data = await response.json();
return data;
}
return [];
}
export async function getGroupChat(groupId) {
const group = groups.find((x) => x.id === groupId);
const chat_id = group.chat_id;
const data = await loadGroupChat(chat_id);
if (Array.isArray(data) && data.length) {
data[0].is_group = true;
for (let key of data) {
@ -155,7 +165,6 @@ export async function getGroupChat(groupId) {
await saveGroupChat(groupId, true);
}
}
function getFirstCharacterMessage(character) {
const mes = {};
@ -197,7 +206,6 @@ export async function renameGroupMember(oldAvatar, newAvatar, newName) {
// Scan every group for our renamed character
for (const group of groups) {
try {
// Try finding the member by old avatar link
const memberIndex = group.members.findIndex(x => x == oldAvatar);
@ -213,16 +221,10 @@ export async function renameGroupMember(oldAvatar, newAvatar, newName) {
// Load all chats from this group
for (const chatId of group.chats) {
const getChatResponse = await fetch("/getgroupchat", {
method: "POST",
headers: getRequestHeaders(),
body: JSON.stringify({ id: chatId }),
});
const messages = await loadGroupChat(chatId);
if (getChatResponse.ok) {
// Only save the chat if there were any changes to the chat content
let hadChanges = false;
const messages = await getChatResponse.json();
// Chat shouldn't be empty
if (Array.isArray(messages) && messages.length) {
// Iterate over every chat message
@ -241,7 +243,6 @@ export async function renameGroupMember(oldAvatar, newAvatar, newName) {
hadChanges = true;
}
}
}
if (hadChanges) {
const saveChatResponse = await fetch("/savegroupchat", {
@ -284,6 +285,9 @@ async function getGroups() {
.filter(x => x)
.filter(onlyUnique)
}
if (group.past_metadata == undefined) {
group.past_metadata = {};
}
}
}
}
@ -696,16 +700,17 @@ async function deleteGroup(id) {
}
}
async function editGroup(id, immediately) {
let group = groups.find((x) => x.id == id);
group = { ...group, chat_metadata };
export async function editGroup(id, immediately, reload=true) {
let group = groups.find((x) => x.id === id);
if (!group) {
return;
}
group['chat_metadata'] = chat_metadata;
if (immediately) {
return await _save(group);
return await _save(group, reload);
}
saveGroupDebounced(group);
@ -1077,8 +1082,8 @@ function filterMembersByFavorites(value) {
}
}
export async function createNewGroupChat() {
const group = groups.find(x => x.id === selected_group);
export async function createNewGroupChat(groupId) {
const group = groups.find(x => x.id === groupId);
if (!group) {
return;
@ -1091,7 +1096,11 @@ export async function createNewGroupChat() {
group.past_metadata = {};
}
clearChat();
chat.length = 0;
if (oldChatName) {
group.past_metadata[oldChatName] = Object.assign({}, chat_metadata);
}
group.chats.push(newChatName);
group.chat_id = newChatName;
group.chat_metadata = {};
@ -1101,6 +1110,76 @@ export async function createNewGroupChat() {
await getGroupChat(group.id);
}
export async function getGroupPastChats(groupId) {
const group = groups.find(x => x.id === groupId);
if (!group) {
return [];
}
const chats = [];
try {
for (const chatId of group.chats) {
const messages = await loadGroupChat(chatId);
const lastMessage = messages.length ? messages[messages.length - 1].mes : '[The chat is empty]';
chats.push({ 'file_name': chatId, 'mes': lastMessage });
}
} catch (err) {
console.error(err);
}
finally {
return chats;
}
}
export async function openGroupChat(groupId, chatId) {
const group = groups.find(x => x.id === groupId);
if (!group || !group.chats.includes(chatId)) {
return;
}
clearChat();
chat.length = 0;
const previousChat = group.chat_id;
group.past_metadata[previousChat] = Object.assign({}, chat_metadata);
group.chat_id = chatId;
group.chat_metadata = group.past_metadata[chatId] || {};
updateChatMetadata(group.chat_metadata, true);
await editGroup(groupId, true);
await getGroupChat(groupId);
}
export async function deleteGroupChat(groupId, chatId) {
const group = groups.find(x => x.id === groupId);
if (!group || !group.chats.includes(chatId)) {
return;
}
group.chats.splice(group.chats.indexOf(chatId), 1);
group.chat_metadata = {};
group.chat_id = '';
delete group.past_metadata[chatId];
updateChatMetadata(group.chat_metadata, true);
const response = await fetch('/deletegroupchat', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify({ id: chatId }),
});
if (response.ok) {
if (group.chats.length) {
await openGroupChat(groupId, group.chats[0]);
} else {
await createNewGroupChat(groupId);
}
}
}
$(document).ready(() => {
$(document).on("click", ".group_select", selectGroup);
$("#rm_group_filter").on("input", filterGroupMembers);

View File

@ -1381,22 +1381,19 @@ app.post("/getallchatsofcharacter", jsonParser, function (request, response) {
lastLine = line;
});
rl.on('close', () => {
ii--;
if (lastLine) {
let jsonData = json5.parse(lastLine);
if (jsonData.name !== undefined) {
if (jsonData.name !== undefined || jsonData.character_name !== undefined) {
chatData[i] = {};
chatData[i]['file_name'] = file;
chatData[i]['mes'] = jsonData['mes'];
ii--;
chatData[i]['mes'] = jsonData['mes'] || '[The chat is empty]';
}
}
if (ii === 0) {
console.log('ii count went to zero, responding with chatData');
response.send(chatData);
}
} else {
console.log('just returning from getallchatsofcharacter');
return;
}
}
console.log('successfully closing getallchatsofcharacter');
rl.close();
});
@ -1794,6 +1791,22 @@ app.post('/getgroupchat', jsonParser, (request, response) => {
}
});
app.post('/deletegroupchat', jsonParser, (request, response) => {
if (!request.body || !request.body.id) {
return response.sendStatus(400);
}
const id = request.body.id;
const pathToFile = path.join(directories.groupChats, `${id}.jsonl`);
if (fs.existsSync(pathToFile)) {
fs.rmSync(pathToFile);
return response.send({ ok: true });
}
return response.send({ error: true });
});
app.post('/savegroupchat', jsonParser, (request, response) => {
if (!request.body || !request.body.id) {
return response.sendStatus(400);