Spaces:
Running
Running
| """ | |
| Prometheus metrics middleware for production monitoring. | |
| Exposes request counts, latency histograms, and RAGAS score gauges | |
| at the GET /metrics endpoint. | |
| """ | |
| from __future__ import annotations | |
| import time | |
| from collections.abc import Awaitable, Callable | |
| from prometheus_client import CONTENT_TYPE_LATEST, Counter, Gauge, Histogram, generate_latest | |
| from starlette.middleware.base import BaseHTTPMiddleware | |
| from starlette.requests import Request | |
| from starlette.responses import Response | |
| requests_total = Counter( | |
| "rag_requests_total", | |
| "Total HTTP requests by endpoint and status", | |
| ["endpoint", "status"], | |
| ) | |
| latency_histogram = Histogram( | |
| "rag_latency_seconds", | |
| "Request latency in seconds by endpoint", | |
| ["endpoint"], | |
| buckets=(0.1, 0.5, 1.0, 5.0, 30.0, 60.0, 120.0, 180.0), | |
| ) | |
| ragas_gauge = Gauge( | |
| "rag_ragas_score", | |
| "Per-query RAGAS evaluation score by metric name", | |
| ["metric_name"], | |
| ) | |
| def update_ragas_metrics(scores: dict[str, float] | None) -> None: | |
| """Update Prometheus gauges with latest RAGAS scores.""" | |
| if scores is None: | |
| return | |
| for metric_name, value in scores.items(): | |
| ragas_gauge.labels(metric_name=metric_name).set(value) | |
| class MetricsMiddleware(BaseHTTPMiddleware): | |
| """Middleware that records request count and latency.""" | |
| async def dispatch(self, request: Request, call_next: Callable[[Request], Awaitable[Response]]) -> Response: | |
| endpoint = request.url.path | |
| start = time.time() | |
| try: | |
| response = await call_next(request) | |
| status = str(response.status_code) | |
| return response | |
| except Exception: | |
| status = "500" | |
| raise | |
| finally: | |
| elapsed = time.time() - start | |
| requests_total.labels(endpoint=endpoint, status=status).inc() | |
| latency_histogram.labels(endpoint=endpoint).observe(elapsed) | |
| async def metrics_endpoint(request: Request) -> Response: | |
| """Return Prometheus-formatted metrics.""" | |
| return Response( | |
| content=generate_latest(), | |
| media_type=CONTENT_TYPE_LATEST, | |
| headers={"Cache-Control": "no-cache"}, | |
| ) | |