How the key_provider Module Implements Database-First API Key Fallback Logic

The key_provider module in lfnovo/open-notebook implements a database-first retrieval strategy that checks the Credential store before falling back to environment variables, ensuring persisted secrets take precedence over system configuration.

The open_notebook.ai.key_provider module serves as the centralized credential broker for the Open Notebook application. Located in open_notebook/ai/key_provider.py, this utility translates database-stored secrets into the environment variables expected by AI SDKs. By prioritizing the database over environment variables, the module ensures that user-configured credentials always override system defaults.

Provider-to-Environment Variable Mapping

The foundation of the fallback system relies on a static configuration dictionary that maps provider names to their expected environment variable names.

The PROVIDER_CONFIG Dictionary

At line 28 of open_notebook/ai/key_provider.py, the PROVIDER_CONFIG dictionary defines the exact environment variable name that each AI provider's SDK expects:


# Simplified representation of the mapping structure

PROVIDER_CONFIG = {
    "openai": "OPENAI_API_KEY",
    "anthropic": "ANTHROPIC_API_KEY",
    # ... additional providers

}

This mapping ensures that when the module retrieves credentials from the database, it knows precisely which environment variable to populate for each provider.

Database-First Retrieval Logic

The core retrieval flow follows a strict hierarchy: database credentials take precedence, with environment variables serving only as a fallback mechanism.

Querying the Default Credential

The private helper _get_default_credential() (line 76) queries the Credential domain model for the first record matching a given provider name. This function interacts with open_notebook/domain/credential.py to fetch the stored secret records.

API Key Extraction with Environment Fallback

The get_api_key() function (line 87) implements the dual-source lookup logic:

  1. Database Priority: The function first checks if a credential exists and contains an api_key field. If found, it returns the secret after pydantic.SecretStr-style deserialization.
  2. Environment Fallback: If no credential exists or the credential lacks an api_key, the function falls back to reading the environment variable defined in PROVIDER_CONFIG (line 100).

# Example 1 – Retrieve a single API key (database‑first)

from open_notebook.ai.key_provider import get_api_key

# Asynchronously obtain the OpenAI key; falls back to OPENAI_API_KEY env var

api_key = await get_api_key("openai")
print("OpenAI key:", api_key)

Provider Provisioning Strategies

The module distinguishes between simple providers that only require an API key and complex providers needing additional configuration parameters.

Simple Provider Provisioning

For providers requiring only an API key, _provision_simple_provider() (line 124) mirrors the database-first logic:

  • Loads the credential using _get_default_credential()
  • Writes the API key to the mapped environment variable from PROVIDER_CONFIG
  • Copies any base_url into a provider-specific <PROVIDER>_API_BASE variable

Complex Provider Configurations

Complex providers such as Vertex AI, Azure OpenAI, and generic OpenAI-compatible endpoints have dedicated provisioning functions that handle multiple configuration fields:

  • _provision_vertex() (line 145): Extracts project, location, and credential path from the database record.
  • _provision_azure() (line 174): Handles Azure-specific endpoints and authentication parameters.
  • _provision_openai_compatible() (line 221): Manages custom base URLs and authentication for compatible endpoints.

Each function pulls relevant fields from the credential record and injects them into the appropriate environment variables expected by the respective SDKs.

Public API Interface

The module exposes two primary entry points for credential provisioning.

Per-Provider Provisioning

The provision_provider_keys() function (line 118) normalizes the provider name, selects the correct provisioning routine (simple or complex), and returns True when any variable was set from the database.


# Example 2 – Provision environment variables for a provider before model creation

from open_notebook.ai.key_provider import provision_provider_keys

await provision_provider_keys("azure")      # Loads Azure credentials into env

# Now any Azure‑OpenAI SDK call will see AZURE_OPENAI_API_KEY, etc.

Bulk Key Provisioning

The provision_all_keys() function (line 83) walks the entire PROVIDER_CONFIG dictionary and runs per-provider provisioning for all entries. It returns a dictionary recording which providers were populated from the database versus environment variables.


# Example 3 – Bulk provisioning at application startup (not recommended per‑request)

from open_notebook.ai.key_provider import provision_all_keys

results = await provision_all_keys()
print("Provisioning results:", results)

# → {'openai': True, 'anthropic': False, ...}

Note: As implemented in the source code, this bulk operation is deprecated for request-time use and intended primarily for application initialization.

Summary

  • The key_provider module uses a static PROVIDER_CONFIG dictionary (line 28) to map provider names to their expected environment variable names.
  • get_api_key() (line 87) implements a database-first lookup that only falls back to environment variables when no credential exists in the database.
  • _provision_simple_provider() (line 124) handles basic API key injection, while dedicated functions like _provision_azure() (line 174) manage complex multi-field configurations.
  • The public entry point provision_provider_keys() (line 118) normalizes provider names and coordinates the appropriate provisioning strategy.
  • provision_all_keys() (line 83) provides bulk provisioning capabilities but is deprecated for per-request usage to avoid unnecessary database queries.

Frequently Asked Questions

How does the key_provider module prioritize credential sources?

The module follows a strict database-first hierarchy: it queries the Credential model via _get_default_credential() before checking environment variables. This ensures that user-configured credentials in the database always override system environment variables, with the fallback only activating when no database record exists.

What happens if no credential exists in the database?

When _get_default_credential() returns None or the credential lacks an api_key field, get_api_key() (line 100) automatically falls back to reading the environment variable defined in PROVIDER_CONFIG. If neither source yields a key, the function returns None.

Which providers require complex provisioning logic?

While simple providers like OpenAI and Anthropic use _provision_simple_provider(), complex providers including Vertex AI, Azure OpenAI, and generic OpenAI-compatible endpoints require specialized functions. These handle additional fields such as project IDs, locations, Azure endpoints, and custom base URLs that must be injected into specific environment variables.

Is provision_all_keys() suitable for per-request use?

No. According to the source implementation in open_notebook/ai/key_provider.py, provision_all_keys() (line 83) is deprecated for request-time use because it queries the database for every configured provider. Instead, use provision_provider_keys() to target only the specific provider needed for the current request, minimizing database load and improving response times.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →