diff --git a/src/controllers/health_controller.py b/src/controllers/health_controller.py index 9a237da..5491b46 100644 --- a/src/controllers/health_controller.py +++ b/src/controllers/health_controller.py @@ -61,7 +61,7 @@ class HealthController(BaseController): "chat_completions": "/v1/chat/completions", "models": "/v1/models", "conversations": "/v1/conversations", - "web_scraper": "/web-scraper/scrape", + "dns_lookup": "/tools/dns/lookup", "infrastructure": "/infrastructure", "health": "/health", "health_full": "/health/full" diff --git a/src/controllers/tools_controller.py b/src/controllers/tools_controller.py index 7313772..d908880 100644 --- a/src/controllers/tools_controller.py +++ b/src/controllers/tools_controller.py @@ -2,16 +2,12 @@ Tools Controller Provides utility tool endpoints including: -- Web scraping and content extraction - DNS lookups """ from fastapi import APIRouter, HTTPException, status from src.controllers.base import BaseController from src.logging_config import get_logger -from src.web_scraper.schemas import WebScraperRequest, WebScraperResponse -from src.web_scraper.service import WebScraperService -from src.web_scraper.exceptions import FetchError, ScrapingError from src.dns.schemas import DNSLookupRequest, DNSLookupResponse from src.dns.service import DNSService from src.dns.exceptions import DNSQueryError @@ -24,79 +20,17 @@ class ToolsController(BaseController): Controller for utility tools Provides endpoints for: - - Web scraping and content extraction - DNS lookups """ def __init__(self): super().__init__(prefix="/tools", tags=["Tools"]) - # Initialize services (could be dependency injected for testing) - self.scraper_service = WebScraperService() self.dns_service = DNSService() def create_router(self) -> APIRouter: """Create and configure the router""" router = APIRouter(prefix=self.prefix, tags=self.tags) - @router.post( - "/scrape", - response_model=WebScraperResponse, - status_code=status.HTTP_200_OK, - summary="Scrape website content", - description=""" - Scrape and extract main content from a website. - - Uses trafilatura for intelligent content extraction (articles, blog posts, documentation), - with BeautifulSoup as fallback. Perfect for feeding webpage content to LLMs. - - **Features:** - - Intelligent main content extraction - - Removes navigation, ads, footers - - Optional link extraction - - Configurable content length limits - - **Rate Limiting:** None (internal network use only) - """ - ) - async def scrape_website(request: WebScraperRequest) -> WebScraperResponse: - """ - Scrape a website and extract its main content - - Args: - request: Scraping request with URL and options - - Returns: - Extracted content with metadata - - Raises: - HTTPException: 400 for fetch errors, 500 for processing errors - """ - try: - logger.info(f"Received scrape request for: {request.url}") - result = await self.scraper_service.scrape_url(request) - return result - - except FetchError as e: - logger.warning(f"Fetch failed: {str(e)}") - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Failed to fetch URL: {str(e)}" - ) - - except ScrapingError as e: - logger.error(f"Scraping failed: {str(e)}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to extract content: {str(e)}" - ) - - except Exception as e: - logger.error(f"Unexpected error: {str(e)}", exc_info=True) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="An unexpected error occurred" - ) - @router.post( "/dns/lookup", response_model=DNSLookupResponse, diff --git a/src/web_scraper/__init__.py b/src/web_scraper/__init__.py deleted file mode 100644 index fa69ae4..0000000 --- a/src/web_scraper/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -Web scraper module for extracting content from websites -""" -from src.web_scraper.router import router -from src.web_scraper.schemas import WebScraperRequest, WebScraperResponse -from src.web_scraper.service import WebScraperService - -__all__ = [ - "router", - "WebScraperRequest", - "WebScraperResponse", - "WebScraperService", -] diff --git a/src/web_scraper/config.py b/src/web_scraper/config.py deleted file mode 100644 index 70c896c..0000000 --- a/src/web_scraper/config.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Configuration for web scraper module -""" -from pydantic_settings import BaseSettings -from functools import lru_cache - - -class WebScraperSettings(BaseSettings): - """Web scraper specific settings""" - - # HTTP client configuration - request_timeout: int = 30 - max_redirects: int = 5 - user_agent: str = "Mozilla/5.0 (compatible; CoreCode/1.0)" - - # Content extraction - default_max_length: int = 10000 - max_links_to_extract: int = 50 - - # Rate limiting (future use) - rate_limit_enabled: bool = False - requests_per_minute: int = 60 - - class Config: - env_prefix = "WEB_SCRAPER_" - case_sensitive = False - - -@lru_cache() -def get_web_scraper_settings() -> WebScraperSettings: - """Cached web scraper settings instance""" - return WebScraperSettings() diff --git a/src/web_scraper/exceptions.py b/src/web_scraper/exceptions.py deleted file mode 100644 index 3fb5443..0000000 --- a/src/web_scraper/exceptions.py +++ /dev/null @@ -1,18 +0,0 @@ -""" -Custom exceptions for web scraper module -""" - - -class WebScraperException(Exception): - """Base exception for web scraper module""" - pass - - -class FetchError(WebScraperException): - """Raised when URL fetch fails""" - pass - - -class ScrapingError(WebScraperException): - """Raised when content extraction fails""" - pass diff --git a/src/web_scraper/router.py b/src/web_scraper/router.py deleted file mode 100644 index afb3065..0000000 --- a/src/web_scraper/router.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -API routes for web scraper module -""" -from fastapi import APIRouter, HTTPException, status - -from src.logging_config import get_logger -from src.web_scraper.schemas import WebScraperRequest, WebScraperResponse -from src.web_scraper.service import WebScraperService -from src.web_scraper.exceptions import FetchError, ScrapingError - -logger = get_logger(__name__) - -router = APIRouter( - prefix="/web-scraper", - tags=["Web Scraper"] -) - -# Initialize service (could be dependency injected for testing) -scraper_service = WebScraperService() - - -@router.post( - "/scrape", - response_model=WebScraperResponse, - status_code=status.HTTP_200_OK, - summary="Scrape website content", - description=""" - Scrape and extract main content from a website. - - Uses trafilatura for intelligent content extraction (articles, blog posts, documentation), - with BeautifulSoup as fallback. Perfect for feeding webpage content to LLMs. - - **Features:** - - Intelligent main content extraction - - Removes navigation, ads, footers - - Optional link extraction - - Configurable content length limits - - **Rate Limiting:** None (internal network use only) - """ -) -async def scrape_website(request: WebScraperRequest) -> WebScraperResponse: - """ - Scrape a website and extract its main content - - Args: - request: Scraping request with URL and options - - Returns: - Extracted content with metadata - - Raises: - HTTPException: 400 for fetch errors, 500 for processing errors - """ - try: - logger.info(f"Received scrape request for: {request.url}") - result = await scraper_service.scrape_url(request) - return result - - except FetchError as e: - logger.warning(f"Fetch failed: {str(e)}") - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Failed to fetch URL: {str(e)}" - ) - - except ScrapingError as e: - logger.error(f"Scraping failed: {str(e)}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to extract content: {str(e)}" - ) - - except Exception as e: - logger.error(f"Unexpected error: {str(e)}", exc_info=True) - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="An unexpected error occurred" - ) diff --git a/src/web_scraper/schemas.py b/src/web_scraper/schemas.py deleted file mode 100644 index 9afde82..0000000 --- a/src/web_scraper/schemas.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Pydantic schemas for web scraper module -""" -from pydantic import HttpUrl, Field -from typing import Optional -from datetime import datetime -from src.base_schema import BaseSchema - - -class WebScraperRequest(BaseSchema): - """Request model for web scraping""" - - url: HttpUrl = Field( - ..., - description="The URL to scrape", - examples=["https://example.com/article"] - ) - - extract_main_content: bool = Field( - default=True, - description="Use intelligent content extraction (trafilatura) vs raw HTML parsing" - ) - - include_links: bool = Field( - default=False, - description="Include list of links found on the page" - ) - - max_length: Optional[int] = Field( - default=10000, - ge=100, - le=100000, - description="Maximum content length to return (100-100000 chars)" - ) - - -class WebScraperResponse(BaseSchema): - """Response model for web scraping""" - - url: str = Field( - ..., - description="The scraped URL" - ) - - title: Optional[str] = Field( - default=None, - description="Page title extracted from