Metadata-Version: 2.4
Name: engramedb
Version: 1.1.5
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
License-File: LICENSE
Summary: Real-time Code-Native Graph Database Engine (Rust Core)
Author: Ghassen Saidi
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# engramedb

[![PyPI Version](https://img.shields.io/pypi/v/engramedb.svg)](https://pypi.org/project/engramedb/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python](https://img.shields.io/pypi/pyversions/engramedb.svg)](https://pypi.org/project/engramedb/)

EngramDB is a high-performance, code-native graph database engine implemented in Rust with PyO3 Python bindings. It is designed for AI agents, MCP servers, code search and static analysis tooling. EngramDB compiles functions, classes, files and their relationships into an in-memory Compressed Sparse Row (CSR) graph for low-latency call-graph queries and impact analysis.

## Overview

Cordyceps and similar systems require fast, language-aware dependency analysis without invoking a full compiler. EngramDB provides:

- Deterministic node identity (`file_path:qualified_name`) with content hashing (Blake3)
- Dual CSR graphs: execution graph (call edges) and full dependency graph (execution + structural)
- Snapshot persistence with versioned binary format for fast cold-start and warm-start

Package name on PyPI is `engramedb`. Import name is `engramdb`:

```bash
pip install engramedb
```

```python
import engramdb
```

## Features

- **CSR Graph Traversal** — Bidirectional CSR with `blast_radius` (callers) and `recursive_callees` (callees), configurable depth, microsecond latency on 50k nodes
- **Thread Safety** — `Arc<RwLock<InnerEngineState>>` with concurrent `read()` and exclusive `write()`; `snapshot_loaded` guard for warm-start correctness
- **Rich Metadata Index** — Nodes for `Function`, `Class`, `File`, `Folder`, `Route`, `Middleware`, `Declaration` with signatures, docstrings, decorators, inheritance (`base_classes`/`inherits`), line ranges, and `extra_json` payloads
- **Typed Edges** — Separation of `resolved_call_edges` (Python contextual resolution), `execution_edges` / `generated_execution_edges` (routes, middleware, HTTP), and `structural_edges` (containment, imports, Django ORM). `get_callers`/`get_callees` traverse execution only; `get_dependencies`/`get_dependents` traverse the full graph
- **Python Contextual Resolution (v3)** — Lexically scoped, import-aware and receiver-typed resolution using `python_import_bindings`, `python_receiver_types`, `python_shadowed_names`; handles aliases, dotted imports, `self`/`cls`/`super()` MRO, shadowed locals, and union annotations
- **Persistence** — Binary snapshot `v3` (`EGRM` magic + version byte) persisting both metadata and typed edge sets; automatic invalidation on stale indexes
- **Pre-built Wheels** — Linux, macOS and Windows for CPython 3.10–3.13

## Installation

```bash
pip install engramedb
```

Requires Python 3.10 or newer.

## Quick Start

```python
import engramdb
import json

# 1. Initialize — workspace path is the snapshot root
engine = engramdb.PyMetadataEngine("/path/to/workspace")

# 2. Index nodes (extra metadata accepted as dict via _extra or JSON via extra_json)
engine.add_node(
    "services.py:create_sale",
    "Function",
    "create_sale",
    "services.py",
    _extra={
        "signature": "def create_sale(user, item_id):",
        "docstring": "Creates a new sales transaction.",
        "decorators": ["transaction.atomic"],
        "lines_start": 10,
        "lines_end": 25,
    }
)
engine.add_node("inventory.py:deduct_stock", "Function", "deduct_stock", "inventory.py")
engine.add_node("services.py", "File", "services.py", "services.py")

# 3. Wire edges — two args only
engine.add_edge("services.py:create_sale", "inventory.py:deduct_stock")          # execution / call edge
engine.add_structural_edge("services.py:create_sale", "services.py")            # containment edge
# generated edges (routes, middleware) use add_generated_edge and are replaced on re-resolve

# 4. Build CSR graphs
engine.build()

# 5. Query — typed traversal
print(engine.get_callees("services.py:create_sale"))       # execution callees only
print(engine.get_dependencies("services.py:create_sale"))  # execution + structural
print(engine.get_callers("inventory.py:deduct_stock"))
print(engine.blast_radius("inventory.py:deduct_stock", 3)) # transitive callers
print(engine.get_recursive_callees("services.py:create_sale", 2))

# 6. Search and metadata
print(engine.search("create_sale"))
print(engine.get_node_meta("services.py:create_sale"))
print(engine.get_all_metadata())
```

Alternative `extra_json` form for raw bindings:

```python
engine.add_node(
    "services.py:create_sale", "Function", "create_sale", "services.py",
    extra_json=json.dumps({"signature": "def create_sale(...):"})
)
```

## API Reference

| Method | Description |
|---|---|
| `PyMetadataEngine(path)` | Create or restore engine for workspace `path`. Restores `v3` snapshot if present and compatible |
| `add_node(node_id, node_type, name, file_path, ..., extra_json=None, _extra=None)` | Upsert node. `node_id` format `file:qualified_name`, files use `file_path` as id. `_extra` accepts dict, `extra_json` accepts JSON string |
| `add_edge(from_id, to_id)` | Add execution edge (call / explicit dependency) |
| `add_structural_edge(from_id, to_id)` | Add structural edge (file contains, import, class contains) |
| `add_generated_edge(from_id, to_id)` | Add generated execution edge (route, middleware, frontend HTTP) |
| `clear_generated_edges()` | Clear generated execution edges before re-resolution |
| `get_callees(node_id)` / `get_callers(node_id)` | Direct execution neighbors (deduplicated, order-preserving) |
| `get_dependencies(node_id)` / `get_dependents(node_id)` | Direct neighbors on the full graph (execution + structural + generated) |
| `blast_radius(node_id, depth)` | Transitive callers (BFS on execution graph) |
| `get_recursive_callees(node_id, depth)` | Transitive callees (BFS on execution graph) |
| `search(keyword)` | Keyword search over `name`, `file_path`, `signature` |
| `get_node_meta(node_id)` / `get_all_metadata()` | Metadata lookup |
| `contains(node_id)` / `snapshot_loaded()` | Existence and warm-start status |
| `invalidate_file(file_path)` / `repopulate_edges()` | Incremental invalidation and call re-resolution |
| `build()` / `rebuild()` / `save()` / `close()` | CSR compilation and snapshot persistence |

## Persistence and Versioning

Snapshots are stored as `.engram_snapshot.bin` in the workspace root. Format version is `3` (`EGRM` + `0x03`). Changing the extraction schema (parser `python-contextual-calls-v3`) or edge typing bumps the version and forces a full rescan when an older snapshot is encountered. `snapshot_loaded()` returns `False` for missing or incompatible snapshots.

## Thread Safety

All mutations acquire the Rust `write()` lock; reads use `read()`. No `py.allow_threads()` is used — graph operations are sub-millisecond, avoiding GIL/mutex interaction.

## License

Distributed under the [MIT License](LICENSE).

