import httpx import asyncio from typing import List, Dict, Any DEFAULT_TIMEOUT = 25.0 async def fetch_json( url: str, params: dict = None, headers: dict = None, timeout: float = DEFAULT_TIMEOUT, ) -> dict: """Fetch JSON from URL with timeout.""" async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client: try: r = await client.get(url, params=params, headers=headers or {}) r.raise_for_status() return r.json() except Exception as e: return {"error": str(e)} async def fetch_text( url: str, params: dict = None, headers: dict = None, timeout: float = DEFAULT_TIMEOUT, ) -> str: """Fetch raw text/XML from URL with timeout.""" async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client: try: r = await client.get(url, params=params, headers=headers or {}) r.raise_for_status() return r.text except Exception as e: return "" def normalize_result( title: str, authors: list, year: int, abstract: str, doi: str, pdf_url: str, source: str, university: str = None, citation_count: int = None, ) -> dict: """Normalize a search result to common format.""" return { "title": title or "N/A", "authors": authors or [], "year": year, "abstract": abstract or "", "doi": doi or "", "pdfUrl": pdf_url or "", "source": source, "university": university or "", "citationCount": citation_count, }