Spaces:
Running
Running
wave-48 backend deploy
Browse files- apex/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/__pycache__/observability.cpython-312.pyc +0 -0
- apex/__pycache__/observability_metrics.cpython-312.pyc +0 -0
- apex/__pycache__/server.cpython-312.pyc +0 -0
- apex/critics/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/critics/__pycache__/orchestrator.cpython-312.pyc +0 -0
- apex/guardian/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/guardian/__pycache__/audit.cpython-312.pyc +0 -0
- apex/instruct/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/instruct/__pycache__/coa_parser.cpython-312.pyc +0 -0
- apex/instruct/__pycache__/narrator.cpython-312.pyc +0 -0
- apex/instruct/__pycache__/openrouter_generator.cpython-312.pyc +0 -0
- apex/judges/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/judges/__pycache__/coa_diff.cpython-312.pyc +0 -0
- apex/lips/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/lips/__pycache__/harness.cpython-312.pyc +0 -0
- apex/observability.py +26 -1
- apex/observability_metrics.py +157 -0
- apex/orchestration/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/orchestration/__pycache__/audit_log.cpython-312.pyc +0 -0
- apex/orchestration/__pycache__/langgraph_runtime.cpython-312.pyc +0 -0
- apex/orchestration/__pycache__/session_context.cpython-312.pyc +0 -0
- apex/orchestration/__pycache__/what_if_replay.cpython-312.pyc +0 -0
- apex/physics/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/physics/__pycache__/projection.cpython-312.pyc +0 -0
- apex/physics/__pycache__/projection_pacejka.cpython-312.pyc +0 -0
- apex/physics/__pycache__/validator.cpython-312.pyc +0 -0
- apex/pipelines/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/pipelines/__pycache__/telemetry_to_log.cpython-312.pyc +0 -0
- apex/server.py +62 -0
- apex/shared/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/shared/contracts/__pycache__/__init__.cpython-312.pyc +0 -0
- apex/shared/contracts/__pycache__/adapters.cpython-312.pyc +0 -0
- apex/shared/contracts/__pycache__/projector.cpython-312.pyc +0 -0
- apex/shared/contracts/__pycache__/shapes.cpython-312.pyc +0 -0
- apex/shared/contracts/__pycache__/violations.cpython-312.pyc +0 -0
- apex/ttm/__pycache__/__init__.cpython-312.pyc +0 -0
- 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 |
-
|
|
|
|
|
|
|
| 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
|
|
|