# How Open-Notebook Uses SqliteSaver for Checkpoint Storage and Conversation State Persistence

> Discover how Open-Notebook leverages SqliteSaver for robust checkpoint storage, ensuring conversation state persistence through serialized ThreadState snapshots.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-21

---

**Open-Notebook persists LangGraph conversation states using SqliteSaver, a SQLite-based checkpoint mechanism that stores serialized ThreadState snapshots to disk via the LANGGRAPH_CHECKPOINT_FILE environment variable.**

The lfnovo/open-notebook repository implements persistent conversation state management for its LangGraph workflows using a lightweight checkpoint storage mechanism. By leveraging LangGraph's built-in **SqliteSaver** class, the application serializes intermediate graph states—including message history and context—to a local SQLite database file. This enables seamless conversation resumption across server restarts without requiring external database infrastructure.

## How SqliteSaver Works in Open-Notebook

### Database Connection Configuration

The checkpoint storage initializes in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) by establishing a standard SQLite connection configured for multi-threaded access. The connection references the file path defined by the `LANGGRAPH_CHECKPOINT_FILE` environment variable (set in [`open_notebook/config.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/config.py)), with `check_same_thread=False` enabled to support async thread sharing.

```python

# open_notebook/graphs/chat.py

import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver
from open_notebook.config import LANGGRAPH_CHECKPOINT_FILE

conn = sqlite3.connect(
    LANGGRAPH_CHECKPOINT_FILE,
    check_same_thread=False,
)
memory = SqliteSaver(conn)

```

### Graph Compilation with Checkpointer

When compiling the LangGraph state machine, the `SqliteSaver` instance attaches to the graph via the `checkpointer` parameter. This instructs LangGraph to automatically serialize the state dictionary (containing **ThreadState** or **SourceChatState**) after each node transition.

```python

# open_notebook/graphs/chat.py

from langgraph.graph import StateGraph, START, END

agent_state = StateGraph(ThreadState)
agent_state.add_node("agent", call_model_with_messages)
agent_state.add_edge(START, "agent")
agent_state.add_edge("agent", END)
graph = agent_state.compile(checkpointer=memory)

```

### State Retrieval and Persistence

LangGraph internally calls `memory.save(state)` to persist checkpoints after each execution step. Retrieval occurs through `memory.get_state(state_id)`, which returns the complete conversation history, notebook references, and model overrides stored in the checkpoint.

## Handling Async Operations with ThreadPoolExecutor

Because `SqliteSaver` operates synchronously, the FastAPI routers in [`api/routers/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/chat.py) wrap checkpoint retrieval in a thread pool to prevent blocking the async event loop. The implementation uses `concurrent.futures.ThreadPoolExecutor` to execute `get_state()` calls in separate threads.

```python

# api/routers/chat.py

import concurrent.futures
import asyncio

async def get_last_state(state_id: str):
    # SqliteSaver is sync, so run in a thread

    with concurrent.futures.ThreadPoolExecutor() as pool:
        state = await asyncio.get_event_loop().run_in_executor(
            pool, memory.get_state, state_id
        )
    return state

```

## Why SQLite for Checkpoint Storage?

SQLite provides a file-based persistence layer that requires no external database service, making it ideal for local development and lightweight deployments. The checkpoint file resides in the data directory (default: `data/sqlite-db/`), storing conversation state as JSON-serialized blobs within the SQLite file structure. This architecture supports the **chat** and **source-chat** graphs documented in the repository's architecture documentation.

## Summary

- **SqliteSaver** attaches to LangGraph workflows via the `checkpointer` parameter in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py)
- The SQLite connection uses `check_same_thread=False` to accommodate async operations across the `LANGGRAPH_CHECKPOINT_FILE` path
- Each checkpoint contains full **ThreadState** or **SourceChatState** objects, including message history and context
- Async endpoints in [`api/routers/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/chat.py) use **ThreadPoolExecutor** to wrap synchronous `get_state()` calls
- Checkpoints enable conversation resumption after server restarts without external database dependencies

## Frequently Asked Questions

### Where is the checkpoint file location configured?

The checkpoint file location is defined by the `LANGGRAPH_CHECKPOINT_FILE` environment variable in [`open_notebook/config.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/config.py). By default, the system stores the SQLite database in the `data/sqlite-db/` directory relative to the application root.

### Does SqliteSaver support asynchronous operations natively?

No, `SqliteSaver` provides only synchronous methods (`get_state`, `save`). The Open-Notebook codebase wraps these calls using `concurrent.futures.ThreadPoolExecutor` inside async FastAPI endpoints to prevent blocking the main event loop while maintaining responsive API performance.

### What data is stored in each checkpoint?

Each checkpoint contains a serialized snapshot of the conversation state, including the complete message history, associated notebook or source objects, context information, and any model overrides specified during the conversation thread.

### How does the source-chat graph differ from the main chat graph in checkpoint storage?

Both implementations use identical checkpoint patterns. The [`source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/source_chat.py) graph (located in [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py)) also uses `SqliteSaver` with the same `LANGGRAPH_CHECKPOINT_FILE` configuration, persisting `SourceChatState` objects that include source-specific context rather than notebook-level state.