How to Add a New AI Provider to the Esperanto-Based System in Open Notebook
Adding a new AI provider requires registering environment variables in key_provider.py, implementing a discovery coroutine in model_discovery.py, and optionally extending the Credential class for complex authentication scenarios.
Open Notebook uses the Esperanto library as its unified front-end for all AI providers. When you add a new AI provider to this Esperanto-based system, you integrate three core touchpoints: credential provisioning, model discovery, and type classification. These modifications enable the system to automatically discover models, classify them by capability (language, embedding, STT, TTS), and inject secure credentials at runtime.
Step 1: Register the Provider's Environment Variables
Start by mapping your provider name to the environment variables that Esperanto expects. In open_notebook/ai/key_provider.py, add an entry to the PROVIDER_CONFIG dictionary:
# open_notebook/ai/key_provider.py
PROVIDER_CONFIG = {
# Existing entries …
"myprovider": {
"env_var": "MYPROVIDER_API_KEY",
# For URL-based providers, add a second entry:
# "base_url_var": "MYPROVIDER_API_BASE",
},
}
The provision_provider_keys() function looks up PROVIDER_CONFIG to copy database-stored credentials into the process environment. This ensures that when a model of provider myprovider is requested, Esperanto can fetch its configuration from the environment or from a Credential record.
Step 2: Implement Model Discovery Logic
If your provider publishes an API that lists available models, implement an async discovery coroutine in open_notebook/ai/model_discovery.py. This function fetches model metadata and returns standardized DiscoveredModel objects:
# open_notebook/ai/model_discovery.py
async def discover_myprovider_models() -> List[DiscoveredModel]:
"""Fetch the list of models from MyProvider's /v1/models endpoint."""
api_key = os.getenv("MYPROVIDER_API_KEY")
base_url = os.getenv("MYPROVIDER_API_BASE", "https://api.myprovider.com")
if not api_key:
return []
models: List[DiscoveredModel] = []
try:
async with httpx.AsyncClient() as client:
resp = await client.get(
f"{base_url}/v1/models",
headers={"Authorization": f"Bearer {api_key}"},
timeout=30.0,
)
resp.raise_for_status()
for m in resp.json().get("models", []):
name = m.get("id")
if name:
model_type = classify_model_type(name, "myprovider")
models.append(
DiscoveredModel(
name=name,
provider="myprovider",
model_type=model_type,
)
)
except Exception as exc:
logger.warning(f"Failed to discover MyProvider models: {exc}")
return models
After defining the coroutine, register it in the PROVIDER_DISCOVERY_FUNCTIONS dictionary at the bottom of the same file:
# open_notebook/ai/model_discovery.py
PROVIDER_DISCOVERY_FUNCTIONS["myprovider"] = discover_myprovider_models
The sync_all_providers() function iterates over PROVIDER_DISCOVERY_FUNCTIONS to automatically populate the model table, making new models selectable in the UI without manual database inserts.
Step 3: Update Provider Type Classification
Teach the classify_model_type helper how to recognize model capabilities for your new provider. If your provider uses predictable naming conventions (e.g., "embed-" for embeddings), extend the type mappings:
# open_notebook/ai/model_discovery.py
MYPROVIDER_MODEL_TYPES = {
"language": ["gpt", "chat", "llm"],
"embedding": ["embed"],
"speech_to_text": ["stt"],
"text_to_speech": ["tts"],
}
# Add to the type_mappings dict inside classify_model_type
type_mappings["myprovider"] = MYPROVIDER_MODEL_TYPES
Accurate classification guarantees that ModelManager.get_default_model() returns the correct model subtype—LanguageModel, EmbeddingModel, SpeechToTextModel, or TextToSpeechModel—which determines what methods are available on the instance.
Step 4: Handle Complex Authentication (Optional)
If your provider requires more than a simple API key (e.g., Azure endpoints, Vertex service accounts, or project IDs), extend the Credential domain model in open_notebook/domain/credential.py:
# open_notebook/domain/credential.py
class Credential(ObjectModel):
# Existing fields…
myprovider_project: Optional[str] = None
myprovider_region: Optional[str] = None
def to_esperanto_config(self) -> Dict[str, Any]:
cfg = super().to_esperanto_config()
if self.myprovider_project:
cfg["project"] = self.myprovider_project
if self.myprovider_region:
cfg["region"] = self.myprovider_region
return cfg
The to_esperanto_config() method builds the configuration dictionary that Esperanto receives when instantiating a model. This keeps sensitive credentials out of the codebase while ensuring they are available at runtime.
End-to-End Integration Flow
Once you have added a new AI provider to the Esperanto-based system, the integration follows this execution path:
- Credential Provisioning: When a workflow requests a model,
ModelManager.get_model()loads theModelrecord and any linkedCredential, then callsprovision_provider_keys()to inject environment variables. - Model Instantiation: The manager calls
AIFactory.create_*()with the provider name and the config dict fromto_esperanto_config(). - Discovery: Running
await sync_provider_models("myprovider")(or clicking "Sync Models" in the UI) executes your discovery coroutine, insertsModelrows into SurrealDB viaModel.save(), and classifies each model by type. - UI Exposure: The
/api/routers/modelsendpoint serves these records to the frontend, populating Settings → Models automatically.
Using the New Model in Workflows
from open_notebook.ai.models import model_manager
# Retrieve the default language model for MyProvider
my_model = await model_manager.get_model(
model_id="open_notebook:myprovider:gpt-4",
temperature=0.7,
)
# Generate text
response = await my_model.generate("Explain quantum computing")
Summary
- Register environment variables in
open_notebook/ai/key_provider.pyby adding toPROVIDER_CONFIGsoprovision_provider_keys()can inject credentials. - Implement discovery logic in
open_notebook/ai/model_discovery.pyby creating an async function and registering it inPROVIDER_DISCOVERY_FUNCTIONS. - Update type classification in
classify_model_type()to map model names to language, embedding, STT, or TTS capabilities. - Extend credentials in
open_notebook/domain/credential.pyfor non-standard auth flows, overridingto_esperanto_config()to expose custom fields. - No UI changes required—the discovery step automatically registers
Modelrecords that appear in the Settings interface.
Frequently Asked Questions
Do I need to modify the frontend code to add a new provider?
No. The UI pulls data from /api/routers/models, which uses ModelManager to query the database. Once your discovery function populates the model table, the provider automatically appears in the Models → Add screen. You only need to modify UI code if you want custom credential input fields beyond the standard API key.
How does the model type classification determine which Esperanto class to instantiate?
The classify_model_type() function returns a string—"language", "embedding", "speech_to_text", or "text_to_speech"—which ModelManager uses to call the appropriate AIFactory.create_language_model(), create_embedding_model(), create_speech_to_text_model(), or create_text_to_speech_model() method. Incorrect classification results in runtime errors when calling provider-specific methods.
What if my AI provider does not expose a model listing endpoint?
If the provider lacks a discovery API, modify your discovery function to return a static list of DiscoveredModel objects instead of making an HTTP request. Hardcode the known model IDs and types, then register the function normally. The system will treat these as discoverable models, and you can still update the list by deploying code changes.
How are credentials securely injected when using the new provider?
The ModelManager loads the Credential record linked to the model, calls to_esperanto_config() to build a configuration dictionary, and temporarily sets the environment variables defined in PROVIDER_CONFIG before instantiating the Esperanto client. This isolation ensures that API keys never leave the server process and are never exposed to the frontend.
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 →