Skip to main content

Embedded claim-native memory for AI agents. Collision detection built-in.

Project description

tensory

Context-aware memory for AI agents. One file. Built-in collision detection.

I evaluated every open-source memory library — Mem0, Graphiti/Zep, Cognee, Letta — and none fit what I needed: a single-file, claim-native memory with built-in contradiction detection, temporal reasoning, and zero infrastructure. So I built tensory.

pip install tensory

Install & Run

1. MCP Server — give your AI agent long-term memory

No install needed. Add to your MCP config and restart:

Claude Code — add to .mcp.json in your project root
{
  "mcpServers": {
    "tensory": {
      "command": "uvx",
      "args": ["--from", "tensory[mcp]", "tensory-mcp"],
      "env": {
        "TENSORY_DB": "~/.local/share/tensory/memory.db",
        "OPENAI_API_KEY": "sk-..."
      }
    }
  }
}
Cursor — Settings → MCP Servers → Add
{
  "mcpServers": {
    "tensory": {
      "command": "uvx",
      "args": ["--from", "tensory[mcp]", "tensory-mcp"],
      "env": {
        "TENSORY_DB": "~/.local/share/tensory/memory.db",
        "OPENAI_API_KEY": "sk-..."
      }
    }
  }
}
Claude Desktopclaude_desktop_config.json
{
  "mcpServers": {
    "tensory": {
      "command": "uvx",
      "args": ["--from", "tensory[mcp]", "tensory-mcp"],
      "env": {
        "TENSORY_DB": "~/.local/share/tensory/memory.db",
        "OPENAI_API_KEY": "sk-..."
      }
    }
  }
}

Config location:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Environment variables:

Variable Required Description
TENSORY_DB Yes Path to SQLite database file
OPENAI_API_KEY Recommended For embeddings (vector search). Without it only FTS + graph search work
ANTHROPIC_API_KEY Optional For tensory_add() claim extraction via LLM
ANTHROPIC_BASE_URL Optional Proxy URL (CLIProxyAPI, LiteLLM, etc.)

After restart, your agent gets 7 tools: tensory_add, tensory_remember, tensory_search, tensory_timeline, tensory_stats, tensory_health, tensory_reset.

2. Dashboard — see your agent's memory

# One command, no install needed
uvx --from "tensory[ui]" tensory-dashboard

# Point to your MCP database
uvx --from "tensory[ui]" tensory-dashboard --db ~/.local/share/tensory/memory.db

# Or via Docker (runs in background, restarts automatically)
docker run -d -p 7770:7770 --name tensory-dashboard \
  -v ~/.local/share/tensory:/data \
  --restart unless-stopped \
  ghcr.io/kryptogrib/tensory

Open http://localhost:7770 — entity graph explorer, claims browser, memory stats.

3. Agent System Prompt

Add this to your agent's system prompt (or project rules) so it knows how to use tensory:

You have access to long-term memory via tensory MCP tools.

STORING MEMORIES:
- Use tensory_add(text) to store raw text — claims are extracted automatically
- Use tensory_remember(claims) to store specific facts: [{"text": "...", "entities": ["..."], "type": "fact"}]
- Claim types: "fact" (verifiable), "experience" (event), "observation" (inference), "opinion" (judgment)

RETRIEVING MEMORIES:
- Use tensory_search(query) before answering questions — check what you already know
- Use tensory_timeline(entity) to see how facts about something changed over time

WHEN TO STORE:
- User shares new facts, preferences, or decisions
- You learn something important from the conversation
- A previous fact changed or was corrected

WHEN TO SEARCH:
- User asks about something discussed before
- You need context from previous conversations
- Before making assumptions — check memory first

4. As a Python library

pip install tensory                # core (SQLite + search)
pip install "tensory[mcp]"         # + MCP server
pip install "tensory[ui]"          # + dashboard
pip install "tensory[all]"         # everything

Why not existing solutions?

