Skip to content
Memory runtimeOpen-source · Apache 2.0 · self-hosted

Zep returns a blob.Statewave returns a receipt.

Zep models memory as a knowledge graph of entities and edges and returns retrieval as an optimized Context Block string. Statewave compiles typed, provenance-traced memories and returns a ranked bundle with explicit metadata per row: kind, confidence, validity, and the source episodes it came from.

zthread.get_user_contextContext Block
optimized string
Alice runs dispatch automation at Northwind. She prefers email over calls. Recently moved offices. Works with Acme on the BluePeak account…
one blob · no per-fact metadata
Sget_context600 budget
ranked bundle · per-row metadata
profile_factruns dispatch · Northwind[ep_4]
profile_factprefers email · conf 0.9[ep_9]
provenance · fact_ids → episodes
4typed memory kinds
4ranking signals, deterministic
0.905LoCoMo, n=1,540
1store: Postgres, no graph DB
Postgres-only, runs offline

A knowledge graph, or a typed-memory runtime

Zep stores memory as a graph: nodes are entities, edges are facts with valid and invalid timestamps, and the graph updates dynamically as new data arrives. Retrieval traverses that graph and returns an optimized Context Block. Statewave records each event as an immutable episode, compiles those into typed memories with confidence and validity, and assembles a ranked bundle the same way on every call.

zZep · knowledge graph
graph.searchnodes & edges, traverse yourself
get_user_context→ Context Block (opaque string)

Retrieval rides on the graph

Traversal and a managed reranker compose a well-shaped Context Block. You get a blob, not per-fact metadata; temporal validity lives on the edges, and the graph updates dynamically as new data lands.

SStatewave · Record → Compile → Context → Govern
01Recordimmutable episodes
02Compiletyped memories
03Contextranked bundle
04Governpolicy + receipt
typed memory kinds · ranking priority
profile_fact
10
procedure
8
episode_summary
5
raw_episode
3

Assembly is deterministic and inspectable

Given the same subject, task, token budget, and point in time, the assembler returns the identical bundle every run. Five signals set the order: kind priority, recency, task relevance, temporal validity, and semantic similarity.

How order is decidedscore = priority + recency + relevance + validity
KIND PRIORITY
3–10
typed profile facts outrank raw episodes
RECENCY
0–5
linear by age, newest scores highest
TASK RELEVANCE
0–8
word overlap (0-5) or cosine similarity (0-8)
TEMPORAL VALIDITY
−4…+3
valid facts gain +3, expired ones lose 4

Where each capability lives

Both give agents durable memory. They diverge on the shape of what comes back: a graph traversal's Context Block, or a ranked bundle of typed rows, and on what you can inspect once it has.

RETRIEVAL & RANKING

What comes back

z
An optimized Context Block: one string
S
A ranked bundle of typed rows with per-memory metadata

How context is selected

z
Graph traversal plus a managed reranker
S
Deterministic assembly, ranked to a token budget

Custom domain modelling

z
First-class entity and edge types (Pydantic-like)
S
Free-form metadata JSONB; typing carried in your app

Same query, same result

z
Not advertised; traversal and reranker can drift
S
Byte-identical bundle every run

Token-bounded output

z
Context Block size shaped by Zep
S
Explicit max_tokens; bundle reports token_estimate
GOVERNANCE & PROVENANCE

Inspectable retrieval

z
Opaque string; no per-fact metadata in the response
S
Explicit kind, confidence, valid_from/to per row

Provenance

z
Edge timestamps for lineage; node history in the graph
S
source_episode_ids per memory; immutable episode chain

Temporal validity

z
Per relationship: edge valid_at / invalid_at
S
Per memory: valid_from / valid_to windows

Subject deletion (GDPR)

z
Remove nodes and edges from the graph
S
One call hard-deletes a subject’s episodes and memories
OPERATIONS & LICENSING

Memory model

z
Knowledge graph: nodes plus edges (Graphiti-based)
S
Typed memories (4 kinds) plus immutable episodes

Storage

z
Managed graph database under the hood; nothing to run
S
Postgres and pgvector; no proprietary store

Deployment

z
Cloud, BYOK, or BYOC; Community Edition discontinued 2025, repo stays Apache 2.0 unsupported
S
Self-hosted only

Best for

