Metadata-Version: 2.4
Name: locidb
Version: 0.2.0
Summary: The cognitive database for LLM memory — vector search, knowledge graph, temporal decay, persona, corrections, contradiction detection, audit trail, time-travel, and health scoring in one engine.
Author: Neetish Singh
License: MIT
Project-URL: Homepage, https://github.com/neetishsingh/locidb
Project-URL: Repository, https://github.com/neetishsingh/locidb
Keywords: llm,memory,vector-database,agent,cognitive,embeddings,graph,persona,rag,context,audit-trail,time-travel
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sentence-transformers>=2.0
Requires-Dist: numpy>=1.24
Requires-Dist: click>=8.0
Requires-Dist: pydantic>=2.0
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.20; extra == "anthropic"
Provides-Extra: langchain
Requires-Dist: langchain>=0.1; extra == "langchain"
Provides-Extra: all
Requires-Dist: openai>=1.0; extra == "all"
Requires-Dist: anthropic>=0.20; extra == "all"
Requires-Dist: langchain>=0.1; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Dynamic: license-file

<div align="center">

# 🏛️ LociDB

**The cognitive database for LLM memory and agent context.**

*Vector search · Knowledge graph · Temporal decay · Importance scoring · Memory portability — in one embedded engine.*

[![PyPI version](https://img.shields.io/pypi/v/locidb.svg)](https://pypi.org/project/locidb/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/tests-134%20passed-brightgreen.svg)](#testing)

```bash
pip install locidb
```

</div>

---

Named after the **method of loci** (memory palace) — the ancient mnemonic technique of placing memories in spatial locations for reliable recall. LociDB brings this concept to AI: memories are *placed* in a spatio-temporal graph, weighted by importance, connected by relationships, and compressed over time. Like the memory palace, the right memory surfaces when you walk through the right door.

## Why LociDB?

Vector databases store embeddings. LLM memory needs more:

| Capability | Vector DB | LociDB |
|---|---|---|
| Semantic search | ✅ | ✅ |
| Entity relationship graph | ❌ | ✅ Typed, weighted edges |
| Temporal decay | ❌ | ✅ Exponential decay scoring |
| Importance weighting | ❌ | ✅ Composite importance formula |
| Memory compression | ❌ | ✅ Hierarchical summarisation |
| Context scoping | ❌ | ✅ Per-project/user scopes |
| Conversation continuity | ❌ | ✅ Auto-memorize exchanges |
| Memory portability | ❌ | ✅ Export/import across LLMs |
| User persona | ❌ | ✅ Persistent identity & preferences |
| Correction memory | ❌ | ✅ Supersede wrong facts |
| Contradiction detection | ❌ | ✅ Auto-flag conflicting memories |
| Reasoning audit trail | ❌ | ✅ Full trace of which memories informed responses |
| Time-travel queries | ❌ | ✅ Snapshot, timeline, diff |
| Memory health scoring | ❌ | ✅ Coverage, freshness, diversity diagnostics |

---

## Table of Contents

- [Quick Start](#quick-start)
- [Installation](#installation)
- [Core API](#core-api)
- [Persona Store](#feature-1--persona-store)
- [Correction Memory](#feature-2--correction-memory)
- [Contradiction Detector](#feature-3--contradiction-detector)
- [Reasoning Audit Trail](#feature-4--reasoning-audit-trail)
- [Time-Travel Queries](#feature-5--time-travel-queries)
- [Memory Health Score](#feature-6--memory-health-score)
- [Memory Portability](#memory-portability--export--import)
- [LLM Adapters](#llm-adapters)
- [MQL Query Language](#mql--memory-query-language)
- [CLI Reference](#cli-reference)
- [Data Model](#data-model)
- [Importance Scoring](#importance-scoring)
- [Architecture](#architecture)
- [Testing](#testing)
- [Contributing](#contributing)
- [License](#license)

---

## Quick Start

```python
from locidb import LociDB

loci = LociDB(path="./memory.loci")

# Store a memory
loci.memorize(
    content="Decided to use hub-spoke model for Dropzi routing",
    entities=["Dropzi", "routing", "architecture"],
    scope="project:dropzi"
)

# Recall relevant memories
results = loci.recall("delivery optimization", entities=["Dropzi"], top_k=5)

for r in results:
    print(f"[{r['composite_score']:.3f}] {r['node'].content}")

# Set up persona
loci.set_persona("name", "Neetish")
loci.set_persona("role", "Founding Engineer")

# Check memory health
report = loci.health()
print(f"Health: {report.overall_score}/100")
```

---

## Installation

```bash
# Core (includes SQLite, numpy, sentence-transformers, Pydantic, Click)
pip install locidb

# With OpenAI adapter
pip install "locidb[openai]"

# With Anthropic adapter
pip install "locidb[anthropic]"

# With LangChain adapter
pip install "locidb[langchain]"

# With all adapters
pip install "locidb[all]"

# Development (adds pytest)
pip install "locidb[dev]"
```

**Requirements:** Python 3.10+

---

## Core API

### `LociDB(path, embedding_model)`

Create or open a database. The SQLite file is created automatically.

```python
loci = LociDB(
    path="./memory.loci",              # SQLite file path
    embedding_model="all-MiniLM-L6-v2" # sentence-transformers model
)
```

### `memorize(content, entities, scope, importance, type, resolve)`

Store a new memory. Returns the created `MemoryNode`.

```python
node = loci.memorize(
    content="Delivery radius capped at 15km",
    entities=["Dropzi", "delivery"],
    scope="project:dropzi",
    importance=0.9,       # optional; auto-scored if omitted
    type="memory",        # "memory" | "correction" | "preference" | "fact"
    resolve=None,         # node ID to supersede (soft-deletes the old node)
)
```

### `recall(query, entities, scope, time_window_days, min_importance, top_k, as_of)`

Retrieve memories by semantic similarity, re-ranked by importance.

```python
results = loci.recall(
    query="delivery optimization",
    entities=["Dropzi"],           # optional entity filter
    scope="project:dropzi",        # optional scope filter
    time_window_days=30,           # optional recency window
    min_importance=0.6,            # minimum importance threshold
    top_k=8,
    as_of=None,                    # optional datetime for time-travel
)
# Returns: [{"node": MemoryNode, "similarity": float, "importance": float, "composite_score": float}]
```

### `traverse(from_entity, relationships, depth)`

Walk the memory graph starting from nodes tagged with an entity.

```python
chain = loci.traverse(from_entity="Dropzi", relationships=["decided"], depth=3)
# Returns: [{"node": MemoryNode, "depth": int, "path": list}]
```

### `link(source_id, target_id, relationship, strength)`

Manually create a graph edge between two nodes.

```python
edge = loci.link(source_id=node_a.id, target_id=node_b.id, relationship="caused", strength=0.8)
```

### `compress(scope, older_than_days)`

Compress old memories into daily summaries.

```python
count = loci.compress(scope="project:dropzi", older_than_days=2)
```

### `forget(node_id, hard=False)`

Delete a memory. Soft-deletes by default (preserves for time-travel). Pass `hard=True` for permanent removal.

```python
loci.forget(node.id)             # soft-delete (sets deleted_at)
loci.forget(node.id, hard=True)  # permanent delete
```

### `stats()` / `get(node_id)` / `close()`

```python
loci.stats()         # {"node_count": int, "edge_count": int, "scopes": [...], "avg_importance": float}
loci.get(node_id)    # retrieve a single MemoryNode by ID
loci.close()         # close the database connection
```

LociDB is also a context manager:

```python
with LociDB("./memory.loci") as loci:
    loci.memorize("Important fact", entities=["fact"])
```

---

## Feature 1 — Persona Store

Persistent user identity, preferences, and communication style that survives across sessions and gets injected into every LLM call.

```python
# Set individual attributes
loci.set_persona("name", "Neetish")
loci.set_persona("role", "Founding Engineer")
loci.set_persona("communication_style", "concise, no fluff")
loci.set_persona("expertise", "Python, distributed systems, ML")
loci.set_persona("preferences", "dark mode, vim keybindings")
loci.set_persona("context", "Building Dropzi — a delivery platform")

# Batch update
loci.update_persona({"name": "Neetish", "role": "CTO"})

# Read persona
loci.get_persona("name")       # "Neetish"
loci.get_persona()             # {"name": "Neetish", "role": "CTO", ...}

# Get formatted system prompt (auto-injected by adapters)
prompt = loci.get_persona_prompt()
# ## User Profile
# - **Communication Style**: concise, no fluff
# - **Expertise**: Python, distributed systems, ML
# - **Name**: Neetish
# - **Role**: CTO

# Clear all persona data
loci.clear_persona()
```

Adapters automatically prepend persona to every system prompt — no extra code needed.

---

## Feature 2 — Correction Memory

Explicitly supersede wrong or outdated memories. The old memory is soft-deleted and a `corrects` edge is created for traceability.

```python
# Original memory
old = loci.memorize("We deploy on AWS us-east-1", entities=["cloud", "deployment"])

# Later, you switch clouds:
new = loci.correct(
    old_node_id=old.id,
    new_content="We deploy on GCP us-central1",
    entities=["cloud", "deployment"],
)
# old node gets deleted_at set, new node has memory_type="correction"
# a "corrects" edge links new → old

# List all active corrections
corrections = loci.get_corrections(scope="project:dropzi")

# You can also use memorize() directly with type + resolve:
loci.memorize(
    content="API uses GraphQL now",
    entities=["api"],
    type="correction",
    resolve=old_rest_node.id,
)
```

Adapters inject active corrections at the top of every prompt so the LLM never uses outdated information.

---

## Feature 3 — Contradiction Detector

Automatically scans for conflicting memories using cosine similarity + negation heuristics.

```python
# After storing many memories, scan for conflicts:
conflicts = loci.check_conflicts(
    scope="project:dropzi",        # optional scope filter
    similarity_threshold=0.85,     # how similar two memories must be to compare
)

for c in conflicts:
    print(f"CONFLICT (similarity={c.similarity}):")
    print(f"  A: {c.node_a.content}")
    print(f"  B: {c.node_b.content}")
    print(f"  Reason: {c.reason}")
    print(f"  Suggestion: {c.suggested_resolution}")
```

**Detection logic:**
1. Two memories have cosine similarity ≥ threshold (discussing the same topic)
2. Negation markers detected: `not`, `never`, `instead`, `switched from`, `stopped`, etc.
3. Or antonym pairs found: `prefer` vs `avoid`, `always` vs `never`, `enabled` vs `disabled`, etc.

Each `ConflictWarning` includes the two nodes, similarity score, reason, and a suggested resolution.

---

## Feature 4 — Reasoning Audit Trail

Record exactly which memories informed each LLM response. Full traceability for debugging, compliance, and understanding.

```python
# Store a trace (adapters do this automatically)
trace_id = loci.store_trace(
    query="Where do we deploy?",
    node_ids_used=[node1.id, node2.id],
    response="You deploy on GCP us-central1.",
    scope="project:dropzi",
    metadata={"model": "gpt-4o", "tokens": 150},
)

# Human-readable explanation
print(loci.explain(trace_id))
# Trace a1b2c3d4 — 2026-03-20 14:30:00 UTC
# Query: Where do we deploy?
# Scope: project:dropzi
#
# Memories used:
#   [f8e2a1b3] We deploy on GCP us-central1
#
# Response: You deploy on GCP us-central1.
# Metadata: {'model': 'gpt-4o', 'tokens': 150}

# List recent traces
traces = loci.traces(scope="project:dropzi", limit=20)

# Find every trace that used a specific memory
traces = loci.traces_using_node(node.id)
```

When using LLM adapters, traces are stored automatically after every `chat()` call.

---

## Feature 5 — Time-Travel Queries

Query memory state at any point in time. See what you knew, when you knew it, and what changed.

### Snapshot — memory state at a point in time

```python
from datetime import datetime, timezone

# What did we know on March 1st?
march_1 = datetime(2026, 3, 1, tzinfo=timezone.utc)
nodes = loci.snapshot(as_of=march_1, scope="project:dropzi")

for n in nodes:
    print(f"[{n.timestamp.strftime('%m/%d')}] {n.content}")
```

### Timeline — chronological event log

```python
# Full timeline
events = loci.timeline(scope="project:dropzi")

# Filtered to one entity
events = loci.timeline(entity="deployment", limit=50)

for n in events:
    status = " [deleted]" if n.deleted_at else ""
    print(f"{n.timestamp:%Y-%m-%d} {n.content}{status}")
```

### Diff — compare two points in time

```python
from datetime import datetime, timedelta, timezone

t1 = datetime(2026, 3, 1, tzinfo=timezone.utc)
t2 = datetime(2026, 3, 15, tzinfo=timezone.utc)

diff = loci.diff(t1, t2, scope="project:dropzi")
print(f"Added: {len(diff['added'])} memories")
print(f"Removed: {len(diff['removed'])} memories")
print(f"State: {diff['t1_count']} → {diff['t2_count']} memories")
```

### Recall with time-travel

```python
# What would the LLM have recalled on March 1st?
results = loci.recall(
    query="deployment strategy",
    as_of=datetime(2026, 3, 1, tzinfo=timezone.utc),
)
```

---

## Feature 6 — Memory Health Score

Diagnostic scoring across 5 dimensions with gap detection, actionable suggestions, and onboarding questions.

```python
report = loci.health(scope="project:dropzi")

print(f"Overall: {report.overall_score}/100")
print()
for dim, score in sorted(report.dimensions.items()):
    bar = "█" * int(score / 10) + "░" * (10 - int(score / 10))
    print(f"  {dim:15s} {bar} {score:.0f}")

# Overall: 72.5/100
#
#   connectivity    ███████░░░ 75
#   coverage        ████████░░ 80
#   diversity       ███████░░░ 70
#   freshness       ██████████ 100
#   persona         ████░░░░░░ 40
```

### Dimensions

| Dimension | Weight | What it measures |
|---|---|---|
| **Coverage** | 25% | Total number of memories stored |
| **Freshness** | 20% | How recent the memories are |
| **Diversity** | 20% | Spread of entities, scopes, and memory types |
| **Connectivity** | 15% | Edge-to-node ratio (how well-linked the graph is) |
| **Persona** | 20% | How complete the user profile is |

### Gap detection & suggestions

```python
for gap in report.gaps:
    print(f"⚠ {gap}")
# ⚠ No persona configured
# ⚠ No edges between memories — graph is disconnected

for s in report.suggestions:
    print(f"→ {s}")
# → Set up a persona with db.set_persona() for personalized responses
# → Use entities when memorizing to auto-create links
```

### Onboarding questions

```python
# Generate bootstrap questions for new users
questions = loci.get_onboarding_questions()
# ["What's your name or preferred way to be addressed?",
#  "What's your current role or job title?",
#  "What are your main areas of expertise?",
#  ...]
```

---

## Memory Portability — Export & Import

Own your AI memory and carry it anywhere. Export ChatGPT context, import into Claude.

### Export

```python
# Full-fidelity backup (embeddings, edges, everything)
loci.export("backup.loci.json", format="native")

# LLM-ready system prompt for any model
loci.export("my_memory.context.md", format="context_prompt", max_tokens=4000)

# Dense plain-text for small context windows
loci.export("quick.snapshot.txt", format="snapshot", max_tokens=2000)

# Filter by scope, recency, or importance
loci.export("dropzi_90d.md", format="context_prompt",
            scope="project:dropzi", time_window_days=90, min_importance=0.5)
```

### Import

```python
loci.import_from("backup.loci.json")                  # restore from backup
loci.import_from("my_memory.context.md")               # parse markdown into memories
loci.import_from("backup.loci.json", mode="merge")     # merge without overwriting
```

### Format comparison

| Format | Extension | Embeddings | Edges | Best for |
|---|---|---|---|---|
| `native` | `.loci.json` | ✅ | ✅ | Backup, migration, LociDB-to-LociDB |
| `context_prompt` | `.context.md` | ❌ | ❌ | Paste as system prompt in any LLM |
| `snapshot` | `.snapshot.txt` | ❌ | ❌ | Quick sharing, small context windows |

---

## LLM Adapters

All adapters automatically handle: persona injection, correction injection, memory recall, auto-memorize, and trace storage.

### OpenAI

```python
from openai import OpenAI
from locidb.adapters import LociMemory

client = OpenAI()
memory = LociMemory(db_path="./memory.loci")

response = memory.chat(
    client=client,
    model="gpt-4o",
    user_message="How should I optimize Dropzi deliveries?",
    scope="project:dropzi",
)
```

### Anthropic

```python
import anthropic
from locidb.adapters import LociAnthropicMemory

client = anthropic.Anthropic()
memory = LociAnthropicMemory(db_path="./memory.loci")

response = memory.chat(
    client=client,
    model="claude-sonnet-4-20250514",
    user_message="How should I optimize Dropzi deliveries?",
    scope="project:dropzi",
)
```

### LangChain

```python
from locidb.adapters import LociLangChainMemory
from langchain.chains import ConversationChain
from langchain_openai import ChatOpenAI

memory = LociLangChainMemory(db_path="./memory.loci", scope="project:dropzi")
chain = ConversationChain(llm=ChatOpenAI(), memory=memory)
chain.run("Tell me about Dropzi delivery optimization")
```

### What adapters do under the hood

```
User message
    │
    ▼
┌─────────────────────┐
│ 1. Extract entities  │
│ 2. Recall memories   │
│ 3. Load persona      │
│ 4. Load corrections  │
│ 5. Build system      │◄── persona + corrections + memories → system prompt
│    prompt            │
│ 6. Call LLM API      │
│ 7. Store trace       │◄── which memories were used + response
│ 8. Auto-memorize     │◄── save both sides of exchange
│ 9. Return response   │
└─────────────────────┘
```

---

## MQL — Memory Query Language

SQL-like query language for memory operations:

### RECALL

```sql
RECALL content, importance FROM memory
WHERE SIMILAR_TO("delivery optimization", threshold=0.75)
AND importance > 0.7
AND ENTITY_MATCH(["Dropzi"])
AND timestamp WITHIN LAST 30 DAYS
LIMIT 10;
```

### MEMORIZE

```sql
MEMORIZE {
  content: "Decided to add real-time tracking",
  entities: ["Dropzi", "tracking"],
  scope: "project:dropzi"
} WITH AUTO_SCORE;
```

### Using MQL in Python

```python
from locidb.query import execute

results = execute(loci, 'RECALL * FROM memory WHERE SIMILAR_TO("routing") LIMIT 5;')
```

---

## CLI Reference

```bash
# ── Database ──
loci init                                        # create a new .loci database
loci inspect                                     # show database statistics

# ── Memory CRUD ──
loci memorize "text" -e entity1 -e entity2 -s scope
loci recall "query" -k 5 -s scope -e entity --days 30 --min-importance 0.5
loci forget <node-id>

# ── Persona ──
loci persona set name "Neetish"
loci persona set role "Engineer"
loci persona show
loci persona clear

# ── Audit Trail ──
loci explain <trace-id>                          # show trace details
loci traces -s scope -l 20                       # list recent traces

# ── Health ──
loci health                                      # show health score + diagnostics
loci health -s project:dropzi                    # scoped health check

# ── Time-Travel ──
loci snapshot 2026-03-01 -s project:dropzi       # memory state at a date
loci timeline -e deployment -s scope -l 50       # chronological event log

# ── Export / Import ──
loci export -f native -o backup.loci.json
loci export -f context_prompt -o memory.md -t 4000 -s project:dropzi
loci export -f snapshot -o quick.txt -t 2000
loci import backup.loci.json --mode merge

# ── Query ──
loci query 'RECALL * FROM memory WHERE SIMILAR_TO("routing") LIMIT 5;'

# ── Maintenance ──
loci compress --older-than 7 -s project:dropzi
```

All commands accept `--db <path>` or the `LOCI_DB` environment variable to specify the database file.

---

## Data Model

### MemoryNode

```python
MemoryNode(
    id="a1b2c3d4...",             # UUID hex
    content="...",                 # Text content
    embedding=[0.12, -0.34, ...], # 384-dim vector (all-MiniLM-L6-v2)
    importance=0.82,              # 0.0–1.0 composite score
    timestamp=datetime(...),      # Creation time (UTC)
    entity_tags=["Dropzi"],       # Extracted entities
    context_scope="project:...",  # Scope for filtering
    summary_level=0,              # 0=raw, 1=daily, 2=weekly, 3=knowledge
    access_count=5,               # Times recalled
    last_accessed=datetime(...),  # Last recall time
    memory_type="memory",         # "memory" | "correction" | "preference" | "fact"
    deleted_at=None,              # Soft-delete timestamp (None = active)
)
```

### MemoryEdge

```python
MemoryEdge(
    source_id="...",
    target_id="...",
    relationship_type="decided",  # "decided", "owns", "related_to", "caused", "corrects"
    strength=0.9,                 # 0.0–1.0
    timestamp=datetime(...)
)
```

### ConflictWarning

```python
ConflictWarning(
    node_a=MemoryNode(...),
    node_b=MemoryNode(...),
    similarity=0.92,
    reason="Negation marker 'not' present in one but not the other",
    suggested_resolution="The newer memory likely supersedes the older one."
)
```

### HealthReport

```python
HealthReport(
    overall_score=72.5,                  # 0–100
    dimensions={                         # per-dimension scores
        "coverage": 80, "freshness": 100,
        "diversity": 70, "connectivity": 75,
        "persona": 40
    },
    gaps=["No persona configured"],      # detected issues
    suggestions=["Set up a persona..."], # actionable advice
    stats={"node_count": 42, ...}        # raw statistics
)
```

---

## Importance Scoring

Every memory has a composite importance score:

```
importance = 0.4 × recency + 0.4 × entity_relevance + 0.2 × usage_frequency
```

| Component | Formula | Description |
|---|---|---|
| **Recency** | `exp(-0.05 × days)` | Exponential decay, ~14-day half-life |
| **Entity relevance** | `\|overlap\| / \|query_entities\|` | Entity tag overlap ratio |
| **Usage frequency** | `min(access_count / 10, 1.0)` | Capped at 10 accesses |

---

## Architecture

```
locidb/
├── core/
│   ├── db.py           # Main LociDB orchestrator (30+ methods)
│   ├── node.py         # MemoryNode Pydantic model
│   ├── edge.py         # MemoryEdge Pydantic model
│   ├── store.py        # SQLite + numpy vector search + CRUD
│   ├── indexer.py      # sentence-transformers embedding (lazy-loaded)
│   ├── scorer.py       # Importance scoring engine
│   ├── summarizer.py   # Memory compression engine
│   ├── portability.py  # Export/import (native JSON, context prompt, snapshot)
│   ├── persona.py      # Persona store (identity, preferences, style)
│   ├── conflict.py     # Contradiction detector (similarity + negation)
│   ├── trace.py        # Reasoning audit trail
│   └── health.py       # Memory health scoring & onboarding
├── query/
│   ├── parser.py       # MQL parser (RECALL / MEMORIZE)
│   ├── planner.py      # Query planner
│   └── executor.py     # Query executor
├── adapters/
│   ├── openai_adapter.py    # OpenAI chat with auto-memory
│   ├── anthropic_adapter.py # Anthropic Claude with auto-memory
│   └── langchain_adapter.py # LangChain-compatible memory backend
└── cli/
    └── main.py         # Click CLI (15+ commands)
```

### Storage

- **SQLite** for persistence — zero configuration, single-file database
- **numpy** for vector similarity — brute-force cosine similarity (suitable for <100K memories)
- **sentence-transformers** (`all-MiniLM-L6-v2`) for embeddings — 384-dim, lazy-loaded

---

## Testing

```bash
# Run all tests
pytest tests/ -v

# Run specific feature tests
pytest tests/test_persona.py -v
pytest tests/test_corrections.py -v
pytest tests/test_contradiction.py -v
pytest tests/test_audit.py -v
pytest tests/test_timetravel.py -v
pytest tests/test_health.py -v
pytest tests/test_integration_v02.py -v
```

**134 tests** across 10 test files, all passing.

| Test file | Tests | Covers |
|---|---|---|
| `test_core.py` | 17 | Core memorize/recall/traverse/link/compress |
| `test_query.py` | 8 | MQL parser and planner |
| `test_adapters.py` | 9 | OpenAI, Anthropic, LangChain adapters |
| `test_portability.py` | 30 | Export native/context_prompt/snapshot, import |
| `test_persona.py` | 11 | Set/get/update/clear/prompt/persistence |
| `test_corrections.py` | 10 | Correct, soft-delete, memory types |
| `test_audit.py` | 10 | Store/list/explain traces, persistence |
| `test_contradiction.py` | 6 | Conflict detection, negation heuristics |
| `test_timetravel.py` | 12 | Snapshot, timeline, diff, recall as_of |
| `test_health.py` | 12 | Health dimensions, onboarding, gaps |
| `test_integration_v02.py` | 5 | End-to-end workflow with all features |

---

## Contributing

```bash
# Clone and install in development mode
git clone https://github.com/neetishsingh/locidb.git
cd locidb
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Run a specific test
pytest tests/test_health.py::TestHealthScore::test_empty_db_health -v
```

### Project philosophy

1. **Zero config** — `LociDB("./memory.loci")` and you're running
2. **Batteries included** — embeddings, vector search, graph, scoring, export all built in
3. **Own your memory** — export to any LLM, no vendor lock-in
4. **Auditable** — every response traceable to the memories that informed it
5. **Correct over time** — memories can be corrected, contradictions flagged

---

## Changelog

### v0.2.0 (2026-03-20)

**New features:**
- **Persona Store** — persistent user identity, preferences, communication style
- **Correction Memory** — supersede wrong memories with explicit corrections + `corrects` edges
- **Contradiction Detector** — auto-scan for conflicting memories via similarity + negation
- **Reasoning Audit Trail** — full trace of which memories informed each response
- **Time-Travel Queries** — snapshot, timeline, diff, recall `as_of`
- **Memory Health Score** — 5-dimension diagnostics, gap detection, onboarding questions

**Improvements:**
- Soft-delete by default (preserves history for time-travel)
- `memory_type` field on nodes (`memory`, `correction`, `preference`, `fact`)
- Adapters now inject persona + corrections + store traces automatically
- 15+ new CLI commands
- SQLite schema migration for existing databases
- 134 tests (up from 68)

### v0.1.0

- Initial release
- Core: memorize, recall, traverse, link, compress, forget
- Vector search with sentence-transformers
- Knowledge graph with typed edges
- Importance scoring with temporal decay
- MQL query language
- OpenAI, Anthropic, LangChain adapters
- CLI with 10 commands
- Export/import (native JSON, context prompt, snapshot)

---

## License

MIT — see [LICENSE](LICENSE) for details.