Library What's great What didn't work for me
Mem0 Simple API, good DX Chunk-based, no collision detection, no temporal tracking. Lost facts silently.
Graphiti/Zep Strong knowledge graph, MMR search Requires Neo4j, heavy infra. Graph-first, not claim-first.
Cognee Pipeline architecture Complex setup, graph-oriented, no built-in contradiction resolution.
Letta/MemGPT Context window management Agent framework, not a memory library. Different abstraction level.
Hindsight Best benchmark scores (92% LoCoMo) Closed-source cloud service. Can't self-host or inspect.

What I wanted: embed a SQLite file, extract atomic claims, detect when facts contradict or supersede each other, and search across text + vectors + entity graph — all in one pip install.

Quick Start

from tensory import Tensory, Claim

store = await Tensory.create("memory.db")
await store.add_claims([Claim(text="EigenLayer has 50 team members", entities=["EigenLayer"])])
results = await store.search("EigenLayer")

What is tensory?

An embedded, claim-native memory library for AI agents. Instead of storing raw text chunks, tensory extracts atomic claims — verifiable statements with entities, confidence, and temporal validity.

  • One file — single SQLite database, no Docker, no Neo4j required
  • Context-aware extraction — same text yields different claims depending on why you're reading it
  • Built-in collision detection — automatically finds contradictions and superseding facts
  • Cognitive mechanisms — salience decay, surprise scoring, priming, all without LLM calls
  • MMR-diversified search — hybrid retrieval with entity crowding prevention
  • Pluggable LLM & embeddings — OpenAI, Anthropic (with proxy), Ollama, or bring your own

Architecture: 4 Storage Layers

Layer 0: RAW       — episodes (raw text). Never deleted.
Layer 1: CLAIMS    — atomic claims + embeddings + salience.
Layer 2: GRAPH     — entities + relations + waypoints.
Layer 3: CONTEXT   — research goals as extraction lenses.

Full API Example

from tensory import Tensory, Claim, Context
from tensory.embedder import OpenAIEmbedder

# Initialize with embedder + LLM
store = await Tensory.create(
    "memory.db",
    llm=my_llm_fn,                                    # any async (str) -> str
    embedder=OpenAIEmbedder(api_key="sk-..."),         # or dim=512 for cheaper
)

# Create a research context — the lens for extraction
ctx = await store.create_context(
    goal="Track DeFi team movements and protocol partnerships",
    domain="crypto",
)

# Mode 1: Raw text → auto-extract claims relative to context
result = await store.add(
    "Google announced partnership with EigenLayer for cloud restaking...",
    source="reddit:r/defi",
    context=ctx,
)
# result.claims      — extracted claims
# result.relations   — entity relations
# result.collisions  — auto-detected conflicts

# Mode 2: Pre-extracted claims (no LLM needed)
await store.add_claims([
    Claim(text="EigenLayer team grew to 60", entities=["EigenLayer"])
])

# Search — hybrid (vector + FTS + graph), context-weighted
results = await store.search("EigenLayer", context=ctx)

# Timeline — how facts about an entity evolved
history = await store.timeline("EigenLayer")

# Re-evaluate — same text, different research goal → different claims
tech_ctx = await store.create_context(goal="Track Big Tech AI strategy")
new_claims = await store.reevaluate(episode_id=result.episode_id, context=tech_ctx)

# Maintenance
await store.cleanup(max_age_days=90)
stats = await store.stats()
source_profile = await store.source_stats("reddit:r/defi")
observations = await store.consolidate(days=7, min_cluster=3)

Embeddings

OpenAI text-embedding-3-small recommended — $0.02/1M tokens (~$0.20 per 100K claims):

from tensory.embedder import OpenAIEmbedder

# Standard (1536 dims)
embedder = OpenAIEmbedder(api_key="sk-...")

# Cost-optimized (512 dims — native Matryoshka reduction, 3x smaller storage)
embedder = OpenAIEmbedder(api_key="sk-...", dim=512)

