From 4cf0e18d7647e61b1f209e205958afef9ecf5b62 Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Tue, 30 May 2023 17:35:14 -0500 Subject: [PATCH 1/6] First commit of Objective extension --- public/script.js | 2 + public/scripts/extensions.js | 1 + public/scripts/extensions/objective/index.js | 302 ++++++++++++++++++ .../extensions/objective/manifest.json | 11 + public/scripts/extensions/objective/style.css | 0 readme.md | 1 + 6 files changed, 317 insertions(+) create mode 100644 public/scripts/extensions/objective/index.js create mode 100644 public/scripts/extensions/objective/manifest.json create mode 100644 public/scripts/extensions/objective/style.css diff --git a/public/script.js b/public/script.js index 8f12cfbd9..6f67db42a 100644 --- a/public/script.js +++ b/public/script.js @@ -419,6 +419,7 @@ export const event_types = { MESSAGE_RECEIVED: 'message_received', MESSAGE_EDITED: 'message_edited', IMPERSONATE_READY: 'impersonate_ready', + CHAT_CHANGED: 'chat_id_changed', } export const eventSource = new EventEmitter(); @@ -3613,6 +3614,7 @@ async function getChatResult() { if (chat.length === 1) { await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1)); } + await eventSource.emit(event_types.CHAT_CHANGED, (getCurrentChatId())); } async function openCharacterChat(file_name) { diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 2fc5fb18c..2bc4e88ad 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -29,6 +29,7 @@ const extension_settings = { sd: {}, chromadb: {}, translate: {}, + objective: {}, }; let modules = []; diff --git a/public/scripts/extensions/objective/index.js b/public/scripts/extensions/objective/index.js new file mode 100644 index 000000000..55bdb0fb5 --- /dev/null +++ b/public/scripts/extensions/objective/index.js @@ -0,0 +1,302 @@ +import { callPopup, extension_prompt_types } from "../../../script.js"; +import { getContext, extension_settings } from "../../extensions.js"; +import { + substituteParams, + eventSource, + event_types, + saveSettingsDebounced +} from "../../../script.js"; + +const MODULE_NAME = "Objective" +const UPDATE_INTERVAL = 1000; +let globalObjective = "" +let globalTasks = [] +let currentChatId = "" +let currentTask = {} +let checkCounter = 0 + + +const objectivePrompts = { + "createTask": `Pause your roleplay and generate a list of tasks to complete an objective. Your next response must be formatted as a numbered list of plain text entries. Do not include anything but the numbered list. The list must be prioritized in the order that tasks must be completed. + + The objective that you must make a numbered task list for is: {{objective}}. + The tasks created should take into account the character traits of {{char}}. These tasks may or may not involve {{user}} + + Given an example objective of 'Make me a four course dinner', here is an example output: + 1. Determine what the courses will be + 2. Find recipes for each course + 3. Go shopping for supplies with {{user}} + 4. Cook the food + 5. Get {{user}} to set the table + 6. Serve the food + 7. Enjoy eating the meal with {{user}} + `, + "checkTaskCompleted": `Pause your roleplay. Determine if this task is completed: {{task}}. + To do this, examine the most recent messages. Your response must only contain either true or false, nothing other words. + Example output: + true + ` +} + +const injectPrompts = { + "task": "Your current task is {{task}}. Balance existing roleplay with completing this task." +} + +// Background prompt generation +async function generateQuietPrompt(quiet_prompt) { + return await new Promise( + async function promptPromise(resolve, reject) { + try { + await getContext().generate('quiet', { resolve, reject, quiet_prompt, force_name2: true, }); + } + catch { + reject(); + } + }); +} + +//###############################// +//# Task Management #// +//###############################// + +// Accepts optional position. Defaults to adding to end of list. +function addTask(description, position=null) { + position = position ? position != null : position = globalTasks.length + globalTasks.splice(position, 0, { + "description": description, + "completed": false + }) +} + +// Get a task either by index or task description. Return current task if none specified +function getTask(index=null, taskDescription=null){ + let task = {} + if (index == null && taskDescription==null) { + task = currentTask + } else if (index != null){ + task = globalObjective[index] + } else if (taskDescription != null){ + task = globalTasks.find(task => { + return true ? task.description == description : false + }) + } + return task +} + +// Complete the current task, setting next task to next incomplete task +function completeTask(task) { + task.completed = true + console.info(`Task successfully completed: ${JSON.stringify(task)}`) + setCurrentTask() + updateUiTaskList() +} + +// Call Quiet Generate to create task list using character context, then convert to tasks. Should not be called much. +async function generateTasks() { + const prompt = substituteParams(objectivePrompts["createTask"].replace(/{{objective}}/gi, globalObjective)); + console.log(`Generating tasks for objective with prompt`) + const taskResponse = await generateQuietPrompt(prompt) + globalTasks = [] + const numberedListPattern = /^\d+\./ + + // Add numbered tasks, store them without the numbers. + for (const task of taskResponse.split('\n')) { + if (task.match(numberedListPattern) != null) { + addTask(task.replace(numberedListPattern,'').trim()) + } + } + updateUiTaskList() + console.info(`Response for Objective: '${globalObjective}' was \n'${taskResponse}', \nwhich created tasks \n${JSON.stringify(globalTasks, null, 2)} `) +} + +// Call Quiet Generate to check if a task is completed +async function checkTaskCompleted() { + // Make sure there are tasks + if (globalObjective == "" || globalTasks.length == 0){ + return + } + + // Check only at specified interval + if (checkCounter <= $('#objective-check-frequency').val()){ + return + } + checkCounter = 0 + + const prompt = substituteParams(objectivePrompts["checkTaskCompleted"].replace(/{{task}}/gi, currentTask.description)); + const taskResponse = (await generateQuietPrompt(prompt)).toLowerCase() + + // Check response if task complete + if (taskResponse.includes("true")){ + console.info(`Character determined task '${JSON.stringify(currentTask)} is completed.`) + completeTask(getTask()) + } else if (!(taskResponse.includes("false"))) { + console.warn(`checkTaskCompleted response did not contain true or false. taskResponse: ${taskResponse}`) + } else { + console.debug(`taskResponse: ${taskResponse}`) + } + } + + +// Set a task in extensionPrompt context. Defaults to first incomplete +function setCurrentTask(index=null) { + const context = getContext(); + currentTask = {} + + // Default to selecting first incomplete task if not given index, else no task + if (index==null){ + currentTask = globalTasks.find(task=>{return true? !task.completed: false}) + } else if (index < globalTasks.length && index > 0){ + currentTask = globalTasks[index] + } + + // Inject task into extension prompt context + if (Object.keys(currentTask).length > 0){ + context.setExtensionPrompt( + MODULE_NAME, injectPrompts["task"].replace(/{{task}}/gi, currentTask.description), 1, $('#objective-chat-depth').val() + ); + console.info(`Current task in context.extensionPrompts.Objective is ${JSON.stringify(context.extensionPrompts.Objective)}`) + } else { + context.setExtensionPrompt(MODULE_NAME,'') + console.info(`No current task`) + } +} + +//###############################// +//# UI AND Settings #// +//###############################// + + +const defaultSettings = { + objective: "", + tasks: [], + chatDepth: 2, + checkFrequency:3, +} + +// Convenient single call +function resetState(){ + checkCounter = 0 + loadSettings(); +} + +function debugObjectiveFunction(){ + console.log({ + "globalObjective": globalObjective, + "globalTasks": globalTasks, + "currentChatId": currentChatId, + "currentTask": currentTask, + "checkCounter": checkCounter, + "currentChatId": currentChatId, + "extension_settings": extension_settings.objective[currentChatId], + }) +} + +window.debugObjectiveFunction = debugObjectiveFunction + +// Create user-friendly task string +function updateUiTaskList() { + let taskPrettyString = "" + for (const index in globalTasks) { + if (globalTasks[index].completed == true){ + taskPrettyString += '[*] ' + } else { + taskPrettyString += '[ ] ' + } + taskPrettyString += `${Number(index) + 1}. ` + taskPrettyString += globalTasks[index].description + if (index != globalTasks.length-1) { + taskPrettyString += '\n' + } + } + $('#objective-tasks').text(taskPrettyString) +} + +// Trigger creation of new tasks with given objective. +async function onGenerateObjectiveClick() { + globalObjective = $('#objective-text').val() + await generateTasks() + extension_settings.objective[currentChatId].objective = globalObjective + extension_settings.objective[currentChatId].tasks = globalTasks + saveSettingsDebounced() +} + +// Update extension prompts +function onChatDepthInput() { + if (currentChatId == ""){ + currentChatId = getContext().chatId + } + extension_settings.objective[currentChatId].chatDepth = $('#objective-chat-depth').val() + setCurrentTask() // Ensure extension prompt is updated + saveSettingsDebounced() +} + +function onCheckFrequencyInput() { + if (currentChatId == ""){ + currentChatId = getContext().chatId + } + extension_settings.objective[currentChatId].checkFrequency = $('#objective-check-frequency').val() + saveSettingsDebounced() +} + +function loadSettings() { + // Load/Init settings for chatId + currentChatId = getContext().chatId + + // Bail on home screen + if (currentChatId == undefined) { + return + } + if (!(currentChatId in extension_settings.objective)) { + extension_settings.objective[currentChatId] = {} + Object.assign(extension_settings.objective[currentChatId], defaultSettings) + } + + // Update globals + globalObjective = extension_settings.objective[currentChatId].objective + globalTasks = extension_settings.objective[currentChatId].tasks + + // Update UI elements + $("#objective-text").text(globalObjective) + updateUiTaskList() + $('#objective-chat-depth').val(extension_settings.objective[currentChatId].chatDepth) + $('#objective-check-frequency').val(extension_settings.objective[currentChatId].checkFrequency) +} + +jQuery(() => { + const settingsHtml = ` +
+
+
+ Objective +
+
+
+ + + + + + + + + + +
+
`; + + $('#extensions_settings').append(settingsHtml); + $('#objective-generate').on('click', onGenerateObjectiveClick) + $('#objective-chat-depth').on('input',onChatDepthInput) + $("#objective-check-frequency").on('input',onCheckFrequencyInput) + loadSettings() + + eventSource.on(event_types.CHAT_CHANGED, () => { + resetState() + }); + + eventSource.on(event_types.MESSAGE_RECEIVED, () => { + checkTaskCompleted(); + checkCounter += 1 + setCurrentTask(); + }); +}); diff --git a/public/scripts/extensions/objective/manifest.json b/public/scripts/extensions/objective/manifest.json new file mode 100644 index 000000000..c7f9513ed --- /dev/null +++ b/public/scripts/extensions/objective/manifest.json @@ -0,0 +1,11 @@ +{ + "display_name": "Objective", + "loading_order": 5, + "requires": [], + "optional": [], + "js": "index.js", + "css": "style.css", + "author": "Ouoertheo", + "version": "0.0.1", + "homePage": "" +} \ No newline at end of file diff --git a/public/scripts/extensions/objective/style.css b/public/scripts/extensions/objective/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/readme.md b/readme.md index 0f055f31a..835c8a381 100644 --- a/readme.md +++ b/readme.md @@ -299,3 +299,4 @@ GNU Affero General Public License for more details.** * AI Horde client library by ZeldaFan0225: https://github.com/ZeldaFan0225/ai_horde * Linux startup script by AlpinDale * Thanks paniphons for providing a FAQ document +* TTS and Objective extensions by Ouoertheo \ No newline at end of file From 00b11d4d44caca300537708ead8ad45ead775b69 Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Tue, 30 May 2023 20:13:29 -0500 Subject: [PATCH 2/6] Add counter, fix some bugs --- public/scripts/extensions/objective/index.js | 69 +++++++++++-------- public/scripts/extensions/objective/style.css | 4 ++ 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/public/scripts/extensions/objective/index.js b/public/scripts/extensions/objective/index.js index 55bdb0fb5..47b53a2e4 100644 --- a/public/scripts/extensions/objective/index.js +++ b/public/scripts/extensions/objective/index.js @@ -19,7 +19,7 @@ let checkCounter = 0 const objectivePrompts = { "createTask": `Pause your roleplay and generate a list of tasks to complete an objective. Your next response must be formatted as a numbered list of plain text entries. Do not include anything but the numbered list. The list must be prioritized in the order that tasks must be completed. - The objective that you must make a numbered task list for is: {{objective}}. + The objective that you must make a numbered task list for is: [{{objective}}]. The tasks created should take into account the character traits of {{char}}. These tasks may or may not involve {{user}} Given an example objective of 'Make me a four course dinner', here is an example output: @@ -31,7 +31,7 @@ const objectivePrompts = { 6. Serve the food 7. Enjoy eating the meal with {{user}} `, - "checkTaskCompleted": `Pause your roleplay. Determine if this task is completed: {{task}}. + "checkTaskCompleted": `Pause your roleplay. Determine if this task is completed: [{{task}}]. To do this, examine the most recent messages. Your response must only contain either true or false, nothing other words. Example output: true @@ -39,7 +39,7 @@ const objectivePrompts = { } const injectPrompts = { - "task": "Your current task is {{task}}. Balance existing roleplay with completing this task." + "task": "Your current task is [{{task}}]. Balance existing roleplay with completing this task." } // Background prompt generation @@ -66,6 +66,7 @@ function addTask(description, position=null) { "description": description, "completed": false }) + saveState() } // Get a task either by index or task description. Return current task if none specified @@ -89,6 +90,7 @@ function completeTask(task) { console.info(`Task successfully completed: ${JSON.stringify(task)}`) setCurrentTask() updateUiTaskList() + saveState() } // Call Quiet Generate to create task list using character context, then convert to tasks. Should not be called much. @@ -117,10 +119,10 @@ async function checkTaskCompleted() { } // Check only at specified interval - if (checkCounter <= $('#objective-check-frequency').val()){ + if (checkCounter >= 0){ return } - checkCounter = 0 + checkCounter = $('#objective-check-frequency').val() const prompt = substituteParams(objectivePrompts["checkTaskCompleted"].replace(/{{task}}/gi, currentTask.description)); const taskResponse = (await generateQuietPrompt(prompt)).toLowerCase() @@ -132,7 +134,7 @@ async function checkTaskCompleted() { } else if (!(taskResponse.includes("false"))) { console.warn(`checkTaskCompleted response did not contain true or false. taskResponse: ${taskResponse}`) } else { - console.debug(`taskResponse: ${taskResponse}`) + console.debug(`Checked task completion. taskResponse: ${taskResponse}`) } } @@ -159,6 +161,7 @@ function setCurrentTask(index=null) { context.setExtensionPrompt(MODULE_NAME,'') console.info(`No current task`) } + saveState() } //###############################// @@ -175,23 +178,29 @@ const defaultSettings = { // Convenient single call function resetState(){ - checkCounter = 0 loadSettings(); } -function debugObjectiveFunction(){ - console.log({ - "globalObjective": globalObjective, - "globalTasks": globalTasks, - "currentChatId": currentChatId, - "currentTask": currentTask, - "checkCounter": checkCounter, - "currentChatId": currentChatId, - "extension_settings": extension_settings.objective[currentChatId], - }) +function saveState(){ + extension_settings.objective[currentChatId].objective = globalObjective + extension_settings.objective[currentChatId].tasks = globalTasks + extension_settings.objective[currentChatId].checkFrequency = $('#objective-check-frequency').val() + extension_settings.objective[currentChatId].chatDepth = $('#objective-chat-depth').val() + saveSettingsDebounced() } -window.debugObjectiveFunction = debugObjectiveFunction +function debugObjectiveExtension(){ + console.log(JSON.stringify({ + "currentTask": currentTask, + "currentChatId": currentChatId, + "checkCounter": checkCounter, + "globalObjective": globalObjective, + "globalTasks": globalTasks, + "extension_settings": extension_settings.objective[currentChatId], + }, null, 2)) +} + +window.debugObjectiveExtension = debugObjectiveExtension // Create user-friendly task string function updateUiTaskList() { @@ -215,9 +224,7 @@ function updateUiTaskList() { async function onGenerateObjectiveClick() { globalObjective = $('#objective-text').val() await generateTasks() - extension_settings.objective[currentChatId].objective = globalObjective - extension_settings.objective[currentChatId].tasks = globalTasks - saveSettingsDebounced() + saveState() } // Update extension prompts @@ -225,17 +232,15 @@ function onChatDepthInput() { if (currentChatId == ""){ currentChatId = getContext().chatId } - extension_settings.objective[currentChatId].chatDepth = $('#objective-chat-depth').val() + saveState() setCurrentTask() // Ensure extension prompt is updated - saveSettingsDebounced() } function onCheckFrequencyInput() { if (currentChatId == ""){ currentChatId = getContext().chatId } - extension_settings.objective[currentChatId].checkFrequency = $('#objective-check-frequency').val() - saveSettingsDebounced() + saveState() } function loadSettings() { @@ -254,12 +259,15 @@ function loadSettings() { // Update globals globalObjective = extension_settings.objective[currentChatId].objective globalTasks = extension_settings.objective[currentChatId].tasks + checkCounter = extension_settings.objective[currentChatId].checkFrequency // Update UI elements + $('#objective-counter').text(checkCounter) $("#objective-text").text(globalObjective) updateUiTaskList() $('#objective-chat-depth').val(extension_settings.objective[currentChatId].chatDepth) $('#objective-check-frequency').val(extension_settings.objective[currentChatId].checkFrequency) + setCurrentTask() } jQuery(() => { @@ -277,10 +285,10 @@ jQuery(() => { - - - - +
+ +
+ Messages until next task completion check 0
`; @@ -296,7 +304,8 @@ jQuery(() => { eventSource.on(event_types.MESSAGE_RECEIVED, () => { checkTaskCompleted(); - checkCounter += 1 + checkCounter -= 1 setCurrentTask(); + $('#objective-counter').text(checkCounter) }); }); diff --git a/public/scripts/extensions/objective/style.css b/public/scripts/extensions/objective/style.css index e69de29bb..5e722ed24 100644 --- a/public/scripts/extensions/objective/style.css +++ b/public/scripts/extensions/objective/style.css @@ -0,0 +1,4 @@ +#objective-counter { + font-weight: 600; + color: orange; +} From 226c66464a2e18dc68711b77bd767df97b6e9610 Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Wed, 31 May 2023 00:09:33 -0500 Subject: [PATCH 3/6] Add better UI for tasks. Fix bug --- public/scripts/extensions/objective/index.js | 79 ++++++++++++++----- public/scripts/extensions/objective/style.css | 7 ++ 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/public/scripts/extensions/objective/index.js b/public/scripts/extensions/objective/index.js index 47b53a2e4..a47e4a275 100644 --- a/public/scripts/extensions/objective/index.js +++ b/public/scripts/extensions/objective/index.js @@ -8,7 +8,7 @@ import { } from "../../../script.js"; const MODULE_NAME = "Objective" -const UPDATE_INTERVAL = 1000; + let globalObjective = "" let globalTasks = [] let currentChatId = "" @@ -20,7 +20,7 @@ const objectivePrompts = { "createTask": `Pause your roleplay and generate a list of tasks to complete an objective. Your next response must be formatted as a numbered list of plain text entries. Do not include anything but the numbered list. The list must be prioritized in the order that tasks must be completed. The objective that you must make a numbered task list for is: [{{objective}}]. - The tasks created should take into account the character traits of {{char}}. These tasks may or may not involve {{user}} + The tasks created should take into account the character traits of {{char}}. These tasks may or may not involve {{user}} directly. Be sure to include the objective as the final task. Given an example objective of 'Make me a four course dinner', here is an example output: 1. Determine what the courses will be @@ -147,6 +147,9 @@ function setCurrentTask(index=null) { // Default to selecting first incomplete task if not given index, else no task if (index==null){ currentTask = globalTasks.find(task=>{return true? !task.completed: false}) + if (currentTask == undefined){ + currentTask = {} + } } else if (index < globalTasks.length && index > 0){ currentTask = globalTasks[index] } @@ -164,6 +167,8 @@ function setCurrentTask(index=null) { saveState() } + + //###############################// //# UI AND Settings #// //###############################// @@ -176,11 +181,12 @@ const defaultSettings = { checkFrequency:3, } -// Convenient single call +// Convenient single call. Not much at the moment. function resetState(){ loadSettings(); } +// function saveState(){ extension_settings.objective[currentChatId].objective = globalObjective extension_settings.objective[currentChatId].tasks = globalTasks @@ -189,6 +195,7 @@ function saveState(){ saveSettingsDebounced() } +// Dump core state function debugObjectiveExtension(){ console.log(JSON.stringify({ "currentTask": currentTask, @@ -202,22 +209,55 @@ function debugObjectiveExtension(){ window.debugObjectiveExtension = debugObjectiveExtension -// Create user-friendly task string + +// Add a single task to the UI and attach event listeners for user edits +function addUiTask(taskIndex, taskComplete, taskDescription){ + let template = ` +
+ {{task_index}} + + {{task_description}} +

+ ` + + // Define id values + const taskLabelId = `objective-task-label-${taskIndex}` + const taskCompleteId = `objective-task-complete-${taskIndex}` + const taskDescriptionId = `objective-task-description-${taskIndex}` + + // Assign id values and populate current state + template = template.replace(/{{task_index}}/gi,taskIndex) + template = template.replace(/{{task_description}}/gi,taskDescription) + template = template.replace(/{{task_label_id}}/gi,taskLabelId) + template = template.replace(/{{task_complete_id}}/gi,taskCompleteId) + template = template.replace(/{{task_description_id}}/gi,taskDescriptionId) + $(`#${taskCompleteId}`).prop('checked',taskComplete) + + // Add the filled out template + $('#objective-tasks').append(template) + + // Add event listeners + $(`#${taskDescriptionId}`).on('keyup', event => { + const index = Number(event.target.id[event.target.id.length - 1]) + globalTasks[index].description = event.target.textContent + }); + $(`#${taskCompleteId}`).on('click', event => { + const index = Number(event.target.id[event.target.id.length - 1]) + JSON.parse("TRUE".toLowerCase()) + globalTasks[index].completed = JSON.parse(event.target.getAttribute("checked")) + }); +} + +// Populate UI task list function updateUiTaskList() { - let taskPrettyString = "" + $('#objective-tasks').empty() for (const index in globalTasks) { - if (globalTasks[index].completed == true){ - taskPrettyString += '[*] ' - } else { - taskPrettyString += '[ ] ' - } - taskPrettyString += `${Number(index) + 1}. ` - taskPrettyString += globalTasks[index].description - if (index != globalTasks.length-1) { - taskPrettyString += '\n' - } + addUiTask( + index, + globalTasks[index].completed, + globalTasks[index].description + ) } - $('#objective-tasks').text(taskPrettyString) } // Trigger creation of new tasks with given objective. @@ -236,6 +276,7 @@ function onChatDepthInput() { setCurrentTask() // Ensure extension prompt is updated } +// Update how often we check for task completion function onCheckFrequencyInput() { if (currentChatId == ""){ currentChatId = getContext().chatId @@ -282,8 +323,7 @@ jQuery(() => { - - +

@@ -303,6 +343,9 @@ jQuery(() => { }); eventSource.on(event_types.MESSAGE_RECEIVED, () => { + if (currentChatId == undefined){ + return + } checkTaskCompleted(); checkCounter -= 1 setCurrentTask(); diff --git a/public/scripts/extensions/objective/style.css b/public/scripts/extensions/objective/style.css index 5e722ed24..c14ab4e10 100644 --- a/public/scripts/extensions/objective/style.css +++ b/public/scripts/extensions/objective/style.css @@ -2,3 +2,10 @@ font-weight: 600; color: orange; } + +[id^='objective-task-label-'] { + border: 1px solid var(--white30a); + border-radius: 10px; + padding: 7px; + align-items: center; +} \ No newline at end of file From e68dd96f24b8701a5c5daab9197973cc6bd98819 Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Wed, 31 May 2023 00:56:28 -0500 Subject: [PATCH 4/6] More bug fix --- public/scripts/extensions/objective/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/public/scripts/extensions/objective/index.js b/public/scripts/extensions/objective/index.js index a47e4a275..a9277fe07 100644 --- a/public/scripts/extensions/objective/index.js +++ b/public/scripts/extensions/objective/index.js @@ -114,7 +114,7 @@ async function generateTasks() { // Call Quiet Generate to check if a task is completed async function checkTaskCompleted() { // Make sure there are tasks - if (globalObjective == "" || globalTasks.length == 0){ + if (currentTask == {}){ return } @@ -231,21 +231,21 @@ function addUiTask(taskIndex, taskComplete, taskDescription){ template = template.replace(/{{task_label_id}}/gi,taskLabelId) template = template.replace(/{{task_complete_id}}/gi,taskCompleteId) template = template.replace(/{{task_description_id}}/gi,taskDescriptionId) - $(`#${taskCompleteId}`).prop('checked',taskComplete) // Add the filled out template $('#objective-tasks').append(template) - // Add event listeners + // Add event listeners and set properties + $(`#${taskCompleteId}`).prop('checked',taskComplete) + $(`#${taskCompleteId}`).on('click', event => { + const index = Number(event.target.id[event.target.id.length - 1]) + globalTasks[index].completed = event.target.checked + setCurrentTask() + }); $(`#${taskDescriptionId}`).on('keyup', event => { const index = Number(event.target.id[event.target.id.length - 1]) globalTasks[index].description = event.target.textContent }); - $(`#${taskCompleteId}`).on('click', event => { - const index = Number(event.target.id[event.target.id.length - 1]) - JSON.parse("TRUE".toLowerCase()) - globalTasks[index].completed = JSON.parse(event.target.getAttribute("checked")) - }); } // Populate UI task list From bdcb3e52ff272d5b7aa353ca73bda79cf9cd27b5 Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Wed, 31 May 2023 08:01:07 -0500 Subject: [PATCH 5/6] Add some instructions. Code readability --- public/scripts/extensions/objective/index.js | 118 ++++++++---------- public/scripts/extensions/objective/style.css | 5 +- 2 files changed, 55 insertions(+), 68 deletions(-) diff --git a/public/scripts/extensions/objective/index.js b/public/scripts/extensions/objective/index.js index a9277fe07..5ab8bd83a 100644 --- a/public/scripts/extensions/objective/index.js +++ b/public/scripts/extensions/objective/index.js @@ -136,36 +136,33 @@ async function checkTaskCompleted() { } else { console.debug(`Checked task completion. taskResponse: ${taskResponse}`) } - } +} // Set a task in extensionPrompt context. Defaults to first incomplete -function setCurrentTask(index=null) { +function setCurrentTask(index = null) { const context = getContext(); - currentTask = {} - - // Default to selecting first incomplete task if not given index, else no task - if (index==null){ - currentTask = globalTasks.find(task=>{return true? !task.completed: false}) - if (currentTask == undefined){ - currentTask = {} - } - } else if (index < globalTasks.length && index > 0){ - currentTask = globalTasks[index] + let currentTask = {}; + + if (index === null) { + currentTask = globalTasks.find(task => !task.completed) || {}; + } else if (index >= 0 && index < globalTasks.length) { + currentTask = globalTasks[index]; } - - // Inject task into extension prompt context - if (Object.keys(currentTask).length > 0){ - context.setExtensionPrompt( - MODULE_NAME, injectPrompts["task"].replace(/{{task}}/gi, currentTask.description), 1, $('#objective-chat-depth').val() - ); - console.info(`Current task in context.extensionPrompts.Objective is ${JSON.stringify(context.extensionPrompts.Objective)}`) + + const { description } = currentTask; + const injectPromptsTask = injectPrompts["task"].replace(/{{task}}/gi, description); + + if (description) { + context.setExtensionPrompt(MODULE_NAME, injectPromptsTask, 1, $('#objective-chat-depth').val()); + console.info(`Current task in context.extensionPrompts.Objective is ${JSON.stringify(context.extensionPrompts.Objective)}`); } else { - context.setExtensionPrompt(MODULE_NAME,'') - console.info(`No current task`) + context.setExtensionPrompt(MODULE_NAME, ''); + console.info(`No current task`); } - saveState() -} + + saveState(); + } @@ -188,6 +185,9 @@ function resetState(){ // function saveState(){ + if (currentChatId == ""){ + currentChatId = getContext().chatId + } extension_settings.objective[currentChatId].objective = globalObjective extension_settings.objective[currentChatId].tasks = globalTasks extension_settings.objective[currentChatId].checkFrequency = $('#objective-check-frequency').val() @@ -211,42 +211,30 @@ window.debugObjectiveExtension = debugObjectiveExtension // Add a single task to the UI and attach event listeners for user edits -function addUiTask(taskIndex, taskComplete, taskDescription){ - let template = ` -
- {{task_index}} - - {{task_description}} -

- ` - - // Define id values - const taskLabelId = `objective-task-label-${taskIndex}` - const taskCompleteId = `objective-task-complete-${taskIndex}` - const taskDescriptionId = `objective-task-description-${taskIndex}` - - // Assign id values and populate current state - template = template.replace(/{{task_index}}/gi,taskIndex) - template = template.replace(/{{task_description}}/gi,taskDescription) - template = template.replace(/{{task_label_id}}/gi,taskLabelId) - template = template.replace(/{{task_complete_id}}/gi,taskCompleteId) - template = template.replace(/{{task_description_id}}/gi,taskDescriptionId) - +function addUiTask(taskIndex, taskComplete, taskDescription) { + const template = ` +
+ ${taskIndex} + + ${taskDescription} +

+ `; + // Add the filled out template - $('#objective-tasks').append(template) - + $('#objective-tasks').append(template); + // Add event listeners and set properties - $(`#${taskCompleteId}`).prop('checked',taskComplete) - $(`#${taskCompleteId}`).on('click', event => { - const index = Number(event.target.id[event.target.id.length - 1]) - globalTasks[index].completed = event.target.checked - setCurrentTask() + $(`#objective-task-complete-${taskIndex}`).prop('checked', taskComplete); + $(`#objective-task-complete-${taskIndex}`).on('click', event => { + const index = Number(event.target.id.split('-').pop()); + globalTasks[index].completed = event.target.checked; + setCurrentTask(); }); - $(`#${taskDescriptionId}`).on('keyup', event => { - const index = Number(event.target.id[event.target.id.length - 1]) - globalTasks[index].description = event.target.textContent + $(`#objective-task-description-${taskIndex}`).on('keyup', event => { + const index = Number(event.target.id.split('-').pop()); + globalTasks[index].description = event.target.textContent; }); -} + } // Populate UI task list function updateUiTaskList() { @@ -269,18 +257,12 @@ async function onGenerateObjectiveClick() { // Update extension prompts function onChatDepthInput() { - if (currentChatId == ""){ - currentChatId = getContext().chatId - } saveState() setCurrentTask() // Ensure extension prompt is updated } // Update how often we check for task completion function onCheckFrequencyInput() { - if (currentChatId == ""){ - currentChatId = getContext().chatId - } saveState() } @@ -320,15 +302,17 @@ jQuery(() => {
- + + Automatically generate tasks for an objective. Will take a moment and populate tasks below

-
- Messages until next task completion check 0 +
+ Messages until next AI task completion check 0 0 to disable auto completion checks +
`; @@ -346,8 +330,10 @@ jQuery(() => { if (currentChatId == undefined){ return } - checkTaskCompleted(); - checkCounter -= 1 + if ($("#objective-check-frequency").val() > 0) { + checkTaskCompleted(); + checkCounter -= 1 + } setCurrentTask(); $('#objective-counter').text(checkCounter) }); diff --git a/public/scripts/extensions/objective/style.css b/public/scripts/extensions/objective/style.css index c14ab4e10..05c4eed93 100644 --- a/public/scripts/extensions/objective/style.css +++ b/public/scripts/extensions/objective/style.css @@ -3,9 +3,10 @@ color: orange; } -[id^='objective-task-label-'] { +/* [id^='objective-task-label-'] { border: 1px solid var(--white30a); border-radius: 10px; padding: 7px; align-items: center; -} \ No newline at end of file +} */ + From 01b629ca9f21d866fd350cb08319e4d5d2c3c384 Mon Sep 17 00:00:00 2001 From: ouoertheo Date: Wed, 31 May 2023 16:22:49 -0500 Subject: [PATCH 6/6] Ability to hide tasks. --- public/scripts/extensions/objective/index.js | 25 +++++++++++++------ public/scripts/extensions/objective/style.css | 10 +------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/public/scripts/extensions/objective/index.js b/public/scripts/extensions/objective/index.js index 5ab8bd83a..cc3fef741 100644 --- a/public/scripts/extensions/objective/index.js +++ b/public/scripts/extensions/objective/index.js @@ -113,13 +113,13 @@ async function generateTasks() { // Call Quiet Generate to check if a task is completed async function checkTaskCompleted() { - // Make sure there are tasks - if (currentTask == {}){ + // Make sure there are tasks and check is enabled + if (currentTask == {} || $('#objective-check-frequency').val() == 0){ return } // Check only at specified interval - if (checkCounter >= 0){ + if (checkCounter > 0){ return } checkCounter = $('#objective-check-frequency').val() @@ -176,6 +176,7 @@ const defaultSettings = { tasks: [], chatDepth: 2, checkFrequency:3, + hideTasks: false } // Convenient single call. Not much at the moment. @@ -192,6 +193,7 @@ function saveState(){ extension_settings.objective[currentChatId].tasks = globalTasks extension_settings.objective[currentChatId].checkFrequency = $('#objective-check-frequency').val() extension_settings.objective[currentChatId].chatDepth = $('#objective-chat-depth').val() + extension_settings.objective[currentChatId].hideTasks = $('#objective-hide-tasks').prop('checked') saveSettingsDebounced() } @@ -266,6 +268,11 @@ function onCheckFrequencyInput() { saveState() } +function onHideTasksInput(){ + $('#objective-tasks').prop('hidden',$('#objective-hide-tasks').prop('checked')) + saveState() +} + function loadSettings() { // Load/Init settings for chatId currentChatId = getContext().chatId @@ -290,6 +297,8 @@ function loadSettings() { updateUiTaskList() $('#objective-chat-depth').val(extension_settings.objective[currentChatId].chatDepth) $('#objective-check-frequency').val(extension_settings.objective[currentChatId].checkFrequency) + $('#objective-hide-tasks').prop('checked',extension_settings.objective[currentChatId].hideTasks) + onHideTasksInput() setCurrentTask() } @@ -304,14 +313,15 @@ jQuery(() => {
- - Automatically generate tasks for an objective. Will take a moment and populate tasks below +
+

-
- Messages until next AI task completion check 0 0 to disable auto completion checks + (0 = disabled)
+ Messages until next AI task completion check 0
`; @@ -320,6 +330,7 @@ jQuery(() => { $('#objective-generate').on('click', onGenerateObjectiveClick) $('#objective-chat-depth').on('input',onChatDepthInput) $("#objective-check-frequency").on('input',onCheckFrequencyInput) + $('#objective-hide-tasks').on('click', onHideTasksInput) loadSettings() eventSource.on(event_types.CHAT_CHANGED, () => { diff --git a/public/scripts/extensions/objective/style.css b/public/scripts/extensions/objective/style.css index 05c4eed93..6cfd8f419 100644 --- a/public/scripts/extensions/objective/style.css +++ b/public/scripts/extensions/objective/style.css @@ -1,12 +1,4 @@ #objective-counter { font-weight: 600; color: orange; -} - -/* [id^='objective-task-label-'] { - border: 1px solid var(--white30a); - border-radius: 10px; - padding: 7px; - align-items: center; -} */ - +} \ No newline at end of file