Spaces:
Sleeping
Sleeping
| import requests | |
| import json | |
| import time | |
| import sys | |
| import base64 | |
| import os | |
| from typing import Dict, Any | |
| class Crawl4AiTester: | |
| def __init__(self, base_url: str = "http://localhost:11235"): | |
| self.base_url = base_url | |
| def submit_and_wait(self, request_data: Dict[str, Any], timeout: int = 300) -> Dict[str, Any]: | |
| # Submit crawl job | |
| response = requests.post(f"{self.base_url}/crawl", json=request_data) | |
| task_id = response.json()["task_id"] | |
| print(f"Task ID: {task_id}") | |
| # Poll for result | |
| start_time = time.time() | |
| while True: | |
| if time.time() - start_time > timeout: | |
| raise TimeoutError(f"Task {task_id} did not complete within {timeout} seconds") | |
| result = requests.get(f"{self.base_url}/task/{task_id}") | |
| status = result.json() | |
| if status["status"] == "failed": | |
| print("Task failed:", status.get("error")) | |
| raise Exception(f"Task failed: {status.get('error')}") | |
| if status["status"] == "completed": | |
| return status | |
| time.sleep(2) | |
| def test_docker_deployment(version="basic"): | |
| tester = Crawl4AiTester() | |
| print(f"Testing Crawl4AI Docker {version} version") | |
| # Health check with timeout and retry | |
| max_retries = 5 | |
| for i in range(max_retries): | |
| try: | |
| health = requests.get(f"{tester.base_url}/health", timeout=10) | |
| print("Health check:", health.json()) | |
| break | |
| except requests.exceptions.RequestException as e: | |
| if i == max_retries - 1: | |
| print(f"Failed to connect after {max_retries} attempts") | |
| sys.exit(1) | |
| print(f"Waiting for service to start (attempt {i+1}/{max_retries})...") | |
| time.sleep(5) | |
| # Test cases based on version | |
| test_basic_crawl(tester) | |
| # if version in ["full", "transformer"]: | |
| # test_cosine_extraction(tester) | |
| # test_js_execution(tester) | |
| # test_css_selector(tester) | |
| # test_structured_extraction(tester) | |
| # test_llm_extraction(tester) | |
| # test_llm_with_ollama(tester) | |
| # test_screenshot(tester) | |
| def test_basic_crawl(tester: Crawl4AiTester): | |
| print("\n=== Testing Basic Crawl ===") | |
| request = { | |
| "urls": "https://www.nbcnews.com/business", | |
| "priority": 10 | |
| } | |
| result = tester.submit_and_wait(request) | |
| print(f"Basic crawl result length: {len(result['result']['markdown'])}") | |
| assert result["result"]["success"] | |
| assert len(result["result"]["markdown"]) > 0 | |
| def test_js_execution(tester: Crawl4AiTester): | |
| print("\n=== Testing JS Execution ===") | |
| request = { | |
| "urls": "https://www.nbcnews.com/business", | |
| "priority": 8, | |
| "js_code": [ | |
| "const loadMoreButton = Array.from(document.querySelectorAll('button')).find(button => button.textContent.includes('Load More')); loadMoreButton && loadMoreButton.click();" | |
| ], | |
| "wait_for": "article.tease-card:nth-child(10)", | |
| "crawler_params": { | |
| "headless": True | |
| } | |
| } | |
| result = tester.submit_and_wait(request) | |
| print(f"JS execution result length: {len(result['result']['markdown'])}") | |
| assert result["result"]["success"] | |
| def test_css_selector(tester: Crawl4AiTester): | |
| print("\n=== Testing CSS Selector ===") | |
| request = { | |
| "urls": "https://www.nbcnews.com/business", | |
| "priority": 7, | |
| "css_selector": ".wide-tease-item__description", | |
| "crawler_params": { | |
| "headless": True | |
| }, | |
| "extra": {"word_count_threshold": 10} | |
| } | |
| result = tester.submit_and_wait(request) | |
| print(f"CSS selector result length: {len(result['result']['markdown'])}") | |
| assert result["result"]["success"] | |
| def test_structured_extraction(tester: Crawl4AiTester): | |
| print("\n=== Testing Structured Extraction ===") | |
| schema = { | |
| "name": "Coinbase Crypto Prices", | |
| "baseSelector": ".cds-tableRow-t45thuk", | |
| "fields": [ | |
| { | |
| "name": "crypto", | |
| "selector": "td:nth-child(1) h2", | |
| "type": "text", | |
| }, | |
| { | |
| "name": "symbol", | |
| "selector": "td:nth-child(1) p", | |
| "type": "text", | |
| }, | |
| { | |
| "name": "price", | |
| "selector": "td:nth-child(2)", | |
| "type": "text", | |
| } | |
| ], | |
| } | |
| request = { | |
| "urls": "https://www.coinbase.com/explore", | |
| "priority": 9, | |
| "extraction_config": { | |
| "type": "json_css", | |
| "params": { | |
| "schema": schema | |
| } | |
| } | |
| } | |
| result = tester.submit_and_wait(request) | |
| extracted = json.loads(result["result"]["extracted_content"]) | |
| print(f"Extracted {len(extracted)} items") | |
| print("Sample item:", json.dumps(extracted[0], indent=2)) | |
| assert result["result"]["success"] | |
| assert len(extracted) > 0 | |
| def test_llm_extraction(tester: Crawl4AiTester): | |
| print("\n=== Testing LLM Extraction ===") | |
| schema = { | |
| "type": "object", | |
| "properties": { | |
| "model_name": { | |
| "type": "string", | |
| "description": "Name of the OpenAI model." | |
| }, | |
| "input_fee": { | |
| "type": "string", | |
| "description": "Fee for input token for the OpenAI model." | |
| }, | |
| "output_fee": { | |
| "type": "string", | |
| "description": "Fee for output token for the OpenAI model." | |
| } | |
| }, | |
| "required": ["model_name", "input_fee", "output_fee"] | |
| } | |
| request = { | |
| "urls": "https://openai.com/api/pricing", | |
| "priority": 8, | |
| "extraction_config": { | |
| "type": "llm", | |
| "params": { | |
| "provider": "openai/gpt-4o-mini", | |
| "api_token": os.getenv("OPENAI_API_KEY"), | |
| "schema": schema, | |
| "extraction_type": "schema", | |
| "instruction": """From the crawled content, extract all mentioned model names along with their fees for input and output tokens.""" | |
| } | |
| }, | |
| "crawler_params": {"word_count_threshold": 1} | |
| } | |
| try: | |
| result = tester.submit_and_wait(request) | |
| extracted = json.loads(result["result"]["extracted_content"]) | |
| print(f"Extracted {len(extracted)} model pricing entries") | |
| print("Sample entry:", json.dumps(extracted[0], indent=2)) | |
| assert result["result"]["success"] | |
| except Exception as e: | |
| print(f"LLM extraction test failed (might be due to missing API key): {str(e)}") | |
| def test_llm_with_ollama(tester: Crawl4AiTester): | |
| print("\n=== Testing LLM with Ollama ===") | |
| schema = { | |
| "type": "object", | |
| "properties": { | |
| "article_title": { | |
| "type": "string", | |
| "description": "The main title of the news article" | |
| }, | |
| "summary": { | |
| "type": "string", | |
| "description": "A brief summary of the article content" | |
| }, | |
| "main_topics": { | |
| "type": "array", | |
| "items": {"type": "string"}, | |
| "description": "Main topics or themes discussed in the article" | |
| } | |
| } | |
| } | |
| request = { | |
| "urls": "https://www.nbcnews.com/business", | |
| "priority": 8, | |
| "extraction_config": { | |
| "type": "llm", | |
| "params": { | |
| "provider": "ollama/llama2", | |
| "schema": schema, | |
| "extraction_type": "schema", | |
| "instruction": "Extract the main article information including title, summary, and main topics." | |
| } | |
| }, | |
| "extra": {"word_count_threshold": 1}, | |
| "crawler_params": {"verbose": True} | |
| } | |
| try: | |
| result = tester.submit_and_wait(request) | |
| extracted = json.loads(result["result"]["extracted_content"]) | |
| print("Extracted content:", json.dumps(extracted, indent=2)) | |
| assert result["result"]["success"] | |
| except Exception as e: | |
| print(f"Ollama extraction test failed: {str(e)}") | |
| def test_cosine_extraction(tester: Crawl4AiTester): | |
| print("\n=== Testing Cosine Extraction ===") | |
| request = { | |
| "urls": "https://www.nbcnews.com/business", | |
| "priority": 8, | |
| "extraction_config": { | |
| "type": "cosine", | |
| "params": { | |
| "semantic_filter": "business finance economy", | |
| "word_count_threshold": 10, | |
| "max_dist": 0.2, | |
| "top_k": 3 | |
| } | |
| } | |
| } | |
| try: | |
| result = tester.submit_and_wait(request) | |
| extracted = json.loads(result["result"]["extracted_content"]) | |
| print(f"Extracted {len(extracted)} text clusters") | |
| print("First cluster tags:", extracted[0]["tags"]) | |
| assert result["result"]["success"] | |
| except Exception as e: | |
| print(f"Cosine extraction test failed: {str(e)}") | |
| def test_screenshot(tester: Crawl4AiTester): | |
| print("\n=== Testing Screenshot ===") | |
| request = { | |
| "urls": "https://www.nbcnews.com/business", | |
| "priority": 5, | |
| "screenshot": True, | |
| "crawler_params": { | |
| "headless": True | |
| } | |
| } | |
| result = tester.submit_and_wait(request) | |
| print("Screenshot captured:", bool(result["result"]["screenshot"])) | |
| if result["result"]["screenshot"]: | |
| # Save screenshot | |
| screenshot_data = base64.b64decode(result["result"]["screenshot"]) | |
| with open("test_screenshot.jpg", "wb") as f: | |
| f.write(screenshot_data) | |
| print("Screenshot saved as test_screenshot.jpg") | |
| assert result["result"]["success"] | |
| if __name__ == "__main__": | |
| version = sys.argv[1] if len(sys.argv) > 1 else "basic" | |
| # version = "full" | |
| test_docker_deployment(version) |