Skip to content

Commit

Permalink
333 version 0018 updates (#337)
Browse files Browse the repository at this point in the history
* 20240818 @Mookse
- versioning

* 20240818 @Mookse
- cosmetic

* 20240824 @Mookse
- remove .env version
- version in `server.mjs` -> Q-avatar

* 20240824 @Mookse
- placeholder for reliving memory in chat
- build out HTML for active item
- css for active item
- listeners
- fetch properties derived from getActiveItem() calls

* 20240826 @Mookse
- stable wip
- Visual Memory Editing Info #292

* 20240826 @Mookse
- Visual Memory Editing Info #292 cleanup

* 20240826 @Mookse
- Visual Memory Editing Info #292
  • Loading branch information
Mookse authored Aug 27, 2024
1 parent 561c230 commit 1f70188
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 99 deletions.
13 changes: 9 additions & 4 deletions inc/js/core.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// imports
import EventEmitter from 'events'
import chalk from 'chalk'
// server-specific imports
import initRouter from './routes.mjs'
// define export Classes for Members and MyLife
Expand Down Expand Up @@ -225,15 +224,13 @@ class Organization extends Member { // form=organization
get values(){
return this.core.values
}
get version(){
return this.core.version ?? '0.0.17'
}
get vision(){
return this.core.vision
}
}
class MyLife extends Organization { // form=server
#avatar // MyLife's private class avatar, _same_ object reference as Member Class's `#avatar`
#version = '0.0.0' // indicates error
constructor(factory){ // no session presumed to exist
super(factory)
}
Expand Down Expand Up @@ -365,6 +362,14 @@ class MyLife extends Organization { // form=server
get being(){
return 'MyLife'
}
get version(){
return this.#version
}
set version(_version){
if(!this.globals.isValidVersion(_version))
throw new Error('Invalid version number')
this.#version = _version
}
}
/* exports */
export {
Expand Down
18 changes: 4 additions & 14 deletions inc/js/functions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,17 @@ async function challenge(ctx){
* @param {Koa} ctx - Koa Context object
*/
async function chat(ctx){
const { botId, message, role, } = ctx.request.body
?? {} /* body nodes sent by fe */
const { botId, itemId, message, shadowId, } = ctx.request.body ?? {} /* body nodes sent by fe */
if(!message?.length)
ctx.throw(400, 'missing `message` content')
const { avatar, MemberSession, } = ctx.state
const { isMyLife, thread_id, } = MemberSession
let conversation
if(isMyLife && !thread_id?.length){
const conversation = await avatar.createConversation('system', undefined, botId, true) // pushes to this.#conversations in Avatar
conversation = await avatar.createConversation('system', undefined, botId, true) // pushes to this.#conversations in Avatar
MemberSession.thread_id = conversation.thread_id
}
const response = await avatar.chatRequest(botId, MemberSession.thread_id, message)
const response = await avatar.chatRequest(message, botId, MemberSession.thread_id, itemId, shadowId, conversation)
ctx.body = response
}
async function collections(ctx){
Expand Down Expand Up @@ -234,15 +234,6 @@ async function privacyPolicy(ctx){
ctx.state.subtitle = `Effective Date: 2024-01-01`
await ctx.render('privacy-policy') // privacy-policy
}
async function shadow(ctx){
const { avatar, } = ctx.state
const { active=true, botId, itemId, message, role, threadId, shadowId, title, } = ctx.request.body // necessary to flip active bot, or just presume to use the creator of the shadow?
if(!itemId?.length)
ctx.throw(400, `missing item id`)
if(!active) // @stub - redirect to normal chat?
ctx.throw(400, `shadow must be active`)
ctx.body = await avatar.shadow(shadowId, itemId, title, message)
}
/**
* Gets the list of shadows.
* @returns {Object[]} - Array of shadow objects.
Expand Down Expand Up @@ -370,7 +361,6 @@ export {
members,
passphraseReset,
privacyPolicy,
shadow,
shadows,
signup,
summarize,
Expand Down
4 changes: 4 additions & 0 deletions inc/js/globals.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ class Globals extends EventEmitter {
isValidGuid(text){
return typeof text === 'string' && mGuidRegex.test(text)
}
isValidVersion(version) {
const regex = /^\d+\.\d+\.\d+$/
return typeof version === 'string' && regex.test(version)
}
stripCosmosFields(object){
return Object.fromEntries(Object.entries(object).filter(([k, v]) => !k.startsWith('_')))
}
Expand Down
63 changes: 34 additions & 29 deletions inc/js/mylife-avatar.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -81,32 +81,40 @@ class Avatar extends EventEmitter {
}
/**
* Processes and executes incoming chat request.
* @todo - cleanup/streamline frontend communication as it really gets limited to Q&A... other events would fire API calls on the same same session, so don't need to be in chat or conversation streams
* @public
* @param {string} message - The chat message content.
* @param {string} activeBotId - The active bot id.
* @param {string} threadId - The openai thread id.
* @param {string} chatMessage - The chat message content.
* @param {Guid} itemId - The active collection-item id (optional).
* @param {Guid} shadowId - The active Shadow Id (optional).
* @param {Conversation} conversation - The conversation object.
* @param {number} processStartTime - The start time of the process.
* @returns {object} - The response(s) to the chat request.
*/
async chatRequest(activeBotId, threadId, chatMessage, conversation, processStartTime=Date.now()){
if(!chatMessage)
async chatRequest(message, activeBotId, threadId, itemId, shadowId, conversation, processStartTime=Date.now()){
if(!message)
throw new Error('No message provided in context')
if(!activeBotId)
throw new Error('Parameter `activeBotId` required.')
const { activeBot, factory } = this
const { id: botId, thread_id, } = activeBot
if(botId!==activeBotId)
throw new Error(`Invalid bot id: ${ activeBotId }, active bot id: ${ botId }`)
// @stub - clean up conversation alterations - Q is immune
conversation = conversation
?? this.getConversation(threadId ?? thread_id)
?? await this.createConversation('chat', threadId ?? thread_id, activeBotId)
if(!conversation)
throw new Error('No conversation found for thread id and could not be created.')
conversation.bot_id = activeBot.bot_id // pass in via quickly mutating conversation (or independently if preferred in end), versus llmServices which are global
const messages = await mCallLLM(this.#llmServices, conversation, chatMessage, factory, this)
let messages
if(shadowId)
messages = await this.shadow(shadowId, itemId, message)
else {
// @stub - one weakness in teh chain might also be the fact that I am not including in instructions how to create integrated summary and left it primarily to the JSON description of function
if(itemId)
message = `update-memory-request: itemId=${ itemId }\n` + message
messages = await mCallLLM(this.#llmServices, conversation, message, factory, this)
}
conversation.addMessages(messages)
if(mAllowSave)
conversation.save()
Expand All @@ -116,12 +124,12 @@ class Avatar extends EventEmitter {
const { activeBot: bot } = this
// current fe will loop through messages in reverse chronological order
const chat = conversation.messages
.filter(message=>{ // limit to current chat response(s); usually one, perhaps faithfully in future [or could be managed in LLM]
return messages.find(_message=>_message.id===message.id)
&& message.type==='chat'
&& message.role!=='user'
.filter(_message=>{ // limit to current chat response(s); usually one, perhaps faithfully in future [or could be managed in LLM]
return messages.find(__message=>__message.id===_message.id)
&& _message.type==='chat'
&& _message.role!=='user'
})
.map(message=>mPruneMessage(bot, message, 'chat', processStartTime))
.map(_message=>mPruneMessage(bot, _message, 'chat', processStartTime))
return chat
}
/**
Expand Down Expand Up @@ -472,14 +480,13 @@ class Avatar extends EventEmitter {
return await this.#factory.resetPassphrase(passphrase)
}
/**
* Takes a shadow message and sends it to the appropriate bot for response.
* Takes a shadow message and sends it to the appropriate bot for response, returning the standard array of bot responses.
* @param {Guid} shadowId - The shadow id.
* @param {Guid} itemId - The item id.
* @param {string} title - The title of the original summary.
* @param {string} message - The member (interacting with shadow) message content.
* @returns {Object} - The response object { error, itemId, messages, processingBotId, success, }
* @returns {Object[]} - The array of bot responses.
*/
async shadow(shadowId, itemId, title, message){
async shadow(shadowId, itemId, message){
const processingStartTime = Date.now()
const shadows = await this.shadows()
const shadow = shadows.find(shadow=>shadow.id===shadowId)
Expand Down Expand Up @@ -515,12 +522,7 @@ class Avatar extends EventEmitter {
messages = messages.map(message=>mPruneMessage(bot, message, 'shadow', processingStartTime))
if(tailgate?.length)
messages.push(mPruneMessage(bot, tailgate, 'system'))
return {
itemId,
messages,
processingBotId: bot.id,
success: true,
}
return messages
}
/**
* Gets the list of shadows.
Expand Down Expand Up @@ -1150,25 +1152,28 @@ class Q extends Avatar {
/* overloaded methods */
/**
* Processes and executes incoming chat request.
* @todo - cleanup/streamline frontend communication as it really gets limited to Q&A... other events would fire API calls on the same same session, so don't need to be in chat or conversation streams
* @public
* @param {string} message - The chat message content.
* @param {string} activeBotId - The active bot id.
* @param {string} threadId - The openai thread id.
* @param {string} chatMessage - The chat message content.
* @param {number} processStartTime - The process start time.
* @param {Guid} itemId - The active collection-item id (optional).
* @param {Guid} shadowId - The active Shadow Id (optional).
* @param {Conversation} conversation - The conversation object.
* @param {number} processStartTime - The start time of the process.
* @returns {object} - The response(s) to the chat request.
*/
async chatRequest(activeBotId=this.activeBotId, threadId, chatMessage, processStartTime=Date.now()){
let conversation = this.getConversation(threadId)
async chatRequest(message, activeBotId, threadId, itemId, shadowId, conversation, processStartTime=Date.now()){
conversation = conversation
?? this.getConversation(threadId)
if(!conversation)
throw new Error('Conversation cannot be found')
this.activeBot.bot_id = mBot_idOverride
?? this.activeBot.bot_id
if(this.isValidating) // trigger confirmation until session (or vld) ends
chatMessage = `CONFIRM REGISTRATION PHASE: registrationId=${ this.registrationId }\n${ chatMessage }`
message = `CONFIRM REGISTRATION PHASE: registrationId=${ this.registrationId }\n${ message }`
if(this.isCreatingAccount)
chatMessage = `CREATE ACCOUNT PHASE: ${ chatMessage }`
return super.chatRequest(activeBotId, threadId, chatMessage, conversation, processStartTime)
message = `CREATE ACCOUNT PHASE: ${ message }`
return super.chatRequest(message, activeBotId, threadId, itemId, shadowId, conversation, processStartTime)
}
upload(){
throw new Error('MyLife avatar cannot upload files.')
Expand Down
2 changes: 0 additions & 2 deletions inc/js/routes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
members,
passphraseReset,
privacyPolicy,
shadow,
shadows,
signup,
summarize,
Expand Down Expand Up @@ -120,7 +119,6 @@ _memberRouter.post('/bots/activate/:bid', activateBot)
_memberRouter.post('/category', category)
_memberRouter.post('/mode', interfaceMode)
_memberRouter.post('/passphrase', passphraseReset)
_memberRouter.post('/shadow', shadow)
_memberRouter.post('/summarize', summarize)
_memberRouter.post('/teams/:tid', team)
_memberRouter.post('/upload', upload)
Expand Down
1 change: 0 additions & 1 deletion sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,5 @@ MYLIFE_HOSTED_MBR_ID=[] # array of ids hosted on your server--OR--remove or leav
MYLIFE_SESSION_KEY=0.0.15 # random string for resetting sessions
MYLIFE_SESSION_TIMEOUT_MS=900000
MYLIFE_SYSTEM_ALERT_CHECK_INTERVAL=120000 # how often to check for alerts in ms
MYLIFE_VERSION=0.0.15
MYLIFE_REGISTRATION_DB_CONTAINER_NAME= # get from admin
MYLIFE_SYSTEM_DB_CONTAINER_NAME= # get from admin
3 changes: 3 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import chalk from 'chalk'
import MyLife from './inc/js/mylife-agent-factory.mjs'
// constants/variables
// @todo - parse environment variables in Globals and then have them available via as values
const version = '0.0.18'
const app = new Koa()
const port = JSON.parse(process.env.PORT ?? '3000')
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const _Maht = await MyLife // Mylife is the pre-instantiated exported version of organization with very unique properties. MyLife class can protect fields that others cannot, #factory as first refactor will request
if(!process.env.MYLIFE_HOSTING_KEY || process.env.MYLIFE_HOSTING_KEY !== _Maht.avatar.hosting_key)
throw new Error('Invalid hosting key. Server will not start.')
_Maht.version = version
console.log(chalk.bgBlue('created-core-entity:'), _Maht.version)
const MemoryStore = new session.MemoryStore()
const mimeTypesToExtensions = {
// Text Formats
Expand Down
47 changes: 45 additions & 2 deletions views/assets/css/chat.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,43 @@
.await-button-spinner {
margin-right: 0.5em;
}
.chat-active-items {
align-items: center;
display: flex;
flex: 1 0 auto;
flex-direction: column;
padding-left: 3rem;
margin-bottom: 0.5rem;
max-width: 100%;
}
.chat-active-item {
background-color: #007BFF;
border: solid thin aliceblue;
border-radius: 0.6rem;
display: none;
flex: 1 0 auto;
flex-direction: row;
padding: 0.3rem;
width: 100%;
}
.chat-active-item-close {
align-self: top;
color: maroon;
cursor: pointer;
font-size: 1.25rem;
margin-left: 2rem;
}
.chat-active-item-icon {
color: #e9eb5c;
font-size: 1.25rem;
margin-right: 0.5em;
}
.chat-active-item-text {
color: aliceblue;
display: flex;
flex: 1 0 auto;
max-width: 80%;
}
.chat-bubble {
background-color: #3427ec; /* Assuming a dark bubble color */
border-radius: 0.8rem;
Expand Down Expand Up @@ -97,15 +134,21 @@
}
.chat-member,
.chat-user {
align-items: center;
align-items: flex-start;
display: flex;
flex: 1 1 auto;
flex-direction: row;
flex-direction: column;
flex-wrap: wrap;
margin: 0.5em 0;
max-height: 60%;
width: 100%;
}
.chat-member-container,
.chat-user-container {
display: flex;
flex-direction: row;
width: 100%;
}
.chat-message-container {
display: flex;
position: relative;
Expand Down
Loading

0 comments on commit 1f70188

Please sign in to comment.