# How Embedding Generation Works Across Different AI Providers in Open Notebook

> Learn how embedding generation works across AI providers using Open Notebook. Discover how the Esperanto library unifies models for efficient text processing and chunking.

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

---

**Open Notebook unifies embedding generation across multiple AI providers by using the Esperanto library's `EmbeddingModel` abstraction, which handles provider-specific initialization while the application layer manages batching, retries, and intelligent chunking for long texts.**

Open Notebook implements a provider-agnostic embedding pipeline that abstracts the differences between AI providers like OpenAI, Anthropic, and Groq. By leveraging the Esperanto library's unified interface, the system generates embeddings through a single API that automatically handles credentials, batching, and text chunking. This architecture ensures that **embedding generation across different AI providers** remains consistent regardless of which backend model serves the request.

## The Unified Embedding Architecture

The embedding system bridges provider-specific implementations through a two-layer abstraction that separates model management from provider details.

### Model Selection and Configuration

When the system requires embeddings, `ModelManager` retrieves the configured default embedding model from the database. The model record specifies the provider name (e.g., `openai`, `anthropic`, `groq`) and an optional credential reference. If credentials are attached, they are loaded and passed to Esperanto; otherwise, the system falls back to environment variables.

In [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py), the `get_embedding_model` method handles this lookup (lines [209-214](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py#L209-L214)), referencing the `default_embedding_model` field defined at line [70](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py#L70).

### Provider Abstraction via Esperanto

`ModelManager.get_model` constructs an `EmbeddingModel` instance through `AIFactory.create_embedding`. Esperanto initializes the concrete provider client—whether OpenAI's Embedding API, Anthropic's implementation, or others—based on the supplied configuration. The resulting object exposes a uniform `aembed` coroutine that works identically across all providers.

This factory invocation appears in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) at lines [158-162](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py#L158-L162).

## Text Processing and Batch Operations

Once the provider-specific model is instantiated, the system handles practical constraints like payload limits and transient failures through robust batching logic.

### Batching and Retry Mechanisms

The `generate_embeddings` function in [`open_notebook/utils/embedding.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py) manages the embedding workflow for text lists. It partitions inputs into batches sized according to `EMBEDDING_BATCH_SIZE` (defaulting to 50, configurable via `OPEN_NOTEBOOK_EMBEDDING_BATCH_SIZE`), then invokes the provider's `aembed` method for each batch.

Transient failures trigger an automatic retry loop using `EMBEDDING_MAX_RETRIES` with exponential backoff. The batching logic resides at lines [111-124](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py#L111-L124), while the retry implementation appears at lines [79-99](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py#L79-L99).

## Handling Long Documents with Chunking

Provider token limits require special handling for documents exceeding `CHUNK_SIZE` thresholds.

### Chunking and Mean-Pooling

For individual texts that exceed token limits, `generate_embedding` splits content using `chunk_text` from the chunking utilities, embeds each chunk via `generate_embeddings`, then combines vectors using **mean pooling**. The `mean_pool_embeddings` function averages chunk embeddings and normalizes the result to unit length, preserving semantic coherence across the full document.

This logic appears in [`open_notebook/utils/embedding.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py) at `generate_embedding` (lines [209-226](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py#L209-L226)) and `mean_pool_embeddings` (lines [55-73](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py#L55-L73)).

## Practical Implementation Examples

The following examples demonstrate how to interact with the embedding pipeline in application code:

```python

# Embed a single short text

from open_notebook.utils.embedding import generate_embedding

embedding = await generate_embedding(
    "The quick brown fox jumps over the lazy dog"
)

# Returns a list[float] from the configured default provider

```

```python

# Batch process multiple documents

from open_notebook.utils.embedding import generate_embeddings

texts = ["First document content", "Second document content", "Third document content"]
embeddings = await generate_embeddings(texts)

# Automatically batches requests and retries on failure

```

```python

# Handle long documents with automatic chunking

from open_notebook.utils.embedding import generate_embedding

long_article = """<text exceeding CHUNK_SIZE tokens>"""
vector = await generate_embedding(long_article)

# Internally: chunk → embed batches → mean pool → normalize

```

```python

# Direct provider model access for custom configurations

from open_notebook.ai.models import model_manager

# Use a specific model by ID

embedding_model = await model_manager.get_model("open_notebook:model:my_openai_ada")
vectors = await embedding_model.aembed(["Custom text input"])

```

## Key Source Files

Understanding the embedding architecture requires familiarity with these components:

- **[`open_notebook/utils/embedding.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/embedding.py)** – Core embedding utilities including `generate_embedding`, `generate_embeddings`, and `mean_pool_embeddings`.
- **[`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py)** – Model management, credential handling, and Esperanto `EmbeddingModel` instantiation via `AIFactory`.
- **[`open_notebook/utils/chunking.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/chunking.py)** – Text splitting logic for handling provider token limits.
- **[`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py)** – Credential storage and conversion to Esperanto configuration format.
- **[`open_notebook/ai/provision.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/provision.py)** – Model selection logic based on context requirements.

## Summary

- **Open Notebook** uses the **Esperanto library** to abstract provider-specific embedding implementations behind a unified `EmbeddingModel` interface.
- **ModelManager** handles provider selection and credential injection, supporting multiple AI providers through configuration rather than code changes.
- **Batching and retry logic** in `generate_embeddings` ensures reliable processing of large text collections while respecting rate limits.
- **Automatic chunking and mean-pooling** allow the system to embed documents of arbitrary length by splitting, embedding, and averaging chunk vectors.
- The architecture separates **provider concerns** (handled by Esperanto) from **application concerns** (batching, chunking, pooling) managed in the utility layer.

## Frequently Asked Questions

### How does Open Notebook handle different embedding model providers?

The system uses Esperanto's `EmbeddingModel` abstraction to normalize provider differences. When `ModelManager` retrieves a model configuration, it passes provider names and credentials to `AIFactory.create_embedding`, which instantiates the appropriate client. This allows the same `aembed` interface to work whether the backend is OpenAI, Anthropic, or Groq.

### What happens if a text exceeds the model's token limit?

The `generate_embedding` function automatically detects oversized content and routes it through `chunk_text` to split the document into provider-friendly segments. After embedding each chunk, `mean_pool_embeddings` averages the vectors and normalizes the result, producing a single embedding that represents the entire document.

### How are API credentials managed for different providers?

Credentials are stored as `Credential` domain objects and optionally attached to model configurations. When initializing an embedding model, the system calls `Credential.to_esperanto_config()` to format secrets for the Esperanto library. If no credential is specified, the application falls back to standard environment variables expected by each provider.

### Can I use multiple embedding providers in the same Open Notebook instance?

Yes. The `ModelManager` supports multiple concurrent model configurations. You can retrieve specific models by ID using `model_manager.get_model()` and pass them directly to embedding functions, or switch the default embedding model in settings to change the global provider used by automated pipelines.