2023-12-02 19:04:51 +01:00
'use strict' ;
2023-08-20 16:28:22 +02:00
2024-09-23 21:44:49 +02:00
import { event _types , eventSource , is _send _press , main _api , substituteParams } from '../script.js' ;
2023-12-02 19:04:51 +01:00
import { is _group _generating } from './group-chats.js' ;
import { Message , TokenHandler } from './openai.js' ;
import { power _user } from './power-user.js' ;
2024-04-28 18:47:53 +02:00
import { debounce , waitUntilCondition , escapeHtml } from './utils.js' ;
import { debounce _timeout } from './constants.js' ;
2024-05-24 21:00:21 +02:00
import { renderTemplateAsync } from './templates.js' ;
2024-09-23 21:44:49 +02:00
import { Popup } from './popup.js' ;
2024-10-01 16:25:20 +02:00
import { t } from './i18n.js' ;
2023-06-14 22:36:14 +02:00
2023-08-15 01:20:42 +02:00
function debouncePromise ( func , delay ) {
let timeoutId ;
return ( ... args ) => {
clearTimeout ( timeoutId ) ;
return new Promise ( ( resolve ) => {
timeoutId = setTimeout ( ( ) => {
const result = func ( ... args ) ;
resolve ( result ) ;
} , delay ) ;
} ) ;
} ;
}
2023-10-11 15:03:36 +02:00
const DEFAULT _DEPTH = 4 ;
/ * *
* @ enum { number }
* /
2023-11-10 14:36:42 +01:00
export const INJECTION _POSITION = {
2023-10-11 15:03:36 +02:00
RELATIVE : 0 ,
ABSOLUTE : 1 ,
2023-12-02 20:11:06 +01:00
} ;
2023-10-11 15:03:36 +02:00
2023-07-17 20:14:56 +02:00
/ * *
2023-07-28 15:23:15 +02:00
* Register migrations for the prompt manager when settings are loaded or an Open AI preset is loaded .
2023-07-17 20:14:56 +02:00
* /
2023-08-10 19:56:18 +02:00
const registerPromptManagerMigration = ( ) => {
2023-08-12 18:17:06 +02:00
const migrate = ( settings , savePreset = null , presetName = null ) => {
2023-08-15 01:28:07 +02:00
if ( 'Default' === presetName ) return ;
2023-08-10 19:56:18 +02:00
if ( settings . main _prompt || settings . nsfw _prompt || settings . jailbreak _prompt ) {
console . log ( 'Running prompt manager configuration migration' ) ;
2023-08-12 18:17:06 +02:00
if ( settings . prompts === undefined || settings . prompts . length === 0 ) settings . prompts = structuredClone ( chatCompletionDefaultPrompts . prompts ) ;
2023-07-31 15:18:44 +02:00
const findPrompt = ( identifier ) => settings . prompts . find ( prompt => identifier === prompt . identifier ) ;
2023-08-10 19:56:18 +02:00
if ( settings . main _prompt ) {
2023-12-02 20:11:06 +01:00
findPrompt ( 'main' ) . content = settings . main _prompt ;
2023-06-29 19:26:35 +02:00
delete settings . main _prompt ;
}
2023-08-10 19:56:18 +02:00
if ( settings . nsfw _prompt ) {
2023-12-02 20:11:06 +01:00
findPrompt ( 'nsfw' ) . content = settings . nsfw _prompt ;
2023-06-29 19:26:35 +02:00
delete settings . nsfw _prompt ;
}
2023-08-10 19:56:18 +02:00
if ( settings . jailbreak _prompt ) {
2023-12-02 20:11:06 +01:00
findPrompt ( 'jailbreak' ) . content = settings . jailbreak _prompt ;
2023-06-29 19:26:35 +02:00
delete settings . jailbreak _prompt ;
}
2023-08-12 18:17:06 +02:00
if ( savePreset && presetName ) savePreset ( presetName , settings , false ) ;
2023-06-29 19:26:35 +02:00
}
2023-08-10 19:56:18 +02:00
} ;
2023-08-08 20:09:55 +02:00
2023-08-10 19:56:18 +02:00
eventSource . on ( event _types . SETTINGS _LOADED _BEFORE , settings => migrate ( settings ) ) ;
2023-09-01 22:23:03 +02:00
eventSource . on ( event _types . OAI _PRESET _CHANGED _BEFORE , event => migrate ( event . preset , event . savePreset , event . presetName ) ) ;
2023-12-02 20:11:06 +01:00
} ;
2023-06-29 19:26:35 +02:00
2023-07-09 15:57:56 +02:00
/ * *
* Represents a prompt .
* /
2023-06-14 22:36:14 +02:00
class Prompt {
2024-07-16 22:29:16 +02:00
identifier ; role ; content ; name ; system _prompt ; position ; injection _position ; injection _depth ; forbid _overrides ; extension ;
2023-07-09 15:57:56 +02:00
/ * *
* Create a new Prompt instance .
*
* @ param { Object } param0 - Object containing the properties of the prompt .
* @ param { string } param0 . identifier - The unique identifier of the prompt .
* @ param { string } param0 . role - The role associated with the prompt .
* @ param { string } param0 . content - The content of the prompt .
* @ param { string } param0 . name - The name of the prompt .
* @ param { boolean } param0 . system _prompt - Indicates if the prompt is a system prompt .
2023-09-21 19:57:28 +02:00
* @ param { string } param0 . position - The position of the prompt in the prompt list .
2023-10-11 15:03:36 +02:00
* @ param { number } param0 . injection _position - The insert position of the prompt .
* @ param { number } param0 . injection _depth - The depth of the prompt in the chat .
2024-03-24 00:28:35 +01:00
* @ param { boolean } param0 . forbid _overrides - Indicates if the prompt should not be overridden .
2024-07-16 22:29:16 +02:00
* @ param { boolean } param0 . extension - Prompt is added by an extension .
2023-07-09 15:57:56 +02:00
* /
2024-07-16 22:29:16 +02:00
constructor ( { identifier , role , content , name , system _prompt , position , injection _depth , injection _position , forbid _overrides , extension } = { } ) {
2023-06-14 22:36:14 +02:00
this . identifier = identifier ;
this . role = role ;
this . content = content ;
this . name = name ;
this . system _prompt = system _prompt ;
2023-09-21 19:57:28 +02:00
this . position = position ;
2023-10-11 15:03:36 +02:00
this . injection _depth = injection _depth ;
this . injection _position = injection _position ;
2024-03-24 00:28:35 +01:00
this . forbid _overrides = forbid _overrides ;
2024-07-16 22:29:16 +02:00
this . extension = extension ? ? false ;
2023-06-14 22:36:14 +02:00
}
}
2023-07-09 15:57:56 +02:00
/ * *
* Representing a collection of prompts .
* /
2024-04-17 20:23:06 +02:00
export class PromptCollection {
2023-06-14 22:36:14 +02:00
collection = [ ] ;
2024-03-24 01:19:10 +01:00
overriddenPrompts = [ ] ;
2023-06-14 22:36:14 +02:00
2023-07-09 15:57:56 +02:00
/ * *
* Create a new PromptCollection instance .
*
* @ param { ... Prompt } prompts - An array of Prompt instances .
* /
2023-07-02 21:50:37 +02:00
constructor ( ... prompts ) {
this . add ( ... prompts ) ;
2023-06-14 22:36:14 +02:00
}
2023-07-09 15:57:56 +02:00
/ * *
* Checks if the provided instances are of the Prompt class .
*
* @ param { ... any } prompts - Instances to check .
* @ throws Will throw an error if one or more instances are not of the Prompt class .
* /
2023-07-02 21:50:37 +02:00
checkPromptInstance ( ... prompts ) {
2023-08-19 20:54:30 +02:00
for ( let prompt of prompts ) {
if ( ! ( prompt instanceof Prompt ) ) {
2023-06-14 22:36:14 +02:00
throw new Error ( 'Only Prompt instances can be added to PromptCollection' ) ;
}
}
2023-07-02 21:50:37 +02:00
}
2023-06-14 22:36:14 +02:00
2023-07-09 15:57:56 +02:00
/ * *
* Adds new Prompt instances to the collection .
*
* @ param { ... Prompt } prompts - An array of Prompt instances .
* /
2023-07-02 21:50:37 +02:00
add ( ... prompts ) {
this . checkPromptInstance ( ... prompts ) ;
2023-06-14 22:36:14 +02:00
this . collection . push ( ... prompts ) ;
}
2023-07-09 15:57:56 +02:00
/ * *
* Sets a Prompt instance at a specific position in the collection .
*
* @ param { Prompt } prompt - The Prompt instance to set .
* @ param { number } position - The position in the collection to set the Prompt instance .
* /
2023-06-18 20:12:21 +02:00
set ( prompt , position ) {
2023-07-02 21:50:37 +02:00
this . checkPromptInstance ( prompt ) ;
2023-06-18 20:12:21 +02:00
this . collection [ position ] = prompt ;
}
2023-07-09 15:57:56 +02:00
/ * *
* Retrieves a Prompt instance from the collection by its identifier .
*
* @ param { string } identifier - The identifier of the Prompt instance to retrieve .
* @ returns { Prompt } The Prompt instance with the provided identifier , or undefined if not found .
* /
2023-06-14 22:36:14 +02:00
get ( identifier ) {
2023-07-02 21:50:37 +02:00
return this . collection . find ( prompt => prompt . identifier === identifier ) ;
2023-06-14 22:36:14 +02:00
}
2023-07-09 15:57:56 +02:00
/ * *
* Retrieves the index of a Prompt instance in the collection by its identifier .
*
2024-04-17 20:23:06 +02:00
* @ param { string } identifier - The identifier of the Prompt instance to find .
2023-07-09 15:57:56 +02:00
* @ returns { number } The index of the Prompt instance in the collection , or - 1 if not found .
* /
2023-07-02 21:50:37 +02:00
index ( identifier ) {
2023-06-14 22:36:14 +02:00
return this . collection . findIndex ( prompt => prompt . identifier === identifier ) ;
}
2023-07-09 15:57:56 +02:00
/ * *
* Checks if a Prompt instance exists in the collection by its identifier .
*
* @ param { string } identifier - The identifier of the Prompt instance to check .
* @ returns { boolean } true if the Prompt instance exists in the collection , false otherwise .
* /
2023-06-14 22:36:14 +02:00
has ( identifier ) {
2023-07-02 21:50:37 +02:00
return this . index ( identifier ) !== - 1 ;
2023-06-14 22:36:14 +02:00
}
2024-03-24 01:19:10 +01:00
override ( prompt , position ) {
this . set ( prompt , position ) ;
this . overriddenPrompts . push ( prompt . identifier ) ;
}
2023-06-14 22:36:14 +02:00
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:14:56 +01:00
class PromptManager {
2023-12-03 18:13:53 +01:00
constructor ( ) {
this . systemPrompts = [
'main' ,
'nsfw' ,
'jailbreak' ,
'enhanceDefinitions' ,
] ;
2024-03-24 00:28:35 +01:00
this . overridablePrompts = [
'main' ,
'jailbreak' ,
] ;
2024-03-24 01:19:10 +01:00
this . overriddenPrompts = [ ] ;
2023-12-03 18:13:53 +01:00
this . configuration = {
version : 1 ,
prefix : '' ,
containerIdentifier : '' ,
listIdentifier : '' ,
listItemTemplateIdentifier : '' ,
toggleDisabled : [ ] ,
promptOrder : {
strategy : 'global' ,
dummyId : 100000 ,
} ,
sortableDelay : 30 ,
warningTokenThreshold : 1500 ,
dangerTokenThreshold : 500 ,
defaultPrompts : {
main : '' ,
nsfw : '' ,
jailbreak : '' ,
enhanceDefinitions : '' ,
} ,
} ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
// Chatcompletion configuration object
this . serviceSettings = null ;
2023-06-18 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
// DOM element containing the prompt manager
this . containerElement = null ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
// DOM element containing the prompt list
this . listElement = null ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
// Currently selected character
this . activeCharacter = null ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
// Message collection of the most recent chatcompletion
this . messages = null ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
// The current token handler instance
this . tokenHandler = null ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
// Token usage of last dry run
this . tokenUsage = 0 ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
// Error state, contains error message.
this . error = null ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
/** Dry-run for generate, must return a promise */
2024-05-24 21:00:21 +02:00
this . tryGenerate = async ( ) => { } ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
/** Called to persist the configuration, must return a promise */
this . saveServiceSettings = ( ) => { } ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
/** Toggle prompt button click */
this . handleToggle = ( ) => { } ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
/** Prompt name click */
this . handleInspect = ( ) => { } ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
/** Edit prompt button click */
this . handleEdit = ( ) => { } ;
2023-07-27 19:38:12 +02:00
2023-12-03 18:13:53 +01:00
/** Detach prompt button click */
this . handleDetach = ( ) => { } ;
2023-08-13 17:43:17 +02:00
2023-12-03 18:13:53 +01:00
/** Save prompt button click */
this . handleSavePrompt = ( ) => { } ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/** Reset prompt button click */
this . handleResetPrompt = ( ) => { } ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/** New prompt button click */
this . handleNewPrompt = ( ) => { } ;
2023-08-20 16:28:42 +02:00
2023-12-03 18:13:53 +01:00
/** Delete prompt button click */
this . handleDeletePrompt = ( ) => { } ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/** Append prompt button click */
this . handleAppendPrompt = ( ) => { } ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/** Import button click */
this . handleImport = ( ) => { } ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/** Full export click */
this . handleFullExport = ( ) => { } ;
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/** Character export click */
this . handleCharacterExport = ( ) => { } ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/** Character reset button click*/
this . handleCharacterReset = ( ) => { } ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/** Debounced version of render */
2024-04-28 06:21:47 +02:00
this . renderDebounced = debounce ( this . render . bind ( this ) , debounce _timeout . relaxed ) ;
2023-12-03 18:13:53 +01:00
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/ * *
2023-12-03 18:14:56 +01:00
* Initializes the PromptManager with provided configuration and service settings .
2023-12-03 18:13:53 +01:00
*
* Sets up various handlers for user interactions , event listeners and initial rendering of prompts .
* It is also responsible for preparing prompt edit form buttons , managing popup form close and clear actions .
*
2023-12-03 18:14:56 +01:00
* @ param { Object } moduleConfiguration - Configuration object for the PromptManager .
* @ param { Object } serviceSettings - Service settings object for the PromptManager .
2023-12-03 18:13:53 +01:00
* /
init ( moduleConfiguration , serviceSettings ) {
this . configuration = Object . assign ( this . configuration , moduleConfiguration ) ;
2024-10-12 00:07:36 +02:00
this . tokenHandler = this . tokenHandler || new TokenHandler ( ( ) => { throw new Error ( 'Token handler not set' ) ; } ) ;
2023-12-03 18:13:53 +01:00
this . serviceSettings = serviceSettings ;
this . containerElement = document . getElementById ( this . configuration . containerIdentifier ) ;
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
if ( 'global' === this . configuration . promptOrder . strategy ) this . activeCharacter = { id : this . configuration . promptOrder . dummyId } ;
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
this . sanitizeServiceSettings ( ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Enable and disable prompts
this . handleToggle = ( event ) => {
const promptID = event . target . closest ( '.' + this . configuration . prefix + 'prompt_manager_prompt' ) . dataset . pmIdentifier ;
const promptOrderEntry = this . getPromptOrderEntry ( this . activeCharacter , promptID ) ;
const counts = this . tokenHandler . getCounts ( ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
counts [ promptID ] = null ;
promptOrderEntry . enabled = ! promptOrderEntry . enabled ;
2024-03-21 18:59:19 +01:00
this . render ( ) ;
this . saveServiceSettings ( ) ;
2023-12-03 18:13:53 +01:00
} ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Open edit form and load selected prompt
this . handleEdit = ( event ) => {
this . clearEditForm ( ) ;
this . clearInspectForm ( ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
const promptID = event . target . closest ( '.' + this . configuration . prefix + 'prompt_manager_prompt' ) . dataset . pmIdentifier ;
const prompt = this . getPromptById ( promptID ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
this . loadPromptIntoEditForm ( prompt ) ;
2023-07-31 17:51:32 +02:00
2023-12-03 18:13:53 +01:00
this . showPopup ( ) ;
} ;
2023-07-01 20:01:31 +02:00
2023-12-03 18:13:53 +01:00
// Open edit form and load selected prompt
this . handleInspect = ( event ) => {
this . clearEditForm ( ) ;
this . clearInspectForm ( ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
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 ) ;
2023-06-27 19:44:10 +02:00
2023-12-03 18:13:53 +01:00
this . loadMessagesIntoInspectForm ( messages ) ;
2023-11-14 21:41:47 +01:00
2023-12-03 18:13:53 +01:00
this . showPopup ( 'inspect' ) ;
}
} ;
2023-06-27 19:44:10 +02:00
2023-12-03 18:13:53 +01:00
// Detach selected prompt from list form and close edit form
this . handleDetach = ( event ) => {
if ( null === this . activeCharacter ) return ;
const promptID = event . target . closest ( '.' + this . configuration . prefix + 'prompt_manager_prompt' ) . dataset . pmIdentifier ;
const prompt = this . getPromptById ( promptID ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
this . detachPrompt ( prompt , this . activeCharacter ) ;
this . hidePopup ( ) ;
this . clearEditForm ( ) ;
2024-03-21 18:59:19 +01:00
this . render ( ) ;
this . saveServiceSettings ( ) ;
2023-12-03 18:13:53 +01:00
} ;
// Save prompt edit form to settings and close form.
this . handleSavePrompt = ( event ) => {
const promptId = event . target . dataset . pmPrompt ;
const prompt = this . getPromptById ( promptId ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
if ( null === prompt ) {
const newPrompt = { } ;
this . updatePromptWithPromptEditForm ( newPrompt ) ;
this . addPrompt ( newPrompt , promptId ) ;
} else {
this . updatePromptWithPromptEditForm ( prompt ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
if ( 'main' === promptId ) this . updateQuickEdit ( 'main' , prompt ) ;
if ( 'nsfw' === promptId ) this . updateQuickEdit ( 'nsfw' , prompt ) ;
if ( 'jailbreak' === promptId ) this . updateQuickEdit ( 'jailbreak' , prompt ) ;
2023-07-01 20:01:31 +02:00
2023-12-03 18:13:53 +01:00
this . log ( 'Saved prompt: ' + promptId ) ;
2023-07-01 20:01:31 +02:00
2023-07-02 21:34:46 +02:00
this . hidePopup ( ) ;
2023-05-28 15:52:45 +02:00
this . clearEditForm ( ) ;
2024-03-21 18:59:19 +01:00
this . render ( ) ;
this . saveServiceSettings ( ) ;
2023-12-02 20:11:06 +01:00
} ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Reset prompt should it be a system prompt
this . handleResetPrompt = ( event ) => {
const promptId = event . target . dataset . pmPrompt ;
const prompt = this . getPromptById ( promptId ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
switch ( promptId ) {
case 'main' :
prompt . name = 'Main Prompt' ;
prompt . content = this . configuration . defaultPrompts . main ;
2024-03-24 00:28:35 +01:00
prompt . forbid _overrides = false ;
2023-12-03 18:13:53 +01:00
break ;
case 'nsfw' :
prompt . name = 'Nsfw Prompt' ;
prompt . content = this . configuration . defaultPrompts . nsfw ;
break ;
case 'jailbreak' :
prompt . name = 'Jailbreak Prompt' ;
prompt . content = this . configuration . defaultPrompts . jailbreak ;
2024-03-24 00:28:35 +01:00
prompt . forbid _overrides = false ;
2023-12-03 18:13:53 +01:00
break ;
case 'enhanceDefinitions' :
prompt . name = 'Enhance Definitions' ;
prompt . content = this . configuration . defaultPrompts . enhanceDefinitions ;
break ;
}
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_name' ) . value = prompt . name ;
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_role' ) . value = 'system' ;
2024-09-18 20:51:10 +02:00
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_prompt' ) . value = prompt . content ? ? '' ;
2023-12-03 18:13:53 +01:00
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_position' ) . value = prompt . injection _position ? ? 0 ;
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_depth' ) . value = prompt . injection _depth ? ? DEFAULT _DEPTH ;
document . getElementById ( this . configuration . prefix + 'prompt_manager_depth_block' ) . style . visibility = prompt . injection _position === INJECTION _POSITION . ABSOLUTE ? 'visible' : 'hidden' ;
2024-03-24 00:28:35 +01:00
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_forbid_overrides' ) . checked = prompt . forbid _overrides ? ? false ;
document . getElementById ( this . configuration . prefix + 'prompt_manager_forbid_overrides_block' ) . style . visibility = this . overridablePrompts . includes ( prompt . identifier ) ? 'visible' : 'hidden' ;
2024-09-18 20:51:10 +02:00
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_prompt' ) . disabled = prompt . marker ? ? false ;
2023-08-15 19:41:34 +02:00
2023-12-03 18:13:53 +01:00
if ( ! this . systemPrompts . includes ( promptId ) ) {
document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_position' ) . removeAttribute ( 'disabled' ) ;
}
2023-12-02 20:11:06 +01:00
} ;
2023-08-15 19:41:34 +02:00
2023-12-03 18:13:53 +01:00
// Append prompt to selected character
this . handleAppendPrompt = ( event ) => {
const promptID = document . getElementById ( this . configuration . prefix + 'prompt_manager_footer_append_prompt' ) . value ;
const prompt = this . getPromptById ( promptID ) ;
if ( prompt ) {
this . appendPrompt ( prompt , this . activeCharacter ) ;
2024-03-21 18:59:19 +01:00
this . render ( ) ;
this . saveServiceSettings ( ) ;
2023-12-03 18:13:53 +01:00
}
} ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
// Delete selected prompt from list form and close edit form
2024-09-23 21:44:49 +02:00
this . handleDeletePrompt = async ( event ) => {
2024-10-01 16:25:20 +02:00
Popup . show . confirm ( t ` Are you sure you want to delete this prompt? ` , null ) . then ( ( userChoice ) => {
2024-09-23 21:44:49 +02:00
if ( ! userChoice ) return ;
const promptID = document . getElementById ( this . configuration . prefix + 'prompt_manager_footer_append_prompt' ) . value ;
const prompt = this . getPromptById ( promptID ) ;
2023-07-05 20:12:21 +02:00
2024-09-23 21:44:49 +02:00
if ( prompt && true === this . isPromptDeletionAllowed ( prompt ) ) {
const promptIndex = this . getPromptIndexById ( promptID ) ;
this . serviceSettings . prompts . splice ( Number ( promptIndex ) , 1 ) ;
2023-07-05 20:12:21 +02:00
2024-09-23 21:44:49 +02:00
this . log ( 'Deleted prompt: ' + prompt . identifier ) ;
2023-12-03 18:13:53 +01:00
2024-09-23 21:44:49 +02:00
this . hidePopup ( ) ;
this . clearEditForm ( ) ;
this . render ( ) ;
this . saveServiceSettings ( ) ;
}
} ) ;
2023-12-02 20:11:06 +01:00
} ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
// Create new prompt, then save it to settings and close form.
this . handleNewPrompt = ( event ) => {
const prompt = {
identifier : this . getUuidv4 ( ) ,
name : '' ,
role : 'system' ,
content : '' ,
} ;
this . loadPromptIntoEditForm ( prompt ) ;
this . showPopup ( ) ;
} ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
// Export all user prompts
this . handleFullExport = ( ) => {
const prompts = this . serviceSettings . prompts . reduce ( ( userPrompts , prompt ) => {
if ( false === prompt . system _prompt && false === prompt . marker ) userPrompts . push ( prompt ) ;
return userPrompts ;
} , [ ] ) ;
let promptOrder = [ ] ;
if ( 'global' === this . configuration . promptOrder . strategy ) {
promptOrder = this . getPromptOrderForCharacter ( { id : this . configuration . promptOrder . dummyId } ) ;
} else if ( 'character' === this . configuration . promptOrder . strategy ) {
promptOrder = [ ] ;
} else {
throw new Error ( 'Prompt order strategy not supported.' ) ;
}
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
const exportPrompts = {
prompts : prompts ,
prompt _order : promptOrder ,
} ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
this . export ( exportPrompts , 'full' , 'st-prompts' ) ;
} ;
2023-07-06 21:20:18 +02:00
2023-12-03 18:13:53 +01:00
// Export user prompts and order for this character
this . handleCharacterExport = ( ) => {
const characterPrompts = this . getPromptsForCharacter ( this . activeCharacter ) . reduce ( ( userPrompts , prompt ) => {
if ( false === prompt . system _prompt && ! prompt . marker ) userPrompts . push ( prompt ) ;
return userPrompts ;
} , [ ] ) ;
2023-07-06 21:20:18 +02:00
2023-12-03 18:13:53 +01:00
const characterList = this . getPromptOrderForCharacter ( this . activeCharacter ) ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
const exportPrompts = {
prompts : characterPrompts ,
prompt _order : characterList ,
} ;
2023-07-06 21:20:18 +02:00
2023-12-03 18:13:53 +01:00
const name = this . activeCharacter . name + '-prompts' ;
this . export ( exportPrompts , 'character' , name ) ;
} ;
2023-07-06 21:20:18 +02:00
2023-12-03 18:13:53 +01:00
// Import prompts for the selected character
this . handleImport = ( ) => {
2024-10-01 16:25:20 +02:00
Popup . show . confirm ( t ` Existing prompts with the same ID will be overridden. Do you want to proceed? ` , null )
2023-12-03 18:13:53 +01:00
. then ( userChoice => {
2024-09-23 21:44:49 +02:00
if ( ! userChoice ) return ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
const fileOpener = document . createElement ( 'input' ) ;
fileOpener . type = 'file' ;
fileOpener . accept = '.json' ;
2023-07-15 17:18:55 +02:00
2023-12-03 18:13:53 +01:00
fileOpener . addEventListener ( 'change' , ( event ) => {
const file = event . target . files [ 0 ] ;
if ( ! file ) return ;
2023-07-15 17:18:55 +02:00
2023-12-03 18:13:53 +01:00
const reader = new FileReader ( ) ;
2023-07-15 17:18:55 +02:00
2023-12-03 18:13:53 +01:00
reader . onload = ( event ) => {
const fileContent = event . target . result ;
2023-08-22 20:37:37 +02:00
2023-12-03 18:13:53 +01:00
try {
const data = JSON . parse ( fileContent ) ;
this . import ( data ) ;
} catch ( err ) {
2024-10-01 16:25:20 +02:00
toastr . error ( t ` An error occurred while importing prompts. More info available in console. ` ) ;
2023-12-03 18:13:53 +01:00
console . log ( 'An error occurred while importing prompts' ) ;
console . log ( err . toString ( ) ) ;
}
} ;
2023-08-22 20:37:37 +02:00
2023-12-03 18:13:53 +01:00
reader . readAsText ( file ) ;
} ) ;
2023-08-22 20:37:37 +02:00
2023-12-03 18:13:53 +01:00
fileOpener . click ( ) ;
} ) ;
2023-08-22 20:37:37 +02:00
} ;
2023-12-03 18:13:53 +01:00
// Restore default state of a characters prompt order
this . handleCharacterReset = ( ) => {
2024-10-01 16:25:20 +02:00
Popup . show . confirm ( t ` This will reset the prompt order for this character. You will not lose any prompts. ` , null )
2023-12-03 18:13:53 +01:00
. then ( userChoice => {
2024-09-23 21:44:49 +02:00
if ( ! userChoice ) return ;
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
this . removePromptOrderForCharacter ( this . activeCharacter ) ;
this . addPromptOrderForCharacter ( this . activeCharacter , promptManagerDefaultPromptOrder ) ;
2023-06-29 19:26:35 +02:00
2024-03-21 18:59:19 +01:00
this . render ( ) ;
this . saveServiceSettings ( ) ;
2023-12-03 18:13:53 +01:00
} ) ;
} ;
2023-08-22 20:49:54 +02:00
2023-12-03 18:13:53 +01:00
// Fill quick edit fields for the first time
if ( 'global' === this . configuration . promptOrder . strategy ) {
const handleQuickEditSave = ( event ) => {
const promptId = event . target . dataset . pmPrompt ;
const prompt = this . getPromptById ( promptId ) ;
2023-08-22 20:49:54 +02:00
2023-12-03 18:13:53 +01:00
prompt . content = event . target . value ;
2023-08-29 20:16:13 +02:00
2023-12-03 18:13:53 +01:00
// Update edit form if present
// @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
const popupEditFormPrompt = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_prompt' ) ;
if ( popupEditFormPrompt . offsetParent ) {
popupEditFormPrompt . value = prompt . content ;
}
2023-08-01 18:21:27 +02:00
2023-12-03 18:13:53 +01:00
this . log ( 'Saved prompt: ' + promptId ) ;
this . saveServiceSettings ( ) . then ( ( ) => this . render ( ) ) ;
} ;
2023-07-03 19:06:18 +02:00
2023-12-03 18:13:53 +01:00
const mainPrompt = this . getPromptById ( 'main' ) ;
const mainElementId = this . updateQuickEdit ( 'main' , mainPrompt ) ;
document . getElementById ( mainElementId ) . addEventListener ( 'blur' , handleQuickEditSave ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
const nsfwPrompt = this . getPromptById ( 'nsfw' ) ;
const nsfwElementId = this . updateQuickEdit ( 'nsfw' , nsfwPrompt ) ;
document . getElementById ( nsfwElementId ) . addEventListener ( 'blur' , handleQuickEditSave ) ;
const jailbreakPrompt = this . getPromptById ( 'jailbreak' ) ;
const jailbreakElementId = this . updateQuickEdit ( 'jailbreak' , jailbreakPrompt ) ;
document . getElementById ( jailbreakElementId ) . addEventListener ( 'blur' , handleQuickEditSave ) ;
2023-08-14 23:01:55 +02:00
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Re-render when chat history changes.
eventSource . on ( event _types . MESSAGE _DELETED , ( ) => this . renderDebounced ( ) ) ;
eventSource . on ( event _types . MESSAGE _EDITED , ( ) => this . renderDebounced ( ) ) ;
eventSource . on ( event _types . MESSAGE _RECEIVED , ( ) => this . renderDebounced ( ) ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Re-render when chatcompletion settings change
eventSource . on ( event _types . CHATCOMPLETION _SOURCE _CHANGED , ( ) => this . renderDebounced ( ) ) ;
2023-06-01 18:51:30 +02:00
2023-12-03 18:13:53 +01:00
eventSource . on ( event _types . CHATCOMPLETION _MODEL _CHANGED , ( ) => this . renderDebounced ( ) ) ;
2023-06-01 18:51:30 +02:00
2023-12-03 18:13:53 +01:00
// Re-render when the character changes.
eventSource . on ( 'chatLoaded' , ( event ) => {
this . handleCharacterSelected ( event ) ;
this . saveServiceSettings ( ) . then ( ( ) => this . renderDebounced ( ) ) ;
} ) ;
2023-06-10 18:13:59 +02:00
2023-12-03 18:13:53 +01:00
// Re-render when the character gets edited.
eventSource . on ( event _types . CHARACTER _EDITED , ( event ) => {
this . handleCharacterUpdated ( event ) ;
this . saveServiceSettings ( ) . then ( ( ) => this . renderDebounced ( ) ) ;
} ) ;
2023-11-14 21:27:07 +01:00
2023-12-03 18:13:53 +01:00
// Re-render when the group changes.
eventSource . on ( 'groupSelected' , ( event ) => {
this . handleGroupSelected ( event ) ;
this . saveServiceSettings ( ) . then ( ( ) => this . renderDebounced ( ) ) ;
} ) ;
2023-06-03 21:25:21 +02:00
2023-12-03 18:13:53 +01:00
// Sanitize settings after character has been deleted.
2024-03-17 18:50:44 +01:00
eventSource . on ( event _types . CHARACTER _DELETED , ( event ) => {
2023-12-03 18:13:53 +01:00
this . handleCharacterDeleted ( event ) ;
this . saveServiceSettings ( ) . then ( ( ) => this . renderDebounced ( ) ) ;
} ) ;
2023-06-04 17:12:27 +02:00
2023-12-03 18:13:53 +01:00
// Trigger re-render when token settings are changed
document . getElementById ( 'openai_max_context' ) . addEventListener ( 'change' , ( event ) => {
this . serviceSettings . openai _max _context = event . target . value ;
if ( this . activeCharacter ) this . renderDebounced ( ) ;
} ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
document . getElementById ( 'openai_max_tokens' ) . addEventListener ( 'change' , ( event ) => {
if ( this . activeCharacter ) this . renderDebounced ( ) ;
} ) ;
2023-06-30 20:30:09 +02:00
2023-12-03 18:13:53 +01:00
// 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 ) ;
2023-06-30 20:30:09 +02:00
2023-12-03 18:13:53 +01:00
const closeAndClearPopup = ( ) => {
this . hidePopup ( ) ;
this . clearEditForm ( ) ;
this . clearInspectForm ( ) ;
} ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Clear forms on closing the popup
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 ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Re-render prompt manager on openai preset change
eventSource . on ( event _types . OAI _PRESET _CHANGED _AFTER , ( ) => {
this . sanitizeServiceSettings ( ) ;
const mainPrompt = this . getPromptById ( 'main' ) ;
this . updateQuickEdit ( 'main' , mainPrompt ) ;
2023-06-07 17:20:11 +02:00
2023-12-03 18:13:53 +01:00
const nsfwPrompt = this . getPromptById ( 'nsfw' ) ;
this . updateQuickEdit ( 'nsfw' , nsfwPrompt ) ;
2023-08-15 19:41:34 +02:00
2023-12-03 18:13:53 +01:00
const jailbreakPrompt = this . getPromptById ( 'jailbreak' ) ;
this . updateQuickEdit ( 'jailbreak' , jailbreakPrompt ) ;
2023-08-15 19:41:34 +02:00
2023-12-03 18:13:53 +01:00
this . hidePopup ( ) ;
this . clearEditForm ( ) ;
this . renderDebounced ( ) ;
} ) ;
2023-06-07 17:12:47 +02:00
2023-12-03 18:13:53 +01:00
// Re-render prompt manager on world settings update
eventSource . on ( event _types . WORLDINFO _SETTINGS _UPDATED , ( ) => this . renderDebounced ( ) ) ;
2023-07-01 20:01:31 +02:00
2023-12-03 18:13:53 +01:00
this . log ( 'Initialized' ) ;
2023-07-01 20:01:31 +02:00
}
2023-05-28 15:52:45 +02:00
2024-06-11 23:26:31 +02:00
/ * *
* Get the scroll position of the prompt manager
* @ returns { number } - Scroll position of the prompt manager
* /
# getScrollPosition ( ) {
return document . getElementById ( this . configuration . prefix + 'prompt_manager' ) ? . closest ( '.scrollableInner' ) ? . scrollTop ;
}
/ * *
* Set the scroll position of the prompt manager
* @ param { number } scrollPosition - The scroll position to set
* /
# setScrollPosition ( scrollPosition ) {
if ( scrollPosition === undefined || scrollPosition === null ) return ;
document . getElementById ( this . configuration . prefix + 'prompt_manager' ) ? . closest ( '.scrollableInner' ) ? . scrollTo ( 0 , scrollPosition ) ;
}
2023-12-03 18:13:53 +01:00
/ * *
* Main rendering function
*
* @ param afterTryGenerate - Whether a dry run should be attempted before rendering
* /
render ( afterTryGenerate = true ) {
if ( main _api !== 'openai' ) return ;
if ( 'character' === this . configuration . promptOrder . strategy && null === this . activeCharacter ) return ;
this . error = null ;
2024-05-24 21:00:21 +02:00
waitUntilCondition ( ( ) => ! is _send _press && ! is _group _generating , 1024 * 1024 , 100 ) . then ( async ( ) => {
2023-12-03 18:13:53 +01:00
if ( true === afterTryGenerate ) {
// Executed during dry-run for determining context composition
this . profileStart ( 'filling context' ) ;
2024-05-24 21:00:21 +02:00
this . tryGenerate ( ) . finally ( async ( ) => {
2023-12-03 18:13:53 +01:00
this . profileEnd ( 'filling context' ) ;
this . profileStart ( 'render' ) ;
2024-06-11 23:26:31 +02:00
const scrollPosition = this . # getScrollPosition ( ) ;
2024-05-24 21:00:21 +02:00
await this . renderPromptManager ( ) ;
await this . renderPromptManagerListItems ( ) ;
2023-12-03 18:13:53 +01:00
this . makeDraggable ( ) ;
2024-06-11 23:26:31 +02:00
this . # setScrollPosition ( scrollPosition ) ;
2023-12-03 18:13:53 +01:00
this . profileEnd ( 'render' ) ;
} ) ;
} else {
// Executed during live communication
this . profileStart ( 'render' ) ;
2024-06-11 23:26:31 +02:00
const scrollPosition = this . # getScrollPosition ( ) ;
2024-05-24 21:00:21 +02:00
await this . renderPromptManager ( ) ;
await this . renderPromptManagerListItems ( ) ;
2023-12-03 18:13:53 +01:00
this . makeDraggable ( ) ;
2024-06-11 23:26:31 +02:00
this . # setScrollPosition ( scrollPosition ) ;
2023-12-03 18:13:53 +01:00
this . profileEnd ( 'render' ) ;
}
} ) . catch ( ( ) => {
console . log ( 'Timeout while waiting for send press to be false' ) ;
} ) ;
}
2023-06-29 19:26:35 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Update a prompt with the values from the HTML form .
* @ param { object } prompt - The prompt to be updated .
* @ returns { void }
* /
updatePromptWithPromptEditForm ( prompt ) {
prompt . name = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_name' ) . value ;
prompt . role = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_role' ) . value ;
prompt . content = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_prompt' ) . value ;
prompt . injection _position = Number ( document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_position' ) . value ) ;
prompt . injection _depth = Number ( document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_depth' ) . value ) ;
2024-03-24 00:28:35 +01:00
prompt . forbid _overrides = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_forbid_overrides' ) . checked ;
2023-12-03 18:13:53 +01:00
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Find a prompt by its identifier and update it with the provided object .
* @ param { string } identifier - The identifier of the prompt .
* @ param { object } updatePrompt - An object with properties to be updated in the prompt .
* @ returns { void }
* /
updatePromptByIdentifier ( identifier , updatePrompt ) {
let prompt = this . serviceSettings . prompts . find ( ( item ) => identifier === item . identifier ) ;
if ( prompt ) prompt = Object . assign ( prompt , updatePrompt ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Iterate over an array of prompts , find each one by its identifier , and update them with the provided data .
* @ param { object [ ] } prompts - An array of prompt updates .
* @ returns { void }
* /
updatePrompts ( prompts ) {
prompts . forEach ( ( update ) => {
let prompt = this . getPromptById ( update . identifier ) ;
if ( prompt ) Object . assign ( prompt , update ) ;
} ) ;
}
2023-06-21 19:42:12 +02:00
2023-12-03 18:13:53 +01:00
getTokenHandler ( ) {
return this . tokenHandler ;
}
2023-06-21 19:42:12 +02:00
2023-12-03 18:13:53 +01:00
isPromptDisabledForActiveCharacter ( identifier ) {
const promptOrderEntry = this . getPromptOrderEntry ( this . activeCharacter , identifier ) ;
if ( promptOrderEntry ) return ! promptOrderEntry . enabled ;
return false ;
}
2023-06-03 20:52:33 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Add a prompt to the current character ' s prompt list .
* @ param { object } prompt - The prompt to be added .
* @ param { object } character - The character whose prompt list will be updated .
* @ returns { void }
* /
appendPrompt ( prompt , character ) {
const promptOrder = this . getPromptOrderForCharacter ( character ) ;
const index = promptOrder . findIndex ( entry => entry . identifier === prompt . identifier ) ;
2023-06-03 17:39:14 +02:00
2024-04-27 22:16:44 +02:00
if ( - 1 === index ) promptOrder . unshift ( { identifier : prompt . identifier , enabled : false } ) ;
2023-08-15 19:41:34 +02:00
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Remove a prompt from the current character ' s prompt list .
* @ param { object } prompt - The prompt to be removed .
* @ param { object } character - The character whose prompt list will be updated .
* @ returns { void }
* /
// Remove a prompt from the current characters prompt list
detachPrompt ( prompt , character ) {
const promptOrder = this . getPromptOrderForCharacter ( character ) ;
const index = promptOrder . findIndex ( entry => entry . identifier === prompt . identifier ) ;
if ( - 1 === index ) return ;
promptOrder . splice ( index , 1 ) ;
2023-08-15 19:41:34 +02:00
}
2023-07-07 22:45:53 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Create a new prompt and add it to the list of prompts .
* @ param { object } prompt - The prompt to be added .
* @ param { string } identifier - The identifier for the new prompt .
* @ returns { void }
* /
addPrompt ( prompt , identifier ) {
2023-06-04 17:12:27 +02:00
2023-12-03 18:13:53 +01:00
if ( typeof prompt !== 'object' || prompt === null ) throw new Error ( 'Object is not a prompt' ) ;
2023-06-06 18:04:27 +02:00
2023-12-03 18:13:53 +01:00
const newPrompt = {
identifier : identifier ,
system _prompt : false ,
enabled : false ,
marker : false ,
... prompt ,
} ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
this . serviceSettings . prompts . push ( newPrompt ) ;
}
2023-06-03 17:39:14 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Sanitize the service settings , ensuring each prompt has a unique identifier .
* @ returns { void }
* /
sanitizeServiceSettings ( ) {
this . serviceSettings . prompts = this . serviceSettings . prompts ? ? [ ] ;
this . serviceSettings . prompt _order = this . serviceSettings . prompt _order ? ? [ ] ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
if ( 'global' === this . configuration . promptOrder . strategy ) {
const dummyCharacter = { id : this . configuration . promptOrder . dummyId } ;
const promptOrder = this . getPromptOrderForCharacter ( dummyCharacter ) ;
2023-06-03 18:11:50 +02:00
2023-12-03 18:13:53 +01:00
if ( 0 === promptOrder . length ) this . addPromptOrderForCharacter ( dummyCharacter , promptManagerDefaultPromptOrder ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
// Check whether the referenced prompts are present.
this . serviceSettings . prompts . length === 0
? this . setPrompts ( chatCompletionDefaultPrompts . prompts )
: this . checkForMissingPrompts ( this . serviceSettings . prompts ) ;
// Add identifiers if there are none assigned to a prompt
this . serviceSettings . prompts . forEach ( prompt => prompt && ( prompt . identifier = prompt . identifier ? ? this . getUuidv4 ( ) ) ) ;
if ( this . activeCharacter ) {
const promptReferences = this . getPromptOrderForCharacter ( this . activeCharacter ) ;
for ( let i = promptReferences . length - 1 ; i >= 0 ; i -- ) {
const reference = promptReferences [ i ] ;
2024-04-19 00:46:15 +02:00
if ( reference && - 1 === this . serviceSettings . prompts . findIndex ( prompt => prompt . identifier === reference . identifier ) ) {
2023-12-03 18:13:53 +01:00
promptReferences . splice ( i , 1 ) ;
this . log ( 'Removed unused reference: ' + reference . identifier ) ;
}
}
}
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Checks whether entries of a characters prompt order are orphaned
* and if all mandatory system prompts for a character are present .
*
* @ param prompts
* /
checkForMissingPrompts ( prompts ) {
const defaultPromptIdentifiers = chatCompletionDefaultPrompts . prompts . reduce ( ( list , prompt ) => { list . push ( prompt . identifier ) ; return list ; } , [ ] ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
const missingIdentifiers = defaultPromptIdentifiers . filter ( identifier =>
! prompts . some ( prompt => prompt . identifier === identifier ) ,
) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
missingIdentifiers . forEach ( identifier => {
const defaultPrompt = chatCompletionDefaultPrompts . prompts . find ( prompt => prompt ? . identifier === identifier ) ;
if ( defaultPrompt ) {
prompts . push ( defaultPrompt ) ;
this . log ( ` Missing system prompt: ${ defaultPrompt . identifier } . Added default. ` ) ;
}
} ) ;
}
2023-06-24 17:01:03 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Check whether a prompt can be inspected .
* @ param { object } prompt - The prompt to check .
* @ returns { boolean } True if the prompt is a marker , false otherwise .
* /
isPromptInspectionAllowed ( prompt ) {
return true ;
2023-06-24 17:01:03 +02:00
}
2023-06-18 15:23:32 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Check whether a prompt can be deleted . System prompts cannot be deleted .
* @ param { object } prompt - The prompt to check .
* @ returns { boolean } True if the prompt can be deleted , false otherwise .
* /
isPromptDeletionAllowed ( prompt ) {
return false === prompt . system _prompt ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Check whether a prompt can be edited .
* @ param { object } prompt - The prompt to check .
* @ returns { boolean } True if the prompt can be edited , false otherwise .
* /
isPromptEditAllowed ( prompt ) {
2024-09-18 20:51:10 +02:00
const forceEditPrompts = [
'charDescription' ,
'charPersonality' ,
'scenario' ,
'personaDescription' ,
'worldInfoBefore' ,
'worldInfoAfter' ,
] ;
return forceEditPrompts . includes ( prompt . identifier ) || ! prompt . marker ;
2023-12-03 18:13:53 +01:00
}
2023-08-15 01:20:42 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Check whether a prompt can be toggled on or off .
* @ param { object } prompt - The prompt to check .
* @ returns { boolean } True if the prompt can be deleted , false otherwise .
* /
isPromptToggleAllowed ( prompt ) {
2024-09-18 20:51:10 +02:00
const forceTogglePrompts = [
'charDescription' ,
'charPersonality' ,
'scenario' ,
'personaDescription' ,
'worldInfoBefore' ,
'worldInfoAfter' ,
'main' ,
'chatHistory' ,
'dialogueExamples' ,
] ;
2023-12-03 18:13:53 +01:00
return prompt . marker && ! forceTogglePrompts . includes ( prompt . identifier ) ? false : ! this . configuration . toggleDisabled . includes ( prompt . identifier ) ;
}
2023-08-15 01:20:42 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Handle the deletion of a character by removing their prompt list and nullifying the active character if it was the one deleted .
* @ param { object } event - The event object containing the character ' s ID .
* @ returns void
* /
handleCharacterDeleted ( event ) {
if ( 'global' === this . configuration . promptOrder . strategy ) return ;
this . removePromptOrderForCharacter ( this . activeCharacter ) ;
if ( this . activeCharacter . id === event . detail . id ) this . activeCharacter = null ;
}
2023-08-15 01:20:42 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Handle the selection of a character by setting them as the active character and setting up their prompt list if necessary .
* @ param { object } event - The event object containing the character ' s ID and character data .
* @ returns { void }
* /
handleCharacterSelected ( event ) {
if ( 'global' === this . configuration . promptOrder . strategy ) {
this . activeCharacter = { id : this . configuration . promptOrder . dummyId } ;
} else if ( 'character' === this . configuration . promptOrder . strategy ) {
console . log ( 'FOO' ) ;
this . activeCharacter = { id : event . detail . id , ... event . detail . character } ;
const promptOrder = this . getPromptOrderForCharacter ( this . activeCharacter ) ;
2023-08-15 01:20:42 +02:00
2023-12-03 18:13:53 +01:00
// ToDo: These should be passed as parameter or attached to the manager as a set of default options.
// Set default prompts and order for character.
if ( 0 === promptOrder . length ) this . addPromptOrderForCharacter ( this . activeCharacter , promptManagerDefaultPromptOrder ) ;
} else {
throw new Error ( 'Unsupported prompt order mode.' ) ;
}
}
2023-08-15 01:20:42 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Set the most recently selected character
*
* @ param event
* /
handleCharacterUpdated ( event ) {
if ( 'global' === this . configuration . promptOrder . strategy ) {
this . activeCharacter = { id : this . configuration . promptOrder . dummyId } ;
} else if ( 'character' === this . configuration . promptOrder . strategy ) {
this . activeCharacter = { id : event . detail . id , ... event . detail . character } ;
} else {
throw new Error ( 'Prompt order strategy not supported.' ) ;
}
}
2023-08-22 20:37:37 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Set the most recently selected character group
*
* @ param event
* /
handleGroupSelected ( event ) {
if ( 'global' === this . configuration . promptOrder . strategy ) {
this . activeCharacter = { id : this . configuration . promptOrder . dummyId } ;
} else if ( 'character' === this . configuration . promptOrder . strategy ) {
const characterDummy = { id : event . detail . id , group : event . detail . group } ;
this . activeCharacter = characterDummy ;
const promptOrder = this . getPromptOrderForCharacter ( characterDummy ) ;
2023-08-15 01:20:42 +02:00
2023-12-03 18:13:53 +01:00
if ( 0 === promptOrder . length ) this . addPromptOrderForCharacter ( characterDummy , promptManagerDefaultPromptOrder ) ;
} else {
throw new Error ( 'Prompt order strategy not supported.' ) ;
}
}
2023-06-25 20:34:57 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Get a list of group characters , regardless of whether they are active or not .
*
* @ returns { string [ ] }
* /
getActiveGroupCharacters ( ) {
// ToDo: Ideally, this should return the actual characters.
return ( this . activeCharacter ? . group ? . members || [ ] ) . map ( member => member && member . substring ( 0 , member . lastIndexOf ( '.' ) ) ) ;
}
2023-06-25 20:34:57 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Get the prompts for a specific character . Can be filtered to only include enabled prompts .
* @ returns { object [ ] } The prompts for the character .
* @ param character
* @ param onlyEnabled
* /
getPromptsForCharacter ( character , onlyEnabled = false ) {
return this . getPromptOrderForCharacter ( character )
. map ( item => true === onlyEnabled ? ( true === item . enabled ? this . getPromptById ( item . identifier ) : null ) : this . getPromptById ( item . identifier ) )
. filter ( prompt => null !== prompt ) ;
}
2023-08-03 21:47:04 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Get the order of prompts for a specific character . If no character is specified or the character doesn ' t have a prompt list , an empty array is returned .
* @ param { object | null } character - The character to get the prompt list for .
* @ returns { object [ ] } The prompt list for the character , or an empty array .
* /
getPromptOrderForCharacter ( character ) {
return ! character ? [ ] : ( this . serviceSettings . prompt _order . find ( list => String ( list . character _id ) === String ( character . id ) ) ? . order ? ? [ ] ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Set the prompts for the manager .
* @ param { object [ ] } prompts - The prompts to be set .
* @ returns { void }
* /
setPrompts ( prompts ) {
this . serviceSettings . prompts = prompts ;
2023-11-10 14:36:42 +01:00
}
2023-12-03 18:13:53 +01:00
/ * *
* Remove the prompt list for a specific character .
* @ param { object } character - The character whose prompt list will be removed .
* @ returns { void }
* /
removePromptOrderForCharacter ( character ) {
const index = this . serviceSettings . prompt _order . findIndex ( list => String ( list . character _id ) === String ( character . id ) ) ;
if ( - 1 !== index ) this . serviceSettings . prompt _order . splice ( index , 1 ) ;
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Adds a new prompt list for a specific character .
* @ param { Object } character - Object with at least an ` id ` property
* @ param { Array < Object > } promptOrder - Array of prompt objects
* /
addPromptOrderForCharacter ( character , promptOrder ) {
this . serviceSettings . prompt _order . push ( {
character _id : character . id ,
order : JSON . parse ( JSON . stringify ( promptOrder ) ) ,
} ) ;
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Searches for a prompt list entry for a given character and identifier .
* @ param { Object } character - Character object
* @ param { string } identifier - Identifier of the prompt list entry
* @ returns { Object | null } The prompt list entry object , or null if not found
* /
getPromptOrderEntry ( character , identifier ) {
return this . getPromptOrderForCharacter ( character ) . find ( entry => entry . identifier === identifier ) ? ? null ;
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Finds and returns a prompt by its identifier .
* @ param { string } identifier - Identifier of the prompt
* @ returns { Object | null } The prompt object , or null if not found
* /
getPromptById ( identifier ) {
return this . serviceSettings . prompts . find ( item => item && item . identifier === identifier ) ? ? null ;
}
2023-11-08 15:28:55 +01:00
2023-12-03 18:13:53 +01:00
/ * *
* Finds and returns the index of a prompt by its identifier .
* @ param { string } identifier - Identifier of the prompt
* @ returns { number | null } Index of the prompt , or null if not found
* /
getPromptIndexById ( identifier ) {
return this . serviceSettings . prompts . findIndex ( item => item . identifier === identifier ) ? ? null ;
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Enriches a generic object , creating a new prompt object in the process
*
* @ param { Object } prompt - Prompt object
* @ param original
* @ returns { Object } An object with "role" and "content" properties
* /
preparePrompt ( prompt , original = null ) {
const groupMembers = this . getActiveGroupCharacters ( ) ;
const preparedPrompt = new Prompt ( prompt ) ;
2023-07-02 21:34:46 +02:00
2023-12-07 00:27:55 +01:00
if ( typeof original === 'string' ) {
2023-12-03 18:13:53 +01:00
if ( 0 < groupMembers . length ) preparedPrompt . content = substituteParams ( prompt . content ? ? '' , null , null , original , groupMembers . join ( ', ' ) ) ;
else preparedPrompt . content = substituteParams ( prompt . content , null , null , original ) ;
} else {
if ( 0 < groupMembers . length ) preparedPrompt . content = substituteParams ( prompt . content ? ? '' , null , null , null , groupMembers . join ( ', ' ) ) ;
else preparedPrompt . content = substituteParams ( prompt . content ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
return preparedPrompt ;
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Factory function for creating a QuickEdit object associated with a prompt element .
*
* The QuickEdit object provides methods to synchronize an input element 's value with a prompt' s content
* and handle input events to update the prompt content .
*
* /
createQuickEdit ( identifier , title ) {
const prompt = this . getPromptById ( identifier ) ;
const textareaIdentifier = ` ${ identifier } _prompt_quick_edit_textarea ` ;
2024-05-19 16:49:43 +02:00
const html = ` <div class="range-block m-t-1">
2023-12-03 18:13:53 +01:00
< div class = "justifyLeft" data - i18n = "${title}" > $ { title } < / d i v >
< div class = "wide100p" >
< textarea id = "${textareaIdentifier}" class = "text_pole textarea_compact" rows = "6" placeholder = "" > $ { prompt . content } < / t e x t a r e a >
< / d i v >
2024-05-19 16:49:43 +02:00
< / d i v > ` ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
const quickEditContainer = document . getElementById ( 'quick-edit-container' ) ;
quickEditContainer . insertAdjacentHTML ( 'afterbegin' , html ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
const debouncedSaveServiceSettings = debouncePromise ( ( ) => this . saveServiceSettings ( ) , 300 ) ;
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
const textarea = document . getElementById ( textareaIdentifier ) ;
textarea . addEventListener ( 'blur' , ( ) => {
prompt . content = textarea . value ;
this . updatePromptByIdentifier ( identifier , prompt ) ;
debouncedSaveServiceSettings ( ) . then ( ( ) => this . render ( ) ) ;
} ) ;
2023-07-21 16:13:25 +02:00
2023-12-03 18:13:53 +01:00
}
2023-07-21 16:13:25 +02:00
2023-12-03 18:13:53 +01:00
updateQuickEdit ( identifier , prompt ) {
const elementId = ` ${ identifier } _prompt_quick_edit_textarea ` ;
const textarea = document . getElementById ( elementId ) ;
textarea . value = prompt . content ;
2023-06-19 19:26:38 +02:00
2023-12-03 18:13:53 +01:00
return elementId ;
}
2023-07-01 20:01:31 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Checks if a given name is accepted by OpenAi API
* @ link https : //platform.openai.com/docs/api-reference/chat/create
*
* @ param name
* @ returns { boolean }
* /
isValidName ( name ) {
const regex = /^[a-zA-Z0-9_]{1,64}$/ ;
2023-07-21 16:13:25 +02:00
2023-12-03 18:13:53 +01:00
return regex . test ( name ) ;
}
2023-06-18 15:23:32 +02:00
2023-12-03 18:13:53 +01:00
sanitizeName ( name ) {
return name . replace ( /[^a-zA-Z0-9_]/g , '_' ) . substring ( 0 , 64 ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Loads a given prompt into the edit form fields .
* @ param { Object } prompt - Prompt object with properties 'name' , 'role' , 'content' , and 'system_prompt'
* /
loadPromptIntoEditForm ( prompt ) {
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' ) ;
const injectionPositionField = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_position' ) ;
const injectionDepthField = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_depth' ) ;
const injectionDepthBlock = document . getElementById ( this . configuration . prefix + 'prompt_manager_depth_block' ) ;
2024-03-24 00:28:35 +01:00
const forbidOverridesField = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_forbid_overrides' ) ;
const forbidOverridesBlock = document . getElementById ( this . configuration . prefix + 'prompt_manager_forbid_overrides_block' ) ;
2023-12-03 18:13:53 +01:00
nameField . value = prompt . name ? ? '' ;
2024-09-18 20:51:10 +02:00
roleField . value = prompt . role ? ? 'system' ;
2023-12-03 18:13:53 +01:00
promptField . value = prompt . content ? ? '' ;
2024-09-18 20:51:10 +02:00
promptField . disabled = prompt . marker ? ? false ;
2023-12-03 18:13:53 +01:00
injectionPositionField . value = prompt . injection _position ? ? INJECTION _POSITION . RELATIVE ;
injectionDepthField . value = prompt . injection _depth ? ? DEFAULT _DEPTH ;
injectionDepthBlock . style . visibility = prompt . injection _position === INJECTION _POSITION . ABSOLUTE ? 'visible' : 'hidden' ;
injectionPositionField . removeAttribute ( 'disabled' ) ;
2024-03-24 00:28:35 +01:00
forbidOverridesField . checked = prompt . forbid _overrides ? ? false ;
forbidOverridesBlock . style . visibility = this . overridablePrompts . includes ( prompt . identifier ) ? 'visible' : 'hidden' ;
2023-12-03 18:13:53 +01:00
if ( this . systemPrompts . includes ( prompt . identifier ) ) {
injectionPositionField . setAttribute ( 'disabled' , 'disabled' ) ;
}
const resetPromptButton = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_reset' ) ;
if ( true === prompt . system _prompt ) {
resetPromptButton . style . display = 'block' ;
resetPromptButton . dataset . pmPrompt = prompt . identifier ;
} else {
resetPromptButton . style . display = 'none' ;
}
2023-07-31 15:28:01 +02:00
2023-12-03 18:13:53 +01:00
injectionPositionField . removeEventListener ( 'change' , ( e ) => this . handleInjectionPositionChange ( e ) ) ;
injectionPositionField . addEventListener ( 'change' , ( e ) => this . handleInjectionPositionChange ( e ) ) ;
2023-06-03 18:13:05 +02:00
2023-12-03 18:13:53 +01:00
const savePromptButton = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_save' ) ;
savePromptButton . dataset . pmPrompt = prompt . identifier ;
}
handleInjectionPositionChange ( event ) {
const injectionDepthBlock = document . getElementById ( this . configuration . prefix + 'prompt_manager_depth_block' ) ;
const injectionPosition = Number ( event . target . value ) ;
if ( injectionPosition === INJECTION _POSITION . ABSOLUTE ) {
injectionDepthBlock . style . visibility = 'visible' ;
} else {
injectionDepthBlock . style . visibility = 'hidden' ;
}
}
/ * *
* Loads a given prompt into the inspect form
* @ param { MessageCollection } messages - Prompt object with properties 'name' , 'role' , 'content' , and 'system_prompt'
* /
loadMessagesIntoInspectForm ( messages ) {
if ( ! messages ) return ;
const createInlineDrawer = ( message ) => {
const truncatedTitle = message . content . length > 32 ? message . content . slice ( 0 , 32 ) + '...' : message . content ;
const title = message . identifier || truncatedTitle ;
const role = message . role ;
const content = message . content || 'No Content' ;
const tokens = message . getTokens ( ) ;
let drawerHTML = `
< div class = "inline-drawer ${this.configuration.prefix}prompt_manager_prompt" >
< div class = "inline-drawer-toggle inline-drawer-header" >
< span > Name : $ { escapeHtml ( title ) } , Role : $ { role } , Tokens : $ { tokens } < / s p a n >
< div class = "fa-solid fa-circle-chevron-down inline-drawer-icon down" > < / d i v >
2023-06-03 18:13:05 +02:00
< / d i v >
2023-12-03 18:13:53 +01:00
< div class = "inline-drawer-content" style = "white-space: pre-wrap;" > $ { escapeHtml ( content ) } < / d i v >
2023-06-03 18:13:05 +02:00
< / d i v >
2023-07-04 21:13:21 +02:00
` ;
2023-12-03 18:13:53 +01:00
let template = document . createElement ( 'template' ) ;
template . innerHTML = drawerHTML . trim ( ) ;
return template . content . firstChild ;
} ;
2023-07-08 18:10:07 +02:00
2023-12-03 18:13:53 +01:00
const messageList = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_inspect_list' ) ;
2023-07-08 18:10:07 +02:00
2023-12-03 18:13:53 +01:00
const messagesCollection = messages instanceof Message ? [ messages ] : messages . getCollection ( ) ;
2023-07-08 18:10:07 +02:00
2023-12-03 18:13:53 +01:00
if ( 0 === messagesCollection . length ) messageList . innerHTML = '<span>This marker does not contain any prompts.</span>' ;
2023-07-08 18:10:07 +02:00
2023-12-03 18:13:53 +01:00
messagesCollection . forEach ( message => {
messageList . append ( createInlineDrawer ( message ) ) ;
} ) ;
}
2023-07-08 18:10:07 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Clears all input fields in the edit form .
* /
clearEditForm ( ) {
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' ) ;
const injectionPositionField = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_position' ) ;
const injectionDepthField = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_injection_depth' ) ;
const injectionDepthBlock = document . getElementById ( this . configuration . prefix + 'prompt_manager_depth_block' ) ;
2024-03-24 00:28:35 +01:00
const forbidOverridesField = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_forbid_overrides' ) ;
const forbidOverridesBlock = document . getElementById ( this . configuration . prefix + 'prompt_manager_forbid_overrides_block' ) ;
2023-12-03 18:13:53 +01:00
nameField . value = '' ;
roleField . selectedIndex = 0 ;
promptField . value = '' ;
2024-09-18 20:51:10 +02:00
promptField . disabled = false ;
2023-12-03 18:13:53 +01:00
injectionPositionField . selectedIndex = 0 ;
injectionPositionField . removeAttribute ( 'disabled' ) ;
injectionDepthField . value = DEFAULT _DEPTH ;
injectionDepthBlock . style . visibility = 'unset' ;
2024-03-24 00:28:35 +01:00
forbidOverridesBlock . style . visibility = 'unset' ;
forbidOverridesField . checked = false ;
2023-12-03 18:13:53 +01:00
roleField . disabled = false ;
}
2023-07-31 15:28:01 +02:00
2023-12-03 18:13:53 +01:00
clearInspectForm ( ) {
const inspectArea = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_inspect' ) ;
inspectArea . style . display = 'none' ;
const messageList = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_entry_form_inspect_list' ) ;
messageList . innerHTML = '' ;
2023-05-28 15:52:45 +02:00
}
2023-12-03 18:13:53 +01:00
/ * *
* Returns a full list of prompts whose content markers have been substituted .
* @ returns { PromptCollection } A PromptCollection object
* /
getPromptCollection ( ) {
const promptOrder = this . getPromptOrderForCharacter ( this . activeCharacter ) ;
const promptCollection = new PromptCollection ( ) ;
promptOrder . forEach ( entry => {
if ( true === entry . enabled ) {
const prompt = this . getPromptById ( entry . identifier ) ;
if ( prompt ) promptCollection . add ( this . preparePrompt ( prompt ) ) ;
2024-03-23 23:28:54 +01:00
} else if ( ! entry . enabled && entry . identifier === 'main' ) {
// Some extensions require main prompt to be present for relative inserts.
// So we make a GMO-free vegan replacement.
2024-04-26 23:08:30 +02:00
const prompt = structuredClone ( this . getPromptById ( entry . identifier ) ) ;
2024-03-23 23:28:54 +01:00
prompt . content = '' ;
if ( prompt ) promptCollection . add ( this . preparePrompt ( prompt ) ) ;
2023-06-18 15:23:32 +02:00
}
2023-12-03 18:13:53 +01:00
} ) ;
2023-06-18 15:23:32 +02:00
2023-12-03 18:13:53 +01:00
return promptCollection ;
}
2023-06-03 18:13:05 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Setter for messages property
*
2024-03-24 01:19:10 +01:00
* @ param { import ( './openai.js' ) . MessageCollection } messages
2023-12-03 18:13:53 +01:00
* /
setMessages ( messages ) {
this . messages = messages ;
}
2023-06-21 19:42:12 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Set and process a finished chat completion object
*
2024-03-24 01:19:10 +01:00
* @ param { import ( './openai.js' ) . ChatCompletion } chatCompletion
2023-12-03 18:13:53 +01:00
* /
setChatCompletion ( chatCompletion ) {
const messages = chatCompletion . getMessages ( ) ;
this . setMessages ( messages ) ;
this . populateTokenCounts ( messages ) ;
2024-03-24 01:19:10 +01:00
this . overriddenPrompts = chatCompletion . getOverriddenPrompts ( ) ;
2023-12-03 18:13:53 +01:00
}
/ * *
* Populates the token handler
*
2024-03-24 01:19:10 +01:00
* @ param { import ( './openai.js' ) . MessageCollection } messages
2023-12-03 18:13:53 +01:00
* /
populateTokenCounts ( messages ) {
this . tokenHandler . resetCounts ( ) ;
const counts = this . tokenHandler . getCounts ( ) ;
messages . getCollection ( ) . forEach ( message => {
counts [ message . identifier ] = message . getTokens ( ) ;
} ) ;
this . tokenUsage = this . tokenHandler . getTotal ( ) ;
this . log ( 'Updated token usage with ' + this . tokenUsage ) ;
}
/ * *
* Empties , then re - assembles the container containing the prompt list .
* /
2024-05-21 12:06:42 +02:00
async renderPromptManager ( ) {
2024-03-21 19:12:36 +01:00
let selectedPromptIndex = 0 ;
const existingAppendSelect = document . getElementById ( ` ${ this . configuration . prefix } prompt_manager_footer_append_prompt ` ) ;
if ( existingAppendSelect instanceof HTMLSelectElement ) {
selectedPromptIndex = existingAppendSelect . selectedIndex ;
}
2023-12-03 18:13:53 +01:00
const promptManagerDiv = this . containerElement ;
promptManagerDiv . innerHTML = '' ;
2023-07-02 21:34:46 +02:00
2024-05-29 16:23:37 +02:00
const errorDiv = this . error ? `
2023-12-03 18:13:53 +01:00
< div class = "${this.configuration.prefix}prompt_manager_error" >
2024-05-24 21:05:28 +02:00
< span class = "fa-solid tooltip fa-triangle-exclamation text_danger" > < / s p a n > $ { D O M P u r i f y . s a n i t i z e ( t h i s . e r r o r ) }
2023-12-03 18:13:53 +01:00
< / d i v >
2024-05-29 16:23:37 +02:00
` : '';
2023-12-03 18:13:53 +01:00
const totalActiveTokens = this . tokenUsage ;
2024-05-24 21:00:21 +02:00
const headerHtml = await renderTemplateAsync ( 'promptManagerHeader' , { error : this . error , errorDiv , prefix : this . configuration . prefix , totalActiveTokens } ) ;
2024-05-21 12:06:42 +02:00
promptManagerDiv . insertAdjacentHTML ( 'beforeend' , headerHtml ) ;
2023-12-03 18:13:53 +01:00
this . listElement = promptManagerDiv . querySelector ( ` # ${ this . configuration . prefix } prompt_manager_list ` ) ;
if ( null !== this . activeCharacter ) {
const prompts = [ ... this . serviceSettings . prompts ]
. filter ( prompt => prompt && ! prompt ? . system _prompt )
2024-03-21 19:12:36 +01:00
. sort ( ( promptA , promptB ) => promptA . name . localeCompare ( promptB . name ) ) ;
const promptsHtml = prompts . reduce ( ( acc , prompt ) => acc + ` <option value=" ${ prompt . identifier } "> ${ escapeHtml ( prompt . name ) } </option> ` , '' ) ;
if ( selectedPromptIndex > 0 ) {
selectedPromptIndex = Math . min ( selectedPromptIndex , prompts . length - 1 ) ;
}
if ( selectedPromptIndex === - 1 && prompts . length ) {
selectedPromptIndex = 0 ;
}
2023-12-03 18:13:53 +01:00
const rangeBlockDiv = promptManagerDiv . querySelector ( '.range-block' ) ;
2024-04-21 13:48:21 +02:00
const headerDiv = promptManagerDiv . querySelector ( '.completion_prompt_manager_header' ) ;
2024-05-24 21:00:21 +02:00
const footerHtml = await renderTemplateAsync ( 'promptManagerFooter' , { promptsHtml , prefix : this . configuration . prefix } ) ;
2024-04-21 13:48:21 +02:00
headerDiv . insertAdjacentHTML ( 'afterend' , footerHtml ) ;
2023-12-03 18:13:53 +01:00
rangeBlockDiv . querySelector ( '#prompt-manager-reset-character' ) . addEventListener ( 'click' , this . handleCharacterReset ) ;
const footerDiv = rangeBlockDiv . querySelector ( ` . ${ this . configuration . prefix } prompt_manager_footer ` ) ;
footerDiv . querySelector ( '.menu_button:nth-child(2)' ) . addEventListener ( 'click' , this . handleAppendPrompt ) ;
footerDiv . querySelector ( '.caution' ) . addEventListener ( 'click' , this . handleDeletePrompt ) ;
footerDiv . querySelector ( '.menu_button:last-child' ) . addEventListener ( 'click' , this . handleNewPrompt ) ;
2024-03-21 19:12:36 +01:00
footerDiv . querySelector ( 'select' ) . selectedIndex = selectedPromptIndex ;
2023-12-03 18:13:53 +01:00
// Add prompt export dialogue and options
2024-05-24 21:00:21 +02:00
2024-05-21 12:06:42 +02:00
const exportForCharacter = await renderTemplateAsync ( 'promptManagerExportForCharacter' ) ;
2024-05-24 21:25:18 +02:00
const exportPopup = await renderTemplateAsync ( 'promptManagerExportPopup' , { isGlobalStrategy : 'global' === this . configuration . promptOrder . strategy , exportForCharacter } ) ;
2023-12-03 18:13:53 +01:00
rangeBlockDiv . insertAdjacentHTML ( 'beforeend' , exportPopup ) ;
2024-04-21 13:48:21 +02:00
// Destroy previous popper instance if it exists
if ( this . exportPopper ) {
this . exportPopper . destroy ( ) ;
}
this . exportPopper = Popper . createPopper (
2023-12-03 18:13:53 +01:00
document . getElementById ( 'prompt-manager-export' ) ,
document . getElementById ( 'prompt-manager-export-format-popup' ) ,
{ placement : 'bottom' } ,
) ;
const showExportSelection = ( ) => {
const popup = document . getElementById ( 'prompt-manager-export-format-popup' ) ;
const show = popup . hasAttribute ( 'data-show' ) ;
if ( show ) popup . removeAttribute ( 'data-show' ) ;
else popup . setAttribute ( 'data-show' , '' ) ;
2024-04-21 13:48:21 +02:00
this . exportPopper . update ( ) ;
2023-12-03 18:13:53 +01:00
} ;
footerDiv . querySelector ( '#prompt-manager-import' ) . addEventListener ( 'click' , this . handleImport ) ;
footerDiv . querySelector ( '#prompt-manager-export' ) . addEventListener ( 'click' , showExportSelection ) ;
rangeBlockDiv . querySelector ( '.export-promptmanager-prompts-full' ) . addEventListener ( 'click' , this . handleFullExport ) ;
rangeBlockDiv . querySelector ( '.export-promptmanager-prompts-character' ) ? . addEventListener ( 'click' , this . handleCharacterExport ) ;
2023-05-28 15:52:45 +02:00
}
2023-12-03 18:13:53 +01:00
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Empties , then re - assembles the prompt list
* /
2024-05-21 12:06:42 +02:00
async renderPromptManagerListItems ( ) {
2023-12-03 18:13:53 +01:00
if ( ! this . serviceSettings . prompts ) return ;
const promptManagerList = this . listElement ;
promptManagerList . innerHTML = '' ;
const { prefix } = this . configuration ;
2024-05-24 21:00:21 +02:00
2024-05-24 21:23:55 +02:00
let listItemHtml = await renderTemplateAsync ( 'promptManagerListHeader' , { prefix } ) ;
2024-05-24 21:00:21 +02:00
2024-05-24 21:23:04 +02:00
this . getPromptsForCharacter ( this . activeCharacter ) . forEach ( prompt => {
2023-12-03 18:13:53 +01:00
if ( ! prompt ) return ;
2024-05-24 21:23:04 +02:00
const listEntry = this . getPromptOrderEntry ( this . activeCharacter , prompt . identifier ) ;
2023-12-03 18:13:53 +01:00
const enabledClass = listEntry . enabled ? '' : ` ${ prefix } prompt_manager_prompt_disabled ` ;
const draggableClass = ` ${ prefix } prompt_manager_prompt_draggable ` ;
const markerClass = prompt . marker ? ` ${ prefix } prompt_manager_marker ` : '' ;
2024-05-24 21:23:04 +02:00
const tokens = this . tokenHandler ? . getCounts ( ) [ prompt . identifier ] ? ? 0 ;
2023-12-03 18:13:53 +01:00
// Warn the user if the chat history goes below certain token thresholds.
let warningClass = '' ;
let warningTitle = '' ;
2024-05-24 21:23:04 +02:00
const tokenBudget = this . serviceSettings . openai _max _context - this . serviceSettings . openai _max _tokens ;
if ( this . tokenUsage > tokenBudget * 0.8 &&
2023-12-03 18:13:53 +01:00
'chatHistory' === prompt . identifier ) {
2024-05-24 21:23:04 +02:00
const warningThreshold = this . configuration . warningTokenThreshold ;
const dangerThreshold = this . configuration . dangerTokenThreshold ;
2023-12-03 18:13:53 +01:00
if ( tokens <= dangerThreshold ) {
warningClass = 'fa-solid tooltip fa-triangle-exclamation text_danger' ;
warningTitle = 'Very little of your chat history is being sent, consider deactivating some other prompts.' ;
} else if ( tokens <= warningThreshold ) {
warningClass = 'fa-solid tooltip fa-triangle-exclamation text_warning' ;
warningTitle = 'Only a few messages worth chat history are being sent.' ;
}
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
const calculatedTokens = tokens ? tokens : '-' ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
let detachSpanHtml = '' ;
2024-05-24 21:23:04 +02:00
if ( this . isPromptDeletionAllowed ( prompt ) ) {
2023-12-03 18:13:53 +01:00
detachSpanHtml = `
< span title = "Remove" class = "prompt-manager-detach-action caution fa-solid fa-chain-broken" > < / s p a n >
` ;
} else {
detachSpanHtml = '<span class="fa-solid"></span>' ;
}
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
let editSpanHtml = '' ;
2024-05-24 21:23:04 +02:00
if ( this . isPromptEditAllowed ( prompt ) ) {
2023-12-03 18:13:53 +01:00
editSpanHtml = `
< span title = "edit" class = "prompt-manager-edit-action fa-solid fa-pencil" > < / s p a n >
` ;
} else {
editSpanHtml = '<span class="fa-solid"></span>' ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
let toggleSpanHtml = '' ;
2024-05-24 21:23:04 +02:00
if ( this . isPromptToggleAllowed ( prompt ) ) {
2023-12-03 18:13:53 +01:00
toggleSpanHtml = `
< span class = "prompt-manager-toggle-action ${listEntry.enabled ? 'fa-solid fa-toggle-on' : 'fa-solid fa-toggle-off'}" > < / s p a n >
` ;
} else {
toggleSpanHtml = '<span class="fa-solid"></span>' ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
const encodedName = escapeHtml ( prompt . name ) ;
2024-09-23 20:04:00 +02:00
const isMarkerPrompt = prompt . marker && prompt . injection _position !== INJECTION _POSITION . ABSOLUTE ;
2024-03-24 00:28:35 +01:00
const isSystemPrompt = ! prompt . marker && prompt . system _prompt && prompt . injection _position !== INJECTION _POSITION . ABSOLUTE && ! prompt . forbid _overrides ;
2024-09-23 21:44:49 +02:00
const isImportantPrompt = ! prompt . marker && prompt . system _prompt && prompt . injection _position !== INJECTION _POSITION . ABSOLUTE && prompt . forbid _overrides ;
2023-12-03 18:13:53 +01:00
const isUserPrompt = ! prompt . marker && ! prompt . system _prompt && prompt . injection _position !== INJECTION _POSITION . ABSOLUTE ;
2024-09-23 20:04:00 +02:00
const isInjectionPrompt = prompt . injection _position === INJECTION _POSITION . ABSOLUTE ;
2024-05-24 21:23:04 +02:00
const isOverriddenPrompt = Array . isArray ( this . overriddenPrompts ) && this . overriddenPrompts . includes ( prompt . identifier ) ;
2024-03-24 00:28:35 +01:00
const importantClass = isImportantPrompt ? ` ${ prefix } prompt_manager_important ` : '' ;
2024-05-24 21:23:55 +02:00
listItemHtml += `
2024-03-24 00:28:35 +01:00
< li class = "${prefix}prompt_manager_prompt ${draggableClass} ${enabledClass} ${markerClass} ${importantClass}" data - pm - identifier = "${prompt.identifier}" >
2023-12-03 18:13:53 +01:00
< span class = "${prefix}prompt_manager_prompt_name" data - pm - name = "${encodedName}" >
2024-09-23 20:04:00 +02:00
$ { isMarkerPrompt ? '<span class="fa-fw fa-solid fa-thumb-tack" title="Marker"></span>' : '' }
2024-03-24 00:28:35 +01:00
$ { isSystemPrompt ? '<span class="fa-fw fa-solid fa-square-poll-horizontal" title="Global Prompt"></span>' : '' }
$ { isImportantPrompt ? '<span class="fa-fw fa-solid fa-star" title="Important Prompt"></span>' : '' }
$ { isUserPrompt ? '<span class="fa-fw fa-solid fa-user" title="User Prompt"></span>' : '' }
$ { isInjectionPrompt ? '<span class="fa-fw fa-solid fa-syringe" title="In-Chat Injection"></span>' : '' }
2024-06-01 22:04:13 +02:00
$ { this . isPromptInspectionAllowed ( prompt ) ? ` <a title=" ${ encodedName } " class="prompt-manager-inspect-action"> ${ encodedName } </a> ` : ` <span title=" ${ encodedName } "> ${ encodedName } </span> ` }
2023-12-03 18:13:53 +01:00
$ { isInjectionPrompt ? ` <small class="prompt-manager-injection-depth">@ ${ prompt . injection _depth } </small> ` : '' }
2024-03-24 01:19:10 +01:00
$ { isOverriddenPrompt ? '<small class="fa-solid fa-address-card prompt-manager-overridden" title="Pulled from a character card"></small>' : '' }
2023-12-03 18:13:53 +01:00
< / s p a n >
< span >
< span class = "prompt_manager_prompt_controls" >
$ { detachSpanHtml }
$ { editSpanHtml }
$ { toggleSpanHtml }
< / s p a n >
< / s p a n >
< span class = "prompt_manager_prompt_tokens" data - pm - tokens = "${calculatedTokens}" > < span class = "${warningClass}" title = "${warningTitle}" > < / s p a n > $ { c a l c u l a t e d T o k e n s } < / s p a n >
< / l i >
` ;
} ) ;
2023-07-04 21:13:21 +02:00
2024-05-24 21:23:55 +02:00
promptManagerList . insertAdjacentHTML ( 'beforeend' , listItemHtml ) ;
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
// Now that the new elements are in the DOM, you can add the event listeners.
Array . from ( promptManagerList . getElementsByClassName ( 'prompt-manager-detach-action' ) ) . forEach ( el => {
el . addEventListener ( 'click' , this . handleDetach ) ;
} ) ;
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
Array . from ( promptManagerList . getElementsByClassName ( 'prompt-manager-inspect-action' ) ) . forEach ( el => {
el . addEventListener ( 'click' , this . handleInspect ) ;
} ) ;
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
Array . from ( promptManagerList . getElementsByClassName ( 'prompt-manager-edit-action' ) ) . forEach ( el => {
el . addEventListener ( 'click' , this . handleEdit ) ;
} ) ;
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
Array . from ( promptManagerList . querySelectorAll ( '.prompt-manager-toggle-action' ) ) . forEach ( el => {
el . addEventListener ( 'click' , this . handleToggle ) ;
} ) ;
}
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Writes the passed data to a json file
*
* @ param data
* @ param type
* @ param name
* /
export ( data , type , name = 'export' ) {
const promptExport = {
version : this . configuration . version ,
type : type ,
data : data ,
} ;
2023-07-05 20:12:21 +02:00
2024-05-24 21:15:28 +02:00
const serializedObject = JSON . stringify ( promptExport , null , 4 ) ;
2023-12-03 18:13:53 +01:00
const blob = new Blob ( [ serializedObject ] , { type : 'application/json' } ) ;
const url = URL . createObjectURL ( blob ) ;
const downloadLink = document . createElement ( 'a' ) ;
downloadLink . href = url ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
const dateString = this . getFormattedDate ( ) ;
downloadLink . download = ` ${ name } - ${ dateString } .json ` ;
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
downloadLink . click ( ) ;
2023-07-11 21:03:12 +02:00
2023-12-03 18:13:53 +01:00
URL . revokeObjectURL ( url ) ;
2023-07-11 21:03:12 +02:00
}
2023-12-03 18:13:53 +01:00
/ * *
* Imports a json file with prompts and an optional prompt list for the active character
*
* @ param importData
* /
import ( importData ) {
const mergeKeepNewer = ( prompts , newPrompts ) => {
let merged = [ ... prompts , ... newPrompts ] ;
2023-07-06 21:20:18 +02:00
2023-12-03 18:13:53 +01:00
let map = new Map ( ) ;
for ( let obj of merged ) {
map . set ( obj . identifier , obj ) ;
}
2023-07-05 20:12:21 +02:00
2023-12-03 18:13:53 +01:00
merged = Array . from ( map . values ( ) ) ;
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
return merged ;
} ;
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
const controlObj = {
version : 1 ,
type : '' ,
data : {
prompts : [ ] ,
prompt _order : null ,
} ,
} ;
if ( false === this . validateObject ( controlObj , importData ) ) {
2024-10-01 16:25:20 +02:00
toastr . warning ( t ` Could not import prompts. Export failed validation. ` ) ;
2023-12-03 18:13:53 +01:00
return ;
2023-07-11 21:03:12 +02:00
}
2023-12-03 18:13:53 +01:00
const prompts = mergeKeepNewer ( this . serviceSettings . prompts , importData . data . prompts ) ;
this . setPrompts ( prompts ) ;
this . log ( 'Prompt import succeeded' ) ;
if ( 'global' === this . configuration . promptOrder . strategy ) {
const promptOrder = this . getPromptOrderForCharacter ( { id : this . configuration . promptOrder . dummyId } ) ;
Object . assign ( promptOrder , importData . data . prompt _order ) ;
this . log ( 'Prompt order import succeeded' ) ;
} else if ( 'character' === this . configuration . promptOrder . strategy ) {
if ( 'character' === importData . type ) {
const promptOrder = this . getPromptOrderForCharacter ( this . activeCharacter ) ;
Object . assign ( promptOrder , importData . data . prompt _order ) ;
this . log ( ` Prompt order import for character ${ this . activeCharacter . name } succeeded ` ) ;
}
2023-07-11 21:03:12 +02:00
} else {
2023-12-03 18:13:53 +01:00
throw new Error ( 'Prompt order strategy not supported.' ) ;
2023-07-11 21:03:12 +02:00
}
2023-12-03 18:13:53 +01:00
2024-10-01 16:25:20 +02:00
toastr . success ( t ` Prompt import complete. ` ) ;
2023-12-03 18:13:53 +01:00
this . saveServiceSettings ( ) . then ( ( ) => this . render ( ) ) ;
2023-07-11 21:03:12 +02:00
}
2023-12-03 18:13:53 +01:00
/ * *
* Helper function to check whether the structure of object matches controlObj
*
* @ param controlObj
* @ param object
* @ returns { boolean }
* /
validateObject ( controlObj , object ) {
for ( let key in controlObj ) {
if ( ! Object . hasOwn ( object , key ) ) {
if ( controlObj [ key ] === null ) continue ;
else return false ;
}
2023-07-11 21:03:12 +02:00
2023-12-03 18:13:53 +01:00
if ( typeof controlObj [ key ] === 'object' && controlObj [ key ] !== null ) {
if ( typeof object [ key ] !== 'object' ) return false ;
if ( ! this . validateObject ( controlObj [ key ] , object [ key ] ) ) return false ;
} else {
if ( typeof object [ key ] !== typeof controlObj [ key ] ) return false ;
}
}
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
return true ;
}
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Get current date as mm / dd / YYYY
*
* @ returns { ` ${ string } _ ${ string } _ ${ string } ` }
* /
getFormattedDate ( ) {
const date = new Date ( ) ;
let month = String ( date . getMonth ( ) + 1 ) ;
let day = String ( date . getDate ( ) ) ;
const year = String ( date . getFullYear ( ) ) ;
2023-07-04 21:13:21 +02:00
2023-12-03 18:13:53 +01:00
if ( month . length < 2 ) month = '0' + month ;
if ( day . length < 2 ) day = '0' + day ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
return ` ${ month } _ ${ day } _ ${ year } ` ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Makes the prompt list draggable and handles swapping of two entries in the list .
* @ typedef { Object } Entry
* @ property { string } identifier
* @ returns { void }
* /
makeDraggable ( ) {
$ ( ` # ${ this . configuration . prefix } prompt_manager_list ` ) . sortable ( {
delay : this . configuration . sortableDelay ,
items : ` . ${ this . configuration . prefix } prompt_manager_prompt_draggable ` ,
update : ( event , ui ) => {
const promptOrder = this . getPromptOrderForCharacter ( this . activeCharacter ) ;
const promptListElement = $ ( ` # ${ this . configuration . prefix } prompt_manager_list ` ) . sortable ( 'toArray' , { attribute : 'data-pm-identifier' } ) ;
const idToObjectMap = new Map ( promptOrder . map ( prompt => [ prompt . identifier , prompt ] ) ) ;
const updatedPromptOrder = promptListElement . map ( identifier => idToObjectMap . get ( identifier ) ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
this . removePromptOrderForCharacter ( this . activeCharacter ) ;
this . addPromptOrderForCharacter ( this . activeCharacter , updatedPromptOrder ) ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
this . log ( ` Prompt order updated for ${ this . activeCharacter . name } . ` ) ;
2023-07-02 21:34:46 +02:00
2023-12-03 18:13:53 +01:00
this . saveServiceSettings ( ) ;
} ,
} ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Slides down the edit form and adds the class 'openDrawer' to the first element of '#openai_prompt_manager_popup' .
* @ returns { void }
* /
showPopup ( area = 'edit' ) {
const areaElement = document . getElementById ( this . configuration . prefix + 'prompt_manager_popup_' + area ) ;
2024-09-23 22:11:28 +02:00
areaElement . style . display = 'flex' ;
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
$ ( '#' + this . configuration . prefix + 'prompt_manager_popup' ) . first ( )
. slideDown ( 200 , 'swing' )
. addClass ( 'openDrawer' ) ;
}
2023-05-28 15:52:45 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Slides up the edit form and removes the class 'openDrawer' from the first element of '#openai_prompt_manager_popup' .
* @ returns { void }
* /
hidePopup ( ) {
$ ( '#' + this . configuration . prefix + 'prompt_manager_popup' ) . first ( )
. slideUp ( 200 , 'swing' )
. removeClass ( 'openDrawer' ) ;
}
2023-07-01 20:01:31 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Quick uuid4 implementation
* @ returns { string } A string representation of an uuid4
* /
getUuidv4 ( ) {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' . replace ( /[xy]/g , function ( c ) {
let r = Math . random ( ) * 16 | 0 ,
v = c === 'x' ? r : ( r & 0x3 | 0x8 ) ;
return v . toString ( 16 ) ;
} ) ;
}
2023-07-01 20:01:31 +02:00
2023-12-03 18:13:53 +01:00
/ * *
* Write to console with prefix
*
* @ param output
* /
log ( output ) {
if ( power _user . console _log _prompts ) console . log ( '[PromptManager] ' + output ) ;
2023-07-01 20:01:31 +02:00
}
2023-12-03 18:13:53 +01:00
/ * *
* Start a profiling task
*
* @ param identifier
* /
profileStart ( identifier ) {
if ( power _user . console _log _prompts ) console . time ( identifier ) ;
}
/ * *
* End a profiling task
*
* @ param identifier
* /
profileEnd ( identifier ) {
if ( power _user . console _log _prompts ) {
this . log ( 'Profiling of "' + identifier + '" finished. Result below.' ) ;
console . timeEnd ( identifier ) ;
}
}
}
2023-07-01 20:01:31 +02:00
2023-07-20 19:49:38 +02:00
const chatCompletionDefaultPrompts = {
2023-12-02 19:04:51 +01:00
'prompts' : [
2023-05-28 15:52:45 +02:00
{
2023-12-02 19:04:51 +01:00
'name' : 'Main Prompt' ,
'system_prompt' : true ,
'role' : 'system' ,
2024-07-21 13:29:13 +02:00
'content' : 'Write {{char}}\'s next reply in a fictional chat between {{charIfNotGroup}} and {{user}}.' ,
2023-12-02 21:06:57 +01:00
'identifier' : 'main' ,
2023-05-28 15:52:45 +02:00
} ,
{
2024-07-21 13:29:13 +02:00
'name' : 'Auxiliary Prompt' ,
2023-12-02 19:04:51 +01:00
'system_prompt' : true ,
'role' : 'system' ,
2024-07-21 13:29:13 +02:00
'content' : '' ,
2023-12-02 21:06:57 +01:00
'identifier' : 'nsfw' ,
2023-05-28 15:52:45 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'dialogueExamples' ,
'name' : 'Chat Examples' ,
'system_prompt' : true ,
'marker' : true ,
2023-05-28 15:52:45 +02:00
} ,
{
2024-07-21 13:29:13 +02:00
'name' : 'Post-History Instructions' ,
2023-12-02 19:04:51 +01:00
'system_prompt' : true ,
'role' : 'system' ,
2024-07-21 13:29:13 +02:00
'content' : '' ,
2023-12-02 21:06:57 +01:00
'identifier' : 'jailbreak' ,
2023-05-28 15:52:45 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'chatHistory' ,
'name' : 'Chat History' ,
'system_prompt' : true ,
'marker' : true ,
2023-05-28 15:52:45 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'worldInfoAfter' ,
'name' : 'World Info (after)' ,
'system_prompt' : true ,
'marker' : true ,
2023-05-28 15:52:45 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'worldInfoBefore' ,
'name' : 'World Info (before)' ,
'system_prompt' : true ,
'marker' : true ,
2023-06-02 19:29:34 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'enhanceDefinitions' ,
'role' : 'system' ,
'name' : 'Enhance Definitions' ,
'content' : 'If you have more knowledge of {{char}}, add to the character\'s lore and personality to enhance them but keep the Character Sheet\'s definitions absolute.' ,
'system_prompt' : true ,
'marker' : false ,
2023-06-10 20:09:48 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'charDescription' ,
'name' : 'Char Description' ,
'system_prompt' : true ,
'marker' : true ,
2023-06-10 20:09:48 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'charPersonality' ,
'name' : 'Char Personality' ,
'system_prompt' : true ,
'marker' : true ,
2023-06-10 20:09:48 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'scenario' ,
'name' : 'Scenario' ,
'system_prompt' : true ,
'marker' : true ,
2023-06-10 20:09:48 +02:00
} ,
2023-08-23 20:40:26 +02:00
{
2023-12-02 19:04:51 +01:00
'identifier' : 'personaDescription' ,
'name' : 'Persona Description' ,
'system_prompt' : true ,
'marker' : true ,
2023-08-23 20:40:26 +02:00
} ,
2023-12-02 21:06:57 +01:00
] ,
2023-05-28 15:52:45 +02:00
} ;
2023-07-27 17:49:49 +02:00
const promptManagerDefaultPromptOrders = {
2023-12-02 21:06:57 +01:00
'prompt_order' : [ ] ,
2023-05-28 15:52:45 +02:00
} ;
2023-07-27 17:49:49 +02:00
const promptManagerDefaultPromptOrder = [
2023-06-07 19:04:15 +02:00
{
2023-12-02 19:04:51 +01:00
'identifier' : 'main' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-07 19:04:15 +02:00
} ,
2023-06-03 17:39:14 +02:00
{
2023-12-02 19:04:51 +01:00
'identifier' : 'worldInfoBefore' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-03 17:39:14 +02:00
} ,
2023-08-23 20:40:26 +02:00
{
2023-12-02 19:04:51 +01:00
'identifier' : 'personaDescription' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-08-23 20:40:26 +02:00
} ,
2023-06-03 17:39:14 +02:00
{
2023-12-02 19:04:51 +01:00
'identifier' : 'charDescription' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-10 20:09:48 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'charPersonality' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-10 20:09:48 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'scenario' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-03 17:39:14 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'enhanceDefinitions' ,
2023-12-02 21:06:57 +01:00
'enabled' : false ,
2023-06-03 17:39:14 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'nsfw' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-03 17:39:14 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'worldInfoAfter' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-03 17:39:14 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'dialogueExamples' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-03 17:39:14 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'chatHistory' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
2023-06-03 17:39:14 +02:00
} ,
{
2023-12-02 19:04:51 +01:00
'identifier' : 'jailbreak' ,
2023-12-02 21:06:57 +01:00
'enabled' : true ,
} ,
2023-06-03 17:39:14 +02:00
] ;
2023-06-03 21:25:21 +02:00
export {
2023-12-03 18:14:56 +01:00
PromptManager ,
2023-06-29 19:26:35 +02:00
registerPromptManagerMigration ,
2023-07-20 19:49:38 +02:00
chatCompletionDefaultPrompts ,
2023-07-27 17:49:49 +02:00
promptManagerDefaultPromptOrders ,
2023-12-02 21:06:57 +01:00
Prompt ,
2023-06-03 21:25:21 +02:00
} ;