Spaces:
Running on Zero
Running on Zero
Download searx_bridge.py from cazyundee/Respite-API: direct link, hf CLI and curl.
- Browser
- Download file 4.5 kB
-
https://huggingface.co/spaces/cazyundee/Respite-API/resolve/main/searx_bridge.py
- Command line
-
hf download hf://spaces/cazyundee/Respite-API/searx_bridge.py
-
curl -L -o searx_bridge.py https://huggingface.co/spaces/cazyundee/Respite-API/resolve/main/searx_bridge.py
4.5 kB
| """SearXNG bridge β runs a SearXNG metasearch instance inside the Space | |
| container (userspace venv, no root) and proxies queries to it. | |
| Mounted under /respite/v2/searx/*: | |
| /status β bootstrap state of the runtime, and of the reranker | |
| /search β proxy ?q=... queries to SearXNG's JSON API | |
| /rerank β score/reorder/drop a result set for a query (see rerank.py) | |
| Engine selection comes from searxng_settings.yml (DDG + Google enabled); | |
| the frontend picks engines per request via the standard `engines` param. | |
| /rerank lives under the same prefix as /search on purpose: the Space's key gate | |
| guards /respite/v2/searx/*, so the reranker inherits that authentication rather | |
| than needing a second one to be got right. | |
| """ | |
| import asyncio | |
| import json | |
| import httpx | |
| from fastapi import Request | |
| from fastapi.responses import JSONResponse | |
| import rerank | |
| import searx_runtime | |
| # Load the reranker's checkpoint as soon as the container boots, not on the | |
| # first search request. The first request has a ~1s deadline and the load takes | |
| # minutes, so without this the reranker can never apply on a cold container and | |
| # the only symptom is a timeout. A daemon thread, so a failure or a slow mirror | |
| # cannot hold the Space's startup hostage. | |
| rerank.warm() | |
| def register_routes(fa_app): | |
| def _status(): | |
| return JSONResponse({ | |
| "runtime_dir": searx_runtime.RUNTIME_DIR, | |
| "installed": searx_runtime.searxng_ready(), | |
| "boot_error": searx_runtime.boot_error(), | |
| "port": searx_runtime.SEARXNG_PORT, | |
| "reranker": rerank.status(), }) | |
| async def _search(request: Request): | |
| if not searx_runtime.searxng_ready(): | |
| ok = searx_runtime.start_searxng() | |
| if not ok: | |
| return JSONResponse({"error": "searxng not running"}, status_code=503) | |
| params = dict(request.query_params) | |
| # SearXNG's JSON API lives at /search with format=json | |
| params.setdefault("format", "json") | |
| url = f"http://127.0.0.1:{searx_runtime.SEARXNG_PORT}/search" | |
| try: | |
| async with httpx.AsyncClient(timeout=httpx.Timeout(30.0, connect=5.0)) as client: | |
| r = await client.get(url, params=params) | |
| return JSONResponse(r.json(), status_code=r.status_code) | |
| except Exception as e: | |
| return JSONResponse({"error": str(e)}, status_code=502) | |
| async def _rerank(request: Request): | |
| """Rerank a result set. Always answers 200 with the results in some | |
| order β the client is a search path, and an exception here would look | |
| like an outage to whoever is trying to read the page.""" | |
| try: | |
| body = await request.body() | |
| payload = json.loads(body or b"{}") | |
| except Exception as e: | |
| return JSONResponse({"ok": False, "error": f"bad request: {e}", "results": []}) | |
| q = str(payload.get("q") or "").strip() | |
| results = payload.get("results") | |
| if not q or not isinstance(results, list) or not results: | |
| return JSONResponse({"ok": False, "error": "q and results are required", "results": results or []}) | |
| budget_ms = int(payload.get("budget_ms") or 800) | |
| # rerank() is synchronous and CPU-bound (torch), so it must not run on | |
| # the event loop β that would stall every other Space request, audio | |
| # generation included, for as long as the model takes. | |
| try: | |
| out = await asyncio.to_thread(rerank.rerank, q, results, budget_ms) | |
| except Exception as e: # noqa: BLE001 | |
| # `no_opinion` as well as `error`: the client reports the former, so | |
| # a crash that never reached rerank() must not read as "the model | |
| # had nothing to say about your query". | |
| return JSONResponse( | |
| { | |
| "ok": False, | |
| "error": f"rerank failed: {e}", | |
| "no_opinion": f"{type(e).__name__}: {e}"[:300], | |
| "results": results, | |
| } | |
| ) | |
| return JSONResponse(out) | |
| def _rerank_recent(limit: int = 20): | |
| """Recent relevance scores, for choosing DROP_BELOW from real traffic | |
| rather than from a guess.""" | |
| return JSONResponse({"scores": rerank.recent_scores(limit)}) | |