Theflame47 commited on
Commit
ebc1fd4
·
verified ·
1 Parent(s): c04bd12

Update Deployment_UI_BE.py

Browse files
Files changed (1) hide show
  1. Deployment_UI_BE.py +74 -96
Deployment_UI_BE.py CHANGED
@@ -152,7 +152,6 @@ def _normalize_blob_url(u: str | None) -> str | None:
152
  u = str(u).strip()
153
  if u.startswith(("http://", "https://")):
154
  return u
155
- # Treat '/x' or 'x' as local to this app (same origin as FE)
156
  if u.startswith("/"):
157
  return f"{_LOCAL_BASE}{u}"
158
  return f"{_LOCAL_BASE}/{u}"
@@ -170,37 +169,35 @@ def _fetch_url(u: str):
170
  def _fetch_blob_from_page():
171
  return _fetch_url(f"{_LOCAL_BASE}/modelblob.json")
172
 
173
- @router.post("/api/ingest/from_landing")
174
- def api_ingest_from_landing(blob_url: str | None = None):
175
- """
176
- Ingest the deployment blob for downstream use.
177
- Mirrors FE behavior: resolve relative paths like '/modelblob.json'
178
- against the app origin.
179
- """
180
- u = _normalize_blob_url(blob_url) or _normalize_blob_url("/modelblob.json")
181
- parsed = _fetch_url(u)
182
- if not parsed:
183
- return JSONResponse({"error": "Blob not available"}, 404)
184
- _ingest_blob(parsed, model_id_hint="", container_image_hint="")
185
- return JSONResponse({"ok": True, "source": u})
186
-
187
- # (Optional compatibility: UI posting to /Deployment_UI; accepts blob_url via query)
188
- @router.post("/Deployment_UI")
189
- async def deployment_ui_ingest(request: Request,
190
- model_id: str = Form(""),
191
- container_image: str = Form(""),
192
- blob: str = Form("")):
193
- """
194
- Legacy entry used by the Deployment UI page.
195
- Prefers blob_url from query string; falls back to the modelblob page JSON.
196
- """
197
- blob_url = request.query_params.get("blob_url")
198
- u = _normalize_blob_url(blob_url) if blob_url else _normalize_blob_url("/modelblob.json")
199
- parsed = _fetch_url(u)
200
- if not parsed:
201
- return HTMLResponse("<pre>Missing blob (no /modelblob.json and no blob_url)</pre>", 400)
202
- _ingest_blob(parsed, model_id_hint=model_id, container_image_hint=container_image)
203
- return RedirectResponse("/Deployment_UI", 303)
204
  # ---------------------------------------------------------------------
205
  # Create instance
206
  # ---------------------------------------------------------------------
@@ -348,8 +345,8 @@ async def api_create_instance(req: Request):
348
  _INST["ip"] = _INST.get("ip") or ""
349
  _INST["port"] = _INST.get("port") or ""
350
  return JSONResponse(content, r.status_code)
351
- # ---------------------------------------------------------------------
352
- # Poll / read instance status + explicit readiness fields
353
  # ---------------------------------------------------------------------
354
  @router.get("/api/compute/pods/{pod_id}")
355
  def api_get_instance(pod_id: str = None):
@@ -380,7 +377,6 @@ def api_get_instance(pod_id: str = None):
380
 
381
  if ip and isinstance(pm, dict) and pm:
382
  # choose mapped public port for the declared internal port; else first mapping
383
- chosen = None
384
  if isinstance(declared, int) and str(declared) in pm:
385
  chosen = str(pm[str(declared)])
386
  else:
@@ -398,45 +394,34 @@ def api_get_instance(pod_id: str = None):
398
  _log_status(f"PORT_MAPPING declared={declared} chosen={chosen} all={pm}")
399
  _log_status(f"RESOLVED_ENDPOINT base={base}")
400
 
