Metadata-Version: 2.4
Name: lithtrix-langgraph
Version: 0.1.0
Summary: LangGraph BaseStore adapter for Lithtrix agent memory
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: httpx<1,>=0.27
Requires-Dist: langgraph==1.2.9
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: respx>=0.21; extra == "dev"

# lithtrix-langgraph

LangGraph `BaseStore` adapter that persists long-term agent memory via the Lithtrix HTTP API.

## Install

```bash
cd lithtrix-langgraph
pip install -e '.[dev]'
```

## Environment

| Variable | Required | Default |
|----------|----------|---------|
| `LITHTRIX_API_KEY` | Yes | — |
| `LITHTRIX_API_URL` | No | `https://api.lithtrix.ai` |

Constructor kwargs `api_key` / `api_url` override env vars (useful in tests).

## Usage

```python
from lithtrix_langgraph import LithtrixStore

store = LithtrixStore()
item = store.get(("my-agent",), "notes")
store.put(("my-agent",), "notes", {"text": "hello"})
```

Compile a LangGraph graph with `store=LithtrixStore(...)` to give nodes access to the same store API.

## Key mapping

Lithtrix keys are flat strings (1–128 chars, charset `[a-zA-Z0-9-_.:]`).

| LangGraph call | Lithtrix key |
|----------------|--------------|
| `get((), "deerflow:rung1:mcp-interop-2025:findings")` | `deerflow:rung1:mcp-interop-2025:findings` |
| `get(("deerflow", "rung1", "mcp-interop-2025"), "findings")` | same |

Empty namespace passes the key through unchanged so DeerFlow Rung 1 flat keys remain readable.

## Values

- **Put:** LangGraph values are `dict` → `PUT /v1/memory/{key}` with body `{"value": <dict>}`. Serialized size is checked locally at **512 KiB** before HTTP (mirrors API `MEMORY_VALUE_TOO_LARGE` / HTTP 413).
- **Get:** JSON objects are returned as-is. String/number/array payloads (DeerFlow Rung 1) are wrapped as `{"content": <raw>}`.
- **Timestamps:** Uses Lithtrix `created_at` / `updated_at` when present; otherwise `datetime.now(UTC)` on read.
- **TTL:** `supports_ttl = False`; `PutOp.ttl` is ignored.

## SearchOp supported subset

| Feature | Support |
|---------|---------|
| `namespace_prefix` | Yes → Lithtrix list `prefix` |
| `query` (semantic) | Yes → `GET /v1/memory/search` |
| `limit` / `offset` | Yes (best-effort pagination) |
| `filter` with `query` | Partial — exact top-level match applied client-side after semantic search |
| `filter` without `query` | Partial — list keys under prefix, fetch values, exact match only |
| `$eq` / `$ne` / `$gt` / … | **No** — raises `NotImplementedError` |
| Cross-namespace search | No |

## HTTP errors

401/403/413/422 responses propagate as `LithtrixAPIError` with `error_code` when the API returns structured JSON (e.g. `MEMORY_VALUE_TOO_LARGE`).

## Tests

```bash
cd lithtrix-langgraph && python -m pytest -q
```

All tests mock HTTP; no live API calls in the default suite.

## Phoenix harness (cross-framework proof)

Optional live run — requires `LITHTRIX_API_KEY` (uses real API credits):

```bash
export LITHTRIX_API_KEY=ltx_...
python scripts/langgraph_phoenix_harness.py --help
python scripts/langgraph_phoenix_harness.py --out /tmp/phoenix_metrics.json
```

Proves DeerFlow-path flat key write → LangGraph `LithtrixStore.get((), MEMORY_KEY)` read on the same agent. Default CI tests mock the harness logic (`tests/test_phoenix_harness_logic.py`).
