tpmkms
Version:
Reusable libraries for Entodicton, a natural language to json converter
451 lines (420 loc) • 13 kB
JavaScript
const { knowledgeModule, where, debug } = require('./runtime').theprogrammablemind
const { defaultContextCheckProperties, defaultContextCheck } = require('./helpers')
const helpers = require('./helpers')
const helpers_conjunction = require('./helpers/conjunction')
const articles = require('./articles')
const evaluate = require('./evaluate')
const stm_tests = require('./stm.test.json')
class API {
initialize({ objects }) {
this._objects = objects
this.isAs = [
(child, parent) => child == parent
]
this._objects.mentioned = []
this._objects.variables = {}
this.idCounter = 0
this.maximumMentioned = 50
}
getId() {
return ++this.idCounter
}
addIsA(isA) {
if (!this.isAs.find( (f) => f == isA )) {
this.isAs.push(isA)
}
}
isA(child, parent) {
for (const isA of this.isAs) {
if (isA(child, parent)) {
return true
}
}
return false
}
getByType(type) {
return this._objects.mentioned.filter( (context) => this.isA(context.marker, type) )
}
remember(args) {
let concept, value, frameOfReference
if (!args.context) {
concept = args
} else {
concept = args.context
value = args.value
frameOfReference = args.frameOfReference
}
if (concept.pullFromContext) {
return
}
if (!frameOfReference) {
frameOfReference = this._objects
}
// TODO value should perhaps have been called id as in concept id and then value could be value
if (value) {
concept = { ...concept, pullFromContext: false }
} else {
concept.pullFromContext = false
}
if (value) {
if (concept.marker == 'unknown') {
if (concept.value) {
concept.marker = concept.value
}
}
concept.value = value
}
concept.fromSTM = true
concept.namespaced ??= {}
concept.namespaced.stm ??= {}
concept.namespaced.stm.id ??= this.getId()
frameOfReference.mentioned = (frameOfReference.mentioned || []).filter( (context) => context.namespaced?.stm && context.namespaced.stm.id != concept.namespaced.stm.id )
helpers.unshiftL(frameOfReference.mentioned, concept, this.maximumMentioned)
}
recall({ context, frameOfReference, useHierarchy=true, all, stopCondition = (() => false), condition = (() => true), filter = ((result) => result) } = {}) {
let mentioned = this._objects.mentioned.filter((m) => !m.namespaced?.stm?.deleted)
let reversed = false
if (frameOfReference) {
if (frameOfReference.namespaced?.stm?.mentioned) {
mentioned = [...frameOfReference[frameOfReference.namespaced.stm.mentioned]]
if (frameOfReference.namespaced.stm.reversed) {
mentioned.reverse()
reversed = true
}
} else {
if (typeof frameOfReference?.mentioned == 'string') {
mentioned = frameOfReference[frameOfReference.mentioned]
} else {
mentioned = frameOfReference.mentioned
}
}
}
if (!mentioned) {
return
}
const findPrevious = !!context?.stm_previous
const forAll = []
function addForAll(context) {
if (!forAll.find( (c) => c.namespaced.stm.id == context.namespaced.stm.id)) {
if (reversed) {
forAll.unshift(context)
} else {
forAll.push(context)
}
}
}
// care about value first
let findCounter = 0
for (const m of mentioned) {
if (stopCondition(m)) {
break
}
if (context.value && (context.value == m.marker || context.value == m.value)) {
findCounter += 1
if (findPrevious && findCounter < 2) {
continue
}
if (context.nameable_named && m.nameable_named) {
continue
}
if (condition(m)) {
if (all) {
addForAll(m)
} else {
return filter(m)
}
}
}
}
if (forAll.length > 0) {
return filter(forAll)
}
if (!useHierarchy) {
return
}
// care about marker second
findCounter = 0
for (const m of mentioned) {
if (stopCondition(m)) {
break
}
if (context.marker != 'unknown' && this.isA(m.marker, context.marker)) {
findCounter += 1
if (findPrevious && findCounter < 2) {
continue
}
if (context.nameable_named && m.nameable_named) {
continue
}
if (condition(m)) {
if (all) {
addForAll(m)
} else {
return filter(m)
}
}
}
// if (context.types && context.types.includes(m.marker)) {
if (context.types) {
for (const parent of context.types) {
if (stopCondition(m)) {
break
}
if (parent != 'unknown' && this.isA(m.marker, parent)) {
findCounter += 1
if (findPrevious && findCounter < 2) {
continue
}
if (context.nameable_named && m.nameable_named) {
continue
}
if (condition(m)) {
if (all) {
addForAll(m)
} else {
return filter(m)
}
}
}
}
}
}
if (forAll.length > 0) {
return filter(forAll)
}
findCounter = 0
if (context.types && context.types.length == 1) {
for (const m of mentioned) {
if (context.unknown) {
findCounter += 1
if (findPrevious && findCounter < 2) {
continue
}
if (stopCondition(m)) {
break
}
if (condition(m)) {
if (all) {
addForAll(m)
} else {
return filter(m)
}
}
}
}
}
if (forAll.length > 0) {
return filter(forAll)
}
}
getVariable(context) {
if (!context || context.marker == 'mentions') {
return
}
let valueNew = this.recall({ context, useHierarchy: false, condition: (context) => context.isVariable })
if (valueNew && valueNew.value) {
valueNew = valueNew.value
}
return valueNew
}
setVariable(variableName, value) {
this.remember({ context: { marker: variableName, isVariable: true }, value })
}
}
const api = new API()
const config = {
name: 'stm',
operators: [
"(<stm_current|current> ([memorable]))",
"(<stm_previous|previous> ([memorable]))",
"(([memorable]) <stm_before|before>)",
"([remember] (memorable/*))",
"([recall] (memorable/*))",
"([delete|delete,forget] ([deletable]))",
],
words: {
literals: {
"m1": [{"id": "memorable", scope: "testing", "initial": "{ value: 'm1' }" }],
"m2": [{"id": "memorable", scope: "testing", "initial": "{ value: 'm2' }" }],
},
},
bridges: [
{
id: 'deletable',
},
{
id: 'delete',
isA: ['verb'],
bridge: "{ ...next(operator), deletable: after[0], operator: operator, interpolate: [{ property: 'operator' }, { property: 'deletable' }] }",
semantic: async ({context, recall}) => {
const object = await recall({ context: context.deletable })
if (object) {
object.namespaced.stm.deleted = true
for (const callback of object.namespaced.stm.callbacks || []) {
callback.callback(object)
}
}
}
},
{
id: 'memorable',
isA: ['theAble', 'deletable'],
words: helpers.words('memorable'),
check: defaultContextCheckProperties(),
},
{
id: 'recall',
bridge: "{ ...next(operator), postModifiers: ['recallee'], recallee: after[0] }",
isA: ['verb'],
semantic: async ({context, recall, resolveResponse, e}) => {
const object = await recall({ context: context.recallee })
resolveResponse(context, object)
},
},
{
id: 'remember',
bridge: "{ ...next(operator), postModifiers: ['rememberee'], rememberee: after[0] }",
isA: ['verb'],
semantic: async ({context, api, e}) => {
let value = (await e(context.rememberee)).evalue
if (value == context.rememberee.value) {
value = context.rememberee
}
api.remember({ context: value })
},
},
{
id: 'stm_previous',
isA: ['adjective'],
bridge: '{ ...after[0], modifiers: ["stm_previous"], stm_previous: operator, pullFromContext: true }',
},
{
id: 'stm_current',
isA: ['adjective'],
bridge: '{ ...after[0], modifiers: ["stm_current"], stm_current: operator, pullFromContext: true }',
},
{
id: 'stm_before',
isA: ['adjective'],
bridge: '{ ...before[0], postModifiers: ["stm_previous"], stm_previous: operator, pullFromContext: true }',
},
],
semantics: [
{
where: where(),
match: ({context}) => context.marker == 'mentions' && context.evaluate && context.args?.context?.distributer,
apply: async ({callId, _continue, toList, context, kms, e, log, toArray, retry}) => {
context.args.filter ??= (r) => r
context.args.all = true
const oldFilter = context.args.filter
context.args.filter = (result) => {
return oldFilter(result)
}
_continue()
}
},
{
where: where(),
match: ({context}) => context.marker == 'mentions' && context.evaluate,
apply: ({context, kms, toList, resolveEvaluate}) => {
resolveEvaluate(context, kms.stm.api.recall(context.args))
}
},
{
where: where(),
notes: 'pull from context',
// match: ({context}) => context.marker == 'it' && context.pullFromContext, // && context.value,
match: ({context, callId}) => context.pullFromContext && !context.same, // && context.value,
apply: async ({callId, recall, toList, context, kms, e, log, retry}) => {
context.value = (await recall({ context }))
if (Array.isArray(context.value)) {
context.value = toList(context.value)
}
if (!context.value) {
context.evalue = { marker: 'answerNotKnown' }
return
}
const instance = await e(context.value)
if (instance.evalue && !instance.edefault) {
context.value = instance.evalue
}
if (context.evaluate) {
context.evalue = context.value
}
},
},
],
}
function initializer({config}) {
config.addArgs(({kms, e, toList}) => ({
remember: (args) => {
kms.stm.api.remember(args)
},
frameOfReference: (context, { mentioned, reversed } = {}) => {
context.namespaced ??= {}
context.namespaced.stm ??= {}
if (mentioned !== null) {
context.namespaced.stm.mentioned = mentioned // name of property that has the mentioned objects
}
if (reversed !== null) {
context.namespaced.stm.reversed = reversed // true iff the list is oldest first rather than newest first
}
},
addCallback: (context, callback) => {
// init
context.namespaced ??= {}
context.namespaced.stm ??= {}
context.namespaced.stm.callbackCounter ??= 1
context.namespaced.stm.callbacks ??= []
// meat
context.namespaced.stm.callbackCounter += 1
const counter = context.namespaced.stm.callbackCounter
context.namespaced.stm.callbacks.push({ callback, counter })
callback.removeCallback = () => {
context.namespaced.stm.callbacks = context.namespaced.stm.callbacks.filter((cb) => cb.counter != counter)
}
return counter
},
recall: async (args) => {
if (args.frameOfReference?.nameable_named) {
const result = await e(args.frameOfReference)
if (result.evalue) {
args.frameOfReference = result.evalue
}
}
const result = await e({ marker: 'mentions', args })
// evalue will return the argument if there is no evalue. dont want that for this case
if (!result.evalue) {
return
}
return helpers_conjunction.asList(helpers.toEValue(result), true)
},
}))
}
knowledgeModule( {
config,
api: () => new API(),
includes: [evaluate, articles],
initializer,
module,
description: 'short term memory',
test: {
name: './stm.test.json',
contents: stm_tests,
checks: {
context: [defaultContextCheck({ extra: ['pullFromContext', 'stm_id'] })],
objects: [
{
property: 'mentioned',
check: [{
property: (key) => typeof key == 'number',
check: [{
property: 'namespaced',
check: [{ property: 'stm', check: ['id', 'deleted', 'named'] }]
}]
}] ,
},
],
},
},
})