firstoff commited on
Commit
e86e349
·
verified ·
1 Parent(s): c170437

chore(deploy): sync backend from GitHub Actions

Browse files
Files changed (1) hide show
  1. app.py +56 -1
app.py CHANGED
@@ -3,6 +3,8 @@ import subprocess
3
  import io
4
  import csv
5
  import asyncio
 
 
6
  from typing import Dict, List, Optional, Tuple, Union, BinaryIO
7
  # Load environment variables from .env if present
8
  try:
@@ -13,7 +15,7 @@ except ImportError:
13
 
14
  import numpy as np
15
  from fastapi import Depends, FastAPI, File, Form, HTTPException, UploadFile, Request
16
- from fastapi.responses import HTMLResponse
17
  from fastapi.middleware.cors import CORSMiddleware
18
  from slowapi import Limiter, _rate_limit_exceeded_handler
19
  from slowapi.util import get_remote_address
@@ -150,7 +152,60 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
150
 
151
  app.add_middleware(SecurityHeadersMiddleware)
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  # --- Globals: DB pool e Redis client ---
 
154
  db_pool = None
155
  redis_conn = None
156
  vision_warmup = {
 
3
  import io
4
  import csv
5
  import asyncio
6
+ import time
7
+ from collections import defaultdict
8
  from typing import Dict, List, Optional, Tuple, Union, BinaryIO
9
  # Load environment variables from .env if present
10
  try:
 
15
 
16
  import numpy as np
17
  from fastapi import Depends, FastAPI, File, Form, HTTPException, UploadFile, Request
18
+ from fastapi.responses import HTMLResponse, PlainTextResponse
19
  from fastapi.middleware.cors import CORSMiddleware
20
  from slowapi import Limiter, _rate_limit_exceeded_handler
21
  from slowapi.util import get_remote_address
 
152
 
153
  app.add_middleware(SecurityHeadersMiddleware)
154
 
155
+ _request_metrics = defaultdict(int)
156
+ _request_latency_ms = defaultdict(float)
157
+
158
+
159
+ class RequestMetricsMiddleware(BaseHTTPMiddleware):
160
+ async def dispatch(self, request: Request, call_next):
161
+ started = time.perf_counter()
162
+ try:
163
+ response = await call_next(request)
164
+ except Exception:
165
+ _request_metrics[(request.method, request.url.path, "500")] += 1
166
+ raise
167
+ elapsed_ms = (time.perf_counter() - started) * 1000.0
168
+ key = (request.method, request.url.path, str(response.status_code))
169
+ _request_metrics[key] += 1
170
+ _request_latency_ms[(request.method, request.url.path)] += elapsed_ms
171
+ response.headers["X-Response-Time-Ms"] = f"{elapsed_ms:.1f}"
172
+ return response
173
+
174
+
175
+ app.add_middleware(RequestMetricsMiddleware)
176
+
177
+
178
+ @app.get("/metrics", include_in_schema=False)
179
+ def metrics():
180
+ lines = [
181
+ "# HELP animalmind_requests_total Total HTTP requests by method, path and status.",
182
+ "# TYPE animalmind_requests_total counter",
183
+ ]
184
+ for (method, path, status), count in sorted(_request_metrics.items()):
185
+ lines.append(
186
+ f'animalmind_requests_total{{method="{method}",path="{path}",status="{status}"}} {count}'
187
+ )
188
+ lines.extend([
189
+ "# HELP animalmind_request_latency_ms_sum Cumulative request latency in milliseconds.",
190
+ "# TYPE animalmind_request_latency_ms_sum counter",
191
+ ])
192
+ for (method, path), total_ms in sorted(_request_latency_ms.items()):
193
+ lines.append(
194
+ f'animalmind_request_latency_ms_sum{{method="{method}",path="{path}"}} {total_ms:.3f}'
195
+ )
196
+ lines.extend([
197
+ "# HELP animalmind_sse_clients Current SSE subscribers.",
198
+ "# TYPE animalmind_sse_clients gauge",
199
+ f"animalmind_sse_clients {len(_sse_subscribers)}",
200
+ "# HELP animalmind_warmup_ready Whether the visual model warm-up completed.",
201
+ "# TYPE animalmind_warmup_ready gauge",
202
+ f"animalmind_warmup_ready {1 if _vit_model is not None else 0}",
203
+ ])
204
+ return PlainTextResponse("\\n".join(lines) + "\\n", media_type="text/plain; version=0.0.4")
205
+
206
+
207
  # --- Globals: DB pool e Redis client ---
208
+
209
  db_pool = None
210
  redis_conn = None
211
  vision_warmup = {