How Open-Notebook's Multi-Provider Credential System Integrates with Esperanto
Open-Notebook's multi-provider credential system stores encrypted API keys per provider and converts them to Esperanto-compatible configuration dictionaries via Credential.to_esperanto_config(), eliminating the need for environment variables while maintaining backward compatibility through a fallback mechanism.
The lfnovo/open-notebook repository implements a database-first approach to managing AI provider credentials, storing one encrypted record per provider instead of relying on global environment variables. This multi-provider credential system seamlessly integrates with Esperanto—the library's AI abstraction layer—by transforming stored database records into provider-specific configuration dictionaries that Esperanto's AIFactory can consume directly.
Architecture of the Multi-Provider Credential System
Database-First Design with Per-Provider Storage
Instead of a single global configuration, Open-Notebook stores one Credential record per AI provider in SurrealDB. Each record contains the raw API key encrypted at rest, alongside provider-specific settings such as base_url, endpoint, and project. This design allows multiple configurations for the same provider (e.g., separate OpenAI accounts for different projects) while maintaining strict isolation between credentials.
Encryption and the Credential Model
The Credential class in open_notebook/domain/credential.py handles encryption and decryption using a symmetric key stored in the application configuration. When a model request occurs, the system calls Credential.get() to retrieve the database row and decrypts the api_key field before converting it to Esperanto format.
The Esperanto Integration Flow
Step 1: Model Resolution via ModelManager
When code requests a model, ModelManager.get_model() in open_notebook/ai/models.py loads the requested Model record from the database. If the model has a credential field, the manager calls Model.get_credential_obj() to initiate the credential retrieval process. This method returns a fully instantiated Credential object ready for configuration conversion.
Step 2: Credential Decryption and Retrieval
The Credential.get() method fetches the record from SurrealDB using the stored credential ID and decrypts the API key using the application's encryption key. This decrypted credential object contains all provider-specific configuration values needed to instantiate the AI client, but in Open-Notebook's internal format.
Step 3: Config Transformation via to_esperanto_config()
The critical integration point occurs in Credential.to_esperanto_config() (lines 102-138 of open_notebook/domain/credential.py). This method builds a plain dictionary matching Esperanto's expected config format, mapping internal fields like api_key, base_url, and endpoint to the exact keys Esperanto requires. The method ensures Esperanto receives a fully-populated configuration dictionary and therefore does not need to read environment variables for that model.
Step 4: Provider Instantiation via AIFactory
ModelManager merges the Esperanto configuration with any additional kwargs, normalizes the provider name (e.g., converting openai-compatible to openai-compatible), and calls the appropriate AIFactory.create_* method—such as create_language(), create_embedding(), or create_speech_to_text(). Esperanto instantiates the concrete provider model using the supplied configuration, caching it internally for subsequent requests.
Environment Variable Fallback for Backward Compatibility
When a model lacks a linked credential, Open-Notebook falls back to the historic environment-variable behavior. The system calls provision_provider_keys(provider) from open_notebook/ai/key_provider.py, which reads the first Credential record for that provider (if any) and writes the relevant values into os.environ. This ensures that providers like Azure or Vertex—which Esperanto may still expect to read from the environment—continue to function while the database remains the source of truth.
Practical Implementation Examples
Creating a Credential via API
Create a new credential record through the REST API:
import httpx
payload = {
"name": "OpenAI-Prod",
"provider": "openai",
"modalities": ["language", "embedding"],
"api_key": "sk-xxxxxxxxxxxxxxxxxxxx"
}
resp = httpx.post("http://localhost:5055/api/credentials", json=payload)
print(resp.json())
The API stores the key encrypted at rest in the credential table.
Linking a Model to a Credential
Associate a credential with a specific model to enable the Esperanto integration:
POST /api/models
{
"name": "gpt-4o",
"provider": "openai",
"type": "language",
"credential": "<credential-record-id>"
}
Using Models in Application Code
Access models through ModelManager, which automatically handles the credential-to-Esperanto conversion:
from open_notebook.ai.models import model_manager
# Returns an Esperanto LanguageModel ready for LangChain usage
chat_model = await model_manager.get_default_model("chat")
response = await chat_model.ainvoke(
{"messages": [{"role": "user", "content": "Hello"}]}
)
print(response)
Behind the scenes, this triggers: get_model() → get_credential_obj() → Credential.get() → to_esperanto_config() → AIFactory.create_language().
Manual Environment Provisioning
For legacy compatibility, manually provision environment variables when no credential is linked:
from open_notebook.ai.key_provider import provision_provider_keys
# Sets AZURE_OPENAI_* env vars from the first matching credential in DB
await provision_provider_keys("azure")
Summary
- Open-Notebook's multi-provider credential system stores one encrypted Credential record per AI provider in SurrealDB, replacing global environment variable configurations.
- The
Credential.to_esperanto_config()method inopen_notebook/domain/credential.pytransforms database records into Esperanto-compatible configuration dictionaries, allowing direct instantiation viaAIFactory. - ModelManager in
open_notebook/ai/models.pyorchestrates the flow: loading models, retrieving credentials, and converting them to Esperanto format before calling the appropriate factory method. - If no credential is linked to a model, the system falls back to
provision_provider_keys()inopen_notebook/ai/key_provider.py, which copies credential values to environment variables for backward compatibility. - This architecture enables multiple concurrent configurations per provider while maintaining seamless integration with the Esperanto abstraction layer.
Frequently Asked Questions
How does Open-Notebook encrypt API keys in the multi-provider credential system?
API keys are encrypted at rest using symmetric encryption implemented in the Credential class within open_notebook/domain/credential.py. When storing a new credential, the system encrypts the raw api_key field before persisting to SurrealDB. During retrieval, Credential.get() automatically decrypts the key before converting it to Esperanto configuration format via to_esperanto_config().
What happens if a model has no linked credential in Open-Notebook?
When ModelManager encounters a model without a linked credential, it invokes provision_provider_keys() from open_notebook/ai/key_provider.py. This function retrieves the first available credential for that provider from the database and writes the values into os.environ. Esperanto then reads these environment variables to instantiate the provider, maintaining backward compatibility with legacy code paths that expect environment-based configuration.
How does the Credential.to_esperanto_config() method work?
The to_esperanto_config() method (lines 102-138 of open_notebook/domain/credential.py) constructs a plain Python dictionary containing exactly the keys Esperanto expects: api_key, base_url, endpoint, and provider-specific settings. This configuration dictionary is passed directly to Esperanto's AIFactory.create_* methods, eliminating the need for Esperanto to access environment variables or external configuration files for that specific model instance.
Can I use multiple credentials for the same provider in Open-Notebook?
Yes. Because the system stores one Credential record per configuration rather than one per provider type, you can create multiple credentials for the same provider (e.g., "OpenAI-Prod" and "OpenAI-Dev"). Each model references a specific credential record via its credential field, allowing different models to use different API keys or endpoints while sharing the same provider type. The ModelManager resolves the correct credential for each model request independently.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →