cazyundee commited on
Commit
d0e8976
·
verified ·
1 Parent(s): e244295

Fix: patch App.__init__ to inject routes into the actual served app

Browse files
Files changed (1) hide show
  1. app.py +71 -91
app.py CHANGED
@@ -27,8 +27,6 @@ if hf_token:
27
  login(token=hf_token)
28
 
29
 
30
- # ZeroGPU Spaces require at least one @spaces.GPU function to be defined
31
- # during startup. This minimal touch satisfies that requirement.
32
  @spaces.GPU(duration=1)
33
  def _gpu_startup_touch():
34
  return "ok"
@@ -145,7 +143,7 @@ API_RESOURCES = {
145
  "extract_top": "integer (0-5, default 0) - fetch full page content for top N results",
146
  "region": "string (default 'wt-wt') - e.g. 'us-en', 'gb-en', 'de-de'",
147
  },
148
- "output": "JSON with title, url, content per result. News results include date and source. Extracted results include extracted_content.",
149
  },
150
  }
151
 
@@ -206,28 +204,13 @@ try:
206
  hits = list(ddgs.news(query, max_results=max_results, region=region))
207
  results = []
208
  for r in hits:
209
- entry = {
210
- "title": r.get("title", ""),
211
- "url": r.get("url", ""),
212
- "content": r.get("body", ""),
213
- "source": r.get("source", ""),
214
- "date": r.get("date", ""),
215
- }
216
  if r.get("image"):
217
  entry["image"] = r["image"]
218
  results.append(entry)
219
  elif backend == "images":
220
  hits = list(ddgs.images(query, max_results=max_results, region=region))
221
- results = []
222
- for r in hits:
223
- results.append({
224
- "title": r.get("title", ""),
225
- "url": r.get("image", ""),
226
- "source": r.get("source", ""),
227
- "thumbnail": r.get("thumbnail", ""),
228
- "width": r.get("width"),
229
- "height": r.get("height"),
230
- })
231
  else:
232
  hits = list(ddgs.text(query, max_results=max_results, region=region))
233
  results = [{"title": r.get("title", ""), "url": r.get("href", ""), "content": r.get("body", "")} for r in hits]
@@ -282,23 +265,14 @@ def _ensure_llama_cpp():
282
  src_dir = os.path.join(_BENCHMARK_CACHE, "llama_src")
283
  if os.path.isdir(src_dir):
284
  shutil.rmtree(src_dir, ignore_errors=True)
285
- subprocess.check_call(
286
- ["git", "clone", "--depth=1", "https://github.com/ggml-org/llama.cpp.git", src_dir],
287
- stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
288
- )
289
  _log("Benchmark: building llama.cpp (this takes 2-5 minutes)...")
290
  n_threads = _parse_cpu_count() or 8
291
  build_dir = os.path.join(_BENCHMARK_CACHE, "llama_build")
292
  os.makedirs(build_dir, exist_ok=True)
293
  os.makedirs(bin_dir, exist_ok=True)
294
- subprocess.check_call(
295
- ["cmake", "-S", src_dir, "-B", build_dir, "-DLLAMA_NATIVE=ON", "-DBUILD_SHARED_LIBS=OFF"],
296
- stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
297
- )
298
- subprocess.check_call(
299
- ["cmake", "--build", build_dir, "-j", str(n_threads), "--target", "llama-cli"],
300
- stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
301
- )
302
  import glob as _glob
303
  candidates = _glob.glob(os.path.join(build_dir, "**", "llama-cli"), recursive=True)
304
  if candidates:
@@ -427,78 +401,84 @@ with gr.Blocks(title="Respite API") as demo:
427
 
428
 
429
  # ---------------------------------------------------------------------------
430
- # Inject routes via monkey-patch on Gradio's App.create_app.
431
- #
432
- # Gradio's Blocks.launch() calls App.create_app() to build a NEW FastAPI app,
433
- # discarding any routes added to demo.app earlier. We wrap that method so our
434
- # routes get added to the app that is actually served.
435
  # ---------------------------------------------------------------------------
436
 
437
- from gradio.routes import App as GradioApp
438
 
439
- _orig_create_app = GradioApp.create_app
 
440
 
441
 
442
- def _patched_create_app(blocks, **kwargs):
443
- fa_app = _orig_create_app(blocks, **kwargs)
444
 
445
- @fa_app.get("/respite/resources")
446
- def _resources():
447
- return JSONResponse({"resources": API_RESOURCES})
448
 
449
- @fa_app.get("/respite/specs")
450
- def _specs():
451
- return JSONResponse({**API_SPECS, "runtime": _get_runtime_specs()})
 
 
 
 
 
 
 
452
 
