UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

122 lines • 6.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.defaultPipeline = void 0; /** * defaultPipeline — the 90%-use-case memory preset. * * Composes Layer 2-3 stages into two flowchart subflows that the wire * layer mounts inside the agent's main flowchart: * * READ : LoadRecent → [Summarize] → [FilterByDecay] → PickByBudget → * FormatDefault * WRITE : WriteMessages * * The two bracketed stages are compiled only when their config is present * (`summarize`, `decay`) — an absent option means an absent stage, never a * stage that runs and does nothing. * * Why this particular composition? * - Load-then-pick-then-format matches the cognitive sequence: * retrieve candidates, choose what fits, present it. * - Single-stage write keeps the persistence story simple for Phase 1; * Phase 1.5 adds a second stage (extractFacts) to the write side. * * This preset is intentionally opinionated. Users who need more * control should compose their own FlowChart and pass it to * `.memory()` directly — the preset is teaching code, not a * one-size-fits-all. * * **Build once, mount many.** Call `defaultPipeline(config)` at application * startup (or whenever the config changes). The returned `{read, write}` * are immutable compiled FlowChart objects safe to share across many * agent builds and many `.run()` calls. Rebuilding per-turn is wasteful — * the stages capture their config at build time and don't read it later. * * Most consumers should use `defineMemory({ type: MEMORY_TYPES.EPISODIC, * strategy: { kind: MEMORY_STRATEGIES.WINDOW, ... }, store })` instead * of calling `defaultPipeline()` directly — the factory dispatches * onto this pipeline under the hood. Use `defaultPipeline()` directly * only when composing memory subflows into a non-Agent flowchart via * `mountMemoryRead`/`mountMemoryWrite`. * * @example Direct usage (low-level — custom flowchart composition): * ```ts * import { defaultPipeline, mountMemoryRead, InMemoryStore } from 'agentfootprint/memory'; * import { flowChart } from 'footprintjs'; * * const pipeline = defaultPipeline({ * store: new InMemoryStore(), * loadCount: 20, * reserveTokens: 512, * }); * * let builder = flowChart('Seed', seedFn, 'seed'); * builder = mountMemoryRead(builder, { pipeline }); * // ... continue building your custom flowchart * ``` */ const footprintjs_1 = require("footprintjs"); const loadRecent_js_1 = require("../stages/loadRecent.js"); const filterByDecay_js_1 = require("../stages/filterByDecay.js"); const summarize_js_1 = require("../stages/summarize.js"); const pickByBudget_js_1 = require("../stages/pickByBudget.js"); const formatDefault_js_1 = require("../stages/formatDefault.js"); const writeMessages_js_1 = require("../stages/writeMessages.js"); /** * Build the default read + write pipelines sharing a single store. * Returns two FlowChart subflows ready to be mounted by the wire layer. */ function defaultPipeline(config) { // Explicit per-stage config construction — keeps each stage's defaults // visible in the preset source (which doubles as teaching code). const loadConfig = { store: config.store, ...(config.loadCount !== undefined && { count: config.loadCount }), ...(config.tiers && { tiers: config.tiers }), }; const pickConfig = { ...(config.reserveTokens !== undefined && { reserveTokens: config.reserveTokens }), ...(config.minimumTokens !== undefined && { minimumTokens: config.minimumTokens }), ...(config.maxEntries !== undefined && { maxEntries: config.maxEntries }), }; const formatConfig = { ...(config.formatHeader !== undefined && { header: config.formatHeader }), ...(config.formatFooter !== undefined && { footer: config.formatFooter }), }; const writeConfig = { store: config.store, ...(config.writeTier && { tier: config.writeTier }), ...(config.writeTtlMs !== undefined && { ttlMs: config.writeTtlMs }), }; // Compose: LoadRecent → [Summarize] → [FilterByDecay] → [PickDecider → // skip-empty | skip-no-budget | pick] → Format // pickByBudget is a builder-extension — it appends a decider + 3 // branches to the pipeline so "why did / didn't we inject memory?" is // answerable via FlowRecorder.onDecision evidence, not just emit events. let readBuilder = (0, footprintjs_1.flowChart)('LoadRecent', (0, loadRecent_js_1.loadRecent)(loadConfig), 'load-recent', { description: 'Read N most-recent entries from storage into scope.loaded', }); if (config.summarize !== undefined) { // Directly after the load, and before every stage that trims: decay and // the budget picker both decide against what recall CONTAINS, and what // recall contains is not settled until the fold has happened. readBuilder = readBuilder.addFunction('Summarize', (0, summarize_js_1.summarize)({ ...config.summarize, store: config.store, ...(config.writeTtlMs !== undefined && { ttlMs: config.writeTtlMs }), }), 'summarize', 'Fold entries older than the verbatim tail into one stored summary entry'); } if (config.decay !== undefined) { // Before the picker: what has faded is not a budget question, and the // budget is better spent on entries that are still worth something. readBuilder = readBuilder.addFunction('FilterByDecay', (0, filterByDecay_js_1.filterByDecay)(config.decay), 'filter-by-decay', 'Drop loaded entries whose age-decayed score is below the floor'); } readBuilder = (0, pickByBudget_js_1.pickByBudget)(pickConfig)(readBuilder); const read = readBuilder .addFunction('Format', (0, formatDefault_js_1.formatDefault)(formatConfig), 'format-default', 'Render selected entries as a system message; writes scope.formatted') .build(); const write = (0, footprintjs_1.flowChart)('WriteMessages', (0, writeMessages_js_1.writeMessages)(writeConfig), 'write-messages', { description: 'Persist new turn messages to storage' }).build(); return { read, write }; } exports.defaultPipeline = defaultPipeline; //# sourceMappingURL=default.js.map