401
- # health + route discovery (no 8080 fallback, no proxy)
402
- code, ms, snippet = _probe("GET", f"{base}/health")
403
- _log_status(f"HEALTH code={code} ms={ms} body_snippet={snippet}")
404
-
405
- # --- Vertex-mirrored route + final prompt URL log ---
406
- pr_explicit = (c.get("predictRoute") or "").strip() if isinstance(c, dict) else ""
407
- if pr_explicit:
408
- _INST["predictRoute"] = pr_explicit
409
- _log_status(f"PREDICT_ROUTE_SET {pr_explicit} (from blob)")
410
- elif not _INST.get("predictRoute"):
411
- _INST["predictRoute"] = "/predict"
412
- _log_status("PREDICT_ROUTE_SET /predict (Vertex default)")
413
-
414
- # If still unset for some reason, try lightweight discovery once
415
- if not _INST.get("predictRoute"):
416
- route = _discover_route(base)
417
- if route:
418
- _INST["predictRoute"] = route
419
- _log_status(f"PREDICT_ROUTE_SET {route} (discovered)")
420
- else:
421
- _log_status("PREDICT_ROUTE_SET none")
422
-
423
- # Final prompt URL (prefer IP; else proxy host)
424
- pr = _INST.get("predictRoute") or "/predict"
425
- if _INST.get("ip") and _INST.get("port"):
426
- prompt_url = f"http://{_INST['ip']}:{_INST['port']}{pr}"
427
  else:
428
- proxy_base = f"https://{pid}-{declared}.proxy.runpod.net" if declared else ""
429
- prompt_url = f"{proxy_base}{pr}" if proxy_base else ""
430
- if prompt_url:
 
 
 
 
 
 
431
  _log_status(f"PROMPT_ENDPOINT {prompt_url}")
432
 
433
- # Always include cached readiness data for the UI
434
  merged = {**last, "cachedState": {
435
  "podId": _INST.get("podId"),
436
  "status": _INST.get("status"),
437
  "ip": _INST.get("ip"),
438
  "port": _INST.get("port"),
439
  "predictRoute": _INST.get("predictRoute"),
 
440
  }}
441
  return JSONResponse(merged)
442
 
@@ -492,7 +477,7 @@ async def api_end_all():
492
  return JSONResponse(status_code=500, content={"error": f"RunPod delete failed: {e}"})
493
 
494
  # ---------------------------------------------------------------------
495
- # Wait instance
496
  # ---------------------------------------------------------------------
497
  @router.get("/api/compute/wait_instance")
498
  def api_wait_instance(pod_id: str = None):
@@ -519,43 +504,35 @@ def api_wait_instance(pod_id: str = None):
519
  pass
520
 
521
  if ip and pm:
522
- try:
523
- if isinstance(declared, int) and str(declared) in pm:
524
- port = str(pm[str(declared)])
525
- except Exception:
526
- pass
527
- if not port and "8080" in pm:
528
  port = str(pm["8080"])
529
- elif not port and pm:
530
  port = str(pm[next(iter(pm.keys()))])
531
 
532
  if ip and port:
533
  base = f"http://{ip}:{port}"
534
  _log_status(f"RESOLVED_IP {base}")
535
- code, ms, snippet = _probe("GET", f"{base}/health")
536
- _log_status(f"HEALTH code={code} ms={ms} body_snippet={snippet}")
537
-
538
- # --- Vertex-mirrored route + final prompt URL log ---
539
- pr_explicit = (c.get("predictRoute") or "").strip() if isinstance(c, dict) else ""
540
- if pr_explicit:
541
- _INST["predictRoute"] = pr_explicit
542
- _log_status(f"PREDICT_ROUTE_SET {pr_explicit} (from blob)")
543
- elif not _INST.get("predictRoute"):
544
- _INST["predictRoute"] = "/predict"
545
- _log_status("PREDICT_ROUTE_SET /predict (Vertex default)")
546
-
547
- if not _INST.get("predictRoute"):
548
- route = _discover_route(base)
549
- if route:
550
- _INST["predictRoute"] = route
551
- _log_status(f"PREDICT_ROUTE_SET {route} (discovered)")
552
- else:
553
- _log_status("PREDICT_ROUTE_SET none")
554
 
