Move itemized prompts into separate module (#4229) * Move itemized prompts into separate module * Update public/scripts/itemized-prompts.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update public/scripts/itemized-prompts.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed| @@ -4,9 +4,7 @@ import { | |||
| 4 | Fuse, | 4 | Fuse, |
| 5 | DOMPurify, | 5 | DOMPurify, |
| 6 | hljs, | 6 | hljs, |
| 7 | localforage, | ||
| 8 | Handlebars, | 7 | Handlebars, |
| 9 | DiffMatchPatch, | ||
| 10 | SVGInject, | 8 | SVGInject, |
| 11 | Popper, | 9 | Popper, |
| 12 | initLibraryShims, | 10 | initLibraryShims, |
| @@ -297,6 +295,7 @@ import { extractReasoningFromData, initReasoning, parseReasoningInSwipes, Prompt | |||
| 297 | import { accountStorage } from './scripts/util/AccountStorage.js'; | 295 | import { accountStorage } from './scripts/util/AccountStorage.js'; |
| 298 | import { initWelcomeScreen, openPermanentAssistantChat, openPermanentAssistantCard, getPermanentAssistantAvatar } from './scripts/welcome-screen.js'; | 296 | import { initWelcomeScreen, openPermanentAssistantChat, openPermanentAssistantCard, getPermanentAssistantAvatar } from './scripts/welcome-screen.js'; |
| 299 | import { initDataMaid } from './scripts/data-maid.js'; | 297 | import { initDataMaid } from './scripts/data-maid.js'; |
| 298 | import { clearItemizedPrompts, deleteItemizedPrompts, findItemizedPromptSet, initItemizedPrompts, itemizedParams, itemizedPrompts, loadItemizedPrompts, promptItemize, replaceItemizedPromptText, saveItemizedPrompts } from './scripts/itemized-prompts.js'; | ||
| 300 | 299 | ||
| 301 | // API OBJECT FOR EXTERNAL WIRING | 300 | // API OBJECT FOR EXTERNAL WIRING |
| 302 | globalThis.SillyTavern = { | 301 | globalThis.SillyTavern = { |
| @@ -304,7 +303,9 @@ globalThis.SillyTavern = { | |||
| 304 | getContext, | 303 | getContext, |
| 305 | }; | 304 | }; |
| 306 | 305 | ||
| 307 | //exporting functions and vars for mods | 306 | /** |
| 307 | * Exports that were once part of this module. For backwards compatibility. | ||
| 308 | */ | ||
| 308 | export { | 309 | export { |
| 309 | user_avatar, | 310 | user_avatar, |
| 310 | setUserAvatar, | 311 | setUserAvatar, |
| @@ -314,6 +315,15 @@ export { | |||
| 314 | isOdd, | 315 | isOdd, |
| 315 | countOccurrences, | 316 | countOccurrences, |
| 316 | renderTemplate, | 317 | renderTemplate, |
| 318 | promptItemize, | ||
| 319 | itemizedPrompts, | ||
| 320 | saveItemizedPrompts, | ||
| 321 | loadItemizedPrompts, | ||
| 322 | itemizedParams, | ||
| 323 | clearItemizedPrompts, | ||
| 324 | replaceItemizedPromptText, | ||
| 325 | deleteItemizedPrompts, | ||
| 326 | findItemizedPromptSet, | ||
| 317 | }; | 327 | }; |
| 318 | 328 | ||
| 319 | /** | 329 | /** |
| @@ -577,9 +587,6 @@ export let mesForShowdownParse; //intended to be used as a context to compare sh | |||
| 577 | export let converter; | 587 | export let converter; |
| 578 | 588 | ||
| 579 | // array for prompt token calculations | 589 | // array for prompt token calculations |
| 580 | console.debug('initializing Prompt Itemization Array on Startup'); | ||
| 581 | const promptStorage = localforage.createInstance({ name: 'SillyTavern_Prompts' }); | ||
| 582 | export let itemizedPrompts = []; | ||
| 583 | 590 | ||
| 584 | export const systemUserName = 'SillyTavern System'; | 591 | export const systemUserName = 'SillyTavern System'; |
| 585 | export const neutralCharacterName = 'Assistant'; | 592 | export const neutralCharacterName = 'Assistant'; |
| @@ -928,8 +935,6 @@ var kobold_horde_model = ''; | |||
| 928 | 935 | ||
| 929 | export let token; | 936 | export let token; |
| 930 | 937 | ||
| 931 | var PromptArrayItemForRawPromptDisplay; | ||
| 932 | var priorPromptArrayItemForRawPromptDisplay; | ||
| 933 | 938 | ||
| 934 | /** The tag of the active character. (NOT the id) */ | 939 | /** The tag of the active character. (NOT the id) */ |
| 935 | export let active_character = ''; | 940 | export let active_character = ''; |
| @@ -1038,6 +1043,7 @@ async function firstLoadInit() { | |||
| 1038 | await initScrapers(); | 1043 | await initScrapers(); |
| 1039 | initCustomSelectedSamplers(); | 1044 | initCustomSelectedSamplers(); |
| 1040 | initDataMaid(); | 1045 | initDataMaid(); |
| 1046 | initItemizedPrompts(); | ||
| 1041 | addDebugFunctions(); | 1047 | addDebugFunctions(); |
| 1042 | doDailyExtensionUpdatesCheck(); | 1048 | doDailyExtensionUpdatesCheck(); |
| 1043 | await hideLoader(); | 1049 | await hideLoader(); |
| @@ -1102,92 +1108,6 @@ export function setActiveGroup(entityOrKey) { | |||
| 1102 | if (active_group) active_character = null; | 1108 | if (active_group) active_character = null; |
| 1103 | } | 1109 | } |
| 1104 | 1110 | ||
| 1105 | /** | ||
| 1106 | * Gets the itemized prompts for a chat. | ||
| 1107 | * @param {string} chatId Chat ID to load | ||
| 1108 | */ | ||
| 1109 | export async function loadItemizedPrompts(chatId) { | ||
| 1110 | try { | ||
| 1111 | if (!chatId) { | ||
| 1112 | itemizedPrompts = []; | ||
| 1113 | return; | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | itemizedPrompts = await promptStorage.getItem(chatId); | ||
| 1117 | |||
| 1118 | if (!itemizedPrompts) { | ||
| 1119 | itemizedPrompts = []; | ||
| 1120 | } | ||
| 1121 | } catch { | ||
| 1122 | console.log('Error loading itemized prompts for chat', chatId); | ||
| 1123 | itemizedPrompts = []; | ||
| 1124 | } | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | /** | ||
| 1128 | * Saves the itemized prompts for a chat. | ||
| 1129 | * @param {string} chatId Chat ID to save itemized prompts for | ||
| 1130 | */ | ||
| 1131 | export async function saveItemizedPrompts(chatId) { | ||
| 1132 | try { | ||
| 1133 | if (!chatId) { | ||
| 1134 | return; | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | await promptStorage.setItem(chatId, itemizedPrompts); | ||
| 1138 | } catch { | ||
| 1139 | console.log('Error saving itemized prompts for chat', chatId); | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | /** | ||
| 1144 | * Replaces the itemized prompt text for a message. | ||
| 1145 | * @param {number} mesId Message ID to get itemized prompt for | ||
| 1146 | * @param {string} promptText New raw prompt text | ||
| 1147 | * @returns | ||
| 1148 | */ | ||
| 1149 | export async function replaceItemizedPromptText(mesId, promptText) { | ||
| 1150 | if (!Array.isArray(itemizedPrompts)) { | ||
| 1151 | itemizedPrompts = []; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | const itemizedPrompt = itemizedPrompts.find(x => x.mesId === mesId); | ||
| 1155 | |||
| 1156 | if (!itemizedPrompt) { | ||
| 1157 | return; | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | itemizedPrompt.rawPrompt = promptText; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | /** | ||
| 1164 | * Deletes the itemized prompts for a chat. | ||
| 1165 | * @param {string} chatId Chat ID to delete itemized prompts for | ||
| 1166 | */ | ||
| 1167 | export async function deleteItemizedPrompts(chatId) { | ||
| 1168 | try { | ||
| 1169 | if (!chatId) { | ||
| 1170 | return; | ||
| 1171 | } | ||
| 1172 | |||
| 1173 | await promptStorage.removeItem(chatId); | ||
| 1174 | } catch { | ||
| 1175 | console.log('Error deleting itemized prompts for chat', chatId); | ||
| 1176 | } | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | /** | ||
| 1180 | * Empties the itemized prompts array and caches. | ||
| 1181 | */ | ||
| 1182 | export async function clearItemizedPrompts() { | ||
| 1183 | try { | ||
| 1184 | await promptStorage.clear(); | ||
| 1185 | itemizedPrompts = []; | ||
| 1186 | } catch { | ||
| 1187 | console.log('Error clearing itemized prompts'); | ||
| 1188 | } | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | async function getStatusHorde() { | 1111 | async function getStatusHorde() { |
| 1192 | try { | 1112 | try { |
| 1193 | const hordeStatus = await checkHordeStatus(); | 1113 | const hordeStatus = await checkHordeStatus(); |
| @@ -2089,7 +2009,7 @@ export async function clearChat() { | |||
| 2089 | } else { console.debug('saw no avatars'); } | 2009 | } else { console.debug('saw no avatars'); } |
| 2090 | 2010 | ||
| 2091 | await saveItemizedPrompts(getCurrentChatId()); | 2011 | await saveItemizedPrompts(getCurrentChatId()); |
| 2092 | itemizedPrompts = []; | 2012 | itemizedPrompts.length = 0; |
| 2093 | } | 2013 | } |
| 2094 | 2014 | ||
| 2095 | export async function deleteLastMessage() { | 2015 | export async function deleteLastMessage() { |
| @@ -5724,231 +5644,6 @@ async function duplicateCharacter() { | |||
| 5724 | return ''; | 5644 | return ''; |
| 5725 | } | 5645 | } |
| 5726 | 5646 | ||
| 5727 | export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMesId) { | ||
| 5728 | const params = { | ||
| 5729 | charDescriptionTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].charDescription), | ||
| 5730 | charPersonalityTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].charPersonality), | ||
| 5731 | scenarioTextTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].scenarioText), | ||
| 5732 | userPersonaStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].userPersona), | ||
| 5733 | worldInfoStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].worldInfoString), | ||
| 5734 | allAnchorsTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].allAnchors), | ||
| 5735 | summarizeStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].summarizeString), | ||
| 5736 | authorsNoteStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].authorsNoteString), | ||
| 5737 | smartContextStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].smartContextString), | ||
| 5738 | beforeScenarioAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].beforeScenarioAnchor), | ||
| 5739 | afterScenarioAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].afterScenarioAnchor), | ||
| 5740 | zeroDepthAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].zeroDepthAnchor), // TODO: unused | ||
| 5741 | thisPrompt_padding: itemizedPrompts[thisPromptSet].padding, | ||
| 5742 | this_main_api: itemizedPrompts[thisPromptSet].main_api, | ||
| 5743 | chatInjects: await getTokenCountAsync(itemizedPrompts[thisPromptSet].chatInjects), | ||
| 5744 | chatVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].chatVectorsString), | ||
| 5745 | dataBankVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].dataBankVectorsString), | ||
| 5746 | modelUsed: chat[incomingMesId]?.extra?.model, | ||
| 5747 | apiUsed: chat[incomingMesId]?.extra?.api, | ||
| 5748 | presetName: itemizedPrompts[thisPromptSet].presetName || t`(Unknown)`, | ||
| 5749 | messagesCount: String(itemizedPrompts[thisPromptSet].messagesCount ?? ''), | ||
| 5750 | examplesCount: String(itemizedPrompts[thisPromptSet].examplesCount ?? ''), | ||
| 5751 | }; | ||
| 5752 | |||
| 5753 | const getFriendlyName = (value) => $(`#rm_api_block select option[value="${value}"]`).first().text() || value; | ||
| 5754 | |||
| 5755 | if (params.apiUsed) { | ||
| 5756 | params.apiUsed = getFriendlyName(params.apiUsed); | ||
| 5757 | } | ||
| 5758 | |||
| 5759 | if (params.this_main_api) { | ||
| 5760 | params.mainApiFriendlyName = getFriendlyName(params.this_main_api); | ||
| 5761 | } | ||
| 5762 | |||
| 5763 | if (params.chatInjects) { | ||
| 5764 | params.ActualChatHistoryTokens = params.ActualChatHistoryTokens - params.chatInjects; | ||
| 5765 | } | ||
| 5766 | |||
| 5767 | if (params.this_main_api == 'openai') { | ||
| 5768 | //for OAI API | ||
| 5769 | //console.log('-- Counting OAI Tokens'); | ||
| 5770 | |||
| 5771 | //params.finalPromptTokens = itemizedPrompts[thisPromptSet].oaiTotalTokens; | ||
| 5772 | params.oaiMainTokens = itemizedPrompts[thisPromptSet].oaiMainTokens; | ||
| 5773 | params.oaiStartTokens = itemizedPrompts[thisPromptSet].oaiStartTokens; | ||
| 5774 | params.ActualChatHistoryTokens = itemizedPrompts[thisPromptSet].oaiConversationTokens; | ||
| 5775 | params.examplesStringTokens = itemizedPrompts[thisPromptSet].oaiExamplesTokens; | ||
| 5776 | params.oaiPromptTokens = itemizedPrompts[thisPromptSet].oaiPromptTokens - (params.afterScenarioAnchorTokens + params.beforeScenarioAnchorTokens) + params.examplesStringTokens; | ||
| 5777 | params.oaiBiasTokens = itemizedPrompts[thisPromptSet].oaiBiasTokens; | ||
| 5778 | params.oaiJailbreakTokens = itemizedPrompts[thisPromptSet].oaiJailbreakTokens; | ||
| 5779 | params.oaiNudgeTokens = itemizedPrompts[thisPromptSet].oaiNudgeTokens; | ||
| 5780 | params.oaiImpersonateTokens = itemizedPrompts[thisPromptSet].oaiImpersonateTokens; | ||
| 5781 | params.oaiNsfwTokens = itemizedPrompts[thisPromptSet].oaiNsfwTokens; | ||
| 5782 | params.finalPromptTokens = | ||
| 5783 | params.oaiStartTokens + | ||
| 5784 | params.oaiPromptTokens + | ||
| 5785 | params.oaiMainTokens + | ||
| 5786 | params.oaiNsfwTokens + | ||
| 5787 | params.oaiBiasTokens + | ||
| 5788 | params.oaiImpersonateTokens + | ||
| 5789 | params.oaiJailbreakTokens + | ||
| 5790 | params.oaiNudgeTokens + | ||
| 5791 | params.ActualChatHistoryTokens + | ||
| 5792 | //charDescriptionTokens + | ||
| 5793 | //charPersonalityTokens + | ||
| 5794 | //allAnchorsTokens + | ||
| 5795 | params.worldInfoStringTokens + | ||
| 5796 | params.beforeScenarioAnchorTokens + | ||
| 5797 | params.afterScenarioAnchorTokens; | ||
| 5798 | // Max context size - max completion tokens | ||
| 5799 | params.thisPrompt_max_context = (oai_settings.openai_max_context - oai_settings.openai_max_tokens); | ||
| 5800 | |||
| 5801 | //console.log('-- applying % on OAI tokens'); | ||
| 5802 | params.oaiStartTokensPercentage = ((params.oaiStartTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 5803 | params.storyStringTokensPercentage = (((params.afterScenarioAnchorTokens + params.beforeScenarioAnchorTokens + params.oaiPromptTokens) / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 5804 | params.ActualChatHistoryTokensPercentage = ((params.ActualChatHistoryTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 5805 | params.promptBiasTokensPercentage = ((params.oaiBiasTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 5806 | params.worldInfoStringTokensPercentage = ((params.worldInfoStringTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 5807 | params.allAnchorsTokensPercentage = ((params.allAnchorsTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 5808 | params.selectedTokenizer = getFriendlyTokenizerName(params.this_main_api).tokenizerName; | ||
| 5809 | params.oaiSystemTokens = params.oaiImpersonateTokens + params.oaiJailbreakTokens + params.oaiNudgeTokens + params.oaiStartTokens + params.oaiNsfwTokens + params.oaiMainTokens; | ||
| 5810 | params.oaiSystemTokensPercentage = ((params.oaiSystemTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 5811 | } else { | ||
| 5812 | //for non-OAI APIs | ||
| 5813 | //console.log('-- Counting non-OAI Tokens'); | ||
| 5814 | params.finalPromptTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].finalPrompt); | ||
| 5815 | params.storyStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].storyString) - params.worldInfoStringTokens; | ||
| 5816 | params.examplesStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].examplesString); | ||
| 5817 | params.mesSendStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].mesSendString); | ||
| 5818 | params.ActualChatHistoryTokens = params.mesSendStringTokens - (params.allAnchorsTokens - (params.beforeScenarioAnchorTokens + params.afterScenarioAnchorTokens)) + power_user.token_padding; | ||
| 5819 | params.instructionTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].instruction); | ||
| 5820 | params.promptBiasTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].promptBias); | ||
| 5821 | |||
| 5822 | params.totalTokensInPrompt = | ||
| 5823 | params.storyStringTokens + //chardefs total | ||
| 5824 | params.worldInfoStringTokens + | ||
| 5825 | params.examplesStringTokens + // example messages | ||
| 5826 | params.ActualChatHistoryTokens + //chat history | ||
| 5827 | params.allAnchorsTokens + // AN and/or legacy anchors | ||
| 5828 | //afterScenarioAnchorTokens + //only counts if AN is set to 'after scenario' | ||
| 5829 | //zeroDepthAnchorTokens + //same as above, even if AN not on 0 depth | ||
| 5830 | params.promptBiasTokens; //{{}} | ||
| 5831 | //- thisPrompt_padding; //not sure this way of calculating is correct, but the math results in same value as 'finalPrompt' | ||
| 5832 | params.thisPrompt_max_context = itemizedPrompts[thisPromptSet].this_max_context; | ||
| 5833 | params.thisPrompt_actual = params.thisPrompt_max_context - params.thisPrompt_padding; | ||
| 5834 | |||
| 5835 | //console.log('-- applying % on non-OAI tokens'); | ||
| 5836 | params.storyStringTokensPercentage = ((params.storyStringTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 5837 | params.ActualChatHistoryTokensPercentage = ((params.ActualChatHistoryTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 5838 | params.promptBiasTokensPercentage = ((params.promptBiasTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 5839 | params.worldInfoStringTokensPercentage = ((params.worldInfoStringTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 5840 | params.allAnchorsTokensPercentage = ((params.allAnchorsTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 5841 | params.selectedTokenizer = itemizedPrompts[thisPromptSet]?.tokenizer || getFriendlyTokenizerName(params.this_main_api).tokenizerName; | ||
| 5842 | } | ||
| 5843 | return params; | ||
| 5844 | } | ||
| 5845 | |||
| 5846 | export function findItemizedPromptSet(itemizedPrompts, incomingMesId) { | ||
| 5847 | var thisPromptSet = undefined; | ||
| 5848 | |||
| 5849 | for (var i = 0; i < itemizedPrompts.length; i++) { | ||
| 5850 | console.log(`looking for ${incomingMesId} vs ${itemizedPrompts[i].mesId}`); | ||
| 5851 | if (itemizedPrompts[i].mesId === incomingMesId) { | ||
| 5852 | console.log(`found matching mesID ${i}`); | ||
| 5853 | thisPromptSet = i; | ||
| 5854 | PromptArrayItemForRawPromptDisplay = i; | ||
| 5855 | console.log(`wanting to raw display of ArrayItem: ${PromptArrayItemForRawPromptDisplay} which is mesID ${incomingMesId}`); | ||
| 5856 | console.log(itemizedPrompts[thisPromptSet]); | ||
| 5857 | break; | ||
| 5858 | } else if (itemizedPrompts[i].rawPrompt) { | ||
| 5859 | priorPromptArrayItemForRawPromptDisplay = i; | ||
| 5860 | } | ||
| 5861 | } | ||
| 5862 | return thisPromptSet; | ||
| 5863 | } | ||
| 5864 | |||
| 5865 | async function promptItemize(itemizedPrompts, requestedMesId) { | ||
| 5866 | console.log('PROMPT ITEMIZE ENTERED'); | ||
| 5867 | var incomingMesId = Number(requestedMesId); | ||
| 5868 | console.debug(`looking for MesId ${incomingMesId}`); | ||
| 5869 | var thisPromptSet = findItemizedPromptSet(itemizedPrompts, incomingMesId); | ||
| 5870 | |||
| 5871 | if (thisPromptSet === undefined) { | ||
| 5872 | console.log(`couldnt find the right mesId. looked for ${incomingMesId}`); | ||
| 5873 | console.log(itemizedPrompts); | ||
| 5874 | return null; | ||
| 5875 | } | ||
| 5876 | |||
| 5877 | const params = await itemizedParams(itemizedPrompts, thisPromptSet, incomingMesId); | ||
| 5878 | const flatten = (rawPrompt) => Array.isArray(rawPrompt) ? rawPrompt.map(x => x.content).join('\n') : rawPrompt; | ||
| 5879 | |||
| 5880 | const template = params.this_main_api == 'openai' | ||
| 5881 | ? await renderTemplateAsync('itemizationChat', params) | ||
| 5882 | : await renderTemplateAsync('itemizationText', params); | ||
| 5883 | |||
| 5884 | const popup = new Popup(template, POPUP_TYPE.TEXT); | ||
| 5885 | |||
| 5886 | /** @type {HTMLElement} */ | ||
| 5887 | const diffPrevPrompt = popup.dlg.querySelector('#diffPrevPrompt'); | ||
| 5888 | if (priorPromptArrayItemForRawPromptDisplay) { | ||
| 5889 | diffPrevPrompt.style.display = ''; | ||
| 5890 | diffPrevPrompt.addEventListener('click', function () { | ||
| 5891 | const dmp = new DiffMatchPatch(); | ||
| 5892 | const text1 = flatten(itemizedPrompts[priorPromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 5893 | const text2 = flatten(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 5894 | |||
| 5895 | dmp.Diff_Timeout = 2.0; | ||
| 5896 | |||
| 5897 | const d = dmp.diff_main(text1, text2); | ||
| 5898 | let ds = dmp.diff_prettyHtml(d); | ||
| 5899 | // make it readable | ||
| 5900 | ds = ds.replaceAll('background:#e6ffe6;', 'background:#b9f3b9; color:black;'); | ||
| 5901 | ds = ds.replaceAll('background:#ffe6e6;', 'background:#f5b4b4; color:black;'); | ||
| 5902 | ds = ds.replaceAll('¶', ''); | ||
| 5903 | const container = document.createElement('div'); | ||
| 5904 | container.innerHTML = DOMPurify.sanitize(ds); | ||
| 5905 | const rawPromptWrapper = document.getElementById('rawPromptWrapper'); | ||
| 5906 | rawPromptWrapper.replaceChildren(container); | ||
| 5907 | $('#rawPromptPopup').slideToggle(); | ||
| 5908 | }); | ||
| 5909 | } else { | ||
| 5910 | diffPrevPrompt.style.display = 'none'; | ||
| 5911 | } | ||
| 5912 | popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('pointerup', async function () { | ||
| 5913 | let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt; | ||
| 5914 | let rawPromptValues = rawPrompt; | ||
| 5915 | |||
| 5916 | if (Array.isArray(rawPrompt)) { | ||
| 5917 | rawPromptValues = rawPrompt.map(x => x.content).join('\n'); | ||
| 5918 | } | ||
| 5919 | |||
| 5920 | await copyText(rawPromptValues); | ||
| 5921 | toastr.info(t`Copied!`); | ||
| 5922 | }); | ||
| 5923 | |||
| 5924 | popup.dlg.querySelector('#showRawPrompt').addEventListener('click', async function () { | ||
| 5925 | //console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 5926 | console.log(PromptArrayItemForRawPromptDisplay); | ||
| 5927 | console.log(itemizedPrompts); | ||
| 5928 | console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 5929 | |||
| 5930 | const rawPrompt = flatten(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 5931 | |||
| 5932 | // Mobile needs special handholding. The side-view on the popup wouldn't work, | ||
| 5933 | // so we just show an additional popup for this. | ||
| 5934 | if (isMobile()) { | ||
| 5935 | const content = document.createElement('div'); | ||
| 5936 | content.classList.add('tokenItemizingMaintext'); | ||
| 5937 | content.innerText = rawPrompt; | ||
| 5938 | const popup = new Popup(content, POPUP_TYPE.TEXT, null, { allowVerticalScrolling: true, leftAlign: true }); | ||
| 5939 | await popup.show(); | ||
| 5940 | return; | ||
| 5941 | } | ||
| 5942 | |||
| 5943 | //let DisplayStringifiedPrompt = JSON.stringify(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt).replace(/\n+/g, '<br>'); | ||
| 5944 | const rawPromptWrapper = document.getElementById('rawPromptWrapper'); | ||
| 5945 | rawPromptWrapper.innerText = rawPrompt; | ||
| 5946 | $('#rawPromptPopup').slideToggle(); | ||
| 5947 | }); | ||
| 5948 | |||
| 5949 | await popup.show(); | ||
| 5950 | } | ||
| 5951 | |||
| 5952 | function setInContextMessages(msgInContextCount, type) { | 5647 | function setInContextMessages(msgInContextCount, type) { |
| 5953 | $('#chat .mes').removeClass('lastInContext'); | 5648 | $('#chat .mes').removeClass('lastInContext'); |
| 5954 | 5649 | ||
| @@ -10474,15 +10169,6 @@ function addDebugFunctions() { | |||
| 10474 | const message = await generateRaw(text, null, false, false); | 10169 | const message = await generateRaw(text, null, false, false); |
| 10475 | alert(message); | 10170 | alert(message); |
| 10476 | }); | 10171 | }); |
| 10477 | |||
| 10478 | registerDebugFunction('clearPrompts', 'Delete itemized prompts', 'Deletes all itemized prompts from the local storage.', async () => { | ||
| 10479 | await clearItemizedPrompts(); | ||
| 10480 | toastr.info('Itemized prompts deleted.'); | ||
| 10481 | if (getCurrentChatId()) { | ||
| 10482 | await reloadCurrentChat(); | ||
| 10483 | } | ||
| 10484 | }); | ||
| 10485 | |||
| 10486 | registerDebugFunction('toggleEventTracing', 'Toggle event tracing', 'Useful to see what triggered a certain event.', () => { | 10172 | registerDebugFunction('toggleEventTracing', 'Toggle event tracing', 'Useful to see what triggered a certain event.', () => { |
| 10487 | localStorage.setItem('eventTracing', localStorage.getItem('eventTracing') === 'true' ? 'false' : 'true'); | 10173 | localStorage.setItem('eventTracing', localStorage.getItem('eventTracing') === 'true' ? 'false' : 'true'); |
| 10488 | toastr.info('Event tracing is now ' + (localStorage.getItem('eventTracing') === 'true' ? 'enabled' : 'disabled')); | 10174 | toastr.info('Event tracing is now ' + (localStorage.getItem('eventTracing') === 'true' ? 'enabled' : 'disabled')); |
| @@ -11614,14 +11300,6 @@ jQuery(async function () { | |||
| 11614 | } | 11300 | } |
| 11615 | }); | 11301 | }); |
| 11616 | 11302 | ||
| 11617 | $(document).on('pointerup', '.mes_prompt', async function () { | ||
| 11618 | let mesIdForItemization = $(this).closest('.mes').attr('mesId'); | ||
| 11619 | console.log(`looking for mesID: ${mesIdForItemization}`); | ||
| 11620 | if (itemizedPrompts.length !== undefined && itemizedPrompts.length !== 0) { | ||
| 11621 | await promptItemize(itemizedPrompts, mesIdForItemization); | ||
| 11622 | } | ||
| 11623 | }); | ||
| 11624 | |||
| 11625 | //******************** | 11303 | //******************** |
| 11626 | //***Message Editor*** | 11304 | //***Message Editor*** |
| 11627 | $(document).on('click', '.mes_edit', async function () { | 11305 | $(document).on('click', '.mes_edit', async function () { |
| @@ -12517,13 +12195,6 @@ jQuery(async function () { | |||
| 12517 | // Added here to prevent execution before script.js is loaded and get rid of quirky timeouts | 12195 | // Added here to prevent execution before script.js is loaded and get rid of quirky timeouts |
| 12518 | await firstLoadInit(); | 12196 | await firstLoadInit(); |
| 12519 | 12197 | ||
| 12520 | eventSource.on(event_types.CHAT_DELETED, async (name) => { | ||
| 12521 | await deleteItemizedPrompts(name); | ||
| 12522 | }); | ||
| 12523 | eventSource.on(event_types.GROUP_CHAT_DELETED, async (name) => { | ||
| 12524 | await deleteItemizedPrompts(name); | ||
| 12525 | }); | ||
| 12526 | |||
| 12527 | window.addEventListener('beforeunload', (e) => { | 12198 | window.addEventListener('beforeunload', (e) => { |
| 12528 | if (isChatSaving) { | 12199 | if (isChatSaving) { |
| 12529 | e.preventDefault(); | 12200 | e.preventDefault(); |
| @@ -0,0 +1,352 @@ | |||
| 1 | import { DiffMatchPatch, DOMPurify, localforage } from '../lib.js'; | ||
| 2 | import { chat, event_types, eventSource, getCurrentChatId, reloadCurrentChat } from '../script.js'; | ||
| 3 | import { t } from './i18n.js'; | ||
| 4 | import { oai_settings } from './openai.js'; | ||
| 5 | import { Popup, POPUP_TYPE } from './popup.js'; | ||
| 6 | import { power_user, registerDebugFunction } from './power-user.js'; | ||
| 7 | import { isMobile } from './RossAscends-mods.js'; | ||
| 8 | import { renderTemplateAsync } from './templates.js'; | ||
| 9 | import { getFriendlyTokenizerName, getTokenCountAsync } from './tokenizers.js'; | ||
| 10 | import { copyText } from './utils.js'; | ||
| 11 | |||
| 12 | let PromptArrayItemForRawPromptDisplay; | ||
| 13 | let priorPromptArrayItemForRawPromptDisplay; | ||
| 14 | |||
| 15 | const promptStorage = localforage.createInstance({ name: 'SillyTavern_Prompts' }); | ||
| 16 | export let itemizedPrompts = []; | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Gets the itemized prompts for a chat. | ||
| 20 | * @param {string} chatId Chat ID to load | ||
| 21 | */ | ||
| 22 | export async function loadItemizedPrompts(chatId) { | ||
| 23 | try { | ||
| 24 | if (!chatId) { | ||
| 25 | itemizedPrompts = []; | ||
| 26 | return; | ||
| 27 | } | ||
| 28 | |||
| 29 | itemizedPrompts = await promptStorage.getItem(chatId); | ||
| 30 | |||
| 31 | if (!itemizedPrompts) { | ||
| 32 | itemizedPrompts = []; | ||
| 33 | } | ||
| 34 | } catch { | ||
| 35 | console.log('Error loading itemized prompts for chat', chatId); | ||
| 36 | itemizedPrompts = []; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Saves the itemized prompts for a chat. | ||
| 42 | * @param {string} chatId Chat ID to save itemized prompts for | ||
| 43 | */ | ||
| 44 | export async function saveItemizedPrompts(chatId) { | ||
| 45 | try { | ||
| 46 | if (!chatId) { | ||
| 47 | return; | ||
| 48 | } | ||
| 49 | |||
| 50 | await promptStorage.setItem(chatId, itemizedPrompts); | ||
| 51 | } catch { | ||
| 52 | console.log('Error saving itemized prompts for chat', chatId); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Replaces the itemized prompt text for a message. | ||
| 58 | * @param {number} mesId Message ID to get itemized prompt for | ||
| 59 | * @param {string} promptText New raw prompt text | ||
| 60 | * @returns | ||
| 61 | */ | ||
| 62 | export async function replaceItemizedPromptText(mesId, promptText) { | ||
| 63 | if (!Array.isArray(itemizedPrompts)) { | ||
| 64 | itemizedPrompts = []; | ||
| 65 | } | ||
| 66 | |||
| 67 | const itemizedPrompt = itemizedPrompts.find(x => x.mesId === mesId); | ||
| 68 | |||
| 69 | if (!itemizedPrompt) { | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | itemizedPrompt.rawPrompt = promptText; | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * Deletes the itemized prompts for a chat. | ||
| 78 | * @param {string} chatId Chat ID to delete itemized prompts for | ||
| 79 | */ | ||
| 80 | export async function deleteItemizedPrompts(chatId) { | ||
| 81 | try { | ||
| 82 | if (!chatId) { | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | await promptStorage.removeItem(chatId); | ||
| 87 | } catch { | ||
| 88 | console.log('Error deleting itemized prompts for chat', chatId); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Empties the itemized prompts array and caches. | ||
| 94 | */ | ||
| 95 | export async function clearItemizedPrompts() { | ||
| 96 | try { | ||
| 97 | await promptStorage.clear(); | ||
| 98 | itemizedPrompts = []; | ||
| 99 | } catch { | ||
| 100 | console.log('Error clearing itemized prompts'); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMesId) { | ||
| 105 | const params = { | ||
| 106 | charDescriptionTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].charDescription), | ||
| 107 | charPersonalityTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].charPersonality), | ||
| 108 | scenarioTextTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].scenarioText), | ||
| 109 | userPersonaStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].userPersona), | ||
| 110 | worldInfoStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].worldInfoString), | ||
| 111 | allAnchorsTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].allAnchors), | ||
| 112 | summarizeStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].summarizeString), | ||
| 113 | authorsNoteStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].authorsNoteString), | ||
| 114 | smartContextStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].smartContextString), | ||
| 115 | beforeScenarioAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].beforeScenarioAnchor), | ||
| 116 | afterScenarioAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].afterScenarioAnchor), | ||
| 117 | zeroDepthAnchorTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].zeroDepthAnchor), // TODO: unused | ||
| 118 | thisPrompt_padding: itemizedPrompts[thisPromptSet].padding, | ||
| 119 | this_main_api: itemizedPrompts[thisPromptSet].main_api, | ||
| 120 | chatInjects: await getTokenCountAsync(itemizedPrompts[thisPromptSet].chatInjects), | ||
| 121 | chatVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].chatVectorsString), | ||
| 122 | dataBankVectorsStringTokens: await getTokenCountAsync(itemizedPrompts[thisPromptSet].dataBankVectorsString), | ||
| 123 | modelUsed: chat[incomingMesId]?.extra?.model, | ||
| 124 | apiUsed: chat[incomingMesId]?.extra?.api, | ||
| 125 | presetName: itemizedPrompts[thisPromptSet].presetName || t`(Unknown)`, | ||
| 126 | messagesCount: String(itemizedPrompts[thisPromptSet].messagesCount ?? ''), | ||
| 127 | examplesCount: String(itemizedPrompts[thisPromptSet].examplesCount ?? ''), | ||
| 128 | }; | ||
| 129 | |||
| 130 | const getFriendlyName = (value) => $(`#rm_api_block select option[value="${value}"]`).first().text() || value; | ||
| 131 | |||
| 132 | if (params.apiUsed) { | ||
| 133 | params.apiUsed = getFriendlyName(params.apiUsed); | ||
| 134 | } | ||
| 135 | |||
| 136 | if (params.this_main_api) { | ||
| 137 | params.mainApiFriendlyName = getFriendlyName(params.this_main_api); | ||
| 138 | } | ||
| 139 | |||
| 140 | if (params.chatInjects) { | ||
| 141 | params.ActualChatHistoryTokens = params.ActualChatHistoryTokens - params.chatInjects; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (params.this_main_api == 'openai') { | ||
| 145 | //for OAI API | ||
| 146 | //console.log('-- Counting OAI Tokens'); | ||
| 147 | |||
| 148 | //params.finalPromptTokens = itemizedPrompts[thisPromptSet].oaiTotalTokens; | ||
| 149 | params.oaiMainTokens = itemizedPrompts[thisPromptSet].oaiMainTokens; | ||
| 150 | params.oaiStartTokens = itemizedPrompts[thisPromptSet].oaiStartTokens; | ||
| 151 | params.ActualChatHistoryTokens = itemizedPrompts[thisPromptSet].oaiConversationTokens; | ||
| 152 | params.examplesStringTokens = itemizedPrompts[thisPromptSet].oaiExamplesTokens; | ||
| 153 | params.oaiPromptTokens = itemizedPrompts[thisPromptSet].oaiPromptTokens - (params.afterScenarioAnchorTokens + params.beforeScenarioAnchorTokens) + params.examplesStringTokens; | ||
| 154 | params.oaiBiasTokens = itemizedPrompts[thisPromptSet].oaiBiasTokens; | ||
| 155 | params.oaiJailbreakTokens = itemizedPrompts[thisPromptSet].oaiJailbreakTokens; | ||
| 156 | params.oaiNudgeTokens = itemizedPrompts[thisPromptSet].oaiNudgeTokens; | ||
| 157 | params.oaiImpersonateTokens = itemizedPrompts[thisPromptSet].oaiImpersonateTokens; | ||
| 158 | params.oaiNsfwTokens = itemizedPrompts[thisPromptSet].oaiNsfwTokens; | ||
| 159 | params.finalPromptTokens = | ||
| 160 | params.oaiStartTokens + | ||
| 161 | params.oaiPromptTokens + | ||
| 162 | params.oaiMainTokens + | ||
| 163 | params.oaiNsfwTokens + | ||
| 164 | params.oaiBiasTokens + | ||
| 165 | params.oaiImpersonateTokens + | ||
| 166 | params.oaiJailbreakTokens + | ||
| 167 | params.oaiNudgeTokens + | ||
| 168 | params.ActualChatHistoryTokens + | ||
| 169 | //charDescriptionTokens + | ||
| 170 | //charPersonalityTokens + | ||
| 171 | //allAnchorsTokens + | ||
| 172 | params.worldInfoStringTokens + | ||
| 173 | params.beforeScenarioAnchorTokens + | ||
| 174 | params.afterScenarioAnchorTokens; | ||
| 175 | // Max context size - max completion tokens | ||
| 176 | params.thisPrompt_max_context = (oai_settings.openai_max_context - oai_settings.openai_max_tokens); | ||
| 177 | |||
| 178 | //console.log('-- applying % on OAI tokens'); | ||
| 179 | params.oaiStartTokensPercentage = ((params.oaiStartTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 180 | params.storyStringTokensPercentage = (((params.afterScenarioAnchorTokens + params.beforeScenarioAnchorTokens + params.oaiPromptTokens) / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 181 | params.ActualChatHistoryTokensPercentage = ((params.ActualChatHistoryTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 182 | params.promptBiasTokensPercentage = ((params.oaiBiasTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 183 | params.worldInfoStringTokensPercentage = ((params.worldInfoStringTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 184 | params.allAnchorsTokensPercentage = ((params.allAnchorsTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 185 | params.selectedTokenizer = getFriendlyTokenizerName(params.this_main_api).tokenizerName; | ||
| 186 | params.oaiSystemTokens = params.oaiImpersonateTokens + params.oaiJailbreakTokens + params.oaiNudgeTokens + params.oaiStartTokens + params.oaiNsfwTokens + params.oaiMainTokens; | ||
| 187 | params.oaiSystemTokensPercentage = ((params.oaiSystemTokens / (params.finalPromptTokens)) * 100).toFixed(2); | ||
| 188 | } else { | ||
| 189 | //for non-OAI APIs | ||
| 190 | //console.log('-- Counting non-OAI Tokens'); | ||
| 191 | params.finalPromptTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].finalPrompt); | ||
| 192 | params.storyStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].storyString) - params.worldInfoStringTokens; | ||
| 193 | params.examplesStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].examplesString); | ||
| 194 | params.mesSendStringTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].mesSendString); | ||
| 195 | params.ActualChatHistoryTokens = params.mesSendStringTokens - (params.allAnchorsTokens - (params.beforeScenarioAnchorTokens + params.afterScenarioAnchorTokens)) + power_user.token_padding; | ||
| 196 | params.instructionTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].instruction); | ||
| 197 | params.promptBiasTokens = await getTokenCountAsync(itemizedPrompts[thisPromptSet].promptBias); | ||
| 198 | |||
| 199 | params.totalTokensInPrompt = | ||
| 200 | params.storyStringTokens + //chardefs total | ||
| 201 | params.worldInfoStringTokens + | ||
| 202 | params.examplesStringTokens + // example messages | ||
| 203 | params.ActualChatHistoryTokens + //chat history | ||
| 204 | params.allAnchorsTokens + // AN and/or legacy anchors | ||
| 205 | //afterScenarioAnchorTokens + //only counts if AN is set to 'after scenario' | ||
| 206 | //zeroDepthAnchorTokens + //same as above, even if AN not on 0 depth | ||
| 207 | params.promptBiasTokens; //{{}} | ||
| 208 | //- thisPrompt_padding; //not sure this way of calculating is correct, but the math results in same value as 'finalPrompt' | ||
| 209 | params.thisPrompt_max_context = itemizedPrompts[thisPromptSet].this_max_context; | ||
| 210 | params.thisPrompt_actual = params.thisPrompt_max_context - params.thisPrompt_padding; | ||
| 211 | |||
| 212 | //console.log('-- applying % on non-OAI tokens'); | ||
| 213 | params.storyStringTokensPercentage = ((params.storyStringTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 214 | params.ActualChatHistoryTokensPercentage = ((params.ActualChatHistoryTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 215 | params.promptBiasTokensPercentage = ((params.promptBiasTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 216 | params.worldInfoStringTokensPercentage = ((params.worldInfoStringTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 217 | params.allAnchorsTokensPercentage = ((params.allAnchorsTokens / (params.totalTokensInPrompt)) * 100).toFixed(2); | ||
| 218 | params.selectedTokenizer = itemizedPrompts[thisPromptSet]?.tokenizer || getFriendlyTokenizerName(params.this_main_api).tokenizerName; | ||
| 219 | } | ||
| 220 | return params; | ||
| 221 | } | ||
| 222 | |||
| 223 | export function findItemizedPromptSet(itemizedPrompts, incomingMesId) { | ||
| 224 | let thisPromptSet = undefined; | ||
| 225 | |||
| 226 | for (let i = 0; i < itemizedPrompts.length; i++) { | ||
| 227 | console.log(`looking for ${incomingMesId} vs ${itemizedPrompts[i].mesId}`); | ||
| 228 | if (itemizedPrompts[i].mesId === incomingMesId) { | ||
| 229 | console.log(`found matching mesID ${i}`); | ||
| 230 | thisPromptSet = i; | ||
| 231 | PromptArrayItemForRawPromptDisplay = i; | ||
| 232 | console.log(`wanting to raw display of ArrayItem: ${PromptArrayItemForRawPromptDisplay} which is mesID ${incomingMesId}`); | ||
| 233 | console.log(itemizedPrompts[thisPromptSet]); | ||
| 234 | break; | ||
| 235 | } else if (itemizedPrompts[i].rawPrompt) { | ||
| 236 | priorPromptArrayItemForRawPromptDisplay = i; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | return thisPromptSet; | ||
| 240 | } | ||
| 241 | |||
| 242 | export async function promptItemize(itemizedPrompts, requestedMesId) { | ||
| 243 | console.log('PROMPT ITEMIZE ENTERED'); | ||
| 244 | var incomingMesId = Number(requestedMesId); | ||
| 245 | console.debug(`looking for MesId ${incomingMesId}`); | ||
| 246 | var thisPromptSet = findItemizedPromptSet(itemizedPrompts, incomingMesId); | ||
| 247 | |||
| 248 | if (thisPromptSet === undefined) { | ||
| 249 | console.log(`couldnt find the right mesId. looked for ${incomingMesId}`); | ||
| 250 | console.log(itemizedPrompts); | ||
| 251 | return null; | ||
| 252 | } | ||
| 253 | |||
| 254 | const params = await itemizedParams(itemizedPrompts, thisPromptSet, incomingMesId); | ||
| 255 | const flatten = (rawPrompt) => Array.isArray(rawPrompt) ? rawPrompt.map(x => x.content).join('\n') : rawPrompt; | ||
| 256 | |||
| 257 | const template = params.this_main_api == 'openai' | ||
| 258 | ? await renderTemplateAsync('itemizationChat', params) | ||
| 259 | : await renderTemplateAsync('itemizationText', params); | ||
| 260 | |||
| 261 | const popup = new Popup(template, POPUP_TYPE.TEXT); | ||
| 262 | |||
| 263 | /** @type {HTMLElement} */ | ||
| 264 | const diffPrevPrompt = popup.dlg.querySelector('#diffPrevPrompt'); | ||
| 265 | if (priorPromptArrayItemForRawPromptDisplay) { | ||
| 266 | diffPrevPrompt.style.display = ''; | ||
| 267 | diffPrevPrompt.addEventListener('click', function () { | ||
| 268 | const dmp = new DiffMatchPatch(); | ||
| 269 | const text1 = flatten(itemizedPrompts[priorPromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 270 | const text2 = flatten(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 271 | |||
| 272 | dmp.Diff_Timeout = 2.0; | ||
| 273 | |||
| 274 | const d = dmp.diff_main(text1, text2); | ||
| 275 | let ds = dmp.diff_prettyHtml(d); | ||
| 276 | // make it readable | ||
| 277 | ds = ds.replaceAll('background:#e6ffe6;', 'background:#b9f3b9; color:black;'); | ||
| 278 | ds = ds.replaceAll('background:#ffe6e6;', 'background:#f5b4b4; color:black;'); | ||
| 279 | ds = ds.replaceAll('¶', ''); | ||
| 280 | const container = document.createElement('div'); | ||
| 281 | container.innerHTML = DOMPurify.sanitize(ds); | ||
| 282 | const rawPromptWrapper = document.getElementById('rawPromptWrapper'); | ||
| 283 | rawPromptWrapper.replaceChildren(container); | ||
| 284 | $('#rawPromptPopup').slideToggle(); | ||
| 285 | }); | ||
| 286 | } else { | ||
| 287 | diffPrevPrompt.style.display = 'none'; | ||
| 288 | } | ||
| 289 | popup.dlg.querySelector('#copyPromptToClipboard').addEventListener('pointerup', async function () { | ||
| 290 | let rawPrompt = itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt; | ||
| 291 | let rawPromptValues = rawPrompt; | ||
| 292 | |||
| 293 | if (Array.isArray(rawPrompt)) { | ||
| 294 | rawPromptValues = rawPrompt.map(x => x.content).join('\n'); | ||
| 295 | } | ||
| 296 | |||
| 297 | await copyText(rawPromptValues); | ||
| 298 | toastr.info(t`Copied!`); | ||
| 299 | }); | ||
| 300 | |||
| 301 | popup.dlg.querySelector('#showRawPrompt').addEventListener('click', async function () { | ||
| 302 | //console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 303 | console.log(PromptArrayItemForRawPromptDisplay); | ||
| 304 | console.log(itemizedPrompts); | ||
| 305 | console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 306 | |||
| 307 | const rawPrompt = flatten(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt); | ||
| 308 | |||
| 309 | // Mobile needs special handholding. The side-view on the popup wouldn't work, | ||
| 310 | // so we just show an additional popup for this. | ||
| 311 | if (isMobile()) { | ||
| 312 | const content = document.createElement('div'); | ||
| 313 | content.classList.add('tokenItemizingMaintext'); | ||
| 314 | content.innerText = rawPrompt; | ||
| 315 | const popup = new Popup(content, POPUP_TYPE.TEXT, null, { allowVerticalScrolling: true, leftAlign: true }); | ||
| 316 | await popup.show(); | ||
| 317 | return; | ||
| 318 | } | ||
| 319 | |||
| 320 | //let DisplayStringifiedPrompt = JSON.stringify(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt).replace(/\n+/g, '<br>'); | ||
| 321 | const rawPromptWrapper = document.getElementById('rawPromptWrapper'); | ||
| 322 | rawPromptWrapper.innerText = rawPrompt; | ||
| 323 | $('#rawPromptPopup').slideToggle(); | ||
| 324 | }); | ||
| 325 | |||
| 326 | await popup.show(); | ||
| 327 | } | ||
| 328 | |||
| 329 | export function initItemizedPrompts() { | ||
| 330 | registerDebugFunction('clearPrompts', 'Delete itemized prompts', 'Deletes all itemized prompts from the local storage.', async () => { | ||
| 331 | await clearItemizedPrompts(); | ||
| 332 | toastr.info('Itemized prompts deleted.'); | ||
| 333 | if (getCurrentChatId()) { | ||
| 334 | await reloadCurrentChat(); | ||
| 335 | } | ||
| 336 | }); | ||
| 337 | |||
| 338 | $(document).on('pointerup', '.mes_prompt', async function () { | ||
| 339 | let mesIdForItemization = $(this).closest('.mes').attr('mesId'); | ||
| 340 | console.log(`looking for mesID: ${mesIdForItemization}`); | ||
| 341 | if (itemizedPrompts.length !== undefined && itemizedPrompts.length !== 0) { | ||
| 342 | await promptItemize(itemizedPrompts, mesIdForItemization); | ||
| 343 | } | ||
| 344 | }); | ||
| 345 | |||
| 346 | eventSource.on(event_types.CHAT_DELETED, async (name) => { | ||
| 347 | await deleteItemizedPrompts(name); | ||
| 348 | }); | ||
| 349 | eventSource.on(event_types.GROUP_CHAT_DELETED, async (name) => { | ||
| 350 | await deleteItemizedPrompts(name); | ||
| 351 | }); | ||
| 352 | } | ||