Metadata-Version: 2.4
Name: cortex-vault
Version: 0.1.0
Summary: LLM-powered long-term memory layer backed by a Neo4j knowledge graph.
Author: tdevansh
License: MIT
Project-URL: Repository, https://github.com/tdevansh/cortex-vault
Keywords: llm,memory,neo4j,langchain,knowledge-graph,retrieval-augmented-generation,agent
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: langchain-groq>=0.2
Requires-Dist: langchain>=0.3
Requires-Dist: sentence-transformers>=3.0
Requires-Dist: neo4j>=5.0
Requires-Dist: python-dotenv>=1.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-mock>=3.14; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"

# cortex-memory

> An LLM-powered long-term memory layer backed by a **Neo4j knowledge graph** — packaged as a reusable Python library you can drop into any project.

[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)

---

## What it does

1. **Chat** — queries the LLM with retrieved memory context injected automatically.
2. **Retrieve** — embeds your query with `all-MiniLM-L6-v2`, runs a Neo4j vector-index search, then re-ranks with:
   - `importance_weight` (very_high → low)
   - `stability_weight` (permanent → transient)
   - `tag_matches` between the query and stored memory tags
3. **Ingest** — asks the LLM to decide if an input is worth storing. If so, it extracts `type`, `label`, `content`, `stability`, `importance`, and `related_entities`, chunks the text, embeds each chunk, and upserts it into Neo4j as a `Memory` node linked to `Entity` nodes via `RELATED_TO`.
4. **Reinforce** — memories recalled during chat have their `last_accessed` refreshed; `transient` memories are promoted to `stable`.

---

## Project structure

```
cortex-ai-memory/
├── src/
│   └── cortex_memory/          ← installable package
│       ├── __init__.py         ← public API
│       ├── __main__.py         ← CLI entrypoint
│       ├── agent.py            ← CortexMemory orchestrator
│       ├── config.py           ← CortexConfig dataclass
│       ├── graph/
│       │   └── memory_graph.py ← Neo4j upsert (MemoryGraph)
│       ├── retrieval/
│       │   └── retriever.py    ← WeightedMemoryRetriever
│       ├── ingestion/
│       │   └── ingestor.py     ← ingestion pipeline
│       ├── llm/
│       │   └── chat.py         ← LLM client + chat_with_assistant
│       └── prompts/
│           └── templates.py    ← all prompt strings
├── examples/
│   ├── basic_chat.py
│   └── ingest_document.py
├── tests/
│   ├── test_retriever.py
│   └── test_ingestor.py
├── setups/
│   └── neo4j/                  ← Docker / docker-compose setup
├── pyproject.toml
├── requirements-dev.txt
└── .env.example
```

---

## Prerequisites

- Python 3.10+
- Docker (for Neo4j 5.13+)
- A [Groq API key](https://console.groq.com/keys)

---

## Installation

### As a dependency in another project

```bash
pip install git+https://github.com/your-org/cortex-ai-memory.git
```

### For local development

```bash
git clone https://github.com/your-org/cortex-ai-memory.git
cd cortex-ai-memory
pip install -e ".[dev]"
```

---

## Configuration

Copy `.env.example` to `.env` and fill in your values:

```env
NEO4J_URI=bolt://localhost:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=your_neo4j_password_here
GROQ_API_KEY=your_groq_api_key_here

# Optional overrides
# CORTEX_MODEL=openai/gpt-oss-20b
# CORTEX_EMBEDDER=all-MiniLM-L6-v2
# CORTEX_TOP_K=5
# CORTEX_SCORE_THRESHOLD=0.65
# CORTEX_CHUNK_MAX_CHARS=400
```

---

## Usage

### Python API

```python
from cortex_memory import CortexMemory

# Context manager — closes Neo4j connections automatically
with CortexMemory.from_env() as memory:
    # Retrieve + chat + ingest + reinforce — all in one call
    response = memory.chat("What do I know about Japan?")
    print(response)

    # Ingest standalone text
    memory.ingest("User booked flights to Tokyo for March.", source="upload")

    # Raw retrieval without chat
    results = memory.retrieve("Japan travel plans", top_k=5)
```

### CLI

```bash
# Chat
cortex-memory chat "What do I know about Japan?"

# Ingest
cortex-memory ingest "User loves hiking in the Alps and dislikes crowded cities."

# Override top-k and disable auto-ingestion
cortex-memory chat "Remind me about my diet goals." --top-k 3 --no-ingest
```

---

## Running Neo4j with Docker

```bash
docker run -d \
  --name memory-graph-neo4j \
  -p 7474:7474 \
  -p 7687:7687 \
  -e NEO4J_AUTH=neo4j/your_neo4j_password_here \
  -v neo4j_data:/data \
  neo4j:5.15
```

Or using **docker-compose** (see `setups/neo4j/`):

```bash
docker-compose -f setups/neo4j/docker-compose.yml up -d
```

Verify at **http://localhost:7474**.

---

## Running tests

```bash
pytest tests/
```

---

## Memory schema

| Field | Description |
|---|---|
| `id` | UUID |
| `type` | `context`, `event`, `fact`, … |
| `label` | Short title |
| `content` | Chunked text |
| `source` | `upload`, `assistant_chat`, `cli`, … |
| `created_at` / `last_accessed` | ISO timestamps |
| `stability` | `transient` → `stable` → `permanent` |
| `status` | `active` |
| `tags` | Keyword list |
| `embedding` | 384-dim vector (`all-MiniLM-L6-v2`) |
| `importance` | `very_high`, `high`, `medium`, `low` |

Related entities are linked as `(:Memory)-[:RELATED_TO]->(:Entity)`.

---

## Using in another project

```python
# my_project/memory_layer.py
from cortex_memory import CortexMemory, CortexConfig

# Explicit config — no .env needed
config = CortexConfig(
    neo4j_uri="bolt://localhost:7687",
    neo4j_username="neo4j",
    neo4j_password="secret",
    groq_api_key="gsk_...",
    top_k=10,
    score_threshold=0.70,
)
memory = CortexMemory(config)
```
