dev0524 commited on
Commit
630cd8b
·
verified ·
1 Parent(s): 424de06

scorevision: push artifact

Browse files
Files changed (1) hide show
  1. warmup_daemon.py +94 -0
warmup_daemon.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Schedule-locked warmup for the Detect-Person chute.
3
+
4
+ TurboVision challenges fire at blocks that are multiples of `WINDOW_BLOCKS`
5
+ (300), ~61 min apart. A cold chute returns HTTP 503 instantly and the
6
+ validator's own warmup helper is broken, so a cold window scores 0. This
7
+ daemon keeps an instance alive only for the minutes around each window:
8
+
9
+ - wakes ~LEAD_SECONDS before the next challenge block
10
+ - pings /predict every PING_EVERY_S until TRAIL_SECONDS past the block
11
+ - sleeps the rest of the hour (instance scales to zero on its own)
12
+
13
+ Run it wherever it can reach api.chutes.ai and the bittensor chain. It only
14
+ needs CHUTES_API_KEY in the environment (source your .env).
15
+ """
16
+
17
+ import base64
18
+ import os
19
+ import time
20
+ import urllib.request
21
+ import json
22
+
23
+ SLUG = os.getenv("SV_CHUTE_SLUG", "aiyoshida-turbovision-dev0524-whale")
24
+ WINDOW_BLOCKS = int(os.getenv("SV_WINDOW_BLOCKS", "300"))
25
+ LEAD_SECONDS = int(os.getenv("SV_LEAD_SECONDS", "420")) # start warming T-7min
26
+ TRAIL_SECONDS = int(os.getenv("SV_TRAIL_SECONDS", "300")) # keep warm to T+5min
27
+ PING_EVERY_S = int(os.getenv("SV_PING_EVERY_S", "40"))
28
+ BLOCK_TIME_S = float(os.getenv("SV_BLOCK_TIME_S", "12.3"))
29
+ API_KEY = os.environ["CHUTES_API_KEY"]
30
+
31
+ _TINY_PNG = base64.b64decode(
32
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="
33
+ )
34
+ _PING_BODY = json.dumps(
35
+ {"frames": [{"frame_id": 0, "data": base64.b64encode(_TINY_PNG).decode()}],
36
+ "meta": {"batch_size": 1, "n_keypoints": 0}}
37
+ ).encode()
38
+
39
+
40
+ def log(msg: str) -> None:
41
+ print(f"{time.strftime('%Y-%m-%d %H:%M:%S')} {msg}", flush=True)
42
+
43
+
44
+ def current_block() -> int:
45
+ from bittensor import Subtensor
46
+
47
+ st = Subtensor(network=os.getenv("BITTENSOR_SUBTENSOR_ENDPOINT", "finney"))
48
+ return int(st.get_current_block())
49
+
50
+
51
+ def ping() -> str:
52
+ req = urllib.request.Request(
53
+ f"https://{SLUG}.chutes.ai/predict",
54
+ data=_PING_BODY,
55
+ headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"},
56
+ method="POST",
57
+ )
58
+ try:
59
+ with urllib.request.urlopen(req, timeout=120) as r:
60
+ ok = json.loads(r.read()).get("success")
61
+ return f"200 success={ok}"
62
+ except urllib.error.HTTPError as e:
63
+ return f"{e.code} {e.read()[:120].decode(errors='ignore')}"
64
+ except Exception as e:
65
+ return f"{type(e).__name__}: {e}"
66
+
67
+
68
+ def seconds_to_next_window() -> tuple[int, float]:
69
+ b = current_block()
70
+ next_block = ((b // WINDOW_BLOCKS) + 1) * WINDOW_BLOCKS
71
+ return next_block, (next_block - b) * BLOCK_TIME_S
72
+
73
+
74
+ def main() -> None:
75
+ log(f"warmup daemon start: slug={SLUG} lead={LEAD_SECONDS}s trail={TRAIL_SECONDS}s")
76
+ while True:
77
+ next_block, eta = seconds_to_next_window()
78
+ sleep_for = eta - LEAD_SECONDS
79
+ if sleep_for > 0:
80
+ log(f"next challenge block {next_block} in ~{eta/60:.1f} min; sleeping {sleep_for/60:.1f} min")
81
+ time.sleep(min(sleep_for, 1800))
82
+ continue
83
+
84
+ deadline = time.time() + (eta if eta > 0 else 0) + TRAIL_SECONDS
85
+ log(f"warming for block {next_block} until T+{TRAIL_SECONDS}s")
86
+ while time.time() < deadline:
87
+ log(f" ping -> {ping()}")
88
+ time.sleep(PING_EVERY_S)
89
+ log(f"window {next_block} done; releasing instance")
90
+ time.sleep(60)
91
+
92
+
93
+ if __name__ == "__main__":
94
+ main()