555
- pr = _INST.get("predictRoute") or "/predict"
556
- prompt_url = f"{base}{pr}"
557
- _log_status(f"PROMPT_ENDPOINT {prompt_url}")
 
 
 
 
 
 
 
 
 
 
 
 
 
558
 
 
559
  try:
560
  cspec = _get_container_spec()
561
  internal, _ = _get_port_and_proto(cspec)
@@ -567,7 +544,7 @@ def api_wait_instance(pod_id: str = None):
567
  except Exception:
568
  pass
569
 
570
- _INST.update({"ip": ip or "", "port": port or "", "status": last.get("desiredStatus", "")})
571
  _save_state()
572
 
573
  merged = {
@@ -579,6 +556,7 @@ def api_wait_instance(pod_id: str = None):
579
  "port": _INST.get("port"),
580
  "base": _INST.get("base"),
581
  "predictRoute": _INST.get("predictRoute"),
 
582
  },
583
  }
584
  return JSONResponse(merged)
 
152
  u = str(u).strip()
153
  if u.startswith(("http://", "https://")):
154
  return u
 
155
  if u.startswith("/"):
156
  return f"{_LOCAL_BASE}{u}"
157
  return f"{_LOCAL_BASE}/{u}"
 
169
  def _fetch_blob_from_page():
170
  return _fetch_url(f"{_LOCAL_BASE}/modelblob.json")
171
 
172
+ # --- UPDATED: healthRoute + predictRoute caching ---
173
+ def _ingest_blob(parsed: dict, model_id_hint: str = "", container_image_hint: str = ""):
174
+ if not isinstance(parsed, dict):
175
+ raise HTTPException(400, "Invalid blob (expected JSON object).")
176
+
177
+ _INST.update({
178
+ "blob": parsed,
179
+ "model_id": model_id_hint or "",
180
+ "container_image_hint": container_image_hint or "",
181
+ })
182
+
183
+ c = (((parsed.get("supportedActions") or {}).get("deploy") or {}).get("containerSpec")
184
+ or parsed.get("container") or {}) or {}
185
+
186
+ for k in ("predictRoute", "healthRoute", "readinessRoute", "livenessRoute"):
187
+ v = c.get(k)
188
+ if isinstance(v, str) and v.strip():
189
+ _INST[k] = v.strip()
190
+
191
+ image_uri = (c.get("imageUri") or "").strip().lower()
192
+ pr_hint, hr_hint = _infer_routes_from_image(image_uri)
193
+
194
+ # mirror Vertex defaults
195
+ if not _INST.get("predictRoute"):
196
+ _INST["predictRoute"] = pr_hint or "/predict"
197
+ if not _INST.get("healthRoute"):
198
+ _INST["healthRoute"] = hr_hint or "/health"
199
+
200
+ return True
 
 
201
  # ---------------------------------------------------------------------
202
  # Create instance
203
  # ---------------------------------------------------------------------
 
345
  _INST["ip"] = _INST.get("ip") or ""
346
  _INST["port"] = _INST.get("port") or ""
347
  return JSONResponse(content, r.status_code)
348
+ # ---------------------------------------------------------------------
349
+ # Poll / read instance status + explicit readiness fields (updated)
350
  # ---------------------------------------------------------------------
351
  @router.get("/api/compute/pods/{pod_id}")
352
  def api_get_instance(pod_id: str = None):
 
377
 
378
  if ip and isinstance(pm, dict) and pm:
379
  # choose mapped public port for the declared internal port; else first mapping
 
380
  if isinstance(declared, int) and str(declared) in pm:
381
  chosen = str(pm[str(declared)])
382
  else:
 
394
  _log_status(f"PORT_MAPPING declared={declared} chosen={chosen} all={pm}")
395
  _log_status(f"RESOLVED_ENDPOINT base={base}")
396
 
397
+ # --- health-first readiness (mirror Vertex), fallback to predict existence ---
398
+ hr = (_INST.get("healthRoute") or "/health").strip()
399
+ pr = (_INST.get("predictRoute") or "/predict").strip()
400
+
401
+ code_h, ms_h, snippet_h = _probe("GET", f"{base}{hr}")
402
+ _log_status(f"HEALTH_PROBE path={hr} code={code_h} ms={ms_h} body_snippet={snippet_h[:120]}")
403
+
404
+ if code_h in (200, 204):
405
+ _INST["status"] = "READY"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  else:
407
+ code_p, ms_p, _ = _probe("HEAD", f"{base}{pr}")
408
+ _log_status(f"PREDICT_PROBE path={pr} code={code_p} ms={ms_p}")
409
+ if code_p in (200, 204, 400, 405):
410
+ _INST["status"] = "READY"
411
+
412
+ # final prompt URL log (unchanged)
413
+ if _INST.get("predictRoute"):
414
+ proute = _INST["predictRoute"]
415
+ prompt_url = f"http://{_INST['ip']}:{_INST['port']}{proute}"
416
  _log_status(f"PROMPT_ENDPOINT {prompt_url}")
417
 
 
418
  merged = {**last, "cachedState": {
419
  "podId": _INST.get("podId"),
420
  "status": _INST.get("status"),
421
  "ip": _INST.get("ip"),
422
  "port": _INST.get("port"),
423
  "predictRoute": _INST.get("predictRoute"),
424
+ "healthRoute": _INST.get("healthRoute"),
425
  }}
426
  return JSONResponse(merged)
427
 
 
477
  return JSONResponse(status_code=500, content={"error": f"RunPod delete failed: {e}"})
478
 
479
  # ---------------------------------------------------------------------
480
+ # Wait instance (updated: same health-first + predict fallback)
481
  # ---------------------------------------------------------------------
482
  @router.get("/api/compute/wait_instance")
483
  def api_wait_instance(pod_id: str = None):
 
504
  pass
505
 
506
  if ip and pm:
507
+ if isinstance(declared, int) and str(declared) in pm:
508
+ port = str(pm[str(declared)])
509
+ elif "8080" in pm:
 
 
 
510
  port = str(pm["8080"])
511
+ elif pm:
512
  port = str(pm[next(iter(pm.keys()))])
513
 
514
  if ip and port:
515
  base = f"http://{ip}:{port}"
516
  _log_status(f"RESOLVED_IP {base}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
 
518
+ hr = (_INST.get("healthRoute") or "/health").strip()
519
+ pr = (_INST.get("predictRoute") or "/predict").strip()
520
+
521
+ code_h, ms_h, snippet_h = _probe("GET", f"{base}{hr}")
522
+ _log_status(f"HEALTH_PROBE path={hr} code={code_h} ms={ms_h} body_snippet={snippet_h[:120]}")
523
+
524
+ if code_h in (200, 204):
525
+ _INST["status"] = "READY"
526
+ else:
527
+ code_p, ms_p, _ = _probe("HEAD", f"{base}{pr}")
528
+ _log_status(f"PREDICT_PROBE path={pr} code={code_p} ms={ms_p}")
529
+ if code_p in (200, 204, 400, 405):
530
+ _INST["status"] = "READY"
531
+
532
+ if _INST.get("predictRoute"):
533
+ _log_status(f"PROMPT_ENDPOINT {base}{_INST['predictRoute']}")
534
 
535
+ # proxy base (unchanged)
536
  try:
537
  cspec = _get_container_spec()
538
  internal, _ = _get_port_and_proto(cspec)
 
544
  except Exception:
545
  pass
546
 
547
+ _INST.update({"ip": ip or "", "port": port or ""})
548
  _save_state()
549
 
550
  merged = {
 
556
  "port": _INST.get("port"),
557
  "base": _INST.get("base"),
558
  "predictRoute": _INST.get("predictRoute"),
559
+ "healthRoute": _INST.get("healthRoute"),
560
  },
561
  }
562
  return JSONResponse(merged)