Files
hurui200320 7164b04019
CI / lint (push) Successful in 1m1s
CI / build (push) Successful in 53s
CI / quality (push) Successful in 1m23s
CI / typecheck (push) Successful in 1m24s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 1m42s
CI / push-validation (push) Successful in 21s
CI / helm (push) Successful in 25s
CI / e2e_tests (push) Successful in 4m6s
CI / integration_tests (push) Successful in 4m12s
CI / unit_tests (push) Successful in 6m39s
CI / docker (push) Successful in 1m44s
CI / coverage (push) Successful in 10m51s
CI / status-check (push) Successful in 2s
CI / benchmark-publish (push) Successful in 1h17m10s
CI / benchmark-publish (pull_request) Has been skipped
CI / helm (pull_request) Successful in 33s
CI / build (pull_request) Successful in 50s
CI / lint (pull_request) Successful in 1m7s
CI / quality (pull_request) Successful in 1m9s
CI / typecheck (pull_request) Successful in 1m20s
CI / security (pull_request) Successful in 1m25s
CI / push-validation (pull_request) Successful in 19s
CI / integration_tests (pull_request) Successful in 3m20s
CI / e2e_tests (pull_request) Failing after 5m3s
CI / unit_tests (pull_request) Successful in 6m40s
CI / docker (pull_request) Successful in 1m30s
CI / coverage (pull_request) Successful in 11m9s
CI / status-check (pull_request) Failing after 3s
CI / benchmark-regression (pull_request) Failing after 1m11s
refactor(providers): unify provider factory behind single source of truth
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
2026-05-05 06:16:46 +00:00

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:

  1. CLEVERAGENTS_DEFAULT_PROVIDER environment variable
  2. Settings.default_provider
  3. 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:

  1. CLEVERAGENTS_DEFAULT_MODEL environment variable
  2. Settings.default_model
  3. 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+): name and model_id are now mutable properties with setters, fixing an AttributeError when PlanService attempted 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.