Skip to content

catello09/agent-memory-store

Repository files navigation

agent-memory-store

Persistent memory layer for TypeScript AI agents — semantic vector search plus SQLite-backed rows in one API.

TypeScript ChromaDB License: MIT Node.js

What is this?

agent-memory-store is a hybrid memory layer for TypeScript agents:

  • Vector similarity (semantic recall) via ChromaDB or an in-memory backend for tests.
  • Structured storage via libSQL (SQLite-compatible file) for transactional, filterable records.

Most chat agents forget context after each session. This library keeps episodic, semantic, and procedural memories on disk, with hybrid retrieval (meaningful search plus SQL-style filters on metadata, time, and type).

Features

Feature Description
Hybrid storage Embeddings in Chroma (or memory) + rows in SQLite
Persistent SQL Memories survive restarts (MEMORY_SQLITE_PATH)
Memory types episodic, semantic, procedural
Embedders OpenAI, Ollama, Transformers.js (optional), or a custom function
Time-aware queries where + orderBy + limit (structured filters over stored rows)
Consolidation Pluggable rules to merge or forget memories
REST API Optional HTTP server on port 3000 (configurable)
Snapshots createSnapshot / restoreSnapshot for SQLite + vector export

Requirements

  • Node.js 22+
  • For semantic search in production, run a Chroma server (for example Docker on port 8000) and set MEMORY_VECTOR_BACKEND=chroma and CHROMA_URL.
  • For OpenAI embeddings, set OPENAI_API_KEY. Without it, the bundled REST server falls back to a deterministic hash embedder (fine for demos; not semantically meaningful).

Installation

cd agent-memory-store
npm install
npm run build
Script Description
npm run build Compile TypeScript → dist/
npm run dev REST API with hot reload (tsx)
npm start REST API from dist/ (after build)
npm test Unit tests
npm run verify build + test (CI-friendly)
npm run test:integration Chroma HTTP test (skipped if no server)
npm run test:performance One recall latency smoke test
npm run test:watch Vitest watch mode

Configuration

Copy .env.example to .env.local and adjust:

