Removed 87 redundant `chat[chat.length - 1]` lookups. (#4963) * Removed redundant `chat[chat.length - 1]` lookups. * Readability. Don't chat[chat.push({}) - 1];. * Chat can't be empty. https://github.com/SillyTavern/SillyTavern/pull/4963#discussion_r2663006489 * Fixed mixup: https://github.com/SillyTavern/SillyTavern/pull/4963#discussion_r2663000183 * Rename it back to item * Add early return on empty chat. * Fix swipe assignment to use item message instead of last message --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

ae5f69585b847e3a0f8e0b3b8d9f046edb070864

DeclineThyself <235079501+DeclineThyself@users.noreply.github.com>

Signed
2 files changed, +100 -88Ignore whitespace
public/script.js+91 -83
@@ -1619,13 +1619,14 @@ export async function sendTextareaMessage() {
1619 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last1619 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last
1620 // message was sent from a character (not the user or the system).1620 // message was sent from a character (not the user or the system).
1621 const textareaText = String($('#send_textarea').val());1621 const textareaText = String($('#send_textarea').val());
1622 const lastMessage = chat[chat.length - 1];
1622 if (power_user.continue_on_send &&1623 if (power_user.continue_on_send &&
1623 !hasPendingFileAttachment() &&1624 !hasPendingFileAttachment() &&
1624 !textareaText &&1625 !textareaText &&
1625 !selected_group &&1626 !selected_group &&
1626 chat.length &&1627 chat.length &&
1627 !chat[chat.length - 1]['is_user'] &&1628 !lastMessage['is_user'] &&
1628 !chat[chat.length - 1]['is_system']1629 !lastMessage['is_system']
1629 ) {1630 ) {
1630 generateType = 'continue';1631 generateType = 'continue';
1631 }1632 }
@@ -4191,6 +4192,8 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4191 return Promise.resolve();4192 return Promise.resolve();
4192 }4193 }
41934194
4195 const lastMessage = chat[chat.length - 1];
4196
4194 let textareaText;4197 let textareaText;
4195 if (type !== 'regenerate' && type !== 'swipe' && type !== 'quiet' && !isImpersonate && !dryRun) {4198 if (type !== 'regenerate' && type !== 'swipe' && type !== 'quiet' && !isImpersonate && !dryRun) {
4196 is_send_press = true;4199 is_send_press = true;
@@ -4198,7 +4201,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4198 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));4201 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
4199 } else {4202 } else {
4200 textareaText = '';4203 textareaText = '';
4201 if (chat.length && chat[chat.length - 1]['is_user']) {4204 if (chat.length && lastMessage['is_user']) {
4202 //do nothing? why does this check exist?4205 //do nothing? why does this check exist?
4203 }4206 }
4204 else if (type !== 'quiet' && type !== 'swipe' && !isImpersonate && !dryRun && chat.length) {4207 else if (type !== 'quiet' && type !== 'swipe' && !isImpersonate && !dryRun && chat.length) {
@@ -4212,13 +4215,13 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
42124215
4213 // Rewrite the generation timer to account for the time passed for all the continuations.4216 // Rewrite the generation timer to account for the time passed for all the continuations.
4214 if (isContinue && chat.length) {4217 if (isContinue && chat.length) {
4215 const prevFinished = chat[chat.length - 1]['gen_finished'];4218 const prevFinished = lastMessage['gen_finished'];
4216 const prevStarted = chat[chat.length - 1]['gen_started'];4219 const prevStarted = lastMessage['gen_started'];
42174220
4218 if (prevFinished && prevStarted) {4221 if (prevFinished && prevStarted) {
4219 const timePassed = Number(prevFinished) - Number(prevStarted);4222 const timePassed = Number(prevFinished) - Number(prevStarted);
4220 generation_started = new Date(Date.now() - timePassed);4223 generation_started = new Date(Date.now() - timePassed);
4221 chat[chat.length - 1]['gen_started'] = generation_started;4224 lastMessage['gen_started'] = generation_started;
4222 }4225 }
4223 }4226 }
42244227
@@ -6387,18 +6390,20 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6387 [type, getMessage, fromStreaming, title, swipes, reasoning, imageUrls, reasoningSignature] = arguments;6390 [type, getMessage, fromStreaming, title, swipes, reasoning, imageUrls, reasoningSignature] = arguments;
6388 }6391 }
63896392
6390 if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (chat[chat.length - 1]['swipe_id'] === undefined ||6393 const lastMessage = chat[chat.length - 1];
6391 chat[chat.length - 1]['is_user'])) {6394
6395 if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (lastMessage['swipe_id'] === undefined ||
6396 lastMessage['is_user'])) {
6392 type = 'normal';6397 type = 'normal';
6393 }6398 }
63946399
6395 if (chat.length && (!chat[chat.length - 1]['extra'] || typeof chat[chat.length - 1]['extra'] !== 'object')) {6400 if (chat.length && (!lastMessage['extra'] || typeof lastMessage['extra'] !== 'object')) {
6396 chat[chat.length - 1]['extra'] = {};6401 lastMessage['extra'] = {};
6397 }6402 }
63986403
6399 // Coerce null/undefined to empty string6404 // Coerce null/undefined to empty string
6400 if (chat.length && !chat[chat.length - 1]['extra']['reasoning']) {6405 if (chat.length && !lastMessage['extra']['reasoning']) {
6401 chat[chat.length - 1]['extra']['reasoning'] = '';6406 lastMessage['extra']['reasoning'] = '';
6402 }6407 }
64036408
6404 if (!reasoning) {6409 if (!reasoning) {
@@ -6408,70 +6413,70 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6408 let oldMessage = '';6413 let oldMessage = '';
6409 const generationFinished = new Date();6414 const generationFinished = new Date();
6410 if (type === 'swipe') {6415 if (type === 'swipe') {
6411 oldMessage = chat[chat.length - 1]['mes'];6416 oldMessage = lastMessage['mes'];
6412 chat[chat.length - 1]['swipes'].length++;6417 lastMessage['swipes'].length++;
6413 if (chat[chat.length - 1]['swipe_id'] === chat[chat.length - 1]['swipes'].length - 1) {6418 if (lastMessage['swipe_id'] === lastMessage['swipes'].length - 1) {
6414 chat[chat.length - 1]['title'] = title;6419 lastMessage['title'] = title;
6415 chat[chat.length - 1]['mes'] = getMessage;6420 lastMessage['mes'] = getMessage;
6416 chat[chat.length - 1]['gen_started'] = generation_started;6421 lastMessage['gen_started'] = generation_started;
6417 chat[chat.length - 1]['gen_finished'] = generationFinished;6422 lastMessage['gen_finished'] = generationFinished;
6418 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6423 lastMessage['send_date'] = getMessageTimeStamp();
6419 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6424 lastMessage['extra']['api'] = getGeneratingApi();
6420 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6425 lastMessage['extra']['model'] = getGeneratingModel();
6421 chat[chat.length - 1]['extra']['reasoning'] = reasoning;6426 lastMessage['extra']['reasoning'] = reasoning;
6422 chat[chat.length - 1]['extra']['reasoning_duration'] = null;6427 lastMessage['extra']['reasoning_duration'] = null;
6423 chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;6428 lastMessage['extra']['reasoning_signature'] = reasoningSignature;
6424 await processImageAttachment(chat[chat.length - 1], { imageUrls });6429 await processImageAttachment(lastMessage, { imageUrls });
6425 if (power_user.message_token_count_enabled) {6430 if (power_user.message_token_count_enabled) {
6426 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];6431 const tokenCountText = (reasoning || '') + lastMessage['mes'];
6427 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6432 lastMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
6428 }6433 }
6429 const chat_id = (chat.length - 1);6434 const chat_id = (chat.length - 1);
6430 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6435 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
6431 addOneMessage(chat[chat_id], { type: 'swipe' });6436 addOneMessage(chat[chat_id], { type: 'swipe' });
6432 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);6437 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);
6433 } else {6438 } else {
6434 chat[chat.length - 1]['mes'] = getMessage;6439 lastMessage['mes'] = getMessage;
6435 }6440 }
6436 } else if (type === 'append' || type === 'continue') {6441 } else if (type === 'append' || type === 'continue') {
6437 console.debug('Trying to append.');6442 console.debug('Trying to append.');
6438 oldMessage = chat[chat.length - 1]['mes'];6443 oldMessage = lastMessage['mes'];
6439 chat[chat.length - 1]['title'] = title;6444 lastMessage['title'] = title;
6440 chat[chat.length - 1]['mes'] += getMessage;6445 lastMessage['mes'] += getMessage;
6441 chat[chat.length - 1]['gen_started'] = generation_started;6446 lastMessage['gen_started'] = generation_started;
6442 chat[chat.length - 1]['gen_finished'] = generationFinished;6447 lastMessage['gen_finished'] = generationFinished;
6443 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6448 lastMessage['send_date'] = getMessageTimeStamp();
6444 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6449 lastMessage['extra']['api'] = getGeneratingApi();
6445 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6450 lastMessage['extra']['model'] = getGeneratingModel();
6446 chat[chat.length - 1]['extra']['reasoning'] = reasoning;6451 lastMessage['extra']['reasoning'] = reasoning;
6447 chat[chat.length - 1]['extra']['reasoning_duration'] = null;6452 lastMessage['extra']['reasoning_duration'] = null;
6448 chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;6453 lastMessage['extra']['reasoning_signature'] = reasoningSignature;
6449 await processImageAttachment(chat[chat.length - 1], { imageUrls });6454 await processImageAttachment(lastMessage, { imageUrls });
6450 if (power_user.message_token_count_enabled) {6455 if (power_user.message_token_count_enabled) {
6451 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];6456 const tokenCountText = (reasoning || '') + lastMessage['mes'];
6452 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6457 lastMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
6453 }6458 }
6454 const chat_id = (chat.length - 1);6459 const chat_id = (chat.length - 1);
6455 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6460 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
6456 addOneMessage(chat[chat_id], { type: 'swipe' });6461 addOneMessage(chat[chat_id], { type: 'swipe' });
6457 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);6462 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);
6458 } else if (type === 'appendFinal') {6463 } else if (type === 'appendFinal') {
6459 oldMessage = chat[chat.length - 1]['mes'];6464 oldMessage = lastMessage['mes'];
6460 console.debug('Trying to appendFinal.');6465 console.debug('Trying to appendFinal.');
6461 chat[chat.length - 1]['title'] = title;6466 lastMessage['title'] = title;
6462 chat[chat.length - 1]['mes'] = getMessage;6467 lastMessage['mes'] = getMessage;
6463 chat[chat.length - 1]['gen_started'] = generation_started;6468 lastMessage['gen_started'] = generation_started;
6464 chat[chat.length - 1]['gen_finished'] = generationFinished;6469 lastMessage['gen_finished'] = generationFinished;
6465 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6470 lastMessage['send_date'] = getMessageTimeStamp();
6466 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6471 lastMessage['extra']['api'] = getGeneratingApi();
6467 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6472 lastMessage['extra']['model'] = getGeneratingModel();
6468 chat[chat.length - 1]['extra']['reasoning'] += reasoning;6473 lastMessage['extra']['reasoning'] += reasoning;
6469 chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;6474 lastMessage['extra']['reasoning_signature'] = reasoningSignature;
6470 await processImageAttachment(chat[chat.length - 1], { imageUrls });6475 await processImageAttachment(lastMessage, { imageUrls });
6471 // We don't know if the reasoning duration extended, so we don't update it here on purpose.6476 // We don't know if the reasoning duration extended, so we don't update it here on purpose.
6472 if (power_user.message_token_count_enabled) {6477 if (power_user.message_token_count_enabled) {
6473 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];6478 const tokenCountText = (reasoning || '') + lastMessage['mes'];
6474 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6479 lastMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
6475 }6480 }
6476 const chat_id = (chat.length - 1);6481 const chat_id = (chat.length - 1);
6477 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6482 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
@@ -6480,27 +6485,28 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
64806485
6481 } else {6486 } else {
6482 console.debug('entering chat update routine for non-swipe post');6487 console.debug('entering chat update routine for non-swipe post');
6483 chat[chat.length] = {};6488 const newMessage = {};
6484 chat[chat.length - 1]['extra'] = {};6489 chat.push(newMessage);
6485 chat[chat.length - 1]['name'] = name2;6490 newMessage['extra'] = {};
6486 chat[chat.length - 1]['is_user'] = false;6491 newMessage['name'] = name2;
6487 chat[chat.length - 1]['send_date'] = getMessageTimeStamp();6492 newMessage['is_user'] = false;
6488 chat[chat.length - 1]['extra']['api'] = getGeneratingApi();6493 newMessage['send_date'] = getMessageTimeStamp();
6489 chat[chat.length - 1]['extra']['model'] = getGeneratingModel();6494 newMessage['extra']['api'] = getGeneratingApi();
6490 chat[chat.length - 1]['extra']['reasoning'] = reasoning;6495 newMessage['extra']['model'] = getGeneratingModel();
6491 chat[chat.length - 1]['extra']['reasoning_duration'] = null;6496 newMessage['extra']['reasoning'] = reasoning;
6492 chat[chat.length - 1]['extra']['reasoning_signature'] = reasoningSignature;6497 newMessage['extra']['reasoning_duration'] = null;
6498 newMessage['extra']['reasoning_signature'] = reasoningSignature;
6493 if (power_user.trim_spaces) {6499 if (power_user.trim_spaces) {
6494 getMessage = getMessage.trim();6500 getMessage = getMessage.trim();
6495 }6501 }
6496 chat[chat.length - 1]['mes'] = getMessage;6502 newMessage['mes'] = getMessage;
6497 chat[chat.length - 1]['title'] = title;6503 newMessage['title'] = title;
6498 chat[chat.length - 1]['gen_started'] = generation_started;6504 newMessage['gen_started'] = generation_started;
6499 chat[chat.length - 1]['gen_finished'] = generationFinished;6505 newMessage['gen_finished'] = generationFinished;
65006506
6501 if (power_user.message_token_count_enabled) {6507 if (power_user.message_token_count_enabled) {
6502 const tokenCountText = (reasoning || '') + chat[chat.length - 1]['mes'];6508 const tokenCountText = (reasoning || '') + newMessage['mes'];
6503 chat[chat.length - 1]['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6509 newMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);
6504 }6510 }
65056511
6506 if (selected_group) {6512 if (selected_group) {
@@ -6509,12 +6515,12 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6509 if (characters[this_chid].avatar != 'none') {6515 if (characters[this_chid].avatar != 'none') {
6510 avatarImg = getThumbnailUrl('avatar', characters[this_chid].avatar);6516 avatarImg = getThumbnailUrl('avatar', characters[this_chid].avatar);
6511 }6517 }
6512 chat[chat.length - 1]['force_avatar'] = avatarImg;6518 newMessage['force_avatar'] = avatarImg;
6513 chat[chat.length - 1]['original_avatar'] = characters[this_chid].avatar;6519 newMessage['original_avatar'] = characters[this_chid].avatar;
6514 chat[chat.length - 1]['extra']['gen_id'] = group_generation_id;6520 newMessage['extra']['gen_id'] = group_generation_id;
6515 }6521 }
65166522
6517 await processImageAttachment(chat[chat.length - 1], { imageUrls });6523 await processImageAttachment(newMessage, { imageUrls });
6518 const chat_id = (chat.length - 1);6524 const chat_id = (chat.length - 1);
65196525
6520 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6526 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
@@ -6538,12 +6544,12 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6538 } else {6544 } else {
6539 item['swipe_id'] = 0;6545 item['swipe_id'] = 0;
6540 item['swipes'] = [];6546 item['swipes'] = [];
6541 item['swipes'][0] = chat[chat.length - 1]['mes'];6547 item['swipes'][0] = item['mes'];
6542 item['swipe_info'][0] = {6548 item['swipe_info'][0] = {
6543 send_date: chat[chat.length - 1]['send_date'],6549 send_date: item['send_date'],
6544 gen_started: chat[chat.length - 1]['gen_started'],6550 gen_started: item['gen_started'],
6545 gen_finished: chat[chat.length - 1]['gen_finished'],6551 gen_finished: item['gen_finished'],
6546 extra: structuredClone(chat[chat.length - 1]['extra']),6552 extra: structuredClone(item['extra']),
6547 };6553 };
6548 }6554 }
65496555
@@ -6564,7 +6570,7 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6564 item.swipe_info.push(...swipeInfoArray);6570 item.swipe_info.push(...swipeInfoArray);
6565 }6571 }
65666572
6567 statMesProcess(chat[chat.length - 1], type, characters, this_chid, oldMessage);6573 statMesProcess(item, type, characters, this_chid, oldMessage);
6568 return { type, getMessage };6574 return { type, getMessage };
6569}6575}
65706576
@@ -12035,7 +12041,9 @@ jQuery(async function () {
12035 }12041 }
12036 if (this_edit_mes_id === undefined && $('#mes_stop').is(':visible')) {12042 if (this_edit_mes_id === undefined && $('#mes_stop').is(':visible')) {
12037 $('#mes_stop').trigger('click');12043 $('#mes_stop').trigger('click');
12038 if (chat.length && Array.isArray(chat[chat.length - 1].swipes) && chat[chat.length - 1].swipe_id == chat[chat.length - 1].swipes.length) {12044 if (chat.length === 0) return;
12045 const lastMessage = chat[chat.length - 1];
12046 if (Array.isArray(lastMessage.swipes) && lastMessage.swipe_id == lastMessage.swipes.length) {
12039 $('.last_mes .swipe_left').trigger('click');12047 $('.last_mes .swipe_left').trigger('click');
12040 }12048 }
12041 }12049 }
public/scripts/extensions/memory/index.js+9 -5
@@ -427,9 +427,13 @@ async function onChatEvent() {
427427
428 const context = getContext();428 const context = getContext();
429 const chat = context.chat;429 const chat = context.chat;
430 // Chat can't be empty.
431 if (chat.length === 0) return;
432
433 const lastMessage = chat[chat.length - 1];
430434
431 // No new messages - do nothing435 // No new messages - do nothing
432 if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) {436 if ((lastMessageId === chat.length && getStringHash(lastMessage.mes) === lastMessageHash)) {
433 return;437 return;
434 }438 }
435439
@@ -441,11 +445,11 @@ async function onChatEvent() {
441445
442 // Message has been edited / regenerated - delete the saved memory446 // Message has been edited / regenerated - delete the saved memory
443 if (chat.length447 if (chat.length
444 && chat[chat.length - 1].extra448 && lastMessage.extra
445 && chat[chat.length - 1].extra.memory449 && lastMessage.extra.memory
446 && lastMessageId === chat.length450 && lastMessageId === chat.length
447 && getStringHash(chat[chat.length - 1].mes) !== lastMessageHash) {451 && getStringHash(lastMessage.mes) !== lastMessageHash) {
448 delete chat[chat.length - 1].extra.memory;452 delete lastMessage.extra.memory;
449 }453 }
450454
451 summarizeChat(context)455 summarizeChat(context)