Extract _get_api_key() and _create_provider_instance() as a single internal factory that handles all provider types, including MOCK. Both create_llm() and create_ai_provider() now delegate to _create_provider_instance(), eliminating the duplicated if/elif provider-switching chains and the divergence between the two code paths. Adding a new provider now requires touching exactly one place. Key changes: - New _get_api_key(provider_type) consolidates API key lookup and validation that was previously duplicated across create_llm(), _create_provider_llm(), and every branch of create_ai_provider(). - New _create_provider_instance() is the unified raw-LLM factory, replacing _create_provider_llm() and the specialised adapter constructors in create_ai_provider(). MOCK is now handled here via FakeListLLM. - create_ai_provider() wraps all providers uniformly in LangChainChatProvider instead of using specialised adapters (OpenAIChatProvider, AnthropicChatProvider, GoogleChatProvider, OpenRouterChatProvider) for the four primary providers. - create_llm() drops its own API key validation block; validation now happens inside _create_provider_instance() via _get_api_key(). - All BDD scenarios updated: _create_provider_llm references become _create_provider_instance, and provider-type assertions on create_ai_provider results are updated to LangChainChatProvider. Review fixes (Cycle 2): - Add _coerce_env_bool() for consistent boolean env-var coercion in CLEVERAGENTS_ALLOW_MOCK_PROVIDER checks. - Introduce _ApiKeyMissing sentinel to distinguish 'not provided' from None in provider configuration. - Set MOCK supports_streaming=False in DEFAULT_CAPABILITIES to align with FakeListLLM semantics. - Make MOCK is_configured conditional on CLEVERAGENTS_ALLOW_MOCK_PROVIDER. - Use FakeListLLM(responses=['mock response'] * 10) to prevent IndexError in multi-step workflows. - Strip max_retries from kwargs before passing to LangChain constructors. - Extract _resolve_provider_type() helper shared by create_llm() and create_ai_provider() to reduce duplication and improve readability. - Ensure __api_key_sentinel flows through factory_kwargs even when api_key is None, avoiding double _get_api_key() calls. - Add CHANGELOG env-var documentation and providers.md env-var table. - Resolve template DB lock issues by persisting in-memory SQLite via VACUUM INTO instead of direct file creation on tmpfs. ISSUES CLOSED: #10949
8.4 KiB
cleveragents.providers — AI Provider Registry
The providers package implements the AI provider registry that discovers,
selects, and instantiates LLM backends. It follows the provider plugin
architecture described in ADR-008 and wires into the actor system via
AIProviderInterface.
Overview
The registry discovers configured providers from environment variables and
Settings, selects a default using a configurable fallback chain, and
creates LangChain LLM instances or AIProviderInterface wrappers on
demand.
from cleveragents.providers.registry import get_provider_registry
registry = get_provider_registry()
provider = registry.create_ai_provider() # uses default provider
llm = registry.create_llm() # returns a LangChain BaseLanguageModel
ProviderType
class ProviderType(StrEnum):
OPENAI = "openai"
ANTHROPIC = "anthropic"
GOOGLE = "google"
AZURE = "azure"
OPENROUTER = "openrouter"
GEMINI = "gemini"
COHERE = "cohere"
GROQ = "groq"
TOGETHER = "together"
MOCK = "mock"
String enum of all supported provider identifiers. Values are used in
CLEVERAGENTS_DEFAULT_PROVIDER and in actor YAML provider: fields.
ProviderCapabilities
@dataclass(frozen=True)
class ProviderCapabilities:
supports_streaming: bool = True
supports_tool_calls: bool = False
supports_vision: bool = False
max_context_length: int = 4096
supports_json_mode: bool = False
Immutable capability metadata for a provider. Used by the actor system to gate features (e.g., tool calls, vision inputs) at runtime.
Default capabilities by provider
| Provider | Streaming | Tool calls | Vision | Context (tokens) | JSON mode |
|---|---|---|---|---|---|
| OpenAI | ✓ | ✓ | ✓ | 128 000 | ✓ |
| Anthropic | ✓ | ✓ | ✓ | 200 000 | ✗ |
| Google / Gemini | ✓ | ✓ | ✓ | 1 000 000 | ✓ |
| Azure OpenAI | ✓ | ✓ | ✓ | 128 000 | ✓ |
| OpenRouter | ✓ | ✓ | ✓ | 128 000 | ✓ |
| Cohere | ✓ | ✓ | ✗ | 128 000 | ✗ |
| Groq | ✓ | ✓ | ✗ | 32 000 | ✓ |
| Together | ✓ | ✓ | ✗ | 32 000 | ✗ |
| Mock | ✗ | ✗ | ✗ | 4 096 | ✗ |
ProviderInfo
class ProviderInfo(BaseModel):
provider_type: ProviderType
name: str
api_key_env_var: str
default_model: str
capabilities: ProviderCapabilities
is_configured: bool
Read-only snapshot of a provider's registration state. Returned by
ProviderRegistry.get_provider_info() and get_all_providers().
ProviderRegistry
Central registry for discovering and managing AI providers.
from cleveragents.providers.registry import ProviderRegistry
registry = ProviderRegistry() # uses global Settings
registry = ProviderRegistry(settings=my_cfg) # override settings
Default models
| Provider | Default model |
|---|---|
| OpenAI | gpt-4o |
| Anthropic | claude-sonnet-4-20250514 |
| Google / Gemini | gemini-2.0-flash |
| Azure | gpt-4o |
| OpenRouter | anthropic/claude-sonnet-4-20250514 |
| Cohere | command-r-plus |
| Groq | llama-3.1-70b-versatile |
| Together | meta-llama/Llama-3.1-70B-Instruct-Turbo |
| Mock | mock-gpt |
Provider fallback order
When CLEVERAGENTS_DEFAULT_PROVIDER is not set, the registry selects the
first configured provider in this order:
openai → anthropic → google → azure → openrouter → groq → together → cohere
Methods
get_configured_providers() → list[ProviderInfo]
Returns only providers that have valid API credentials configured.
get_all_providers() → list[ProviderInfo]
Returns all known providers regardless of configuration state.
get_provider_info(provider_type) → ProviderInfo | None
Look up a provider by ProviderType or string name. Returns None if
the provider is unknown.
is_provider_configured(provider_type) → bool
Returns True if the provider has a non-empty API key in Settings.
get_default_provider_type() → ProviderType | None
Resolves the default provider using this precedence:
CLEVERAGENTS_DEFAULT_PROVIDERenvironment variableSettings.default_provider- First configured provider in fallback order
Returns None if no provider is configured.
get_default_model(provider_type=None) → str | None
Resolves the default model using this precedence:
CLEVERAGENTS_DEFAULT_MODELenvironment variableSettings.default_model- Provider's built-in default model
create_llm(provider_type=None, model_id=None, **kwargs) → BaseLanguageModel
Creates a LangChain BaseLanguageModel for the specified provider and model.
llm = registry.create_llm("anthropic", model_id="claude-3-haiku-20240307")
Raises ValueError if no provider is configured or the provider name is
unknown.
create_ai_provider(provider_type=None, model_id=None, max_retries=3) → AIProviderInterface
Creates an AIProviderInterface implementation. All providers are now
wrapped uniformly in LangChainChatProvider; the raw LLM instantiation is
delegated to _create_provider_instance() internally. Previously, some
providers (Google, OpenRouter) returned specialised adapter classes, but the
unified factory now handles every provider type consistently.
provider = registry.create_ai_provider("openai", model_id="gpt-4o-mini")
LangChainChatProvider
Module: cleveragents.providers.llm.langchain_chat_provider
Wraps a LangChain chat model with the AIProviderInterface protocol and
wires it into PlanGenerationGraph.
class LangChainChatProvider(AIProviderInterface):
def __init__(
self,
*,
name: str,
model_id: str,
llm_factory: Callable[[str], BaseLanguageModel],
max_retries: int = 3,
supports_streaming: bool = True,
progress_map: dict[str, int] | None = None,
) -> None: ...
@property
def name(self) -> str: ...
@name.setter
def name(self, value: str) -> None: ...
@property
def model_id(self) -> str: ...
@model_id.setter
def model_id(self, value: str) -> None: ...
Note (v3.7.0+):
nameandmodel_idare now mutable properties with setters, fixing anAttributeErrorwhenPlanServiceattempted to resolve provider names after instantiation. (#1553)
Module-level helpers
get_provider_registry(settings=None) → ProviderRegistry
Returns the global singleton ProviderRegistry. Pass settings to force
a fresh instance (useful in tests).
reset_provider_registry() → None
Clears the global singleton. Call this between tests to ensure clean state.
resolve_provider_by_name(name, settings=None) → AIProviderInterface
Convenience function that resolves a provider by string name and raises a
descriptive ValueError if the provider is not configured.
from cleveragents.providers.registry import resolve_provider_by_name
provider = resolve_provider_by_name("anthropic")
Environment variables
| Variable | Description |
|---|---|
CLEVERAGENTS_DEFAULT_PROVIDER |
Pin the global provider (e.g. openai) |
CLEVERAGENTS_DEFAULT_MODEL |
Pin the global model ID |
OPENAI_API_KEY |
OpenAI credentials |
ANTHROPIC_API_KEY |
Anthropic credentials |
GOOGLE_API_KEY / GOOGLE_GENAI_API_KEY |
Google credentials |
AZURE_OPENAI_API_KEY |
Azure OpenAI credentials |
AZURE_OPENAI_ENDPOINT |
Azure OpenAI endpoint URL |
AZURE_OPENAI_DEPLOYMENT |
Azure deployment name |
OPENROUTER_API_KEY |
OpenRouter credentials |
GEMINI_API_KEY / GOOGLE_GEMINI_API_KEY |
Gemini credentials |
COHERE_API_KEY |
Cohere credentials |
GROQ_API_KEY |
Groq credentials |
TOGETHER_API_KEY |
Together AI credentials |
CLEVERAGENTS_TESTING_USE_MOCK_AI |
Force mock provider in tests |
CLEVERAGENTS_ALLOW_MOCK_PROVIDER |
Enable MOCK provider for testing (set to true) |
ASV Performance Benchmarks
The benchmarks/ directory contains ASV (airspeed velocity) benchmark
suites for the providers module. Run them with:
nox -s benchmarks
# or directly:
asv run --config asv.conf.json
Benchmarks cover provider discovery, registry initialization, and LLM factory creation latency. Results are tracked over time to detect performance regressions.