UNPKG

mahler

Version:

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

212 lines (211 loc) • 9.83 kB
import { Lens } from '../lens'; import type { AnyOp, Update } from '../operation'; import type { PathType, Root } from '../path'; import { Path } from '../path'; import { View } from '../view'; import type { TaskArgs, TaskOp } from './context'; import { Context } from './context'; import type { Action, Instruction, Method } from './instructions'; import { MethodExpansion } from './instructions'; import type { ActionTaskProps, ContextWithSystem, MethodTaskProps } from './props'; interface TaskSpec<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update> { /** * A unique identifier for the task. This is automatically generated * when constructing he task, and is not user configurable. */ readonly id: string; /** * The path to the element that this task applies to */ readonly lens: Path<TPath>; /** * The operation that this task applies to */ readonly op: TOp; /** * A descriptor for this task. The descriptor can be either a string or a Context * instance. The description does not receive the current state to allow actions to * be compared by their description (useful for testing and debugging). */ readonly description: string | ((c: Context<TState, TPath, TOp>) => string); /** * A condition that must be met for the task to be chosen by the planner or executed by * an agent. */ readonly condition: (s: Lens<TState, TPath>, ctx: ContextWithSystem<TState, TPath, TOp>) => boolean; } /** * An action task defines a primitive operation that can be chosen by a planner and * executed by an agent. Action tasks can be used to perform composite behaviors via * methods. * * Action tasks can be created via the Task constructor as follows * * ```typescript * const plusOne = Task.from({ * // A condition for chosing/executing the task * condition: (state: number, { target }) => state < target, * // The effect of the action is increasing the system * // counter by 1. This will be used during planning * effect: (state: View<number>) => ++state._, * // The actual action that will be executed * action: async (state: View<number>) => { * ++state._; * }, * // An optional description. Useful for testing * description: '+1', * }); * ``` * * An ActionTask is also a function that binds the task to a specific context. This allows * the taks to be used in methods. * * ```ts * const plusTwo = Task.from<number>({ * // We want this method to be chosen only if the difference between the current * // state and the target is bigger than one * condition: (state, { target }) => target - state > 1, * // The method returns two instances of the plusOne task bound to the given target * method: (_, { target }) => [plusOne({ target }), plusOne({ target })], * description: '+2', * }); * ``` */ export interface ActionTask<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update> extends TaskSpec<TState, TPath, TOp> { /** * The effect on the state that the task performs. * * The effect function will only be ran if the condition is met and developers * can trust that the condition will be checked beforehand. * * The effect function receives a view to the state, which allows * it to mutate the state. The effect function should not return * anything. * * If the task operation is `create`, the task constructor will add as * condition that the property pointed by the lens is undefined. The value will * be created before calling the effect function provided by the user. * * If the task operation is `delete`, the task constructor will add as a condition * that the property pointed by the lens is not undefined. The value will be deleted * automatically after the effect function provided by the user. * * @param view - A view to the state pointed by the lens. * @param ctx - The calling context for the task. It includes any lens properties and the system object */ readonly effect: (view: View<TState, TPath, TOp>, ctx: ContextWithSystem<TState, TPath, TOp>) => void; /** * TThe actual action the task performs. * * The action function will only be ran if the condition is met and developers * can trust that the condition will be checked beforehand. * * The action function receives a view to the state, which allows * it to mutate the state. The action function should not return * anything. * * If the task operation is `create`, the task constructor will add as * condition that the property pointed by the lens is undefined. The value will * be created before calling the action function provided by the user. * * If the task operation is `delete`, the task constructor will add as a condition * that the property pointed by the lens is not undefined. The value will be deleted * automatically after the action functions provided by the user. * * @param view - A view to the state pointed by the lens. * @param ctx - The calling context for the task. It includes any lens properties and the system object */ readonly action: (view: View<TState, TPath, TOp>, ctx: ContextWithSystem<TState, TPath, TOp>) => Promise<void>; /** * The task function binds the task to a context * * Grounding the task converts the specification into an instruction, that is, * something that can be evaluated by the planner. It does this by * contextualizing the task for a specific target. * * ActionTask --- ground --> Action * MethodTask --- ground --> Method */ (ctx: TaskArgs<TState, TPath, TOp>): Action<TState, TPath, TOp>; } /** * A method task defines a composite operation that can be chosen by a planner. * * A method can guide the planner towards following a certain path by providing * a sequence of operations to be used if conditions are suitable. * * Method tasks can be created via the Task constructor as follows * * ```ts * const plusTwo = Task.from<number>({ * // We want this method to be chosen only if the difference between the current * // state and the target is bigger than one * condition: (state, { target }) => target - state > 1, * // The method returns two instances of the plusOne task bound to the given target * method: (_, { target }) => [plusOne({ target }), plusOne({ target })], * description: '+2', * }); * ``` * * Methods can also reference methods for hierarchical task definition. */ export interface MethodTask<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update> extends TaskSpec<TState, TPath, TOp> { /** * The method expansion. The default is 'detect', meaning the planner will try to execute * the instructions returned by the method in parallel and go back to sequential expansion * if conflicts are detected. If sequential is chosen, the planner will jump straight to * sequential expansion. This is a workaround to handle those cases where detection may fail * due to instructios that read data handled by a parallel branch. */ readonly expansion: MethodExpansion; /** * The method to be called when the task is executed. * * The method should return a list of instructions to be used by the planner. * It should never modify the state object. * * if the method returns an empty list, this means there are no * further instructions that can be applied */ readonly method: (s: Lens<TState, TPath>, c: ContextWithSystem<TState, TPath, TOp>) => Instruction<TState> | Array<Instruction<TState>>; /** * The task function grounds the task * * Grounding the task converts the specification into an instruction, that is, * something that can be evaluated by the planner. It does this by * contextualizing the task for a specific target. * * ActionTask --- ground --> Action * MethodTask --- ground --> Method */ (ctx: TaskArgs<TState, TPath, TOp>): Method<TState, TPath, TOp>; } /** * Check if a task is a method */ declare function isMethodTask<TState = unknown, TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: Task<TState, TPath, TOp>): t is MethodTask<TState, TPath, TOp>; /** * Check if a task or an instruction is an action */ declare function isActionTask<TState = unknown, TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: Task<TState, TPath, TOp>): t is ActionTask<TState, TPath, TOp>; /** * A task is base unit of knowledge of an autonomous agent. */ export type Task<TState = unknown, TPath extends PathType = Root, TOp extends TaskOp = 'update'> = ActionTask<TState, TPath, TOp> | MethodTask<TState, TPath, TOp>; /** * Construct a new task (action or method) from a task specification */ declare function from<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update>(t: MethodTaskProps<TState, TPath, TOp>): MethodTask<TState, TPath, TOp>; declare function from<TState = unknown, TPath extends PathType = Root, TOp extends AnyOp = Update>(t: ActionTaskProps<TState, TPath, TOp>): ActionTask<TState, TPath, TOp>; interface TaskBuilder<TState> { from<TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: ActionTaskProps<TState, TPath, TOp>): ActionTask<TState, TPath, TOp>; from<TPath extends PathType = Root, TOp extends TaskOp = 'update'>(t: MethodTaskProps<TState, TPath, TOp>): MethodTask<TState, TPath, TOp>; } declare function of<TState>(): TaskBuilder<TState>; export declare const Task: { of: typeof of; from: typeof from; isMethod: typeof isMethodTask; isAction: typeof isActionTask; }; export {};