Spaces:
Running
Running
| from contextlib import asynccontextmanager | |
| import json | |
| from typing import Optional | |
| from duckduckgo_search import DDGS | |
| from duckduckgo_search.exceptions import RatelimitException | |
| import expiringdict | |
| from fastapi import FastAPI | |
| from pydantic import BaseModel, Field | |
| from playwright.async_api import async_playwright, Browser, BrowserContext, Page | |
| from urllib.parse import quote_plus | |
| import logging | |
| import re | |
| import uvicorn | |
| from backends import APISearchResults, APIPatentResults, query_bing_search, query_brave_search, query_ddg_search, query_google_patents | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d]: %(message)s', | |
| datefmt='%Y-%m-%d %H:%M:%S' | |
| ) | |
| # playwright global context | |
| playwright = None | |
| pw_browser: Optional[Browser] = None | |
| async def api_lifespan(app: FastAPI): | |
| global playwright, pw_browser | |
| playwright = await async_playwright().start() | |
| pw_browser = await playwright.chromium.launch(headless=True) | |
| yield | |
| await pw_browser.close() | |
| await playwright.stop() | |
| app = FastAPI(lifespan=api_lifespan) | |
| backend_status = expiringdict.ExpiringDict(max_len=5, max_age_seconds=15*60) | |
| class APISearchParams(BaseModel): | |
| queries: list[str] = Field(..., | |
| description="The list of queries to search for") | |
| n_results: int = Field( | |
| 10, description="Number of results to return for each query. Valid values are 10, 25, 50 and 100") | |
| async def query_google_scholar(params: APISearchParams): | |
| """Queries google scholar for the specified query""" | |
| return {"error": "Unimplemented"} | |
| async def status(): | |
| backend_keys = [k[0] for k in backend_status.items()] | |
| backend_status_dict = {} | |
| for k in backend_keys: | |
| backend_status_dict[k] = backend_status.get(k) | |
| return {"status": "running", "backend_status": backend_status_dict} | |
| async def search_patents(params: APISearchParams) -> APIPatentResults: | |
| """Searches google patents for the specified queries and returns the found documents.""" | |
| results = [] | |
| for q in params.queries: | |
| logging.info(f"Searching Google Patents with query `{q}`") | |
| try: | |
| res = await query_google_patents(pw_browser, q, params.n_results) | |
| results.extend(res) | |
| except Exception as e: | |
| backend_status["gpatents"] = "rate-limited" | |
| logging.error( | |
| f"Failed to query Google Patents with query `{q}`: {e}") | |
| return APIPatentResults(results=results, error=None) | |
| async def search_brave(params: APISearchParams) -> APISearchResults: | |
| """Searches brave search for the specified queries and returns the found documents.""" | |
| results = [] | |
| last_exception: Optional[Exception] = None | |
| for q in params.queries: | |
| logging.info(f"Searching Brave search with query `{q}`") | |
| try: | |
| res = await query_brave_search(pw_browser, q, params.n_results) | |
| results.extend(res) | |
| except Exception as e: | |
| last_exception = e | |
| backend_status["brave"] = "rate-limited" | |
| logging.error( | |
| f"Failed to query Brave search with query `{q}`: {e}") | |
| return APISearchResults(results=results, error=str(last_exception) if len(results) == 0 and last_exception else None) | |
| async def search_bing(params: APISearchParams) -> APISearchResults: | |
| """Searches Bing search for the specified queries and returns the found documents.""" | |
| results = [] | |
| last_exception: Optional[Exception] = None | |
| for q in params.queries: | |
| logging.info(f"Searching Bing search with query `{q}`") | |
| try: | |
| res = await query_brave_search(pw_browser, q, params.n_results) | |
| results.extend(res) | |
| except Exception as e: | |
| last_exception = e | |
| backend_status["bing"] = "rate-limited" | |
| logging.error( | |
| f"Failed to query Bing search with query `{q}`: {e}") | |
| return APISearchResults(results=results, error=str(last_exception) if len(results) == 0 and last_exception else None) | |
| async def search_duck(params: APISearchParams) -> APISearchResults: | |
| """Searches duckduckgo for the specified queries and returns the found documents""" | |
| results = [] | |
| last_exception: Optional[Exception] = None | |
| for q in params.queries: | |
| logging.info(f"Querying DDG with query: `{q}`") | |
| try: | |
| res = await query_ddg_search(q, params.n_results) | |
| results.extend(res) | |
| except Exception as e: | |
| last_exception = e | |
| backend_status["duckduckgo"] = "rate-limited" | |
| logging.error(f"Failed to query DDG with query `{q}`: {e}") | |
| return APISearchResults(results=results, error=str(last_exception) if len(results) == 0 and last_exception else None) | |
| async def search(params: APISearchParams): | |
| """Attempts to search the specified queries using ALL backends""" | |
| results = [] | |
| for q in params.queries: | |
| try: | |
| logging.info(f"Querying DDG with query: `{q}`") | |
| res = await query_ddg_search(q, params.n_results) | |
| results.extend(res) | |
| continue | |
| except Exception as e: | |
| logging.error(f"Failed to query DDG with query `{q}`: {e}") | |
| logging.info("Trying with next browser backend.") | |
| try: | |
| logging.info(f"Querying Brave Search with query: `{q}`") | |
| res = await query_brave_search(pw_browser, q, params.n_results) | |
| results.extend(res) | |
| continue | |
| except Exception as e: | |
| logging.error( | |
| f"Failed to query Brave Search with query `{q}`: {e}") | |
| logging.info("Trying with next browser backend.") | |
| try: | |
| logging.info(f"Querying Bing with query: `{q}`") | |
| res = await query_bing_search(pw_browser, q, params.n_results) | |
| results.extend(res) | |
| continue | |
| except Exception as e: | |
| logging.error(f"Failed to query Bing search with query `{q}`: {e}") | |
| logging.info("Trying with next browser backend.") | |
| if len(results) == 0: | |
| return APISearchResults(results=[], error="All backends are rate-limited.") | |
| return APISearchResults(results=results, error=None) | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |