mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
- Added JSDoc and inline comments to `src/services/memory/index.js` to clarify module flow and internal invariants. - Fixed a race condition in `performSave` where `currentSavePromise` was cleared before the pending save was chained, potentially allowing concurrent writes. - Created `docs/memory-service-index-overview.md` with high-level architecture. - Verified changes with `npm test src/services/memory/index.js` and `npm run lint`. - Updated `TEST_LOG` and `task-logs` to reflect the fix. Co-authored-by: PR0M3TH3AN <[email protected]>
5.3 KiB
5.3 KiB
Memory Service Overview (src/services/memory/index.js)
This document provides a high-level overview of the src/services/memory/index.js module, which orchestrates memory ingestion, retrieval, pruning, and persistence for the TORCH agent system.
What this module does
src/services/memory/index.js acts as the central controller for the agent memory system. It coordinates:
- Ingestion: Validating raw events, generating summaries, and persisting them.
- Retrieval: Fetching relevant memories based on semantic similarity (or simple filtering) and updating usage stats.
- Pruning: Identifying and removing stale, unpinned memories to keep the store manageable.
- Persistence: Managing an in-memory store backed by a JSON file (
.scheduler-memory/memory-store.json), with serialized writes to prevent corruption. - Caching: Providing a short-lived cache for retrieval queries to improve performance within a single run.
It serves as the main API surface for agents and the scheduler to interact with long-term memory.
Key Flows
1. Ingestion Flow (ingestEvents)
- Validate: Checks input events against schema.
- Ingest: Calls
ingestMemoryWindow(iningestor.js) to process events. - Persist: Writes new records to the in-memory store and triggers a save.
- Invalidate: Clears the retrieval cache (
cache.clear()) because new data might change query results. - Telemetry: Emits metrics and telemetry events.
2. Retrieval Flow (getRelevantMemories)
- Check Cache: Returns cached results if the exact query params match.
- List/Filter: Fetches all memories or a filtered subset from the store.
- Rank: Calls
filterAndRankMemories(inretriever.js) to score relevance (e.g., using embeddings or keyword matching). - Update Usage: Updates
last_seentimestamp for retrieved memories (async, fire-and-forget). - Cache: Stores the ranked result in the retrieval cache.
- Return: Returns the top-k memories.
3. Pruning Flow (runPruneCycle)
- Identify Candidates: Calls
listPruneCandidates(inpruner.js) or scans the store for unpinned memories older thanretentionMs. - Check Mode:
off: Does nothing.dry-run: Returns candidates without deleting.on(default): Deletes candidates from the store.
- Persist: Saves the updated store if changes occurred.
- Invalidate: Clears the retrieval cache.
Public API
| Function | Description |
|---|---|
ingestEvents(events, options) |
Ingests raw agent events, creates memory records, and persists them. |
getRelevantMemories(params) |
Retrieves relevant memories for a query, updating usage stats. |
runPruneCycle(options) |
Prunes stale, unpinned memories based on retention policy. |
listMemories(filters, options) |
Lists memories with optional filtering (tags, agent_id, type) and pagination. |
inspectMemory(id, options) |
Retrieves a single memory record by ID. |
pinMemory(id, options) |
Pins a memory to prevent it from being pruned. |
unpinMemory(id, options) |
Unpins a memory, allowing it to be pruned if it becomes stale. |
markMemoryMerged(id, dest, options) |
Marks a memory as merged into another, effectively archiving it. |
triggerPruneDryRun(options) |
Simulates a prune cycle to preview what would be deleted. |
memoryStats(options) |
Returns aggregate statistics (counts, rates, estimated size) for observability. |
Usage Example
import { ingestEvents, getRelevantMemories } from './src/services/memory/index.js';
// 1. Ingest a new observation
await ingestEvents([{
agent_id: 'test-agent',
content: 'The api/v1/users endpoint requires Bearer auth.',
timestamp: Date.now(),
tags: ['api', 'auth']
}]);
// 2. Retrieve relevant context later
const memories = await getRelevantMemories({
agent_id: 'test-agent',
query: 'how to authenticate user api',
k: 5
});
console.log(memories);
Key Invariants & Assumptions
- Process-Local Truth: The
memoryStoreMap is the source of truth for the running process. Multi-process coordination (locking) is handled at the scheduler level, not here. - Serialized Writes:
saveMemoryStoreuses a promise queue (currentSavePromise,pendingSavePromise) to ensure writes tomemory-store.jsondo not overlap or race, preventing data corruption. - Cache Invalidation: Any mutation (ingest, prune, pin, merge) must clear the retrieval cache (
cache.clear()) to ensure consistency. - Fire-and-Forget Usage Updates:
updateMemoryUsageis often called without awaiting its save operation to minimize latency on the retrieval path.
When to Change
- Schema Updates: If the memory record structure changes (e.g., new fields), update
ingestor.jsandschema.js, and ensureindex.jspasses the new fields correctly. - Persistence Strategy: If moving from a JSON file to a real database (SQLite, Postgres), this file would need significant refactoring to replace the in-memory
Mapandfscalls with DB queries. - Concurrency: If multiple agents run in parallel processes writing to the same memory file without a coordinator, the file-based locking/serialization here is insufficient. You would need a real DB or a dedicated memory server process.