naldo commited on
Commit
efbddab
·
1 Parent(s): 2f53f16

Add service watchdog for continuous Minecraft autonomy

Browse files
Files changed (1) hide show
  1. app.py +52 -0
app.py CHANGED
@@ -528,6 +528,54 @@ def _stream(payload: dict[str, Any]) -> Iterator[str]:
528
 
529
  api = FastAPI(title="Programa Voyager OpenAI Endpoint", version="6.0.0")
530
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
  @api.on_event("startup")
532
  def _startup() -> None:
533
  if PRELOAD:
@@ -537,6 +585,7 @@ def _startup() -> None:
537
  STACK.start_async()
538
  if os.environ.get("VOYAGER_AUTOSTART", "1") == "1":
539
  VOYAGER.start_async()
 
540
 
541
  @api.get("/")
542
  def root():
@@ -560,6 +609,9 @@ def health():
560
  "embedding_error": _embed_error or None,
561
  "backend": "cpu-only",
562
  "n_gpu_layers": 0,
 
 
 
563
  "error": _model_error or None,
564
  }
565
 
 
528
 
529
  api = FastAPI(title="Programa Voyager OpenAI Endpoint", version="6.0.0")
530
 
531
+ _supervisor_lock = threading.Lock()
532
+ _supervisor_recoveries = 0
533
+ _supervisor_last_action = "startup"
534
+ _supervisor_last_check = 0.0
535
+
536
+
537
+ def _service_supervisor() -> None:
538
+ global _supervisor_recoveries, _supervisor_last_action, _supervisor_last_check
539
+ backoff = 10
540
+ while True:
541
+ try:
542
+ time.sleep(30)
543
+ _supervisor_last_check = time.time()
544
+ mc = STACK.status()
545
+ processes = mc.get("processes") or {}
546
+ critical = ("minecraft", "viaproxy", "friends")
547
+ dead = [name for name in critical if name in processes and not processes[name].get("running")]
548
+ missing = [name for name in critical if mc.get("state") == "running" and name not in processes]
549
+ if mc.get("state") == "error" or dead or missing:
550
+ with _supervisor_lock:
551
+ _supervisor_recoveries += 1
552
+ _supervisor_last_action = f"minecraft recovery #{_supervisor_recoveries}: dead={dead} missing={missing} state={mc.get('state')}"
553
+ VOYAGER.stop()
554
+ STACK.stop()
555
+ time.sleep(backoff)
556
+ STACK.start_async()
557
+ VOYAGER.start_async()
558
+ backoff = min(backoff * 2, 120)
559
+ continue
560
+
561
+ vs = VOYAGER.status()
562
+ if mc.get("state") == "running" and vs.get("state") in {"error", "exited", "stopped"} and not vs.get("running"):
563
+ with _supervisor_lock:
564
+ _supervisor_recoveries += 1
565
+ _supervisor_last_action = f"voyager recovery #{_supervisor_recoveries}: state={vs.get('state')}"
566
+ VOYAGER.start_async()
567
+ backoff = min(backoff * 2, 120)
568
+ continue
569
+
570
+ if mc.get("state") == "running" and vs.get("running"):
571
+ backoff = 10
572
+ _supervisor_last_action = "healthy"
573
+ except Exception as exc:
574
+ _supervisor_last_action = f"supervisor exception: {type(exc).__name__}: {str(exc)[:160]}"
575
+ time.sleep(min(backoff, 120))
576
+ backoff = min(backoff * 2, 120)
577
+
578
+
579
  @api.on_event("startup")
580
  def _startup() -> None:
581
  if PRELOAD:
 
585
  STACK.start_async()
586
  if os.environ.get("VOYAGER_AUTOSTART", "1") == "1":
587
  VOYAGER.start_async()
588
+ threading.Thread(target=_service_supervisor, daemon=True, name="service-supervisor").start()
589
 
590
  @api.get("/")
591
  def root():
 
609
  "embedding_error": _embed_error or None,
610
  "backend": "cpu-only",
611
  "n_gpu_layers": 0,
612
+ "supervisor_recoveries": _supervisor_recoveries,
613
+ "supervisor_last_action": _supervisor_last_action,
614
+ "supervisor_last_check": _supervisor_last_check or None,
615
  "error": _model_error or None,
616
  }
617