cazyundee commited on
Commit
afb0c33
·
verified ·
1 Parent(s): c7f0d89

Restore working create_app monkey-patch with staticmethod

Browse files
Files changed (1) hide show
  1. app.py +142 -221
app.py CHANGED
@@ -25,23 +25,21 @@ hf_token = os.environ.get("HF_TOKEN")
25
  if hf_token:
26
  login(token=hf_token)
27
 
28
-
29
  @spaces.GPU(duration=1)
30
  def _gpu_startup_touch():
31
  return "ok"
32
 
33
-
34
  try:
35
  _gpu_startup_touch()
36
  except Exception as e:
37
  print(f"Warning: ZeroGPU touch failed: {e}", flush=True)
38
 
39
 
40
- def _log(message):
41
- print(message.encode("ascii", "backslashreplace").decode("ascii"), flush=True)
42
 
43
 
44
- def _read_cgroup_file(path):
45
  try:
46
  with open(path, "r", encoding="utf-8") as f:
47
  return f.read().strip()
@@ -50,63 +48,40 @@ def _read_cgroup_file(path):
50
 
51
 
52
  def _parse_cpu_count():
53
- cpu_max = _read_cgroup_file("/sys/fs/cgroup/cpu.max")
54
- if cpu_max:
55
- parts = cpu_max.split()
56
- if len(parts) == 2 and parts[0] != "max":
57
  try:
58
- quota, period = int(parts[0]), int(parts[1])
59
- if period > 0:
60
- return quota // period
61
- except ValueError:
62
- pass
63
- quota = _read_cgroup_file("/sys/fs/cgroup/cpu/cpu.cfs_quota_us")
64
- period = _read_cgroup_file("/sys/fs/cgroup/cpu/cpu.cfs_period_us")
65
- if quota and period:
66
- try:
67
- q, p = int(quota), int(period)
68
- if q > 0 and p > 0:
69
- return q // p
70
- except ValueError:
71
- pass
72
- cpuset = _read_cgroup_file("/sys/fs/cgroup/cpuset.cpus")
73
- if cpuset:
74
- count = 0
75
- for part in cpuset.split(","):
76
  if "-" in part:
77
- start, end = part.split("-", 1)
78
- count += int(end) - int(start) + 1
79
  else:
80
- count += 1
81
- if count > 0:
82
- return count
83
  return os.cpu_count()
84
 
85
 
86
- def _parse_ram_bytes():
87
- mem_max = _read_cgroup_file("/sys/fs/cgroup/memory.max")
88
- if mem_max and mem_max != "max":
89
  try:
90
- val = int(mem_max)
91
- if val < 2**62:
92
- return val
93
- except ValueError:
94
- pass
95
- mem_limit = _read_cgroup_file("/sys/fs/cgroup/memory/memory.limit_in_bytes")
96
- if mem_limit:
97
- try:
98
- val = int(mem_limit)
99
- if val < 2**62:
100
- return val
101
- except ValueError:
102
- pass
103
  try:
104
- with open("/proc/meminfo", "r", encoding="utf-8") as meminfo:
105
- for line in meminfo:
106
  if line.startswith("MemTotal:"):
107
  return int(line.split()[1]) * 1024
108
- except (FileNotFoundError, OSError, ValueError):
109
- pass
110
  return None
111
 
112
 
