feat(warmup): per-module canary records (thread + timing observability)
Adds a canary record for each module submitted to the warmup, tracking: canary_id, module, thread_name, thread_id, submit_ts, start_ts, end_ts, elapsed_ms, status, error. Surface: - WarmupManager.canaries() returns list[dict] (defensive copy) - AppController.warmup_canaries() returns list[dict] (delegation) - GET /api/warmup_canaries Hook API endpoint - ApiHookClient.get_warmup_canaries() returns list[dict] Example: the warmup of google.genai records a 1187ms canary on thread controller-io_0 with thread_id 50420, canary_id 1. 11 new tests (8 unit in test_warmup_canaries + 3 in test_api_hooks_warmup). All pass; live_gui smoke test confirms endpoint returns real data.
This commit is contained in:
@@ -364,6 +364,24 @@ class HookHandler(BaseHTTPRequestHandler):
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(payload).encode("utf-8"))
|
||||
elif self.path == "/api/warmup_canaries" or self.path.startswith("/api/warmup_canaries?"):
|
||||
# Per-module import canary records (startup_speedup_20260606 sub-track 4+).
|
||||
# Each record carries canary_id, module, thread_name, thread_id,
|
||||
# submit_ts, start_ts, end_ts, elapsed_ms, status, error.
|
||||
# Cheap (lock-guarded copy on the WarmupManager). Direct call,
|
||||
# no GUI trampoline (the WarmupManager is already thread-safe).
|
||||
controller = _get_app_attr(app, "controller", None)
|
||||
if controller and hasattr(controller, "warmup_canaries"):
|
||||
try:
|
||||
payload = {"canaries": controller.warmup_canaries()}
|
||||
except Exception:
|
||||
payload = {"canaries": []}
|
||||
else:
|
||||
payload = {"canaries": []}
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.end_headers()
|
||||
self.wfile.write(json.dumps(payload).encode("utf-8"))
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
Reference in New Issue
Block a user