| 19 | |
| 20 | |
| 21 | class ApiServer: |
| 22 | def __init__(self, max_queue_size: int = 10, app: Optional[FastAPI] = None): |
| 23 | self.app = app or FastAPI(title="LightX2V API", version="1.0.0") |
| 24 | self.max_queue_size = max_queue_size |
| 25 | |
| 26 | self.processing_thread = None |
| 27 | self.stop_processing = threading.Event() |
| 28 | |
| 29 | self._setup_routes() |
| 30 | |
| 31 | def _setup_routes(self): |
| 32 | @self.app.middleware("http") |
| 33 | async def metrics_middleware(request, call_next): |
| 34 | start_time = time.monotonic() |
| 35 | method = request.method |
| 36 | endpoint = request.url.path |
| 37 | monitor_cli.lightx2v_api_request_total.labels(method=method, endpoint=endpoint).inc() |
| 38 | |
| 39 | try: |
| 40 | response = await call_next(request) |
| 41 | status_code = response.status_code |
| 42 | status_label = "success" if status_code < 400 else "error" |
| 43 | if status_code >= 400: |
| 44 | monitor_cli.lightx2v_api_request_error_total.labels( |
| 45 | method=method, |
| 46 | endpoint=endpoint, |
| 47 | error_type=f"http_{status_code}", |
| 48 | ).inc() |
| 49 | return response |
| 50 | except Exception as e: |
| 51 | status_label = "error" |
| 52 | monitor_cli.lightx2v_api_request_error_total.labels( |
| 53 | method=method, |
| 54 | endpoint=endpoint, |
| 55 | error_type=type(e).__name__, |
| 56 | ).inc() |
| 57 | raise |
| 58 | finally: |
| 59 | duration = time.monotonic() - start_time |
| 60 | monitor_cli.lightx2v_api_request_e2e_duration_seconds.labels( |
| 61 | method=method, |
| 62 | endpoint=endpoint, |
| 63 | status=status_label, |
| 64 | ).observe(duration) |
| 65 | |
| 66 | @self.app.get("/") |
| 67 | def redirect_to_docs(): |
| 68 | return RedirectResponse(url="/docs") |
| 69 | |
| 70 | @self.app.get("/health") |
| 71 | def health_check(): |
| 72 | return {"status": "ok"} |
| 73 | |
| 74 | api_router = create_api_router() |
| 75 | self.app.include_router(api_router) |
| 76 | |
| 77 | def _ensure_processing_thread_running(self): |
| 78 | if self.processing_thread is None or not self.processing_thread.is_alive(): |