UNPKG

mahler

Version:

A automated task composer and HTN based planner for building autonomous system agents

201 lines • 6.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Task = void 0; const crypto_1 = require("crypto"); const assert_1 = require("../assert"); const lens_1 = require("../lens"); const path_1 = require("../path"); const view_1 = require("../view"); const context_1 = require("./context"); const instructions_1 = require("./instructions"); // Bind a task to a specific context function ground(task, args) { const templateParts = path_1.Path.split(task.lens); // Form the context path from the task lens and the // given task arguments const path = path_1.Path.from(templateParts.map((p) => { if (p.startsWith(':')) { const key = p.slice(1); (0, assert_1.default)(key in args, `Missing parameter '${key}' in context given to task '${task.id}', required by lens '${task.lens}'`); return String(args[key]); } return p; })); // We clone the target before passing it to the task as an action // could assign it to the state and modify it and we don't want actions // from methods altering the target from the parent method const target = structuredClone(args.target); const lensCtx = lens_1.Lens.context(task.lens, path, target); const context = context_1.Context.from(lensCtx); const { id, description: taskDescription } = task; const description = typeof taskDescription === 'function' ? taskDescription(context) : taskDescription; const condition = (s) => { const lens = lens_1.Lens.from(s, path); return task.condition(lens, { ...context, system: s, }); }; if (isActionTask(task)) { const { effect: taskEffect, action: taskAction } = task; const effect = (s) => { taskEffect(view_1.View.from(s, path), { ...context, system: s._ }); }; const action = (s) => taskAction(view_1.View.from(s, path), { ...context, system: s._ }); return Object.assign(action, { id, path: context.path, target, _tag: 'action', description, condition, effect, toJSON() { return { id, path: context.path, description, target: args.target, }; }, }); } const { expansion } = task; const method = (s) => task.method(lens_1.Lens.from(s, context.path), { ...context, system: s, }); return Object.assign(method, { id, path: context.path, target, _tag: 'method', description, condition, expansion, toJSON() { return { id, path: context.path, description, target: args.target, }; }, }); } /** * Check if a task is a method */ function isMethodTask(t) { return t.method != null && typeof t.method === 'function'; } /** * Check if a task or an instruction is an action */ function isActionTask(t) { return (t.effect != null && typeof t.effect === 'function' && t.action != null && typeof t.action === 'function'); } // We put this here instead of props so we don't export it externally when doing // export * from props function isActionProps(x) { return (typeof x.effect === 'function' || typeof x.action === 'function'); } function serialize(props) { // Serialize the task specification converting all elements to strings // including the function bodys where it applies const serialized = Object.keys(props).reduce((o, k) => ({ ...o, [k]: String(props[k]) }), {}); return (0, crypto_1.createHash)('sha256').update(JSON.stringify(serialized)).digest('hex'); } function from(taskProps) { const { op = 'update', condition: taskCondition = () => true } = taskProps; // Check that the path is valid const lens = path_1.Path.from(taskProps.lens ?? '/'); // Generate a deterministic id for // the task. This is useful for diagramming const id = serialize(taskProps); const opLabel = op === '*' ? 'modify' : op; // The default description is // update /a/b/c or // [method] update /a/b/c const description = (ctx) => `${opLabel} ${ctx.path}`; // Create operations require that the sub-element pointed by the value // does not exist yet let condition = taskCondition; if (op === 'create') { condition = (v, c) => v === undefined && taskCondition(v, c); } // Delete and update operations require that the sub-element pointed by the value // exists if (['delete', 'update'].includes(op)) { condition = (v, c) => v !== undefined && taskCondition(v, c); } // The task properties const tProps = (() => { if (isActionProps(taskProps)) { const { effect: taskEffect = () => void 0, action: taskAction = (v, c) => { taskEffect(v, c); // The action function needs to return a promise // but taskEffect returns `void` return Promise.resolve(); }, } = taskProps; let effect = taskEffect; let action = taskAction; if (op === 'delete') { // If the task defines a delete operation for the value pointed by // the lens, then we need to delete the property after the action succeeds effect = (v, c) => { taskEffect(v, c); v.delete(); }; action = async (v, c) => { await taskAction(v, c); v.delete(); }; } return { description, op: op, ...taskProps, lens, condition, effect, action, }; } else { return { description, op: op, expansion: instructions_1.MethodExpansion.DETECT, ...taskProps, lens, condition, }; } })(); // The final task spec. Typescript doesn't seem to // correctly infer the type here unfortunately const tSpec = { id, ...tProps }; const t = Object.assign((ctx) => { return ground(tSpec, ctx); }, tSpec); return t; } function of() { return { from, }; } exports.Task = { of, from, isMethod: isMethodTask, isAction: isActionTask, }; //# sourceMappingURL=tasks.js.map