453
- @fa_app.get("/respite/search")
454
- def _search(q: str = "", backend: str = "text", max_results: int = 10, extract_top: int = 0, region: str = "wt-wt"):
455
- if not q:
456
- return JSONResponse({"error": "Missing query parameter 'q'"}, status_code=400)
457
- if backend not in ("text", "news", "images"):
458
- return JSONResponse({"error": f"Invalid backend '{backend}'. Use 'text', 'news', or 'images'."}, status_code=400)
459
- try:
460
- results = _search_web(q, backend=backend, max_results=max_results, extract_top=extract_top, region=region)
461
- return JSONResponse(results)
462
- except Exception as e:
463
- return JSONResponse({"error": str(e)}, status_code=502)
464
 
465
- @fa_app.get("/respite/benchmark")
466
- def _benchmark(force: bool = False, model: str = ""):
467
- try:
468
- global _BENCHMARK_RESULTS
469
- if force:
470
- _BENCHMARK_RESULTS = []
471
- model_filter = model.strip() if model.strip() else None
472
- results = _run_benchmark(model_filter=model_filter)
473
- return JSONResponse({
474
- "status": "complete",
475
- "models": results,
476
- "system": {"cpu_cores": _parse_cpu_count(), "ram_bytes": _parse_ram_bytes(), "threads": _parse_cpu_count() or 8},
477
- })
478
- except Exception as e:
479
- return JSONResponse({"error": str(e)}, status_code=500)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
 
481
- @fa_app.get("/respite/benchmark/test")
482
- def _benchmark_test():
483
- import subprocess as _sp
484
- try:
485
- llama_cli = _ensure_llama_cpp()
486
- r = _sp.run([llama_cli, "--version"], capture_output=True, text=True, timeout=10)
487
- binary_ok = r.returncode == 0
488
- binary_info = r.stdout.strip()[:200] if binary_ok else r.stderr.strip()[:200]
489
- from huggingface_hub import hf_hub_download
490
- model_path = hf_hub_download(repo_id="unsloth/gemma-4-E4B-it-GGUF", filename="gemma-4-E4B-it-Q4_K_M.gguf", cache_dir=_BENCHMARK_CACHE)
491
- model_ok = os.path.isfile(model_path)
492
- model_size = os.path.getsize(model_path) if model_ok else 0
493
- return JSONResponse({"binary_path": llama_cli, "binary_exists": os.path.isfile(llama_cli), "binary_ok": binary_ok, "binary_info": binary_info, "model_path": model_path, "model_ok": model_ok, "model_size_gb": round(model_size / 1024**3, 2)})
494
- except Exception as e:
495
- return JSONResponse({"error": str(e)}, status_code=500)
496
 
497
- _log("Respite API routes injected into Gradio app")
498
- return fa_app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
 
500
 
501
- GradioApp.create_app = _patched_create_app
502
 
503
  # Disable the Gradio Spaces file watcher thread which crashes because
504
  # importing `spaces` in a new thread after CUDA init fails.
 
27
  login(token=hf_token)
28
 
29
 
 
 
30
  @spaces.GPU(duration=1)
31
  def _gpu_startup_touch():
32
  return "ok"
 
143
  "extract_top": "integer (0-5, default 0) - fetch full page content for top N results",
144
  "region": "string (default 'wt-wt') - e.g. 'us-en', 'gb-en', 'de-de'",
145
  },
146
+ "output": "JSON with title, url, content per result.",
147
  },
148
  }
149
 
 
204
  hits = list(ddgs.news(query, max_results=max_results, region=region))
205
  results = []
206
  for r in hits:
207
+ entry = {"title": r.get("title", ""), "url": r.get("url", ""), "content": r.get("body", ""), "source": r.get("source", ""), "date": r.get("date", "")}
 
 
 
 
 
 
208
  if r.get("image"):
209
  entry["image"] = r["image"]
210
  results.append(entry)
211
  elif backend == "images":
212
  hits = list(ddgs.images(query, max_results=max_results, region=region))
213
+ results = [{"title": r.get("title", ""), "url": r.get("image", ""), "source": r.get("source", ""), "thumbnail": r.get("thumbnail", ""), "width": r.get("width"), "height": r.get("height")} for r in hits]
 
 
 
 
 
 
 
 
 
214
  else:
215
  hits = list(ddgs.text(query, max_results=max_results, region=region))
216
  results = [{"title": r.get("title", ""), "url": r.get("href", ""), "content": r.get("body", "")} for r in hits]
 
265
  src_dir = os.path.join(_BENCHMARK_CACHE, "llama_src")
266
  if os.path.isdir(src_dir):
267
  shutil.rmtree(src_dir, ignore_errors=True)