z
Graph-shaped reasoning and explicit entity modelling
S
Eval-driven, inspectable retrieval on a single Postgres

Zep is a Graph RAG memory product built on Graphiti, its open-source temporal-graph engine: nodes are entities, edges are facts with valid/invalid timestamps, and the graph updates dynamically in response to new data. Zep discontinued its self-hosted Community Edition in 2025. Cloud, BYOK, and Bring-Your-Own-Cloud are the only deployment options today. Rows reflect Zep's public docs as of August 2026.

zReach for Zep when

Your domain has entities and explicit relationships the agent should reason about: "what connects Alice to BluePeak?" is a graph question. You want temporal validity on relationships for free, prefer defining custom entity types, and are fine running entirely as a managed cloud or BYOC service. Zep has no supported self-hosted edition.

SReach for Statewave when

Inspectable retrieval matters, determinism is part of the contract, and a single-Postgres, self-hosted footprint is operationally simpler than adopting a managed graph service. Most support, coding, and sales agents need "what did this user say, what's their plan, what's open": typed-fact retrieval covers that and skips the graph tax.

"Why did the agent say that?"

An agent resumes a returning customer and states a fact about them. Later, someone asks where that fact came from. The same history runs through each system: one returns a blob, the other a finite provenance chain.

zZep · Context Block
# ask the graph for context
› zep.thread.get_user_context("alice-1")
  ↳ ctx.context # optimized string
"Alice runs dispatch automation at Northwind. Works with Acme on BluePeak…"
which message? which edge?not in the response
You get a well-shaped string. The confidence of each fact, the message it came from, whether it's still valid: that lineage lives in the graph, not in what the agent was handed.
SStatewave · bundle + provenance
# ranked rows with metadata
› sw.get_context("user-alice",
  task="who is alice", max_tokens=600)
runs dispatch · Northwindconf 0.92 · [ep_4]
prefers emailvalid · [ep_9]
provenancefact_ids → episodes
Every row carries its kind, confidence, validity, and source_episode_ids. "Why did the agent say X" is a finite walk (fact_ids to memories to source episodes), not a forensic dig through the graph.

Every call can leave a receipt

Zep tracks lineage as timestamps on graph edges and returns retrieval as an opaque Context Block. In Statewave assembly is governed on the read path and can emit an immutable receipt, with per-memory provenance back to the source episode, in the core, under Apache 2.0.

state-assembly receiptimmutable · ULID-addressable
receipt_id01J9Z4RT8K···
integrity_hashsha256:a3f9c1e0···
policy_bundlebundle:7c21 (enforce)
included · 3 facts, 2 episodes · 1,180/1,500 tokens
profile_factconf 0.92 · valid[ep_4, ep_9]
procedureconf 0.88 · valid[ep_2]
episode_summarysupersededdropped
1 memory redacted · label:pii
{}
Policy engine
Content-hashed YAML or JSON bundles. Deny or redact by sensitivity label and caller identity; log_only audits a policy before you enforce it.
#
Sensitivity labels
Per-memory pii, financial, and secret tags in a GIN-indexed array, so policy filters run inside the query.
←
Full provenance
Every compiled memory keeps the source episode ids, confidence score, and validity window it was derived from.
⌫
Subject deletion
One GDPR-style call erases every episode, memory, and receipt for a subject. No orphaned rows.

The same query returns the same bundle

Statewave is evaluated on two long-horizon memory benchmarks, and the scores are fixed: no model and no reranker sit on the read path, so the same subject, task, and budget return the same bytes every run. Zep publishes no number produced under the same conditions: graph traversal with a managed reranker composes the Context Block, and Zep's own docs describe the graph updating dynamically as new data arrives.

SStatewave: fixed benchmark scoresDETERMINISTIC READ PATH
0.905accuracy
LoCoMo
n=1,540 · robust figure
0.967accuracy
LongMemEval
n=30 · directional
Hybrid retrieval lift · v10
LoCoMo +2.1LongMemEval +16.0
Storage footprint
postgres + pgvector·no graph DB
One store to operate, usually already in your stack.
zZep: managed, not deterministicVARIES WITH INDEX STATE

Zep doesn't advertise determinism: graph traversal and reranker behaviour are part of the managed retrieval, and the graph updates dynamically in response to new data. The same query can compose a different Context Block as index state shifts. help.getzep.com/concepts

