"gradually replacing property access with a dot operator" (#4965) * "gradually replacing property access with a dot operator" https://github.com/SillyTavern/SillyTavern/pull/4963#discussion_r2663003561 (?<=\w|\])\['([a-zA-Z]\w+)'\] My regex found 593 matches across 47 files. Also, two typos. * Fixed chat[0].chat_metadata type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664275854 * Fixed `swipedElementsDiv[0]?.getAnimations().filter((a) => a.animationName` type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664274593 * Fixed config.MESSAGE_SANITIZE and config.MESSAGE_ALLOW_SYSTEM_UI type errors. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664266271 * Fixed group.date_last_chat type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664295652 * Reverted SlashCommandParser dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664310931 * LLM fixed canUseNegativeLookbehind.result; type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664314288 * Reverted chat-completions.js bodyParams and headers dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664317848 https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664320088 https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664324438 * Reverted openai.js data dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664326244 * Reverted tests/frontend/MacroEnvBuilder.e2e.js env.dynamicMacros dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664330990 * Partially reverted `window` dot property access. * Reverted result.json() and settings dot property access. * Reverted google.js headers dot property access. * Fixed regex: `(?<=\w|\])\['([a-zA-Z]\w*)'\]` * Swapped window to globalThis with dot property access. * LLM fixed canUseNegativeLookbehind type. * Refactor property access * Consistency --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
Signed| @@ -38,6 +38,7 @@ declare global { | ||
| 38 | 38 | avatar_url?: string; |
| 39 | 39 | hideMutedSprites?: boolean; |
| 40 | 40 | fav?: boolean; |
| 41 | + date_last_chat?: MessageTimestamp; | |
| 41 | 42 | } |
| 42 | 43 | |
| 43 | 44 | interface ChatFile extends Array<ChatMessage> { |
| @@ -235,3 +236,11 @@ declare global { | ||
| 235 | 236 | |
| 236 | 237 | type SwipeEvent = JQuery.TriggeredEvent<any, any, HTMLElement, HTMLElement>; |
| 237 | 238 | } |
| 239 | + | |
| 240 | +//Overrides for public/scripts/chats.js | |
| 241 | +declare module 'dompurify' { | |
| 242 | + interface Config { | |
| 243 | + MESSAGE_SANITIZE?: boolean; | |
| 244 | + MESSAGE_ALLOW_SYSTEM_UI?: boolean; | |
| 245 | + } | |
| 246 | +} | |
| @@ -1176,8 +1176,8 @@ export async function getOneCharacter(avatarUrl) { | ||
| 1176 | 1176 | |
| 1177 | 1177 | if (response.ok) { |
| 1178 | 1178 | const getData = await response.json(); |
| 1179 | 1179 | getData['.name'] = DOMPurify.sanitize(getData['.name']); |
| 1180 | 1180 | getData['.chat'] = String(getData['.chat']); |
| 1181 | 1181 | |
| 1182 | 1182 | const indexOf = characters.findIndex(x => x.avatar === avatarUrl); |
| 1183 | 1183 | |
| @@ -1248,14 +1248,14 @@ export async function getCharacters() { | ||
| 1248 | 1248 | const getData = await response.json(); |
| 1249 | 1249 | for (let i = 0; i < getData.length; i++) { |
| 1250 | 1250 | characters[i] = getData[i]; |
| 1251 | 1251 | characters[i]['.name'] = DOMPurify.sanitize(characters[i]['.name']); |
| 1252 | 1252 | |
| 1253 | 1253 | // For dropped-in cards |
| 1254 | 1254 | if (!characters[i]['.chat']) { |
| 1255 | 1255 | characters[i]['.chat'] = `${characters[i]['.name']} - ${humanizedDateTime()}`; |
| 1256 | 1256 | } |
| 1257 | 1257 | |
| 1258 | 1258 | characters[i]['.chat'] = String(characters[i]['.chat']); |
| 1259 | 1259 | } |
| 1260 | 1260 | |
| 1261 | 1261 | if (previousAvatar) { |
| @@ -1555,7 +1555,7 @@ export async function deleteMessage(id, swipeDeletionIndex = undefined, askConfi | ||
| 1555 | 1555 | chat.splice(id, 1); |
| 1556 | 1556 | messageElement.remove(); |
| 1557 | 1557 | |
| 1558 | 1558 | chat_metadata['.tainted'] = true; |
| 1559 | 1559 | |
| 1560 | 1560 | const startIndex = [0, minId].includes(id) ? id : null; |
| 1561 | 1561 | updateViewMessageIds(startIndex); |
| @@ -1625,8 +1625,8 @@ export async function sendTextareaMessage() { | ||
| 1625 | 1625 | !textareaText && |
| 1626 | 1626 | !selected_group && |
| 1627 | 1627 | chat.length && |
| 1628 | 1628 | !lastMessage['.is_user'] && |
| 1629 | 1629 | !lastMessage['.is_system'] |
| 1630 | 1630 | ) { |
| 1631 | 1631 | generateType = 'continue'; |
| 1632 | 1632 | } |
| @@ -2406,7 +2406,7 @@ export function addCopyToCodeBlocks(messageElement) { | ||
| 2406 | 2406 | * @returns {void} |
| 2407 | 2407 | */ |
| 2408 | 2408 | export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true, insertBefore = null, forceId = null, showSwipes = true } = {}) { |
| 2409 | 2409 | let messageText = mes['.mes']; |
| 2410 | 2410 | const momentDate = timestampToMoment(mes.send_date); |
| 2411 | 2411 | const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : ''; |
| 2412 | 2412 | |
| @@ -2425,8 +2425,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2425 | 2425 | const isSystem = mes.is_system; |
| 2426 | 2426 | const title = mes.title; |
| 2427 | 2427 | |
| 2428 | 2428 | //for non-user mesagesmessages |
| 2429 | 2429 | if (!mes['.is_user']) { |
| 2430 | 2430 | if (mes.force_avatar) { |
| 2431 | 2431 | avatarImg = mes.force_avatar; |
| 2432 | 2432 | } else if (this_chid === undefined) { |
| @@ -2442,9 +2442,9 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2442 | 2442 | //if messge is from sytem, use the name provided in the message JSONL to proceed, |
| 2443 | 2443 | //if not system message, use name2 (char's name) to proceed |
| 2444 | 2444 | //characterName = mes.is_system || mes.force_avatar ? mes.name : name2; |
| 2445 | 2445 | } else if (mes['.is_user'] && mes['.force_avatar']) { |
| 2446 | 2446 | // Special case for persona images. |
| 2447 | 2447 | avatarImg = mes['.force_avatar']; |
| 2448 | 2448 | } |
| 2449 | 2449 | |
| 2450 | 2450 | // if mes.extra.uses_system_ui is true, set an override on the sanitizer options |
| @@ -3265,7 +3265,7 @@ export function getCharacterCardFieldsLazy({ chid = undefined } = {}) { | ||
| 3265 | 3265 | persona: () => baseChatReplace(power_user.persona_description?.trim()), |
| 3266 | 3266 | system: () => { |
| 3267 | 3267 | if (!character) return ''; |
| 3268 | 3268 | const systemPrompt = chat_metadata['.system_prompt'] || character.data?.system_prompt || ''; |
| 3269 | 3269 | return power_user.prefer_character_prompt ? baseChatReplace(systemPrompt.trim()) : ''; |
| 3270 | 3270 | }, |
| 3271 | 3271 | jailbreak: () => { |
| @@ -3295,13 +3295,13 @@ export function getCharacterCardFieldsLazy({ chid = undefined } = {}) { | ||
| 3295 | 3295 | scenario: () => { |
| 3296 | 3296 | if (groupCardsLazy) return groupCardsLazy.scenario; |
| 3297 | 3297 | if (!character) return ''; |
| 3298 | 3298 | const scenarioText = chat_metadata['.scenario'] || character.scenario || ''; |
| 3299 | 3299 | return baseChatReplace(scenarioText.trim()); |
| 3300 | 3300 | }, |
| 3301 | 3301 | mesExamples: () => { |
| 3302 | 3302 | if (groupCardsLazy) return groupCardsLazy.mesExamples; |
| 3303 | 3303 | if (!character) return ''; |
| 3304 | 3304 | const exampleDialog = chat_metadata['.mes_example'] || character.mes_example || ''; |
| 3305 | 3305 | return baseChatReplace(exampleDialog.trim()); |
| 3306 | 3306 | }, |
| 3307 | 3307 | }; |
| @@ -3516,39 +3516,39 @@ class StreamingProcessor { | ||
| 3516 | 3516 | this.sendTextarea.value = processedText; |
| 3517 | 3517 | this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true })); |
| 3518 | 3518 | } else { |
| 3519 | 3519 | const mesChanged = chat[messageId]['.mes'] !== processedText; |
| 3520 | 3520 | await this.#checkDomElements(messageId); |
| 3521 | 3521 | this.#updateMessageBlockVisibility(); |
| 3522 | 3522 | const currentTime = new Date(); |
| 3523 | 3523 | chat[messageId]['.mes'] = processedText; |
| 3524 | 3524 | chat[messageId]['.gen_started'] = this.timeStarted; |
| 3525 | 3525 | chat[messageId]['.gen_finished'] = currentTime; |
| 3526 | 3526 | if (!chat[messageId]['.extra']) { |
| 3527 | 3527 | chat[messageId]['.extra'] = {}; |
| 3528 | 3528 | } |
| 3529 | 3529 | chat[messageId]['.extra']['.time_to_first_token'] = this.timeToFirstToken; |
| 3530 | 3530 | |
| 3531 | 3531 | // Update reasoning |
| 3532 | 3532 | await this.reasoningHandler.process(messageId, mesChanged, this.promptReasoning); |
| 3533 | 3533 | processedText = chat[messageId]['.mes']; |
| 3534 | 3534 | |
| 3535 | 3535 | // Token count update. |
| 3536 | 3536 | const tokenCountText = this.reasoningHandler.reasoning + processedText; |
| 3537 | 3537 | const currentTokenCount = isFinal && power_user.message_token_count_enabled ? await getTokenCountAsync(tokenCountText, 0) : 0; |
| 3538 | 3538 | if (currentTokenCount) { |
| 3539 | 3539 | chat[messageId]['.extra']['.token_count'] = currentTokenCount; |
| 3540 | 3540 | if (this.messageTokenCounterDom instanceof HTMLElement) { |
| 3541 | 3541 | this.messageTokenCounterDom.textContent = `${currentTokenCount}t`; |
| 3542 | 3542 | } |
| 3543 | 3543 | } |
| 3544 | 3544 | |
| 3545 | 3545 | if ((this.type == 'swipe' || this.type === 'continue') && Array.isArray(chat[messageId]['.swipes'])) { |
| 3546 | 3546 | chat[messageId]['.swipes'][chat[messageId]['.swipe_id']] = processedText; |
| 3547 | 3547 | chat[messageId]['.swipe_info'][chat[messageId]['.swipe_id']] = { |
| 3548 | 3548 | 'send_date': chat[messageId]['.send_date'], |
| 3549 | 3549 | 'gen_started': chat[messageId]['.gen_started'], |
| 3550 | 3550 | 'gen_finished': chat[messageId]['.gen_finished'], |
| 3551 | 3551 | 'extra': structuredClone(chat[messageId]['.extra']), |
| 3552 | 3552 | }; |
| 3553 | 3553 | } |
| 3554 | 3554 | |
| @@ -3657,13 +3657,13 @@ class StreamingProcessor { | ||
| 3657 | 3657 | |
| 3658 | 3658 | setFirstSwipe(messageId) { |
| 3659 | 3659 | if (this.type !== 'swipe' && this.type !== 'impersonate') { |
| 3660 | 3660 | if (Array.isArray(chat[messageId]['.swipes']) && chat[messageId]['.swipes'].length === 1 && chat[messageId]['.swipe_id'] === 0) { |
| 3661 | 3661 | chat[messageId]['.swipes'][0] = chat[messageId]['.mes']; |
| 3662 | 3662 | chat[messageId]['.swipe_info'][0] = { |
| 3663 | 3663 | 'send_date': chat[messageId]['.send_date'], |
| 3664 | 3664 | 'gen_started': chat[messageId]['.gen_started'], |
| 3665 | 3665 | 'gen_finished': chat[messageId]['.gen_finished'], |
| 3666 | 3666 | 'extra': structuredClone(chat[messageId]['.extra']), |
| 3667 | 3667 | }; |
| 3668 | 3668 | } |
| 3669 | 3669 | } |
| @@ -4143,7 +4143,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4143 | 4143 | // Hide swipes if not in a dry run. |
| 4144 | 4144 | hideSwipeButtons(); |
| 4145 | 4145 | // If generated any message, set the flag to indicate it can't be recreated again. |
| 4146 | 4146 | chat_metadata['.tainted'] = true; |
| 4147 | 4147 | } |
| 4148 | 4148 | |
| 4149 | 4149 | if (selected_group && !is_group_generating) { |
| @@ -4201,7 +4201,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4201 | 4201 | $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); |
| 4202 | 4202 | } else { |
| 4203 | 4203 | textareaText = ''; |
| 4204 | 4204 | if (chat.length && lastMessage['.is_user']) { |
| 4205 | 4205 | //do nothing? why does this check exist? |
| 4206 | 4206 | } |
| 4207 | 4207 | else if (type !== 'quiet' && type !== 'swipe' && !isImpersonate && !dryRun && chat.length) { |
| @@ -4215,13 +4215,13 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4215 | 4215 | |
| 4216 | 4216 | // Rewrite the generation timer to account for the time passed for all the continuations. |
| 4217 | 4217 | if (isContinue && chat.length) { |
| 4218 | 4218 | const prevFinished = lastMessage['.gen_finished']; |
| 4219 | 4219 | const prevStarted = lastMessage['.gen_started']; |
| 4220 | 4220 | |
| 4221 | 4221 | if (prevFinished && prevStarted) { |
| 4222 | 4222 | const timePassed = Number(prevFinished) - Number(prevStarted); |
| 4223 | 4223 | generation_started = new Date(Date.now() - timePassed); |
| 4224 | 4224 | lastMessage['.gen_started'] = generation_started; |
| 4225 | 4225 | } |
| 4226 | 4226 | } |
| 4227 | 4227 | |
| @@ -4903,7 +4903,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro | ||
| 4903 | 4903 | let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding); |
| 4904 | 4904 | |
| 4905 | 4905 | if (thisPromptContextSize > this_max_context) { //if the prepared prompt is larger than the max context size... |
| 4906 | 4906 | if (count_exm_add > 0) { // ..and we have example mesagesmessages.. |
| 4907 | 4907 | count_exm_add--; // remove the example messages... |
| 4908 | 4908 | await checkPromptSize(); // and try agin... |
| 4909 | 4909 | } else if (mesSend.length > 0) { // if the chat history is longer than 0 |
| @@ -5631,7 +5631,7 @@ export function getBiasStrings(textareaText, type) { | ||
| 5631 | 5631 | function formatMessageHistoryItem(chatItem, isInstruct, forceOutputSequence) { |
| 5632 | 5632 | const isNarratorType = chatItem?.extra?.type === system_message_types.NARRATOR; |
| 5633 | 5633 | const characterName = chatItem?.name ? chatItem.name : name2; |
| 5634 | 5634 | const itemName = chatItem.is_user ? chatItem['.name'] : characterName; |
| 5635 | 5635 | const shouldPrependName = !isNarratorType; |
| 5636 | 5636 | |
| 5637 | 5637 | // If this symbol flag is set, completely ignore the message. |
| @@ -5700,7 +5700,7 @@ export async function sendMessageAsUser(messageText, messageBias, insertAt = nul | ||
| 5700 | 5700 | await populateFileAttachment(message); |
| 5701 | 5701 | statMesProcess(message, 'user', characters, this_chid, ''); |
| 5702 | 5702 | |
| 5703 | 5703 | chat_metadata['.tainted'] = true; |
| 5704 | 5704 | |
| 5705 | 5705 | if (typeof insertAt === 'number' && insertAt >= 0 && insertAt <= chat.length) { |
| 5706 | 5706 | chat.splice(insertAt, 0, message); |
| @@ -5851,7 +5851,7 @@ function setInContextMessages(msgInContextCount, type) { | ||
| 5851 | 5851 | |
| 5852 | 5852 | // Update last id to chat. No metadata save on purpose, gets hopefully saved via another call |
| 5853 | 5853 | const lastMessageId = Math.max(0, chat.length - msgInContextCount); |
| 5854 | 5854 | chat_metadata['.lastInContextMessageId'] = lastMessageId; |
| 5855 | 5855 | } |
| 5856 | 5856 | |
| 5857 | 5857 | /** |
| @@ -6392,18 +6392,18 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title | ||
| 6392 | 6392 | |
| 6393 | 6393 | const lastMessage = chat[chat.length - 1]; |
| 6394 | 6394 | |
| 6395 | 6395 | if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (lastMessage['.swipe_id'] === undefined || |
| 6396 | 6396 | lastMessage['.is_user'])) { |
| 6397 | 6397 | type = 'normal'; |
| 6398 | 6398 | } |
| 6399 | 6399 | |
| 6400 | 6400 | if (chat.length && (!lastMessage['.extra'] || typeof lastMessage['.extra'] !== 'object')) { |
| 6401 | 6401 | lastMessage['.extra'] = {}; |
| 6402 | 6402 | } |
| 6403 | 6403 | |
| 6404 | 6404 | // Coerce null/undefined to empty string |
| 6405 | 6405 | if (chat.length && !lastMessage['.extra']['.reasoning']) { |
| 6406 | 6406 | lastMessage['.extra']['.reasoning'] = ''; |
| 6407 | 6407 | } |
| 6408 | 6408 | |
| 6409 | 6409 | if (!reasoning) { |
| @@ -6413,70 +6413,70 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title | ||
| 6413 | 6413 | let oldMessage = ''; |
| 6414 | 6414 | const generationFinished = new Date(); |
| 6415 | 6415 | if (type === 'swipe') { |
| 6416 | 6416 | oldMessage = lastMessage['.mes']; |
| 6417 | 6417 | lastMessage['.swipes'].length++; |
| 6418 | 6418 | if (lastMessage['.swipe_id'] === lastMessage['.swipes'].length - 1) { |
| 6419 | 6419 | lastMessage['.title'] = title; |
| 6420 | 6420 | lastMessage['.mes'] = getMessage; |
| 6421 | 6421 | lastMessage['.gen_started'] = generation_started; |
| 6422 | 6422 | lastMessage['.gen_finished'] = generationFinished; |
| 6423 | 6423 | lastMessage['.send_date'] = getMessageTimeStamp(); |
| 6424 | 6424 | lastMessage['.extra']['.api'] = getGeneratingApi(); |
| 6425 | 6425 | lastMessage['.extra']['.model'] = getGeneratingModel(); |
| 6426 | 6426 | lastMessage['.extra']['.reasoning'] = reasoning; |
| 6427 | 6427 | lastMessage['.extra']['.reasoning_duration'] = null; |
| 6428 | 6428 | lastMessage['.extra']['.reasoning_signature'] = reasoningSignature; |
| 6429 | 6429 | await processImageAttachment(lastMessage, { imageUrls }); |
| 6430 | 6430 | if (power_user.message_token_count_enabled) { |
| 6431 | 6431 | const tokenCountText = (reasoning || '') + lastMessage['.mes']; |
| 6432 | 6432 | lastMessage['.extra']['.token_count'] = await getTokenCountAsync(tokenCountText, 0); |
| 6433 | 6433 | } |
| 6434 | 6434 | const chat_id = (chat.length - 1); |
| 6435 | 6435 | !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type); |
| 6436 | 6436 | addOneMessage(chat[chat_id], { type: 'swipe' }); |
| 6437 | 6437 | !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type); |
| 6438 | 6438 | } else { |
| 6439 | 6439 | lastMessage['.mes'] = getMessage; |
| 6440 | 6440 | } |
| 6441 | 6441 | } else if (type === 'append' || type === 'continue') { |
| 6442 | 6442 | console.debug('Trying to append.'); |
| 6443 | 6443 | oldMessage = lastMessage['.mes']; |
| 6444 | 6444 | lastMessage['.title'] = title; |
| 6445 | 6445 | lastMessage['.mes'] += getMessage; |
| 6446 | 6446 | lastMessage['.gen_started'] = generation_started; |
| 6447 | 6447 | lastMessage['.gen_finished'] = generationFinished; |
| 6448 | 6448 | lastMessage['.send_date'] = getMessageTimeStamp(); |
| 6449 | 6449 | lastMessage['.extra']['.api'] = getGeneratingApi(); |
| 6450 | 6450 | lastMessage['.extra']['.model'] = getGeneratingModel(); |
| 6451 | 6451 | lastMessage['.extra']['.reasoning'] = reasoning; |
| 6452 | 6452 | lastMessage['.extra']['.reasoning_duration'] = null; |
| 6453 | 6453 | lastMessage['.extra']['.reasoning_signature'] = reasoningSignature; |
| 6454 | 6454 | await processImageAttachment(lastMessage, { imageUrls }); |
| 6455 | 6455 | if (power_user.message_token_count_enabled) { |
| 6456 | 6456 | const tokenCountText = (reasoning || '') + lastMessage['.mes']; |
| 6457 | 6457 | lastMessage['.extra']['.token_count'] = await getTokenCountAsync(tokenCountText, 0); |
| 6458 | 6458 | } |
| 6459 | 6459 | const chat_id = (chat.length - 1); |
| 6460 | 6460 | !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type); |
| 6461 | 6461 | addOneMessage(chat[chat_id], { type: 'swipe' }); |
| 6462 | 6462 | !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type); |
| 6463 | 6463 | } else if (type === 'appendFinal') { |
| 6464 | 6464 | oldMessage = lastMessage['.mes']; |
| 6465 | 6465 | console.debug('Trying to appendFinal.'); |
| 6466 | 6466 | lastMessage['.title'] = title; |
| 6467 | 6467 | lastMessage['.mes'] = getMessage; |
| 6468 | 6468 | lastMessage['.gen_started'] = generation_started; |
| 6469 | 6469 | lastMessage['.gen_finished'] = generationFinished; |
| 6470 | 6470 | lastMessage['.send_date'] = getMessageTimeStamp(); |
| 6471 | 6471 | lastMessage['.extra']['.api'] = getGeneratingApi(); |
| 6472 | 6472 | lastMessage['.extra']['.model'] = getGeneratingModel(); |
| 6473 | 6473 | lastMessage['.extra']['.reasoning'] += reasoning; |
| 6474 | 6474 | lastMessage['.extra']['.reasoning_signature'] = reasoningSignature; |
| 6475 | 6475 | await processImageAttachment(lastMessage, { imageUrls }); |
| 6476 | 6476 | // We don't know if the reasoning duration extended, so we don't update it here on purpose. |
| 6477 | 6477 | if (power_user.message_token_count_enabled) { |
| 6478 | 6478 | const tokenCountText = (reasoning || '') + lastMessage['.mes']; |
| 6479 | 6479 | lastMessage['.extra']['.token_count'] = await getTokenCountAsync(tokenCountText, 0); |
| 6480 | 6480 | } |
| 6481 | 6481 | const chat_id = (chat.length - 1); |
| 6482 | 6482 | !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type); |
| @@ -6487,26 +6487,26 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title | ||
| 6487 | 6487 | console.debug('entering chat update routine for non-swipe post'); |
| 6488 | 6488 | const newMessage = {}; |
| 6489 | 6489 | chat.push(newMessage); |
| 6490 | 6490 | newMessage['.extra'] = {}; |
| 6491 | 6491 | newMessage['.name'] = name2; |
| 6492 | 6492 | newMessage['.is_user'] = false; |
| 6493 | 6493 | newMessage['.send_date'] = getMessageTimeStamp(); |
| 6494 | 6494 | newMessage['.extra']['.api'] = getGeneratingApi(); |
| 6495 | 6495 | newMessage['.extra']['.model'] = getGeneratingModel(); |
| 6496 | 6496 | newMessage['.extra']['.reasoning'] = reasoning; |
| 6497 | 6497 | newMessage['.extra']['.reasoning_duration'] = null; |
| 6498 | 6498 | newMessage['.extra']['.reasoning_signature'] = reasoningSignature; |
| 6499 | 6499 | if (power_user.trim_spaces) { |
| 6500 | 6500 | getMessage = getMessage.trim(); |
| 6501 | 6501 | } |
| 6502 | 6502 | newMessage['.mes'] = getMessage; |
| 6503 | 6503 | newMessage['.title'] = title; |
| 6504 | 6504 | newMessage['.gen_started'] = generation_started; |
| 6505 | 6505 | newMessage['.gen_finished'] = generationFinished; |
| 6506 | 6506 | |
| 6507 | 6507 | if (power_user.message_token_count_enabled) { |
| 6508 | 6508 | const tokenCountText = (reasoning || '') + newMessage['.mes']; |
| 6509 | 6509 | newMessage['.extra']['.token_count'] = await getTokenCountAsync(tokenCountText, 0); |
| 6510 | 6510 | } |
| 6511 | 6511 | |
| 6512 | 6512 | if (selected_group) { |
| @@ -6515,9 +6515,9 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title | ||
| 6515 | 6515 | if (characters[this_chid].avatar != 'none') { |
| 6516 | 6516 | avatarImg = getThumbnailUrl('avatar', characters[this_chid].avatar); |
| 6517 | 6517 | } |
| 6518 | 6518 | newMessage['.force_avatar'] = avatarImg; |
| 6519 | 6519 | newMessage['.original_avatar'] = characters[this_chid].avatar; |
| 6520 | 6520 | newMessage['.extra']['.gen_id'] = group_generation_id; |
| 6521 | 6521 | } |
| 6522 | 6522 | |
| 6523 | 6523 | await processImageAttachment(newMessage, { imageUrls }); |
| @@ -6529,27 +6529,27 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title | ||
| 6529 | 6529 | } |
| 6530 | 6530 | |
| 6531 | 6531 | const item = chat[chat.length - 1]; |
| 6532 | 6532 | if (item['.swipe_info'] === undefined) { |
| 6533 | 6533 | item['.swipe_info'] = []; |
| 6534 | 6534 | } |
| 6535 | 6535 | if (item['.swipe_id'] !== undefined) { |
| 6536 | 6536 | const swipeId = item['.swipe_id']; |
| 6537 | 6537 | item['.swipes'][swipeId] = item['.mes']; |
| 6538 | 6538 | item['.swipe_info'][swipeId] = { |
| 6539 | 6539 | send_date: item['.send_date'], |
| 6540 | 6540 | gen_started: item['.gen_started'], |
| 6541 | 6541 | gen_finished: item['.gen_finished'], |
| 6542 | 6542 | extra: structuredClone(item['.extra']), |
| 6543 | 6543 | }; |
| 6544 | 6544 | } else { |
| 6545 | 6545 | item['.swipe_id'] = 0; |
| 6546 | 6546 | item['.swipes'] = []; |
| 6547 | 6547 | item['.swipes'][0] = item['.mes']; |
| 6548 | 6548 | item['.swipe_info'][0] = { |
| 6549 | 6549 | send_date: item['.send_date'], |
| 6550 | 6550 | gen_started: item['.gen_started'], |
| 6551 | 6551 | gen_finished: item['.gen_finished'], |
| 6552 | 6552 | extra: structuredClone(item['.extra']), |
| 6553 | 6553 | }; |
| 6554 | 6554 | } |
| 6555 | 6555 | |
| @@ -7155,7 +7155,7 @@ export async function saveChat({ chatName, withMetadata, mesId, force = false } | ||
| 7155 | 7155 | return; |
| 7156 | 7156 | } |
| 7157 | 7157 | |
| 7158 | 7158 | characters[this_chid]['.date_last_chat'] = Date.now(); |
| 7159 | 7159 | |
| 7160 | 7160 | const trimmedChat = (mesId !== undefined && mesId >= 0 && mesId < chat.length) |
| 7161 | 7161 | ? chat.slice(0, Number(mesId) + 1) |
| @@ -7387,15 +7387,19 @@ export async function getChat() { | ||
| 7387 | 7387 | dataType: 'json', |
| 7388 | 7388 | contentType: 'application/json', |
| 7389 | 7389 | }); |
| 7390 | - if (response[0] !== undefined) { | |
| 7390 | + if (Array.isArray(response) && response.length > 0) { | |
| 7391 | + /** @type {ChatHeader} */ | |
| 7392 | + const chatHeader = response.shift(); | |
| 7393 | + chat_metadata = chatHeader?.chat_metadata ?? {}; | |
| 7391 | 7394 | chat.splice(0, chat.length, ...response); |
| 7392 | - chat_metadata = chat[0]['chat_metadata'] ?? {}; | |
| 7393 | - | |
| 7394 | - chat.shift(); | |
| 7395 | 7395 | chat.forEach(ensureMessageMediaIsArray); |
| 7396 | + } else { | |
| 7397 | + // An empty/corrupted chat file | |
| 7398 | + chat.splice(0, chat.length); | |
| 7399 | + chat_metadata = {}; | |
| 7396 | 7400 | } |
| 7397 | 7401 | if (!chat_metadata['.integrity']) { |
| 7398 | 7402 | chat_metadata['.integrity'] = uuidv4(); |
| 7399 | 7403 | } |
| 7400 | 7404 | await getChatResult(); |
| 7401 | 7405 | eventSource.emit('chatLoaded', { detail: { id: this_chid, character: characters[this_chid] } }); |
| @@ -7460,9 +7464,9 @@ function getFirstMessage() { | ||
| 7460 | 7464 | message.mes = swipes[0]; |
| 7461 | 7465 | } |
| 7462 | 7466 | |
| 7463 | 7467 | message['.swipe_id'] = 0; |
| 7464 | 7468 | message['.swipes'] = swipes; |
| 7465 | 7469 | message['.swipe_info'] = swipes.map(_ => ({ |
| 7466 | 7470 | send_date: message.send_date, |
| 7467 | 7471 | gen_started: void 0, |
| 7468 | 7472 | gen_finished: void 0, |
| @@ -7476,7 +7480,7 @@ function getFirstMessage() { | ||
| 7476 | 7480 | export async function openCharacterChat(file_name) { |
| 7477 | 7481 | await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10); |
| 7478 | 7482 | await clearChat(); |
| 7479 | 7483 | characters[this_chid]['.chat'] = file_name; |
| 7480 | 7484 | chat.length = 0; |
| 7481 | 7485 | chat_metadata = {}; |
| 7482 | 7486 | await getChat(); |
| @@ -7862,7 +7866,7 @@ function updateMessage(div) { | ||
| 7862 | 7866 | const mes = chat[mesElement.attr('mesid')]; |
| 7863 | 7867 | |
| 7864 | 7868 | // editing old messages |
| 7865 | 7869 | mes['.extra'] ??= {}; |
| 7866 | 7870 | |
| 7867 | 7871 | let regexPlacement; |
| 7868 | 7872 | if (mes?.is_user) { |
| @@ -7893,10 +7897,10 @@ function updateMessage(div) { | ||
| 7893 | 7897 | if (bias) { |
| 7894 | 7898 | text = removeMacros(text); |
| 7895 | 7899 | } |
| 7896 | 7900 | mes['.mes'] = text; |
| 7897 | 7901 | if (mes['.swipe_id'] !== undefined) { |
| 7898 | 7902 | ensureSwipes(mes); |
| 7899 | 7903 | mes['.swipes'][mes['.swipe_id']] = text; |
| 7900 | 7904 | } |
| 7901 | 7905 | |
| 7902 | 7906 | if (mes?.is_system || mes?.is_user || mes.extra?.type === system_message_types.NARRATOR) { |
| @@ -7905,7 +7909,7 @@ function updateMessage(div) { | ||
| 7905 | 7909 | mes.extra.bias = null; |
| 7906 | 7910 | } |
| 7907 | 7911 | |
| 7908 | 7912 | chat_metadata['.tainted'] = true; |
| 7909 | 7913 | |
| 7910 | 7914 | return { mesBlock, text, mes, bias }; |
| 7911 | 7915 | } |
| @@ -8019,7 +8023,7 @@ export async function messageEdit(editMessageId) { | ||
| 8019 | 8023 | * @param {number} [messageId=this_edit_mes_id] |
| 8020 | 8024 | */ |
| 8021 | 8025 | async function messageEditCancel(messageId = this_edit_mes_id) { |
| 8022 | 8026 | let text = chat[messageId]['.mes']; |
| 8023 | 8027 | let thisMesDiv; |
| 8024 | 8028 | // If this is the button then select it's parent. Otherwise, select by messageId. |
| 8025 | 8029 | if (this?.classList?.contains('mes_edit_cancel')) { |
| @@ -8167,7 +8171,7 @@ async function messageEditDone(div) { | ||
| 8167 | 8171 | export async function getChatsFromFiles(data, isGroupChat) { |
| 8168 | 8172 | const context = getContext(); |
| 8169 | 8173 | let chat_dict = {}; |
| 8170 | 8174 | let chat_list = Object.values(data).sort((a, b) => a['.file_name'].localeCompare(b['.file_name'])).reverse(); |
| 8171 | 8175 | |
| 8172 | 8176 | let chat_promise = chat_list.map(({ file_name }) => { |
| 8173 | 8177 | return new Promise(async (res, rej) => { |
| @@ -8244,7 +8248,7 @@ export async function getPastCharacterChats(characterId = null) { | ||
| 8244 | 8248 | } |
| 8245 | 8249 | |
| 8246 | 8250 | const chats = Object.values(data); |
| 8247 | 8251 | return chats.sort((a, b) => a['.file_name'].localeCompare(b['.file_name'])).reverse(); |
| 8248 | 8252 | } |
| 8249 | 8253 | |
| 8250 | 8254 | /** |
| @@ -8256,9 +8260,9 @@ export function getCurrentChatDetails() { | ||
| 8256 | 8260 | } |
| 8257 | 8261 | |
| 8258 | 8262 | const group = selected_group ? groups.find(x => x.id === selected_group) : null; |
| 8259 | 8263 | const currentChat = selected_group ? group?.chat_id : characters[this_chid]['.chat']; |
| 8260 | 8264 | const displayName = selected_group ? group?.name : characters[this_chid].name; |
| 8261 | 8265 | const avatarImg = selected_group ? group?.avatar_url : getThumbnailUrl('avatar', characters[this_chid]['.avatar']); |
| 8262 | 8266 | return { sessionName: currentChat, group: group, characterName: displayName, avatarImgURL: avatarImg }; |
| 8263 | 8267 | } |
| 8264 | 8268 | |
| @@ -8722,9 +8726,9 @@ export async function setCharacterSettingsOverrides() { | ||
| 8722 | 8726 | return; |
| 8723 | 8727 | } |
| 8724 | 8728 | |
| 8725 | 8729 | const scenarioOverrideValue = chat_metadata['.scenario'] || ''; |
| 8726 | 8730 | const exampleMessagesValue = chat_metadata['.mes_example'] || ''; |
| 8727 | 8731 | const systemPromptValue = chat_metadata['.system_prompt'] || ''; |
| 8728 | 8732 | const isGroup = !!selected_group; |
| 8729 | 8733 | |
| 8730 | 8734 | const $template = $(await renderTemplateAsync('scenarioOverride')); |
| @@ -8771,9 +8775,9 @@ export async function setCharacterSettingsOverrides() { | ||
| 8771 | 8775 | allowVerticalScrolling: true, |
| 8772 | 8776 | }); |
| 8773 | 8777 | |
| 8774 | 8778 | chat_metadata['.scenario'] = pendingChanges.scenario; |
| 8775 | 8779 | chat_metadata['.mes_example'] = pendingChanges.examples; |
| 8776 | 8780 | chat_metadata['.system_prompt'] = pendingChanges.system_prompt; |
| 8777 | 8781 | await saveMetadata(); |
| 8778 | 8782 | } |
| 8779 | 8783 | |
| @@ -9071,7 +9075,7 @@ export async function deleteSwipe(swipeId = null, messageId = chat.length - 1) { | ||
| 9071 | 9075 | // Select the next swipe, or the one before if it was the last one |
| 9072 | 9076 | const newSwipeId = Math.min(swipeId, message.swipes.length - 1); |
| 9073 | 9077 | |
| 9074 | 9078 | chat_metadata['.tainted'] = true; |
| 9075 | 9079 | |
| 9076 | 9080 | messageId = Number(messageId); |
| 9077 | 9081 | swipeId = Number(swipeId); |
| @@ -9585,7 +9589,7 @@ export async function createOrEditCharacter(e) { | ||
| 9585 | 9589 | !isNewChat && |
| 9586 | 9590 | message.mes && |
| 9587 | 9591 | !selected_group && |
| 9588 | 9592 | !chat_metadata['.tainted'] && |
| 9589 | 9593 | (chat.length === 0 || (chat.length === 1 && !chat[0].is_user && !chat[0].is_system)); |
| 9590 | 9594 | |
| 9591 | 9595 | if (shouldRegenerateMessage) { |
| @@ -9745,7 +9749,7 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 9745 | 9749 | } |
| 9746 | 9750 | |
| 9747 | 9751 | //Clamp Id between swipes. |
| 9748 | 9752 | let clampedId = clamp(chat[mesId]['.swipe_id'], 0, Math.max(0, chat[mesId]['.swipes'].length - 1)); |
| 9749 | 9753 | |
| 9750 | 9754 | await updateSwipeCounter(mesId); |
| 9751 | 9755 | //Fallback. |
| @@ -9837,7 +9841,7 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 9837 | 9841 | */ |
| 9838 | 9842 | async function loadFromSwipeId(mesId, newSwipeId) { |
| 9839 | 9843 | //Update the swipe_id. |
| 9840 | 9844 | chat[mesId]['.swipe_id'] = newSwipeId; |
| 9841 | 9845 | |
| 9842 | 9846 | clearMessageData(chat[mesId]); |
| 9843 | 9847 | |
| @@ -9909,7 +9913,8 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 9909 | 9913 | return true; |
| 9910 | 9914 | }; |
| 9911 | 9915 | //Wait for the animation's end. https://developer.mozilla.org/en-US/docs/Web/API/Animation/finished |
| 9912 | 9916 | const animationanimations = swipedElementsDiv[0]?.getAnimations().filter((a) => a['animationName'] ==?? 'slide')[0]; |
| 9917 | + const animation = animations.filter((a) => a instanceof globalThis.CSSAnimation && a.animationName == 'slide')[0]; | |
| 9913 | 9918 | try { |
| 9914 | 9919 | await Promise.race([animation?.finished, createTimeout(duration * 2, `The ${duration}ms swipe animation has not ended after ${duration * 2}ms. It has been skipped.`)].filter(Boolean)); |
| 9915 | 9920 | } catch (error) { |
| @@ -9997,7 +10002,7 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 9997 | 10002 | |
| 9998 | 10003 | const tokenCountText = (chat[mesId]?.extra?.reasoning || '') + chat[mesId].mes; |
| 9999 | 10004 | const tokenCount = await getTokenCountAsync(tokenCountText, 0); |
| 10000 | 10005 | chat[mesId]['.extra']['.token_count'] = tokenCount; |
| 10001 | 10006 | thisMesDiv.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 10002 | 10007 | } |
| 10003 | 10008 | } |
| @@ -10036,20 +10041,20 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 10036 | 10041 | // Make sure ad-hoc changes to extras are saved before swiping away |
| 10037 | 10042 | syncMesToSwipe(mesId); |
| 10038 | 10043 | |
| 10039 | 10044 | if (chat[mesId]['.swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array |
| 10040 | 10045 | chat[mesId]['.swipe_id'] = 0; // set it to id 0 |
| 10041 | 10046 | chat[mesId]['.swipes'] = []; // empty the array |
| 10042 | 10047 | chat[mesId]['.swipe_info'] = []; |
| 10043 | 10048 | chat[mesId]['.swipes'][0] = chat[mesId]['.mes']; //assign swipe array with last chat[mesId] from chat |
| 10044 | 10049 | chat[mesId]['.swipe_info'][0] = { |
| 10045 | 10050 | 'send_date': chat[mesId]['.send_date'], |
| 10046 | 10051 | 'gen_started': chat[mesId]['.gen_started'], |
| 10047 | 10052 | 'gen_finished': chat[mesId]['.gen_finished'], |
| 10048 | 10053 | 'extra': structuredClone(chat[mesId]['.extra']), |
| 10049 | 10054 | }; |
| 10050 | 10055 | } |
| 10051 | 10056 | // If the user is holding down the key and we're at the last or first swipe, don't do anything. |
| 10052 | 10057 | let isLastSwipe = (direction === SWIPE_DIRECTION.RIGHT) ? (chat[mesId].swipe_id === Math.max(0, chat[mesId]['.swipes'].length - 1)) : chat[mesId].swipe_id === 0; |
| 10053 | 10058 | if (source === SWIPE_SOURCE.KEYBOARD && repeated && isLastSwipe) { |
| 10054 | 10059 | await endSwipe(); |
| 10055 | 10060 | return; |
| @@ -10065,12 +10070,12 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 10065 | 10070 | if (forceSwipeId == null) newSwipeId--; |
| 10066 | 10071 | //Loop to last swipe if negative. |
| 10067 | 10072 | if (newSwipeId < 0) { |
| 10068 | 10073 | newSwipeId = Math.max(0, chat[mesId]['.swipes'].length - 1); |
| 10069 | 10074 | } |
| 10070 | 10075 | //Limit swipe_id to swipes. |
| 10071 | 10076 | if (newSwipeId > chat[mesId]['.swipes'].length - 1) { |
| 10072 | 10077 | toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to ${chat[mesId]['.swipes'].length - 1}.`); |
| 10073 | 10078 | chat[mesId]['.swipe_id'] = chat[mesId]['.swipes'].length - 1; |
| 10074 | 10079 | await endSwipe(); |
| 10075 | 10080 | return; |
| 10076 | 10081 | } |
| @@ -10085,24 +10090,24 @@ export async function swipe(event, direction, { source, repeated, message = chat | ||
| 10085 | 10090 | //Minimum of zero. |
| 10086 | 10091 | if (newSwipeId < 0) { |
| 10087 | 10092 | toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to zero.`); |
| 10088 | 10093 | chat[mesId]['.swipe_id'] = 0; |
| 10089 | 10094 | await endSwipe(); |
| 10090 | 10095 | return; |
| 10091 | 10096 | } |
| 10092 | 10097 | |
| 10093 | 10098 | //If overswiping. |
| 10094 | 10099 | if (newSwipeId >= chat[mesId]['.swipes'].length) { |
| 10095 | 10100 | newSwipeId = chat[mesId]['.swipes'].length; |
| 10096 | 10101 | |
| 10097 | 10102 | //Update the swipe_id. |
| 10098 | 10103 | chat[mesId]['.swipe_id'] = newSwipeId; |
| 10099 | 10104 | |
| 10100 | 10105 | const overswipe = getOverswipeBehavior(mesId); |
| 10101 | 10106 | |
| 10102 | 10107 | //Cancel the generation. |
| 10103 | 10108 | if (overswipe == OVERSWIPE_BEHAVIOR.NONE) { |
| 10104 | 10109 | //Cancel swipe. |
| 10105 | 10110 | chat[mesId]['.swipe_id'] = originalSwipeId; |
| 10106 | 10111 | await endSwipe(); |
| 10107 | 10112 | return; |
| 10108 | 10113 | } |
| @@ -11430,7 +11435,7 @@ jQuery(async function () { | ||
| 11430 | 11435 | chatElement.find(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove(); |
| 11431 | 11436 | chatElement.find(`.mes[mesid="${this_del_mes}"]`).remove(); |
| 11432 | 11437 | chat.length = this_del_mes; |
| 11433 | 11438 | chat_metadata['.tainted'] = true; |
| 11434 | 11439 | await saveChatConditional(); |
| 11435 | 11440 | chatElement.scrollTop(chatElement[0].scrollHeight); |
| 11436 | 11441 | await eventSource.emit(event_types.MESSAGE_DELETED, chat.length); |
| @@ -11517,7 +11522,7 @@ jQuery(async function () { | ||
| 11517 | 11522 | if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) { |
| 11518 | 11523 | try { |
| 11519 | 11524 | const messageId = $(this).closest('.mes').attr('mesid'); |
| 11520 | 11525 | const text = chat[messageId]['.mes']; |
| 11521 | 11526 | await copyText(text); |
| 11522 | 11527 | toastr.info('Copied!', '', { timeOut: 2000 }); |
| 11523 | 11528 | } catch (err) { |
| @@ -11544,8 +11549,8 @@ jQuery(async function () { | ||
| 11544 | 11549 | let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done'); |
| 11545 | 11550 | if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...) |
| 11546 | 11551 | let run_edit = true; |
| 11547 | 11552 | if (chat[edit_mes_id]['.swipe_id'] !== undefined) { |
| 11548 | 11553 | if (chat[edit_mes_id]['.swipes'].length === chat[edit_mes_id]['.swipe_id']) { |
| 11549 | 11554 | run_edit = false; |
| 11550 | 11555 | } |
| 11551 | 11556 | } |
| @@ -11684,8 +11689,8 @@ jQuery(async function () { | ||
| 11684 | 11689 | $(document).on('click', '.mes_edit_delete', async function (event, customData) { |
| 11685 | 11690 | const fromSlashCommand = customData?.fromSlashCommand || false; |
| 11686 | 11691 | const message = chat[this_edit_mes_id]; |
| 11687 | 11692 | const selectedSwipe = message['.swipe_id'] ?? undefined; |
| 11688 | 11693 | const swipesArray = Array.isArray(message['.swipes']) ? message['.swipes'] : []; |
| 11689 | 11694 | const canDeleteSwipe = power_user.confirm_message_delete && !fromSlashCommand && !message.is_user && swipesArray.length > 1 && this_edit_mes_id === chat.length - 1 && selectedSwipe !== undefined; |
| 11690 | 11695 | await deleteMessage(Number(this_edit_mes_id), canDeleteSwipe ? selectedSwipe : undefined, power_user.confirm_message_delete && fromSlashCommand !== true); |
| 11691 | 11696 | }); |
| @@ -12102,7 +12107,7 @@ jQuery(async function () { | ||
| 12102 | 12107 | }); |
| 12103 | 12108 | |
| 12104 | 12109 | // Remember the chat currently selected, so we can reload it after the replacement |
| 12105 | 12110 | const currentChatFile = characters[this_chid]['.chat']; |
| 12106 | 12111 | async function postReplace() { |
| 12107 | 12112 | await openCharacterChat(currentChatFile); |
| 12108 | 12113 | } |
| @@ -996,7 +996,7 @@ export function initRossMods() { | ||
| 996 | 996 | } |
| 997 | 997 | |
| 998 | 998 | //Enter to send when send_textarea in focus |
| 999 | 999 | if (document.activeElement == hotkeyTargets['.send_textarea']) { |
| 1000 | 1000 | const sendOnEnter = shouldSendOnEnter(); |
| 1001 | 1001 | if (!event.isComposing && !event.shiftKey && !event.ctrlKey && !event.altKey && event.key == 'Enter' && sendOnEnter) { |
| 1002 | 1002 | event.preventDefault(); |
| @@ -1004,7 +1004,7 @@ export function initRossMods() { | ||
| 1004 | 1004 | return; |
| 1005 | 1005 | } |
| 1006 | 1006 | } |
| 1007 | 1007 | if (document.activeElement == hotkeyTargets['.dialogue_popup_input'] && !isMobile()) { |
| 1008 | 1008 | if (!event.shiftKey && !event.ctrlKey && event.key == 'Enter') { |
| 1009 | 1009 | event.preventDefault(); |
| 1010 | 1010 | $('#dialogue_popup_ok').trigger('click'); |
| @@ -1139,7 +1139,7 @@ export function initRossMods() { | ||
| 1139 | 1139 | |
| 1140 | 1140 | if (event.ctrlKey && event.key == 'ArrowUp') { //edits last USER message if chatbar is empty and focused |
| 1141 | 1141 | if ( |
| 1142 | 1142 | hotkeyTargets['.send_textarea'].value === '' && |
| 1143 | 1143 | chatbarInFocus === true && |
| 1144 | 1144 | ($('.swipe_right:last').css('display') === 'flex' || $('.last_mes').attr('is_system') === 'true') && |
| 1145 | 1145 | $('#character_popup').css('display') === 'none' && |
| @@ -1158,7 +1158,7 @@ export function initRossMods() { | ||
| 1158 | 1158 | if (event.key == 'ArrowUp') { //edits last message if chatbar is empty and focused |
| 1159 | 1159 | console.log('got uparrow input'); |
| 1160 | 1160 | if ( |
| 1161 | 1161 | hotkeyTargets['.send_textarea'].value === '' && |
| 1162 | 1162 | chatbarInFocus === true && |
| 1163 | 1163 | //$('.swipe_right:last').css('display') === 'flex' && |
| 1164 | 1164 | $('.last_mes .mes_buttons').is(':visible') && |
| @@ -103,8 +103,8 @@ async function getBookmarkName({ isReplace = false, forceName = null } = {}) { | ||
| 103 | 103 | |
| 104 | 104 | function getMainChatName() { |
| 105 | 105 | if (chat_metadata) { |
| 106 | 106 | if (chat_metadata['.main_chat']) { |
| 107 | 107 | return chat_metadata['.main_chat']; |
| 108 | 108 | } |
| 109 | 109 | // groups didn't support bookmarks before chat metadata was introduced |
| 110 | 110 | else if (selected_group) { |
| @@ -112,8 +112,8 @@ function getMainChatName() { | ||
| 112 | 112 | } |
| 113 | 113 | else if (characters[this_chid].chat && characters[this_chid].chat.includes(bookmarkNameToken)) { |
| 114 | 114 | const tokenIndex = characters[this_chid].chat.lastIndexOf(bookmarkNameToken); |
| 115 | 115 | chat_metadata['.main_chat'] = characters[this_chid].chat.substring(0, tokenIndex).trim(); |
| 116 | 116 | return chat_metadata['.main_chat']; |
| 117 | 117 | } |
| 118 | 118 | } |
| 119 | 119 | return null; |
| @@ -127,7 +127,7 @@ export function showBookmarksButtons() { | ||
| 127 | 127 | $('#option_convert_to_group').show(); |
| 128 | 128 | } |
| 129 | 129 | |
| 130 | 130 | if (chat_metadata['.main_chat']) { |
| 131 | 131 | // In bookmark chat |
| 132 | 132 | $('#option_back_to_main').show(); |
| 133 | 133 | $('#option_new_bookmark').show(); |
| @@ -184,10 +184,10 @@ export async function createBranch(mesId) { | ||
| 184 | 184 | if (typeof lastMes.extra !== 'object') { |
| 185 | 185 | lastMes.extra = {}; |
| 186 | 186 | } |
| 187 | 187 | if (typeof lastMes.extra['.branches'] !== 'object') { |
| 188 | 188 | lastMes.extra['.branches'] = []; |
| 189 | 189 | } |
| 190 | 190 | lastMes.extra['.branches'].push(name); |
| 191 | 191 | return name; |
| 192 | 192 | } |
| 193 | 193 | |
| @@ -236,7 +236,7 @@ export async function createNewBookmark(mesId, { forceName = null } = {}) { | ||
| 236 | 236 | await saveChat({ chatName: name, withMetadata: newMetadata, mesId }); |
| 237 | 237 | } |
| 238 | 238 | |
| 239 | 239 | lastMes.extra['.bookmark_link'] = name; |
| 240 | 240 | |
| 241 | 241 | const mes = $(`.mes[mesid="${mesId}"]`); |
| 242 | 242 | updateBookmarkDisplay(mes, name); |
| @@ -42,13 +42,13 @@ function setCharCfg(tempValue, setting) { | ||
| 42 | 42 | |
| 43 | 43 | switch (setting) { |
| 44 | 44 | case settingType.guidance_scale: |
| 45 | 45 | tempCharaCfg['.guidance_scale'] = Number(tempValue); |
| 46 | 46 | break; |
| 47 | 47 | case settingType.negative_prompt: |
| 48 | 48 | tempCharaCfg['.negative_prompt'] = tempValue; |
| 49 | 49 | break; |
| 50 | 50 | case settingType.positive_prompt: |
| 51 | 51 | tempCharaCfg['.positive_prompt'] = tempValue; |
| 52 | 52 | break; |
| 53 | 53 | default: |
| 54 | 54 | return false; |
| @@ -239,31 +239,31 @@ function migrateSettings() { | ||
| 239 | 239 | |
| 240 | 240 | if (power_user.guidance_scale) { |
| 241 | 241 | extension_settings.cfg.global.guidance_scale = power_user.guidance_scale; |
| 242 | 242 | delete power_user['.guidance_scale']; |
| 243 | 243 | performSettingsSave = true; |
| 244 | 244 | } |
| 245 | 245 | |
| 246 | 246 | if (power_user.negative_prompt) { |
| 247 | 247 | extension_settings.cfg.global.negative_prompt = power_user.negative_prompt; |
| 248 | 248 | delete power_user['.negative_prompt']; |
| 249 | 249 | performSettingsSave = true; |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | 252 | if (chat_metadata['.cfg_negative_combine']) { |
| 253 | 253 | chat_metadata[metadataKeys.prompt_combine] = chat_metadata['.cfg_negative_combine']; |
| 254 | 254 | chat_metadata['.cfg_negative_combine'] = undefined; |
| 255 | 255 | performMetaSave = true; |
| 256 | 256 | } |
| 257 | 257 | |
| 258 | 258 | if (chat_metadata['.cfg_negative_insertion_depth']) { |
| 259 | 259 | chat_metadata[metadataKeys.prompt_insertion_depth] = chat_metadata['.cfg_negative_insertion_depth']; |
| 260 | 260 | chat_metadata['.cfg_negative_insertion_depth'] = undefined; |
| 261 | 261 | performMetaSave = true; |
| 262 | 262 | } |
| 263 | 263 | |
| 264 | 264 | if (chat_metadata['.cfg_negative_separator']) { |
| 265 | 265 | chat_metadata[metadataKeys.prompt_separator] = chat_metadata['.cfg_negative_separator']; |
| 266 | 266 | chat_metadata['.cfg_negative_separator'] = undefined; |
| 267 | 267 | performMetaSave = true; |
| 268 | 268 | } |
| 269 | 269 | |
| @@ -148,8 +148,8 @@ export async function bindModelTemplates(power_user, online_status) { | ||
| 148 | 148 | ?? power_user.model_templates_mappings[chatTemplateHash] |
| 149 | 149 | ?? {}; |
| 150 | 150 | const bindingsMatch = bindModelTemplates |
| 151 | 151 | && power_user.context.preset == bindModelTemplates['.context'] |
| 152 | 152 | && (!power_user.instruct.enabled || power_user.instruct.preset === bindModelTemplates['.instruct']); |
| 153 | 153 | |
| 154 | 154 | const bound = []; |
| 155 | 155 | |
| @@ -160,21 +160,21 @@ export async function bindModelTemplates(power_user, online_status) { | ||
| 160 | 160 | toastr.info(t`Context preset for ${online_status} will use defaults when loaded the next time.`); |
| 161 | 161 | } else { |
| 162 | 162 | if (power_user.context_derived) { |
| 163 | 163 | if (power_user.context.preset !== bindModelTemplates['.context']) { |
| 164 | 164 | bound.push(`${power_user.context.preset} context preset`); |
| 165 | 165 | // toastr.info(`Bound ${power_user.context.preset} preset to currently loaded model and all models that share its chat template.`); |
| 166 | 166 | |
| 167 | 167 | // map current preset to current chat template hash |
| 168 | 168 | bindModelTemplates['.context'] = power_user.context.preset; |
| 169 | 169 | } |
| 170 | 170 | } else { |
| 171 | 171 | toastr.warning(t`Note: Context derivation is disabled. Not including context preset.`); |
| 172 | 172 | } |
| 173 | 173 | if (power_user.instruct.enabled) { |
| 174 | 174 | if (power_user.instruct_derived) { |
| 175 | 175 | if (power_user.instruct.preset !== bindModelTemplates['.instruct']) { |
| 176 | 176 | bound.push(`${power_user.instruct.preset} instruct preset`); |
| 177 | 177 | bindModelTemplates['.instruct'] = power_user.instruct.preset; |
| 178 | 178 | } |
| 179 | 179 | } else { |
| 180 | 180 | toastr.warning(t`Note: Instruct derivation is disabled. Not including instruct preset.`); |
| @@ -685,7 +685,7 @@ export function formatCreatorNotes(text, avatarId) { | ||
| 685 | 685 | const preference = new StylesPreference(avatarId); |
| 686 | 686 | const sanitizeStyles = !preference.get(); |
| 687 | 687 | const decodeStyleParam = { prefix: sanitizeStyles ? '#creator_notes_spoiler ' : '' }; |
| 688 | 688 | /** @type {import('dompurify').Config & { MESSAGE_SANITIZE: boolean }} */ |
| 689 | 689 | const config = { |
| 690 | 690 | RETURN_DOM: false, |
| 691 | 691 | RETURN_DOM_FRAGMENT: false, |
| @@ -1911,13 +1911,13 @@ export function addDOMPurifyHooks() { | ||
| 1911 | 1911 | }); |
| 1912 | 1912 | |
| 1913 | 1913 | DOMPurify.addHook('uponSanitizeAttribute', (node, data, config) => { |
| 1914 | 1914 | if (!config['.MESSAGE_SANITIZE']) { |
| 1915 | 1915 | return; |
| 1916 | 1916 | } |
| 1917 | 1917 | |
| 1918 | 1918 | /* Retain the classes on UI elements of messages that interact with the main UI */ |
| 1919 | 1919 | const permittedNodeTypes = ['BUTTON', 'DIV']; |
| 1920 | 1920 | if (config['.MESSAGE_ALLOW_SYSTEM_UI'] && node.classList.contains('menu_button') && permittedNodeTypes.includes(node.nodeName)) { |
| 1921 | 1921 | return; |
| 1922 | 1922 | } |
| 1923 | 1923 | |
| @@ -1938,7 +1938,7 @@ export function addDOMPurifyHooks() { | ||
| 1938 | 1938 | }); |
| 1939 | 1939 | |
| 1940 | 1940 | DOMPurify.addHook('uponSanitizeElement', (node, _, config) => { |
| 1941 | 1941 | if (!config['.MESSAGE_SANITIZE']) { |
| 1942 | 1942 | return; |
| 1943 | 1943 | } |
| 1944 | 1944 | |
| @@ -103,10 +103,10 @@ async function downloadAssetsList(url) { | ||
| 103 | 103 | |
| 104 | 104 | for (const i of json) { |
| 105 | 105 | //console.log(DEBUG_PREFIX,i) |
| 106 | 106 | if (availableAssets[i['.type']] === undefined) |
| 107 | 107 | availableAssets[i['.type']] = []; |
| 108 | 108 | |
| 109 | 109 | availableAssets[i['.type']].push(i); |
| 110 | 110 | } |
| 111 | 111 | |
| 112 | 112 | console.debug(DEBUG_PREFIX, 'Updated available assets to', availableAssets); |
| @@ -139,7 +139,7 @@ async function downloadAssetsList(url) { | ||
| 139 | 139 | assetTypeMenu.append(await renderExtensionTemplateAsync('assets', 'installation')); |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | 142 | for (const asset of availableAssets[assetType].sort((a, b) => a?.name && b?.name && a['.name'].localeCompare(b['.name']))) { |
| 143 | 143 | const i = availableAssets[assetType].indexOf(asset); |
| 144 | 144 | const elemId = `assets_install_${assetType}_${i}`; |
| 145 | 145 | let element = $('<div />', { id: elemId, class: 'asset-download-button right_menu_button' }); |
| @@ -149,13 +149,13 @@ async function downloadAssetsList(url) { | ||
| 149 | 149 | //if (DEBUG_TONY_SAMA_FORK_MODE) |
| 150 | 150 | // asset["url"] = asset["url"].replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG |
| 151 | 151 | |
| 152 | 152 | console.debug(DEBUG_PREFIX, 'Checking asset', asset['.id'], asset['.url']); |
| 153 | 153 | |
| 154 | 154 | const assetInstall = async function () { |
| 155 | 155 | element.off('click'); |
| 156 | 156 | label.removeClass('fa-download'); |
| 157 | 157 | this.classList.add('asset-download-button-loading'); |
| 158 | 158 | await installAsset(asset['.url'], assetType, asset['.id']); |
| 159 | 159 | label.addClass('fa-check'); |
| 160 | 160 | this.classList.remove('asset-download-button-loading'); |
| 161 | 161 | element.on('click', assetDelete); |
| @@ -173,11 +173,11 @@ async function downloadAssetsList(url) { | ||
| 173 | 173 | const assetDelete = async function () { |
| 174 | 174 | if (assetType === 'character') { |
| 175 | 175 | toastr.error('Go to the characters menu to delete a character.', 'Character deletion not supported'); |
| 176 | 176 | await executeSlashCommandsWithOptions(`/go ${asset['.id']}`); |
| 177 | 177 | return; |
| 178 | 178 | } |
| 179 | 179 | element.off('click'); |
| 180 | 180 | await deleteAsset(assetType, asset['.id']); |
| 181 | 181 | label.removeClass('fa-check'); |
| 182 | 182 | label.removeClass('redOverlayGlow'); |
| 183 | 183 | label.removeClass('fa-trash'); |
| @@ -186,7 +186,7 @@ async function downloadAssetsList(url) { | ||
| 186 | 186 | element.on('click', assetInstall); |
| 187 | 187 | }; |
| 188 | 188 | |
| 189 | 189 | if (isAssetInstalled(assetType, asset['.id'])) { |
| 190 | 190 | console.debug(DEBUG_PREFIX, 'installed, checked'); |
| 191 | 191 | label.toggleClass('fa-download'); |
| 192 | 192 | label.toggleClass('fa-check'); |
| @@ -207,14 +207,14 @@ async function downloadAssetsList(url) { | ||
| 207 | 207 | element.on('click', assetInstall); |
| 208 | 208 | } |
| 209 | 209 | |
| 210 | 210 | console.debug(DEBUG_PREFIX, 'Created element for ', asset['.id']); |
| 211 | 211 | |
| 212 | 212 | const displayName = DOMPurify.sanitize(asset['.name'] || asset['.id']); |
| 213 | 213 | const description = DOMPurify.sanitize(asset['.description'] || ''); |
| 214 | 214 | const url = isValidUrl(asset['.url']) ? asset['.url'] : ''; |
| 215 | 215 | const title = assetType === 'extension' ? t`Extension repo/guide:` + ` ${url}` : t`Preview in browser`; |
| 216 | 216 | const previewIcon = (assetType === 'extension' || assetType === 'character') ? 'fa-arrow-up-right-from-square' : 'fa-headphones-simple'; |
| 217 | 217 | const toolTag = assetType === 'extension' && asset['.tool']; |
| 218 | 218 | const author = url && assetType === 'extension' ? getAuthorFromUrl(url) : EMPTY_AUTHOR; |
| 219 | 219 | |
| 220 | 220 | const assetBlock = $('<i></i>') |
| @@ -246,7 +246,7 @@ async function downloadAssetsList(url) { | ||
| 246 | 246 | if (asset.highlight) { |
| 247 | 247 | assetBlock.find('.asset-name').append('<i class="fa-solid fa-sm fa-trophy"></i>'); |
| 248 | 248 | } |
| 249 | 249 | assetBlock.find('.asset-name').prepend(`<div class="avatar"><img src="${asset['.url']}" alt="${displayName}"></div>`); |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | 252 | assetBlock.addClass('asset-block'); |
| @@ -204,7 +204,7 @@ async function sendCaptionedMessage(caption, image, mimeType) { | ||
| 204 | 204 | inline_image: !!extension_settings.caption.show_in_chat, |
| 205 | 205 | }, |
| 206 | 206 | }; |
| 207 | 207 | chat_metadata['.tainted'] = true; |
| 208 | 208 | context.chat.push(message); |
| 209 | 209 | const messageId = context.chat.length - 1; |
| 210 | 210 | await eventSource.emit(event_types.MESSAGE_SENT, messageId); |
| @@ -456,7 +456,7 @@ async function onChatEvent() { | ||
| 456 | 456 | .catch(console.error) |
| 457 | 457 | .finally(() => { |
| 458 | 458 | lastMessageId = context.chat?.length ?? null; |
| 459 | 459 | lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['.mes']) ?? ''); |
| 460 | 460 | }); |
| 461 | 461 | } |
| 462 | 462 | |
| @@ -185,7 +185,7 @@ const init = async () => { | ||
| 185 | 185 | buttons.show(); |
| 186 | 186 | settings.onSave = ()=>buttons.refresh(); |
| 187 | 187 | |
| 188 | 188 | window['globalThis.executeQuickReplyByName'] = async(name, args = {}, options = {}) => { |
| 189 | 189 | let qr = [ |
| 190 | 190 | ...settings.config.setList, |
| 191 | 191 | ...(settings.chatConfig?.setList ?? []), |
| @@ -77,7 +77,7 @@ export class SlashCommandHandler { | ||
| 77 | 77 | }, |
| 78 | 78 | }; |
| 79 | 79 | |
| 80 | 80 | window['globalThis.qrEnumProviderExecutables'] = localEnumProviders.qrExecutables; |
| 81 | 81 | |
| 82 | 82 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr', |
| 83 | 83 | callback: (_, value) => this.executeQuickReplyByIndex(Number(value)), |
| @@ -426,7 +426,7 @@ function processTriggers(chat, _, abort, type) { | ||
| 426 | 426 | } |
| 427 | 427 | } |
| 428 | 428 | |
| 429 | 429 | window['globalThis.SD_ProcessTriggers'] = processTriggers; |
| 430 | 430 | |
| 431 | 431 | function getSdRequestBody() { |
| 432 | 432 | switch (extension_settings.sd.source) { |
| @@ -207,13 +207,13 @@ class CoquiTtsProvider { | ||
| 207 | 207 | this.settings.customVoices = {}; |
| 208 | 208 | for (let voiceName in this.settings.voiceMapDict) { |
| 209 | 209 | const voiceId = this.settings.voiceMapDict[voiceName]; |
| 210 | 210 | this.settings.customVoices[voiceName] = voiceId['.model_id']; |
| 211 | 211 | |
| 212 | 212 | if (voiceId['.model_language'] != null) |
| 213 | 213 | this.settings.customVoices[voiceName] += '[' + voiceId['.model_language'] + ']'; |
| 214 | 214 | |
| 215 | 215 | if (voiceId['.model_speaker'] != null) |
| 216 | 216 | this.settings.customVoices[voiceName] += '[' + voiceId['.model_speaker'] + ']'; |
| 217 | 217 | } |
| 218 | 218 | |
| 219 | 219 | // Update UI select list with voices |
| @@ -493,8 +493,8 @@ class CoquiTtsProvider { | ||
| 493 | 493 | .append('<option value="none">Select language</option>') |
| 494 | 494 | .val('none'); |
| 495 | 495 | |
| 496 | 496 | for (let i = 0; i < model_settings['.languages'].length; i++) { |
| 497 | 497 | const language_label = JSON.stringify(model_settings['.languages'][i]).replaceAll('"', ''); |
| 498 | 498 | $('#coqui_api_model_settings_language').append(new Option(language_label, i)); |
| 499 | 499 | } |
| 500 | 500 | } |
| @@ -512,8 +512,8 @@ class CoquiTtsProvider { | ||
| 512 | 512 | .append('<option value="none">Select speaker</option>') |
| 513 | 513 | .val('none'); |
| 514 | 514 | |
| 515 | 515 | for (let i = 0; i < model_settings['.speakers'].length; i++) { |
| 516 | 516 | const speaker_label = JSON.stringify(model_settings['.speakers'][i]).replaceAll('"', ''); |
| 517 | 517 | $('#coqui_api_model_settings_speaker').append(new Option(speaker_label, i)); |
| 518 | 518 | } |
| 519 | 519 | } |
| @@ -525,7 +525,7 @@ class CoquiTtsProvider { | ||
| 525 | 525 | $('#coqui_api_model_install_status').show(); |
| 526 | 526 | |
| 527 | 527 | // Check if already installed and propose to do it otherwise |
| 528 | 528 | const model_id = modelDict[model_language][model_dataset][model_name]['.id']; |
| 529 | 529 | console.debug(DEBUG_PREFIX,'Check if model is already installed',model_id); |
| 530 | 530 | let result = await CoquiTtsProvider.checkmodel_state(model_id); |
| 531 | 531 | result = await result.json(); |
| @@ -175,7 +175,7 @@ class CosyVoiceProvider { | ||
| 175 | 175 | }; |
| 176 | 176 | |
| 177 | 177 | if (streaming) { |
| 178 | 178 | params['.streaming'] = 1; |
| 179 | 179 | } |
| 180 | 180 | |
| 181 | 181 | const url = `${this.settings.provider_endpoint}/`; |
| @@ -126,16 +126,16 @@ class ElevenLabsTtsProvider { | ||
| 126 | 126 | this.settings = this.defaultSettings; |
| 127 | 127 | |
| 128 | 128 | // Migrate old settings |
| 129 | 129 | if (settings['.multilingual'] !== undefined) { |
| 130 | 130 | settings.model = settings.multilingual ? 'eleven_multilingual_v1' : 'eleven_monolingual_v1'; |
| 131 | 131 | delete settings['.multilingual']; |
| 132 | 132 | } |
| 133 | 133 | |
| 134 | 134 | if (Object.hasOwn(settings, 'apiKey')) { |
| 135 | 135 | if (settings.apiKey && !secret_state[SECRET_KEYS.ELEVENLABS]){ |
| 136 | 136 | await writeSecret(SECRET_KEYS.ELEVENLABS, settings.apiKey); |
| 137 | 137 | } |
| 138 | 138 | delete settings['.apiKey']; |
| 139 | 139 | } |
| 140 | 140 | |
| 141 | 141 | $('#elevenlabs_tts_key').toggleClass('success', !!secret_state[SECRET_KEYS.ELEVENLABS]); |
| @@ -284,7 +284,7 @@ function debugTtsPlayback() { | ||
| 284 | 284 | }, |
| 285 | 285 | )); |
| 286 | 286 | } |
| 287 | 287 | window['globalThis.debugTtsPlayback'] = debugTtsPlayback; |
| 288 | 288 | |
| 289 | 289 | //##################// |
| 290 | 290 | // Audio Control // |
| @@ -321,8 +321,8 @@ async function playAudioData(audioJob) { | ||
| 321 | 321 | const srcUrl = await getBase64Async(audioBlob); |
| 322 | 322 | |
| 323 | 323 | // VRM lip sync |
| 324 | 324 | if (extension_settings.vrm?.enabled && typeof window['globalThis.vrmLipSync'] === 'function') { |
| 325 | 325 | await window['globalThis.vrmLipSync'](audioBlob, char); |
| 326 | 326 | } |
| 327 | 327 | |
| 328 | 328 | audioElement.src = srcUrl; |
| @@ -339,7 +339,7 @@ async function playAudioData(audioJob) { | ||
| 339 | 339 | }); |
| 340 | 340 | } |
| 341 | 341 | |
| 342 | 342 | window['globalThis.tts_preview'] = function (id) { |
| 343 | 343 | const audio = document.getElementById(id); |
| 344 | 344 | |
| 345 | 345 | if (audio instanceof HTMLAudioElement && !$(audio).data('disabled')) { |
| @@ -474,8 +474,8 @@ function completeTtsJob() { | ||
| 474 | 474 | async function tts(text, voiceId, char, voiceMapKey = null) { |
| 475 | 475 | async function processResponse(response) { |
| 476 | 476 | // RVC injection |
| 477 | 477 | if (typeof window['globalThis.rvcVoiceConversion'] === 'function' && extension_settings.rvc.enabled) |
| 478 | 478 | response = await window['globalThis.rvcVoiceConversion'](response, char, text); |
| 479 | 479 | |
| 480 | 480 | await addAudioJob(response, char); |
| 481 | 481 | } |
| @@ -806,7 +806,7 @@ async function playFullConversation() { | ||
| 806 | 806 | ttsJobQueue = chat; |
| 807 | 807 | } |
| 808 | 808 | |
| 809 | 809 | window['globalThis.playFullConversation'] = playFullConversation; |
| 810 | 810 | |
| 811 | 811 | //#############################// |
| 812 | 812 | // Extension UI and Settings // |
| @@ -746,7 +746,7 @@ function overlapChunks(chunk, index, chunks, overlapSize) { | ||
| 746 | 746 | return overlappedChunk; |
| 747 | 747 | } |
| 748 | 748 | |
| 749 | 749 | window['globalThis.vectors_rearrangeChat'] = rearrangeChat; |
| 750 | 750 | |
| 751 | 751 | const onChatEvent = debounce(async () => await moduleWorker.update(), debounce_timeout.relaxed); |
| 752 | 752 | |
| @@ -1620,7 +1620,7 @@ jQuery(async () => { | ||
| 1620 | 1620 | } |
| 1621 | 1621 | |
| 1622 | 1622 | // Migrate from old settings |
| 1623 | 1623 | if (settings['.enabled']) { |
| 1624 | 1624 | settings.enabled_chats = true; |
| 1625 | 1625 | } |
| 1626 | 1626 | |
| @@ -274,8 +274,8 @@ export async function getGroupChat(groupId, reload = false) { | ||
| 274 | 274 | } |
| 275 | 275 | |
| 276 | 276 | // Add integrity slug if missing |
| 277 | 277 | if (!metadata['.integrity']) { |
| 278 | 278 | metadata['.integrity'] = uuidv4(); |
| 279 | 279 | } |
| 280 | 280 | |
| 281 | 281 | await loadItemizedPrompts(getCurrentChatId()); |
| @@ -559,8 +559,8 @@ export function getGroupCharacterCardsLazy(groupId, characterId) { | ||
| 559 | 559 | return values.filter(x => x.length).join('\n'); |
| 560 | 560 | } |
| 561 | 561 | |
| 562 | 562 | const scenarioOverride = String(chat_metadata['.scenario'] || ''); |
| 563 | 563 | const mesExamplesOverride = String(chat_metadata['.mes_example'] || ''); |
| 564 | 564 | |
| 565 | 565 | return createLazyFields({ |
| 566 | 566 | description: () => collectField('Description', c => c.description), |
| @@ -593,16 +593,16 @@ async function getFirstCharacterMessage(character) { | ||
| 593 | 593 | } |
| 594 | 594 | |
| 595 | 595 | const mes = {}; |
| 596 | 596 | mes['.is_user'] = false; |
| 597 | 597 | mes['.is_system'] = false; |
| 598 | 598 | mes['.name'] = character.name; |
| 599 | 599 | mes['.send_date'] = getMessageTimeStamp(); |
| 600 | 600 | mes['.original_avatar'] = character.avatar; |
| 601 | 601 | mes['.extra'] = { 'gen_id': Date.now() * Math.random() * 1000000 }; |
| 602 | 602 | mes['.mes'] = messageText |
| 603 | 603 | ? substituteParams(messageText.trim(), { name2Override: character.name }) |
| 604 | 604 | : ''; |
| 605 | 605 | mes['.force_avatar'] = |
| 606 | 606 | character.avatar != 'none' |
| 607 | 607 | ? getThumbnailUrl('avatar', character.avatar) |
| 608 | 608 | : default_avatar; |
| @@ -628,7 +628,7 @@ async function saveGroupChat(groupId, shouldSaveGroup, force = false) { | ||
| 628 | 628 | return; |
| 629 | 629 | } |
| 630 | 630 | const chatId = group.chat_id; |
| 631 | 631 | group['.date_last_chat'] = Date.now(); |
| 632 | 632 | /** @type {ChatHeader} */ |
| 633 | 633 | const chatHeader = { |
| 634 | 634 | chat_metadata: { ...chat_metadata }, |
| @@ -2179,7 +2179,7 @@ export async function openGroupChat(groupId, chatId) { | ||
| 2179 | 2179 | await clearChat(); |
| 2180 | 2180 | chat.length = 0; |
| 2181 | 2181 | group.chat_id = chatId; |
| 2182 | 2182 | group['.date_last_chat'] = Date.now(); |
| 2183 | 2183 | updateChatMetadata({}, true); |
| 2184 | 2184 | |
| 2185 | 2185 | await editGroup(groupId, true, false); |
| @@ -205,11 +205,11 @@ export async function generateHorde(prompt, params, signal, reportProgress) { | ||
| 205 | 205 | delete params.prompt; |
| 206 | 206 | |
| 207 | 207 | // No idea what these do |
| 208 | 208 | params['.n'] = 1; |
| 209 | 209 | params['.frmtadsnsp'] = false; |
| 210 | 210 | params['.frmtrmblln'] = false; |
| 211 | 211 | params['.frmtrmspch'] = false; |
| 212 | 212 | params['.frmttriminc'] = false; |
| 213 | 213 | |
| 214 | 214 | const payload = { |
| 215 | 215 | 'prompt': prompt, |
| @@ -258,8 +258,8 @@ function addLanguagesToDropdown() { | ||
| 258 | 258 | const uiLanguageSelects = $('#ui_language_select, #onboarding_ui_language_select'); |
| 259 | 259 | for (const langObj of langs) { // Set the value to the language code |
| 260 | 260 | const option = document.createElement('option'); |
| 261 | 261 | option.value = langObj['.lang']; // Set the value to the language code |
| 262 | 262 | option.innerText = langObj['.display']; // Set the display text to the language name |
| 263 | 263 | uiLanguageSelects.append(option); |
| 264 | 264 | } |
| 265 | 265 | |
| @@ -161,7 +161,7 @@ export async function loadInstructMode(data) { | ||
| 161 | 161 | */ |
| 162 | 162 | export function updateBindModelTemplatesState() { |
| 163 | 163 | const bindModelTemplates = power_user.model_templates_mappings[online_status] ?? power_user.model_templates_mappings[power_user.chat_template_hash]; |
| 164 | 164 | const bindingsMatch = (bindModelTemplates && power_user.context.preset === bindModelTemplates['.context'] && (!power_user.instruct.enabled || power_user.instruct.preset === bindModelTemplates['.instruct'])) ?? false; |
| 165 | 165 | const currentState = $('#bind_model_templates').prop('checked'); |
| 166 | 166 | if (bindingsMatch === currentState) { |
| 167 | 167 | // No change needed |
| @@ -312,14 +312,14 @@ export class MacrosParser { | ||
| 312 | 312 | * @returns {number} The hashed chat id |
| 313 | 313 | */ |
| 314 | 314 | function getChatIdHash() { |
| 315 | 315 | const cachedIdHash = chat_metadata['.chat_id_hash']; |
| 316 | 316 | |
| 317 | 317 | // If chat_id_hash is not already set, calculate it |
| 318 | 318 | if (!cachedIdHash) { |
| 319 | 319 | // Use the main_chat if it's available, otherwise get the current chat ID |
| 320 | 320 | const chatId = chat_metadata['.main_chat'] ?? getCurrentChatId(); |
| 321 | 321 | const chatIdHash = getStringHash(chatId); |
| 322 | 322 | chat_metadata['.chat_id_hash'] = chatIdHash; |
| 323 | 323 | return chatIdHash; |
| 324 | 324 | } |
| 325 | 325 | |
| @@ -361,7 +361,7 @@ export function getLastMessageId({ exclude_swipe_in_propress = true, filter = nu | ||
| 361 | 361 | * @returns {number|null} The ID of the first message in the context |
| 362 | 362 | */ |
| 363 | 363 | function getFirstIncludedMessageId() { |
| 364 | 364 | return chat_metadata['.lastInContextMessageId']; |
| 365 | 365 | } |
| 366 | 366 | |
| 367 | 367 | /** |
| @@ -104,7 +104,7 @@ function getLastCharMessage() { | ||
| 104 | 104 | } |
| 105 | 105 | |
| 106 | 106 | function getFirstIncludedMessageId() { |
| 107 | 107 | const value = chat_metadata['.lastInContextMessageId']; |
| 108 | 108 | return typeof value === 'number' ? value : null; |
| 109 | 109 | } |
| 110 | 110 | |
| @@ -433,13 +433,13 @@ export function registerCoreMacros() { | ||
| 433 | 433 | } |
| 434 | 434 | |
| 435 | 435 | function getChatIdHash() { |
| 436 | 436 | const cachedIdHash = chat_metadata['.chat_id_hash']; |
| 437 | 437 | if (typeof cachedIdHash === 'number') { |
| 438 | 438 | return cachedIdHash; |
| 439 | 439 | } |
| 440 | 440 | |
| 441 | 441 | const chatId = chat_metadata['.main_chat'] ?? getCurrentChatId(); |
| 442 | 442 | const chatIdHash = getStringHash(chatId); |
| 443 | 443 | chat_metadata['.chat_id_hash'] = chatIdHash; |
| 444 | 444 | return chatIdHash; |
| 445 | 445 | } |
| @@ -529,8 +529,8 @@ function setOpenAIMessages(chat) { | ||
| 529 | 529 | const currentModel = getChatCompletionModel(); |
| 530 | 530 | |
| 531 | 531 | for (let i = chat.length - 1; i >= 0; i--) { |
| 532 | 532 | let role = chat[j]['.is_user'] ? 'user' : 'assistant'; |
| 533 | 533 | let content = chat[j]['.mes']; |
| 534 | 534 | |
| 535 | 535 | // If this symbol flag is set, completely ignore the message. |
| 536 | 536 | // This can be used to hide messages without affecting the number of messages in the chat. |
| @@ -567,7 +567,7 @@ function setOpenAIMessages(chat) { | ||
| 567 | 567 | // remove caret return (waste of tokens) |
| 568 | 568 | content = content.replace(/\r/gm, ''); |
| 569 | 569 | |
| 570 | 570 | const name = chat[j]['.name']; |
| 571 | 571 | const media = chat[j]?.extra?.media; |
| 572 | 572 | const mediaDisplay = getMediaDisplay(chat[j]); |
| 573 | 573 | const mediaIndex = getMediaIndex(chat[j]); |
| @@ -2588,14 +2588,14 @@ export async function createGenerationParameters(settings, model, type, messages | ||
| 2588 | 2588 | |
| 2589 | 2589 | if (settings.reverse_proxy && proxySupportedSources.includes(settings.chat_completion_source)) { |
| 2590 | 2590 | await validateReverseProxy(); |
| 2591 | 2591 | generate_data['.reverse_proxy'] = settings.reverse_proxy; |
| 2592 | 2592 | generate_data['.proxy_password'] = settings.proxy_password; |
| 2593 | 2593 | } |
| 2594 | 2594 | |
| 2595 | 2595 | // Add logprobs request (max 5 per OpenAI docs) |
| 2596 | 2596 | const useLogprobs = !!power_user.request_token_probabilities; |
| 2597 | 2597 | if (useLogprobs && logprobsSupportedSources.includes(settings.chat_completion_source)) { |
| 2598 | 2598 | generate_data['.logprobs'] = 5; |
| 2599 | 2599 | } |
| 2600 | 2600 | |
| 2601 | 2601 | // Remove logit bias/logprobs/stop-strings if not supported by the model |
| @@ -2610,67 +2610,67 @@ export async function createGenerationParameters(settings, model, type, messages | ||
| 2610 | 2610 | } |
| 2611 | 2611 | |
| 2612 | 2612 | if (settings.chat_completion_source === chat_completion_sources.CLAUDE) { |
| 2613 | 2613 | generate_data['.top_k'] = Number(settings.top_k_openai); |
| 2614 | 2614 | generate_data['.use_sysprompt'] = settings.use_sysprompt; |
| 2615 | 2615 | generate_data['.stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings. |
| 2616 | 2616 | // Don't add a prefill on quiet gens (summarization) and when using continue prefill. |
| 2617 | 2617 | if (type !== 'quiet' && !(type === 'continue' && settings.continue_prefill)) { |
| 2618 | 2618 | generate_data['.assistant_prefill'] = type === 'impersonate' |
| 2619 | 2619 | ? substituteParams(settings.assistant_impersonation) |
| 2620 | 2620 | : substituteParams(settings.assistant_prefill); |
| 2621 | 2621 | } |
| 2622 | 2622 | } |
| 2623 | 2623 | |
| 2624 | 2624 | if (settings.chat_completion_source === chat_completion_sources.OPENROUTER) { |
| 2625 | 2625 | generate_data['.top_k'] = Number(settings.top_k_openai); |
| 2626 | 2626 | generate_data['.min_p'] = Number(settings.min_p_openai); |
| 2627 | 2627 | generate_data['.repetition_penalty'] = Number(settings.repetition_penalty_openai); |
| 2628 | 2628 | generate_data['.top_a'] = Number(settings.top_a_openai); |
| 2629 | 2629 | generate_data['.use_fallback'] = settings.openrouter_use_fallback; |
| 2630 | 2630 | generate_data['.provider'] = settings.openrouter_providers; |
| 2631 | 2631 | generate_data['.allow_fallbacks'] = settings.openrouter_allow_fallbacks; |
| 2632 | 2632 | generate_data['.middleout'] = settings.openrouter_middleout; |
| 2633 | 2633 | } |
| 2634 | 2634 | |
| 2635 | 2635 | if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(settings.chat_completion_source)) { |
| 2636 | 2636 | const stopStringsLimit = 5; |
| 2637 | 2637 | generate_data['.top_k'] = Number(settings.top_k_openai); |
| 2638 | 2638 | generate_data['.stop'] = getCustomStoppingStrings(stopStringsLimit).slice(0, stopStringsLimit).filter(x => x.length >= 1 && x.length <= 16); |
| 2639 | 2639 | generate_data['.use_sysprompt'] = settings.use_sysprompt; |
| 2640 | 2640 | if (settings.chat_completion_source === chat_completion_sources.VERTEXAI) { |
| 2641 | 2641 | generate_data['.vertexai_auth_mode'] = settings.vertexai_auth_mode; |
| 2642 | 2642 | generate_data['.vertexai_region'] = settings.vertexai_region; |
| 2643 | 2643 | generate_data['.vertexai_express_project_id'] = settings.vertexai_express_project_id; |
| 2644 | 2644 | } |
| 2645 | 2645 | } |
| 2646 | 2646 | |
| 2647 | 2647 | if (settings.chat_completion_source === chat_completion_sources.MISTRALAI) { |
| 2648 | 2648 | generate_data['.safe_prompt'] = false; // already defaults to false, but just incase they change that in the future. |
| 2649 | 2649 | generate_data['.stop'] = getCustomStoppingStrings(); // Mistral shouldn't have limits on stop strings. |
| 2650 | 2650 | } |
| 2651 | 2651 | |
| 2652 | 2652 | if (settings.chat_completion_source === chat_completion_sources.CUSTOM) { |
| 2653 | 2653 | generate_data['.custom_url'] = settings.custom_url; |
| 2654 | 2654 | generate_data['.custom_include_body'] = settings.custom_include_body; |
| 2655 | 2655 | generate_data['.custom_exclude_body'] = settings.custom_exclude_body; |
| 2656 | 2656 | generate_data['.custom_include_headers'] = settings.custom_include_headers; |
| 2657 | 2657 | } |
| 2658 | 2658 | |
| 2659 | 2659 | if (settings.chat_completion_source === chat_completion_sources.COHERE) { |
| 2660 | 2660 | // Clamp to 0.01 -> 0.99 |
| 2661 | 2661 | generate_data['.top_p'] = Math.min(Math.max(Number(settings.top_p_openai), 0.01), 0.99); |
| 2662 | 2662 | generate_data['.top_k'] = Number(settings.top_k_openai); |
| 2663 | 2663 | // Clamp to 0 -> 1 |
| 2664 | 2664 | generate_data['.frequency_penalty'] = Math.min(Math.max(Number(settings.freq_pen_openai), 0), 1); |
| 2665 | 2665 | generate_data['.presence_penalty'] = Math.min(Math.max(Number(settings.pres_pen_openai), 0), 1); |
| 2666 | 2666 | generate_data['.stop'] = getCustomStoppingStrings(5); |
| 2667 | 2667 | } |
| 2668 | 2668 | |
| 2669 | 2669 | if (settings.chat_completion_source === chat_completion_sources.PERPLEXITY) { |
| 2670 | 2670 | generate_data['.top_k'] = Number(settings.top_k_openai); |
| 2671 | 2671 | generate_data['.frequency_penalty'] = Number(settings.freq_pen_openai); |
| 2672 | 2672 | generate_data['.presence_penalty'] = Number(settings.pres_pen_openai); |
| 2673 | 2673 | delete generate_data['.stop']; |
| 2674 | 2674 | } |
| 2675 | 2675 | |
| 2676 | 2676 | // https://console.groq.com/docs/openai |
| @@ -2713,35 +2713,35 @@ export async function createGenerationParameters(settings, model, type, messages | ||
| 2713 | 2713 | |
| 2714 | 2714 | // https://docs.electronhub.ai/api-reference/chat/completions |
| 2715 | 2715 | if (settings.chat_completion_source === chat_completion_sources.ELECTRONHUB) { |
| 2716 | 2716 | generate_data['.top_k'] = Number(settings.top_k_openai); |
| 2717 | 2717 | } |
| 2718 | 2718 | |
| 2719 | 2719 | if (settings.chat_completion_source === chat_completion_sources.CHUTES) { |
| 2720 | 2720 | generate_data['.min_p'] = Number(settings.min_p_openai); |
| 2721 | 2721 | generate_data['.top_k'] = settings.top_k_openai > 0 ? Number(settings.top_k_openai) : undefined; |
| 2722 | 2722 | generate_data['.repetition_penalty'] = Number(settings.repetition_penalty_openai); |
| 2723 | 2723 | generate_data['.stop'] = getCustomStoppingStrings(); |
| 2724 | 2724 | } |
| 2725 | 2725 | |
| 2726 | 2726 | // https://docs.z.ai/api-reference/llm/chat-completion |
| 2727 | 2727 | if (settings.chat_completion_source === chat_completion_sources.ZAI) { |
| 2728 | 2728 | generate_data['.top_p'] = generate_data.top_p || 0.01; |
| 2729 | 2729 | generate_data['.stop'] = getCustomStoppingStrings(1); |
| 2730 | 2730 | generate_data['.zai_endpoint'] = settings.zai_endpoint || ZAI_ENDPOINT.COMMON; |
| 2731 | 2731 | delete generate_data.presence_penalty; |
| 2732 | 2732 | delete generate_data.frequency_penalty; |
| 2733 | 2733 | } |
| 2734 | 2734 | |
| 2735 | 2735 | // https://docs.nano-gpt.com/api-reference/endpoint/chat-completion#temperature-&-nucleus |
| 2736 | 2736 | if (settings.chat_completion_source === chat_completion_sources.NANOGPT) { |
| 2737 | 2737 | generate_data['.top_k'] = Number(settings.top_k_openai); |
| 2738 | 2738 | generate_data['.min_p'] = Number(settings.min_p_openai); |
| 2739 | 2739 | generate_data['.repetition_penalty'] = Number(settings.repetition_penalty_openai); |
| 2740 | 2740 | generate_data['.top_a'] = Number(settings.top_a_openai); |
| 2741 | 2741 | } |
| 2742 | 2742 | |
| 2743 | 2743 | if (seedSupportedSources.includes(settings.chat_completion_source) && settings.seed >= 0) { |
| 2744 | 2744 | generate_data['.seed'] = settings.seed; |
| 2745 | 2745 | } |
| 2746 | 2746 | |
| 2747 | 2747 | if ([chat_completion_sources.OPENAI, chat_completion_sources.AZURE_OPENAI].includes(settings.chat_completion_source) && /^(o1|o3|o4)/.test(model) || |
| @@ -6162,7 +6162,7 @@ async function onVertexAIValidateServiceAccount() { | ||
| 6162 | 6162 | } |
| 6163 | 6163 | |
| 6164 | 6164 | // Save to backend secret storage |
| 6165 | 6165 | const keyLabel = serviceAccount['.client_email'] || ''; |
| 6166 | 6166 | await writeSecret(SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT, jsonContent, keyLabel); |
| 6167 | 6167 | |
| 6168 | 6168 | // Show success status |
| @@ -835,7 +835,7 @@ async function renamePersona(avatarId) { | ||
| 835 | 835 | async function selectCurrentPersona({ toastPersonaNameChange = true } = {}) { |
| 836 | 836 | const personaName = power_user.personas[user_avatar]; |
| 837 | 837 | if (personaName) { |
| 838 | 838 | const shouldAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata['.persona']; |
| 839 | 839 | |
| 840 | 840 | if (personaName !== name1) { |
| 841 | 841 | console.log(`Auto-updating user name to ${personaName}`); |
| @@ -871,7 +871,7 @@ async function selectCurrentPersona({ toastPersonaNameChange = true } = {}) { | ||
| 871 | 871 | |
| 872 | 872 | // Update the locked persona if setting is enabled |
| 873 | 873 | if (shouldAutoLock) { |
| 874 | 874 | chat_metadata['.persona'] = user_avatar; |
| 875 | 875 | console.log(`Auto locked persona to ${user_avatar}`); |
| 876 | 876 | if (toastPersonaNameChange && power_user.persona_show_notifications) { |
| 877 | 877 | toastr.success(t`Persona ${personaName} selected and auto-locked to current chat`, t`Persona Selected`); |
| @@ -914,7 +914,7 @@ export function isPersonaLocked(type = 'chat') { | ||
| 914 | 914 | case 'default': |
| 915 | 915 | return power_user.default_persona === user_avatar; |
| 916 | 916 | case 'chat': |
| 917 | 917 | return chat_metadata['.persona'] == user_avatar; |
| 918 | 918 | case 'character': { |
| 919 | 919 | return !!power_user.persona_descriptions[user_avatar]?.connections?.some(isPersonaConnectionLocked); |
| 920 | 920 | } |
| @@ -960,9 +960,9 @@ async function unlockPersona(type = 'chat') { | ||
| 960 | 960 | break; |
| 961 | 961 | } |
| 962 | 962 | case 'chat': { |
| 963 | 963 | if (chat_metadata['.persona']) { |
| 964 | 964 | console.log(`Unlocking persona ${user_avatar} from this chat`); |
| 965 | 965 | delete chat_metadata['.persona']; |
| 966 | 966 | await saveMetadata(); |
| 967 | 967 | if (power_user.persona_show_notifications && !isPersonaPanelOpen()) { |
| 968 | 968 | toastr.info(t`Persona ${name1} is now unlocked from this chat.`, t`Persona Unlocked`); |
| @@ -1021,7 +1021,7 @@ async function lockPersona(type = 'chat') { | ||
| 1021 | 1021 | } |
| 1022 | 1022 | case 'chat': { |
| 1023 | 1023 | console.log(`Locking persona ${user_avatar} to this chat`); |
| 1024 | 1024 | chat_metadata['.persona'] = user_avatar; |
| 1025 | 1025 | saveMetadataDebounced(); |
| 1026 | 1026 | if (power_user.persona_show_notifications && !isPersonaPanelOpen()) { |
| 1027 | 1027 | toastr.success(t`User persona ${name1} is locked to ${name2} in this chat`, t`Persona Locked`); |
| @@ -1105,9 +1105,9 @@ async function deleteUserAvatar() { | ||
| 1105 | 1105 | power_user.default_persona = null; |
| 1106 | 1106 | } |
| 1107 | 1107 | |
| 1108 | 1108 | if (avatarId === chat_metadata['.persona']) { |
| 1109 | 1109 | toastr.warning(t`The locked persona was deleted. You will need to set a new persona for this chat.`, t`Persona Deleted`); |
| 1110 | 1110 | delete chat_metadata['.persona']; |
| 1111 | 1111 | await saveMetadata(); |
| 1112 | 1112 | } |
| 1113 | 1113 | |
| @@ -1317,7 +1317,7 @@ async function toggleDefaultPersona(avatarId, { quiet = false } = {}) { | ||
| 1317 | 1317 | */ |
| 1318 | 1318 | function getPersonaStates(avatarId) { |
| 1319 | 1319 | const isDefaultPersona = power_user.default_persona === avatarId; |
| 1320 | 1320 | const hasChatLock = chat_metadata['.persona'] == avatarId; |
| 1321 | 1321 | |
| 1322 | 1322 | /** @type {PersonaConnection[]} */ |
| 1323 | 1323 | const connections = power_user.persona_descriptions[avatarId]?.connections; |
| @@ -1413,13 +1413,13 @@ function updatePersonaUIStates({ navigateToCurrent = false } = {}) { | ||
| 1413 | 1413 | * @returns {PersonaLockInfo} An object containing flags and a message describing the persona lock status. |
| 1414 | 1414 | */ |
| 1415 | 1415 | function getPersonaTemporaryLockInfo() { |
| 1416 | 1416 | const hasDifferentChatLock = !!chat_metadata['.persona'] && chat_metadata['.persona'] !== user_avatar; |
| 1417 | 1417 | const hasDifferentDefaultLock = power_user.default_persona && power_user.default_persona !== user_avatar; |
| 1418 | 1418 | const isTemporary = hasDifferentChatLock || (!chat_metadata['.persona'] && hasDifferentDefaultLock); |
| 1419 | 1419 | const info = isTemporary ? t`A different persona is locked to this chat, or you have a different default persona set. The currently selected persona will only be temporary, and resets on reload. Consider locking this persona to the chat if you want to permanently use it.` |
| 1420 | 1420 | + '\n\n' |
| 1421 | 1421 | + t`Current Persona: ${power_user.personas[user_avatar]}` |
| 1422 | 1422 | + (hasDifferentChatLock ? '\n' + t`Chat persona: ${power_user.personas[chat_metadata['.persona']]}` : '') |
| 1423 | 1423 | + (hasDifferentDefaultLock ? '\n' + t`Default persona: ${power_user.personas[power_user.default_persona]}` : '') : ''; |
| 1424 | 1424 | |
| 1425 | 1425 | return { |
| @@ -1458,14 +1458,14 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | ||
| 1458 | 1458 | let connectType = null; |
| 1459 | 1459 | |
| 1460 | 1460 | // If persona is locked in chat metadata, select it |
| 1461 | 1461 | if (chat_metadata['.persona']) { |
| 1462 | 1462 | console.log(`Using locked persona ${chat_metadata['.persona']}`); |
| 1463 | 1463 | chatPersona = chat_metadata['.persona']; |
| 1464 | 1464 | |
| 1465 | 1465 | // Verify it exists |
| 1466 | 1466 | if (!userAvatars.includes(chatPersona)) { |
| 1467 | 1467 | console.warn('Chat-locked persona avatar not found, unlocking persona'); |
| 1468 | 1468 | delete chat_metadata['.persona']; |
| 1469 | 1469 | saveSettingsDebounced(); |
| 1470 | 1470 | chatPersona = ''; |
| 1471 | 1471 | } |
| @@ -1528,9 +1528,9 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | ||
| 1528 | 1528 | } |
| 1529 | 1529 | |
| 1530 | 1530 | // Whatever way we selected a persona, if it doesn't exist, unlock this chat |
| 1531 | 1531 | if (chat_metadata['.persona'] && !userAvatars.includes(chat_metadata['.persona'])) { |
| 1532 | 1532 | console.warn('Persona avatar not found, unlocking persona'); |
| 1533 | 1533 | delete chat_metadata['.persona']; |
| 1534 | 1534 | } |
| 1535 | 1535 | |
| 1536 | 1536 | // Default persona missing |
| @@ -1542,7 +1542,7 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | ||
| 1542 | 1542 | |
| 1543 | 1543 | // Persona avatar found, select it |
| 1544 | 1544 | if (chatPersona && user_avatar !== chatPersona) { |
| 1545 | 1545 | const willAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata['.persona']; |
| 1546 | 1546 | await setUserAvatar(chatPersona, { toastPersonaNameChange: false, navigateToCurrent: true }); |
| 1547 | 1547 | |
| 1548 | 1548 | if (power_user.persona_show_notifications) { |
| @@ -1554,7 +1554,7 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | ||
| 1554 | 1554 | } |
| 1555 | 1555 | } |
| 1556 | 1556 | // Even if it's the same persona, we still might need to auto-lock to chat if that's enabled |
| 1557 | 1557 | else if (chatPersona && power_user.persona_auto_lock && !chat_metadata['.persona']) { |
| 1558 | 1558 | await lockPersona('chat'); |
| 1559 | 1559 | } |
| 1560 | 1560 | |
| @@ -163,7 +163,7 @@ class PresetManager { | ||
| 163 | 163 | const manager = getPresetManager('textgenerationwebui'); |
| 164 | 164 | const name = manager.getSelectedPresetName(); |
| 165 | 165 | const data = manager.getPresetSettings(name); |
| 166 | 166 | data['.name'] = name; |
| 167 | 167 | return data; |
| 168 | 168 | }, |
| 169 | 169 | setData: (data) => { |
| @@ -652,22 +652,22 @@ class PresetManager { | ||
| 652 | 652 | return textgen_settings; |
| 653 | 653 | case 'context': { |
| 654 | 654 | const context_preset = getContextSettings(); |
| 655 | 655 | context_preset['.name'] = name || power_user.context.preset; |
| 656 | 656 | return context_preset; |
| 657 | 657 | } |
| 658 | 658 | case 'instruct': { |
| 659 | 659 | const instruct_preset = structuredClone(power_user.instruct); |
| 660 | 660 | instruct_preset['.name'] = name || power_user.instruct.preset; |
| 661 | 661 | return instruct_preset; |
| 662 | 662 | } |
| 663 | 663 | case 'sysprompt': { |
| 664 | 664 | const sysprompt_preset = structuredClone(power_user.sysprompt); |
| 665 | 665 | sysprompt_preset['.name'] = name || power_user.sysprompt.preset; |
| 666 | 666 | return sysprompt_preset; |
| 667 | 667 | } |
| 668 | 668 | case 'reasoning': { |
| 669 | 669 | const reasoning_preset = structuredClone(power_user.reasoning); |
| 670 | 670 | reasoning_preset['.name'] = name || power_user.reasoning.preset; |
| 671 | 671 | return reasoning_preset; |
| 672 | 672 | } |
| 673 | 673 | default: |
| @@ -1115,7 +1115,7 @@ export async function initPresetManager() { | ||
| 1115 | 1115 | const fileName = file.name.replace('.json', '').replace('.settings', ''); |
| 1116 | 1116 | const data = await parseJsonFile(file); |
| 1117 | 1117 | const name = data?.name ?? fileName; |
| 1118 | 1118 | data['.name'] = name; |
| 1119 | 1119 | |
| 1120 | 1120 | await presetManager.savePreset(name, data); |
| 1121 | 1121 | const successToast = !presetManager.isAdvancedFormatting() ? t`Preset imported` : t`Template imported`; |
| @@ -2120,7 +2120,7 @@ export function initDefaultSlashCommands() { | ||
| 2120 | 2120 | isRequired: true, |
| 2121 | 2121 | enumProvider: (executor, scope) => [ |
| 2122 | 2122 | ...commonEnumProviders.variables('scope')(executor, scope), |
| 2123 | 2123 | ...(typeof window['globalThis.qrEnumProviderExecutables'] === 'function') ? window['globalThis.qrEnumProviderExecutables']() : [], |
| 2124 | 2124 | ], |
| 2125 | 2125 | }), |
| 2126 | 2126 | ], |
| @@ -3572,7 +3572,7 @@ async function runCallback(args, name) { | ||
| 3572 | 3572 | return result.pipe; |
| 3573 | 3573 | } |
| 3574 | 3574 | |
| 3575 | 3575 | if (typeof window['globalThis.executeQuickReplyByName'] !== 'function') { |
| 3576 | 3576 | throw new Error(t`Quick Reply extension is not loaded`); |
| 3577 | 3577 | } |
| 3578 | 3578 | |
| @@ -3583,7 +3583,7 @@ async function runCallback(args, name) { | ||
| 3583 | 3583 | abortController: args._abortController, |
| 3584 | 3584 | debugController: args._debugController, |
| 3585 | 3585 | }; |
| 3586 | 3586 | return await window['globalThis.executeQuickReplyByName'](name, args, options); |
| 3587 | 3587 | } catch (error) { |
| 3588 | 3588 | throw new Error(t`Error running Quick Reply "${name}": ${error.message}`); |
| 3589 | 3589 | } |
| @@ -4788,7 +4788,7 @@ export async function sendMessageAs(args, text) { | ||
| 4788 | 4788 | insertAt = chat.length + insertAt; |
| 4789 | 4789 | } |
| 4790 | 4790 | |
| 4791 | 4791 | chat_metadata['.tainted'] = true; |
| 4792 | 4792 | |
| 4793 | 4793 | if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { |
| 4794 | 4794 | chat.splice(insertAt, 0, message); |
| @@ -4840,7 +4840,7 @@ export async function sendNarratorMessage(args, text) { | ||
| 4840 | 4840 | insertAt = chat.length + insertAt; |
| 4841 | 4841 | } |
| 4842 | 4842 | |
| 4843 | 4843 | chat_metadata['.tainted'] = true; |
| 4844 | 4844 | |
| 4845 | 4845 | if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { |
| 4846 | 4846 | chat.splice(insertAt, 0, message); |
| @@ -4892,7 +4892,7 @@ export async function promptQuietForLoudResponse(who, text) { | ||
| 4892 | 4892 | }, |
| 4893 | 4893 | }; |
| 4894 | 4894 | |
| 4895 | 4895 | chat_metadata['.tainted'] = true; |
| 4896 | 4896 | |
| 4897 | 4897 | chat.push(message); |
| 4898 | 4898 | await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1)); |
| @@ -4928,7 +4928,7 @@ async function sendCommentMessage(args, text) { | ||
| 4928 | 4928 | insertAt = chat.length + insertAt; |
| 4929 | 4929 | } |
| 4930 | 4930 | |
| 4931 | 4931 | chat_metadata['.tainted'] = true; |
| 4932 | 4932 | |
| 4933 | 4933 | if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) { |
| 4934 | 4934 | chat.splice(insertAt, 0, message); |
| @@ -880,8 +880,8 @@ async function downloadTabbyModel() { | ||
| 880 | 880 | } |
| 881 | 881 | |
| 882 | 882 | // Params for the server side of ST |
| 883 | 883 | params['.api_server'] = serverUrl; |
| 884 | 884 | params['.api_type'] = textgen_settings.type; |
| 885 | 885 | |
| 886 | 886 | toastr.info('Downloading. Check the Tabby console for progress reports.'); |
| 887 | 887 | |
| @@ -755,7 +755,7 @@ async function getStatusTextgen() { | ||
| 755 | 755 | power_user.chat_template_hash = chat_template_hash; |
| 756 | 756 | |
| 757 | 757 | if (wantsContextSize && 'default_generation_settings' in data) { |
| 758 | 758 | const backend_max_context = data['.default_generation_settings']['.n_ctx']; |
| 759 | 759 | if (backend_max_context && typeof backend_max_context === 'number') { |
| 760 | 760 | const old_value = max_context; |
| 761 | 761 | if (max_context !== backend_max_context) { |
| @@ -913,16 +913,16 @@ export function initTextGenSettings() { | ||
| 913 | 913 | $('#ban_eos_token_textgenerationwebui').prop('checked', false); //Aphro should not ban EOS, just ignore it; 'add token '2' to ban list do to this' |
| 914 | 914 | //special handling for vLLM/Aphrodite topK -1 disable state |
| 915 | 915 | $('#top_k_textgenerationwebui').attr('min', -1); |
| 916 | 916 | if ($('#top_k_textgenerationwebui').val() === '0' || textgenerationwebui_settings['.top_k'] === 0) { |
| 917 | 917 | textgenerationwebui_settings['.top_k'] = -1; |
| 918 | 918 | $('#top_k_textgenerationwebui').val('-1').trigger('input'); |
| 919 | 919 | } |
| 920 | 920 | } else { |
| 921 | 921 | $('#mirostat_mode_textgenerationwebui').attr('step', 1); |
| 922 | 922 | //undo special vLLM/Aphrodite setup for topK |
| 923 | 923 | $('#top_k_textgenerationwebui').attr('min', 0); |
| 924 | 924 | if ($('#top_k_textgenerationwebui').val() === '-1' || textgenerationwebui_settings['.top_k'] === -1) { |
| 925 | 925 | textgenerationwebui_settings['.top_k'] = 0; |
| 926 | 926 | $('#top_k_textgenerationwebui').val('0').trigger('input'); |
| 927 | 927 | } |
| 928 | 928 | } |
| @@ -410,8 +410,8 @@ export class ToolManager { | ||
| 410 | 410 | if (tools.length) { |
| 411 | 411 | console.log('[ToolManager] Registered function tools:', tools); |
| 412 | 412 | |
| 413 | 413 | data['.tools'] = tools; |
| 414 | 414 | data['.tool_choice'] = 'auto'; |
| 415 | 415 | } |
| 416 | 416 | } |
| 417 | 417 | |
| @@ -36,10 +36,16 @@ export const localizePagination = function (container) { | ||
| 36 | 36 | |
| 37 | 37 | /** |
| 38 | 38 | * Checks if the current environment supports negative lookbehind in regular expressions. |
| 39 | + * @type {{ (): boolean; result?: boolean }} Defines the function as a memoized object with a cached result. | |
| 39 | 40 | * @returns {boolean} True if negative lookbehind is supported, false otherwise. |
| 40 | 41 | */ |
| 41 | 42 | export function canUseNegativeLookbehind() { |
| 42 | - let result = canUseNegativeLookbehind['result']; | |
| 43 | + /** | |
| 44 | + * A reference to the function itself, typed as a callable object with a cache property. | |
| 45 | + * @type {{ (): boolean; result?: boolean }} | |
| 46 | + */ | |
| 47 | + const fn = canUseNegativeLookbehind; | |
| 48 | + let result = fn.result; | |
| 43 | 49 | if (typeof result !== 'boolean') { |
| 44 | 50 | try { |
| 45 | 51 | new RegExp('(?<!_)'); |
| @@ -47,7 +53,7 @@ export function canUseNegativeLookbehind() { | ||
| 47 | 53 | } catch (e) { |
| 48 | 54 | result = false; |
| 49 | 55 | } |
| 50 | 56 | canUseNegativeLookbehind['fn.result'] = result; |
| 51 | 57 | } |
| 52 | 58 | return result; |
| 53 | 59 | } |
| @@ -2440,8 +2446,8 @@ export async function fetchFaFile(name) { | ||
| 2440 | 2446 | const sheet = style.sheet; |
| 2441 | 2447 | style.remove(); |
| 2442 | 2448 | return [...sheet.cssRules] |
| 2443 | 2449 | .filter(rule => rule.['style']?.content) |
| 2444 | 2450 | .map(rule => rule.['selectorText'].split(/,\s*/).map(selector => selector.split('::').shift().slice(1))) |
| 2445 | 2451 | ; |
| 2446 | 2452 | } |
| 2447 | 2453 | |
| @@ -3271,7 +3271,7 @@ export async function getWorldEntry(name, data, entry) { | ||
| 3271 | 3271 | const commentInput = headerTemplate.find('textarea[name="comment"]'); |
| 3272 | 3272 | |
| 3273 | 3273 | //Update the commentInput's placeholder. |
| 3274 | 3274 | const keys = entry['.key'].join(', '); |
| 3275 | 3275 | setCommentPlaceholder(keys, commentInput); |
| 3276 | 3276 | |
| 3277 | 3277 | commentInput.data('uid', entry.uid); |
| @@ -145,7 +145,7 @@ router.post('/get', async (request, response) => { | ||
| 145 | 145 | for (let file of files) { |
| 146 | 146 | if (!file.endsWith('.placeholder')) { |
| 147 | 147 | //console.debug("Asset VRM model found:",file) |
| 148 | 148 | output['.vrm']['.model'].push(clientRelativePath(request.user.directories.root, file)); |
| 149 | 149 | } |
| 150 | 150 | } |
| 151 | 151 | |
| @@ -156,7 +156,7 @@ router.post('/get', async (request, response) => { | ||
| 156 | 156 | for (let file of files) { |
| 157 | 157 | if (!file.endsWith('.placeholder')) { |
| 158 | 158 | //console.debug("Asset VRM animation found:",file) |
| 159 | 159 | output['.vrm']['.animation'].push(clientRelativePath(request.user.directories.root, file)); |
| 160 | 160 | } |
| 161 | 161 | } |
| 162 | 162 | continue; |
| @@ -245,7 +245,7 @@ async function sendClaudeRequest(request, response) { | ||
| 245 | 245 | }; |
| 246 | 246 | if (useSystemPrompt) { |
| 247 | 247 | if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) { |
| 248 | 248 | convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1]['.cache_control'] = { type: 'ephemeral', ttl: cacheTTL }; |
| 249 | 249 | } |
| 250 | 250 | |
| 251 | 251 | requestBody.system = convertedPrompt.systemPrompt; |
| @@ -261,7 +261,7 @@ async function sendClaudeRequest(request, response) { | ||
| 261 | 261 | .map(fn => ({ name: fn.name, description: fn.description, input_schema: flattenSchema(fn.parameters, request.body.chat_completion_source) })); |
| 262 | 262 | |
| 263 | 263 | if (enableSystemPromptCache && requestBody.tools.length) { |
| 264 | 264 | requestBody.tools[requestBody.tools.length - 1]['.cache_control'] = { type: 'ephemeral', ttl: cacheTTL }; |
| 265 | 265 | } |
| 266 | 266 | } |
| 267 | 267 | |
| @@ -76,7 +76,7 @@ router.post('/generate', async function (request, response_generate) { | ||
| 76 | 76 | sampler_seed: request.body.sampler_seed, |
| 77 | 77 | }; |
| 78 | 78 | if (request.body.stop_sequence) { |
| 79 | 79 | this_settings['.stop_sequence'] = request.body.stop_sequence; |
| 80 | 80 | } |
| 81 | 81 | } |
| 82 | 82 | |
| @@ -257,10 +257,10 @@ router.post('/props', async function (request, response) { | ||
| 257 | 257 | /** @type {any} */ |
| 258 | 258 | const props = await propsReply.json(); |
| 259 | 259 | // TEMPORARY: llama.cpp's /props endpoint has a bug which replaces the last newline with a \0 |
| 260 | 260 | if (apiType === TEXTGEN_TYPES.LLAMACPP && props['.chat_template'] && props['.chat_template'].endsWith('\u0000')) { |
| 261 | 261 | props['.chat_template'] = props['.chat_template'].slice(0, -1) + '\n'; |
| 262 | 262 | } |
| 263 | 263 | props['.chat_template_hash'] = createHash('sha256').update(props['.chat_template']).digest('hex'); |
| 264 | 264 | console.debug(`Model properties: ${JSON.stringify(props)}`); |
| 265 | 265 | return response.send(props); |
| 266 | 266 | } catch (error) { |
| @@ -380,7 +380,7 @@ router.post('/generate', async function (request, response) { | ||
| 380 | 380 | const keepAlive = Number(getConfigValue('ollama.keepAlive', -1, 'number')); |
| 381 | 381 | const numBatch = Number(getConfigValue('ollama.batchSize', -1, 'number')); |
| 382 | 382 | if (numBatch > 0) { |
| 383 | 383 | request.body['.num_batch'] = numBatch; |
| 384 | 384 | } |
| 385 | 385 | args.body = JSON.stringify({ |
| 386 | 386 | model: request.body.model, |
| @@ -410,7 +410,7 @@ router.post('/generate', async function (request, response) { | ||
| 410 | 410 | |
| 411 | 411 | // Map InfermaticAI response to OAI completions format |
| 412 | 412 | if (apiType === TEXTGEN_TYPES.INFERMATICAI) { |
| 413 | 413 | data['.choices'] = (data?.choices || []).map(choice => ({ text: choice?.message?.content || choice.text, logprobs: choice?.logprobs, index: choice?.index })); |
| 414 | 414 | } |
| 415 | 415 | |
| 416 | 416 | return response.send(data); |
| @@ -616,7 +616,7 @@ tabby.post('/download', async function (request, response) { | ||
| 616 | 616 | /** @type {any} */ |
| 617 | 617 | const permissionJson = await permissionResponse.json(); |
| 618 | 618 | |
| 619 | 619 | if (permissionJson['.permission'] !== 'admin') { |
| 620 | 620 | return response.status(403).send({ error: true }); |
| 621 | 621 | } |
| 622 | 622 | } else { |
| @@ -412,16 +412,16 @@ const processCharacter = async (item, directories, { shallow }) => { | ||
| 412 | 412 | let jsonObject = getCharaCardV2(JSON.parse(imgData), directories, false); |
| 413 | 413 | jsonObject.avatar = item; |
| 414 | 414 | const character = jsonObject; |
| 415 | 415 | character['.json_data'] = imgData; |
| 416 | 416 | const charStat = fs.statSync(path.join(directories.characters, item)); |
| 417 | 417 | character['.date_added'] = charStat.ctimeMs; |
| 418 | 418 | character['.create_date'] = jsonObject['.create_date'] || new Date(Math.round(charStat.ctimeMs)).toISOString(); |
| 419 | 419 | const chatsDirectory = path.join(directories.chats, item.replace('.png', '')); |
| 420 | 420 | |
| 421 | 421 | const { chatSize, dateLastChat } = calculateChatSize(chatsDirectory); |
| 422 | 422 | character['.chat_size'] = chatSize; |
| 423 | 423 | character['.date_last_chat'] = dateLastChat; |
| 424 | 424 | character['.data_size'] = calculateDataSize(jsonObject?.data); |
| 425 | 425 | return shallow ? toShallow(character) : character; |
| 426 | 426 | } |
| 427 | 427 | catch (err) { |
| @@ -504,7 +504,7 @@ function unsetPrivateFields(char) { | ||
| 504 | 504 | |
| 505 | 505 | function readFromV2(char) { |
| 506 | 506 | if (_.isUndefined(char.data)) { |
| 507 | 507 | console.warn(`Char ${char['.name']} has Spec v2 data missing`); |
| 508 | 508 | return char; |
| 509 | 509 | } |
| 510 | 510 | |
| @@ -542,17 +542,17 @@ function readFromV2(char) { | ||
| 542 | 542 | //console.warn(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`); |
| 543 | 543 | char[charField] = defaultValue; |
| 544 | 544 | } else { |
| 545 | 545 | console.warn(`Char ${char['.name']} has Spec v2 data missing for unknown field: ${charField}`); |
| 546 | 546 | return; |
| 547 | 547 | } |
| 548 | 548 | } |
| 549 | 549 | if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) { |
| 550 | 550 | console.warn(`Char ${char['.name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value); |
| 551 | 551 | } |
| 552 | 552 | char[charField] = v2Value; |
| 553 | 553 | }); |
| 554 | 554 | |
| 555 | 555 | char['.chat'] = char['.chat'] ?? `${char.name} - ${humanizedDateTime()}`; |
| 556 | 556 | |
| 557 | 557 | return char; |
| 558 | 558 | } |
| @@ -779,7 +779,7 @@ async function importFromCharX(uploadPath, { request }, preservedFileName) { | ||
| 779 | 779 | // Apply standard character transformations |
| 780 | 780 | let processedCard = readFromV2(card); |
| 781 | 781 | unsetPrivateFields(processedCard); |
| 782 | 782 | processedCard['.create_date'] = new Date().toISOString(); |
| 783 | 783 | processedCard.name = sanitize(processedCard.name); |
| 784 | 784 | |
| 785 | 785 | const fileName = preservedFileName || getPngName(processedCard.name, request.user.directories); |
| @@ -893,7 +893,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) { | ||
| 893 | 893 | importRisuSprites(request.user.directories, jsonData); |
| 894 | 894 | unsetPrivateFields(jsonData); |
| 895 | 895 | jsonData = readFromV2(jsonData); |
| 896 | 896 | jsonData['.create_date'] = new Date().toISOString(); |
| 897 | 897 | const pngName = preservedFileName || getPngName(jsonData.data?.name || jsonData.name, request.user.directories); |
| 898 | 898 | const char = JSON.stringify(jsonData); |
| 899 | 899 | const result = await writeCharacterData(DEFAULT_AVATAR_PATH, char, pngName, request); |
| @@ -976,7 +976,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) { | ||
| 976 | 976 | importRisuSprites(request.user.directories, jsonData); |
| 977 | 977 | unsetPrivateFields(jsonData); |
| 978 | 978 | jsonData = readFromV2(jsonData); |
| 979 | 979 | jsonData['.create_date'] = new Date().toISOString(); |
| 980 | 980 | const char = JSON.stringify(jsonData); |
| 981 | 981 | const result = await writeCharacterData(uploadPath, char, pngName, request); |
| 982 | 982 | fs.unlinkSync(uploadPath); |
| @@ -397,8 +397,8 @@ export async function getChatInfo(pathToFile, additionalData = {}, withMetadata | ||
| 397 | 397 | const jsonData = tryParse(lastLine); |
| 398 | 398 | if (jsonData && (jsonData.name || jsonData.character_name || jsonData.chat_metadata)) { |
| 399 | 399 | chatData.chat_items = (itemCounter - 1); |
| 400 | 400 | chatData.mes = jsonData['.mes'] || '[The message is empty]'; |
| 401 | 401 | chatData.last_mes = jsonData['.send_date'] || new Date(Math.round(stats.mtimeMs)).toISOString(); |
| 402 | 402 | |
| 403 | 403 | res(chatData); |
| 404 | 404 | } else { |
| @@ -126,8 +126,8 @@ router.post('/all', (request, response) => { | ||
| 126 | 126 | const fileContents = fs.readFileSync(filePath, 'utf8'); |
| 127 | 127 | const group = JSON.parse(fileContents); |
| 128 | 128 | const groupStat = fs.statSync(filePath); |
| 129 | 129 | group['.date_added'] = groupStat.birthtimeMs; |
| 130 | 130 | group['.create_date'] = new Date(groupStat.birthtimeMs).toISOString(); |
| 131 | 131 | |
| 132 | 132 | let chat_size = 0; |
| 133 | 133 | let date_last_chat = 0; |
| @@ -142,8 +142,8 @@ router.post('/all', (request, response) => { | ||
| 142 | 142 | } |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | 145 | group['.date_last_chat'] = date_last_chat; |
| 146 | 146 | group['.chat_size'] = chat_size; |
| 147 | 147 | groups.push(group); |
| 148 | 148 | } |
| 149 | 149 | catch (error) { |
| @@ -228,7 +228,7 @@ router.post('/get-model', async (request, response) => { | ||
| 228 | 228 | }); |
| 229 | 229 | /** @type {any} */ |
| 230 | 230 | const data = await result.json(); |
| 231 | 231 | return response.send(data['.sd_model_checkpoint']); |
| 232 | 232 | } catch (error) { |
| 233 | 233 | console.error(error); |
| 234 | 234 | return response.sendStatus(500); |
| @@ -277,8 +277,8 @@ router.post('/set-model', async (request, response) => { | ||
| 277 | 277 | /** @type {any} */ |
| 278 | 278 | const progressState = await getProgress(); |
| 279 | 279 | |
| 280 | 280 | const progress = progressState['.progress']; |
| 281 | 281 | const jobCount = progressState['.state']['.job_count']; |
| 282 | 282 | if (progress === 0.0 && jobCount === 0) { |
| 283 | 283 | break; |
| 284 | 284 | } |
| @@ -834,7 +834,7 @@ drawthings.post('/get-model', async (request, response) => { | ||
| 834 | 834 | /** @type {any} */ |
| 835 | 835 | const data = await result.json(); |
| 836 | 836 | |
| 837 | 837 | return response.send(data['.model']); |
| 838 | 838 | } catch (error) { |
| 839 | 839 | console.error(error); |
| 840 | 840 | return response.sendStatus(500); |
| @@ -853,7 +853,7 @@ drawthings.post('/get-upscaler', async (request, response) => { | ||
| 853 | 853 | /** @type {any} */ |
| 854 | 854 | const data = await result.json(); |
| 855 | 855 | |
| 856 | 856 | return response.send(data['.upscaler']); |
| 857 | 857 | } catch (error) { |
| 858 | 858 | console.error(error); |
| 859 | 859 | return response.sendStatus(500); |
| @@ -1052,8 +1052,8 @@ router.post('/remote/kobold/count', async function (request, response) { | ||
| 1052 | 1052 | |
| 1053 | 1053 | /** @type {any} */ |
| 1054 | 1054 | const data = await result.json(); |
| 1055 | 1055 | const count = data['.value']; |
| 1056 | 1056 | const ids = data['.ids'] ?? []; |
| 1057 | 1057 | return response.send({ count, ids }); |
| 1058 | 1058 | } catch (error) { |
| 1059 | 1059 | console.error(error); |
| @@ -639,8 +639,8 @@ test.describe('MacroEngine', () => { | ||
| 639 | 639 | await page.evaluate(async ([originalHash]) => { |
| 640 | 640 | /** @type {import('../../public/script.js')} */ |
| 641 | 641 | const { chat_metadata } = await import('./script.js'); |
| 642 | 642 | originalHash = chat_metadata['.chat_id_hash']; |
| 643 | 643 | chat_metadata['.chat_id_hash'] = 123456; |
| 644 | 644 | }, [originalHash]); |
| 645 | 645 | |
| 646 | 646 | const input = 'Choices: {{pick::red::green::blue}}, {{pick::red::green::blue}}.'; |
| @@ -668,7 +668,7 @@ test.describe('MacroEngine', () => { | ||
| 668 | 668 | await page.evaluate(async ([originalHash]) => { |
| 669 | 669 | /** @type {import('../../public/script.js')} */ |
| 670 | 670 | const { chat_metadata } = await import('./script.js'); |
| 671 | 671 | chat_metadata['.chat_id_hash'] = originalHash; |
| 672 | 672 | }, [originalHash]); |
| 673 | 673 | }); |
| 674 | 674 | }); |