# How Note Creation Links to Sources and Insights in Open Notebook

> Learn how note creation links to sources and insights in Open Notebook. Discover how artifact edges and context builders surface relevant material for LLM workflows.

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

---

**In Open Notebook, note creation does not directly bind sources; instead, a note is attached to a notebook via an artifact edge, and downstream context builders traverse the notebook‑source‑insight graph to surface relevant material for LLM workflows.**

Understanding how note creation links to sources and insights is essential for building effective retrieval workflows in Open Notebook. According to the `lfnovo/open-notebook` source code, the relationship is graph-based and indirect: a **Note** is persisted and optionally linked to a **Notebook**, which aggregates **Source** records and their **SourceInsight** children. When a user later queries or chats with the note, the **ContextBuilder** utility walks this relationship chain to assemble the full prompt context.

## How Note Creation Works in Open Notebook

When a client POSTs a new note to `/api/notes`, the FastAPI handler defined in [`api/routers/notes.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/notes.py) instantiates a `Note` domain object from [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py) and stores it. The process splits into persistence, embedding, and optional notebook attachment.

### Persisting and Embedding the Note

The handler awaits **`Note.save()`**, which writes the note record to **SurrealDB** and asynchronously fires an `embed_note` command so the note receives a vector embedding. In [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py) at lines 567‑580, `Note.save()` handles both persistence and embedding, ensuring the note is searchable before any notebook link is created.

### Attaching a Note to a Notebook

If the JSON payload includes a `notebook_id`, the handler calls **`Note.add_to_notebook()`**. This method creates an `artifact` edge that links the note record to the specified notebook. As implemented in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py) at lines 595‑600, this edge is the only structural bond between the note and the notebook.

A typical POST payload and handler flow looks like this:

```json
{
  "title": "My summary",
  "content": "Lorem ipsum …",
  "note_type": "human",
  "notebook_id": "nb-12345"
}

```

```python

# Simplified excerpt from api/routers/notes.py

new_note = Note(title=title, content=note_data.content, note_type=note_type)
command_id = await new_note.save()               # persists + embeds

if note_data.notebook_id:
    await new_note.add_to_notebook(note_data.notebook_id)   # artifact edge

```

## How Sources and Insights Connect to Notebooks

A **Notebook** acts as the aggregation layer between notes and raw material. It collects many **Source** records via a `has` edge queried through methods like `Notebook.get_sources()`. Each **Source** can own zero or more **SourceInsight** records, which are created and retrieved through `Source`-level methods in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py).

### Adding and Retrieving Source Insights

To attach an insight to a source, the codebase uses **`Source.add_insight()`** at lines 459‑497 in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py). This method inserts a new `source_insight` record into SurrealDB and queues an `embed_insight` command for vectorization. Retrieval happens through **`Source.get_insights()`** at lines 392‑404, which returns all insight records tied to that source.

```python
source = await Source.get(source_id)
insight_id = await source.add_insight(
    insight_type="summary",
    content="Key take‑aways from the PDF"
)

```

## Traversing the Graph to Link Notes to Insights

Because a note is only directly tied to a notebook, the path to sources and insights is traversed at query time rather than at creation time. The graph follows this pattern:

```text
note ──artifact──> notebook ──has──> source ──has──> source_insight

```

The **ContextBuilder** utility in [`open_notebook/utils/context_builder.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/context_builder.py) materializes this traversal. At lines 182‑190, it optionally pulls in insights using `include_insights=True` by default when assembling prompts for chat, ask, or transformation graphs. Therefore, note creation links to sources and insights through graph traversal, not explicit foreign keys.

```python
builder = ContextBuilder(
    source_ids=[source.id],
    include_insights=True,
    max_tokens=2000,
)
context = await builder.build()

# context now contains note text, source text, and any source_insight content.

```

## Summary

- **`Note.save()`** persists a note to SurrealDB and triggers an `embed_note` command in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py).
- **`Note.add_to_notebook()`** creates an `artifact` edge linking the note to a notebook.
- **Notebooks aggregate sources** via a `has` edge, and sources aggregate insights via `Source.add_insight()` and `Source.get_insights()`.
- **`ContextBuilder`** traverses `note → notebook → source → source_insight` at query time, with `include_insights=True` by default.
- The relationship is indirect: note creation links to sources and insights through the notebook graph, not through direct source references in the note record.

## Frequently Asked Questions

### Does a note store direct references to its sources?

No. As implemented in `lfnovo/open-notebook`, a note does not store direct source identifiers. Instead, it links to a notebook via an `artifact` edge, and the notebook aggregates the sources. The `ContextBuilder` traverses this relationship when assembling context.

### When are source insights pulled into a note’s context?

Insights are retrieved at query time by the `ContextBuilder` in [`open_notebook/utils/context_builder.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/context_builder.py). When `include_insights=True` (the default), the builder walks the graph from note to notebook to source to source_insight and includes that content in the assembled prompt.

### What happens if a note is created without a notebook_id?

If the POST payload to `/api/notes` omits `notebook_id`, the note is persisted and embedded but no `artifact` edge is created. Without that edge, standard graph traversals in `ContextBuilder` cannot discover related sources or insights for that note.

### Which method is responsible for vectorizing a new insight?

`Source.add_insight()` in [`open_notebook/domain/notebook.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/notebook.py) inserts the record and queues an `embed_insight` command. This ensures the insight is vectorized and available for semantic retrieval alongside the source and note content.