Metadata-Version: 2.4
Name: lithtrix-langgraph
Version: 0.1.1
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

A [LangGraph](https://github.com/langchain-ai/langgraph) `BaseStore` adapter that gives your graph persistent, per-agent memory backed by the [Lithtrix](https://lithtrix.ai) API — identity, memory, and reputation infrastructure for AI agents.

## What this is (and isn't)

This package is a **memory store adapter only**. It gives a LangGraph graph a `store=` that reads and writes to Lithtrix's memory API, so your agent's memory survives restarts and is queryable by key or by semantic search.

It does **not** include Lithtrix's swarm primitives (spawning sub-agents, signed delegation contracts, audit traces) — those exist in the wider Lithtrix API but are not wrapped by this package today. If you need them, call the REST API directly; see [docs.lithtrix.ai](https://docs.lithtrix.ai).

## Install

```bash
pip install lithtrix-langgraph
```

Requires Python 3.11+.

## 1. Get an API key

Every Lithtrix agent needs its own identity and key. Register one with a single unauthenticated call — no dashboard, no approval step:

```bash
curl -X POST https://api.lithtrix.ai/v1/register \
  -H "Content-Type: application/json" \
  -H "User-Agent: my-agent/1.0" \
  -d '{
    "agent_name": "my-langgraph-agent",
    "owner_identifier": "you@example.com",
    "agree_to_terms": true
  }'
```

`agent_name` + `owner_identifier` must be unique together — reusing the same pair returns `409`. `agree_to_terms` must be `true` (accepts the [Gentle-Agent Agreement](https://lithtrix.ai/terms)).

The response includes an `api_key` field (starts with `ltx_`). **Save it now — it is only ever shown once.** Set it as an environment variable:

```bash
export LITHTRIX_API_KEY=ltx_your_key_here
```

## 2. Configure the store

```python
from lithtrix_langgraph import LithtrixStore

store = LithtrixStore()  # reads LITHTRIX_API_KEY from the environment
```

`api_key` / `api_url` can also be passed as constructor kwargs, which override the environment — useful in tests or when running multiple agents from one process.

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

## 3. A complete working example

Compile a graph with `store=` and any node can read and write memory via LangGraph's `get_store()`:

```python
from lithtrix_langgraph import LithtrixStore
from langgraph.graph import StateGraph
from langgraph.config import get_store
from typing_extensions import TypedDict


class State(TypedDict):
    note: str


def remember(state: State) -> State:
    store = get_store()
    store.put(("my-agent",), "last-note", {"text": state["note"]})
    item = store.get(("my-agent",), "last-note")
    return {"note": item.value["text"]}


store = LithtrixStore()
graph = StateGraph(State)
graph.add_node("remember", remember)
graph.set_entry_point("remember")
graph.set_finish_point("remember")
compiled = graph.compile(store=store)

result = compiled.invoke({"note": "hello from LangGraph"})
print(result)  # {'note': 'hello from LangGraph'}
```

This example was run against the live production API as part of writing this README — not just tested against mocks.

## 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`).

## Free tier

New agents get **1,000 memory writes/month** and **5 MiB of KV storage**, no credit card required. Reads and semantic search don't count against the write limit. See [docs.lithtrix.ai](https://docs.lithtrix.ai) for paid tiers if you outgrow it.

## Tests

```bash
git clone https://github.com/lithtrix/api.git
cd api/lithtrix-langgraph
pip install -e '.[dev]'
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`).
