Skip to content

Commit

Permalink
Merge branch 'azure-deploy-prod' into base
Browse files Browse the repository at this point in the history
  • Loading branch information
Mookse committed Sep 17, 2024
2 parents da371ca + 6eeef0f commit 21dfce3
Show file tree
Hide file tree
Showing 14 changed files with 827 additions and 264 deletions.
112 changes: 69 additions & 43 deletions inc/js/api-functions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,31 @@ async function availableExperiences(ctx){
mbr_id,
}
}
async function entry(ctx){
await mAPIKeyValidation(ctx)
const { assistantType, mbr_id } = ctx.state
if(!ctx.request.body?.summary?.length)
throw new Error('No entry summary provided. Use `summary` field.')
console.log(chalk.yellowBright('entry()::entry attempted:'), ctx.request.body)
const summary = {
...ctx.request.body,
assistantType,
mbr_id,
}
const entry = await ctx.MyLife.entry(summary)
console.log(chalk.yellowBright('entry()::entry submitted:'), entry, summary)
ctx.status = 200
ctx.body = {
id: entry.id,
message: 'entry submitted successfully.',
success: true,
}

}
// @todo implement builder functionality, allowing for interface creation of experiences by members
// @todo implement access to exposed member experiences using `mbr_key` as parameter to `factory.getItem()`
async function experienceBuilder(ctx){
mAPIKeyValidation(ctx)
await mAPIKeyValidation(ctx)
const { assistantType, mbr_id } = ctx.state
const { eid, sid } = ctx.params
const { experience } = ctx.request.body?.experience
Expand Down Expand Up @@ -54,7 +75,7 @@ async function experienceCast(ctx){
* @property {object} scene - Scene data, regardless if "current" or new.
*/
async function experience(ctx){
mAPIKeyValidation(ctx)
await mAPIKeyValidation(ctx)
const { MemberSession, } = ctx.state
const { eid, } = ctx.params
const { memberInput, } = ctx.request.body
Expand All @@ -66,8 +87,8 @@ async function experience(ctx){
* @returns {Object} - Represents `ctx.body` object with following `experience` properties.
* @property {boolean} success - Success status, true/false.
*/
function experienceEnd(ctx){
mAPIKeyValidation(ctx)
async function experienceEnd(ctx){
await mAPIKeyValidation(ctx)
const { MemberSession, } = ctx.state
const { eid, } = ctx.params
ctx.body = MemberSession.experienceEnd(eid)
Expand All @@ -80,17 +101,17 @@ function experienceEnd(ctx){
* @property {Array} cast - Experience cast array.
* @property {Object} navigation - Navigation object (optional - for interactive display purposes only).
*/
function experienceManifest(ctx){
mAPIKeyValidation(ctx)
async function experienceManifest(ctx){
await mAPIKeyValidation(ctx)
const { avatar, } = ctx.state
ctx.body = avatar.manifest
return
}
/**
* Navigation array of scenes for experience.
*/
function experienceNavigation(ctx){
mAPIKeyValidation(ctx)
async function experienceNavigation(ctx){
await mAPIKeyValidation(ctx)
const { avatar, } = ctx.state
ctx.body = avatar.navigation
return
Expand All @@ -103,18 +124,26 @@ function experienceNavigation(ctx){
* @property {array} experiences - Array of Experience shorthand objects.
*/
async function experiences(ctx){
mAPIKeyValidation(ctx)
await 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
}
async function experiencesLived(ctx){
mAPIKeyValidation(ctx)
await mAPIKeyValidation(ctx)
const { MemberSession, } = ctx.state
ctx.body = MemberSession.experiencesLived
}
async function keyValidation(ctx){ // from openAI
/**
* Validates member key and returns member data. Leverages the key validation structure to ensure payload is liegimate. Currently in use by OpenAI GPT and local Postman instance.
* @param {Koa} ctx - Koa Context object
* @returns {object} - Object with following properties.
* @property {boolean} success - Success status.
* @property {string} message - Message to querying intelligence.
* @property {object} data - Consented Member data.
*/
async function keyValidation(ctx){
await mAPIKeyValidation(ctx)
ctx.status = 200 // OK
if(ctx.method === 'HEAD') return
Expand All @@ -137,10 +166,10 @@ async function keyValidation(ctx){ // from openAI
?? names?.[0].split(' ')[0]
?? '',
}
console.log(chalk.yellowBright(`keyValidation():${memberCoreData.mbr_id}`), memberCoreData.fullName)
console.log(chalk.yellowBright(`keyValidation()::`), chalk.redBright(`success::`), chalk.redBright(memberCoreData.mbr_id))
ctx.body = {
success: true,
message: 'Valid member.',
message: 'Valid Member',
data: memberCoreData,
}
}
Expand Down Expand Up @@ -195,21 +224,25 @@ async function register(ctx){
/**
* Functionality around story contributions.
* @param {Koa} ctx - Koa Context object
* @returns {Koa} Koa Context object
*/
async function story(ctx){
async function memory(ctx){
await mAPIKeyValidation(ctx) // sets ctx.state.mbr_id and more
const { assistantType, mbr_id } = ctx.state
const { storySummary } = ctx.request?.body??{}
if(!storySummary?.length)
ctx.throw(400, 'No story summary provided. Use `storySummary` field.')
// write to cosmos db
const _story = await ctx.MyLife.story(mbr_id, assistantType, storySummary) // @todo: remove await
console.log(chalk.yellowBright('story submitted:'), _story)
ctx.status = 200 // OK
if(!ctx.request.body?.summary?.length)
throw new Error('No memory summary provided. Use `summary` field.')
console.log(chalk.yellowBright('memory()::memory attempted:'), ctx.request.body)
const summary = {
...ctx.request.body,
assistantType,
mbr_id,
}
const memory = await ctx.MyLife.memory(summary)
console.log(chalk.yellowBright('memory()::memory submitted:'), memory, summary)
ctx.status = 200
ctx.body = {
id: memory.id,
message: 'memory submitted successfully.',
success: true,
message: 'Story submitted successfully.',
}
}
/**
Expand All @@ -220,21 +253,21 @@ async function story(ctx){
* @param {function} next Koa next function
* @returns {function} Koa next function
*/
async function tokenValidation(ctx, next) {
async function tokenValidation(ctx, next){
try {
const authHeader = ctx.request.headers['authorization']
if(!authHeader){
ctx.status = 401
ctx.body = { error: 'Authorization header is missing' }
return
}
const _token = authHeader.split(' ')[1] // Bearer TOKEN_VALUE
if(!mTokenValidation(_token)){
const token = authHeader.split(' ')[1] // Bearer TOKEN_VALUE
if(!mTokenValidation(token)){
ctx.status = 401
ctx.body = { error: 'Authorization token failure' }
return
}
ctx.state.token = _token // **note:** keep first, as it is used in mTokenType()
ctx.state.token = token // **note:** keep first, as it is used in mTokenType()
ctx.state.assistantType = mTokenType(ctx)
await next()
} catch (error) {
Expand Down Expand Up @@ -283,19 +316,11 @@ async function mAPIKeyValidation(ctx){ // transforms ctx.state
ctx.throw(400, 'Missing member key.')
else // unlocked, providing passphrase
return
let isValidated
if(!ctx.state.locked || ctx.session?.isAPIValidated){
isValidated = true
} else {
const serverHostedMembers = JSON.parse(process.env.MYLIFE_HOSTED_MBR_ID ?? '[]')
const localHostedMembers = [
'system-one|4e6e2f26-174b-43e4-851f-7cf9cdf056df',
].filter(member=>serverHostedMembers.includes(member)) // none currently
serverHostedMembers.push(...localHostedMembers)
if(serverHostedMembers.includes(memberId))
isValidated = await ctx.MyLife.testPartitionKey(memberId)
}
if(isValidated){
if( // validated
!ctx.state.locked
|| ( ctx.session.isAPIValidated ?? false )
|| await ctx.MyLife.isMemberHosted(memberId)
){
ctx.state.isValidated = true
ctx.state.mbr_id = memberId
ctx.state.assistantType = mTokenType(ctx)
Expand All @@ -308,12 +333,13 @@ function mTokenType(ctx){
const assistantType = mBotSecrets?.[token] ?? 'personal-avatar'
return assistantType
}
function mTokenValidation(_token){
return mBotSecrets?.[_token]?.length??false
function mTokenValidation(token){
return mBotSecrets?.[token]?.length??false
}
/* exports */
export {
availableExperiences,
entry,
experience,
experienceCast,
experienceEnd,
Expand All @@ -323,8 +349,8 @@ export {
experiencesLived,
keyValidation,
logout,
memory,
register,
story,
tokenValidation,
upload,
}
84 changes: 68 additions & 16 deletions inc/js/core.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ class MyLife extends Organization { // form=server
this.#avatar = await this.factory.getAvatar()
return await super.init(this.#avatar)
}
/* public functions */
/**
* Retrieves all public experiences (i.e., owned by MyLife).
* @returns {Object[]} - An array of the currently available public experiences.
Expand All @@ -262,7 +263,22 @@ class MyLife extends Organization { // form=server
if(!_mbr_id || _mbr_id===this.mbr_id) throw new Error('datacore cannot be accessed')
return await this.factory.datacore(_mbr_id)
}
/* public functions */
/**
* Submits and returns the journal or diary entry to MyLife via API.
* @public
* @todo - consent check-in with spawned Member Avatar
* @param {object} summary - Object with story summary and metadata
* @returns {object} - The story document from Cosmos.
*/
async entry(summary){
if(!summary.mbr_id?.length)
throw new Error('entry `mbr_id` required')
if(!summary.summary?.length)
throw new Error('entry `summary` required')
summary.being = 'entry'
summary.form = summary.form ?? 'journal'
return await this.summary(summary)
}
/**
* Server MyLife _Maht instantiation uses this function to populate the most current alerts in the modular factory memoryspace. Currently only applicable to system types, but since this is implemented at the `core.mjs` scope, we can account
* @public
Expand All @@ -274,6 +290,10 @@ class MyLife extends Organization { // form=server
async getMyLifeSession(){
return await this.factory.getMyLifeSession()
}
async hostedMemberList(){
let members = await this.hostedMembers()
return members.map(member=>member.mbr_id)
}
/**
* Returns Array of hosted members based on validation requirements.
* @param {Array} validations - Array of validation strings to filter membership.
Expand All @@ -282,6 +302,20 @@ class MyLife extends Organization { // form=server
async hostedMembers(validations){
return await this.factory.hostedMembers(validations)
}
/**
* Returns whether a specified member id is hosted on this instance.
* @param {string} memberId - Member id
* @returns {boolean} - Returns true if member is hosted
*/
async isMemberHosted(memberId){
const hostedMembers = await this.hostedMemberList()
const isHosted = hostedMembers.includes(memberId)
let isValidated = false
if(isHosted)
isValidated = await this.testPartitionKey(memberId)
console.log('isMemberHosted:', isHosted, isValidated, memberId)
return isValidated
}
/**
* Registers a new candidate to MyLife membership
* @public
Expand All @@ -291,26 +325,44 @@ class MyLife extends Organization { // form=server
return await this.factory.registerCandidate(candidate)
}
/**
* Submits a story to MyLife via API. Unclear if can be dual-purposed for internal, or if internal still instantiates API context.
* Submits and returns the memory to MyLife via API.
* @public
* @param {string} _mbr_id - Member id
* @param {string} _assistantType - String name of assistant type
* @param {string} _summary - String summary of story
* @todo - consent check-in with spawned Member Avatar
* @param {object} summary - Object with story summary and metadata
* @returns {object} - The story document from Cosmos.
*/
async memory(summary){
if(!summary.mbr_id?.length)
throw new Error('story `mbr_id` required')
if(!summary.summary?.length)
throw new Error('story `summary` required')
summary.being = 'story'
summary.form = 'memory'
return await this.summary(summary) // @todo - convert modular
}
/**
* Submits and returns a summary to MyLife via API.
* @param {object} summary - Object with story summary and metadata
* @returns {object} - The story document from Cosmos.
*/
async story(_mbr_id, _assistantType, storySummary){
const id = this.globals.newGuid
const _story = {
assistantType: _assistantType,
being: 'story',
form: _assistantType,
async summary(summary){
const {
being='story',
form='story',
id=this.globals.newGuid,
mbr_id,
title=`untitled ${ form }`,
} = summary
const story = {
...summary,
being,
form,
id,
mbr_id: _mbr_id,
name: `story_${_assistantType}_${_mbr_id}`,
summary: storySummary,
mbr_id,
name: `${ being }_${ title.substring(0,64) }_${ mbr_id }`,
}
const _storyCosmos = await this.factory.story(_story)
return this.globals.stripCosmosFields(_storyCosmos)
const savedStory = this.globals.stripCosmosFields(await this.factory.summary(story))
return savedStory
}
/**
* Tests partition key for member
Expand Down
11 changes: 6 additions & 5 deletions inc/js/factory-class-extenders/class-conversation-functions.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
async function mSaveConversation(_factory, _conversation){
const { thread, messages, ..._retainedProperties} = _conversation.inspect(true)
_retainedProperties.thread = _conversation.thread
_retainedProperties.messages = [] // populated separately as unshifted array to cosmos
await _factory.dataservices.pushItem(_retainedProperties)
async function mSaveConversation(factory, conversation){
const { thread, messages, ...properties} = conversation.inspect(true)
properties.thread = conversation.thread
properties.messages = [] // populated separately as unshifted array to cosmos
const savedConversation = await factory.dataservices.pushItem(properties)
console.log('mSaveConversation', savedConversation)
}
export {
mSaveConversation,
Expand Down
Loading

0 comments on commit 21dfce3

Please sign in to comment.