agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
98 lines • 5.09 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.narrativePipeline = void 0;
/**
* narrativePipeline — beats-based memory preset.
*
* Compresses each turn into `NarrativeBeat`s on write, recalls them
* as a story paragraph on read. Two subflows, shared store:
*
* READ : LoadRecent → PickByBudget → FormatAsNarrative
* WRITE : ExtractBeats → WriteBeats
*
* Contrast with `defaultPipeline` (raw messages, block-formatted) —
* the read side is structurally identical; only the format stage and
* write path differ. `LoadRecent` + `PickByBudget` don't care whether
* the entry payload is a `Message` or a `NarrativeBeat` — they operate
* on `MemoryEntry<T>` generically.
*
* **Why narrative memory?**
* Raw-message recall grows linearly with conversation length and
* loses salience signal. Beats compress 100 turns of chat into
* ~20 summary sentences, each with `refs[]` traceable back to the
* source messages. The picker prefers high-importance beats; the
* formatter composes them into a single cohesive paragraph.
*
* **Default extractor**: `heuristicExtractor()` — zero-dep, zero-cost.
* Produces sensible-but-baseline beats. Opt into `llmExtractor({ provider })`
* when you want semantic quality.
*
* @example
* Most consumers reach for `narrativePipeline` indirectly through
* `defineMemory({ type: MEMORY_TYPES.NARRATIVE, strategy: { kind:
* MEMORY_STRATEGIES.EXTRACT, extractor: 'pattern' | 'llm' }, store })`.
*
* @example Direct usage (low-level — custom flowchart composition):
* ```ts
* import { narrativePipeline, llmExtractor, InMemoryStore } from 'agentfootprint/memory';
*
* // Cheap default — heuristic beats, no LLM cost.
* const pipeline = narrativePipeline({ store: new InMemoryStore() });
*
* // Or opt into LLM-backed beats (pass any LLMProvider):
* const pipelineHQ = narrativePipeline({
* store: new InMemoryStore(),
* extractor: llmExtractor({ provider: yourLLMProvider }),
* });
* ```
*/
const footprintjs_1 = require("footprintjs");
const loadRecent_js_1 = require("../stages/loadRecent.js");
const pickByBudget_js_1 = require("../stages/pickByBudget.js");
const extractBeats_js_1 = require("../beats/extractBeats.js");
const writeBeats_js_1 = require("../beats/writeBeats.js");
const formatAsNarrative_js_1 = require("../beats/formatAsNarrative.js");
const heuristicExtractor_js_1 = require("../beats/heuristicExtractor.js");
/**
* Build the narrative read + write pipelines sharing a single store.
* Returns `{ read, write }` ready to be passed to `Agent.memory()` via the appropriate `defineMemory` config (or used directly via `mountMemoryRead`/`mountMemoryWrite`).
*/
function narrativePipeline(config) {
const extractor = config.extractor ?? (0, heuristicExtractor_js_1.heuristicExtractor)();
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 }),
...(config.formatShowRefs !== undefined && { showRefs: config.formatShowRefs }),
...(config.formatLeadIn !== undefined && { leadIn: config.formatLeadIn }),
};
const extractConfig = {
extractor,
...(config.writeTier && { tier: config.writeTier }),
...(config.writeTtlMs !== undefined && { ttlMs: config.writeTtlMs }),
};
// ── Read subflow: LoadRecent → PickByBudget (decider + 3 branches) → FormatAsNarrative
let readBuilder = (0, footprintjs_1.flowChart)('LoadRecent', (0, loadRecent_js_1.loadRecent)(loadConfig), 'load-recent', {
description: 'Load N most-recent beats from storage into scope.loaded',
});
readBuilder = (0, pickByBudget_js_1.pickByBudget)(pickConfig)(readBuilder);
const read = readBuilder
.addFunction('Format', (0, formatAsNarrative_js_1.formatAsNarrative)(formatConfig), 'format-as-narrative', 'Compose selected beats into a story paragraph; writes scope.formatted')
.build();
// ── Write subflow: ExtractBeats → WriteBeats
const write = (0, footprintjs_1.flowChart)('ExtractBeats', (0, extractBeats_js_1.extractBeats)(extractConfig), 'extract-beats', { description: 'Compress scope.newMessages into NarrativeBeat entries' })
.addFunction('WriteBeats', (0, writeBeats_js_1.writeBeats)({ store: config.store }), 'write-beats', 'Batch-persist extracted beats via store.putMany')
.build();
return { read, write };
}
exports.narrativePipeline = narrativePipeline;
//# sourceMappingURL=narrative.js.map