diff --git a/AGENTS.md b/AGENTS.md index 37af4c968..493879000 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -421,6 +421,7 @@ The system uses `_config_version` to detect outdated configs: API keys are loaded from `~/.hermes/.env`: - `OPENROUTER_API_KEY` - Main LLM API access (primary provider) - `FIRECRAWL_API_KEY` - Web search/extract tools +- `FIRECRAWL_API_URL` - Self-hosted Firecrawl endpoint (optional) - `BROWSERBASE_API_KEY` / `BROWSERBASE_PROJECT_ID` - Browser automation - `FAL_KEY` - Image generation (FLUX model) - `NOUS_API_KEY` - Vision and Mixture-of-Agents tools diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 2330c2cb0..7e444cb9a 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -179,6 +179,14 @@ OPTIONAL_ENV_VARS = { "password": True, "category": "tool", }, + "FIRECRAWL_API_URL": { + "description": "Firecrawl API URL for self-hosted instances (optional)", + "prompt": "Firecrawl API URL (leave empty for cloud)", + "url": None, + "password": False, + "category": "tool", + "advanced": True, + }, "BROWSERBASE_API_KEY": { "description": "Browserbase API key for browser automation", "prompt": "Browserbase API key", @@ -821,7 +829,7 @@ def set_config_value(key: str, value: str): # Check if it's an API key (goes to .env) api_keys = [ 'OPENROUTER_API_KEY', 'ANTHROPIC_API_KEY', 'VOICE_TOOLS_OPENAI_KEY', - 'FIRECRAWL_API_KEY', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', + 'FIRECRAWL_API_KEY', 'FIRECRAWL_API_URL', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'FAL_KEY', 'TELEGRAM_BOT_TOKEN', 'DISCORD_BOT_TOKEN', 'TERMINAL_SSH_HOST', 'TERMINAL_SSH_USER', 'TERMINAL_SSH_KEY', 'SUDO_PASSWORD', 'SLACK_BOT_TOKEN', 'SLACK_APP_TOKEN', diff --git a/tests/tools/test_web_tools_config.py b/tests/tools/test_web_tools_config.py new file mode 100644 index 000000000..8486d08c3 --- /dev/null +++ b/tests/tools/test_web_tools_config.py @@ -0,0 +1,49 @@ +"""Tests for Firecrawl client configuration.""" + +import os +import pytest +from unittest.mock import patch, MagicMock + + +class TestFirecrawlClientConfig: + """Test suite for Firecrawl client initialization with API URL support.""" + + def teardown_method(self): + """Reset client between tests.""" + import tools.web_tools + + tools.web_tools._firecrawl_client = None + + def test_client_with_api_key_only(self): + """Test client initialization with only API key (no custom URL).""" + env_vars = {"FIRECRAWL_API_KEY": "test-key"} + env_vars.pop("FIRECRAWL_API_URL", None) + + with patch.dict(os.environ, env_vars, clear=False): + # Remove FIRECRAWL_API_URL from env if it exists + if "FIRECRAWL_API_URL" in os.environ: + del os.environ["FIRECRAWL_API_URL"] + + with patch("tools.web_tools.Firecrawl") as mock_firecrawl: + from tools.web_tools import _get_firecrawl_client + + _get_firecrawl_client() + mock_firecrawl.assert_called_once_with(api_key="test-key") + + def test_client_with_api_key_and_url(self): + """Test client initialization with API key and custom URL.""" + with patch.dict( + os.environ, + { + "FIRECRAWL_API_KEY": "test-key", + "FIRECRAWL_API_URL": "http://localhost:3002", + }, + clear=False, + ): + with patch("tools.web_tools.Firecrawl") as mock_firecrawl: + from tools.web_tools import _get_firecrawl_client + + _get_firecrawl_client() + mock_firecrawl.assert_called_once_with( + api_key="test-key", api_url="http://localhost:3002" + ) diff --git a/tools/web_tools.py b/tools/web_tools.py index 541404e6d..d0e6e0d74 100644 --- a/tools/web_tools.py +++ b/tools/web_tools.py @@ -62,7 +62,12 @@ def _get_firecrawl_client(): api_key = os.getenv("FIRECRAWL_API_KEY") if not api_key: raise ValueError("FIRECRAWL_API_KEY environment variable not set") - _firecrawl_client = Firecrawl(api_key=api_key) + + api_url = os.getenv("FIRECRAWL_API_URL") + if api_url: + _firecrawl_client = Firecrawl(api_key=api_key, api_url=api_url) + else: + _firecrawl_client = Firecrawl(api_key=api_key) return _firecrawl_client DEFAULT_MIN_LENGTH_FOR_SUMMARIZATION = 5000 diff --git a/website/docs/getting-started/installation.md b/website/docs/getting-started/installation.md index 1d0241c3a..21a4e0092 100644 --- a/website/docs/getting-started/installation.md +++ b/website/docs/getting-started/installation.md @@ -164,6 +164,7 @@ OPENROUTER_API_KEY=sk-or-v1-your-key-here # Optional — enable additional tools: FIRECRAWL_API_KEY=fc-your-key # Web search & scraping +FIRECRAWL_API_URL=http://localhost:3002 # Self-hosted Firecrawl (optional) FAL_KEY=your-fal-key # Image generation (FLUX) ``` diff --git a/website/docs/reference/environment-variables.md b/website/docs/reference/environment-variables.md index 8da1954ea..7273d57d2 100644 --- a/website/docs/reference/environment-variables.md +++ b/website/docs/reference/environment-variables.md @@ -35,6 +35,7 @@ All variables go in `~/.hermes/.env`. You can also set them with `hermes config | Variable | Description | |----------|-------------| | `FIRECRAWL_API_KEY` | Web scraping ([firecrawl.dev](https://firecrawl.dev/)) | +| `FIRECRAWL_API_URL` | Custom Firecrawl API endpoint for self-hosted instances (optional) | | `BROWSERBASE_API_KEY` | Browser automation ([browserbase.com](https://browserbase.com/)) | | `BROWSERBASE_PROJECT_ID` | Browserbase project ID | | `BROWSER_INACTIVITY_TIMEOUT` | Browser session inactivity timeout in seconds | diff --git a/website/docs/user-guide/configuration.md b/website/docs/user-guide/configuration.md index df2e868ed..d8a0d7a48 100644 --- a/website/docs/user-guide/configuration.md +++ b/website/docs/user-guide/configuration.md @@ -79,6 +79,7 @@ Even when using Nous Portal, Codex, or a custom endpoint, some tools (vision, we | Feature | Provider | Env Variable | |---------|----------|--------------| | Web scraping | [Firecrawl](https://firecrawl.dev/) | `FIRECRAWL_API_KEY` | +| Web scraping (self-hosted) | Firecrawl | `FIRECRAWL_API_URL` | | Browser automation | [Browserbase](https://browserbase.com/) | `BROWSERBASE_API_KEY`, `BROWSERBASE_PROJECT_ID` | | Image generation | [FAL](https://fal.ai/) | `FAL_KEY` | | Premium TTS voices | [ElevenLabs](https://elevenlabs.io/) | `ELEVENLABS_API_KEY` |