Allow markers to inspected for their content

This commit is contained in:
maver 2023-07-02 21:34:46 +02:00
parent 2452b37e28
commit a0bd67e455
3 changed files with 201 additions and 61 deletions

View File

@ -3584,41 +3584,60 @@
</div>
</div>
<div id="completion_prompt_manager_popup" style="display:none;">
<h3>OpenAI Prompt Manager Settings</h3>
<div class="completion_prompt_manager_popup_entry">
<form class="completion_prompt_manager_popup_entry_form">
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_name">
<span>Name</span>
</label>
<div class="text_muted">A name for this prompt.</div>
<input id="completion_prompt_manager_popup_entry_form_name" class="text_pole" type="text" name="name" />
</div>
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_role">
<span>Role</span>
</label>
<div class="text_muted">To whom this message will be attributed.</div>
<select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
<option value="system">System</option>
<option value="user">User</option>
<option value="assistant">AI Assistant</option>
</select>
</div>
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_prompt">
<span>Prompt</span>
</label>
<div class="text_muted">The prompt to be sent.</div>
<textarea id="completion_prompt_manager_popup_entry_form_prompt" class="text_pole" name="prompt">
<div id="completion_prompt_manager_popup_inspect">
<h3>Inspect</h3>
<div class="completion_prompt_manager_popup_entry">
<form class="completion_prompt_manager_popup_entry_form">
<div class="completion_prompt_manager_popup_entry_form_control">
<div class="completion_prompt_manager_popup_header">
<label for="completion_prompt_manager_popup_entry_form_prompt">
<span>Prompt List</span>
</label>
<a id="completion_prompt_manager_popup_close_button" title="close" class="fa-solid fa-close menu_button"></a>
</div>
<div class="text_muted">The list of prompts associated with this marker.</div>
<div id="completion_prompt_manager_popup_entry_form_inspect_list" class="text_pole"></div>
</div>
</form>
</div>
</div>
<div id="completion_prompt_manager_popup_edit">
<h3>Edit</h3>
<div class="completion_prompt_manager_popup_entry">
<form class="completion_prompt_manager_popup_entry_form">
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_name">
<span>Name</span>
</label>
<div class="text_muted">A name for this prompt.</div>
<input id="completion_prompt_manager_popup_entry_form_name" class="text_pole" type="text" name="name" />
</div>
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_role">
<span>Role</span>
</label>
<div class="text_muted">To whom this message will be attributed.</div>
<select id="completion_prompt_manager_popup_entry_form_role" class="text_pole" name="role">
<option value="system">System</option>
<option value="user">User</option>
<option value="assistant">AI Assistant</option>
</select>
</div>
<div class="completion_prompt_manager_popup_entry_form_control">
<label for="completion_prompt_manager_popup_entry_form_prompt">
<span>Prompt</span>
</label>
<div class="text_muted">The prompt to be sent.</div>
<textarea id="completion_prompt_manager_popup_entry_form_prompt" class="text_pole" name="prompt">
</textarea>
</div>
<div id="completion_prompt_manager_popup_entry_form_footer" >
<a id="completion_prompt_manager_popup_entry_form_close" title="close" class="fa-solid fa-close menu_button"></a>
<a id="completion_prompt_manager_popup_entry_form_reset" title="reset" class="fa-solid fa-undo menu_button"></a>
<a id="completion_prompt_manager_popup_entry_form_save" title="save" class="fa-solid fa-save menu_button" data-pm-prompt=""></a>
</div>
</form>
</div>
<div id="completion_prompt_manager_popup_entry_form_footer" >
<a id="completion_prompt_manager_popup_entry_form_close" title="close" class="fa-solid fa-close menu_button"></a>
<a id="completion_prompt_manager_popup_entry_form_reset" title="reset" class="fa-solid fa-undo menu_button"></a>
<a id="completion_prompt_manager_popup_entry_form_save" title="save" class="fa-solid fa-save menu_button" data-pm-prompt=""></a>
</div>
</form>
</div>
</div>
</div>
<div id="shadow_select_chat_popup">

View File

@ -128,14 +128,16 @@ function PromptManagerModule() {
this.containerElement = null;
this.listElement = null;
this.activeCharacter = null;
this.messages = null;
this.tokenHandler = null;
this.tokenCache = 0;
this.tokenUsage = 0;
this.error = null;
this.tryGenerate = () => { };
this.saveServiceSettings = () => { };
this.handleToggle = () => { };
this.handleInspect = () => { };
this.handleEdit = () => { };
this.handleDetach = () => { };
this.handleSavePrompt = () => { };
@ -172,12 +174,30 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
// Open edit form and load selected prompt
this.handleEdit = (event) => {
this.clearInspectForm();
this.clearEditForm();
const promptID = event.target.closest('.' + this.configuration.prefix + 'prompt_manager_prompt').dataset.pmIdentifier;
const prompt = this.getPromptById(promptID);
this.loadPromptIntoEditForm(prompt);
this.showEditForm();
this.showPopup();
}
// Open edit form and load selected prompt
this.handleInspect = (event) => {
this.clearInspectForm();
this.clearEditForm();
const promptID = event.target.closest('.' + this.configuration.prefix + 'prompt_manager_prompt').dataset.pmIdentifier;
if (true === this.messages.hasItemWithIdentifier(promptID)) {
const messages = this.messages.getItemByIdentifier(promptID);
this.loadMessagesIntoInspectForm(messages);
this.showPopup('inspect');
}
}
// Detach selected prompt from list form and close edit form
@ -187,7 +207,7 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
const prompt = this.getPromptById(promptID);
this.detachPrompt(prompt, this.activeCharacter);
this.hideEditForm();
this.hidePopup();
this.clearEditForm();
this.saveServiceSettings().then(() => this.render());
};
@ -207,7 +227,7 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
this.log('Saved prompt: ' + promptId);
this.hideEditForm();
this.hidePopup();
this.clearEditForm();
this.saveServiceSettings().then(() => this.render());
}
@ -253,7 +273,7 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
this.log('Deleted prompt: ' + prompt.identifier);
this.hideEditForm();
this.hidePopup();
this.clearEditForm();
this.saveServiceSettings().then(() => this.render());
}
@ -269,7 +289,7 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
}
this.loadPromptIntoEditForm(prompt);
this.showEditForm();
this.showPopup();
}
// Re-render when the character changes.
@ -321,10 +341,15 @@ PromptManagerModule.prototype.init = function (moduleConfiguration, serviceSetti
// Prepare prompt edit form buttons
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_save').addEventListener('click', this.handleSavePrompt);
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_reset').addEventListener('click', this.handleResetPrompt);
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_close').addEventListener('click', () => {
this.hideEditForm();
const closeAndClearPopup = () => {
this.hidePopup();
this.clearInspectForm();
this.clearEditForm();
});
};
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_close').addEventListener('click', closeAndClearPopup);
document.getElementById(this.configuration.prefix + 'prompt_manager_popup_close_button').addEventListener('click', closeAndClearPopup);
// Re-render Prompt manager on openai preset change
eventSource.on(event_types.OAI_PRESET_CHANGED, settings => this.render());
@ -494,6 +519,15 @@ PromptManagerModule.prototype.checkForMissingPrompts = function(prompts) {
});
};
/**
* Check whether a prompt is a marker.
* @param {object} prompt - The prompt to check.
* @returns {boolean} True if the prompt is a marker, false otherwise.
*/
PromptManagerModule.prototype.isStPromptMarker = function (prompt) {
return true === prompt.marker;
}
/**
* Check whether a prompt can be deleted. System prompts cannot be deleted.
* @param {object} prompt - The prompt to check.
@ -506,7 +540,7 @@ PromptManagerModule.prototype.isPromptDeletionAllowed = function (prompt) {
/**
* Check whether a prompt can be edited.
* @param {object} prompt - The prompt to check.
* @returns {boolean} True if the prompt can be deleted, false otherwise.
* @returns {boolean} True if the prompt can be edited, false otherwise.
*/
PromptManagerModule.prototype.isPromptEditAllowed = function (prompt) {
return true;
@ -710,10 +744,48 @@ PromptManagerModule.prototype.loadPromptIntoEditForm = function (prompt) {
savePromptButton.dataset.pmPrompt = prompt.identifier;
}
/**
* Loads a given prompt into the inspect form
* @param {MessageCollection} messages - Prompt object with properties 'name', 'role', 'content', and 'system_prompt'
*/
PromptManagerModule.prototype.loadMessagesIntoInspectForm = function (messages) {
if (!messages) return;
const createInlineDrawer = (title, content) => {
let drawerHTML = `
<div class="inline-drawer completion_prompt_manager_prompt">
<div class="inline-drawer-toggle inline-drawer-header">
<span>${title}</span>
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
</div>
<div class="inline-drawer-content">
${content}
</div>
</div>
`;
let template = document.createElement('template');
template.innerHTML = drawerHTML.trim();
return template.content.firstChild;
}
const messageList = document.getElementById('completion_prompt_manager_popup_entry_form_inspect_list');
if (0 === messages.getCollection().length) messageList.innerHTML = `<span>This marker does not contain any prompts.</span>`;
messages.getCollection().forEach(message => {
const truncatedTitle = message.content.length > 32 ? message.content.slice(0, 32) + '...' : message.content;
messageList.append(createInlineDrawer(message.title || truncatedTitle, message.content || 'No Content'));
});
}
/**
* Clears all input fields in the edit form.
*/
PromptManagerModule.prototype.clearEditForm = function () {
const editArea = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_edit');
editArea.style.display = 'none';
const nameField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_name');
const roleField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_role');
const promptField = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_entry_form_prompt');
@ -725,6 +797,13 @@ PromptManagerModule.prototype.clearEditForm = function () {
roleField.disabled = false;
}
PromptManagerModule.prototype.clearInspectForm = function() {
const inspectArea = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_inspect');
inspectArea.style.display = 'none';
const messageList = document.getElementById('completion_prompt_manager_popup_entry_form_inspect_list');
messageList.innerHTML = '';
}
/**
* Returns a full list of prompts whose content markers have been substituted.
* @returns {PromptCollection} A PromptCollection object
@ -743,16 +822,20 @@ PromptManagerModule.prototype.getPromptCollection = function () {
return promptCollection;
}
PromptManagerModule.prototype.setMessages = function (messages) {
this.messages = messages;
};
PromptManagerModule.prototype.populateTokenHandler = function(messageCollection) {
this.tokenHandler.resetCounts();
const counts = this.tokenHandler.getCounts();
messageCollection.getCollection().forEach((message) => {
messageCollection.getCollection().forEach(message => {
counts[message.identifier] = message.getTokens();
});
this.tokenCache = this.tokenHandler.getTotal();
this.tokenUsage = this.tokenHandler.getTotal();
this.log('Updated token cache with ' + this.tokenCache);
this.log('Updated token cache with ' + this.tokenUsage);
}
// Empties, then re-assembles the container containing the prompt list.
@ -769,7 +852,7 @@ PromptManagerModule.prototype.renderPromptManager = function () {
</div>
`;
const activeTokenInfo = `<span class="tooltip fa-solid fa-info-circle" title="Including tokens from hidden prompts"></span>`;
const totalActiveTokens = this.tokenCache;
const totalActiveTokens = this.tokenUsage;
promptManagerDiv.insertAdjacentHTML('beforeend', `
<div class="range-block-title" data-i18n="Prompts">
@ -862,7 +945,7 @@ PromptManagerModule.prototype.renderPromptManagerListItems = function () {
const markerClass = prompt.marker ? `${prefix}prompt_manager_marker` : '';
const tokens = this.tokenHandler?.getCounts()[prompt.identifier] ?? 0;
// Warn the user if the chat history goes under certain token thresholds.
// Warn the user if the chat history goes below certain token thresholds.
let warningClass = '';
let warningTitle = '';
@ -897,11 +980,20 @@ PromptManagerModule.prototype.renderPromptManagerListItems = function () {
`;
}
let inspectSpanHtml = '';
if (this.isStPromptMarker(prompt)) {
inspectSpanHtml = `
<span title="inspect" class="prompt-manager-inspect-action fa-solid fa-magnifying-glass"></span>
`;
}
let toggleSpanHtml = '';
if (this.isPromptToggleAllowed(prompt)) {
toggleSpanHtml = `
<span class="prompt-manager-toggle-action ${listEntry.enabled ? 'fa-solid fa-toggle-on' : 'fa-solid fa-toggle-off'}"></span>
`;
} else {
toggleSpanHtml = `<span class="fa-solid'"></span>`;
}
listItemHtml += `
@ -910,8 +1002,14 @@ PromptManagerModule.prototype.renderPromptManagerListItems = function () {
${prompt.marker ? '<span class="fa-solid fa-thumb-tack"></span>' : ''}
${prompt.name}
</span>
${prompt.marker ? '<span></span>' : `
<span>
${prompt.marker
? `<span>
<span class="prompt_manager_prompt_controls">
<span></span>
${inspectSpanHtml}
</span>
</span>`
: `<span>
<span class="prompt_manager_prompt_controls">
${detachSpanHtml}
${editSpanHtml}
@ -931,6 +1029,10 @@ PromptManagerModule.prototype.renderPromptManagerListItems = function () {
el.addEventListener('click', this.handleDetach);
});
Array.from(promptManagerList.getElementsByClassName('prompt-manager-inspect-action')).forEach(el => {
el.addEventListener('click', this.handleInspect);
});
Array.from(promptManagerList.getElementsByClassName('prompt-manager-edit-action')).forEach(el => {
el.addEventListener('click', this.handleEdit);
});
@ -977,7 +1079,10 @@ PromptManagerModule.prototype.makeDraggable = function () {
* Slides down the edit form and adds the class 'openDrawer' to the first element of '#openai_prompt_manager_popup'.
* @returns {void}
*/
PromptManagerModule.prototype.showEditForm = function () {
PromptManagerModule.prototype.showPopup = function (area = 'edit') {
const areaElement = document.getElementById(this.configuration.prefix + 'prompt_manager_popup_' + area);
areaElement.style.display = 'block';
$('#'+this.configuration.prefix +'prompt_manager_popup').first()
.slideDown(200, "swing")
.addClass('openDrawer');
@ -987,7 +1092,7 @@ PromptManagerModule.prototype.showEditForm = function () {
* Slides up the edit form and removes the class 'openDrawer' from the first element of '#openai_prompt_manager_popup'.
* @returns {void}
*/
PromptManagerModule.prototype.hideEditForm = function () {
PromptManagerModule.prototype.hidePopup = function () {
$('#'+this.configuration.prefix +'prompt_manager_popup').first()
.slideUp(200, "swing")
.removeClass('openDrawer');

View File

@ -672,7 +672,10 @@ function prepareOpenAIMessages({
chatCompletion.log(error);
}
} finally {
promptManager.populateTokenHandler(chatCompletion.getMessages());
const messages = chatCompletion.getMessages();
promptManager.populateTokenHandler(messages);
promptManager.setMessages(messages);
console.log(messages)
// All information are up-to-date, render without dry-run.
if (false === dryRun) promptManager.render(false);
@ -1289,6 +1292,14 @@ class MessageCollection {
this.identifier = identifier;
}
getChat() {
return this.collection.reduce((acc, message) => {
const name = message.name;
if (message.content) acc.push({role: message.role, ...(name && { name }), content: message.content});
return acc;
}, []);
}
getCollection() {
return this.collection;
}
@ -1297,6 +1308,14 @@ class MessageCollection {
this.collection.push(item);
}
getItemByIdentifier(identifier) {
return this.collection.find(item => item.identifier === identifier);
}
hasItemWithIdentifier(identifier) {
return this.collection.some(message => message.identifier === identifier);
}
getTokens() {
return this.collection.reduce((tokens, message) => tokens + message.getTokens(), 0);
}
@ -1373,7 +1392,7 @@ class ChatCompletion {
}
has(identifier) {
return this.messages.collection.some(message => message.identifier === identifier);
return this.messages.hasItemWithIdentifier(identifier);
}
getTotalTokenCount() {
@ -1384,12 +1403,9 @@ class ChatCompletion {
const chat = [];
for (let item of this.messages.collection) {
if (item instanceof MessageCollection) {
const messages = item.collection.reduce((acc, message) => {
const name = message.name;
if (message.content) acc.push({role: message.role, ...(name && { name }), content: message.content});
return acc;
}, []);
chat.push(...messages);
chat.push(...item.getChat());
} else {
chat.push(item);
}
}
return chat;