diff --git a/plugins/web/firecrawl/provider.py b/plugins/web/firecrawl/provider.py index 97718a6cae2..ae02b43a6eb 100644 --- a/plugins/web/firecrawl/provider.py +++ b/plugins/web/firecrawl/provider.py @@ -122,8 +122,10 @@ Firecrawl = _FirecrawlProxy() def _get_direct_firecrawl_config() -> Optional[tuple]: """Return explicit direct Firecrawl kwargs + cache key, or None when unset.""" - api_key = os.getenv("FIRECRAWL_API_KEY", "").strip() - api_url = os.getenv("FIRECRAWL_API_URL", "").strip().rstrip("/") + from hermes_cli.config import get_env_value + + api_key = (get_env_value("FIRECRAWL_API_KEY") or "").strip() + api_url = (get_env_value("FIRECRAWL_API_URL") or "").strip().rstrip("/") if not api_key and not api_url: return None diff --git a/tests/tools/test_web_tools_config.py b/tests/tools/test_web_tools_config.py index 6466e615f04..5b1ec35f789 100644 --- a/tests/tools/test_web_tools_config.py +++ b/tests/tools/test_web_tools_config.py @@ -787,3 +787,44 @@ class TestNonBuiltinProviderAvailability: "web_search tool was filtered out despite custom provider being available" assert web_extract_entry is not None, \ "web_extract tool was filtered out despite custom provider being available" + + +class TestFirecrawlEnvResolution: + """Verify Firecrawl reads env values from hermes_cli.config.get_env_value, + not just os.getenv. This catches the regression reported in #40190 where + values stored in ~/.hermes/.env were invisible to the provider.""" + + def test_direct_config_reads_via_get_env_value(self, monkeypatch: pytest.MonkeyPatch) -> None: + """_get_direct_firecrawl_config() must use get_env_value, not os.getenv.""" + # Ensure os.environ does NOT carry the key + monkeypatch.delenv("FIRECRAWL_API_KEY", raising=False) + monkeypatch.delenv("FIRECRAWL_API_URL", raising=False) + + fake_key = "fc-test-key-from-dotenv" + with patch( + "hermes_cli.config.get_env_value", + side_effect=lambda k: fake_key if k == "FIRECRAWL_API_KEY" else None, + ): + from plugins.web.firecrawl.provider import _get_direct_firecrawl_config + + result = _get_direct_firecrawl_config() + assert result is not None, "get_env_value fallback should find the key" + kwargs, _cache_key = result + assert kwargs["api_key"] == fake_key + + def test_direct_config_reads_url_via_get_env_value(self, monkeypatch: pytest.MonkeyPatch) -> None: + """Self-hosted URL from .env must be picked up.""" + monkeypatch.delenv("FIRECRAWL_API_KEY", raising=False) + monkeypatch.delenv("FIRECRAWL_API_URL", raising=False) + + fake_url = "https://firecrawl.internal.example.com" + with patch( + "hermes_cli.config.get_env_value", + side_effect=lambda k: fake_url if k == "FIRECRAWL_API_URL" else None, + ): + from plugins.web.firecrawl.provider import _get_direct_firecrawl_config + + result = _get_direct_firecrawl_config() + assert result is not None + kwargs, _cache_key = result + assert kwargs["api_url"] == fake_url.rstrip("/") diff --git a/tools/web_tools.py b/tools/web_tools.py index 1e2c4a03a07..5fa1e872832 100644 --- a/tools/web_tools.py +++ b/tools/web_tools.py @@ -1001,8 +1001,9 @@ if __name__ == "__main__": # Check if API keys are available web_available = check_web_api_key() tool_gateway_available = _is_tool_gateway_ready() - firecrawl_key_available = bool(os.getenv("FIRECRAWL_API_KEY", "").strip()) - firecrawl_url_available = bool(os.getenv("FIRECRAWL_API_URL", "").strip()) + from hermes_cli.config import get_env_value as _gev + firecrawl_key_available = bool((_gev("FIRECRAWL_API_KEY") or "").strip()) + firecrawl_url_available = bool((_gev("FIRECRAWL_API_URL") or "").strip()) if web_available: backend = _get_backend() @@ -1020,7 +1021,7 @@ if __name__ == "__main__": elif backend == "ddgs": print(" Using DuckDuckGo via ddgs package (search only)") elif firecrawl_url_available: - print(f" Using self-hosted Firecrawl: {os.getenv('FIRECRAWL_API_URL').strip().rstrip('/')}") + print(f" Using self-hosted Firecrawl: {(_gev('FIRECRAWL_API_URL') or '').strip().rstrip('/')}") elif firecrawl_key_available: print(" Using direct Firecrawl cloud API") elif tool_gateway_available: