mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 17:33:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb4e1d8b9e |
No files matched your search
+1
-1
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
flask \
|
||||
"praisonai>=2.3.7" \
|
||||
"praisonai>=2.3.8" \
|
||||
"praisonai[api]" \
|
||||
gunicorn \
|
||||
markdown
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.7" \
|
||||
"praisonai>=2.3.8" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.7" \
|
||||
"praisonai>=2.3.8" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.7" \
|
||||
"praisonai>=2.3.8" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -208,6 +208,16 @@ TOOL_MAPPINGS = {
|
||||
'exa_answer_async': ('.exa_tools', None),
|
||||
'ExaTools': ('.exa_tools', 'ExaTools'),
|
||||
'exa_tools': ('.exa_tools', None),
|
||||
|
||||
# Crawl4AI Tools (async web crawling)
|
||||
'crawl4ai': ('.crawl4ai_tools', None),
|
||||
'crawl4ai_many': ('.crawl4ai_tools', None),
|
||||
'crawl4ai_extract': ('.crawl4ai_tools', None),
|
||||
'crawl4ai_llm_extract': ('.crawl4ai_tools', None),
|
||||
'crawl4ai_sync': ('.crawl4ai_tools', None),
|
||||
'crawl4ai_extract_sync': ('.crawl4ai_tools', None),
|
||||
'Crawl4AITools': ('.crawl4ai_tools', 'Crawl4AITools'),
|
||||
'crawl4ai_tools': ('.crawl4ai_tools', None),
|
||||
}
|
||||
|
||||
_instances = {} # Cache for class instances
|
||||
@@ -219,8 +229,8 @@ def __getattr__(name: str) -> Any:
|
||||
|
||||
module_path, class_name = TOOL_MAPPINGS[name]
|
||||
|
||||
# Return class itself (not instance) for ClaudeMemoryTool, TavilyTools, YouTools, ExaTools
|
||||
if name in ('ClaudeMemoryTool', 'TavilyTools', 'YouTools', 'ExaTools'):
|
||||
# Return class itself (not instance) for ClaudeMemoryTool, TavilyTools, YouTools, ExaTools, Crawl4AITools
|
||||
if name in ('ClaudeMemoryTool', 'TavilyTools', 'YouTools', 'ExaTools', 'Crawl4AITools'):
|
||||
module = import_module(module_path, __package__)
|
||||
return getattr(module, class_name)
|
||||
|
||||
@@ -242,11 +252,13 @@ def __getattr__(name: str) -> Any:
|
||||
'tavily_search_async', 'tavily_extract_async',
|
||||
'ydc', 'ydc_search', 'ydc_contents', 'ydc_news', 'ydc_images',
|
||||
'exa', 'exa_search', 'exa_search_contents', 'exa_find_similar', 'exa_answer',
|
||||
'exa_search_async', 'exa_search_contents_async', 'exa_answer_async'
|
||||
'exa_search_async', 'exa_search_contents_async', 'exa_answer_async',
|
||||
'crawl4ai', 'crawl4ai_many', 'crawl4ai_extract', 'crawl4ai_llm_extract',
|
||||
'crawl4ai_sync', 'crawl4ai_extract_sync'
|
||||
]:
|
||||
return getattr(module, name)
|
||||
if name in ['file_tools', 'pandas_tools', 'wikipedia_tools',
|
||||
'newspaper_tools', 'arxiv_tools', 'spider_tools', 'duckdb_tools', 'mongodb_tools', 'csv_tools', 'json_tools', 'excel_tools', 'xml_tools', 'yaml_tools', 'calculator_tools', 'python_tools', 'shell_tools', 'cot_tools', 'tavily_tools', 'youdotcom_tools', 'exa_tools']:
|
||||
'newspaper_tools', 'arxiv_tools', 'spider_tools', 'duckdb_tools', 'mongodb_tools', 'csv_tools', 'json_tools', 'excel_tools', 'xml_tools', 'yaml_tools', 'calculator_tools', 'python_tools', 'shell_tools', 'cot_tools', 'tavily_tools', 'youdotcom_tools', 'exa_tools', 'crawl4ai_tools']:
|
||||
return module # Returns the callable module
|
||||
return getattr(module, name)
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,630 @@
|
||||
"""Crawl4AI web crawling and extraction tools.
|
||||
|
||||
Crawl4AI provides async web crawling with JavaScript rendering, content extraction,
|
||||
and LLM-based data extraction capabilities.
|
||||
|
||||
Usage:
|
||||
from praisonaiagents.tools import crawl4ai, crawl4ai_extract
|
||||
|
||||
# Simple crawl
|
||||
result = await crawl4ai("https://example.com")
|
||||
print(result["markdown"])
|
||||
|
||||
# Extract structured data
|
||||
result = await crawl4ai_extract(
|
||||
"https://example.com/products",
|
||||
schema={"name": "Products", "baseSelector": ".product", ...}
|
||||
)
|
||||
|
||||
# Or use the class directly
|
||||
from praisonaiagents.tools import Crawl4AITools
|
||||
tools = Crawl4AITools()
|
||||
result = await tools.crawl("https://example.com")
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Any, Optional, Union
|
||||
import logging
|
||||
import asyncio
|
||||
from importlib import util
|
||||
|
||||
|
||||
def _check_crawl4ai_available() -> tuple[bool, Optional[str]]:
|
||||
"""Check if Crawl4AI is available.
|
||||
|
||||
Returns:
|
||||
Tuple of (is_available, error_message)
|
||||
"""
|
||||
if util.find_spec("crawl4ai") is None:
|
||||
return False, "crawl4ai package is not installed. Install it with: pip install crawl4ai && crawl4ai-setup"
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
class Crawl4AITools:
|
||||
"""Crawl4AI web crawling and extraction tools.
|
||||
|
||||
Provides methods for:
|
||||
- crawl: Basic web crawling with markdown output
|
||||
- crawl_many: Crawl multiple URLs concurrently
|
||||
- extract_css: Extract data using CSS selectors
|
||||
- extract_llm: Extract data using LLM
|
||||
|
||||
Example:
|
||||
from praisonaiagents.tools import Crawl4AITools
|
||||
|
||||
tools = Crawl4AITools()
|
||||
result = await tools.crawl("https://example.com")
|
||||
print(result["markdown"])
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
headless: bool = True,
|
||||
verbose: bool = False,
|
||||
browser_type: str = "chromium"
|
||||
):
|
||||
"""Initialize Crawl4AITools.
|
||||
|
||||
Args:
|
||||
headless: Run browser in headless mode (default True)
|
||||
verbose: Enable verbose logging (default False)
|
||||
browser_type: Browser type - "chromium", "firefox", or "webkit"
|
||||
"""
|
||||
self.headless = headless
|
||||
self.verbose = verbose
|
||||
self.browser_type = browser_type
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self._crawler = None
|
||||
|
||||
async def _get_crawler(self):
|
||||
"""Get or create AsyncWebCrawler instance."""
|
||||
is_available, error = _check_crawl4ai_available()
|
||||
if not is_available:
|
||||
raise ImportError(error)
|
||||
|
||||
from crawl4ai import AsyncWebCrawler, BrowserConfig
|
||||
|
||||
if self._crawler is None:
|
||||
browser_config = BrowserConfig(
|
||||
headless=self.headless,
|
||||
verbose=self.verbose,
|
||||
browser_type=self.browser_type
|
||||
)
|
||||
self._crawler = AsyncWebCrawler(config=browser_config)
|
||||
await self._crawler.start()
|
||||
|
||||
return self._crawler
|
||||
|
||||
async def close(self):
|
||||
"""Close the crawler and release resources."""
|
||||
if self._crawler is not None:
|
||||
await self._crawler.close()
|
||||
self._crawler = None
|
||||
|
||||
async def crawl(
|
||||
self,
|
||||
url: str,
|
||||
word_count_threshold: int = 10,
|
||||
css_selector: Optional[str] = None,
|
||||
excluded_tags: Optional[List[str]] = None,
|
||||
exclude_external_links: bool = False,
|
||||
js_code: Optional[Union[str, List[str]]] = None,
|
||||
wait_for: Optional[str] = None,
|
||||
screenshot: bool = False,
|
||||
pdf: bool = False,
|
||||
bypass_cache: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""Crawl a single URL and return content.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl
|
||||
word_count_threshold: Minimum words per text block (default 10)
|
||||
css_selector: CSS selector to focus on specific content
|
||||
excluded_tags: HTML tags to exclude (e.g., ["nav", "footer"])
|
||||
exclude_external_links: Remove external links from content
|
||||
js_code: JavaScript code to execute before crawling
|
||||
wait_for: Wait condition - "css:selector" or "js:() => boolean"
|
||||
screenshot: Capture screenshot (base64)
|
||||
pdf: Generate PDF
|
||||
bypass_cache: Skip cache and fetch fresh content (default True)
|
||||
|
||||
Returns:
|
||||
Dict with 'markdown', 'html', 'cleaned_html', 'links', 'media', etc.
|
||||
"""
|
||||
try:
|
||||
crawler = await self._get_crawler()
|
||||
|
||||
from crawl4ai import CrawlerRunConfig, CacheMode
|
||||
|
||||
config = CrawlerRunConfig(
|
||||
word_count_threshold=word_count_threshold,
|
||||
css_selector=css_selector,
|
||||
excluded_tags=excluded_tags or [],
|
||||
exclude_external_links=exclude_external_links,
|
||||
js_code=js_code,
|
||||
wait_for=wait_for,
|
||||
screenshot=screenshot,
|
||||
pdf=pdf,
|
||||
cache_mode=CacheMode.BYPASS if bypass_cache else CacheMode.ENABLED
|
||||
)
|
||||
|
||||
result = await crawler.arun(url=url, config=config)
|
||||
|
||||
if not result.success:
|
||||
return {"error": f"Crawl failed: {result.error_message}", "success": False}
|
||||
|
||||
response = {
|
||||
"success": True,
|
||||
"url": result.url,
|
||||
"markdown": result.markdown.raw_markdown if hasattr(result.markdown, 'raw_markdown') else str(result.markdown),
|
||||
"html": result.html,
|
||||
"cleaned_html": result.cleaned_html,
|
||||
"links": result.links,
|
||||
"media": result.media,
|
||||
"metadata": result.metadata
|
||||
}
|
||||
|
||||
if hasattr(result.markdown, 'fit_markdown') and result.markdown.fit_markdown:
|
||||
response["fit_markdown"] = result.markdown.fit_markdown
|
||||
|
||||
if screenshot and result.screenshot:
|
||||
response["screenshot"] = result.screenshot
|
||||
|
||||
if pdf and result.pdf:
|
||||
response["pdf"] = result.pdf
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Crawl4AI crawl error: {e}")
|
||||
return {"error": f"Crawl4AI crawl error: {str(e)}", "success": False}
|
||||
|
||||
async def crawl_many(
|
||||
self,
|
||||
urls: List[str],
|
||||
word_count_threshold: int = 10,
|
||||
css_selector: Optional[str] = None,
|
||||
bypass_cache: bool = True,
|
||||
stream: bool = False
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Crawl multiple URLs concurrently.
|
||||
|
||||
Args:
|
||||
urls: List of URLs to crawl
|
||||
word_count_threshold: Minimum words per text block
|
||||
css_selector: CSS selector to focus on specific content
|
||||
bypass_cache: Skip cache and fetch fresh content
|
||||
stream: If True, returns async generator
|
||||
|
||||
Returns:
|
||||
List of crawl results
|
||||
"""
|
||||
try:
|
||||
crawler = await self._get_crawler()
|
||||
|
||||
from crawl4ai import CrawlerRunConfig, CacheMode
|
||||
|
||||
config = CrawlerRunConfig(
|
||||
word_count_threshold=word_count_threshold,
|
||||
css_selector=css_selector,
|
||||
cache_mode=CacheMode.BYPASS if bypass_cache else CacheMode.ENABLED,
|
||||
stream=stream
|
||||
)
|
||||
|
||||
results = []
|
||||
|
||||
if stream:
|
||||
async for result in await crawler.arun_many(urls, config=config):
|
||||
if result.success:
|
||||
results.append({
|
||||
"success": True,
|
||||
"url": result.url,
|
||||
"markdown": result.markdown.raw_markdown if hasattr(result.markdown, 'raw_markdown') else str(result.markdown),
|
||||
"html": result.html
|
||||
})
|
||||
else:
|
||||
results.append({
|
||||
"success": False,
|
||||
"url": result.url,
|
||||
"error": result.error_message
|
||||
})
|
||||
else:
|
||||
raw_results = await crawler.arun_many(urls, config=config)
|
||||
for result in raw_results:
|
||||
if result.success:
|
||||
results.append({
|
||||
"success": True,
|
||||
"url": result.url,
|
||||
"markdown": result.markdown.raw_markdown if hasattr(result.markdown, 'raw_markdown') else str(result.markdown),
|
||||
"html": result.html
|
||||
})
|
||||
else:
|
||||
results.append({
|
||||
"success": False,
|
||||
"url": result.url,
|
||||
"error": result.error_message
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Crawl4AI crawl_many error: {e}")
|
||||
return [{"error": f"Crawl4AI crawl_many error: {str(e)}", "success": False}]
|
||||
|
||||
async def extract_css(
|
||||
self,
|
||||
url: str,
|
||||
schema: Dict[str, Any],
|
||||
js_code: Optional[Union[str, List[str]]] = None,
|
||||
wait_for: Optional[str] = None,
|
||||
bypass_cache: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""Extract structured data using CSS selectors.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl (or "raw://<html>" for raw HTML)
|
||||
schema: Extraction schema with baseSelector and fields
|
||||
js_code: JavaScript to execute before extraction
|
||||
wait_for: Wait condition before extraction
|
||||
bypass_cache: Skip cache
|
||||
|
||||
Returns:
|
||||
Dict with 'data' containing extracted items
|
||||
|
||||
Example schema:
|
||||
{
|
||||
"name": "Products",
|
||||
"baseSelector": "div.product",
|
||||
"fields": [
|
||||
{"name": "title", "selector": "h2", "type": "text"},
|
||||
{"name": "price", "selector": ".price", "type": "text"},
|
||||
{"name": "link", "selector": "a", "type": "attribute", "attribute": "href"}
|
||||
]
|
||||
}
|
||||
"""
|
||||
try:
|
||||
crawler = await self._get_crawler()
|
||||
|
||||
from crawl4ai import CrawlerRunConfig, CacheMode
|
||||
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
|
||||
|
||||
extraction_strategy = JsonCssExtractionStrategy(schema, verbose=self.verbose)
|
||||
|
||||
config = CrawlerRunConfig(
|
||||
extraction_strategy=extraction_strategy,
|
||||
js_code=js_code,
|
||||
wait_for=wait_for,
|
||||
cache_mode=CacheMode.BYPASS if bypass_cache else CacheMode.ENABLED
|
||||
)
|
||||
|
||||
result = await crawler.arun(url=url, config=config)
|
||||
|
||||
if not result.success:
|
||||
return {"error": f"Extraction failed: {result.error_message}", "success": False}
|
||||
|
||||
import json
|
||||
try:
|
||||
data = json.loads(result.extracted_content) if result.extracted_content else []
|
||||
except json.JSONDecodeError:
|
||||
data = result.extracted_content
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"url": result.url,
|
||||
"data": data,
|
||||
"count": len(data) if isinstance(data, list) else 1
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Crawl4AI extract_css error: {e}")
|
||||
return {"error": f"Crawl4AI extract_css error: {str(e)}", "success": False}
|
||||
|
||||
async def extract_llm(
|
||||
self,
|
||||
url: str,
|
||||
instruction: str,
|
||||
schema: Optional[Dict[str, Any]] = None,
|
||||
provider: str = "openai/gpt-4o-mini",
|
||||
api_token: Optional[str] = None,
|
||||
js_code: Optional[Union[str, List[str]]] = None,
|
||||
wait_for: Optional[str] = None,
|
||||
bypass_cache: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""Extract structured data using LLM.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl
|
||||
instruction: Instructions for the LLM on what to extract
|
||||
schema: Optional Pydantic-style JSON schema for structured output
|
||||
provider: LLM provider string (e.g., "openai/gpt-4o-mini", "ollama/llama3.3")
|
||||
api_token: API token for the provider (uses env var if not provided)
|
||||
js_code: JavaScript to execute before extraction
|
||||
wait_for: Wait condition before extraction
|
||||
bypass_cache: Skip cache
|
||||
|
||||
Returns:
|
||||
Dict with 'data' containing extracted content
|
||||
"""
|
||||
try:
|
||||
crawler = await self._get_crawler()
|
||||
|
||||
import os
|
||||
from crawl4ai import CrawlerRunConfig, CacheMode, LLMConfig
|
||||
from crawl4ai.extraction_strategy import LLMExtractionStrategy
|
||||
|
||||
# Get API token from env if not provided
|
||||
if api_token is None and "openai" in provider.lower():
|
||||
api_token = os.environ.get("OPENAI_API_KEY")
|
||||
|
||||
llm_config = LLMConfig(provider=provider, api_token=api_token)
|
||||
|
||||
extraction_strategy = LLMExtractionStrategy(
|
||||
llm_config=llm_config,
|
||||
schema=schema,
|
||||
extraction_type="schema" if schema else "block",
|
||||
instruction=instruction,
|
||||
verbose=self.verbose
|
||||
)
|
||||
|
||||
config = CrawlerRunConfig(
|
||||
extraction_strategy=extraction_strategy,
|
||||
js_code=js_code,
|
||||
wait_for=wait_for,
|
||||
cache_mode=CacheMode.BYPASS if bypass_cache else CacheMode.ENABLED
|
||||
)
|
||||
|
||||
result = await crawler.arun(url=url, config=config)
|
||||
|
||||
if not result.success:
|
||||
return {"error": f"LLM extraction failed: {result.error_message}", "success": False}
|
||||
|
||||
import json
|
||||
try:
|
||||
data = json.loads(result.extracted_content) if result.extracted_content else None
|
||||
except json.JSONDecodeError:
|
||||
data = result.extracted_content
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"url": result.url,
|
||||
"data": data
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Crawl4AI extract_llm error: {e}")
|
||||
return {"error": f"Crawl4AI extract_llm error: {str(e)}", "success": False}
|
||||
|
||||
|
||||
# Standalone async functions for easy access
|
||||
|
||||
async def crawl4ai(
|
||||
url: str,
|
||||
css_selector: Optional[str] = None,
|
||||
js_code: Optional[str] = None,
|
||||
wait_for: Optional[str] = None,
|
||||
screenshot: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
"""Crawl a URL and return markdown content.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl
|
||||
css_selector: CSS selector to focus on specific content
|
||||
js_code: JavaScript to execute before crawling
|
||||
wait_for: Wait condition - "css:selector" or "js:() => boolean"
|
||||
screenshot: Capture screenshot
|
||||
|
||||
Returns:
|
||||
Dict with 'markdown', 'html', 'links', 'media', etc.
|
||||
|
||||
Example:
|
||||
result = await crawl4ai("https://example.com")
|
||||
print(result["markdown"])
|
||||
"""
|
||||
is_available, error = _check_crawl4ai_available()
|
||||
if not is_available:
|
||||
logging.error(error)
|
||||
return {"error": error, "success": False}
|
||||
|
||||
tools = Crawl4AITools()
|
||||
try:
|
||||
result = await tools.crawl(
|
||||
url=url,
|
||||
css_selector=css_selector,
|
||||
js_code=js_code,
|
||||
wait_for=wait_for,
|
||||
screenshot=screenshot
|
||||
)
|
||||
return result
|
||||
finally:
|
||||
await tools.close()
|
||||
|
||||
|
||||
async def crawl4ai_many(
|
||||
urls: List[str],
|
||||
css_selector: Optional[str] = None
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""Crawl multiple URLs concurrently.
|
||||
|
||||
Args:
|
||||
urls: List of URLs to crawl
|
||||
css_selector: CSS selector to focus on specific content
|
||||
|
||||
Returns:
|
||||
List of crawl results
|
||||
|
||||
Example:
|
||||
results = await crawl4ai_many(["https://example.com", "https://example.org"])
|
||||
"""
|
||||
is_available, error = _check_crawl4ai_available()
|
||||
if not is_available:
|
||||
logging.error(error)
|
||||
return [{"error": error, "success": False}]
|
||||
|
||||
tools = Crawl4AITools()
|
||||
try:
|
||||
return await tools.crawl_many(urls=urls, css_selector=css_selector)
|
||||
finally:
|
||||
await tools.close()
|
||||
|
||||
|
||||
async def crawl4ai_extract(
|
||||
url: str,
|
||||
schema: Dict[str, Any],
|
||||
js_code: Optional[str] = None,
|
||||
wait_for: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Extract structured data using CSS selectors.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl
|
||||
schema: Extraction schema with baseSelector and fields
|
||||
js_code: JavaScript to execute before extraction
|
||||
wait_for: Wait condition before extraction
|
||||
|
||||
Returns:
|
||||
Dict with 'data' containing extracted items
|
||||
|
||||
Example:
|
||||
schema = {
|
||||
"name": "Products",
|
||||
"baseSelector": "div.product",
|
||||
"fields": [
|
||||
{"name": "title", "selector": "h2", "type": "text"},
|
||||
{"name": "price", "selector": ".price", "type": "text"}
|
||||
]
|
||||
}
|
||||
result = await crawl4ai_extract("https://example.com/products", schema)
|
||||
"""
|
||||
is_available, error = _check_crawl4ai_available()
|
||||
if not is_available:
|
||||
logging.error(error)
|
||||
return {"error": error, "success": False}
|
||||
|
||||
tools = Crawl4AITools()
|
||||
try:
|
||||
return await tools.extract_css(
|
||||
url=url,
|
||||
schema=schema,
|
||||
js_code=js_code,
|
||||
wait_for=wait_for
|
||||
)
|
||||
finally:
|
||||
await tools.close()
|
||||
|
||||
|
||||
async def crawl4ai_llm_extract(
|
||||
url: str,
|
||||
instruction: str,
|
||||
schema: Optional[Dict[str, Any]] = None,
|
||||
provider: str = "openai/gpt-4o-mini"
|
||||
) -> Dict[str, Any]:
|
||||
"""Extract data using LLM.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl
|
||||
instruction: Instructions for the LLM
|
||||
schema: Optional JSON schema for structured output
|
||||
provider: LLM provider (default "openai/gpt-4o-mini")
|
||||
|
||||
Returns:
|
||||
Dict with 'data' containing extracted content
|
||||
|
||||
Example:
|
||||
result = await crawl4ai_llm_extract(
|
||||
"https://example.com/about",
|
||||
instruction="Extract company name, founding year, and description"
|
||||
)
|
||||
"""
|
||||
is_available, error = _check_crawl4ai_available()
|
||||
if not is_available:
|
||||
logging.error(error)
|
||||
return {"error": error, "success": False}
|
||||
|
||||
tools = Crawl4AITools()
|
||||
try:
|
||||
return await tools.extract_llm(
|
||||
url=url,
|
||||
instruction=instruction,
|
||||
schema=schema,
|
||||
provider=provider
|
||||
)
|
||||
finally:
|
||||
await tools.close()
|
||||
|
||||
|
||||
# Synchronous wrappers for convenience
|
||||
|
||||
def crawl4ai_sync(
|
||||
url: str,
|
||||
css_selector: Optional[str] = None,
|
||||
js_code: Optional[str] = None,
|
||||
wait_for: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Synchronous version of crawl4ai.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl
|
||||
css_selector: CSS selector to focus on specific content
|
||||
js_code: JavaScript to execute
|
||||
wait_for: Wait condition
|
||||
|
||||
Returns:
|
||||
Dict with crawl results
|
||||
"""
|
||||
return asyncio.get_event_loop().run_until_complete(
|
||||
crawl4ai(url, css_selector, js_code, wait_for)
|
||||
)
|
||||
|
||||
|
||||
def crawl4ai_extract_sync(
|
||||
url: str,
|
||||
schema: Dict[str, Any],
|
||||
js_code: Optional[str] = None
|
||||
) -> Dict[str, Any]:
|
||||
"""Synchronous version of crawl4ai_extract.
|
||||
|
||||
Args:
|
||||
url: The URL to crawl
|
||||
schema: Extraction schema
|
||||
js_code: JavaScript to execute
|
||||
|
||||
Returns:
|
||||
Dict with extracted data
|
||||
"""
|
||||
return asyncio.get_event_loop().run_until_complete(
|
||||
crawl4ai_extract(url, schema, js_code)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("Crawl4AI Tools - Example Usage")
|
||||
print("=" * 60)
|
||||
|
||||
is_available, error = _check_crawl4ai_available()
|
||||
if not is_available:
|
||||
print(f"\nCrawl4AI is not available: {error}")
|
||||
print("\nTo use Crawl4AI tools:")
|
||||
print("1. Install the package: pip install crawl4ai")
|
||||
print("2. Run setup: crawl4ai-setup")
|
||||
else:
|
||||
print("Crawl4AI is available!")
|
||||
|
||||
# Example crawl
|
||||
print("\n1. Basic Crawl")
|
||||
print("-" * 30)
|
||||
result = await crawl4ai("https://example.com")
|
||||
if result.get("success"):
|
||||
print(f"URL: {result['url']}")
|
||||
print(f"Markdown length: {len(result.get('markdown', ''))}")
|
||||
print(f"Links found: {len(result.get('links', {}).get('internal', []))}")
|
||||
else:
|
||||
print(f"Error: {result.get('error')}")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Demonstration Complete")
|
||||
print("=" * 60)
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.185"
|
||||
version = "0.0.187"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
|
||||
Generated
+1
-1
@@ -2422,7 +2422,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.185"
|
||||
version = "0.0.187"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
@@ -3,8 +3,8 @@ class Praisonai < Formula
|
||||
|
||||
desc "AI tools for various AI applications"
|
||||
homepage "https://github.com/MervinPraison/PraisonAI"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.7.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.7.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.8.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.8.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -57,7 +57,7 @@ class CloudDeployer:
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.7 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.8 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "2.3.7"
|
||||
__version__ = "2.3.8"
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.0.185",
|
||||
"praisonaiagents>=0.0.187",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
|
||||
Generated
+4
-4
@@ -4197,7 +4197,7 @@ requires-dist = [
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen-v4'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.185" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.187" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = "==0.2.29" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -4252,7 +4252,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.185"
|
||||
version = "0.0.187"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -4261,9 +4261,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/b9/e04ca0b88ccd779820f895c773afdb124a01242fe3e4f4831f24eea951de/praisonaiagents-0.0.185.tar.gz", hash = "sha256:1213893e3a690e64c5ef3f0991cd230ec94b052114340ebcf0b84a6c3797a41a", size = 425649, upload-time = "2025-12-16T07:44:28.169Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d9/81/64f01ac3755126842de60144fe5092d645b21a9536b944533ab602b10ec6/praisonaiagents-0.0.187.tar.gz", hash = "sha256:26c8dcb40d24a56c01db40e737dda53ba0175f58f9cef9327103afff96a88e39", size = 429535, upload-time = "2025-12-16T07:53:14.206Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/04/9f60d36df1b3340847fd7b930c99f101678d7b8a0c301024d7940e771aa8/praisonaiagents-0.0.185-py3-none-any.whl", hash = "sha256:bad408a451b8e26957fa444caa305ff8a784461a67dd8a71e0507af533fa7972", size = 410732, upload-time = "2025-12-16T07:44:26.525Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/bb/eaa82f06c0cf1503bb478ec20e2fc85a0a632dceeeacae03b10d7ff29bd8/praisonaiagents-0.0.187-py3-none-any.whl", hash = "sha256:6776997544673e06a55860c558270b43c4b73fce72f105ca1ad6b0f3724b49f2", size = 415005, upload-time = "2025-12-16T07:53:12.564Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user