# How the source_chat Workflow Differs from the General Chat Workflow in LangGraph

> Explore the key differences between LangGraph's source_chat and general chat workflows. Understand how source_chat enhances context retrieval for richer interactions.

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

---

**The `source_chat` workflow augments the standard chat flow with a dedicated context-building step that retrieves a specific source's text and insights, while the general `chat` workflow simply forwards message history to the LLM without external document retrieval.**

Open‑Notebook implements two distinct LangGraph state machines to handle conversational AI: a general-purpose chat for open-ended notebook queries and a specialized source-grounded chat for document-centric interactions. Understanding how the **source_chat workflow differs from the general chat workflow** is essential for developers extending the `lfnovo/open-notebook` architecture or debugging context-retrieval issues.

## State Schema and Data Model

The fundamental difference begins with the underlying state definitions.

**General Chat** uses `ThreadState`, defined in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py). This state contains:
- `messages` – the conversation history
- Optional `notebook`, `context`, `context_config`
- `model_override` for temporary model switching

**Source Chat** uses `SourceChatState`, defined in [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py), which extends the base structure with:
- **Required `source_id`** – the primary key of the document to query
- Optional `source`, `insights`, `context_indicators`
- `model_override` and `context` fields

This schema difference enforces that every `source_chat` invocation must target a specific document, whereas the general chat operates over the notebook's message history without document constraints.

## Context Building: The Key Architectural Difference

The most significant divergence occurs in the context preparation phase.

**General Chat** relies solely on the existing message history. As implemented in [`api/routers/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/chat.py), the system renders a static system prompt from the current state without fetching external content:

```python
payload = [SystemMessage(content=system_prompt)] + state["messages"]
model = await provision_langchain_model(str(payload), model_id, "chat", max_tokens=8192)

```

**Source Chat** triggers an explicit retrieval pipeline. In [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py), the workflow invokes `ContextBuilder` to fetch the source's full text and associated insights:

```python
context_data = ContextBuilder(
    source_id=state["source_id"],
    include_insights=True,
    include_notes=False,
    max_tokens=50000,
).build()

```

The `ContextBuilder` (located in [`open_notebook/utils/context_builder.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/context_builder.py)) retrieves the document, extracts relevant insights, and formats them using `_format_source_context()`. This enriched data populates a `prompt_data` dictionary containing the source metadata, insights list, formatted context, and tracking indicators.

## Prompt Templates and System Instructions

The two workflows use distinct prompt templates to handle their different data payloads.

**General Chat** uses the generic `chat/system` template, which receives only basic conversation context and notebook metadata.

**Source Chat** uses the specialized `source_chat/system` template, which expects a richer payload:

```python
prompt_data = {
    "source": source.model_dump() if source else None,
    "insights": [i.model_dump() for i in insights],
    "context": _format_source_context(context_data),
    "context_indicators": context_indicators,
}
system_prompt = Prompter(prompt_template="source_chat/system").render(data=prompt_data)

```

This template renders the source content and insights directly into the system message, grounding the LLM's responses in specific document content rather than general knowledge.

## Return Values and State Updates

The state update mechanisms reflect their different operational scopes.

**General Chat** returns a minimal state update containing only the cleaned message list:

```python
return {"messages": cleaned}

```

**Source Chat** returns an enriched state that preserves the retrieval context for subsequent turns:

```python
return {
    "messages": cleaned_message,
    "source": source,
    "insights": insights,
    "context": formatted_context,
    "context_indicators": context_indicators,
}

```

This return structure allows the `source_chat` workflow to maintain provenance information about which sources and insights informed the current response, enabling transparency features like citation tracking.

## Implementation in Code

Both workflows share the same model provisioning helper, `provision_langchain_model`, but differ in how they prepare the payload and handle state.

### General Chat Implementation

Found in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) and exposed via [`api/routers/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/chat.py), the general chat follows a straightforward pattern:

1. Render system prompt from current state
2. Assemble message payload
3. Invoke model via `provision_langchain_model`
4. Return cleaned messages

This approach suits queries like "Summarize my notes" or "What did I write last Tuesday?" that require no external document retrieval.

### Source Chat Implementation

Found in [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py) and exposed via [`api/routers/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/source_chat.py), the source chat adds the retrieval layer:

1. Validate `source_id` exists in state
2. Build context using `ContextBuilder` with `include_insights=True`
3. Format context and prepare `prompt_data` dict
4. Render source-specific system prompt
5. Invoke model via `provision_langchain_model`
6. Return enriched state with source metadata and context indicators

This implementation handles queries like "Explain this PDF" or "What are the key insights from source X?" where the LLM must reference specific document content.

## Summary

- **State Structure**: General chat uses `ThreadState` with optional metadata; source chat requires `SourceChatState` with a mandatory `source_id` and fields for insights and context indicators.
- **Context Retrieval**: Only `source_chat` invokes `ContextBuilder` to fetch document text and insights; general chat relies entirely on existing message history.
- **Prompt Enrichment**: `source_chat` uses the `source_chat/system` template with formatted document context; general chat uses the basic `chat/system` template.
- **Response Metadata**: `source_chat` returns source provenance and context indicators; general chat returns messages only.
- **Use Cases**: General chat handles open-ended notebook queries; source chat handles document-specific questions requiring grounded retrieval.

## Frequently Asked Questions

### Can I use the general chat workflow to query specific documents?

No. The general chat workflow in [`open_notebook/graphs/chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) lacks the `source_id` field and `ContextBuilder` integration required for document retrieval. To query specific sources, you must use the `source_chat` workflow, which enforces a `source_id` parameter and retrieves document content via the `ContextBuilder` utility.

### Does the source_chat workflow support multiple sources simultaneously?

Based on the current implementation in [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py), the state schema accepts a single `source_id` and associated `source` object. The `ContextBuilder` is instantiated with one `source_id` per invocation. For multi-source queries, you would need to either extend the state schema to accept a list of source IDs or chain multiple context-building steps before the LLM call.

### Why does source_chat return context_indicators while general chat does not?

The `context_indicators` field tracks which specific insights and document sections informed the LLM's response. Since the general chat workflow ([`chat.py`](https://github.com/lfnovo/open-notebook/blob/main/chat.py)) does not retrieve external documents, there are no source references to track. The `source_chat` workflow requires these indicators to provide transparency and citation capabilities for document-grounded answers.

### Are both workflows checkpointed in the same database?

Yes. Both workflows use the same SQLite checkpoint configuration defined by `LANGGRAPH_CHECKPOINT_FILE`. However, because `source_chat` uses the expanded `SourceChatState` schema, its checkpointed state contains additional fields (`source`, `insights`, `context_indicators`) that are absent from general chat checkpoints.