# How Open Notebook Integrates the Esperanto Library with 18+ AI Providers (Including Ollama and LM Studio)

> Open Notebook integrates 18+ AI providers like Ollama and LM Studio via its ModelManager and Esperanto library, simplifying your AI workflows. Learn how.

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

---

**Open Notebook uses a centralized `ModelManager` class in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) to normalize provider configurations and route all AI requests through the Esperanto library's `AIFactory`, enabling seamless support for over 18 providers including local servers like Ollama and LM Studio.**

The `lfnovo/open-notebook` repository leverages the **Esperanto** library to abstract away provider-specific implementation details, allowing users to switch between cloud APIs and local LLM servers without code changes. This integration handles everything from credential management to model discovery, supporting diverse AI capabilities including chat completion, embeddings, speech-to-text, and text-to-speech across more than 18 different providers.

## Provider-Agnostic Architecture via ModelManager

The integration centers on the `ModelManager` class defined in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py). This manager reads model definitions from **SurrealDB** tables and constructs provider-specific configurations at runtime.

When loading a model record, the manager normalizes provider names to match Esperanto's expectations. The database stores provider names with underscores (e.g., `ollama_local` or `lm_studio`), while Esperanto requires hyphenated format. The code performs this transformation automatically:

```python
provider = model.provider.replace("_", "-")

```

This normalization occurs in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) (lines 47-49) before every factory call, ensuring compatibility across all 18+ supported providers.

## Credential Handling and Configuration

The system supports two configuration paths: stored credentials in SurrealDB or environment variable fallbacks.

### Stored Credentials

When a model record links to a credential, `Model.get_credential_obj()` loads the `Credential` record from [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py). The credential object converts to Esperanto configuration via `Credential.to_esperanto_config()`. For **Ollama-specific** deployments, this includes critical overrides like `num_ctx` (context window size), which is defined in the `Credential` class schema (line 80).

### Environment Fallback

If no credential exists, [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) falls back to environment variables (e.g., `OLLAMA_BASE_URL` or `OPENAI_API_KEY`) and injects them into the Esperanto configuration object. This allows immediate local testing without database setup.

## The Esperanto Factory Pattern

Depending on the model's `type` field, `ModelManager` invokes one of four Esperanto constructors from `AIFactory`:

- **`AIFactory.create_language`** for LLM chat completions
- **`AIFactory.create_embedding`** for vector store operations
- **`AIFactory.create_speech_to_text`** for transcription services
- **`AIFactory.create_text_to_speech`** for TTS generation

These calls automatically cache the underlying provider client, so subsequent requests reuse the same connection (see [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py), lines 50-57). The rest of the Open Notebook codebase interacts with generic `LanguageModel` or `EmbeddingModel` interfaces, remaining unaware of whether the backend is OpenAI, Anthropic, Ollama, or LM Studio.

## Local AI Support: Ollama and LM Studio

Open Notebook provides first-class support for local inference through specialized handling in [`open_notebook/ai/model_discovery.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/model_discovery.py) and URL validation.

### URL Validation for Local Endpoints

The API accepts localhost and private IP URLs for local providers. Unit tests in [`tests/test_url_validation.py`](https://github.com/lfnovo/open-notebook/blob/main/tests/test_url_validation.py) (lines 11-49) explicitly confirm that "localhost should be allowed for Ollama" and similar local services.

### Automatic Model Discovery

The [`model_discovery.py`](https://github.com/lfnovo/open-notebook/blob/main/model_discovery.py) module contacts running Ollama instances at `/api/tags` to populate the database with available models (lines 295-322). This auto-discovery eliminates manual configuration when local models are added or updated.

### Context Window Overrides

For Ollama deployments, the `Credential` schema includes a `num_ctx` field that merges into the Esperanto config (see [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), line 80). This allows users to request larger context windows than the default 8,000 tokens, critical for processing long documents.

## Practical Implementation Examples

### Instantiating Any Language Model

```python
from open_notebook.ai.models import ModelManager

async def get_chat_model():
    manager = ModelManager()
    # model_id refers to a SurrealDB record in the model table

    llm = await manager.get_model(
        model_id="model:my-ollama-gpt4",
        temperature=0.7,
    )
    # Returns an Esperanto LanguageModel instance

    response = await llm.generate("Explain the difference between Ollama and LM Studio.")
    return response

```

The `get_model` method internally calls `AIFactory.create_language` to instantiate the concrete client.

### Connecting to Local Ollama with Custom Context

```python
from open_notebook.ai.models import ModelManager

async def chat_with_local_llm():
    manager = ModelManager()
    # Discovers models from http://localhost:11434 automatically

    model = await manager.get_model(
        model_id="model:ollama:llama2",
        num_ctx=16384,  # Overrides default 8k context window

    )
    return await model.generate("Summarize this research paper.")

```

### Credential Configuration for Ollama

```python

# Example Credential record in SurrealDB

{
    "name": "Local Ollama",
    "provider": "ollama",
    "num_ctx": 16384,
    "config": {}
}

# When loaded, Credential.to_esperanto_config() produces:

# {"num_ctx": 16384, "base_url": "http://localhost:11434"}

```

This configuration merges with Esperanto's provider defaults before instantiation.

## Summary

- **Centralized abstraction**: The `ModelManager` class in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) provides a single interface for all AI operations across 18+ providers.
- **Name normalization**: Provider names are automatically converted from underscores to hyphens to match Esperanto's naming conventions.
- **Flexible credentialing**: Supports both database-stored credentials with provider-specific overrides (like `num_ctx` for Ollama) and environment variable fallbacks.
- **Local server support**: Built-in URL validation allows localhost/private IPs, while [`model_discovery.py`](https://github.com/lfnovo/open-notebook/blob/main/model_discovery.py) automatically detects available Ollama models via `/api/tags`.
- **Factory pattern**: Uses `AIFactory.create_language`, `create_embedding`, and related methods to generate cached, provider-specific clients without exposing implementation details to the application layer.

## Frequently Asked Questions

### How does Open Notebook normalize provider names for the Esperanto library?

Open Notebook stores provider names with underscores in SurrealDB (e.g., `ollama_local`), but Esperanto expects hyphenated names (e.g., `ollama-local`). The `ModelManager` performs this transformation automatically using `provider = model.provider.replace("_", "-")` before every `AIFactory` call in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py).

### What is the purpose of the `num_ctx` field in Ollama credentials?

The `num_ctx` field in the `Credential` schema allows users to override the default context window size for Ollama models. When `to_esperanto_config()` is called in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), this value merges into the configuration dictionary, enabling context windows larger than the standard 8,000 tokens for local LLM inference.

### How does model discovery work for local Ollama instances?

The [`open_notebook/ai/model_discovery.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/model_discovery.py) module queries the Ollama API endpoint `/api/tags` on `http://localhost:11434` to retrieve a list of available models (lines 295-322). It then automatically registers these models in the SurrealDB `model` table, making them immediately available through the `ModelManager` without manual configuration.

### Can I use environment variables instead of stored credentials for AI providers?

Yes. If a model record has no linked credential, [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) falls back to environment variables such as `OLLAMA_BASE_URL` or `OPENAI_API_KEY`. These variables are injected into the Esperanto configuration object, allowing immediate connections to providers without database credential records.