# How Open Notebook's Multi-Provider AI Architecture Works with Esperanto

> Explore Open Notebook's multi-provider AI architecture. Learn how it seamlessly integrates with Esperanto, utilizing encrypted credentials and dynamic key provisioning for effortless switching between major AI models.

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

---

**Open Notebook abstracts diverse AI providers through the Esperanto library using encrypted credential storage, dynamic key provisioning, and cached model instantiation to enable seamless switching between OpenAI, Anthropic, Google Vertex, and other providers.**

The Open Notebook multi-provider AI architecture provides a unified interface for integrating with dozens of large language model and embedding providers. By leveraging the Esperanto library as an abstraction layer, the system securely manages provider credentials in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py) while normalizing configuration patterns across disparate APIs. This architecture enables users to configure multiple AI providers simultaneously, route specific workflows to optimal models, and maintain enterprise-grade security for API secrets.

## Core Components of the Architecture

The architecture centers on three primary components that bridge database records with Esperanto's factory methods.

### Encrypted Credential Storage

In [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), the `Credential` model handles the secure persistence of provider secrets. The model implements `encrypt_value` and `decrypt_value` methods to ensure API keys are encrypted at rest. When the system needs to instantiate a model, the `to_esperanto_config()` method exports a dictionary containing `api_key`, `base_url`, and provider-specific parameters (such as Google Vertex project IDs) compatible with Esperanto's `AIFactory`.

### Dynamic Key Provisioning

The `provision_provider_keys()` function in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) loads credentials from the database and injects them into environment variables that Esperanto expects. If no credential exists for a provider, the function gracefully falls back to existing environment variables, preserving backward compatibility with standard configuration methods. This DB-first approach allows individual models to point to distinct credentials while maintaining a clean fallback chain.

### Model Resolution and Caching

The `ModelManager` class in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) orchestrates the instantiation process. When `get_model()` is called, it fetches the `Model` record (defined in the same file), resolves the linked `Credential` (if any), and normalizes the provider name before calling the appropriate `AIFactory.create_*` method. Esperanto caches the instantiated model internally, making subsequent inference calls cheap without requiring additional API client initialization.

## Provider Name Normalization

Database entries use underscores for provider names (e.g., `openai_compatible`), but Esperanto expects hyphenated names. The `ModelManager` bridges this gap by applying `provider.replace("_", "-")` before passing the provider name to `AIFactory`. This normalization keeps the database schema stable while satisfying Esperanto's naming conventions, allowing seamless integration as new providers are added to the `PROVIDER_CONFIG` mapping.

## Request Flow from Client to Inference

When a user triggers an AI operation, the system follows a strict resolution chain:

1. The client request hits an API endpoint that delegates to `ModelsService`.

2. `ModelsService` calls `ModelManager.get_model()` in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py).

3. `ModelManager` retrieves the `Model` record, including the provider type and optional credential ID.

4. If a credential exists, `Credential.get_credential_obj()` loads the encrypted record from [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py).

5. `Credential.to_esperanto_config()` generates the configuration dictionary.

6. `provision_provider_keys()` in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) loads values into environment variables or uses existing env-vars as fallback.

7. Finally, `AIFactory.create_language(name, provider, config)` returns a cached model instance ready for inference.

## Implementation Examples

### Provisioning Credentials and Retrieving Default Models

To load provider credentials from the database and retrieve the default chat model:

```python
from open_notebook.ai.key_provider import provision_provider_keys
from open_notebook.ai.models import model_manager

# Load OpenAI credentials from DB (or keep existing env vars)

await provision_provider_keys("openai")

# Retrieve the default chat model (configured in the DB)

chat_model = await model_manager.get_default_model("chat")

# `chat_model` is an Esperanto LanguageModel ready for inference

```

### Creating Specific Model Instances with Custom Parameters

To instantiate a specific embedding model with additional parameters:

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

# Assume a Model record with id="model:123" points to an Anthropic credential

embedding = await model_manager.get_model(
    model_id="model:123",
    # optional extra kwargs – e.g., temperature

    temperature=0.2,
)

# Use the embedding in a downstream workflow

vectors = await embedding.embed_documents(["Open Notebook is awesome!"])

```

### Direct Credential Access for Administration

To access and inspect credentials directly:

```python
from open_notebook.domain.credential import Credential

cred = await Credential.get_by_provider("google")
if cred:
    print("Google Vertex project:", cred.project)
    # The returned dict can be passed to Esperanto if needed

    config = cred.to_esperanto_config()

```

## Summary

- **Secure Credential Management**: API keys are encrypted at rest using the `Credential` model in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py) and only decrypted during model instantiation.
- **Flexible Provider Configuration**: The `provision_provider_keys()` function supports both database-stored credentials and environment variable fallbacks for maximum deployment flexibility.
- **Automatic Model Caching**: `ModelManager` leverages Esperanto's internal caching, ensuring that repeated calls to `get_model()` return the same instance without re-initialization overhead.
- **Provider Name Bridging**: Underscore-to-hyphen normalization (`provider.replace("_", "-")`) ensures database schema compatibility with Esperanto's naming conventions.
- **Plug-and-Play Extensibility**: Adding new providers requires only a `PROVIDER_CONFIG` entry and a corresponding credential form, enabling rapid integration of emerging AI services.

## Frequently Asked Questions

### How does Open Notebook secure API keys?

According to the source code in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), the `Credential` model encrypts API keys at rest using `encrypt_value` and decrypts them only when needed via `decrypt_value`. The keys are never exposed in plain text outside the provisioning flow, and the `to_esperanto_config()` method securely packages them for Esperanto's consumption.

### What happens if no credential is stored in the database?

The `provision_provider_keys()` function in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) implements a graceful fallback mechanism. If no database credential exists for a provider, the system retains existing environment variables, allowing the application to operate with standard env-var configuration while still supporting database-driven setups for other providers.

### How does the architecture handle different naming conventions between the database and Esperanto?

The `ModelManager` class normalizes provider names by replacing underscores with hyphens using `provider.replace("_", "-")` before passing them to `AIFactory`. This allows the database to use snake_case naming (e.g., `openai_compatible`) while Esperanto receives the expected hyphenated format (e.g., `openai-compatible`).

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

Yes. The Open Notebook multi-provider AI architecture supports runtime flexibility where individual `Model` records can point to distinct credentials. This enables per-account or per-project authentication, allowing one instance to simultaneously use OpenAI for chat, Anthropic for analysis, and Google Vertex for embeddings, each with separate API keys.