Skip to content

feat(models): add @extensible decorator to get_api_key()#1258

Merged
frdel merged 1 commit intoagent0ai:developmentfrom
Deimos-AI:pr/extensible-models-get-api-key
Mar 26, 2026
Merged

feat(models): add @extensible decorator to get_api_key()#1258
frdel merged 1 commit intoagent0ai:developmentfrom
Deimos-AI:pr/extensible-models-get-api-key

Conversation

@Krashnicov
Copy link
Contributor

Problem

get_api_key() in models.py resolves API keys exclusively from OS environment variables via os.getenv(). This means external secrets backends (OpenBao/Vault, AWS Secrets Manager, Azure Key Vault, etc.) cannot provide API keys to LiteLLM without injecting them into the process environment — a workaround that is fragile, doesn't support cache TTLs, and bypasses the existing SecretsManager architecture.

While helpers/secrets.py factory functions were recently made extensible (#1246), the actual API key resolution path for chat/embedding/utility models goes through models.get_api_key()dotenv.get_dotenv_value()os.getenv(), completely bypassing SecretsManager.

Solution

Add the @extensible decorator to get_api_key(), enabling plugins to intercept API key resolution at the models_get_api_key_start extension point.

This is a 2-line change:

  1. Import extensible from helpers.extension
  2. Add @extensible decorator to get_api_key()

How it works

With @extensible, a plugin extension can:

  • Set data["result"] in a _start hook to provide an API key from any backend, short-circuiting the default dotenv lookup
  • Fall through to the default os.getenv() behaviour when no plugin is active
  • Modify data["args"] or data["kwargs"] to alter the service name lookup

Backward Compatibility

No behavioural change for existing users. The @extensible decorator only adds extension hooks around the existing function. Without any registered extensions, get_api_key() behaves identically to before.

Motivating Use Case

OpenBao secrets backend plugin that needs to provide LiteLLM API keys from a KV v2 secrets engine, with TTL-based caching and automatic fallback to .env files.

Related

@Krashnicov Krashnicov force-pushed the pr/extensible-models-get-api-key branch 2 times, most recently from ea88e18 to f17682d Compare March 15, 2026 08:57
@Krashnicov
Copy link
Contributor Author

Why helpers/settings.py is included

models.py is imported at module level by settings.py (line 9: import models). Adding from helpers.extension import extensible to models.py creates a circular import:

settings.py → import models → helpers.extension → helpers.subagents
  → helpers.plugins → extension (not yet initialized) → AttributeError

The fix moves import models from top-level in settings.py into the three functions that actually use it: convert_out(), _get_api_key_field(), and _load_sensitive_settings(). This is standard Python circular-import resolution — settings.py was the unusual importer (top-level), while models.py is widely imported across the codebase and shouldn't have import restrictions.

Python caches modules after first load, so the deferred import has zero performance impact on subsequent calls. No behavioural change.

@Krashnicov Krashnicov force-pushed the pr/extensible-models-get-api-key branch from f17682d to 88dcc88 Compare March 20, 2026 10:04
@Krashnicov
Copy link
Contributor Author

This PR has been rebased onto current development and scoped down to the single models.py change only.

The helpers/settings.py deferred-import fix that was previously bundled here has been extracted into a standalone bugfix PR: #1295. That fix addresses an upstream regression where import models was removed at the top level but functions (convert_out(), _get_api_key_field(), _load_sensitive_settings()) still reference it — causing NameError at runtime.

Both PRs are independent and can be merged in any order.

@ehlowr0ld
Copy link
Contributor

This will be useful to implement different credential helpers/providers - like vault for example - or keystone / other cloud provider secrets management solution.

Add @extensible decorator to get_api_key() in models.py, enabling
plugins to intercept or replace the API key resolution logic for
any LLM provider.

Currently, get_api_key() resolves API keys exclusively from OS
environment variables via os.getenv(). This means external secrets
backends (OpenBao/Vault, AWS Secrets Manager, Azure Key Vault, etc.)
cannot provide API keys without injecting them into the process
environment.

With @extensible, a plugin can intercept the start hook to resolve
API keys from any backend, falling through to the default dotenv
behaviour when no plugin is active.

No behavioural change for existing users — the decorator only adds
extension hooks around the existing function.

Note: the companion fix for the circular import this previously
required (deferred import in settings.py) is submitted separately
in PR agent0ai#1295.
@Krashnicov Krashnicov force-pushed the pr/extensible-models-get-api-key branch from 88dcc88 to 7d9ec2c Compare March 26, 2026 11:07
@frdel frdel merged commit 2e0ec63 into agent0ai:development Mar 26, 2026
@Krashnicov Krashnicov deleted the pr/extensible-models-get-api-key branch March 26, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants