ssookra commited on
Commit
6c420e2
·
verified ·
1 Parent(s): 027ca33

wave-48 backend deploy

Browse files
Files changed (38) hide show
  1. apex/__pycache__/__init__.cpython-312.pyc +0 -0
  2. apex/__pycache__/observability.cpython-312.pyc +0 -0
  3. apex/__pycache__/observability_metrics.cpython-312.pyc +0 -0
  4. apex/__pycache__/server.cpython-312.pyc +0 -0
  5. apex/critics/__pycache__/__init__.cpython-312.pyc +0 -0
  6. apex/critics/__pycache__/orchestrator.cpython-312.pyc +0 -0
  7. apex/guardian/__pycache__/__init__.cpython-312.pyc +0 -0
  8. apex/guardian/__pycache__/audit.cpython-312.pyc +0 -0
  9. apex/instruct/__pycache__/__init__.cpython-312.pyc +0 -0
  10. apex/instruct/__pycache__/coa_parser.cpython-312.pyc +0 -0
  11. apex/instruct/__pycache__/narrator.cpython-312.pyc +0 -0
  12. apex/instruct/__pycache__/openrouter_generator.cpython-312.pyc +0 -0
  13. apex/judges/__pycache__/__init__.cpython-312.pyc +0 -0
  14. apex/judges/__pycache__/coa_diff.cpython-312.pyc +0 -0
  15. apex/lips/__pycache__/__init__.cpython-312.pyc +0 -0
  16. apex/lips/__pycache__/harness.cpython-312.pyc +0 -0
  17. apex/observability.py +26 -1
  18. apex/observability_metrics.py +157 -0
  19. apex/orchestration/__pycache__/__init__.cpython-312.pyc +0 -0
  20. apex/orchestration/__pycache__/audit_log.cpython-312.pyc +0 -0
  21. apex/orchestration/__pycache__/langgraph_runtime.cpython-312.pyc +0 -0
  22. apex/orchestration/__pycache__/session_context.cpython-312.pyc +0 -0
  23. apex/orchestration/__pycache__/what_if_replay.cpython-312.pyc +0 -0
  24. apex/physics/__pycache__/__init__.cpython-312.pyc +0 -0
  25. apex/physics/__pycache__/projection.cpython-312.pyc +0 -0
  26. apex/physics/__pycache__/projection_pacejka.cpython-312.pyc +0 -0
  27. apex/physics/__pycache__/validator.cpython-312.pyc +0 -0
  28. apex/pipelines/__pycache__/__init__.cpython-312.pyc +0 -0
  29. apex/pipelines/__pycache__/telemetry_to_log.cpython-312.pyc +0 -0
  30. apex/server.py +62 -0
  31. apex/shared/__pycache__/__init__.cpython-312.pyc +0 -0
  32. apex/shared/contracts/__pycache__/__init__.cpython-312.pyc +0 -0
  33. apex/shared/contracts/__pycache__/adapters.cpython-312.pyc +0 -0
  34. apex/shared/contracts/__pycache__/projector.cpython-312.pyc +0 -0
  35. apex/shared/contracts/__pycache__/shapes.cpython-312.pyc +0 -0
  36. apex/shared/contracts/__pycache__/violations.cpython-312.pyc +0 -0
  37. apex/ttm/__pycache__/__init__.cpython-312.pyc +0 -0
  38. apex/ttm/__pycache__/forecast.cpython-312.pyc +0 -0
apex/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (198 Bytes). View file
 
apex/__pycache__/observability.cpython-312.pyc ADDED
Binary file (7.96 kB). View file
 
apex/__pycache__/observability_metrics.cpython-312.pyc ADDED
Binary file (7.65 kB). View file
 
apex/__pycache__/server.cpython-312.pyc ADDED
Binary file (25 kB). View file
 
apex/critics/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (571 Bytes). View file
 
apex/critics/__pycache__/orchestrator.cpython-312.pyc ADDED
Binary file (9.68 kB). View file
 
apex/guardian/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (468 Bytes). View file
 
apex/guardian/__pycache__/audit.cpython-312.pyc ADDED
Binary file (12.5 kB). View file
 
apex/instruct/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (260 Bytes). View file
 
