from typing import TYPE_CHECKING, Any, Dict, Optional, Type, List from crewai.tools import BaseTool, EnvVar from pydantic import BaseModel, ConfigDict, Field, PrivateAttr if TYPE_CHECKING: from firecrawl import FirecrawlApp try: from firecrawl import FirecrawlApp FIRECRAWL_AVAILABLE = True except ImportError: FIRECRAWL_AVAILABLE = False class FirecrawlSearchToolSchema(BaseModel): query: str = Field(description="Search query") class FirecrawlSearchTool(BaseTool): """ Tool for searching webpages using Firecrawl. To run this tool, you need to have a Firecrawl API key. Args: api_key (str): Your Firecrawl API key. config (dict): Optional. It contains Firecrawl API parameters. Default configuration options: limit (int): Maximum number of pages to crawl. Default: 5 tbs (str): Time before search. Default: None lang (str): Language. Default: "en" country (str): Country. Default: "us" location (str): Location. Default: None timeout (int): Timeout in milliseconds. Default: 60000 """ model_config = ConfigDict( arbitrary_types_allowed=True, validate_assignment=True, frozen=False ) model_config = ConfigDict( arbitrary_types_allowed=True, validate_assignment=True, frozen=False ) name: str = "Firecrawl web search tool" description: str = "Search webpages using Firecrawl and return the results" args_schema: Type[BaseModel] = FirecrawlSearchToolSchema api_key: Optional[str] = None config: Optional[dict[str, Any]] = Field( default_factory=lambda: { "limit": 5, "tbs": None, "lang": "en", "country": "us", "location": None, "timeout": 60000, } ) _firecrawl: Optional["FirecrawlApp"] = PrivateAttr(None) package_dependencies: List[str] = ["firecrawl-py"] env_vars: List[EnvVar] = [ EnvVar(name="FIRECRAWL_API_KEY", description="API key for Firecrawl services", required=True), ] def __init__(self, api_key: Optional[str] = None, **kwargs): super().__init__(**kwargs) self.api_key = api_key self._initialize_firecrawl() def _initialize_firecrawl(self) -> None: try: from firecrawl import FirecrawlApp # type: ignore self._firecrawl = FirecrawlApp(api_key=self.api_key) except ImportError: import click if click.confirm( "You are missing the 'firecrawl-py' package. Would you like to install it?" ): import subprocess try: subprocess.run(["uv", "add", "firecrawl-py"], check=True) from firecrawl import FirecrawlApp self._firecrawl = FirecrawlApp(api_key=self.api_key) except subprocess.CalledProcessError: raise ImportError("Failed to install firecrawl-py package") else: raise ImportError( "`firecrawl-py` package not found, please run `uv add firecrawl-py`" ) def _run( self, query: str, ) -> Any: if not self._firecrawl: raise RuntimeError("FirecrawlApp not properly initialized") return self._firecrawl.search( query=query, params=self.config, ) try: from firecrawl import FirecrawlApp # type: ignore # Only rebuild if the class hasn't been initialized yet if not hasattr(FirecrawlSearchTool, "_model_rebuilt"): FirecrawlSearchTool.model_rebuild() FirecrawlSearchTool._model_rebuilt = True except ImportError: """ When this tool is not used, then exception can be ignored. """ pass