Spaces:
Running
Running
chore(sync): mirror backend .py + Dockerfile to Space (hf-sync-backend)
Browse filesAutomated backend sync from szl-holdings/a11oy main via hf-sync-backend.
Updated (differed from the Space): serve.py, szl_energy_operator.py
Deleted (gone from the repo + Dockerfile COPY set): (none)
Keeps the Space-built backend (serve.py + the Dockerfile-COPY'd .py
modules) identical to GitHub main so the Space never rebuilds from a
stale backend, new endpoints don't 404 there, and orphaned modules
removed from the repo don't linger in the Space tree.
- serve.py +25 -12
- szl_energy_operator.py +33 -8
serve.py
CHANGED
|
@@ -4930,26 +4930,39 @@ async def preflight_status() -> JSONResponse:
|
|
| 4930 |
|
| 4931 |
@app.get("/api/a11oy/readyz")
|
| 4932 |
async def readyz() -> JSONResponse:
|
| 4933 |
-
#
|
| 4934 |
-
#
|
| 4935 |
-
#
|
| 4936 |
-
#
|
| 4937 |
-
|
| 4938 |
-
|
| 4939 |
-
operator = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4940 |
try:
|
| 4941 |
import szl_energy_operator as _szl_eo_ready
|
| 4942 |
operator = _szl_eo_ready.readiness()
|
| 4943 |
-
except Exception as _eo_ready_e: # pragma: no cover —
|
| 4944 |
-
operator
|
| 4945 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4946 |
return JSONResponse(
|
| 4947 |
{
|
| 4948 |
-
"status":
|
| 4949 |
"backend": "local+proxy",
|
| 4950 |
"operator": operator,
|
| 4951 |
},
|
| 4952 |
-
status_code=200 if
|
| 4953 |
)
|
| 4954 |
|
| 4955 |
|
|
|
|
| 4930 |
|
| 4931 |
@app.get("/api/a11oy/readyz")
|
| 4932 |
async def readyz() -> JSONResponse:
|
| 4933 |
+
# Keep service readiness separate from optional capability readiness. A public
|
| 4934 |
+
# CPU-only surface may serve in a truthful degraded state, while a deployment
|
| 4935 |
+
# that declares the operator required must fail closed. A reachable lung with
|
| 4936 |
+
# a stopped loop remains a hard 503 redeploy-stall signal.
|
| 4937 |
+
required = (os.environ.get("A11OY_ENERGY_OPERATOR_REQUIRED", "0").strip().lower()
|
| 4938 |
+
not in ("0", "false", "no", "off", ""))
|
| 4939 |
+
operator = {
|
| 4940 |
+
"service": "energy-operator",
|
| 4941 |
+
"ready": False,
|
| 4942 |
+
"service_ready": not required,
|
| 4943 |
+
"required": required,
|
| 4944 |
+
"state": "unknown",
|
| 4945 |
+
"reason": "operator_check_unavailable",
|
| 4946 |
+
}
|
| 4947 |
try:
|
| 4948 |
import szl_energy_operator as _szl_eo_ready
|
| 4949 |
operator = _szl_eo_ready.readiness()
|
| 4950 |
+
except Exception as _eo_ready_e: # pragma: no cover — fail closed when required
|
| 4951 |
+
operator["reason"] = f"operator_check_error:{type(_eo_ready_e).__name__}"
|
| 4952 |
+
service_ready = bool(operator.get("service_ready", False))
|
| 4953 |
+
capability_ready = bool(operator.get("ready", False))
|
| 4954 |
+
status = (
|
| 4955 |
+
"ready" if service_ready and capability_ready
|
| 4956 |
+
else "ready_degraded" if service_ready
|
| 4957 |
+
else "not_ready"
|
| 4958 |
+
)
|
| 4959 |
return JSONResponse(
|
| 4960 |
{
|
| 4961 |
+
"status": status,
|
| 4962 |
"backend": "local+proxy",
|
| 4963 |
"operator": operator,
|
| 4964 |
},
|
| 4965 |
+
status_code=200 if service_ready else 503,
|
| 4966 |
)
|
| 4967 |
|
| 4968 |
|
szl_energy_operator.py
CHANGED
|
@@ -1524,6 +1524,7 @@ def handle_status() -> dict:
|
|
| 1524 |
# Env flag gating the boot auto-start. Defaults ON ("1"). Set to "0"/"false" to
|
| 1525 |
# disable (e.g. a box that should stay idle until a manual press-play).
|
| 1526 |
A11OY_ENERGY_AUTOSTART_ENV = "A11OY_ENERGY_AUTOSTART"
|
|
|
|
| 1527 |
|
| 1528 |
|
| 1529 |
def autostart_enabled() -> bool:
|
|
@@ -1532,6 +1533,12 @@ def autostart_enabled() -> bool:
|
|
| 1532 |
not in ("0", "false", "no", "off", ""))
|
| 1533 |
|
| 1534 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1535 |
def autostart_if_lung_reachable() -> dict:
|
| 1536 |
"""Boot hook: auto-press-play the operator loop, but ONLY when honest to do so.
|
| 1537 |
|
|
@@ -1559,23 +1566,41 @@ def autostart_if_lung_reachable() -> dict:
|
|
| 1559 |
|
| 1560 |
|
| 1561 |
def readiness() -> dict:
|
| 1562 |
-
"""
|
| 1563 |
|
| 1564 |
-
|
| 1565 |
-
|
| 1566 |
-
|
| 1567 |
-
|
|
|
|
|
|
|
| 1568 |
op = get_operator()
|
| 1569 |
running = op.is_running()
|
| 1570 |
lung = op.any_lung_reachable()
|
| 1571 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1572 |
return {
|
| 1573 |
"service": "energy-operator",
|
| 1574 |
-
"ready":
|
|
|
|
|
|
|
|
|
|
| 1575 |
"operator_running": running,
|
| 1576 |
"lung_reachable": lung,
|
| 1577 |
"autostart_enabled": autostart_enabled(),
|
| 1578 |
-
"reason":
|
| 1579 |
}
|
| 1580 |
|
| 1581 |
|
|
|
|
| 1524 |
# Env flag gating the boot auto-start. Defaults ON ("1"). Set to "0"/"false" to
|
| 1525 |
# disable (e.g. a box that should stay idle until a manual press-play).
|
| 1526 |
A11OY_ENERGY_AUTOSTART_ENV = "A11OY_ENERGY_AUTOSTART"
|
| 1527 |
+
A11OY_ENERGY_OPERATOR_REQUIRED_ENV = "A11OY_ENERGY_OPERATOR_REQUIRED"
|
| 1528 |
|
| 1529 |
|
| 1530 |
def autostart_enabled() -> bool:
|
|
|
|
| 1533 |
not in ("0", "false", "no", "off", ""))
|
| 1534 |
|
| 1535 |
|
| 1536 |
+
def operator_required() -> bool:
|
| 1537 |
+
"""Return whether deployment readiness requires this capability."""
|
| 1538 |
+
return (os.environ.get(A11OY_ENERGY_OPERATOR_REQUIRED_ENV, "0").strip().lower()
|
| 1539 |
+
not in ("0", "false", "no", "off", ""))
|
| 1540 |
+
|
| 1541 |
+
|
| 1542 |
def autostart_if_lung_reachable() -> dict:
|
| 1543 |
"""Boot hook: auto-press-play the operator loop, but ONLY when honest to do so.
|
| 1544 |
|
|
|
|
| 1566 |
|
| 1567 |
|
| 1568 |
def readiness() -> dict:
|
| 1569 |
+
"""Return separate capability and deployment-readiness truth.
|
| 1570 |
|
| 1571 |
+
``ready`` describes the operator capability and is true only when the loop
|
| 1572 |
+
runs. ``service_ready`` controls the host readiness contract: an explicitly
|
| 1573 |
+
optional, unreachable operator degrades the host without removing it from
|
| 1574 |
+
service. A reachable-but-stopped loop always fails readiness because that is
|
| 1575 |
+
the redeploy-stall failure this guard exists to catch.
|
| 1576 |
+
"""
|
| 1577 |
op = get_operator()
|
| 1578 |
running = op.is_running()
|
| 1579 |
lung = op.any_lung_reachable()
|
| 1580 |
+
required = operator_required()
|
| 1581 |
+
if running:
|
| 1582 |
+
state = "ready"
|
| 1583 |
+
reason = "ok"
|
| 1584 |
+
service_ready = True
|
| 1585 |
+
elif lung:
|
| 1586 |
+
state = "stopped"
|
| 1587 |
+
reason = "operator_stopped_while_lung_reachable"
|
| 1588 |
+
service_ready = False
|
| 1589 |
+
else:
|
| 1590 |
+
state = "unavailable"
|
| 1591 |
+
reason = ("required_lung_unreachable" if required
|
| 1592 |
+
else "optional_lung_unreachable")
|
| 1593 |
+
service_ready = not required
|
| 1594 |
return {
|
| 1595 |
"service": "energy-operator",
|
| 1596 |
+
"ready": running,
|
| 1597 |
+
"service_ready": service_ready,
|
| 1598 |
+
"required": required,
|
| 1599 |
+
"state": state,
|
| 1600 |
"operator_running": running,
|
| 1601 |
"lung_reachable": lung,
|
| 1602 |
"autostart_enabled": autostart_enabled(),
|
| 1603 |
+
"reason": reason,
|
| 1604 |
}
|
| 1605 |
|
| 1606 |
|