Repository: phidatahq/phidata
Issue category: Bug Report

Crawl4AI tool has error
I tweaked example code from here:
https://docs.phidata.com/tools/crawl4ai
and used this code:
```
from phi.agent import Agent
from phi.model.openai import OpenAIChat
from phi.tools.crawl4ai_tools import Crawl4aiTools
from dotenv import load_dotenv

load_dotenv()

agent = Agent(
    model=OpenAIChat(id="gpt-4o"),
    tools=[Crawl4aiTools(max_length=None)],
    show_tool_calls=True
)
agent.print_response("Summarize me the key points of this: https://blog.google/products/gemini/google-gemini-deep-research/")
```
but I've got error:
```
(phidata-venv) PS D:\Projects\AI_testing\phidata> python .\crawl4ai_example.py
▰▰▱▱▱▱▱ Thinking...INFO:httpx:HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
WARNING  Could not run function web_crawler(url=https://blog.google/products/gemini/google-gemini-deep-research, max_length=500)
ERROR    'NoneType' object is not callable
         Traceback (most recent call last):
           File "D:\Projects\AI_testing\phidata\phidata-venv\Lib\site-packages\phi\tools\function.py", line 313, in execute
             self.result = self.function.entrypoint(**entrypoint_args, **self.arguments)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "D:\Projects\AI_testing\phidata\phidata-venv\Lib\site-packages\pydantic\_internal\_validate_call.py", line 38, in wrapper_function
             return wrapper(*args, **kwargs)
                    ^^^^^^^^^^^^^^^^^^^^^^^^
           File "D:\Projects\AI_testing\phidata\phidata-venv\Lib\site-packages\pydantic\_internal\_validate_call.py", line 111, in __call__
             res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           File "D:\Projects\AI_testing\phidata\phidata-venv\Lib\site-packages\phi\tools\crawl4ai_tools.py", line 35, in web_crawler
             crawler = WebCrawler(verbose=True)
                       ^^^^^^^^^^^^^^^^^^^^^^^^
         TypeError: 'NoneType' object is not callable
```

**phidata v2.7.2**
and
**crawl4ai v0.4.1**
is used.


Functions to edit:
phi/tools/crawl4ai_tools.py:Crawl4aiTools.web_crawler
Functions added: phi/tools/crawl4ai_tools.py:Crawl4aiTools._async_web_crawler

Patch:
diff --git a/phi/tools/crawl4ai_tools.py b/phi/tools/crawl4ai_tools.py
index a7ca95c78..172953744 100644
--- a/phi/tools/crawl4ai_tools.py
+++ b/phi/tools/crawl4ai_tools.py
@@ -1,9 +1,10 @@
+import asyncio
 from typing import Optional
 
 from phi.tools import Toolkit
 
 try:
-    from crawl4ai import WebCrawler
+    from crawl4ai import AsyncWebCrawler, CacheMode
 except ImportError:
     raise ImportError("`crawl4ai` not installed. Please install using `pip install crawl4ai`")
 
@@ -31,21 +32,31 @@ def web_crawler(self, url: str, max_length: Optional[int] = None) -> str:
         if url is None:
             return "No URL provided"
 
-        # Create an instance of WebCrawler
-        crawler = WebCrawler(verbose=True)
-        crawler.warmup()
+        # Run the async crawler function synchronously
+        return asyncio.run(self._async_web_crawler(url, max_length))
 
-        # Run the crawler on a URL
-        result = crawler.run(url=url)
+    async def _async_web_crawler(self, url: str, max_length: Optional[int] = None) -> str:
+        """
+        Asynchronous method to crawl a website using AsyncWebCrawler.
+
+        :param url: The URL to crawl.
+
+        :return: The results of the crawling as a markdown string, or None if no result.
+        """
+
+        async with AsyncWebCrawler(thread_safe=True) as crawler:
+            result = await crawler.arun(url=url, cache_mode=CacheMode.BYPASS)
 
-        # Determine the length to use
-        length = self.max_length or max_length
+            # Determine the length to use
+            length = self.max_length or max_length
+            if not result.markdown:
+                return "No result"
 
-        # Remove spaces and truncate if length is specified
-        if length:
-            result = result.markdown[:length]
-            result = result.replace(" ", "")
-            return result
+            # Remove spaces and truncate if length is specified
+            if length:
+                result = result.markdown[:length]
+                result = result.replace(" ", "")
+                return result
 
-        result = result.markdown.replace(" ", "")
+            result = result.markdown.replace(" ", "")
         return result