apex/instruct/__pycache__/coa_parser.cpython-312.pyc ADDED
Binary file (7.84 kB). View file
 
apex/instruct/__pycache__/narrator.cpython-312.pyc ADDED
Binary file (19.3 kB). View file
 
apex/instruct/__pycache__/openrouter_generator.cpython-312.pyc ADDED
Binary file (8.63 kB). View file
 
apex/judges/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (521 Bytes). View file
 
apex/judges/__pycache__/coa_diff.cpython-312.pyc ADDED
Binary file (7 kB). View file
 
apex/lips/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (439 Bytes). View file
 
apex/lips/__pycache__/harness.cpython-312.pyc ADDED
Binary file (5.28 kB). View file
 
apex/observability.py CHANGED
@@ -40,6 +40,9 @@ from __future__ import annotations
40
 
41
  import logging
42
  import os
 
 
 
43
 
44
  logger = logging.getLogger(__name__)
45
 
@@ -162,14 +165,36 @@ def setup_observability(app):
162
  @app.middleware("http")
163
  async def apex_otel_request_middleware(request, call_next):
164
  path = request.url.path
165
- if path in {"/healthz", "/favicon.ico"}:
 
 
166
  return await call_next(request)
 
167
  with tracer.start_as_current_span(f"{request.method} {path}") as span:
168
  span.set_attribute("http.method", request.method)
169
  span.set_attribute("http.url", str(request.url))
170
  span.set_attribute("http.route", path)
171
  response = await call_next(request)
172
  span.set_attribute("http.status_code", response.status_code)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  return response
174
 