SAME QUERY · RUN 1
facts A, B, C composed
SAME QUERY · RUN 2
re-ranked, D swapped in
SAME QUERY · RUN 3
graph updated since

Illustrative: Zep publishes no per-run determinism guarantee. Statewave's compile-time scoring and ranked-pack assembly return a byte-identical bundle for the same (subject, task, budget).

reproduce it yourself · statewave-memory-benchmarks
$ git clone https://github.com/smaramwbc/statewave-memory-benchmarks.git
$ cd statewave-memory-benchmarks && pip install -r requirements.txt
$ python -m benchmarks.locomo.run --backend statewave \
  --answerer-model gpt-4o --judge-model gpt-4o
# deterministic read path, same bundle on every run

Moving from Zep to Statewave

The conceptual translation is straightforward, but the data shapes differ. Plan a parallel-run period: the cutover is typically read-then-write rather than a flag flip.

one command · connects any MCP client
$ npx @statewavedev/statewave
→ API + admin console + Postgres up via Docker · healthy in under 2 min · no account
ZEP
STATEWAVE
thread.add_messages("alice-1", …)
sw.create_episode("user-alice", …)
direct swap
Messages land as immutable episodes; compilers extract typed memories.
thread.get_user_context("alice-1")
sw.get_context("user-alice", task="…", max_tokens=600)
direct swap
Ranked, token-bounded bundle with per-memory metadata and an optional receipt.
graph.search("…")
query by subject_id + kind + metadata
needs rework
No graph-traversal surface. Query typed memories, or keep a graph layer in your app.
edge valid_at / invalid_at
valid_from / valid_to on the memory
direct swap
Temporal validity moves from the edge onto the memory itself.

Relational facts that explicitly link two entities ("Alice works at Acme") don't survive directly: encode them in the subject's memory content, write to both subjects, or keep relationships in your own graph layer and use Statewave for episode and memory storage.

Frequently asked

OVERVIEW

How is Statewave different from Zep?

Zep is a Graph RAG product: it models memory as a knowledge graph of entities and edges and returns retrieval as an optimized Context Block string. Statewave compiles raw episodes into typed memories with confidence and validity, ranks them with a fixed scoring model to a token budget, and returns a structured bundle: per-row kind, confidence, validity, and source episode ids, with no graph to traverse.

CAPABILITY

Can I still do graph reasoning?

Not natively. Statewave has no graph-traversal surface, so "find everything Alice is connected to within two hops" isn’t its shape. If your domain needs entity-relationship reasoning, keep that in Zep or your own data layer, and use Statewave for episode and typed-memory storage.

MIGRATION

What happens to relational facts like "Alice works at Acme"?

Facts about a single subject migrate cleanly. Relational facts that link two entities don’t survive directly. You encode the relationship in the subject’s memory content, write the memory to both subjects with cross-references, or keep the relationship in your application’s graph.

DETERMINISM

What makes retrieval deterministic?

The bundle is compiled and assembled the same way every run: four ranking signals (kind priority, recency, task relevance, and temporal validity) combined to a fixed token budget. The same subject, task, and point in time produce the same bytes. Graph traversal with a reranker can’t promise that, because index state and reranker variation introduce drift.

STORAGE

Do I have to run a graph database?

No. Storage is Postgres plus pgvector and nothing else, usually already in your stack. There is no separate graph store to operate, back up, or scale.

DEPLOYMENT

Does Zep offer a self-hosted option?

No. Zep discontinued its self-hosted Community Edition in 2025 and now concentrates its open-source work on Graphiti, the temporal-graph engine underneath; the memory API this page compares (thread.get_user_context, graph.search) is only available through Zep Cloud, BYOK, or Bring-Your-Own-Cloud. Statewave runs the whole stack, Postgres included, on your own infrastructure, with no cloud dependency and no usage credits to meter.

INTEGRATIONS

Does it work with Claude, Cursor, or Codex?

Yes. One command (npx @statewavedev/statewave) boots the runtime, and its shipped MCP server connects any MCP-compatible client: Claude, Cursor, Copilot, and agent runtimes. Zep is cloud-only; Statewave runs entirely on your own infrastructure.

Give your agent context it can prove

Self-host the Apache 2.0 runtime, wire it to your MCP client, and every context call can return a receipt.