268
+ subprocess.check_call(["git", "clone", "--depth=1", "https://github.com/ggml-org/llama.cpp.git", src_dir], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 
 
 
269
  _log("Benchmark: building llama.cpp (this takes 2-5 minutes)...")
270
  n_threads = _parse_cpu_count() or 8
271
  build_dir = os.path.join(_BENCHMARK_CACHE, "llama_build")
272
  os.makedirs(build_dir, exist_ok=True)
273
  os.makedirs(bin_dir, exist_ok=True)
274
+ subprocess.check_call(["cmake", "-S", src_dir, "-B", build_dir, "-DLLAMA_NATIVE=ON", "-DBUILD_SHARED_LIBS=OFF"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
275
+ subprocess.check_call(["cmake", "--build", build_dir, "-j", str(n_threads), "--target", "llama-cli"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 
 
 
 
 
 
276
  import glob as _glob
277
  candidates = _glob.glob(os.path.join(build_dir, "**", "llama-cli"), recursive=True)
278
  if candidates:
 
401
 
402
 
403
  # ---------------------------------------------------------------------------
404
+ # Route handlers
 
 
 
 
405
  # ---------------------------------------------------------------------------
406
 
 
407
 
408
+ def _resources_handler():
409
+ return JSONResponse({"resources": API_RESOURCES})
410
 
411
 
412
+ def _specs_handler():
413
+ return JSONResponse({**API_SPECS, "runtime": _get_runtime_specs()})
414
 
 
 
 
415
 
416
+ def _search_handler(q: str = "", backend: str = "text", max_results: int = 10, extract_top: int = 0, region: str = "wt-wt"):
417
+ if not q:
418
+ return JSONResponse({"error": "Missing query parameter 'q'"}, status_code=400)
419
+ if backend not in ("text", "news", "images"):
420
+ return JSONResponse({"error": f"Invalid backend '{backend}'. Use 'text', 'news', or 'images'."}, status_code=400)
421
+ try:
422
+ results = _search_web(q, backend=backend, max_results=max_results, extract_top=extract_top, region=region)
423
+ return JSONResponse(results)
424
+ except Exception as e:
425
+ return JSONResponse({"error": str(e)}, status_code=502)
426
 
 
 
 
 
 
 
 
 
 
 
 
427
 
428
+ def _benchmark_handler(force: bool = False, model: str = ""):
429
+ try:
430
+ global _BENCHMARK_RESULTS
431
+ if force:
432
+ _BENCHMARK_RESULTS = []
433
+ model_filter = model.strip() if model.strip() else None
434
+ results = _run_benchmark(model_filter=model_filter)
435
+ return JSONResponse({
436
+ "status": "complete",
437
+ "models": results,
438
+ "system": {"cpu_cores": _parse_cpu_count(), "ram_bytes": _parse_ram_bytes(), "threads": _parse_cpu_count() or 8},
439
+ })
440
+ except Exception as e:
441
+ return JSONResponse({"error": str(e)}, status_code=500)
442
+
443
+
444
+ def _benchmark_test_handler():
445
+ import subprocess as _sp
446
+ try:
447
+ llama_cli = _ensure_llama_cpp()
448
+ r = _sp.run([llama_cli, "--version"], capture_output=True, text=True, timeout=10)
449
+ binary_ok = r.returncode == 0
450
+ binary_info = r.stdout.strip()[:200] if binary_ok else r.stderr.strip()[:200]
451
+ from huggingface_hub import hf_hub_download
452
+ model_path = hf_hub_download(repo_id="unsloth/gemma-4-E4B-it-GGUF", filename="gemma-4-E4B-it-Q4_K_M.gguf", cache_dir=_BENCHMARK_CACHE)
453
+ model_ok = os.path.isfile(model_path)
454
+ model_size = os.path.getsize(model_path) if model_ok else 0
455
+ return JSONResponse({"binary_path": llama_cli, "binary_exists": os.path.isfile(llama_cli), "binary_ok": binary_ok, "binary_info": binary_info, "model_path": model_path, "model_ok": model_ok, "model_size_gb": round(model_size / 1024**3, 2)})
456
+ except Exception as e:
457
+ return JSONResponse({"error": str(e)}, status_code=500)
458
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
 
460
+ # ---------------------------------------------------------------------------
461
+ # Monkey-patch: intercept Gradio server creation to inject routes.
462
+ # ---------------------------------------------------------------------------
463
+
464
+ _orig_routes = gr.routes.App.__init__
465
+
466
+
467
+ def _patched_init(self, *args, **kwargs):
468
+ _orig_routes(self, *args, **kwargs)
469
+ _log("Gradio server __init__ patched - adding routes")
470
+ try:
471
+ self.add_api_route("/respite/resources", _resources_handler, methods=["GET"])
472
+ self.add_api_route("/respite/specs", _specs_handler, methods=["GET"])
473
+ self.add_api_route("/respite/search", _search_handler, methods=["GET"])
474
+ self.add_api_route("/respite/benchmark", _benchmark_handler, methods=["GET"])
475
+ self.add_api_route("/respite/benchmark/test", _benchmark_test_handler, methods=["GET"])
476
+ _log("Routes added via __init__ patch")
477
+ except Exception as e:
478
+ _log(f"Route patching failed: {e}")
479
 
480
 
481
+ gr.routes.App.__init__ = _patched_init
482
 
483
  # Disable the Gradio Spaces file watcher thread which crashes because
484
  # importing `spaces` in a new thread after CUDA init fails.