"""Patent Search Router - Search for similar patents using Lens.org API""" import logging from typing import Optional, List from fastapi import APIRouter from pydantic import BaseModel from ..services.patent_search_service import patent_search_service logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/patent", tags=["patent"]) # ===== Request/Response Models ===== class PatentSearchRequest(BaseModel): """Patent search request""" query: str # Search query (description or keywords) max_results: int = 10 # Maximum results to return (1-20) class PatentResult(BaseModel): """Single patent result from Lens.org""" lens_id: str doc_number: str jurisdiction: str kind: str title: str abstract: Optional[str] = None date_published: Optional[str] = None applicants: List[str] = [] inventors: List[str] = [] legal_status: Optional[str] = None classifications_cpc: List[str] = [] families_simple: List[str] = [] url: str class PatentSearchResponse(BaseModel): """Patent search response""" query: str total_results: int patents: List[PatentResult] error: Optional[str] = None class BatchPatentSearchRequest(BaseModel): """Batch patent search request - search multiple descriptions""" queries: List[str] # List of descriptions to search max_results_per_query: int = 5 # Max results per query class BatchPatentSearchResult(BaseModel): """Results for a single query in batch search""" query: str total_results: int patents: List[PatentResult] error: Optional[str] = None class BatchPatentSearchResponse(BaseModel): """Batch patent search response""" results: List[BatchPatentSearchResult] total_queries: int # ===== Endpoints ===== @router.post("/search", response_model=PatentSearchResponse) async def search_patents(request: PatentSearchRequest): """ Search for patents similar to the given description/query. Uses Lens.org API to find related patents based on title, abstract, and claims. """ logger.info(f"Patent search request: {request.query[:100]}...") # Limit max_results to reasonable range max_results = min(max(1, request.max_results), 20) result = await patent_search_service.search( query=request.query, max_results=max_results, ) return PatentSearchResponse( query=request.query, total_results=result.get("total_results", 0), patents=[PatentResult(**p) for p in result.get("patents", [])], error=result.get("error"), ) @router.post("/search/batch", response_model=BatchPatentSearchResponse) async def batch_search_patents(request: BatchPatentSearchRequest): """ Search for patents for multiple descriptions at once. Useful for checking multiple creative descriptions against patents. """ logger.info(f"Batch patent search: {len(request.queries)} queries") # Limit results per query max_per_query = min(max(1, request.max_results_per_query), 10) results: List[BatchPatentSearchResult] = [] for query in request.queries: result = await patent_search_service.search( query=query, max_results=max_per_query, ) results.append(BatchPatentSearchResult( query=query, total_results=result.get("total_results", 0), patents=[PatentResult(**p) for p in result.get("patents", [])], error=result.get("error"), )) return BatchPatentSearchResponse( results=results, total_queries=len(request.queries), ) @router.get("/health") async def patent_search_health(): """Check if patent search service is working""" # Do a simple test search result = await patent_search_service.search("test", max_results=1) if result.get("error"): return {"status": "unhealthy", "error": result["error"]} return {"status": "healthy"}