# Via proxy (CLIProxyAPI, LiteLLM, etc.)
embedder = OpenAIEmbedder(base_url="http://localhost:8317", api_key="local-key")

# No embeddings (FTS + graph search still work)
store = await Tensory.create("memory.db")  # NullEmbedder by default

LLM Extraction

Any async function (str) -> str works as LLM. Ready-made adapters in examples/llm_adapters.py:

from examples.llm_adapters import anthropic_llm, openai_llm, ollama_llm

# OpenAI
store = await Tensory.create("memory.db", llm=openai_llm())

# Anthropic via proxy (as in openHunter)
store = await Tensory.create("memory.db", llm=anthropic_llm(
    base_url="http://localhost:8317", api_key="local-key",
))

# Ollama (free, local)
store = await Tensory.create("memory.db", llm=ollama_llm("llama3.1"))

# Or from env vars (ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY)
from examples.llm_adapters import anthropic_from_env
store = await Tensory.create("memory.db", llm=anthropic_from_env())

Context as Lens

The core innovation: a Context defines why you're reading. One episode can yield completely different claims under different contexts via reevaluate().

# Same news article...
crypto_ctx = await store.create_context(goal="Track DeFi protocols")
tech_ctx = await store.create_context(goal="Track Big Tech AI strategy")

result = await store.add("Google partners with EigenLayer...", context=crypto_ctx)
# → Claims about EigenLayer, restaking, DeFi partnerships

new_claims = await store.reevaluate(result.episode_id, context=tech_ctx)
# → Claims about Google's cloud strategy, AI infrastructure moves

Cognitive Mechanisms

All algorithmic, zero LLM calls:

Mechanism What it does
Salience + decay Claims fade over time (exponential, per-type rates)
Surprise score Novel claims get salience boost
Priming Recently-searched entities boost related results
Reinforce on access Searched claims get stronger (+0.05 salience)
Waypoints Auto-link similar claims (cosine >= 0.75)
Structural collision Same entity + different value = auto-conflict
Consolidation Cluster claims into OBSERVATION summaries
Source fingerprinting Per-source reliability profiles
Sentiment tagging Keyword-based sentiment + urgency detection

Collision Detection

Two-level, zero LLM:

  1. Structural — same entities + overlapping temporal validity
  2. Semantic — weighted composite: vector (40%) + entity overlap (25%) + temporal proximity (20%) + waypoint link (15%)

Salience updates on collision:

  • contradiction → salience x 0.5
  • supersedes → salience x 0.1
  • confirms → salience + 0.2
  • related → salience + 0.05

Hybrid Search

Three parallel channels merged via Reciprocal Rank Fusion (RRF), then diversified via MMR:

FTS5 + Vector + Graph → RRF merge (over-fetch 3x) → MMR reranking → top-k
Channel Weight Method
Vector 0.4 sqlite-vec cosine similarity
FTS 0.3 SQLite FTS5 full-text search
Graph 0.3 Entity traversal via recursive CTEs

MMR reranking (Maximal Marginal Relevance) prevents entity crowding — when a popular entity with many similar claims dominates results and pushes out specific facts. Falls back to entity-cap filtering when embeddings are unavailable.

Weights are configurable. Any channel that fails returns empty results (graceful degradation).

Graph Backends

Default: SQLiteGraphBackend — uses recursive CTEs, zero dependencies, sufficient for <100K claims.

# Default — everything in one SQLite file
store = await Tensory.create("memory.db")

# Enterprise — Neo4j (planned)
# store = await Tensory.create("memory.db", graph_backend=Neo4jBackend("bolt://localhost:7687"))

Configuration

Copy .env.example to .env:

cp .env.example .env
Variable Purpose Required
OPENAI_API_KEY Embeddings + LLM extraction via OpenAI Only if using OpenAI
ANTHROPIC_API_KEY LLM extraction via Anthropic/proxy Only if using Anthropic
ANTHROPIC_BASE_URL Proxy URL (CLIProxyAPI etc.) Optional
TENSORY_MODEL Model for extraction Default: claude-haiku-4-5-20251001

Benchmarks

Testing on LoCoMo (Long-term Conversational Memory, ACL 2024) via Open Memory Benchmark:

Memory System Accuracy Queries Notes
Hindsight (cloud) 92.0% 1,540 Closed-source
Cognee 80.3% 152 Partial evaluation
Hybrid Search (Qdrant) 79.1% 1,540 Vector-only baseline
Tensory testing 1,540 Full evaluation in progress

Tensory extraction cost per conversation: ~$0.12 (Haiku extraction + OpenAI embeddings). Full benchmark is running — results will be published here.

Status

Active development. 275+ tests, pyright strict, ruff clean, CI on every push.

The vision: become the best open-source memory for AI agents — a single-file SQLite database that gives your agent real long-term memory with contradiction detection, temporal reasoning, and cognitive mechanisms that work without LLM calls.

What's done

  • Episodic + semantic + procedural memory (full cognitive stack)
  • Hybrid search with MMR diversity (FTS5 + vector + graph → RRF → MMR)
  • Two-level collision detection (structural + semantic)
  • Context-aware extraction (same text, different lens → different claims)
  • Topic segmentation for long texts (parallel extraction)
  • MCP server for tool-use integration
  • Smart context formatting (entity grouping, temporal annotations, memory-type routing)
  • Web dashboard (graph explorer, claims browser, stats)
  • LoCoMo benchmark integration (AMB provider with exact cost tracking)

What's next

  • Full benchmark results on LoCoMo (1,540 questions, 10 conversations)
  • Cross-encoder reranking for retrieval precision
  • Session-aware diversification (SSD) for sequential agent queries
  • Research agent built on tensory as a reference application
  • LangChain / LlamaIndex integrations

Development

uv sync --all-extras                    # Install all deps
uv run pytest tests/                    # Run all tests (~275)
uv run pytest tests/test_store.py::test_name   # Single test
uv run pyright tensory/                 # Type check (strict mode)
uv run ruff check tensory/ tests/       # Lint
uv run ruff format tensory/ tests/      # Format
uv run python examples/demo.py          # Integration demo

License

MIT

Attribution

Deduplication logic adapted from Graphiti (Apache-2.0 License, Copyright 2024 Zep Software, Inc.)


Built because no existing memory library did what I needed. If you're building agents that need to remember, contradict, and evolve — give tensory a try.

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

tensory-0.2.2.tar.gz (167.0 kB view details)

Uploaded Source

Built Distribution

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

tensory-0.2.2-py3-none-any.whl (81.7 kB view details)

Uploaded Python 3

File details

Details for the file tensory-0.2.2.tar.gz.

File metadata

  • Download URL: tensory-0.2.2.tar.gz
  • Upload date:
  • Size: 167.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tensory-0.2.2.tar.gz
Algorithm Hash digest
SHA256 0803bc73bb9d576d2d6f18e31be4ded5b48ce43431bcecfbbabe24f2ad93a9bb
MD5 ae97727dd6af8ce92c35497452780158
BLAKE2b-256 3602b9f591f82f83212f04bb07ac7146ce61690e1384cc365e426ba4f5178e44

See more details on using hashes here.

Provenance

The following attestation bundles were made for tensory-0.2.2.tar.gz:

Publisher: publish-pypi.yml on kryptogrib/tensory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tensory-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: tensory-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 81.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tensory-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 409c3cbdf60a6f7a9d609a4b5757d7c58d155f6de05a0e25a0e6e3e170ca8e97
MD5 97cd559470ac72ec5fd99f7309027e92
BLAKE2b-256 7dbcf52d354f5176acecd988c693d79362726e2b635f72019607bbe9de0d67a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for tensory-0.2.2-py3-none-any.whl:

Publisher: publish-pypi.yml on kryptogrib/tensory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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