fix(firecrawl): pass tool base_url to FirecrawlApp as api_url (#5392)

_get_firecrawl_client only read api_key from the tool config and ignored
base_url, so self-hosted Firecrawl deployments were unreachable: the SDK
defaulted to https://api.firecrawl.dev and raised 'No API key provided'.
Now base_url is forwarded as api_url; api_url is omitted when unset so
cloud behavior is unchanged.

Also document the optional self-hosted base_url on both Firecrawl entries
in config.example.yaml and reconcile their headers to the fastCRW house
style (Cloud requires FIRECRAWL_API_KEY; self-host may need no key.).
This commit is contained in:
Aleksandr Sapronov 2026-09-13 16:23:04 +07:00 committed by GitHub
parent dfe9a520b9
commit b448c02bbe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 5 deletions

View File

@ -9,9 +9,16 @@ from deerflow.config import get_app_config
def _get_firecrawl_client(tool_name: str = "web_search") -> FirecrawlApp:
config = get_app_config().get_tool_config(tool_name)
api_key = None
if config is not None and "api_key" in config.model_extra:
api_key = config.model_extra.get("api_key")
return FirecrawlApp(api_key=api_key) # type: ignore[arg-type]
api_url = None
if config is not None:
if "api_key" in config.model_extra:
api_key = config.model_extra.get("api_key")
if "base_url" in config.model_extra:
api_url = config.model_extra.get("base_url")
kwargs = {"api_key": api_key}
if api_url:
kwargs["api_url"] = api_url
return FirecrawlApp(**kwargs) # type: ignore[arg-type]
@tool("web_search", parse_docstring=True)

View File

@ -64,3 +64,51 @@ class TestWebFetchTool:
"https://example.com",
formats=["markdown"],
)
class TestFirecrawlBaseUrl:
@patch("deerflow.community.firecrawl.tools.FirecrawlApp")
@patch("deerflow.community.firecrawl.tools.get_app_config")
def test_fetch_passes_base_url_as_api_url(self, mock_get_app_config, mock_firecrawl_cls):
fetch_config = MagicMock()
fetch_config.model_extra = {"base_url": "http://192.168.0.47:3002"}
def get_tool_config(name):
if name == "web_fetch":
return fetch_config
return None
mock_get_app_config.return_value.get_tool_config.side_effect = get_tool_config
mock_scrape_result = MagicMock()
mock_scrape_result.markdown = "Fetched markdown"
mock_scrape_result.metadata = MagicMock(title="Fetched Page")
mock_firecrawl_cls.return_value.scrape.return_value = mock_scrape_result
from deerflow.community.firecrawl.tools import web_fetch_tool
result = web_fetch_tool.invoke({"url": "https://example.com"})
assert result == "# Fetched Page\n\nFetched markdown"
mock_firecrawl_cls.assert_called_once_with(api_key=None, api_url="http://192.168.0.47:3002")
@patch("deerflow.community.firecrawl.tools.FirecrawlApp")
@patch("deerflow.community.firecrawl.tools.get_app_config")
def test_search_passes_base_url_and_api_key(self, mock_get_app_config, mock_firecrawl_cls):
search_config = MagicMock()
search_config.model_extra = {
"api_key": "firecrawl-key",
"base_url": "http://192.168.0.47:3002",
"max_results": 5,
}
mock_get_app_config.return_value.get_tool_config.return_value = search_config
mock_result = MagicMock()
mock_result.web = []
mock_firecrawl_cls.return_value.search.return_value = mock_result
from deerflow.community.firecrawl.tools import web_search_tool
web_search_tool.invoke({"query": "test query"})
mock_firecrawl_cls.assert_called_once_with(api_key="firecrawl-key", api_url="http://192.168.0.47:3002")

View File

@ -868,12 +868,13 @@ tools:
# contents_max_characters: 1000
# # api_key: $EXA_API_KEY
# Web search tool (uses Firecrawl, requires FIRECRAWL_API_KEY)
# Web search tool (uses Firecrawl. Cloud requires FIRECRAWL_API_KEY; self-host may need no key.)
# - name: web_search
# group: web
# use: deerflow.community.firecrawl.tools:web_search_tool
# max_results: 5
# # api_key: $FIRECRAWL_API_KEY
# # base_url: http://<host>:3002 # optional: self-hosted Firecrawl; no cloud API key needed
# Web search tool (uses GroundRoute, requires GROUNDROUTE_API_KEY)
# GroundRoute is a meta search layer: one API in front of six engines (Serper,
@ -1059,11 +1060,12 @@ tools:
# # Timeout for navigating to the page (in seconds). Set to positive value to enable, -1 to disable
# navigation_timeout: 30
# Web fetch tool (uses Firecrawl, requires FIRECRAWL_API_KEY)
# Web fetch tool (uses Firecrawl. Cloud requires FIRECRAWL_API_KEY; self-host may need no key.)
# - name: web_fetch
# group: web
# use: deerflow.community.firecrawl.tools:web_fetch_tool
# # api_key: $FIRECRAWL_API_KEY
# # base_url: http://<host>:3002 # optional: self-hosted Firecrawl; no cloud API key needed
# Web fetch tool (uses GroundRoute, requires GROUNDROUTE_API_KEY)
# Fetches a page's extracted text via GroundRoute mode=page.