From 026ab4737d67f6d5d70e871219b6ff0435bfaf34 Mon Sep 17 00:00:00 2001 From: liuhao1024 Date: Sat, 6 Jun 2026 08:17:40 +0800 Subject: [PATCH] fix(web): use get_env_value for Firecrawl config resolution The Firecrawl provider used os.getenv() to read FIRECRAWL_API_KEY and FIRECRAWL_API_URL, which only checks the process environment. When values are supplied through Hermes's ~/.hermes/.env config mechanism (via hermes_cli.config.get_env_value), they are not guaranteed to be present in os.environ for every gateway/tool execution path. Switch to get_env_value() which checks both os.environ and the .env file, matching the pattern used by other providers (nous_subscription, setup, discord adapter). Fixes #40190 --- plugins/web/firecrawl/provider.py | 6 ++-- tests/tools/test_web_tools_config.py | 41 ++++++++++++++++++++++++++++ tools/web_tools.py | 7 +++-- 3 files changed, 49 insertions(+), 5 deletions(-) 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: