feat: Add new API endpoints and HTML pages for ML model management
- Implemented HTML pages for datasets, models, training, testing, and results. - Created API endpoints for managing repositories, results, tests, and training sessions. - Added functionality for streaming training progress via Server-Sent Events (SSE). - Introduced a Dockerfile for the ML runner with necessary dependencies. - Developed an SDK for user code execution within the runner container. - Enhanced CSS styles for improved UI layout and navigation. - Established a layout template for consistent HTML structure across pages. - Added JavaScript for dynamic interactions on the models page. - Implemented WebSocket handling for real-time communication with kiosk devices and controllers. - Implemented model registration and management API at /api/models - Added Gitea proxy API for repository interactions at /api/repos - Created results API for listing and comparing training results at /api/results - Developed training management API for enqueueing and retrieving training jobs at /api/trainings - Introduced SSE endpoint for live training progress updates - Added HTML pages for models, datasets, and training management - Created a Dockerfile for the ML runner with necessary dependencies - Developed SDK for user code execution within the runner container - Enhanced CSS styles for improved UI/UX - Implemented WebSocket communication for real-time device and controller interactions in the kiosk system
This commit is contained in:
109
ml/routers/tests.py
Normal file
109
ml/routers/tests.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""API /api/tests — sessioni di test su training esistente (max 2 utenti simultanei)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
from typing import Optional
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
||||
from core import api_client, db, minio_client
|
||||
from core.auth import require_auth
|
||||
from core.docker_runner import run_test_once
|
||||
|
||||
router = APIRouter(prefix="/api/tests", tags=["tests"])
|
||||
|
||||
|
||||
def _row(r):
|
||||
if r is None:
|
||||
return None
|
||||
d = dict(r)
|
||||
for k in ("started_at", "ended_at"):
|
||||
if d.get(k) is not None and hasattr(d[k], "isoformat"):
|
||||
d[k] = d[k].isoformat()
|
||||
return d
|
||||
|
||||
|
||||
@router.post("/sessions", status_code=201)
|
||||
async def start_session(body: dict, user=Depends(require_auth)):
|
||||
training_id = body.get("training_id")
|
||||
if not training_id:
|
||||
raise HTTPException(400, "training_id required")
|
||||
|
||||
tr = await db.fetchrow(
|
||||
"SELECT id, status FROM trainings WHERE id = $1", uuid.UUID(training_id)
|
||||
)
|
||||
if not tr:
|
||||
raise HTTPException(404, "training not found")
|
||||
if tr["status"] != "succeeded":
|
||||
raise HTTPException(409, "training not completed")
|
||||
|
||||
sid = str(uuid.uuid4())
|
||||
try:
|
||||
await api_client.page_connect("test", user.get("username") or "unknown", sid)
|
||||
except httpx.HTTPStatusError as e:
|
||||
if e.response.status_code == 429:
|
||||
raise HTTPException(429, "test slots full (max 2 users)")
|
||||
raise HTTPException(502, f"api: {e}")
|
||||
|
||||
row = await db.fetchrow(
|
||||
"INSERT INTO tests (id, training_id, user_id) VALUES ($1,$2,$3) RETURNING *",
|
||||
uuid.UUID(sid),
|
||||
uuid.UUID(training_id),
|
||||
user.get("username") or "unknown",
|
||||
)
|
||||
return _row(row)
|
||||
|
||||
|
||||
@router.post("/sessions/{session_id}/ping")
|
||||
async def ping_session(session_id: str, user=Depends(require_auth)):
|
||||
try:
|
||||
await api_client.page_ping(session_id)
|
||||
except httpx.HTTPStatusError as e:
|
||||
raise HTTPException(e.response.status_code, e.response.text)
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.post("/sessions/{session_id}/runs", status_code=201)
|
||||
async def run_test(session_id: str, body: dict, user=Depends(require_auth)):
|
||||
row = await db.fetchrow("SELECT * FROM tests WHERE id = $1", uuid.UUID(session_id))
|
||||
if not row:
|
||||
raise HTTPException(404, "session not found")
|
||||
|
||||
inputs = body.get("inputs") or {}
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
result = await run_test_once(str(row["training_id"]), inputs)
|
||||
except Exception as e:
|
||||
raise HTTPException(500, f"test run failed: {e}")
|
||||
dt_ms = int((time.monotonic() - t0) * 1000)
|
||||
|
||||
run = {
|
||||
"inputs": inputs,
|
||||
"outputs": result.get("outputs", {}),
|
||||
"duration_ms": dt_ms,
|
||||
"cpu_peak": result.get("cpu_peak"),
|
||||
"mem_peak_mb": result.get("mem_peak_mb"),
|
||||
"ts": time.time(),
|
||||
}
|
||||
await db.execute(
|
||||
"UPDATE tests SET runs = runs || $1::jsonb WHERE id = $2",
|
||||
json.dumps([run]),
|
||||
uuid.UUID(session_id),
|
||||
)
|
||||
return run
|
||||
|
||||
|
||||
@router.delete("/sessions/{session_id}", status_code=204)
|
||||
async def end_session(session_id: str, user=Depends(require_auth)):
|
||||
await db.execute(
|
||||
"UPDATE tests SET ended_at = NOW() WHERE id = $1 AND ended_at IS NULL",
|
||||
uuid.UUID(session_id),
|
||||
)
|
||||
try:
|
||||
await api_client.page_disconnect(session_id)
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
Reference in New Issue
Block a user