Skip to main content

Production-grade memory infrastructure for AI agents — tiered STM / MTM / LTM with first-class supersession and bi-temporal queries.

Project description

Continuum

Production-grade memory infrastructure for AI agents. Tiered storage (STM / MTM / LTM), first-class supersession and bi-temporal queries, cost-efficient retrieval. Plugs in under whatever reasoner you're using — LangGraph, AutoGen, custom — and provides the memory layer they all lack.

git clone https://github.com/Genkryptos/Continuum.git && cd Continuum
make demo-chat       # 60-second walkthrough, no infrastructure, no API key
make bench-all       # the four benchmarks Continuum is built to win

What it is

Most "memory" for LLM agents is one of:

  • a vector database (great recall, no notion of "current" vs "stale" facts),
  • an append-only chat history (no organization, gets expensive fast),
  • a manually-curated profile object (works until the user changes their mind).

Continuum is the operational memory layer: tiered storage so recent conversation, mid-term summaries, and long-term facts are addressable separately; supersession — superseded LTM facts are stamped invalidated_at (never deleted), so every read filters on invalidated_at IS NULL and the system always knows which version is current; and bi-temporal columnsvalid_from/valid_to (world-time) plus created_at/invalidated_at (system-time) — so it can answer "what did the user say about X as of date Y?", including retroactive corrections.

It is not a reasoning engine. The framework's value-prop is what the layer below the reasoner should have been doing all along, with benchmark numbers below to back it up.

Why this exists — the headline measurements

