How the Source Chat Graph Differs from the General Chat Graph in Open-Notebook

The source chat graph automatically enriches LLM conversations with document-specific context by loading source content and insights from SurrealDB, while the general chat graph operates as a simple message loop without external context retrieval.

Open-Notebook implements two distinct LangGraph workflows for handling chat interactions. Understanding how the source chat graph differs from the general chat graph is critical for developers building context-aware AI features. While both graphs share the same underlying checkpointing and model provisioning infrastructure, they diverge significantly in state management, context construction, and prompt engineering strategies.

State Shape: ThreadState vs SourceChatState

The most fundamental difference lies in the state definitions that drive each workflow.

ThreadState (defined in open_notebook/graphs/chat.py) maintains a minimal conversation footprint. It tracks messages, an optional notebook reference, generic context, and an optional model_override. This lean structure supports free-form dialogue without external dependencies.

SourceChatState (defined in open_notebook/graphs/source_chat.py) expands this schema to support grounded conversations. It requires a mandatory source_id and maintains the loaded Source object, a list of SourceInsight objects, formatted context strings, and context_indicators for tracking which insights were utilized. This richer state enables the assistant to reason over specific documents, PDFs, or web pages rather than relying solely on the conversation history.

Context Construction: The ContextBuilder Pipeline

The general chat graph employs no automatic context building. The system prompt renders statically from the chat/system template, supplemented only by whatever context the caller manually supplies. This creates a truly stateless conversational experience suitable for general Q&A.

The source chat graph implements a sophisticated ContextBuilder pipeline (located in open_notebook/utils/context_builder.py). When invoked, the graph:

  1. Retrieves the source record from SurrealDB using the provided source_id
  2. Fetches associated insights and metadata
  3. Formats them via _format_source_context into structured text
  4. Injects the built context into the source_chat/system prompt template

This automatic enrichment ensures the LLM receives relevant source material with every turn, enabling grounded responses that cite specific document content.

Prompt Template Architecture

The two graphs utilize distinct Jinja2 templates that reflect their operational modes.

chat/system serves the general graph with a generic system prompt designed for unconstrained conversation. It expects minimal variables and focuses on conversational continuity.

source_chat/system accepts four specific fields: source, insights, context, and context_indicators. This template structure forces the LLM to acknowledge and reference the provided source material, creating a Retrieval-Augmented Generation (RAG) pattern without requiring manual prompt engineering at the call site.

Checkpointing and State Persistence

Both graphs leverage identical checkpointing mechanisms through SQLite (SqliteSaver) using the file path defined in LANGGRAPH_CHECKPOINT_FILE (configured in open_notebook/config.py). However, the source chat graph stores significantly richer checkpoint data due to its additional state fields. When resuming a thread, the source chat graph restores not just the message history, but the complete source object and insight cache, maintaining conversation continuity across sessions.

Model Provisioning

Both workflows utilize the provision_langchain_model helper from open_notebook/ai/provision.py to handle async-to-sync model initialization. The key difference lies in the payload construction: the general chat graph sends only the system prompt plus message history, while the source chat graph includes the enriched source-aware system prompt containing formatted document content.

Practical Implementation Examples

Invoking the General Chat Graph

Use this pattern for simple conversational interactions without external document context:

from open_notebook.graphs.chat import graph as chat_graph
from langgraph.graph import RunnableConfig
from langchain_core.messages import HumanMessage

# Simple state with a user message

state = {
    "messages": [HumanMessage(content="Hey, how are you?")],
    "model_override": None,
}

# Run the graph (no source context)

result = chat_graph.ainvoke(state, config=RunnableConfig())
print(result["messages"].content)   # → LLM reply

Invoking the Source Chat Graph

Use this pattern when you need document-grounded responses. The source_id must exist in SurrealDB:

from open_notebook.graphs.source_chat import source_chat_graph
from langgraph.graph import RunnableConfig
from langchain_core.messages import HumanMessage

# State must include a source identifier

state = {
    "messages": [HumanMessage(content="Summarize the key points.")],
    "source_id": "src_12345",      # Identifier of the source record in SurrealDB

    "model_override": None,
}

# Run the graph – it will automatically load the source and its insights

result = source_chat_graph.ainvoke(state, config=RunnableConfig())
print(result["messages"].content)          # LLM reply based on source

print(result["source"].title)              # Title of the source document

print([i.insight_type for i in result["insights"]])  # Types of insights used

The second example demonstrates how the source chat graph enriches the conversation with domain-specific data, returning the source, insights, context, and context_indicators alongside the messages for downstream UI rendering or further processing.

Summary

  • The general chat graph (open_notebook/graphs/chat.py) provides a minimal ThreadState implementation for stateless, free-form conversation using the chat/system prompt template.
  • The source chat graph (open_notebook/graphs/source_chat.py) extends this with SourceChatState, requiring a source_id and automatically fetching document content via ContextBuilder.
  • Both graphs share SQLite checkpointing and model provisioning infrastructure, but the source chat graph persists richer state including insights and metadata.
  • Template specialization drives the behavioral difference: source_chat/system expects source-specific variables while chat/system remains generic.
  • Choose the general graph for simple Q&A and the source graph when you need citations and reasoning grounded in specific documents stored in SurrealDB.

Frequently Asked Questions

When should I use the source chat graph versus the general chat graph?

Use the source chat graph when your application requires the LLM to answer questions about specific documents, PDFs, or web pages stored in SurrealDB. The automatic context injection ensures grounded, citeable responses. Use the general chat graph for open-ended conversations, brainstorming, or tasks that do not require reference to external source material, as it offers lower latency and simpler state management.

How does the ContextBuilder integrate with the database?

The ContextBuilder (in open_notebook/utils/context_builder.py) queries SurrealDB using the source_id provided in the state. It retrieves the raw source content, associated metadata, and any pre-computed SourceInsight objects, then formats them into a structured context string via _format_source_context. This eliminates the need for manual database queries in your application code.

Can I manually provide context to the general chat graph?

Yes. While the general chat graph lacks automatic context retrieval, you can populate the context field in ThreadState with custom strings before invocation. However, this requires manual formatting and does not provide the structured insight tracking or automatic source object management available in the dedicated source chat implementation.

What happens if the source_id is missing when invoking the source chat graph?

The SourceChatState requires source_id as a mandatory field. If omitted, the graph will fail during state validation before reaching the LLM call. Ensure your application validates the presence of a valid SurrealDB record identifier before invoking source_chat_graph.ainvoke(), as the graph depends on this ID to fetch the source content and insights.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →