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:
Giuseppe Raffa
2026-04-28 09:24:38 +02:00
parent ee478e52ef
commit 0ce879aa44
81 changed files with 7491 additions and 746 deletions

90
ml/core/model_spec.py Normal file
View File

@@ -0,0 +1,90 @@
"""Parse e validazione del contratto `model.yml` nelle repo utente.
Schema sintetico (vedi piano):
name, type, version, python
train: {entrypoint, inputs, outputs, metrics}
test: {entrypoint, io, input_schema[], output_schema[]}
resources: {cpu, mem_mb, gpu}
"""
from __future__ import annotations
from typing import Any, Optional
import yaml
from pydantic import BaseModel, ValidationError
from core import gitea, redis_client
class _FieldSpec(BaseModel):
name: str
dtype: str
min: Optional[float] = None
max: Optional[float] = None
unit: Optional[str] = None
class _Train(BaseModel):
entrypoint: str
inputs: dict = {}
outputs: dict = {}
metrics: dict = {}
class _Test(BaseModel):
entrypoint: str
io: str = "stdio_json"
input_schema: list[_FieldSpec] = []
output_schema: list[_FieldSpec] = []
class ModelSpec(BaseModel):
name: str
type: str
version: str = "0.1.0"
python: str = "3.11"
train: _Train
test: Optional[_Test] = None
resources: dict = {}
def parse_yaml(content: bytes | str) -> dict:
"""Parsa stringa YAML → dict validato. Solleva ValueError su errore."""
if isinstance(content, bytes):
content = content.decode("utf-8")
try:
raw = yaml.safe_load(content) or {}
spec = ModelSpec(**raw)
return spec.model_dump()
except (yaml.YAMLError, ValidationError) as e:
raise ValueError(f"invalid model.yml: {e}") from e
async def fetch_and_parse_spec(owner_repo: str, ref: str) -> Optional[dict]:
"""Recupera model.yml dalla repo alla revisione e lo parsa.
Cache Redis `ml:modelspec:{repo}:{ref}` TTL 1h.
"""
cache_key = f"ml:modelspec:{owner_repo}:{ref}"
try:
cached = await redis_client.client().get(cache_key)
if cached:
import json
return json.loads(cached)
except Exception:
pass
try:
raw = await gitea.get_file_raw(owner_repo, ref, "model.yml")
except Exception:
try:
raw = await gitea.get_file_raw(owner_repo, ref, "model.yaml")
except Exception:
return None
spec = parse_yaml(raw)
try:
import json
await redis_client.client().set(cache_key, json.dumps(spec), ex=3600)
except Exception:
pass
return spec