""" RAG search router for Library Desk API. Endpoints for web, news, and image search with content extraction. """ import httpx from fastapi import APIRouter, HTTPException, Depends import logging from src.models.rag_search import RAGSearchRequest, RAGSearchResponse from src.services.rag_search_service import RAGSearchService from src.core.dependencies import verify_api_key logger = logging.getLogger(__name__) router = APIRouter(prefix="/rag", tags=["RAG Search"]) # Lazy import to avoid circular dependency def get_rag_search_service() -> RAGSearchService: """Get RAG search service instance.""" from src.core.dependencies import get_rag_search_service as _get_service return _get_service() @router.post("/search", response_model=RAGSearchResponse) async def search( request: RAGSearchRequest, api_key: str = Depends(verify_api_key) ): """ Execute RAG-optimized web search with content extraction. Searches via SearXNG and extracts full content from results using Trafilatura. Results are cached in Redis for efficiency. **Search Types:** - `web`: General web search (default) - `news`: News articles with recency filtering - `images`: Image search results **Example Request:** ```json { "query": "Python async programming best practices", "search_type": "web", "limit": 10, "user": "default" } ``` **Response includes:** - Full extracted text content per result - Original search snippets - Source domain names - Markdown sources summary for LLM consumption **Error Codes:** - 400: Invalid query (empty or too long) - 502: Search provider (SearXNG) error - 504: Search timeout """ try: service = get_rag_search_service() response = await service.search( query=request.query, search_type=request.search_type, limit=request.limit, user=request.user ) return response except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except httpx.TimeoutException: logger.error(f"Search timed out for query: {request.query}") raise HTTPException(status_code=504, detail="Search timed out") except httpx.HTTPError as e: logger.error(f"Search provider error: {e}") raise HTTPException(status_code=502, detail="Search provider error") except Exception as e: logger.error(f"RAG search failed: {e}", exc_info=True) raise HTTPException(status_code=500, detail="Search failed")