Spaces:
Runtime error
Runtime error
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): 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.
- szl_energy_operator.py +49 -4
szl_energy_operator.py
CHANGED
|
@@ -524,19 +524,64 @@ _EMBED_TEXTS = [
|
|
| 524 |
# ---------------------------------------------------------------------------
|
| 525 |
# Exporter sampling — reuse the EXISTING betterwithage joule-meter path.
|
| 526 |
# ---------------------------------------------------------------------------
|
| 527 |
-
def
|
| 528 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 529 |
try:
|
| 530 |
# Browser-like UA so a Cloudflare-fronted meter (e.g. meter.a-11-oy.com)
|
| 531 |
# does not 403 the request behind bot protection. Honest self-probe.
|
| 532 |
-
req = urllib.request.Request(
|
| 533 |
-
_JOULE_METER_URL, headers={"User-Agent": _PROBE_UA})
|
| 534 |
with urllib.request.urlopen(req, timeout=timeout) as r: # noqa: S310
|
| 535 |
return json.loads(r.read().decode("utf-8", "replace"))
|
| 536 |
except Exception: # noqa: BLE001 — unreachable meter => no sample, stay honest
|
| 537 |
return None
|
| 538 |
|
| 539 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
def _exporter_sample_for_node(meter: Optional[dict], exporter_node: str,
|
| 541 |
now: Optional[float] = None) -> Optional[dict]:
|
| 542 |
"""Build a szl_joules_truth exporter_sample for one node from the meter JSON.
|
|
|
|
| 524 |
# ---------------------------------------------------------------------------
|
| 525 |
# Exporter sampling — reuse the EXISTING betterwithage joule-meter path.
|
| 526 |
# ---------------------------------------------------------------------------
|
| 527 |
+
def _joule_meter_urls() -> list[str]:
|
| 528 |
+
"""All joule-meter URLs to scrape. A11OY_JOULE_METER_URLS (comma-separated) is the
|
| 529 |
+
multi-node form (e.g. omen's meter.a-11-oy.com + the laptop's meter2.a-11-oy.com);
|
| 530 |
+
falls back to the single A11OY_JOULE_METER_URL. De-duped, order preserved."""
|
| 531 |
+
multi = (os.environ.get("A11OY_JOULE_METER_URLS") or "").strip()
|
| 532 |
+
urls = [u.strip() for u in multi.split(",") if u.strip()] if multi else [_JOULE_METER_URL]
|
| 533 |
+
seen, out = set(), []
|
| 534 |
+
for u in urls:
|
| 535 |
+
if u and u not in seen:
|
| 536 |
+
seen.add(u); out.append(u)
|
| 537 |
+
return out
|
| 538 |
+
|
| 539 |
+
|
| 540 |
+
def _fetch_one_meter(url: str, timeout: float) -> Optional[dict]:
|
| 541 |
try:
|
| 542 |
# Browser-like UA so a Cloudflare-fronted meter (e.g. meter.a-11-oy.com)
|
| 543 |
# does not 403 the request behind bot protection. Honest self-probe.
|
| 544 |
+
req = urllib.request.Request(url, headers={"User-Agent": _PROBE_UA})
|
|
|
|
| 545 |
with urllib.request.urlopen(req, timeout=timeout) as r: # noqa: S310
|
| 546 |
return json.loads(r.read().decode("utf-8", "replace"))
|
| 547 |
except Exception: # noqa: BLE001 — unreachable meter => no sample, stay honest
|
| 548 |
return None
|
| 549 |
|
| 550 |
|
| 551 |
+
def _fetch_joule_meter(timeout: float = 4.0) -> Optional[dict]:
|
| 552 |
+
"""Fetch the live NVML joule-meter JSON, or None on any failure (honest).
|
| 553 |
+
|
| 554 |
+
Multi-node: scrapes every URL from _joule_meter_urls() and MERGES their engines[]
|
| 555 |
+
into one meter dict, so per-node lookups (_exporter_sample_for_node) see every GPU
|
| 556 |
+
in the mesh from a single call. Honest by design (Doctrine v11): an unreachable
|
| 557 |
+
meter contributes nothing (never faked); a duplicate engine name keeps the first
|
| 558 |
+
seen (no double-count). Single-URL config → original behaviour, unchanged.
|
| 559 |
+
"""
|
| 560 |
+
urls = _joule_meter_urls()
|
| 561 |
+
if len(urls) == 1:
|
| 562 |
+
return _fetch_one_meter(urls[0], timeout)
|
| 563 |
+
merged_engines: list[dict] = []
|
| 564 |
+
total = 0.0
|
| 565 |
+
seen_names: set = set()
|
| 566 |
+
any_ok = False
|
| 567 |
+
for u in urls:
|
| 568 |
+
d = _fetch_one_meter(u, timeout)
|
| 569 |
+
if not isinstance(d, dict):
|
| 570 |
+
continue
|
| 571 |
+
any_ok = True
|
| 572 |
+
for e in (d.get("engines") or []):
|
| 573 |
+
name = str(e.get("engine") or "").lower()
|
| 574 |
+
if not name or name in seen_names:
|
| 575 |
+
continue
|
| 576 |
+
seen_names.add(name)
|
| 577 |
+
merged_engines.append(e)
|
| 578 |
+
if isinstance(e.get("joules"), (int, float)):
|
| 579 |
+
total += float(e["joules"])
|
| 580 |
+
if not any_ok:
|
| 581 |
+
return None
|
| 582 |
+
return {"engines": merged_engines, "totals": {"joules": round(total, 3)}}
|
| 583 |
+
|
| 584 |
+
|
| 585 |
def _exporter_sample_for_node(meter: Optional[dict], exporter_node: str,
|
| 586 |
now: Optional[float] = None) -> Optional[dict]:
|
| 587 |
"""Build a szl_joules_truth exporter_sample for one node from the meter JSON.
|