Skip to content

Persistence

What is stored, when it is recomputed, and what that costs.

Embedding lifecycle

flowchart TB
  C["POST /api/memory"] --> C1["embed(content)"] --> C2["INSERT row + embedding_json"]
  P["PATCH /api/context/{id}"] --> P1{"did content change?"}
  P1 -->|no| P2["reuse stored embedding"]
  P1 -->|yes| P3["recompute embedding"] --> P4["UPDATE embedding_json + version entry"]
  R["GET .../relevant"] --> R1{"stored embedding<br/>present and right dimension?"}
  R1 -->|yes| R2["use it"]
  R1 -->|no| R3["recompute for this request only<br/><i>not written back</i>"]

Three rules, all observable:

  1. Every write persists an embedding. Computed inline, stored as a JSON float array in contexts.embedding_json.
  2. A content edit recomputes it. Without this, an edited memory would keep ranking on its old text — the classic silent-staleness bug. It is fixed and verified.
  3. A missing or wrong-dimension embedding is recomputed on read, for that request only. The memory is never dropped from results for lack of a vector, but the recomputation is not persisted, so it repeats on every query until backfilled.

Storage layout

Column contexts.embedding_json (TEXT)
Format JSON array of floats
Dimension 128 (hashing) or 384 (semantic)
Index none

There is no pgvector, no native vector column and no ANN index. Cosine similarity is computed in Python over the loaded candidate set.

This is a deliberate, documented choice, not an oversight: the schema is a 1:1 match for a pgvector Vector column with identical cosine semantics, so the migration is a column type change plus a backfill, on the day the scale justifies it. Today it does not.

Practical implications

Performance

  • Write: one embedding computation on the request path. Negligible with hashing; a MiniLM forward pass with semantic.
  • Read: O(candidates) cosine computations, plus one query embedding, plus a recomputation for every candidate whose embedding is stale.
  • The read cost is linear in project size. Watch it as projects grow past a few thousand memories.

Storage

Roughly: 128 floats × ~20 chars ≈ 2.5 KB per memory with hashing, and 384 floats ≈ 7.5 KB with semantic — JSON text, so several times what a packed binary vector would cost. At 100 000 memories that is ~750 MB for the semantic provider. Budget for it, or plan the pgvector migration.

Changing providers

Switching VENKAI_EMBEDDING_PROVIDER changes the dimension, which makes every stored embedding incompatible. Nothing breaks and nothing is lost — but every retrieval re-embeds every candidate, on every request, forever.

flowchart LR
  A["switch provider"] --> B["all stored embeddings<br/>wrong dimension"]
  B --> C["recomputed per read"]
  C --> D["correct results,<br/>degraded latency"]
  D --> E["backfill to recover"]

A backfill is a PATCH of each memory with its own content, which recomputes and stores the new vector. Planned — a dedicated backfill command; there is no built-in one today.

Decide the provider before loading data if you can.

Versioning

Editing a memory records a version entry.

curl -s https://api.venkai.fr/api/context/ctx_cc55e4a7bbc5/versions \
  -H "Authorization: Bearer $VENKAI_API_KEY"
{"context_id": "ctx_cc55e4a7bbc5", "versions": [ ... ], "count": 1}

Restore:

curl -sX POST https://api.venkai.fr/api/context/ctx_cc55e4a7bbc5/restore \
  -H "Authorization: Bearer $VENKAI_API_KEY" \
  -H 'Content-Type: application/json' -d '{"version": 1}'

A restore creates a new version entry rather than rewinding history, so the trail is append-only.

Durability

Aspect Reality
Development store SQLite file (default VENKAI_DATABASE_URL)
Production store PostgreSQL
Data volume (Docker) venkai-data named volume, mounted at /data
Backups Not provided by Venkai. Your database, your backup policy.
Retention / TTL None. Memories persist until edited or the org is deleted.
Bulk export GET /api/export
Bulk delete DELETE /api/organization — removes the organization and its data

There is no per-memory delete endpoint. Supersede with a PATCH, or export and re-import into a fresh organization.