175
  logger.info(
 
40
 
41
  import logging
42
  import os
43
+ import time
44
+
45
+ from apex import observability_metrics
46
 
47
  logger = logging.getLogger(__name__)
48
 
 
165
  @app.middleware("http")
166
  async def apex_otel_request_middleware(request, call_next):
167
  path = request.url.path
168
+ # Skip readiness + the observability panel's own polling so the
169
+ # live dashboard reflects real product traffic, not self-polling.
170
+ if path in {"/healthz", "/favicon.ico", "/api/observability/summary"}:
171
  return await call_next(request)
172
+ started = time.perf_counter()
173
  with tracer.start_as_current_span(f"{request.method} {path}") as span:
174
  span.set_attribute("http.method", request.method)
175
  span.set_attribute("http.url", str(request.url))
176
  span.set_attribute("http.route", path)
177
  response = await call_next(request)
178
  span.set_attribute("http.status_code", response.status_code)
179
+ # Mirror into the in-process live-metrics aggregator with the
180
+ # REAL Honeycomb trace_id so /api/observability/summary can
181
+ # deep-link each recent request to its trace waterfall.
182
+ try:
183
+ span_ctx = span.get_span_context()
184
+ trace_id_hex = (
185
+ format(span_ctx.trace_id, "032x")
186
+ if span_ctx and span_ctx.trace_id
187
+ else None
188
+ )
189
+ except Exception: # pragma: no cover - defensive
190
+ trace_id_hex = None
191
+ observability_metrics.record_request(
192
+ route=path,
193
+ method=request.method,
194
+ status=response.status_code,
195
+ duration_ms=(time.perf_counter() - started) * 1000.0,
196
+ trace_id=trace_id_hex,
197
+ )
198
  return response
199
 
200
  logger.info(
apex/observability_metrics.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """In-process live request metrics for the APEX observability cockpit.
2
+
3
+ wave-53 production-observability surface. The /judges page renders a LIVE
4
+ telemetry panel fed by `GET /api/observability/summary`. This module holds
5
+ the in-process aggregator that the request middleware writes to and the
6
+ summary route reads from. Pure stdlib, thread-safe, zero new dependencies.
7
+
8
+ Honesty contract (see project memory feedback_conceptual_stack_vs_shipped_
9
+ stack): every number here is REAL traffic this process has served since
10
+ boot. A cold start (zero requests) is reported truthfully, never faked. The
11
+ same spans are exported to Honeycomb over OTLP by `apex/observability.py`;
12
+ this aggregator is the in-product mirror so judges can see live metrics
13
+ without a Honeycomb login, and each recent request carries its real
14
+ Honeycomb `trace_id` for a deep-link into the actual trace waterfall.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import threading
20
+ import time
21
+ from collections import deque
22
+ from typing import Any
23
+
24
+ _MAX_RECENT = 200 # ring buffer used for latency percentiles
25
+ _MAX_RECENT_TRACES = 12 # recent requests surfaced to the panel (deep-linked)
26
+ _STATUS_CLASSES = ("2xx", "3xx", "4xx", "5xx")
27
+
28
+
29
+ def _percentile(sorted_values: list[float], pct: float) -> float:
30
+ """Nearest-rank percentile over an already-sorted list (honest, simple)."""
31
+ if not sorted_values:
32
+ return 0.0
33
+ idx = int(round((pct / 100.0) * (len(sorted_values) - 1)))
34
+ idx = max(0, min(len(sorted_values) - 1, idx))
35
+ return round(sorted_values[idx], 2)
36
+
37
+
38
+ class LiveMetrics:
39
+ """Thread-safe in-process request aggregator."""
40
+
41
+ def __init__(self) -> None:
42
+ self._lock = threading.Lock()
43
+ self._reset_locked()
44
+
45
+ def _reset_locked(self) -> None:
46
+ self._start = time.time()
47
+ self._total = 0
48
+ self._status_class: dict[str, int] = {c: 0 for c in _STATUS_CLASSES}
49
+ self._by_route: dict[str, dict[str, float]] = {}
50
+ self._recent: deque = deque(maxlen=_MAX_RECENT)
51
+
52
+ def reset(self) -> None:
53
+ with self._lock:
54
+ self._reset_locked()
55
+
56
+ def record(
57
+ self,
58
+ *,
59
+ route: str,
60
+ method: str,
61
+ status: int,
62
+ duration_ms: float,
63
+ trace_id: str | None = None,
64
+ ) -> None:
65
+ cls = f"{int(status) // 100}xx"
66
+ entry = {
67
+ "ts": time.time(),
68
+ "route": route,
69
+ "method": method,
70
+ "status": int(status),
71
+ "duration_ms": round(float(duration_ms), 2),
72
+ "trace_id": trace_id,
73
+ }
74
+ with self._lock:
75
+ self._total += 1
76
+ self._status_class[cls] = self._status_class.get(cls, 0) + 1
77
+ bucket = self._by_route.setdefault(route, {"count": 0.0, "sum_ms": 0.0})
78
+ bucket["count"] += 1.0
79
+ bucket["sum_ms"] += float(duration_ms)
80
+ self._recent.append(entry)
81
+
82
+ def snapshot(self) -> dict[str, Any]:
83
+ now = time.time()
84
+ with self._lock:
85
+ recent = list(self._recent)
86
+ total = self._total
87
+ status_class = dict(self._status_class)
88
+ by_route = {
89
+ route: {
90
+ "count": int(v["count"]),
91
+ "avg_ms": round(v["sum_ms"] / v["count"], 2) if v["count"] else 0.0,
92
+ }
93
+ for route, v in self._by_route.items()
94
+ }
95
+ uptime_s = now - self._start
96
+ durations = sorted(e["duration_ms"] for e in recent)
97
+ requests_last_60s = sum(1 for e in recent if now - e["ts"] <= 60.0)
98
+ recent_traces = [
99
+ {
100
+ "trace_id": e["trace_id"],
101
+ "route": e["route"],
102
+ "method": e["method"],
103
+ "status": e["status"],
104
+ "duration_ms": e["duration_ms"],
105
+ "ts": e["ts"],
106
+ }
107
+ for e in reversed(recent)
108
+ if e["trace_id"]
109
+ ][:_MAX_RECENT_TRACES]
110
+ return {
111
+ "total_requests": total,
112
+ "status_class": status_class,
113
+ "by_route": by_route,
114
+ "latency_ms": {
115
+ "p50": _percentile(durations, 50),
116
+ "p95": _percentile(durations, 95),
117
+ "p99": _percentile(durations, 99),
118
+ "window": len(durations),
119
+ },
120
+ "uptime_s": round(uptime_s, 1),
121
+ "eps_1m": round(requests_last_60s / 60.0, 3),
122
+ "recent_traces": recent_traces,
123
+ }
124
+
125
+
126
+ _LIVE = LiveMetrics()
127
+
128
+
129
+ def record_request(
130
+ *,
131
+ route: str,
132
+ method: str,
133
+ status: int,
134
+ duration_ms: float,
135
+ trace_id: str | None = None,
136
+ ) -> None:
137
+ """Record one served request into the process-global live aggregator."""
138
+ _LIVE.record(
139
+ route=route,
140
+ method=method,
141
+ status=status,
142
+ duration_ms=duration_ms,
143
+ trace_id=trace_id,
144
+ )
145
+
146
+
147
+ def snapshot() -> dict[str, Any]:
148
+ """Return the current live-metrics snapshot (real traffic since boot)."""
149
+ return _LIVE.snapshot()
150
+
151
+
152
+ def reset_metrics() -> None:
153
+ """Reset the process-global aggregator (used by tests)."""
154
+ _LIVE.reset()
155
+
156
+
157
+ __all__ = ["LiveMetrics", "record_request", "snapshot", "reset_metrics"]
apex/orchestration/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (539 Bytes). View file
 
apex/orchestration/__pycache__/audit_log.cpython-312.pyc ADDED
Binary file (7.71 kB). View file
 
apex/orchestration/__pycache__/langgraph_runtime.cpython-312.pyc ADDED
Binary file (11.4 kB). View file
 
apex/orchestration/__pycache__/session_context.cpython-312.pyc ADDED
Binary file (5.17 kB). View file
 
apex/orchestration/__pycache__/what_if_replay.cpython-312.pyc ADDED
Binary file (6.01 kB). View file
 
apex/physics/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (240 Bytes). View file
 
apex/physics/__pycache__/projection.cpython-312.pyc ADDED
Binary file (6.97 kB). View file
 
apex/physics/__pycache__/projection_pacejka.cpython-312.pyc ADDED
Binary file (12 kB). View file
 
apex/physics/__pycache__/validator.cpython-312.pyc ADDED
Binary file (13.2 kB). View file
 
apex/pipelines/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (493 Bytes). View file
 
apex/pipelines/__pycache__/telemetry_to_log.cpython-312.pyc ADDED
Binary file (10.1 kB). View file
 
apex/server.py CHANGED
@@ -14,6 +14,7 @@ Exposes:
14
  - GET /api/judges/coa-diff (wave-49 V14 paired COA verdict diff)
15
  - GET /api/tire-degradation (wave-49 Phase 7.2 wear extrapolation)
16
  - POST /api/critics/verdict (wave-49 D-018 tri-agent Mellea IVR critic)
 
17
  - GET /healthz (container readiness probe)
18
 
19
  Deploy target: any Docker host (Modal / Fly.io / Vercel functions /
@@ -32,12 +33,14 @@ import json
32
  import os
33
  import shutil
34
  import tempfile
 
35
  from pathlib import Path
36
  from typing import Any, Final
37
 
38
  from fastapi import FastAPI, File, HTTPException, Request, UploadFile
39
  from fastapi.middleware.cors import CORSMiddleware
40
 
 
41
  from apex.critics import run_tri_agent_critics
42
  from apex.instruct.narrator import Narrator
43
  from apex.instruct.openrouter_generator import build_openrouter_generator
@@ -146,6 +149,30 @@ app.add_middleware(
146
  _tracer = setup_observability(app)
147
 
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  @app.get("/healthz", response_model=HealthzResp)
150
  def healthz() -> HealthzResp:
151
  """Container readiness probe. Returns 200 once the singletons load."""
@@ -650,4 +677,39 @@ async def post_critics_verdict(request: Request) -> dict[str, Any]:
650
  return run_tri_agent_critics(report_summary)
651
 
652
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
653
  __all__ = ["app"]
 
14
  - GET /api/judges/coa-diff (wave-49 V14 paired COA verdict diff)
15
  - GET /api/tire-degradation (wave-49 Phase 7.2 wear extrapolation)
16
  - POST /api/critics/verdict (wave-49 D-018 tri-agent Mellea IVR critic)
17
+ - GET /api/observability/summary (wave-53 live OTel telemetry cockpit)
18
  - GET /healthz (container readiness probe)
19
 
20
  Deploy target: any Docker host (Modal / Fly.io / Vercel functions /
 
33
  import os
34
  import shutil
35
  import tempfile
36
+ import time
37
  from pathlib import Path
38
  from typing import Any, Final
39
 
40
  from fastapi import FastAPI, File, HTTPException, Request, UploadFile
41
  from fastapi.middleware.cors import CORSMiddleware
42
 
43
+ from apex import observability_metrics
44
  from apex.critics import run_tri_agent_critics
45
  from apex.instruct.narrator import Narrator
46
  from apex.instruct.openrouter_generator import build_openrouter_generator
 
149
  _tracer = setup_observability(app)
150
 
151
 
152
+ # Live-metrics fallback for the /judges observability cockpit. When OTel is
153
+ # active (production HF Space), apex/observability.py's span middleware
154
+ # records each request WITH its Honeycomb trace_id. When OTel is off
155
+ # (CI/local/dev), this lightweight middleware still feeds the live panel so
156
+ # `GET /api/observability/summary` is never empty in development. Gated on
157
+ # `_tracer is None` to avoid double-counting when the OTel middleware runs.
158
+ if _tracer is None:
159
+
160
+ @app.middleware("http")
161
+ async def _apex_metrics_fallback_middleware(request: Request, call_next):
162
+ path = request.url.path
163
+ if path in {"/healthz", "/favicon.ico", "/api/observability/summary"}:
164
+ return await call_next(request)
165
+ started = time.perf_counter()
166
+ response = await call_next(request)
167
+ observability_metrics.record_request(
168
+ route=path,
169
+ method=request.method,
170
+ status=response.status_code,
171
+ duration_ms=(time.perf_counter() - started) * 1000.0,
172
+ )
173
+ return response
174
+
175
+
176
  @app.get("/healthz", response_model=HealthzResp)
177
  def healthz() -> HealthzResp:
178
  """Container readiness probe. Returns 200 once the singletons load."""
 
677
  return run_tri_agent_critics(report_summary)
678
 
679
 
680
+ # ---- GET /api/observability/summary -----------------------------------
681
+
682
+ @app.get("/api/observability/summary")
683
+ def get_observability_summary() -> dict[str, Any]:
684
+ """Live in-process request telemetry for the /judges observability cockpit.
685
+
686
+ Honest in-product mirror of the same spans this service exports to
687
+ Honeycomb over OTLP (see apex/observability.py). Every number is REAL
688
+ traffic served since process boot; a cold start reports zeroes, never
689
+ fabricated values. Each recent request carries its Honeycomb trace_id so
690
+ the frontend can deep-link into the real trace waterfall.
691
+ """
692
+ otel_enabled = os.environ.get("APEX_OTEL_ENABLED", "").strip().lower() in {
693
+ "1",
694
+ "true",
695
+ "yes",
696
+ }
697
+ otlp_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", "").strip()
698
+ otlp_active = bool(otel_enabled and otlp_endpoint)
699
+ if otlp_active:
700
+ exporter = "otlp"
701
+ elif otel_enabled:
702
+ exporter = "console"
703
+ else:
704
+ exporter = "none"
705
+ return {
706
+ "service_name": os.environ.get("OTEL_SERVICE_NAME", "apex-backend"),
707
+ "otel_enabled": otel_enabled,
708
+ "otlp_active": otlp_active,
709
+ "exporter": exporter,
710
+ "captured_at": time.time(),
711
+ **observability_metrics.snapshot(),
712
+ }
713
+
714
+
715
  __all__ = ["app"]
apex/shared/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (251 Bytes). View file
 
apex/shared/contracts/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (1.28 kB). View file
 
apex/shared/contracts/__pycache__/adapters.cpython-312.pyc ADDED
Binary file (3.75 kB). View file
 
apex/shared/contracts/__pycache__/projector.cpython-312.pyc ADDED
Binary file (4.41 kB). View file
 
apex/shared/contracts/__pycache__/shapes.cpython-312.pyc ADDED
Binary file (2.61 kB). View file
 
apex/shared/contracts/__pycache__/violations.cpython-312.pyc ADDED
Binary file (8.94 kB). View file
 
apex/ttm/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (257 Bytes). View file
 
apex/ttm/__pycache__/forecast.cpython-312.pyc ADDED
Binary file (10.1 kB). View file