@@ -136,8 +111,8 @@ API_RESOURCES = {
136
  "endpoint": "/respite/search",
137
  "method": "GET",
138
  "input": {
139
- "q": "string (search query)",
140
- "backend": "string: 'text' (default), 'news', or 'images'",
141
  "max_results": "integer (1-50, default 10)",
142
  "extract_top": "integer (0-5, default 0)",
143
  "region": "string (default 'wt-wt')",
@@ -165,28 +140,24 @@ API_SPECS = {
165
 
166
  def _get_runtime_specs():
167
  storage = shutil.disk_usage(os.getcwd())
168
- hardware = os.environ.get("SPACE_HARDWARE", "zero-a10g")
169
- is_zerogpu = "zero" in hardware.lower()
170
  return {
171
  "platform": platform.platform(),
172
  "python_version": platform.python_version(),
173
  "cpu_cores": _parse_cpu_count(),
174
  "host_cpu_cores": os.cpu_count(),
175
- "ram_bytes": _parse_ram_bytes(),
176
  "storage_total_bytes": storage.total,
177
  "storage_used_bytes": storage.used,
178
  "storage_free_bytes": storage.free,
179
- "hardware": hardware,
180
- "gpu": {
181
- "type": "NVIDIA RTX Pro 6000 Blackwell",
182
- "vram_bytes": 48 * 1024**3 if is_zerogpu else None,
183
- "shared_zero_gpu": is_zerogpu,
184
- } if is_zerogpu else None,
185
  }
186
 
187
 
188
  # ---------------------------------------------------------------------------
189
- # Web search (DuckDuckGo)
190
  # ---------------------------------------------------------------------------
191
 
192
  try:
@@ -197,28 +168,23 @@ try:
197
  max_results = max(1, min(50, max_results))
198
  with DDGS() as ddgs:
199
  if backend == "news":
200
- hits = list(ddgs.news(query, max_results=max_results, region=region))
201
- results = [{"title": r.get("title", ""), "url": r.get("url", ""), "content": r.get("body", ""), "source": r.get("source", ""), "date": r.get("date", "")} for r in hits]
202
  elif backend == "images":
203
- hits = list(ddgs.images(query, max_results=max_results, region=region))
204
- 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]
205
  else:
206
- hits = list(ddgs.text(query, max_results=max_results, region=region))
207
- results = [{"title": r.get("title", ""), "url": r.get("href", ""), "content": r.get("body", "")} for r in hits]
208
  if extract_top > 0 and results and backend != "images":
209
- urls_to_extract = [r["url"] for r in results[:extract_top] if r.get("url")]
210
  with DDGS() as ddgs:
211
- for url in urls_to_extract:
212
  try:
213
- extracted = ddgs.extract(url)
214
- page_content = extracted.get("content", "")
215
- if page_content:
216
  for r in results:
217
  if r["url"] == url:
218
- r["extracted_content"] = page_content[:8000]
219
  break
220
- except Exception:
221
- pass
222
  return {"query": query, "backend": backend, "results": results, "total_results": len(results)}
223
 
224
  except ImportError:
@@ -227,99 +193,81 @@ except ImportError:
227
 
228
 
229
  # ---------------------------------------------------------------------------
230
- # CPU LLM benchmark
231
  # ---------------------------------------------------------------------------
232
 
233
- _BENCHMARK_MODELS = [
234
  {"name": "Gemma 4 E4B", "repo": "unsloth/gemma-4-E4B-it-GGUF", "file": "gemma-4-E4B-it-Q4_K_M.gguf", "size_gb": 2.5},
235
  {"name": "Qwen3 8B", "repo": "Qwen/Qwen3-8B-GGUF", "file": "Qwen3-8B-Q4_K_M.gguf", "size_gb": 5.0},
236
  {"name": "Qwen3.8 27B", "repo": "unsloth/Qwen3.8-27B-GGUF", "file": "Qwen3.8-27B-Q4_0.gguf", "size_gb": 16.0},
237
  ]
238
- _BENCHMARK_CACHE = os.path.join(tempfile.gettempdir(), "llm_benchmark")
239
- _BENCHMARK_RESULTS = []
240
- _LLAMA_CPP_BIN = None
241
 
242
 
243
- def _ensure_llama_cpp():
244
- global _LLAMA_CPP_BIN
245
- if _LLAMA_CPP_BIN and os.path.isfile(_LLAMA_CPP_BIN):
246
- return _LLAMA_CPP_BIN
247
  import subprocess
248
- os.makedirs(_BENCHMARK_CACHE, exist_ok=True)
249
- bin_dir = os.path.join(_BENCHMARK_CACHE, "llama_bin")
250
- llama_cli = os.path.join(bin_dir, "llama-cli")
251
- if os.path.isfile(llama_cli):
252
- _LLAMA_CPP_BIN = llama_cli
253
- return llama_cli
254
- _log("Benchmark: building llama.cpp from source...")
255
- src_dir = os.path.join(_BENCHMARK_CACHE, "llama_src")
256
- if os.path.isdir(src_dir):
257
- shutil.rmtree(src_dir, ignore_errors=True)
258
- subprocess.check_call(["git", "clone", "--depth=1", "https://github.com/ggml-org/llama.cpp.git", src_dir], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
259
- n_threads = _parse_cpu_count() or 8
260
- build_dir = os.path.join(_BENCHMARK_CACHE, "llama_build")
261
- os.makedirs(build_dir, exist_ok=True)
262
- os.makedirs(bin_dir, exist_ok=True)
263
- subprocess.check_call(["cmake", "-S", src_dir, "-B", build_dir, "-DLLAMA_NATIVE=ON", "-DBUILD_SHARED_LIBS=OFF"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
264
- subprocess.check_call(["cmake", "--build", build_dir, "-j", str(n_threads), "--target", "llama-cli"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
265
- import glob as _glob
266
- candidates = _glob.glob(os.path.join(build_dir, "**", "llama-cli"), recursive=True)
267
- if candidates:
268
- shutil.copy2(candidates[0], llama_cli)
269
- os.chmod(llama_cli, 0o755)
270
- else:
271
- raise RuntimeError("llama-cli not found after build")
272
- _LLAMA_CPP_BIN = llama_cli
273
- return llama_cli
274
-
275
-
276
- def _run_benchmark(model_filter=None):
277
- global _BENCHMARK_RESULTS
278
- if _BENCHMARK_RESULTS and not model_filter:
279
- return _BENCHMARK_RESULTS
280
  import subprocess, re
281
  from huggingface_hub import hf_hub_download
282
- os.makedirs(_BENCHMARK_CACHE, exist_ok=True)
283
- llama_cli = _ensure_llama_cpp()
284
- n_threads = _parse_cpu_count() or 8
285
- prompt = "Explain recursion in programming in 3 sentences."
286
- models = [m for m in _BENCHMARK_MODELS if m["name"] == model_filter] if model_filter else _BENCHMARK_MODELS
287
- if model_filter and not models:
288
- raise ValueError(f"Unknown model. Available: {[m['name'] for m in _BENCHMARK_MODELS]}")
289
  results = []
290
- for model_info in models:
291
- result = {"name": model_info["name"], "status": "error"}
292
  try:
293
- model_path = hf_hub_download(repo_id=model_info["repo"], filename=model_info["file"], cache_dir=_BENCHMARK_CACHE)
294
  env = os.environ.copy()
295
- env["LD_LIBRARY_PATH"] = os.path.dirname(llama_cli)
296
- n_predict = 128 if model_info["size_gb"] <= 6 else 256
297
- timeout_sec = 300 if model_info["size_gb"] <= 6 else 600
298
- cmd = [llama_cli, "-m", model_path, "-t", str(n_threads), "-c", "2048", "-n", str(n_predict), "--temp", "0.7", "--top-p", "0.9", "-p", prompt, "--no-display-prompt", "--log-disable"]
299
  t0 = time.time()
300
- proc = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout_sec, env=env)
301
- wall_time = time.time() - t0
302
- prompt_tps = gen_tps = 0.0
303
- prompt_tok = gen_tok = 0
304
- for line in proc.stderr.splitlines():
305
- tps_m = re.search(r"(\d+\.?\d*) tokens per second", line)
306
- tok_m = re.search(r"/ (\d+) tokens", line)
307
  if "prompt eval" in line.lower():
308
- if tps_m: prompt_tps = float(tps_m.group(1))
309
- if tok_m: prompt_tok = int(tok_m.group(1))
310
  elif "eval time" in line.lower() and "prompt" not in line.lower():
311
- if tps_m: gen_tps = float(tps_m.group(1))
312
- if tok_m: gen_tok = int(tok_m.group(1))
313
- result.update({"status": "success", "total_time_sec": round(wall_time, 2), "prompt_tokens": prompt_tok, "completion_tokens": gen_tok, "prompt_tokens_per_sec": round(prompt_tps, 1), "generation_tokens_per_sec": round(gen_tps, 1), "output": proc.stdout.strip()[:500]})
314
  except Exception as e:
315
- result["error"] = str(e)
316
- results.append(result)
317
- import glob as _glob
318
- for f in _glob.glob(os.path.join(_BENCHMARK_CACHE, "**", "*.gguf"), recursive=True):
319
  try: os.unlink(f)
320
  except: pass
321
- if not model_filter:
322
- _BENCHMARK_RESULTS = results
323
  return results
324
 
325
 
@@ -329,22 +277,19 @@ def _run_benchmark(model_filter=None):
329
 
330
  MODEL_CACHE = {}
331
 
332
-
333
- def load_model(model_name):
334
- if model_name not in MODEL_CACHE:
335
- model = StableAudioModel.from_pretrained(model_name, device="cpu")
336
- MODEL_CACHE[model_name] = model
337
- return MODEL_CACHE[model_name]
338
-
339
 
340
  def generate_audio(prompt, duration, steps, cfg_scale, seed, model_name):
341
  model = load_model(model_name)
342
  audio = model.generate(prompt=prompt, duration=duration, steps=steps, cfg_scale=cfg_scale, seed=seed, batch_size=1)
343
  audio = rearrange(audio, "b d n -> d (b n)")
344
  audio = audio.to(torch.float32).clamp(-1, 1).mul(32767).to(torch.int16).cpu()
345
- output_path = os.path.join(tempfile.gettempdir(), f"stable_audio_{seed}_{hash(prompt) & 0xFFFFFFFF:08x}.wav")
346
- torchaudio.save(output_path, audio, 44100)
347
- return output_path
348
 
349
 
350
  # ---------------------------------------------------------------------------
@@ -355,12 +300,12 @@ with gr.Blocks(title="Respite API") as demo:
355
  gr.Markdown("# Respite API")
356
  with gr.Row():
357
  with gr.Column():
358
- model_name = gr.Dropdown(choices=["small-music", "small-sfx"], value="small-music", label="Model")
359
  prompt = gr.Textbox(label="Prompt", placeholder="Describe the audio...", lines=2)
360
- duration = gr.Slider(minimum=1, maximum=120, value=30, step=1, label="Duration (seconds)")
361
  steps = gr.Slider(minimum=1, maximum=50, value=8, step=1, label="Steps")
362
  cfg_scale = gr.Slider(minimum=0.0, maximum=10.0, value=1.0, step=0.1, label="CFG Scale")
363
- seed = gr.Number(value=-1, label="Seed (-1 for random)")
364
  btn = gr.Button("Generate", variant="primary")
365
  with gr.Column():
366
  audio_output = gr.Audio(label="Generated Audio", type="filepath")
@@ -368,72 +313,48 @@ with gr.Blocks(title="Respite API") as demo:
368
 
369
 
370
  # ---------------------------------------------------------------------------
371
- # After Gradio is launched, inject routes into the running server.
372
- #
373
- # Strategy: Gradio blocks while the server runs, so we start a thread that
374
- # waits for the server to be ready, then adds our routes to it.
375
  # ---------------------------------------------------------------------------
376
 
377
- def _inject_routes():
378
- """Wait for Gradio server to start, then add API routes."""
379
- for attempt in range(60):
380
- time.sleep(2)
381
- # demo.server is set after launch() starts uvicorn
382
- server = getattr(demo, "server", None)
383
- if server is None:
384
- continue
385
- app = getattr(server, "app", None)
386
- if app is None:
387
- continue
388
- _log(f"Injecting routes (attempt {attempt+1})...")
 
 
 
 
 
 
 
 
 
 
389
  try:
390
- # Add all API routes
391
- from fastapi import Request as _Req
392
-
393
- @app.get("/respite/resources")
394
- async def _resources():
395
- return JSONResponse({"resources": API_RESOURCES})
396
-
397
- @app.get("/respite/specs")
398
- async def _specs():
399
- return JSONResponse({**API_SPECS, "runtime": _get_runtime_specs()})
400
-
401
- @app.get("/respite/search")
402
- async def _search(q: str = "", backend: str = "text", max_results: int = 10, extract_top: int = 0, region: str = "wt-wt"):
403
- if not q:
404
- return JSONResponse({"error": "Missing 'q' parameter"}, status_code=400)
405
- try:
406
- return JSONResponse(_search_web(q, backend=backend, max_results=max_results, extract_top=extract_top, region=region))
407
- except Exception as e:
408
- return JSONResponse({"error": str(e)}, status_code=502)
409
-
410
- @app.get("/respite/benchmark")
411
- async def _benchmark(force: bool = False, model: str = ""):
412
- try:
413
- global _BENCHMARK_RESULTS
414
- if force:
415
- _BENCHMARK_RESULTS = []
416
- results = _run_benchmark(model_filter=model.strip() or None)
417
- return JSONResponse({"status": "complete", "models": results, "system": {"cpu_cores": _parse_cpu_count(), "ram_bytes": _parse_ram_bytes(), "threads": _parse_cpu_count() or 8}})
418
- except Exception as e:
419
- return JSONResponse({"error": str(e)}, status_code=500)
420
-
421
- _log("Routes injected into running server!")
422
- return
423
- except Exception as e:
424
- _log(f"Route injection failed: {e}")
425
- _log("WARNING: Could not inject routes after 120s")
426
 
427
 
428
- # Start route injection in background
429
- _inject_thread = threading.Thread(target=_inject_routes, daemon=True)
430
- _inject_thread.start()
431
 
432
  # Disable file watcher
433
  import gradio.utils as _gr_utils
434
  if hasattr(_gr_utils, "watchfn_spaces"):
435
  _gr_utils.watchfn_spaces = lambda *a, **kw: None
436
 
437
- _log("Starting Gradio server...")
438
  demo.queue(max_size=4, default_concurrency_limit=1)
439
- demo.launch()
 
25
  if hf_token:
26
  login(token=hf_token)
27
 
 
28
  @spaces.GPU(duration=1)
29
  def _gpu_startup_touch():
30
  return "ok"
31
 
 
32
  try:
33
  _gpu_startup_touch()
34
  except Exception as e:
35
  print(f"Warning: ZeroGPU touch failed: {e}", flush=True)
36
 
37
 
38
+ def _log(msg):
39
+ print(msg.encode("ascii", "backslashreplace").decode("ascii"), flush=True)
40
 
41
 
42
+ def _read_cgroup(path):
43
  try:
44
  with open(path, "r", encoding="utf-8") as f:
45
  return f.read().strip()
 
48
 
49
 
50
  def _parse_cpu_count():
51
+ v = _read_cgroup("/sys/fs/cgroup/cpu.max")
52
+ if v:
53
+ p = v.split()
54
+ if len(p) == 2 and p[0] != "max":
55
  try:
56
+ q, per = int(p[0]), int(p[1])
57
+ if per > 0: return q // per
58
+ except: pass
59
+ v = _read_cgroup("/sys/fs/cgroup/cpuset.cpus")
60
+ if v:
61
+ c = 0
62
+ for part in v.split(","):
 
 
 
 
 
 
 
 
 
 
 
63
  if "-" in part:
64
+ s, e = part.split("-", 1)
65
+ c += int(e) - int(s) + 1
66
  else:
67
+ c += 1
68
+ if c > 0: return c
 
69
  return os.cpu_count()
70
 
71
 
72
+ def _parse_ram():
73
+ v = _read_cgroup("/sys/fs/cgroup/memory.max")
74
+ if v and v != "max":
75
  try:
76
+ val = int(v)
77
+ if val < 2**62: return val
78
+ except: pass
 
 
 
 
 
 
 
 
 
 
79
  try:
80
+ with open("/proc/meminfo", "r", encoding="utf-8") as f:
81
+ for line in f:
82
  if line.startswith("MemTotal:"):
83
  return int(line.split()[1]) * 1024
84
+ except: pass
 
85
  return None
86
 
87
 
 
111
  "endpoint": "/respite/search",
112
  "method": "GET",
113
  "input": {
114
+ "q": "string",
115
+ "backend": "'text', 'news', or 'images'",
116
  "max_results": "integer (1-50, default 10)",
117
  "extract_top": "integer (0-5, default 0)",
118
  "region": "string (default 'wt-wt')",
 
140
 
141
  def _get_runtime_specs():
142
  storage = shutil.disk_usage(os.getcwd())
143
+ hw = os.environ.get("SPACE_HARDWARE", "zero-a10g")
144
+ is_zgpu = "zero" in hw.lower()
145
  return {
146
  "platform": platform.platform(),
147
  "python_version": platform.python_version(),
148
  "cpu_cores": _parse_cpu_count(),
149
  "host_cpu_cores": os.cpu_count(),
150
+ "ram_bytes": _parse_ram(),
151
  "storage_total_bytes": storage.total,
152
  "storage_used_bytes": storage.used,
153
  "storage_free_bytes": storage.free,
154
+ "hardware": hw,
155
+ "gpu": {"type": "NVIDIA RTX Pro 6000 Blackwell", "vram_bytes": 48*1024**3, "shared_zero_gpu": True} if is_zgpu else None,
 
 
 
 
156
  }
157
 
158
 
159
  # ---------------------------------------------------------------------------
160
+ # Web search
161
  # ---------------------------------------------------------------------------
162
 
163
  try:
 
168
  max_results = max(1, min(50, max_results))
169
  with DDGS() as ddgs:
170
  if backend == "news":
171
+ results = [{"title": r.get("title",""), "url": r.get("url",""), "content": r.get("body",""), "source": r.get("source",""), "date": r.get("date","")} for r in ddgs.news(query, max_results=max_results, region=region)]
 
172
  elif backend == "images":
173
+ results = [{"title": r.get("title",""), "url": r.get("image",""), "source": r.get("source","")} for r in ddgs.images(query, max_results=max_results, region=region)]
 
174
  else:
175
+ results = [{"title": r.get("title",""), "url": r.get("href",""), "content": r.get("body","")} for r in ddgs.text(query, max_results=max_results, region=region)]
 
176
  if extract_top > 0 and results and backend != "images":
 
177
  with DDGS() as ddgs:
178
+ for url in [r["url"] for r in results[:extract_top] if r.get("url")]:
179
  try:
180
+ ex = ddgs.extract(url)
181
+ ct = ex.get("content","")
182
+ if ct:
183
  for r in results:
184
  if r["url"] == url:
185
+ r["extracted_content"] = ct[:8000]
186
  break
187
+ except: pass
 
188
  return {"query": query, "backend": backend, "results": results, "total_results": len(results)}
189
 
190
  except ImportError:
 
193
 
194
 
195
  # ---------------------------------------------------------------------------
196
+ # Benchmark
197
  # ---------------------------------------------------------------------------
198
 
199
+ _BENCH_MODELS = [
200
  {"name": "Gemma 4 E4B", "repo": "unsloth/gemma-4-E4B-it-GGUF", "file": "gemma-4-E4B-it-Q4_K_M.gguf", "size_gb": 2.5},
201
  {"name": "Qwen3 8B", "repo": "Qwen/Qwen3-8B-GGUF", "file": "Qwen3-8B-Q4_K_M.gguf", "size_gb": 5.0},
202
  {"name": "Qwen3.8 27B", "repo": "unsloth/Qwen3.8-27B-GGUF", "file": "Qwen3.8-27B-Q4_0.gguf", "size_gb": 16.0},
203
  ]
204
+ _BENCH_CACHE = os.path.join(tempfile.gettempdir(), "llm_bench")
205
+ _BENCH_RESULTS = []
 
206
 
207
 
208
+ def _ensure_llama():
209
+ d = os.path.join(_BENCH_CACHE, "bin")
210
+ cli = os.path.join(d, "llama-cli")
211
+ if os.path.isfile(cli): return cli
212
  import subprocess
213
+ os.makedirs(d, exist_ok=True)
214
+ src = os.path.join(_BENCH_CACHE, "src")
215
+ if os.path.isdir(src): shutil.rmtree(src, ignore_errors=True)
216
+ _log("Building llama.cpp...")
217
+ subprocess.check_call(["git","clone","--depth=1","https://github.com/ggml-org/llama.cpp.git",src], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
218
+ bd = os.path.join(_BENCH_CACHE, "build")
219
+ os.makedirs(bd, exist_ok=True)
220
+ nt = _parse_cpu_count() or 8
221
+ subprocess.check_call(["cmake","-S",src,"-B",bd,"-DLLAMA_NATIVE=ON","-DBUILD_SHARED_LIBS=OFF"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
222
+ subprocess.check_call(["cmake","--build",bd,"-j",str(nt),"--target","llama-cli"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
223
+ import glob as _g
224
+ cands = _g.glob(os.path.join(bd,"**","llama-cli"), recursive=True)
225
+ if cands: shutil.copy2(cands[0], cli); os.chmod(cli, 0o755)
226
+ else: raise RuntimeError("llama-cli not found")
227
+ return cli
228
+
229
+
230
+ def _run_bench(model_filter=None):
231
+ global _BENCH_RESULTS
232
+ if _BENCH_RESULTS and not model_filter: return _BENCH_RESULTS
 
 
 
 
 
 
 
 
 
 
 
 
233
  import subprocess, re
234
  from huggingface_hub import hf_hub_download
235
+ os.makedirs(_BENCH_CACHE, exist_ok=True)
236
+ cli = _ensure_llama()
237
+ nt = _parse_cpu_count() or 8
238
+ prompt = "Explain recursion in 3 sentences."
239
+ models = [m for m in _BENCH_MODELS if m["name"]==model_filter] if model_filter else _BENCH_MODELS
 
 
240
  results = []
241
+ for mi in models:
242
+ r = {"name": mi["name"], "status": "error"}
243
  try:
244
+ mp = hf_hub_download(repo_id=mi["repo"], filename=mi["file"], cache_dir=_BENCH_CACHE)
245
  env = os.environ.copy()
246
+ env["LD_LIBRARY_PATH"] = os.path.dirname(cli)
247
+ np = 128 if mi["size_gb"]<=6 else 256
248
+ to = 300 if mi["size_gb"]<=6 else 600
 
249
  t0 = time.time()
250
+ p = subprocess.run([cli,"-m",mp,"-t",str(nt),"-c","2048","-n",str(np),"--temp","0.7","--top-p","0.9","-p",prompt,"--no-display-prompt","--log-disable"], capture_output=True, text=True, timeout=to, env=env)
251
+ wt = time.time()-t0
252
+ pt=gt=0.0; ptk=gtk=0
253
+ for line in p.stderr.splitlines():
254
+ tm = re.search(r"(\d+\.?\d*) tokens per second", line)
255
+ km = re.search(r"/ (\d+) tokens", line)
 
256
  if "prompt eval" in line.lower():
257
+ if tm: pt=float(tm.group(1))
258
+ if km: ptk=int(km.group(1))
259
  elif "eval time" in line.lower() and "prompt" not in line.lower():
260
+ if tm: gt=float(tm.group(1))
261
+ if km: gtk=int(km.group(1))
262
+ r.update({"status":"success","total_time_sec":round(wt,2),"prompt_tokens":ptk,"completion_tokens":gtk,"prompt_tokens_per_sec":round(pt,1),"generation_tokens_per_sec":round(gt,1),"output":p.stdout.strip()[:500]})
263
  except Exception as e:
264
+ r["error"] = str(e)
265
+ results.append(r)
266
+ import glob as _g
267
+ for f in _g.glob(os.path.join(_BENCH_CACHE,"**","*.gguf"), recursive=True):
268
  try: os.unlink(f)
269
  except: pass
270
+ if not model_filter: _BENCH_RESULTS = results
 
271
  return results
272
 
273
 
 
277
 
278
  MODEL_CACHE = {}
279
 
280
+ def load_model(name):
281
+ if name not in MODEL_CACHE:
282
+ MODEL_CACHE[name] = StableAudioModel.from_pretrained(name, device="cpu")
283
+ return MODEL_CACHE[name]
 
 
 
284
 
285
  def generate_audio(prompt, duration, steps, cfg_scale, seed, model_name):
286
  model = load_model(model_name)
287
  audio = model.generate(prompt=prompt, duration=duration, steps=steps, cfg_scale=cfg_scale, seed=seed, batch_size=1)
288
  audio = rearrange(audio, "b d n -> d (b n)")
289
  audio = audio.to(torch.float32).clamp(-1, 1).mul(32767).to(torch.int16).cpu()
290
+ out = os.path.join(tempfile.gettempdir(), f"stable_audio_{seed}_{hash(prompt)&0xFFFFFFFF:08x}.wav")
291
+ torchaudio.save(out, audio, 44100)
292
+ return out
293
 
294
 
295
  # ---------------------------------------------------------------------------
 
300
  gr.Markdown("# Respite API")
301
  with gr.Row():
302
  with gr.Column():
303
+ model_name = gr.Dropdown(choices=["small-music","small-sfx"], value="small-music", label="Model")
304
  prompt = gr.Textbox(label="Prompt", placeholder="Describe the audio...", lines=2)
305
+ duration = gr.Slider(minimum=1, maximum=120, value=30, step=1, label="Duration")
306
  steps = gr.Slider(minimum=1, maximum=50, value=8, step=1, label="Steps")
307
  cfg_scale = gr.Slider(minimum=0.0, maximum=10.0, value=1.0, step=0.1, label="CFG Scale")
308
+ seed = gr.Number(value=-1, label="Seed (-1=random)")
309
  btn = gr.Button("Generate", variant="primary")
310
  with gr.Column():
311
  audio_output = gr.Audio(label="Generated Audio", type="filepath")
 
313
 
314
 
315
  # ---------------------------------------------------------------------------
316
+ # Monkey-patch: intercept Gradio App creation to inject routes
 
 
 
317
  # ---------------------------------------------------------------------------
318
 
319
+ from gradio.routes import App as GradioApp
320
+
321
+ _orig_create_app = GradioApp.create_app
322
+
323
+
324
+ def _patched_create_app(blocks, **kwargs):
325
+ fa_app = _orig_create_app(blocks, **kwargs)
326
+
327
+ @fa_app.get("/respite/resources")
328
+ def _r(): return JSONResponse({"resources": API_RESOURCES})
329
+
330
+ @fa_app.get("/respite/specs")
331
+ def _s(): return JSONResponse({**API_SPECS, "runtime": _get_runtime_specs()})
332
+
333
+ @fa_app.get("/respite/search")
334
+ def _q(q: str="", backend: str="text", max_results: int=10, extract_top: int=0, region: str="wt-wt"):
335
+ if not q: return JSONResponse({"error":"Missing 'q'"}, status_code=400)
336
+ try: return JSONResponse(_search_web(q, backend=backend, max_results=max_results, extract_top=extract_top, region=region))
337
+ except Exception as e: return JSONResponse({"error": str(e)}, status_code=502)
338
+
339
+ @fa_app.get("/respite/benchmark")
340
+ def _b(force: bool=False, model: str=""):
341
  try:
342
+ global _BENCH_RESULTS
343
+ if force: _BENCH_RESULTS = []
344
+ results = _run_bench(model_filter=model.strip() or None)
345
+ return JSONResponse({"status":"complete","models":results,"system":{"cpu_cores":_parse_cpu_count(),"ram_bytes":_parse_ram(),"threads":_parse_cpu_count() or 8}})
346
+ except Exception as e: return JSONResponse({"error": str(e)}, status_code=500)
347
+
348
+ _log("Routes injected via create_app monkey-patch")
349
+ return fa_app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
 
352
+ GradioApp.create_app = _patched_create_app
 
 
353
 
354
  # Disable file watcher
355
  import gradio.utils as _gr_utils
356
  if hasattr(_gr_utils, "watchfn_spaces"):
357
  _gr_utils.watchfn_spaces = lambda *a, **kw: None
358
 
 
359
  demo.queue(max_size=4, default_concurrency_limit=1)
360
+ demo.launch(ssr_mode=False)