The robust, reproducible wins are the deterministic ones (they don't move between runs); the LongMemEval number is honest but carries reader variance.

benchmark Continuum naive baseline
Supersession correctness (50 scripted updates) 100 % 38 %
Bi-temporal "as of date Y" (20 scripted timelines) 100 % 75 %
LongMemEval-S (500 Q, judged, gpt-oss-120b) ~74 % (73.6 – 75.6 % across repeated runs) 60.8 % (v1.0) · 34.4 % (May ceiling)
↳ same-setup per-category (control run) multi-session 62 % · temporal 75 % · KU 64 % · assistant 96 % · user 91 %
Retrieval recall @ 4 (200-session synthetic corpus) 55 % 55 % (tied — recency signal absent)
Ingest p50 / session (1 user turn) 0.18 ms + 6 LLM-extraction calls 0.00 ms (raw list)

Sources: LongMemEval-S numbers are documented in the v1 findings report and regenerated with make repro-everything (raw run outputs are gitignored, not committed); synthetic benchmarks from bench/, reproducible via make bench-all (~60 s, no infra, no API key).

Honest headline — read this. On LongMemEval-S the judged number is ~74 % (73.6 – 75.6 % across repeated same-setup runs). gpt-oss-120b has ~±3–5 pp per-category run-to-run variance, so a single run is not a stable estimate: the 76.4 % in earlier v2.0 notes was a favorable draw, and read-side additions (a bounded reflect pass + vote-of-3) are within that noise of the no-additions baseline (73.8 %) — approximately accuracy-neutral overall. Per-category figures swing between runs and should not be read as fixed. The reproducible wins are the two deterministic benches above — supersession and bi-temporal "as of date Y", both 100 % — which don't move. We also disproved four "have the memory layer fix the reader" levers (synthesis, router, distillation, temporal code-math — all net-negative), and learned the methodology lesson that measuring gains only on known failures overstates them. Full write-up: docs/limitations.md · docs/report.md.

v2.0retrieval reranking (architecture-native — no new model, no compression). Over-fetch the candidate pool, then a cross-encoder (continuum/retrieval/reranker.py) reorders it and keeps the best 24:

  • Lifts recall on the retrieval-bound category — multi-session 57.9% → 69.9% (+12pp, n=133 A/B) — with a +2.3pp temporal bonus; knowledge-update flat; gated off for single-session-preference (it reorders away the rubric turns).
  • Honest note: this improves retrieval recall, but on the full-500 judged aggregate it lands within gpt-oss-120b's run-to-run noise of the baseline. The judged number is ~74 % (see the honest headline above), not the 76.4 % an earlier favorable single draw reported. The win here is multi-session retrieval quality, not a lift in the headline accuracy.

v1.160.8% → 72.8% judged (confirmed full-500, gpt-oss-120b), from two memory-attributable fixes — no new model, no reasoning loop, just surfacing data the schema already had:

  • Temporal date-surfacing (the driver): temporal questions were answered with the turn dates stripped from the prompt (the model literally replied "no dates available"). Surfacing each turn's date + the reference "now" lifted temporal 41.4% → 72.9% (+32pp) — confirmed by an n=133 A/B (signal +33pp vs a 0.8pp A/A noise floor).
  • Supersession-recency: mark the current (non-invalidated) LTM facts and prefer them over stale turns → knowledge-update ~+4.5pp (n=78 A/B).

Both gated to their categories, so the solved ones (single-session 96–98%) didn't move. Honest note: 90%+ on this benchmark needs a frontier reader — our gains are what the memory layer earns at a fixed mid-tier model. The aggregation-prompt and preference levers were tested and cut as net-neutral / unmeasurable (see findings/roadmap_v1.1.md).

v1.0.0 — Continuum broke the May 2026 ~34% LongMemEval-S ceiling to 60.8% judged — and did it without the iterative reasoning we predicted we'd need. What actually moved the number (a stronger answerer + clean direct retrieval + honest scoring), and the reasoning loop we built and cut as net-negative, are documented in findings/reasoning_loop_2026-06.md. A LOCOMO head-to-head vs Mem0 is preliminary (clean run pending) and not yet a published claim.

The supersession + bi-temporal wins aren't tunable parameters — they're consequences of the schema. Append-only stores and vector databases cannot reach these numbers without re-implementing this architecture on top.


The 60-second demo

make demo-chat

Runs a scripted walkthrough where the user:

  1. introduces themselves with I live in NYC and just adopted a dog named Rex → Continuum extracts user.location = NYC and user.pets.dog = Rex into LTM.
  2. updates: I just moved to Boston → the NYC fact is marked superseded_by the Boston one. Both are still in storage; only Boston is "current".
  3. queries /show ltm → current facts only (Boston + Rex).
  4. queries /show ltm --all → full history with the supersession edge visible.
  5. /query "where do I live" → returns Boston, not NYC, by construction.

The full demo runs in 63 ms with the canned LLM (no API key). It's the canonical show of what bench-supersession proves as a number.

See examples/chat_agent/ for source + the interactive REPL.


Architecture in one diagram

┌────────────────────────────────────────────────────────────────────┐
│                       ContinuumSession                             │
│                                                                    │
│   process_turn(user_msg)                                           │
│        │                                                           │
│        ▼                                                           │
│   ┌────────┐    ┌──────────┐   ┌─────────┐   ┌──────────────┐     │
│   │  STM   │ →  │ Retriever│ → │Optimizer│ → │  Responder   │     │
│   │ recent │    │ cosine + │   │ chain   │   │ your LLM     │     │
│   │ turns  │    │ composite│   │ (compress│   │ + ctx        │     │
│   │        │    │ scorer   │   │  budget) │   │              │     │
│   └────┬───┘    └──────────┘   └─────────┘   └──────┬───────┘     │
│        │                                            │              │
│        │ promotion (background queue)               │ reply        │
│        ▼                                            ▼              │
│   ┌────────┐                                  to caller            │
│   │  MTM   │ ← session summaries (LLM-generated)                   │
│   └────┬───┘                                                       │
│        ▼                                                           │
│   ┌────────┐                                                       │
│   │  LTM   │ ← atomic facts + entities + supersession             │
│   │ pgvector│   + bi-temporal columns (valid_from, invalidated_at)│
│   └────────┘                                                       │
└────────────────────────────────────────────────────────────────────┘

Each box is a swappable component. The retriever and optimizer chain are protocol-based; the stores live behind STMProtocol / MTMProtocol / LTMProtocol so the in-memory variant works for local dev and the Postgres variant scales to production.


Quick start (no infra)

Use it in Python — the library API (pip install continuum-memory):

import asyncio
from continuum import Memory

async def main():
    mem = Memory.in_memory()                        # zero-config — no Postgres, no model download
    async with mem:
        await mem.add("I moved to Boston")
        await mem.add("Actually I'm in NYC now")    # supersession handles the update
        print(await mem.recall("where do I live?"))
        print(await mem.current("user", "residence"))  # the resolved, current value
        print(await mem.timeline("residence"))          # bi-temporal history, oldest→newest

asyncio.run(main())

On the Postgres path there is a sixth verb, forget — maintenance for a store that would otherwise only ever grow:

report = await mem.forget(unused_for=timedelta(days=180))   # dry run by default
print(report.matched, report.samples)                        # see it before you mean it
await mem.forget(unused_for=timedelta(days=180), dry_run=False)

It is deliberately timid: only facts that are no longer true (superseded) and untouched for unused_for are eligible, it is scoped to the namespace, capped by limit, and it retires rows the same way supersession does — invalidated_at is set, nothing is deleted, so a policy you regret is recoverable. Nothing prunes on a timer, and it is not exposed as an MCP tool: an agent should not be able to decide to forget things about you.

Or plug it into any MCP client (Claude Code, Cursor, …) with zero glue — see docs/mcp.md: pip install "continuum-memory[mcp]" then continuum-mcp.

Supersession + bi-temporal history are strongest on the Postgres path (below); Memory.in_memory() is the zero-setup path for demos and tests. Honest scope and known limits: docs/limitations.md.

From source (demo + benchmarks):

git clone https://github.com/Genkryptos/Continuum.git && cd Continuum

# 1. Install Continuum + its dev tooling
pip install -e .

# 2. See the 60-second story
make demo-chat

# 3. Reproduce the benchmarks
make bench-all

# 4. (Optional) reproduce the v1 LongMemEval-S numbers
#    answerer = gpt-oss-120b via OpenRouter; non-reasoning judge.
#    The dataset is fetched on demand (not committed to the repo).
export OPENROUTER_API_KEY=sk-or-…
make repro-everything

That's the complete loop. No Postgres needed for the demo or any of the benchmarks. The full production path with Postgres+pgvector is documented under continuum/stores/postgres/ and exercised by the integration tests under tests/integration/.

Production path (Postgres) + verifying your .env

cp .env.example .env       # fill in a provider key and/or CONTINUUM_DB_DSN
make db-up                 # start Postgres + pgvector (docker-compose), wait until ready
make db-migrate            # create the pgvector extension + LTM schema
make check-env             # ✓/✗ report: config loads, provider key, DB reachable + schema, smoke
make run                   # interactive chat REPL on a real Postgres-backed session

make run (i.e. python -m continuum.chat) starts an interactive you> REPL on a real ContinuumSessionPostgresSTM + PostgresLTM + the hybrid Retriever + an OpenRouter responder. Every turn persists to Postgres and is indexed for recall, so quitting and re-running with the same --session remembers the conversation. make run keeps the embedder off (LTM sparse/trigram + STM recency, no model download); use make run-full for dense semantic recall (downloads bge-m3, ~2.3 GB, once). Useful flags: --session <id> (named persistent session), --model <id> (OpenRouter model, default openai/gpt-4o-mini), --in-memory (no DB — also make run-mem), --mock (deterministic, no LLM call). Inside the REPL: /help, /search <q>, /stats, /session <id>, /exit. Pass extra flags via make run ARGS="--session alice".

make check-env (i.e. python -m continuum.doctor) is the "did I set this up right?" command — it loads your config, detects which LLM provider key is present (in .env or your shell), probes the configured Postgres DSN for the pgvector extension, and runs one in-memory turn end-to-end. A missing key or unreachable DB is a warning (the offline path still works); a broken config or a session that won't start is a failure (non-zero exit, so it works in CI too). Add --ping to validate each provider key against a live read-only API call, or --full to actually load the embedder.

The compose defaults match .env.example's DSN (postgresql://postgres:postgres@localhost:5432/continuum). If your .env uses different credentials, set POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB (env or shell) before make db-up so the two agree.


Make targets

target what
make demo-chat scripted 60-second chat-agent walkthrough (in-memory)
make run interactive chat REPL on a real Postgres-backed session (embedder off)
make run-full same, with the embedder for dense recall (downloads bge-m3)
make db-up / make db-down start / stop local Postgres+pgvector (docker-compose)
make db-migrate apply migrations (pgvector ext + LTM schema)
make check-env verify your .env: config, provider key, DB+schema, smoke
make bench-ingest ingest throughput (Continuum vs raw list vs mem0)
make bench-retrieval retrieval recall@k vs naive cosine
make bench-supersession the killer feature — 100 % vs 38 %
make bench-bitemporal "as of date Y" lookups — 100 % vs 75 %
make bench-all run all four benchmarks in sequence
make bench-locomo LOCOMO + Mem0 head-to-head (preliminary)
make repro-longmemeval reproduce the LongMemEval-S evaluation
make repro-everything reproduce both headline runs (LongMemEval + benches)
make test full test suite (unit + integration)
make test-fast unit tests only — no infra required
make check format + lint + mypy strict
make build build wheel + sdist into dist/
make build-verify build, then install + smoke-test in a fresh venv

Documentation

Four focused docs under docs/:

  • Quickstart — five commands to a working ContinuumSession.
  • Architecture — the three tiers, promotion lifecycle, supersession, bi-temporal.
  • Config reference — every env-var and YAML knob with defaults.
  • Operations — Postgres setup, scaling, observability, common production issues.

If you're new, the recommended path is: this README → make demo-chatdocs/architecture.mddocs/quickstart.md. About 30 minutes if you read everything; 10 if you skim.

Honest evaluation — the LongMemEval-S reports

Two reports, read in order — the second corrects the first, which is the point:

findings/longmemeval_2026-05.md (May) — seven full sweeps across four model families and six retrievers found a hard 32-34% substring ceiling, even at 100% recall, and concluded the bottleneck was multi-hop reasoning: a single-shot retrieve → answer pipeline couldn't exceed it "without an external reasoning loop."

findings/reasoning_loop_2026-06.md (June, v1) — we built that reasoning loop (the IterativeReasoner), A/B'd it, and it was net-negative — so we cut it. v1 broke the ceiling to 60.8% judged with a single-shot pipeline anyway. What actually moved the number:

  • a stronger answerer (the dominant lever — we say so plainly),
  • the LLM judge revealing substring under-counted paraphrases by +12.4 pp,
  • session-aware retrieval + fixing two silent truncation bugs (multi-session ~16% → 55%),
  • LTM supersession making knowledge-update work (98.7% recall).

The June report is deliberately honest about what we got wrong (the reasoning-loop prediction), which lever bought which points, where we still lose (temporal-reasoning at 41%), and why the LOCOMO/Mem0 head-to-head is still preliminary rather than a published win. Reproduce both headline runs with make repro-everything.

The throughline that informs Continuum's positioning: a strong model fed clean, complete context beats elaborate scaffolding — be the memory layer that surfaces the right context cheaply; don't try to out-reason the reasoner.


Project layout

continuum/                  the framework itself
├── core/                   session orchestration, types, config
├── stores/                 STM / MTM / LTM implementations
│   ├── stm/                in-memory + thread-safe + Postgres
│   ├── in_memory/          in-memory LTM with supersession (eval + local dev)
│   └── postgres/           pgvector-backed MTM + LTM with supersession + bi-temporal
├── retrieval/              composite scorer + BM25 + reciprocal-rank-fusion hybrid
├── extraction/             entity / fact / LLM extractors + cached SmallLLM helper
├── promotion/              Mem0Promoter, triggers, IdleStmFlush
├── optimizer/              token-budget compression chain (5 strategies)
├── scoring/                composite scorer (relevance / importance / recency / confidence)
├── policies/               policy engine + 8 default policies (migration 004)
├── reasoning/              IterativeReasoner — shipped but cut from v1 (tested negative result)
├── embeddings/             embedding service (sentence-transformers)
└── db/                     pgvector upgrade helpers

memory/                     legacy STM engine the framework re-exports (ConversationSTM)

bench/                      memory-operation benchmarks (make bench-all)
├── ingest_throughput.py    Continuum vs raw list vs mem0
├── retrieval_quality.py    recall@k vs naive cosine
├── supersession_correctness.py   100% vs 38%
└── bi_temporal.py          "as of date Y" — 100% vs 75%

evals/                      reproduction harness (datasets fetched on demand)
├── longmemeval/            LongMemEval-S driver (the v1 60.8% result)
└── locomo/                 LOCOMO + Mem0 head-to-head (preliminary)

findings/                   evaluation reports + reproducibility
├── longmemeval_2026-05.md  the May "32% ceiling" report
├── reasoning_loop_2026-06.md   the v1 correction (supersedes it)
└── charts/                 summary-generation scripts

docs/                       quickstart · architecture · config · operations
examples/chat_agent/        the 60-second CLI demo (make demo-chat)
scripts/                    diagnostic-sample + bench-regression + install-verify helpers
migrations/                 numbered Postgres migrations (pgvector, lexical search, policies)

tests/
├── unit/                   per-component unit tests
├── integration/            cross-tier flows + Postgres pgvector
└── acceptance/             phase-completion gates

This release branch ships the essential library + reproduction harness only. Vendored datasets, raw run outputs (results/, bench/results/*.json, .wiki_cache*), and earlier prototype code are intentionally excluded — they're regenerated on demand and gitignored. Full history lives on main.


What Continuum is not

  • Not a vector database. It uses pgvector under the hood but adds tier semantics, supersession, and bi-temporal queries that pgvector alone doesn't provide.
  • Not a reasoning engine. No agentic loop, no multi-hop retrieval, no chain-of-thought orchestration. Use LangGraph, AutoGen, or your own loop on top — Continuum is what they should plug into for memory.
  • Not a managed service. It's an open-source Python framework. Self-host on your own Postgres.
  • Not a leaderboard-tuned LongMemEval contender. We measured honestly; see the report.

Status

Phase 1 (storage + protocols + tests), Phase 2 (extraction + promotion + policies + retrieval), and Phase 3 (benchmarks + demo + findings) are complete. The framework passes strict mypy + full unit / integration / acceptance test gates.

Production readiness (deploy scripts, observability, multi-tenant testing) is the next phase if and when there's interest.


License

MIT.

Citation

If Continuum's supersession or bi-temporal benchmarks are useful in your work:

Continuum: production-grade memory infrastructure for AI agents.
https://github.com/Genkryptos/Continuum, 2026.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

continuum_mcp-2.0.3.tar.gz (675.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

continuum_mcp-2.0.3-py3-none-any.whl (330.5 kB view details)

Uploaded Python 3

File details

Details for the file continuum_mcp-2.0.3.tar.gz.

File metadata

  • Download URL: continuum_mcp-2.0.3.tar.gz
  • Upload date:
  • Size: 675.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for continuum_mcp-2.0.3.tar.gz
Algorithm Hash digest
SHA256 92e8eef170fe8c9aa5bc3f8810277030a5fa574da578768a8ff60ff96e7633ab
MD5 fe6a2852b1863a535e7d2813a0a0430a
BLAKE2b-256 7e27386009c222e43031b8188bcb529764573ea8f233583320959e2fd4fac406

See more details on using hashes here.

File details

Details for the file continuum_mcp-2.0.3-py3-none-any.whl.

File metadata

  • Download URL: continuum_mcp-2.0.3-py3-none-any.whl
  • Upload date:
  • Size: 330.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.8

File hashes

Hashes for continuum_mcp-2.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 3f9ab1765e331d2300eb98e10460194070bb4f3fb22e3d1be528a510ca5f63e2
MD5 b8407cdcf7bf977c828d477f53b29415
BLAKE2b-256 330ce2820f3081507ec86280449453c913c8438aa1c2f91f7009e6ac907eb1a5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page