# LangGraph Workflow Architecture in Open Notebook: How StateGraph Orchestrates AI Pipelines

> Explore Open Notebook's LangGraph workflow architecture. Discover how StateGraph orchestrates AI pipelines with typed dictionaries, async functions, and conditional edges for efficient processing.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: architecture
- Published: 2026-06-23

---

**Open Notebook implements four specialized LangGraph StateGraph workflows—source ingestion, retrieval-augmented generation, chat sessions, and content transformation—using typed dictionaries for state management, async node functions, and conditional edges to handle complex AI processing pipelines.**

Open Notebook leverages LangGraph to orchestrate its core AI processing pipelines, moving beyond simple linear execution to robust graph-based state machines. Each workflow is implemented as a compiled `StateGraph` that maintains type-safe state across asynchronous nodes. According to the lfnovo/open-notebook source code, this architecture enables the application to handle everything from document ingestion to conversational memory with explicit control over data flow.

## Core Architectural Concepts

LangGraph workflows in Open Notebook follow a consistent pattern built on five foundational concepts.

### TypedDict State Management

Every workflow defines a specific `TypedDict` that describes the exact data passed between nodes. For example, `SourceState` contains the raw `ProcessSourceState`, target notebook IDs, transformation lists, and embed flags, while `ThreadState` tracks messages, questions, and accumulated answers. This type-safe approach enforces runtime contracts and makes the flow of information explicit across the graph.

### Async Node Functions

Each node is an async callable that receives the current state and a `RunnableConfig`. These functions perform single, discrete steps such as content extraction, model invocation via `provision_langchain_model`, or vector search. They return dictionaries that merge back into the state, enabling functional state updates without side effects.

### Edge Routing and Conditional Logic

The graph connects nodes with `add_edge` for linear flows or `add_conditional_edges` for dynamic branching. Conditional edges use trigger functions—such as `trigger_transformations` or `trigger_queries`—to determine the next path based on runtime state values.

### Graph Compilation and Execution

Each workflow is compiled via `workflow.compile()` into a LangGraph `Runnable`. The compiled graph exposes `ainvoke()` for asynchronous execution, allowing the system to await `await graph.ainvoke(initial_state)` and receive the final mutated state.

### Checkpoint Persistence

The chat workflow specifically implements persistence using `SqliteSaver` from LangGraph. According to [`open_notebook/config.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/config.py), the checkpoint file path is defined by `LANGGRAPH_CHECKPOINT_FILE`, enabling chat sessions to survive server restarts.

## The Four Main Workflow Types

Open Notebook organizes its processing domains into modular graph definitions, each living in a dedicated file under `open_notebook/graphs/`.

### Source Ingestion Pipeline

Located in [`open_notebook/graphs/source.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source.py), this workflow handles document extraction, optional transformation, and vector storage.

**State**: `SourceState` (contains `ProcessSourceState`, notebook IDs, transformation list, embed flag)

**Node Functions**:
- `content_process` – Calls `content_core.extract_content` to parse the raw source
- `save_source` – Persists the `Source` record and invokes `vectorize()` if embedding is enabled
- `transform_content` – Delegates to the transformation graph when processing is requested

**Graph Flow**: `START → content_process → save_source`. A conditional edge `trigger_transformations` determines whether to continue to `transform_content`, which then flows to `END`.

### Ask / Retrieval-Augmented Generation

The [`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py) workflow implements multi-step RAG, decomposing questions into search strategies and synthesizing answers from vector search results.

**State**: `ThreadState` (question, generated `Strategy`, accumulated sub-answers, final answer)

**Node Functions**:
- `agent` (`call_model_with_messages`) – Prompts the LLM to produce a JSON-encoded `Strategy` with search terms
- `provide_answer` – Executes `vector_search` for each term and queries the LLM for sub-query answers
- `write_final_answer` – Combines all sub-answers into a polished final response

**Graph Flow**: `START → agent`. The conditional edge `trigger_queries` branches to `provide_answer` for each search term in the strategy. After all sub-answers complete, the flow proceeds to `write_final_answer → END`.

### Chat Session Management

The chat workflow in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) maintains conversational state with checkpoint persistence.

**State**: `ThreadState` (messages list, optional notebook/context metadata)

**Node Functions**:
- `agent` (`call_model_with_messages`) – Builds a system prompt, calls `provision_langchain_model`, and returns cleaned AI messages (stripping thinking tags)

**Graph Flow**: Simple linear execution: `START → agent → END`. The graph is configured with a SQLite checkpoint (`SqliteSaver`) enabling session resumption after process restarts.

### Content Transformation

Located in [`open_notebook/graphs/transformation.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/transformation.py), this workflow applies user-selected text transformations like summarization or translation.

**State**: `TransformationState` (source content + transformation directive)

**Node Functions**:
- `apply_transformation` – Invokes the dedicated `transform_graph.ainvoke` to process the content

**Graph Flow**: `START → apply_transformation → END`

## Key Implementation Patterns

Several architectural patterns emerge across the codebase that ensure maintainability and performance.

**Modular Graph Organization**: Each domain lives in its own module ([`source.py`](https://github.com/lfnovo/open-notebook/blob/main/source.py), [`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py), [`chat.py`](https://github.com/lfnovo/open-notebook/blob/main/chat.py), [`transformation.py`](https://github.com/lfnovo/open-notebook/blob/main/transformation.py)), making the system extensible. New workflows can be added by defining a new `StateGraph` and wiring it into the API layer.

**Async-First Design**: All node functions are `async`, allowing non-blocking I/O for database calls and external LLM APIs. This is critical for handling concurrent source processing and chat sessions.

**Reusable AI Components**: The `provision_langchain_model` helper in [`open_notebook/ai/provision.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/provision.py) centralizes model provisioning and configuration. This function is imported and reused across [`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py), [`chat.py`](https://github.com/lfnovo/open-notebook/blob/main/chat.py), and other workflows.

**Conditional Branching Logic**: Dynamic edges enable data-dependent routing. For example, the source graph only processes transformations if the `apply_transformations` list is non-empty, preventing unnecessary computation.

## How to Invoke Workflows

The API layer invokes these compiled graphs using asynchronous state initialization.

Ingest a new source:

```python
from open_notebook.graphs.source import source_graph

initial_state = {
    "content_state": {...},          # prepared by the API handler

    "apply_transformations": [],    # list of Transformation objects

    "source_id": "src_123",
    "notebook_ids": ["nb_1"],
    "source": source_obj,
    "transformation": [],
    "embed": True,
}
result = await source_graph.ainvoke(initial_state)

```

Execute a retrieval-augmented question:

```python
from open_notebook.graphs.ask import graph as ask_graph

answer = await ask_graph.ainvoke({"question": "What is quantum computing?"})
print(answer["final_answer"])

```

Continue a chat session:

```python
from open_notebook.graphs.chat import graph as chat_graph

chat_state = {"messages": [...], "notebook": None}
reply = await chat_graph.ainvoke(chat_state)
print(reply["messages"].content)

```

## Summary

- **StateGraph Architecture**: Open Notebook uses compiled LangGraph StateGraphs with `TypedDict` state definitions to ensure type safety across nodes
- **Four Core Workflows**: Source ingestion ([`source.py`](https://github.com/lfnovo/open-notebook/blob/main/source.py)), RAG Q&A ([`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py)), chat sessions ([`chat.py`](https://github.com/lfnovo/open-notebook/blob/main/chat.py)), and text transformation ([`transformation.py`](https://github.com/lfnovo/open-notebook/blob/main/transformation.py)) cover all AI processing needs
- **Conditional Routing**: `add_conditional_edges` enables dynamic branching based on runtime data, such as triggering transformations only when requested
- **Persistence**: Chat workflows use `SqliteSaver` checkpointing defined in [`config.py`](https://github.com/lfnovo/open-notebook/blob/main/config.py) to maintain session state across restarts
- **Async Execution**: All node functions are async, leveraging `ainvoke()` for non-blocking I/O with databases and LLM APIs

## Frequently Asked Questions

### What is the role of StateGraph in Open Notebook?

The `StateGraph` class from LangGraph serves as the foundational orchestration layer for all AI processing in Open Notebook. It provides a structured way to define processing nodes as async functions, connect them with explicit edges, and maintain type-safe state between steps. Each workflow file ([`source.py`](https://github.com/lfnovo/open-notebook/blob/main/source.py), [`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py), etc.) instantiates a `StateGraph` with a specific `TypedDict` state schema, compiles it into a `Runnable`, and exposes it for invocation via `ainvoke()`.

### How does Open Notebook handle state persistence in chat workflows?

Chat sessions implement checkpoint persistence using LangGraph's `SqliteSaver`. In [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py), the compiled graph is configured with a SQLite-based checkpointer that writes state to the file path defined by `LANGGRAPH_CHECKPOINT_FILE` in [`open_notebook/config.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/config.py). This allows conversation history to survive server restarts, enabling users to resume chat sessions without losing context.

### What is the difference between the ask and chat workflows in Open Notebook?

The **ask workflow** ([`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py)) implements retrieval-augmented generation with a multi-step branching structure: it first generates a search strategy, then conditionally branches to execute multiple vector searches in parallel (via `trigger_queries`), and finally synthesizes a single answer. The **chat workflow** ([`chat.py`](https://github.com/lfnovo/open-notebook/blob/main/chat.py)) is a simple linear graph (`START → agent → END`) designed for conversational interaction, featuring message history management and checkpoint persistence but without the complex conditional branching of the RAG pipeline.

### How are conditional edges used in Open Notebook's LangGraph implementation?

Conditional edges enable dynamic workflow routing based on runtime state inspection. In [`open_notebook/graphs/source.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source.py), the `trigger_transformations` function examines the `apply_transformations` field to decide whether to route to the `transform_content` node or skip to `END`. Similarly, [`ask.py`](https://github.com/lfnovo/open-notebook/blob/main/ask.py) uses `trigger_queries` to map each search term in the generated strategy to parallel `provide_answer` nodes. These patterns allow the graphs to adapt their execution paths based on the actual data being processed rather than following fixed linear sequences.