Variable Purpose
OPENAI_API_KEY OpenAI embeddings when embedder: 'openai'
OLLAMA_BASE_URL Default http://127.0.0.1:11434
OLLAMA_EMBED_MODEL e.g. nomic-embed-text
MEMORY_SQLITE_PATH SQLite file (default ./data/memory.db)
MEMORY_CHROMA_PATH Used to derive the Chroma collection name suffix when MEMORY_VECTOR_BACKEND=chroma (not a filesystem path for the HTTP client)
CHROMA_URL Chroma HTTP API (default http://127.0.0.1:8000)
MEMORY_VECTOR_BACKEND memory (default) or chroma
MEMORY_SNAPSHOT_DIR Snapshot directory (default ./snapshots)
PORT REST API port (default 3000)

Library-only options (constructor): openaiApiKey, openaiModel, ollamaBaseUrl, ollamaModel (override env for named embedders); chromaUrl, chromaPath or collectionSuffix (Chroma collection naming); embeddingCacheSize, snapshotDir.

Vector backends

  • memory — in-process vectors; fastest for unit tests. Vectors are not persisted (SQL rows are still persisted).
  • chroma — persistent vectors through a Chroma HTTP service.

Quick start (library)

import { MemoryStore } from "agent-memory-store";
import { createHashEmbedder } from "agent-memory-store"; // local dev / tests

const store = new MemoryStore({
  embedder: createHashEmbedder(64), // or 'openai' | 'ollama' | 'transformers' | async (t) => [...]
  sqlitePath: "./data/memory.db",
  vectorBackend: "memory",
});

await store.init();

const id = await store.remember({
  content: "User prefers concise answers with bullet points",
  type: "semantic",
  metadata: { topic: "preferences", importance: 0.9 },
});

const similar = await store.recall({
  query: "How should I format responses?",
  limit: 3,
});

const important = await store.query({
  where: {
    type: "semantic",
    "metadata.importance": { $gte: 0.8 },
  },
  orderBy: { timestamp: "desc" },
  limit: 5,
});

REST API

npm run dev
# or: npm run build && npm start

Endpoints (JSON):

Method Path Description
GET /health Liveness
POST /api/memories Body: { content, type, metadata?, id?, timestamp? }{ id } (content string; type: episodic, semantic, or procedural)
POST /api/recall Body: { query, limit?, type?, minSimilarity? } (query non-empty string)
POST /api/query Body: { where?, orderBy?, limit? }
DELETE /api/memories/:id Delete one memory
POST /api/consolidate Body: { dryRun?: boolean }{ mergedPairs } (no-op unless rules were set via setConsolidationRules in code)
GET /api/working-memory?limit= Recent episodic memories
POST /api/snapshots Create snapshot → { id }
POST /api/snapshots/:id/restore Restore snapshot

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    MemoryStore                               │
│  remember()  recall()  query()  forget()  consolidate()     │
└─────────────────────────────┬───────────────────────────────┘
                              │
            ┌─────────────────┼─────────────────┐
            ▼                 ▼                 ▼
   ┌────────────────┐  ┌──────────────┐  ┌─────────────────┐
   │ Vector backend │  │ LibSQL       │  │ Embedding cache │
   │ Chroma / RAM   │  │ (SQLite file)│  │ (LRU)           │
   └────────────────┘  └──────────────┘  └─────────────────┘

API reference (MemoryStore)

init()

Creates the SQLite schema if needed. Call once before other methods.

close()

Closes the libSQL connection (call when shutting down a long-running process).

remember(memory)

Stores a memory; returns id. Embeddings are generated automatically unless embedding is provided.

type must be one of episodic, semantic, or procedural (validated at runtime so bad JSON cannot corrupt the store).

content must be a string. timestamp may be a Date or an ISO string (typical for JSON bodies from the REST API).

recall({ query, limit?, type?, minSimilarity? })

Semantic search. query must be a non-empty string (whitespace-only is rejected). minSimilarity is applied after converting Chroma distance to a 0–1 style score (see implementation). Tune for your embedding model.

query({ where?, orderBy?, limit? })

Structured filters over stored rows, including:

  • type, timestamp (with $gte, $lte, …) — timestamp bounds may be Date, epoch ms, or ISO strings (as in JSON over REST),
  • metadata.someKey for nested metadata (dot path).

forget(id)

Deletes from SQL and the vector index.

consolidate({ dryRun? })

Runs setConsolidationRules handlers. Returns the number of merge operations performed (forget rules run but are not counted).

workingMemory(limit?)

Latest episodic memories (by timestamp). Invalid or non-positive limit values fall back to 20.

createSnapshot() / restoreSnapshot(id)

Copies the SQLite file and exports vectors to MEMORY_SNAPSHOT_DIR/<id>/, then can restore both stores.

setConsolidationRules(rules)

Provide merge rules (condition + merge) or forget rules (condition + action: 'forget').

Optional helpers (same package):

  • defaultMergeCondition(a, b, minSim?) — same-type semantic memories with cosine similarity ≥ minSim.
  • defaultMergeFn(a, b) — concatenates content and merges metadata.

SQLite paths

MEMORY_SQLITE_PATH may be a plain path (./data/memory.db) or a libSQL-style file: URL (file:./data/memory.db). Directory creation and snapshot file copies use a normalized filesystem path automatically.

Docker

A sample docker-compose.yml is included (Chroma + API). Build the image after npm run build:

npm run build
docker compose build
docker compose up -d

Requires Docker; set OPENAI_API_KEY for real embeddings or rely on the server’s hash embedder for smoke tests only.

Testing

See the Scripts table for npm test, npm run verify, npm run test:performance, and npm run test:watch.

Chroma: tests/chroma.integration.test.ts is excluded from default npm test. Run npm run test:integration to probe CHROMA_URL first — if no server responds, the suite is skipped (exit 0); with Chroma running, the test executes.

Project layout

agent-memory-store/
├── src/
│   ├── MemoryStore.ts
│   ├── util/          # SQLite path helpers for fs
│   ├── sql/           # LibSQL adapter + where helpers
│   ├── vector/        # Chroma + in-memory adapters
│   ├── embedding/     # OpenAI, Ollama, transformers (optional), hash
│   ├── consolidation/
│   └── api/           # Express REST + server entry
├── tests/             # `*.test.ts` + optional `chroma.integration.test.ts`
├── vitest.config.ts
├── docker-compose.yml
├── Dockerfile
├── .env.example
├── package.json
├── LICENSE
└── tsconfig.json

License

MIT — see LICENSE. Helpers isMemoryType and assertMemoryType are exported for callers validating payloads.

About

TypeScript agent memory layer: semantic vector recall + SQLite-backed storage, Chroma or in-memory vectors, REST API, MIT.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages