Skip to content

Commit

Permalink
Merge pull request #193 from MyLife-Services/181-version-005-updates
Browse files Browse the repository at this point in the history
181 version 005 updates
  • Loading branch information
Mookse authored May 7, 2024
2 parents 59344b8 + 0d8ac12 commit e6ca541
Show file tree
Hide file tree
Showing 19 changed files with 453 additions and 337 deletions.
98 changes: 25 additions & 73 deletions inc/js/api-functions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -36,70 +36,22 @@ async function experienceCast(ctx){
* @property {object} scene - Scene data, regardless if "current" or new.
*/
async function experience(ctx){
/* reject if experience is locked */
/* lock could extend to gating requests as well */
if(ctx.state.MemberSession.experienceLock){
// @stub - if experience is locked, perhaps request to run an internal check to see if exp is bugged or timed out
ctx.throw(500, 'Experience is locked. Wait for previous event to complete. If bugged, end experience and begin again.')
}
await mAPIKeyValidation(ctx)
const { assistantType, avatar, mbr_id, } = ctx.state
mAPIKeyValidation(ctx)
const { MemberSession, } = ctx.state
const { eid, } = ctx.params
let events = []
ctx.state.MemberSession.experienceLock = true
try{ // requires try, as locks would otherwise not release on unidentified errors
if(!avatar.isInExperience){
await avatar.experienceStart(eid)
}
else {
const eventSequence = await avatar.experiencePlay(eid, ctx.request.body)
events = eventSequence
console.log(chalk.yellowBright('experience() events'), events?.length)
}
} catch (error){
console.log(chalk.redBright('experience() error'), error, avatar.experience)
const { experience } = avatar
if(experience){ // embed error in experience
experience.errors = experience.errors ?? []
experience.errors.push(error)
}
}
const { experience } = avatar
const { autoplay, location, title, } = experience
ctx.body = {
autoplay,
events,
location,
title,
}
ctx.state.MemberSession.experienceLock = false
if(events.find(event=>{ return event.action==='end' && event.type==='experience' })){
if(!avatar.experienceEnd(eid)) // attempt to end experience
throw new Error('Experience failed to end.')
}
return
ctx.body = await MemberSession.experience(eid, ctx.request.body)
}
/**
* Request to end an active Living-Experience for member.
* @param {Koa} ctx - Koa Context object.
* @returns {Object} - Represents `ctx.body` object with following `experience` properties.
* @property {boolean} success - Success status, true/false.
*/
async function experienceEnd(ctx){
await mAPIKeyValidation(ctx)
const { assistantType, avatar, mbr_id } = ctx.state
const { eid } = ctx.params
let endSuccess = false
try {
endSuccess = avatar.experienceEnd(eid)
} catch(err) {
console.log(chalk.redBright('experienceEnd() error'), err)
// can determine if error is critical or not, currently implies there is no running experience
endSuccess = true
}
ctx.body = endSuccess
ctx.state.MemberSession.experienceLock = !ctx.body
return
function experienceEnd(ctx){
mAPIKeyValidation(ctx)
const { MemberSession, } = ctx.state
const { eid, } = ctx.params
ctx.body = MemberSession.experienceEnd(eid)
}
/**
* Delivers the manifest of an experience. Manifests are the data structures that define the experience, including scenes, events, and other data. Experience must be "started" in order to request.
Expand All @@ -109,18 +61,18 @@ async function experienceEnd(ctx){
* @property {Array} cast - Experience cast array.
* @property {Object} navigation - Navigation object (optional - for interactive display purposes only).
*/
async function experienceManifest(ctx){
await mAPIKeyValidation(ctx)
const { assistantType, avatar, mbr_id } = ctx.state
function experienceManifest(ctx){
mAPIKeyValidation(ctx)
const { avatar, } = ctx.state
ctx.body = avatar.manifest
return
}
/**
* Navigation array of scenes for experience.
*/
async function experienceNavigation(ctx){
await mAPIKeyValidation(ctx)
const { assistantType, avatar, mbr_id } = ctx.state
function experienceNavigation(ctx){
mAPIKeyValidation(ctx)
const { avatar, } = ctx.state
ctx.body = avatar.navigation
return
}
Expand All @@ -132,12 +84,16 @@ async function experienceNavigation(ctx){
* @property {array} experiences - Array of Experience shorthand objects.
*/
async function experiences(ctx){
await mAPIKeyValidation(ctx)
const { assistantType, MemberSession } = ctx.state
mAPIKeyValidation(ctx)
const { MemberSession, } = ctx.state
// limit one mandatory experience (others could be highlighted in alerts) per session
const experiencesObject = await MemberSession.experiences()
ctx.body = experiencesObject
return
}
async function experiencesLived(ctx){
mAPIKeyValidation(ctx)
const { MemberSession, } = ctx.state
ctx.body = MemberSession.experiencesLived
}
async function keyValidation(ctx){ // from openAI
await mAPIKeyValidation(ctx)
Expand Down Expand Up @@ -192,7 +148,6 @@ async function library(ctx){
message: `library function(s) completed successfully.`,
success: true,
}
return
}
/**
* Login function for member. Requires mid in params.
Expand All @@ -207,7 +162,6 @@ async function login(ctx){
ctx.throw(400, `missing member id`) // currently only accepts single contributions via post with :cid
ctx.session.MemberSession.challenge_id = decodeURIComponent(ctx.params.mid)
ctx.body = { challengeId: ctx.session.MemberSession.challenge_id }
return
}
/**
* Logout function for member.
Expand All @@ -218,7 +172,6 @@ async function logout(ctx){
ctx.session = null
ctx.status = 200
ctx.body = { success: true }
return
}
async function register(ctx){
const _registrationData = ctx.request.body
Expand Down Expand Up @@ -253,7 +206,6 @@ async function register(ctx){
message: 'Registration completed successfully.',
data: _return,
}
return
}
/**
* Functionality around story contributions.
Expand All @@ -274,7 +226,6 @@ async function story(ctx){
success: true,
message: 'Story submitted successfully.',
}
return
}
/**
* Management of Member Story Libraries. Note: Key validation is performed in library(). Story library may have additional functionality inside of core/MyLife
Expand Down Expand Up @@ -368,9 +319,9 @@ async function mAPIKeyValidation(ctx){ // transforms ctx.state
}
}
function mTokenType(ctx){
const _token = ctx.state.token
const _assistantType = mBotSecrets?.[_token]??'personal-avatar'
return _assistantType
const { token, } = ctx.state
const assistantType = mBotSecrets?.[token] ?? 'personal-avatar'
return assistantType
}
function mTokenValidation(_token){
return mBotSecrets?.[_token]?.length??false
Expand All @@ -383,6 +334,7 @@ export {
experienceManifest,
experienceNavigation,
experiences,
experiencesLived,
keyValidation,
library,
login,
Expand Down
69 changes: 60 additions & 9 deletions inc/js/mylife-agent-factory.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -219,20 +219,30 @@ class BotFactory extends EventEmitter{
) ?? []
if(!includeLived){
const livedExperiences = await this.experiencesLived() ?? []
experiences = experiences.filter(
// filter out those not found (by id in lived experiences)
_experience=>!livedExperiences.find(
_livedExperience=>_livedExperience.experienceId===_experience.id
experiences = experiences.filter( // filter out `lived-experience.id`)
experience=>!livedExperiences.find(
livedExperience=>livedExperience.experience_id===experience.id
)
)
}
return experiences
}
/**
* Returns array of member `lived-experiences` from database.
* @returns {Promise<array>} - Array of lived experience objects.
*/
async experiencesLived(){
const experienceFields = [
'experience_date',
'experience_id',
'title',
'variables',
]
const livedExperiences = await this.dataservices.getItems(
'experience-lived',
['experienceId'], // default includes being, id, mbr_id, object_id
'lived-experience',
experienceFields, // default includes being, id, mbr_id, object_id
)
return livedExperiences
}
/**
* Gets a specified `experience` from database.
Expand Down Expand Up @@ -511,10 +521,51 @@ class AgentFactory extends BotFactory{
/**
* Registers a new candidate to MyLife membership
* @public
* @param {object} _candidate { 'email': string, 'humanName': string, 'avatarNickname': string }
* @param {object} candidate { 'email': string, 'humanName': string, 'avatarNickname': string }
*/
async registerCandidate(candidate){
return await this.dataservices.registerCandidate(candidate)
}
/**
* Saves a completed lived experience to MyLife.
* @param {Object} experience - The Lived Experience Object to save.
* @returns
*/
async registerCandidate(_candidate){
return await this.dataservices.registerCandidate(_candidate)
async saveExperience(experience){
/* validate structure */
if(!experience?.id?.length)
throw new Error('experience id invalid')
if(!experience?.location?.completed)
throw new Error('experience not completed')
/* clean experience */
const { cast: _cast, events, id, title, variables, } = experience
const cast = _cast.map(({ id, role }) => ({ id, role }))
const _experience = {
being: 'lived-experience',
events: events
.filter(event=>event?.dialog?.dialog?.length || event.character?.characterId?.length)
.map(event=>{
const { character: _character, dialog, id, input, } = event
const character = cast.find(_cast=>_cast.id===_character?.characterId)
if(_character?.role?.length)
character.role = _character.role
return {
character: character?.role,
dialog: dialog?.dialog,
id,
// input, // currently am not storing memberInput event correctly
}
}),
experience_date: Date.now(),
experience_id: experience.id,
id: this.newGuid,
mbr_id: this.mbr_id,
name: (`lived-experience_${ title }_${ id }`).substring(0, 256),
title,
variables,
}
const savedExperience = await this.dataservices.saveExperience(_experience)
return savedExperience
}
/**
* Submits a story to MyLife. Currently via API, but could be also work internally.
Expand Down
Loading

0 comments on commit e6ca541

Please sign in to comment.