"""Step definitions for provider_fixes.feature.""" from __future__ import annotations import logging import os from typing import Any from unittest.mock import MagicMock from behave import given, then, when # type: ignore[import-untyped] def _make_pf_mock_settings( *, openai: str | None = None, mock_providers: bool = False, env: str = "test", ) -> MagicMock: """Create a lightweight Settings-like mock.""" settings = MagicMock() settings.openai_api_key = openai settings.anthropic_api_key = None settings.google_api_key = None settings.gemini_api_key = None settings.azure_api_key = None settings.openrouter_api_key = None settings.cohere_api_key = None settings.groq_api_key = None settings.together_api_key = None settings.default_provider = None settings.default_model = None settings.azure_openai_endpoint = None settings.azure_openai_api_version = None settings.azure_openai_deployment = None settings.openrouter_organization = None settings.mock_providers = mock_providers settings.env = env return settings # ── Given steps ────────────────────────────────────────────────────── @given("a provider-fix settings instance with openai configured") def step_pf_settings_with_openai(context: Any) -> None: context.pf_settings = _make_pf_mock_settings(openai="sk-test-key") @given("a provider-fix settings instance with no providers configured") def step_pf_settings_no_providers(context: Any) -> None: context.pf_settings = _make_pf_mock_settings() @given("provider-fix mock_providers is disabled") def step_pf_mock_disabled(context: Any) -> None: context.pf_settings.mock_providers = False @given("provider-fix mock_providers is disabled on that instance") def step_pf_mock_disabled_on_instance(context: Any) -> None: context.pf_settings.mock_providers = False @given("a provider-fix settings instance with mock_providers enabled") def step_pf_settings_mock(context: Any) -> None: context.pf_settings = _make_pf_mock_settings(mock_providers=True) @given('the provider-fix environment is set to "{env_name}"') def step_pf_set_env(context: Any, env_name: str) -> None: context.pf_settings.env = env_name @given("I create a provider-fix fresh settings instance") def step_pf_fresh_settings(context: Any) -> None: from cleveragents.config.settings import Settings saved: dict[str, str | None] = {} for key in ("CLEVERAGENTS_MOCK_PROVIDERS",): saved[key] = os.environ.pop(key, None) try: Settings._instance = None # type: ignore[attr-defined] context.pf_fresh_settings = Settings() finally: for key, val in saved.items(): if val is not None: os.environ[key] = val @given("I have a provider-fix FakeListLLM instance for testing") def step_pf_create_fake_llm(context: Any) -> None: from langchain_community.llms import FakeListLLM context.pf_test_llm = FakeListLLM( responses=[ "Dependencies: ['os', 'sys']", "Relevance: High", "Summary: test summary", ] ) # ── When steps ─────────────────────────────────────────────────────── @when("I create a provider-fix registry from those settings") def step_pf_create_registry(context: Any) -> None: from cleveragents.providers.registry import ProviderRegistry context.pf_registry = ProviderRegistry(settings=context.pf_settings) @when("I call provider-fix validate_provider_availability") def step_pf_validate(context: Any) -> None: from cleveragents.config.settings import Settings # Clear all provider env vars so Settings sees no providers provider_env_keys = [ "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GOOGLE_API_KEY", "GOOGLE_GENAI_API_KEY", "AZURE_OPENAI_API_KEY", "AZURE_API_KEY", "OPENROUTER_API_KEY", "GEMINI_API_KEY", "GOOGLE_GEMINI_API_KEY", "HF_TOKEN", "HUGGINGFACEHUB_API_TOKEN", "HUGGING_FACE_HUB_TOKEN", "COHERE_API_KEY", "PERPLEXITY_API_KEY", "GROQ_API_KEY", "TOGETHER_API_KEY", ] saved_env: dict[str, str | None] = {} if not context.pf_settings.openai_api_key: for key in provider_env_keys: saved_env[key] = os.environ.pop(key, None) # Set env vars for Settings to pick up env_overrides: dict[str, str] = {} if context.pf_settings.mock_providers: env_overrides["CLEVERAGENTS_MOCK_PROVIDERS"] = "true" if hasattr(context.pf_settings, "env"): env_overrides["CLEVERAGENTS_ENV"] = context.pf_settings.env for k, v in env_overrides.items(): os.environ[k] = v try: Settings._instance = None # type: ignore[attr-defined] real = Settings() if context.pf_settings.openai_api_key: real.openai_api_key = context.pf_settings.openai_api_key context.pf_error = None context.pf_warning_logged = False class _Catcher(logging.Handler): def __init__(self) -> None: super().__init__() self.warnings: list[str] = [] def emit(self, record: logging.LogRecord) -> None: if record.levelno >= logging.WARNING: self.warnings.append(record.getMessage()) handler = _Catcher() log = logging.getLogger("cleveragents.config.settings") log.addHandler(handler) try: real.validate_provider_availability() except ValueError as exc: context.pf_error = exc finally: log.removeHandler(handler) context.pf_warning_logged = len(handler.warnings) > 0 finally: for key, val in saved_env.items(): if val is not None: os.environ[key] = val else: os.environ.pop(key, None) for k in env_overrides: os.environ.pop(k, None) @when("I build the provider-fix AI provider from the container helper") def step_pf_build_provider(context: Any) -> None: from cleveragents.application.container import get_ai_provider from cleveragents.providers.registry import ( ProviderRegistry, reset_provider_registry, ) reset_provider_registry() registry = ProviderRegistry(settings=context.pf_settings) context.pf_provider_result = get_ai_provider( settings=context.pf_settings, provider_registry=registry, ) @when("I attempt to create a provider-fix PlanGenerationGraph without an LLM") def step_pf_plan_gen_no_llm(context: Any) -> None: from cleveragents.agents.graphs.plan_generation import ( PlanGenerationGraph, ) context.pf_error = None try: PlanGenerationGraph(llm=None) except ValueError as exc: context.pf_error = exc @when("I attempt to create a provider-fix ContextAnalysisAgent without an LLM") def step_pf_ctx_no_llm(context: Any) -> None: from cleveragents.agents.graphs.context_analysis import ( ContextAnalysisAgent, ) context.pf_error = None try: ContextAnalysisAgent(llm=None) except ValueError as exc: context.pf_error = exc @when("I attempt to create a provider-fix AutoDebugAgent without an LLM") def step_pf_debug_no_llm(context: Any) -> None: from cleveragents.agents.graphs.auto_debug import AutoDebugAgent context.pf_error = None try: AutoDebugAgent(llm=None) except ValueError as exc: context.pf_error = exc @when("I create a provider-fix registry and request the default provider") def step_pf_registry_default(context: Any) -> None: from cleveragents.providers.registry import ProviderRegistry context.pf_registry = ProviderRegistry(settings=context.pf_settings) context.pf_default = context.pf_registry.get_default_provider_type() @when('I provider-fix resolve provider by name "{name}"') def step_pf_resolve_by_name(context: Any, name: str) -> None: from cleveragents.providers.registry import ( reset_provider_registry, resolve_provider_by_name, ) reset_provider_registry() context.pf_error = None context.pf_resolved = None try: context.pf_resolved = resolve_provider_by_name( name, settings=context.pf_settings ) except ValueError as exc: context.pf_error = exc @when("I call provider-fix get_ai_provider with those settings") def step_pf_get_ai_provider(context: Any) -> None: from cleveragents.application.container import get_ai_provider from cleveragents.providers.registry import ( ProviderRegistry, reset_provider_registry, ) reset_provider_registry() registry = ProviderRegistry(settings=context.pf_settings) context.pf_provider_result = get_ai_provider( settings=context.pf_settings, provider_registry=registry, ) @when("I create a provider-fix PlanGenerationGraph with that LLM") def step_pf_create_graph(context: Any) -> None: from cleveragents.agents.graphs.plan_generation import ( PlanGenerationGraph, ) context.pf_error = None try: context.pf_graph = PlanGenerationGraph(llm=context.pf_test_llm) except Exception as exc: context.pf_error = exc @when("I create a provider-fix ContextAnalysisAgent with that LLM") def step_pf_create_ctx_agent(context: Any) -> None: from cleveragents.agents.graphs.context_analysis import ( ContextAnalysisAgent, ) context.pf_error = None try: context.pf_agent = ContextAnalysisAgent(llm=context.pf_test_llm) except Exception as exc: context.pf_error = exc @when("I create a provider-fix AutoDebugAgent with that LLM") def step_pf_create_debug_agent(context: Any) -> None: from cleveragents.agents.graphs.auto_debug import AutoDebugAgent context.pf_error = None try: context.pf_agent = AutoDebugAgent(llm=context.pf_test_llm) except Exception as exc: context.pf_error = exc # ── Then steps ─────────────────────────────────────────────────────── @then('the provider-fix default provider type should be "{expected}"') def step_pf_check_default(context: Any, expected: str) -> None: from cleveragents.providers.registry import ProviderType default = context.pf_registry.get_default_provider_type() assert default is not None, "Expected a default provider" assert default == ProviderType(expected), f"Expected {expected}, got {default}" @then('a provider-fix ValueError should be raised containing "{fragment}"') def step_pf_check_error(context: Any, fragment: str) -> None: assert context.pf_error is not None, ( f"Expected ValueError with '{fragment}' but no error" ) assert fragment in str(context.pf_error), ( f"Expected '{fragment}' in: {context.pf_error}" ) @then("a provider-fix mock AI provider should be returned") def step_pf_check_mock(context: Any) -> None: if context.pf_provider_result is not None: name = getattr(context.pf_provider_result, "_name", "") cls = type(context.pf_provider_result).__name__.lower() assert "mock" in name.lower() or "mock" in cls @then("the provider-fix selection should be logged") def step_pf_check_logging(context: Any) -> None: assert context.pf_default is not None @then("I should receive a provider-fix AI provider instance") def step_pf_check_instance(context: Any) -> None: assert context.pf_resolved is not None @then("no provider-fix error should be raised") def step_pf_no_error(context: Any) -> None: assert context.pf_error is None, f"Expected no error but got: {context.pf_error}" @then("a provider-fix warning should be logged about mock_providers") def step_pf_check_warning(context: Any) -> None: assert context.pf_warning_logged, "Expected a warning" @then("provider-fix mock_providers should be False by default") def step_pf_check_default_mock(context: Any) -> None: assert context.pf_fresh_settings.mock_providers is False @then("a provider-fix mock provider or None should be returned") def step_pf_mock_or_none(context: Any) -> None: result = context.pf_provider_result if result is not None: cls = type(result).__name__.lower() assert "mock" in cls or hasattr(result, "_name") @then("the provider-fix graph should be created successfully") def step_pf_graph_ok(context: Any) -> None: assert context.pf_error is None, f"Expected no error but got: {context.pf_error}" assert context.pf_graph is not None @then("the provider-fix agent should be created successfully") def step_pf_agent_ok(context: Any) -> None: assert context.pf_error is None, f"Expected no error but got: {context.pf_error}" assert context.pf_agent is not None