build-small-hackathon/HF-Agentic-Search-Live
3
1"""HF Agentic Search - Gradio Space entrypoint."""2from __future__ import annotations3 4import json5import os6import sys7import threading8import uuid9 10os.environ.setdefault("GRADIO_ANALYTICS_ENABLED", "False")11 12_ROOT = os.path.dirname(os.path.abspath(__file__))13if _ROOT not in sys.path:14 sys.path.insert(0, _ROOT)15 16import gradio as gr17from fastapi import Request18from fastapi.responses import FileResponse, JSONResponse, StreamingResponse19from fastapi.staticfiles import StaticFiles20 21from backend.agent import MAX_TASK_LENGTH, weave, weave_events22 23app = gr.Server()24_DIST = os.path.join(_ROOT, "frontend", "dist")25_sessions: dict[str, dict] = {}26_lock = threading.Lock()27 28 29def _session_id(request: Request) -> str:30 supplied = (request.headers.get("X-Session-Id") or "").strip()31 return supplied[:80] if supplied else f"dw-{uuid.uuid4().hex[:16]}"32 33 34async def _task_from_request(request: Request) -> tuple[str | None, JSONResponse | None]:35 try:36 body = await request.json()37 except Exception:38 return None, JSONResponse({"error": "Body is not valid JSON."}, status_code=400)39 task = str(body.get("task") or "").strip()40 if not task:41 return None, JSONResponse({"error": "Task description is required."}, status_code=400)42 if len(task) > MAX_TASK_LENGTH:43 return None, JSONResponse(44 {"error": f"Task description must be {MAX_TASK_LENGTH} characters or fewer."},45 status_code=422,46 )47 return task, None48 49 50@app.post("/weave")51async def api_weave(request: Request):52 task, error = await _task_from_request(request)53 if error:54 return error55 sid = _session_id(request)56 try:57 result = weave(task or "")58 except Exception as exc:59 return JSONResponse({"error": str(exc)}, status_code=502)60 with _lock:61 _sessions[sid] = result62 return JSONResponse(result, headers={"X-Session-Id": sid})63 64 65@app.post("/weave/stream")66async def api_weave_stream(request: Request):67 task, error = await _task_from_request(request)68 if error:69 return error70 sid = _session_id(request)71 72 def stream():73 try:74 for event in weave_events(task or ""):75 if event["type"] == "complete":76 with _lock:77 _sessions[sid] = event["result"]78 yield json.dumps(event, ensure_ascii=True) + "\n"79 except Exception as exc:80 yield json.dumps({"type": "error", "message": str(exc)}) + "\n"81 82 return StreamingResponse(83 stream(),84 media_type="application/x-ndjson",85 headers={"X-Session-Id": sid, "Cache-Control": "no-store"},86 )87 88 89@app.get("/state")90async def get_state(request: Request):91 sid = _session_id(request)92 with _lock:93 result = _sessions.get(sid)94 if result is None:95 result = {96 "datasets": [], "nodes": [], "threads": [], "task": "",97 "top_pick": None, "fallback_used": False,98 }99 return JSONResponse(result, headers={"X-Session-Id": sid})100 101 102_DIST_ASSETS = os.path.join(_DIST, "assets")103if os.path.isdir(_DIST_ASSETS):104 app.mount("/assets", StaticFiles(directory=_DIST_ASSETS), name="assets")105 106 107@app.get("/")108def index():109 path = os.path.join(_DIST, "index.html")110 if os.path.isfile(path):111 return FileResponse(path)112 return JSONResponse({"error": "Frontend not built."}, status_code=503)113 114 115if __name__ == "__main__":116 app.launch(117 server_name="0.0.0.0",118 server_port=int(os.environ.get("PORT", os.environ.get("GRADIO_SERVER_PORT", "7860"))),119 show_error=True,120 )121 