How ModelManager Integrates with Provider-Specific Credential Configuration in Open Notebook
ModelManager acts as a central factory that transforms database Model records into fully configured Esperanto AI clients by dynamically resolving provider-specific settings from linked Credential objects, with automatic fallback to environment variables when credentials are absent.
Managing API keys, base URLs, and provider-specific parameters across multiple AI services can become unwieldy in production applications. In the lfnovo/open-notebook repository, the ModelManager class eliminates hard-coded secrets by seamlessly bridging the Model and Credential domain models. This integration allows the platform to instantiate ready-to-use AI clients for OpenAI, Anthropic, Azure, Google Vertex, Ollama, and other providers while keeping sensitive configuration in the database.
The ModelManager Architecture
The ModelManager implementation lives in [open_notebook/ai/models.py](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py#L98) and serves as the primary interface between SurrealDB records and the Esperanto AI library. Rather than storing connection details in code, the manager retrieves a Model record by its SurrealDB ID, validates its type, and constructs the appropriate Esperanto client using provider-specific configuration extracted from an associated Credential object.
This architecture ensures that authentication details, endpoint URLs, and provider-specific parameters remain decoupled from application logic, enabling secure multi-tenant deployments where different models may use different API keys or cloud regions.
Provider-Specific Credential Resolution Process
The integration follows a deterministic sequence to ensure every model receives the correct configuration for its provider.
Loading the Model Record
The process begins when ModelManager.get_model() fetches a Model record using await Model.get(model_id). The manager immediately validates that the model's type column matches one of the four supported categories: language, embedding, speech-to-text, or text-to-speech. This validation ensures that the subsequent factory method receives the correct parameters for constructing the appropriate AI client.
Resolving Credential Configuration
When a model includes a credential field, the manager loads the associated Credential object via await model.get_credential_obj(). According to the implementation in [open_notebook/domain/credential.py](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py#L72), the Credential class stores provider-specific fields such as api_key, base_url, api_version, endpoint, and cloud-specific parameters like project or location.
The credential's to_esperanto_config() method transforms these fields into a plain dictionary that Esperanto recognizes. This method extracts every provider-specific setting, ensuring that Azure OpenAI deployments receive their required base_url and api_version, while Ollama instances receive the correct num_ctx or local endpoint configuration.
Fallback to Environment Variables
If a model lacks a linked credential or the credential cannot be loaded, ModelManager invokes provision_provider_keys(provider) to populate the configuration dictionary from environment variables. This fallback mechanism ensures that models remain functional even when database credentials are temporarily unavailable, reading standard environment variables like OPENAI_API_KEY or ANTHROPIC_API_KEY based on the provider name.
Normalizing Provider Names
Database records store provider names using underscores (e.g., openai_compatible or azure_openai), but Esperanto expects hyphenated formats (openai-compatible, azure-openai). The manager automatically performs this conversion using provider.replace("_", "-") before passing the value to the factory, ensuring compatibility without requiring manual string manipulation in the database.
Creating the Concrete Esperanto Model
Finally, the manager merges any caller-provided runtime arguments—such as temperature or max_tokens—into the configuration dictionary. Based on the model's type, it invokes the appropriate AIFactory.create_* method (e.g., create_language_model(), create_embedding_model()), passing the model name, normalized provider string, and the merged config. Esperanto internally caches these instantiated clients, allowing subsequent calls to reuse existing connections.
Practical Implementation Example
The following workflow demonstrates how to create a credential with Azure-specific settings, link it to a model, and retrieve a configured client via ModelManager.
First, create a credential with provider-specific fields:
from open_notebook.domain.credential import Credential
from pydantic import SecretStr
cred = Credential(
name="My Azure OpenAI",
provider="azure",
modalities=["language", "embedding"],
api_key=SecretStr("my-azure-key"),
base_url="https://my-resource.openai.azure.com/",
api_version="2023-05-15",
endpoint="deployments",
)
await cred.save()
Next, create a model that references this credential:
from open_notebook.ai.models import Model
model = Model(
name="gpt-4o-mini",
provider="azure",
type="language",
credential=cred.id,
)
await model.save()
Retrieve the ready-to-use Esperanto model through the manager:
from open_notebook.ai.models import model_manager
language_model = await model_manager.get_model(model.id, temperature=0.7)
# Returns an esperanto.LanguageModel configured with Azure endpoint and API key
Use the model within a LangGraph workflow or similar pipeline:
from open_notebook.ai.models import ModelManager
async def embed_source(text: str):
manager = ModelManager()
embedding_model = await manager.get_embedding_model()
vector = await embedding_model.embed(text)
return vector
Summary
- ModelManager in
open_notebook/ai/models.pyserves as the central factory for converting database records into functional AI clients. - Credential resolution occurs via
to_esperanto_config(), which extracts provider-specific settings likebase_url,api_version, and cloud project details from theCredentialmodel. - Environment fallback activates automatically when credentials are missing, using
provision_provider_keys()to read standard API keys from environment variables. - Provider name normalization converts underscore-separated database values to hyphenated formats expected by Esperanto.
- Runtime parameter merging allows caller-specified arguments like
temperatureto override or extend stored configuration.
Frequently Asked Questions
What happens if a Model does not have a linked Credential?
When ModelManager encounters a model without a credential field, it automatically calls provision_provider_keys(provider) to populate the configuration from environment variables. This ensures the model remains functional using standard env-var naming conventions (e.g., OPENAI_API_KEY) even when no explicit credential record exists in the database.
How does ModelManager handle different provider naming conventions?
The manager normalizes provider names by replacing underscores with hyphens using provider.replace("_", "-") before passing the value to Esperanto. This bridges the gap between database storage conventions (e.g., openai_compatible) and the hyphenated format required by the Esperanto AI library (e.g., openai-compatible).
Can runtime parameters override credential settings?
Yes. Any additional keyword arguments passed to ModelManager.get_model()—such as temperature, max_tokens, or top_p—are merged into the configuration dictionary after the credential settings are applied. This allows dynamic adjustment of model behavior without modifying stored credentials.
Where is the Credential model defined?
The Credential domain model and its to_esperanto_config() method are defined in [open_notebook/domain/credential.py](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py#L72). This file contains the Pydantic model that stores API keys as SecretStr and translates provider-specific fields into the flat dictionary format required by Esperanto.
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 →