From d17705d2951e13972053418486a597957ec3b14e Mon Sep 17 00:00:00 2001 From: Erik Jespersen <42016062+Mookse@users.noreply.github.com> Date: Sun, 30 Jun 2024 22:53:26 -0400 Subject: [PATCH] 241 version 0012 updates (#247) * 231 version 0010 updates (#239) (#240) * 20240604 @Mookse - teams() route - teams(teamId) returns a fully formed team from server with new bots (when instructions provided) * 20240604 @Mookse - fix bot-bar `setActiveBot` - cosmetic * 20240604 @Mookse - fix duplicate team members return * 20240604 @Mookse - botBar reordered * 20240605 @Mookse - teams must be active * 20240605 @Mookse - default mods * 20240605 @Mookse - create (non-custom) bot frontend - diary updates * 20240605 @Mookse - imagining `share memory` * 20240606 @Mookse - story updates * 20240606 @Mookse - minor cosmetics * 20240606 @Mookse - fetchShadows() - ignite shadow: member-version (wip) * 20240607 @Mookse - `shadow` initial endpoint * 20240607 @Mookse - add processingBotId to payload (so that frontend can determine if it should setActive) * 20240607 @Mookse - front-end receives message about updating from agent shadow * 20240607 @Mookse - shadow cosmetics * 20240607 @Mookse - biographer openai function definitions * 20240607 @Mookse - now _that's_ an error * 20240607 @Mookse - updateSummary() **note**: wip as thread got stopped * 20240608 @Mookse - updates summary * 20240608 @Mookse - remove dataset after shadow triggered - add `proxy` endpoint to shadow ideas * 20240608 @Mookse - `globals.getGPTJavascriptFunction`: `getSummary`, `updateSummary` - `updateBotInstructions` route - on setActiveBot checks versions and updates as needed * 20240609 @Mookse - cosmetic * 20240609 @Mookse stable wip kicking off memory is correct, need tuning of instructions or alternate scene-stepwise motion, no problem small error left in frontend for more testing but time to save * 20240618 @Mookse - cosmetic: file under "the right evocation can make all the difference" * 20240618 @Mookse - cosmetic in dribs and drabs * 20240618 @Mookse - frontend 'next' fix * version 0011 updates (#244) * 231 version 0010 updates (#239) * 20240604 @Mookse - teams() route - teams(teamId) returns a fully formed team from server with new bots (when instructions provided) * 20240604 @Mookse - fix bot-bar `setActiveBot` - cosmetic * 20240604 @Mookse - fix duplicate team members return * 20240604 @Mookse - botBar reordered * 20240605 @Mookse - teams must be active * 20240605 @Mookse - default mods * 20240605 @Mookse - create (non-custom) bot frontend - diary updates * 20240605 @Mookse - imagining `share memory` * 20240606 @Mookse - story updates * 20240606 @Mookse - minor cosmetics * 20240606 @Mookse - fetchShadows() - ignite shadow: member-version (wip) * 20240607 @Mookse - `shadow` initial endpoint * 20240607 @Mookse - add processingBotId to payload (so that frontend can determine if it should setActive) * 20240607 @Mookse - front-end receives message about updating from agent shadow * 20240607 @Mookse - shadow cosmetics * 20240607 @Mookse - biographer openai function definitions * 20240607 @Mookse - now _that's_ an error * 20240607 @Mookse - updateSummary() **note**: wip as thread got stopped * 20240608 @Mookse - updates summary * 20240608 @Mookse - remove dataset after shadow triggered - add `proxy` endpoint to shadow ideas * 20240608 @Mookse - `globals.getGPTJavascriptFunction`: `getSummary`, `updateSummary` - `updateBotInstructions` route - on setActiveBot checks versions and updates as needed * 20240609 @Mookse - cosmetic * 20240609 @Mookse stable wip kicking off memory is correct, need tuning of instructions or alternate scene-stepwise motion, no problem small error left in frontend for more testing but time to save * 20240618 @Mookse - cosmetic: file under "the right evocation can make all the difference" * 20240618 @Mookse - cosmetic in dribs and drabs * 20240618 @Mookse - frontend 'next' fix * 20240621 @Mookse - redirect logged in members to `/members` * 20240626 @Mookse - registration -> createAccount with Q * 20240626 @Mookse - hostedMembers fix * 20240626 @Mookse - avatar document name fix * 20240626 @Mookse - instruction change * 20240626 @Mookse - page-loader * 20240626 @Mookse - special pages fix --------- Signed-off-by: Erik Jespersen <42016062+Mookse@users.noreply.github.com> * Version 0.0.11 (#243) * 231 version 0010 updates (#239) * 20240604 @Mookse - teams() route - teams(teamId) returns a fully formed team from server with new bots (when instructions provided) * 20240604 @Mookse - fix bot-bar `setActiveBot` - cosmetic * 20240604 @Mookse - fix duplicate team members return * 20240604 @Mookse - botBar reordered * 20240605 @Mookse - teams must be active * 20240605 @Mookse - default mods * 20240605 @Mookse - create (non-custom) bot frontend - diary updates * 20240605 @Mookse - imagining `share memory` * 20240606 @Mookse - story updates * 20240606 @Mookse - minor cosmetics * 20240606 @Mookse - fetchShadows() - ignite shadow: member-version (wip) * 20240607 @Mookse - `shadow` initial endpoint * 20240607 @Mookse - add processingBotId to payload (so that frontend can determine if it should setActive) * 20240607 @Mookse - front-end receives message about updating from agent shadow * 20240607 @Mookse - shadow cosmetics * 20240607 @Mookse - biographer openai function definitions * 20240607 @Mookse - now _that's_ an error * 20240607 @Mookse - updateSummary() **note**: wip as thread got stopped * 20240608 @Mookse - updates summary * 20240608 @Mookse - remove dataset after shadow triggered - add `proxy` endpoint to shadow ideas * 20240608 @Mookse - `globals.getGPTJavascriptFunction`: `getSummary`, `updateSummary` - `updateBotInstructions` route - on setActiveBot checks versions and updates as needed * 20240609 @Mookse - cosmetic * 20240609 @Mookse stable wip kicking off memory is correct, need tuning of instructions or alternate scene-stepwise motion, no problem small error left in frontend for more testing but time to save * 20240618 @Mookse - cosmetic: file under "the right evocation can make all the difference" * 20240618 @Mookse - cosmetic in dribs and drabs * 20240618 @Mookse - frontend 'next' fix * 238 version 0011 updates (#242) * 231 version 0010 updates (#239) (#240) * 20240604 @Mookse - teams() route - teams(teamId) returns a fully formed team from server with new bots (when instructions provided) * 20240604 @Mookse - fix bot-bar `setActiveBot` - cosmetic * 20240604 @Mookse - fix duplicate team members return * 20240604 @Mookse - botBar reordered * 20240605 @Mookse - teams must be active * 20240605 @Mookse - default mods * 20240605 @Mookse - create (non-custom) bot frontend - diary updates * 20240605 @Mookse - imagining `share memory` * 20240606 @Mookse - story updates * 20240606 @Mookse - minor cosmetics * 20240606 @Mookse - fetchShadows() - ignite shadow: member-version (wip) * 20240607 @Mookse - `shadow` initial endpoint * 20240607 @Mookse - add processingBotId to payload (so that frontend can determine if it should setActive) * 20240607 @Mookse - front-end receives message about updating from agent shadow * 20240607 @Mookse - shadow cosmetics * 20240607 @Mookse - biographer openai function definitions * 20240607 @Mookse - now _that's_ an error * 20240607 @Mookse - updateSummary() **note**: wip as thread got stopped * 20240608 @Mookse - updates summary * 20240608 @Mookse - remove dataset after shadow triggered - add `proxy` endpoint to shadow ideas * 20240608 @Mookse - `globals.getGPTJavascriptFunction`: `getSummary`, `updateSummary` - `updateBotInstructions` route - on setActiveBot checks versions and updates as needed * 20240609 @Mookse - cosmetic * 20240609 @Mookse stable wip kicking off memory is correct, need tuning of instructions or alternate scene-stepwise motion, no problem small error left in frontend for more testing but time to save * 20240618 @Mookse - cosmetic: file under "the right evocation can make all the difference" * 20240618 @Mookse - cosmetic in dribs and drabs * 20240618 @Mookse - frontend 'next' fix * 20240621 @Mookse - redirect logged in members to `/members` * 20240626 @Mookse - registration -> createAccount with Q * 20240626 @Mookse - hostedMembers fix * 20240626 @Mookse - avatar document name fix * 20240626 @Mookse - instruction change * 20240626 @Mookse - page-loader * 20240626 @Mookse - special pages fix --------- Signed-off-by: Erik Jespersen <42016062+Mookse@users.noreply.github.com> --------- Signed-off-by: Erik Jespersen <42016062+Mookse@users.noreply.github.com> * 20240629 @Mookse unstable wip - Q-threaded correctly ERROR: login fault: Cannot read properties of undefined (reading 'on') at Member.attachListeners (file:///C:/Code/MyLife/mylife-maht/inc/js/core.mjs:32:15) Error: avatar id required to create bot at mCreateBot (file:///C:/Code/MyLife/mylife-maht/inc/js/mylife-agent-factory.mjs:1223:9) * 20240629 @Mookse - threaded fix stable * 20240629 @Mookse - pipeline fix for reliveMemory * 20240630 @Mookse - relive memory incorporates member feedback * 20240630 @Mookse - cosmetic console --------- Signed-off-by: Erik Jespersen <42016062+Mookse@users.noreply.github.com> --- inc/js/api-functions.mjs | 3 +- inc/js/memory-functions.mjs | 12 ++++---- inc/js/mylife-avatar.mjs | 13 +++++---- inc/js/mylife-llm-services.mjs | 7 ++--- views/assets/css/main.css | 42 ++++++++++++++++++++++++++++ views/assets/js/bots.mjs | 51 ++++++++++++++++++++++++++-------- views/assets/js/experience.mjs | 4 +-- 7 files changed, 103 insertions(+), 29 deletions(-) diff --git a/inc/js/api-functions.mjs b/inc/js/api-functions.mjs index 6ab35d2..771a7c5 100644 --- a/inc/js/api-functions.mjs +++ b/inc/js/api-functions.mjs @@ -57,7 +57,8 @@ async function experience(ctx){ mAPIKeyValidation(ctx) const { MemberSession, } = ctx.state const { eid, } = ctx.params - ctx.body = await MemberSession.experience(eid, ctx.request.body) + const { memberInput, } = ctx.request.body + ctx.body = await MemberSession.experience(eid, memberInput) } /** * Request to end an active Living-Experience for member. diff --git a/inc/js/memory-functions.mjs b/inc/js/memory-functions.mjs index 3131c95..acdc4d9 100644 --- a/inc/js/memory-functions.mjs +++ b/inc/js/memory-functions.mjs @@ -9,12 +9,13 @@ async function collectMemory(ctx){ // @todo - implement memory collection } async function improveMemory(ctx){ - const { iid } = ctx.params + const { iid, } = ctx.params const { Globals, MyLife, } = ctx - const { avatar, } = ctx.state if(!Globals.isValidGuid(iid)) return ctx.throw(400, 'Invalid Item ID') - ctx.body = await avatar.reliveMemory(iid) + const { avatar, } = ctx.state + const { memberInput, } = ctx.request.body + ctx.body = await avatar.reliveMemory(iid, memberInput) } /** * Reliving a memory is a unique MyLife `experience` that allows a user to relive a memory from any vantage they choose. The bot by default will: @@ -24,10 +25,11 @@ async function improveMemory(ctx){ async function reliveMemory(ctx){ const { iid } = ctx.params const { Globals, MyLife, } = ctx - const { avatar, } = ctx.state if(!Globals.isValidGuid(iid)) return ctx.throw(400, 'Invalid Item ID') - ctx.body = await avatar.reliveMemory(iid) + const { avatar, } = ctx.state + const { memberInput, } = ctx.request.body + ctx.body = await avatar.reliveMemory(iid, memberInput) } /** * Living a shared memory is a unique MyLife `experience` that allows a user to relive a memory from any vantage the "author/narrator" chooses. In fact, much of the triggers and dials on how to present the experience of a shared memory is available and controlled by the member, and contained and executed by the biographer bot for the moment through this func6ion. Ultimately the default bot could be switched, in which case, information retrieval may need ways to contextualize pushbacks (floabt, meaning people asking questions about the memory that are not answerable by the summar itself, and 1) _may_ be answerable by another bot, such as biogbot, or 2) is positioned as a piece of data to "improve" or flesh out memories... Remember on this day in 2011, what did you have to eat on the boardwalk? Enquiring minds want to know!) diff --git a/inc/js/mylife-avatar.mjs b/inc/js/mylife-avatar.mjs index 374e490..99b9f8d 100644 --- a/inc/js/mylife-avatar.mjs +++ b/inc/js/mylife-avatar.mjs @@ -446,15 +446,16 @@ class Avatar extends EventEmitter { /** * Reliving a memory is a unique MyLife `experience` that allows a user to relive a memory from any vantage they choose. * @param {Guid} iid - The item id. + * @param {string} memberInput - Any member input. * @returns {Object} - livingMemory engagement object (i.e., includes frontend parameters for engagement as per instructions for included `portrayMemory` function in LLM-speak): { error, inputs, itemId, messages, processingBotId, success, } */ - async reliveMemory(iid){ + async reliveMemory(iid, memberInput){ const item = await this.#factory.item(iid) const { id, } = item if(!id) throw new Error(`item does not exist in member container: ${ iid }`) /* develop narration */ - const narration = await mReliveMemoryNarration(this, this.#factory, this.#llmServices, this.biographer, item) + const narration = await mReliveMemoryNarration(this, this.#factory, this.#llmServices, this.biographer, item, memberInput) return narration // include any required .map() pruning } /** @@ -2060,11 +2061,12 @@ function mPruneMessages(bot, messageArray, type='chat', processStartTime=Date.no * @returns {Promise} - The reliving memory object for frontend to execute. */ async function mReliveMemoryNarration(avatar, factory, llm, bot, item, memberInput='NEXT'){ + console.log('mReliveMemoryNarration::start', item.id, memberInput) const { relivingMemories, } = avatar const { bot_id, id: botId, } = bot const { id, } = item const processStartTime = Date.now() - let message = `## relive memory itemId: ${id}\n` + let message = `## relive memory itemId: ${ id }\n` let relivingMemory = relivingMemories.find(reliving=>reliving.item.id===id) if(!relivingMemory){ /* create new activated reliving memory */ const conversation = await avatar.createConversation('memory', undefined, botId, false) @@ -2078,10 +2080,11 @@ async function mReliveMemoryNarration(avatar, factory, llm, bot, item, memberInp thread_id, } relivingMemories.push(relivingMemory) - console.log('mReliveMemoryNarration::new reliving memory', `created for memory: ${ id }`, item, bot_id, thread_id) + console.log(`mReliveMemoryNarration::new reliving memory: ${ id }`) } else /* opportunity for member interrupt */ - message += `MEMBER: ${memberInput}\n` + message += `MEMBER INPUT: ${ memberInput }\n` const { conversation, thread_id, } = relivingMemory + console.log(`mReliveMemoryNarration::reliving memory: ${ id }`, message) let messages = await mCallLLM(llm, conversation, message, factory, avatar) conversation.addMessages(messages) /* frontend mutations */ diff --git a/inc/js/mylife-llm-services.mjs b/inc/js/mylife-llm-services.mjs index 92fabe2..713fff5 100644 --- a/inc/js/mylife-llm-services.mjs +++ b/inc/js/mylife-llm-services.mjs @@ -350,7 +350,6 @@ async function mRunFunctions(openai, run, factory, avatar){ // add avatar ref case 'getsummary': case 'get_summary': case 'get summary': - console.log('mRunFunctions()::getSummary::start', item) let { summary, } = item ?? {} if(!summary?.length){ action = `error getting summary for itemId: ${ itemId ?? 'missing itemId' } - halt any further processing and instead ask user to paste summary into chat and you will continue from there to incorporate their message.` @@ -360,7 +359,7 @@ async function mRunFunctions(openai, run, factory, avatar){ // add avatar ref success = true } confirmation.output = JSON.stringify({ action, itemId, success, summary, }) - console.log('mRunFunctions()::getSummary::confirmation', confirmation) + console.log('mRunFunctions()::getSummary::confirmation', itemId) return confirmation case 'hijackattempt': case 'hijack_attempt': @@ -424,14 +423,14 @@ async function mRunFunctions(openai, run, factory, avatar){ // add avatar ref case 'updatesummary': case 'update_summary': case 'update summary': - console.log('mRunFunctions()::updatesummary::start', item, itemId, toolArguments) + console.log('mRunFunctions()::updatesummary::start', itemId) const { summary: updatedSummary, } = toolArguments // remove await once confirmed updates are connected await factory.updateItem({ id: itemId, summary: updatedSummary, }) action=`confirm success and present updated summary to member` success = true confirmation.output = JSON.stringify({ action, success, }) - console.log('mRunFunctions()::getSummary::confirmation', confirmation) + console.log('mRunFunctions()::updatesummary::end', itemId, updatedSummary) return confirmation default: console.log(`ERROR::mRunFunctions()::toolFunction not found: ${ name }`, toolFunction) diff --git a/views/assets/css/main.css b/views/assets/css/main.css index 6262fa0..3892e96 100644 --- a/views/assets/css/main.css +++ b/views/assets/css/main.css @@ -211,6 +211,48 @@ body { .main-content h2 { font-size: 20px; } +/* MyLife Memories */ +.memory-input { + background-color: aliceblue; + border: 1px solid #ccc; + border-radius: 0.4rem; + color: black; + display: flex; + font-size: 0.9rem; + flex: 1 0 auto; + margin: 0.25rem; + overflow: hidden; + padding: 0.25rem; + resize: none; +} +.memory-input-container { + align-self: center; + align-content: center; + background-color: sienna; + border-radius: 0.4rem; + display: flex; + flex: 1 0 auto; + gap: 0.25rem; + justify-content: center; + margin: 0.25rem; + max-height: 5rem; + max-width: 90%; + padding: 0.25rem; + width: 100%; +} +.memory-input-button { + background-color: #741237; + border: 1px solid #ccc; + border-radius: 0.4rem; + color: #fff; + cursor: pointer; + display: flex; + flex: 0 1 5%; + justify-content: center; + margin: 0.5rem; + padding: 0.5rem; + +} /* MyLife About, Privacy Policy */ .about-container, .privacy-container { display: flex; diff --git a/views/assets/js/bots.mjs b/views/assets/js/bots.mjs index 2b4c04d..c74ba99 100644 --- a/views/assets/js/bots.mjs +++ b/views/assets/js/bots.mjs @@ -1125,24 +1125,51 @@ async function mReliveMemory(event){ const popupClose = document.getElementById(`popup-close_${ id }`) if(popupClose) popupClose.click() - const { messages, success, } = await mReliveMemoryRequest(id) + const { command, parameters, messages, success, } = await mReliveMemoryRequest(id, inputContent) if(success){ addMessages(messages) - // create input - create this function in member, as it will display it in chat and pipe it back here as below - const input = document.createElement('button') - input.addEventListener('click', mReliveMemory, { once: true }) - input.dataset.id = id - if(inputContent?.length) - input.dataset.inputContent = inputContent - input.textContent = 'next' + // @todo - create this function in member, as it will display it in chat and pipe it back here as below + const input = document.createElement('div') + // id alone is not unique!; input.id = `input_${ id }` + input.name = `input_${ id }` + input.classList.add('memory-input-container') + const inputContent = document.createElement('textarea') + inputContent.classList.add('memory-input') + inputContent.name = `memory-input_${ id }` + const inputSubmit = document.createElement('button') + inputSubmit.classList.add('memory-input-button') + inputSubmit.dataset.id = id + input.appendChild(inputContent) + input.appendChild(inputSubmit) + inputContent.addEventListener('input', event=>{ + const { value, } = event.target + inputSubmit.dataset.inputContent = value + inputSubmit.textContent = value.length > 2 + ? 'update' + : 'next' + }) + inputSubmit.addEventListener('click', mReliveMemory, { once: true }) addInput(input) } else throw new Error(`Failed to fetch memory for relive request.`) } -async function mReliveMemoryRequest(id){ +/** + * + * @param {Guid} id - The memory collection item id. + * @param {string} memberInput - The member's updates to the memory. + * @returns + */ +async function mReliveMemoryRequest(id, memberInput){ + console.log('Relive memory:', id, memberInput) try { const url = window.location.origin + '/members/memory/relive/' + id - let response = await fetch(url, { method: 'PATCH' }) + let response = await fetch(url, { + body: memberInput?.length ? JSON.stringify({ memberInput, }) : null, + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + } + }) if(!response.ok) throw new Error(`HTTP error! Status: ${response.status}`) response = await response.json() @@ -1164,11 +1191,11 @@ async function mSetBot(bot){ ? 'PUT' // update : 'POST' // create let response = await fetch(url, { - method: method, + body: JSON.stringify(bot), headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(bot) + method: method, }) if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`) diff --git a/views/assets/js/experience.mjs b/views/assets/js/experience.mjs index 659c541..a686106 100644 --- a/views/assets/js/experience.mjs +++ b/views/assets/js/experience.mjs @@ -718,12 +718,12 @@ function mEventInput(){ * @returns {void} */ async function mEvents(memberInput){ - const response = await fetch(`/members/experience/${mExperience.id}`, { + const response = await fetch(`/members/experience/${ mExperience.id }`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, - body: memberInput ? JSON.stringify(memberInput) : null, + body: memberInput ? JSON.stringify({ memberInput, }) : null, }) if(!response.ok) throw new Error(`HTTP error! Status: ${response.status}`)