| import { DiffMatchPatch, DOMPurify, localforage } from '../lib.js'; |
| import { chat, event_types, eventSource, getCurrentChatId, reloadCurrentChat } from '../script.js'; |
| import { t } from './i18n.js'; |
| import { oai_settings } from './openai.js'; |
| import { Popup, POPUP_TYPE } from './popup.js'; |
| import { power_user, registerDebugFunction } from './power-user.js'; |
| import { isMobile } from './RossAscends-mods.js'; |
| import { renderTemplateAsync } from './templates.js'; |
| import { getFriendlyTokenizerName, getTokenCountAsync } from './tokenizers.js'; |
| import { copyText } from './utils.js'; |
|
|
| let PromptArrayItemForRawPromptDisplay; |
| let priorPromptArrayItemForRawPromptDisplay; |
|
|
| const promptStorage = localforage.createInstance({ name: 'SillyTavern_Prompts' }); |
| export let itemizedPrompts = []; |
|
|
| |
| |
| |
| |
| export async function loadItemizedPrompts(chatId) { |
| try { |
| if (!chatId) { |
| itemizedPrompts = []; |
| return; |
| } |
|
|
| itemizedPrompts = await promptStorage.getItem(chatId); |
|
|
| if (!itemizedPrompts) { |
| itemizedPrompts = []; |
| } |
| } catch { |
| console.log('Error loading itemized prompts for chat', chatId); |
| itemizedPrompts = []; |
| } |
| } |
|
|
| |
| |
| |
| |
| export async function saveItemizedPrompts(chatId) { |
| try { |
| if (!chatId) { |
| return; |
| } |
|
|
| await promptStorage.setItem(chatId, itemizedPrompts); |
| } catch { |
| console.log('Error saving itemized prompts for chat', chatId); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export async function replaceItemizedPromptText(mesId, promptText) { |
| if (!Array.isArray(itemizedPrompts)) { |
| itemizedPrompts = []; |
| } |
|
|
| const itemizedPrompt = itemizedPrompts.find(x => x.mesId === mesId); |
|
|
| if (!itemizedPrompt) { |
| return; |
| } |
|
|
| itemizedPrompt.rawPrompt = promptText; |
| } |
|
|
| |
| |
| |
| |
| export async function deleteItemizedPrompts(chatId) { |
| try { |
| if (!chatId) { |
| return; |
| } |
|
|
| await promptStorage.removeItem(chatId); |
| } catch { |
| console.log('Error deleting itemized prompts for chat', chatId); |
| } |
| } |
|
|
| |
| |
| |
| export async function clearItemizedPrompts() { |
| try { |
| await promptStorage.clear(); |
| itemizedPrompts = []; |
| } catch { |
| console.log('Error clearing itemized prompts'); |
| } |
| } |
|
|
| export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMesId) { |
| const params = { |
| charDescriptionTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].charDescription), |
| charPersonalityTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].charPersonality), |
| scenarioTextTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].scenarioText), |
| userPersonaStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].userPersona), |
| worldInfoStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].worldInfoString), |
| allAnchorsTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].allAnchors), |
| summarizeStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].summarizeString), |
| authorsNoteStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].authorsNoteString), |
| smartContextStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].smartContextString), |
| beforeScenarioAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].beforeScenarioAnchor), |
| afterScenarioAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].afterScenarioAnchor), |
| zeroDepthAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].zeroDepthAnchor), |
| thisPrompt_padding: itemizedPrompts[thisPromptSet].padding, |
| this_main_api: itemizedPrompts[thisPromptSet].main_api, |
| chatInjects: await getTokenCountAsync(itemizedPrompts[thisPromptSet].chatInjects), |
| chatVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].chatVectorsString), |
| dataBankVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].dataBankVectorsString), |
| modelUsed: chat[incomingMesId]?.extra?.model, |
| apiUsed: chat[incomingMesId]?.extra?.api, |
| presetName: itemizedPrompts[thisPromptSet].presetName || t`(Unknown)`, |
| messagesCount: String(itemizedPrompts[thisPromptSet].messagesCount ?? ''), |
| examplesCount: String(itemizedPrompts[thisPromptSet].examplesCount ?? ''), |
| }; |
|
|
| const getFriendlyName = (value) => $(`#rm_api_block select option[value="${value}"]`).first().text() || value; |
|
|
| if (params.apiUsed) { |
| params.apiUsed = getFriendlyName(params.apiUsed); |
| } |
|
|
| if (params.this_main_api) { |
| params.mainApiFriendlyName = getFriendlyName(params.this_main_api); |
| } |
|
|
| if (params.chatInjects) { |
| params.ActualChatHistoryTokens = params.ActualChatHistoryTokens - params.chatInjects; |
| } |
|
|
| if (params.this_main_api == 'openai') { |
| |
| |
|
|
| |
| params.oaiMainTokens = itemizedPrompts[thisPromptSet].oaiMainTokens; |
| params.oaiStartTokens = itemizedPrompts[thisPromptSet].oaiStartTokens; |
| params.ActualChatHistoryTokens = itemizedPrompts[thisPromptSet].oaiConversationTokens; |
| params.examplesStringTokens = itemizedPrompts[thisPromptSet].oaiExamplesTokens; |
| params.oaiPromptTokens = itemizedPrompts[thisPromptSet].oaiPromptTokens - (params.afterScenarioAnchorTokens + params.beforeScenarioAnchorTokens) + params.examplesStringTokens; |
| params.oaiBiasTokens = itemizedPrompts[thisPromptSet].oaiBiasTokens; |
| params.oaiJailbreakTokens = itemizedPrompts[thisPromptSet].oaiJailbreakTokens; |
| params.oaiNudgeTokens = itemizedPrompts[thisPromptSet].oaiNudgeTokens; |
| params.oaiImpersonateTokens = itemizedPrompts[thisPromptSet].oaiImpersonateTokens; |
| params.oaiNsfwTokens = itemizedPrompts[thisPromptSet].oaiNsfwTokens; |
| params.finalPromptTokens = |
| params.oaiStartTokens + |
| params.oaiPromptTokens + |
| params.oaiMainTokens + |
| params.oaiNsfwTokens + |
| params.oaiBiasTokens + |
| params.oaiImpersonateTokens + |
| params.oaiJailbreakTokens + |
| params.oaiNudgeTokens + |
| params.ActualChatHistoryTokens + |
| |
| |
| |
| params.worldInfoStringTokens + |
| params.beforeScenarioAnchorTokens + |
| params.afterScenarioAnchorTokens; |
| |
| params.thisPrompt_max_context = (oai_settings.openai_max_context - oai_settings.openai_max_tokens); |
|
|
| |
| params.oaiStartTokensPercentage = ((params.oaiStartTokens / (params.finalPromptTokens)) * 100).toFixed(2); |
| params.storyStringTokensPercentage = (((params.afterScenarioAnchorTokens + params.beforeScenarioAnchorTokens + params.oaiPromptTokens) / (params.finalPromptTokens)) * 100).toFixed(2); |
| params.ActualChatHistoryTokensPercentage = ((params.ActualChatHistoryTokens / (params.finalPromptTokens)) * 100).toFixed(2); |
| params.promptBiasTokensPercentage = ((params.oaiBiasTokens / (params.finalPromptTokens)) * 100).toFixed(2); |
| params.worldInfoStringTokensPercentage = ((params.worldInfoStringTokens / (params.finalPromptTokens)) * 100).toFixed(2); |
| params.allAnchorsTokensPercentage = ((params.allAnchorsTokens / (params.finalPromptTokens)) * 100).toFixed(2); |
| params.selectedTokenizer = getFriendlyTokenizerName(params.this_main_api).tokenizerName; |
| params.oaiSystemTokens = params.oaiImpersonateTokens + params.oaiJailbreakTokens + params.oaiNudgeTokens + params.oaiStartTokens + params.oaiNsfwTokens + params.oaiMainTokens; |
| params.oaiSystemTokensPercentage = ((params.oaiSystemTokens / (params.finalPromptTokens)) * 100).toFixed(2); |
| } else { |
| |
| |
| params.finalPromptTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].finalPrompt); |
| params.storyStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].storyString) - params.worldInfoStringTokens; |
| params.examplesStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].examplesString); |
| params.mesSendStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].mesSendString); |
| params.ActualChatHistoryTokens = params.mesSendStringTokens - (params.allAnchorsTokens - (params.beforeScenarioAnchorTokens + params.afterScenarioAnchorTokens)) + power_user.token_padding; |
| params.instructionTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].instruction); |
| params.promptBiasTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].promptBias); |
|
|
| params.totalTokensInPrompt = |
| params.storyStringTokens + |
| params.worldInfoStringTokens + |
| params.examplesStringTokens + |
| params.ActualChatHistoryTokens + |
| params.allAnchorsTokens + |
| |
| |
| params.promptBiasTokens; |
| |
| params.thisPrompt_max_context = itemizedPrompts[thisPromptSet].this_max_context; |
| params.thisPrompt_actual = params.thisPrompt_max_context - params.thisPrompt_padding; |
|
|
| |
| params.storyStringTokensPercentage = ((params.storyStringTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); |
| params.ActualChatHistoryTokensPercentage = ((params.ActualChatHistoryTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); |
| params.promptBiasTokensPercentage = ((params.promptBiasTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); |
| params.worldInfoStringTokensPercentage = ((params.worldInfoStringTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); |
| params.allAnchorsTokensPercentage = ((params.allAnchorsTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); |
| params.selectedTokenizer = itemizedPrompts[thisPromptSet]?.tokenizer || getFriendlyTokenizerName(params.this_main_api).tokenizerName; |
| } |
| return params; |
| } |
|
|
| export function findItemizedPromptSet(itemizedPrompts, incomingMesId) { |
| let thisPromptSet = undefined; |
|
|
| for (let i = 0; i < itemizedPrompts.length; i++) { |
| console.log(`looking for ${incomingMesId} vs ${itemizedPrompts[i].mesId}`); |
| if (itemizedPrompts[i].mesId === incomingMesId) { |
| console.log(`found matching mesID ${i}`); |
| thisPromptSet = i; |
| PromptArrayItemForRawPromptDisplay = i; |
| console.log(`wanting to raw display of ArrayItem: ${PromptArrayItemForRawPromptDisplay} which is mesID ${incomingMesId}`); |
| console.log(itemizedPrompts[thisPromptSet]); |
| break; |
| } else if (itemizedPrompts[i].rawPrompt) { |
| priorPromptArrayItemForRawPromptDisplay = i; |
| } |
| } |
| return thisPromptSet; |
| } |
|
|
| export async function promptItemize(itemizedPrompts, requestedMesId) { |
| console.log('PROMPT ITEMIZE ENTERED'); |
| var incomingMesId = Number(requestedMesId); |
| console.debug(`looking for MesId ${incomingMesId}`); |
| var thisPromptSet = findItemizedPromptSet(itemizedPrompts, incomingMesId); |
|
|
| if (thisPromptSet === undefined) { |
| console.log(`couldnt find the right mesId. looked for ${incomingMesId}`); |
| console.log(itemizedPrompts); |
| return null; |
| } |
|
|
| const params = await itemizedParams(itemizedPrompts, thisPromptSet, incomingMesId); |
| const flatten = (rawPrompt) => Array.isArray(rawPrompt) ? rawPrompt.map(x => x.content).join('\n') : rawPrompt; |
|
|
| const template = params.this_main_api == 'openai' |
| ? await renderTemplateAsync('itemizationChat', params) |
| : await renderTemplateAsync('itemizationText', params); |
|
|
| const popup = new Popup(template, POPUP_TYPE.TEXT); |
|
|
| |
| const diffPrevPrompt = popup.dlg.querySelector('#diffPrevPrompt'); |
| if (priorPromptArrayItemForRawPromptDisplay) { |
| diffPrevPrompt.style.display = ''; |
| diffPrevPrompt.addEventListener('click', function () { |
| const dmp = new DiffMatchPatch(); |
| const text1 = flatten(itemizedPrompts[priorPromptArrayItemForRawPromptDisplay].rawPrompt); |
| const text2 = flatten(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); |
|
|
| dmp.Diff_Timeout = 2.0; |
|
|
| const d = dmp.diff_main(text1, text2); |
| let ds = dmp.diff_prettyHtml(d); |
| |
| ds = ds.replaceAll('background:#e6ffe6;', 'background:#b9f3b9; color:black;'); |
| ds = ds.replaceAll('background:#ffe6e6;', 'background:#f5b4b4; color:black;'); |
| ds = ds.replaceAll('¶', ''); |
| const container = document.createElement('div'); |
| container.innerHTML = DOMPurify.sanitize(ds); |
| const rawPromptWrapper = document.getElementById('rawPromptWrapper'); |
| rawPromptWrapper.replaceChildren(container); |
| $('#rawPromptPopup').slideToggle(); |
| }); |
| } else { |
| diffPrevPrompt.style.display = 'none'; |
| } |
| popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('pointerup', async function () { |
| let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt; |
| let rawPromptValues = rawPrompt; |
|
|
| if (Array.isArray(rawPrompt)) { |
| rawPromptValues = rawPrompt.map(x => x.content).join('\n'); |
| } |
|
|
| await copyText(rawPromptValues); |
| toastr.info(t`Copied!`); |
| }); |
|
|
| popup.dlg.querySelector('#showRawPrompt').addEventListener('click', async function () { |
| |
| console.log(PromptArrayItemForRawPromptDisplay); |
| console.log(itemizedPrompts); |
| console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); |
|
|
| const rawPrompt = flatten(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); |
|
|
| |
| |
| if (isMobile()) { |
| const content = document.createElement('div'); |
| content.classList.add('tokenItemizingMaintext'); |
| content.innerText = rawPrompt; |
| const popup = new Popup(content, POPUP_TYPE.TEXT, null, { allowVerticalScrolling: true, leftAlign: true }); |
| await popup.show(); |
| return; |
| } |
|
|
| |
| const rawPromptWrapper = document.getElementById('rawPromptWrapper'); |
| rawPromptWrapper.innerText = rawPrompt; |
| $('#rawPromptPopup').slideToggle(); |
| }); |
|
|
| await popup.show(); |
| } |
|
|
| export function initItemizedPrompts() { |
| registerDebugFunction('clearPrompts', 'Delete itemized prompts', 'Deletes all itemized prompts from the local storage.', async () => { |
| await clearItemizedPrompts(); |
| toastr.info('Itemized prompts deleted.'); |
| if (getCurrentChatId()) { |
| await reloadCurrentChat(); |
| } |
| }); |
|
|
| $(document).on('pointerup', '.mes_prompt', async function () { |
| let mesIdForItemization = $(this).closest('.mes').attr('mesId'); |
| console.log(`looking for mesID: ${mesIdForItemization}`); |
| if (itemizedPrompts.length !== undefined && itemizedPrompts.length !== 0) { |
| await promptItemize(itemizedPrompts, mesIdForItemization); |
| } |
| }); |
|
|
| eventSource.on(event_types.CHAT_DELETED, async (name) => { |
| await deleteItemizedPrompts(name); |
| }); |
| eventSource.on(event_types.GROUP_CHAT_DELETED, async (name) => { |
| await deleteItemizedPrompts(name); |
| }); |
| } |
|
|