# langgraph-checkpoint-hana

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

**LangGraph checkpoint saver for SAP HANA Cloud** — persist AI agent state where your enterprise data already lives.

This package implements the [LangGraph `BaseCheckpointSaver`](https://langchain-ai.github.io/langgraph/reference/checkpoints/) interface for SAP HANA Cloud, enabling conversation memory, human-in-the-loop workflows, time travel, and fault recovery for LangGraph agents running on SAP BTP.

## Why HANA?

LangGraph provides official checkpointers for PostgreSQL, SQLite, and Redis. But if your enterprise runs on SAP, your business data is in HANA — and your AI agents should persist their state there too.

**Benefits of keeping agent state in HANA Cloud:**

- **Co-located with business data** — agent state lives alongside financial documents, master data, and transactional records. One database to manage, back up, and secure.
- **Enterprise-grade security** — leverage HANA's native audit logging, role-based access control, and encryption at rest.
- **BTP-native deployment** — deploy LangGraph agents on SAP BTP (Kyma / Cloud Foundry) with HANA Cloud as the persistence layer. No external database dependencies.
- **Unified querying** — join agent decision traces with business data using standard SQL.

## Installation

```bash
pip install langgraph-checkpoint-hana
```

**Requirements:**
- Python 3.10+
- SAP HANA Cloud instance (or HANA on-premise 2.0 SPS 05+)
- `hdbcli` driver (installed automatically)

## Quick Start

```python
from langgraph_checkpoint_hana import HANASaver
from langgraph.graph import StateGraph, MessagesState, START, END

# Create checkpointer from connection parameters
with HANASaver.from_conn_info(
    address="your-instance.hanacloud.ondemand.com",
    port=443,
    user="DBADMIN",
    password="your-password",
) as checkpointer:
    
    # Build your graph
    workflow = StateGraph(MessagesState)
    workflow.add_node("chatbot", chatbot_node)
    workflow.add_edge(START, "chatbot")
    workflow.add_edge("chatbot", END)
    
    # Compile with HANA persistence
    graph = workflow.compile(checkpointer=checkpointer)
    
    # Invoke — state is automatically persisted to HANA
    config = {"configurable": {"thread_id": "user-session-42"}}
    result = graph.invoke(
        {"messages": [("human", "What's our AP aging?")]},
        config,
    )
    
    # State survives restarts — resume anytime
    state = graph.get_state(config)
```

## Usage with Environment Variables

Convenient for containerised deployments on Kyma or Cloud Foundry:

```python
import os
os.environ["HANA_HOST"] = "your-instance.hanacloud.ondemand.com"
os.environ["HANA_PORT"] = "443"
os.environ["HANA_USER"] = "DBADMIN"
os.environ["HANA_PASSWORD"] = "your-password"

checkpointer = HANASaver.from_env()
checkpointer.setup()
```

## Usage with Existing Connection

If you already manage HANA connections in your application (e.g. via a connection pool or SAP CAP service bindings):

```python
from hdbcli import dbapi
from langgraph_checkpoint_hana import HANASaver

conn = dbapi.connect(address="...", port=443, user="...", password="...")
checkpointer = HANASaver(conn=conn)
checkpointer.setup()  # creates tables if needed

# Use with your graph
graph = workflow.compile(checkpointer=checkpointer)
```

## Tables Created

`HANASaver.setup()` creates two tables (if they don't already exist):

| Table | Purpose |
|---|---|
| `LANGGRAPH_CHECKPOINTS` | Stores graph state snapshots (one row per checkpoint) |
| `LANGGRAPH_CHECKPOINT_WRITES` | Stores pending writes for fault recovery |

Both tables use `NCLOB` columns for serialised data and are keyed by `(thread_id, checkpoint_ns, checkpoint_id)`.

## Thread Management

```python
# Delete all state for a thread (cleanup, GDPR, error recovery)
checkpointer.delete_thread("user-session-42")

# List checkpoint history
for cp in checkpointer.list(config, limit=10):
    print(cp.checkpoint["id"], cp.metadata)

# Time travel — get a specific checkpoint
historical_config = {
    "configurable": {
        "thread_id": "user-session-42",
        "checkpoint_id": "1ef4f797-8335-6428-8001-8a1503f9b875",
    }
}
past_state = graph.get_state(historical_config)
```

## Async Support

The `hdbcli` driver is synchronous. Async methods (`aget_tuple`, `alist`, `aput`, `aput_writes`, `adelete_thread`) delegate to their sync counterparts. For high-concurrency async deployments, consider wrapping calls with `asyncio.to_thread()`.

## Development

```bash
git clone https://github.com/stubborncoder/langgraph-checkpoint-hana.git
cd langgraph-checkpoint-hana
pip install -e ".[dev]"
pytest
```

## Compatibility

| Component | Tested Versions |
|---|---|
| LangGraph | 0.2.x, 0.3.x |
| `langgraph-checkpoint` | 2.x |
| SAP HANA Cloud | 2024.x, 2025.x |
| Python | 3.10, 3.11, 3.12 |

## License

MIT

## Contributing

Contributions welcome. Please open an issue to discuss before submitting a PR.
