feat(vision): il worker che espone il runner, versione stampata al build

Container FastAPI separato (Dockerfile.vision, python:3.13-slim) che
espone run_graph/engine_version del Task 2 via POST /run e GET /health,
cosi' l'immagine del server principale non importa mai VisionSuite.

engine_version() ora legge VISION_ENGINE_VERSION se impostata, altrimenti
ricade su git rev-parse nel checkout di sviluppo, e non inventa mai un
valore: senza nessuna delle due solleva un errore esplicito. Nel container
il fallback a git non puo' funzionare (.git del submodule punta fuori dal
build context), quindi Dockerfile.vision prende il commit come build arg
e lo fissa in ambiente; i compose file lo passano da VISION_ENGINE_VERSION.

Nessuna porta pubblicata e nessuna label Traefik sul servizio vision: e'
raggiungibile solo dal server, su tmflow-net.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BBnuACZSCJqXrMYC3LUMU
This commit is contained in:
2026-08-16 18:12:04 +02:00
parent 9387e7c306
commit 71da162e1f
12 changed files with 334 additions and 6 deletions
+46
View File
@@ -403,6 +403,51 @@ uv sync --extra vision --extra dev --python 3.13
uv run --python 3.13 --extra vision --extra dev pytest src/vision/tests
```
Lo stesso vale per `src/vision_worker/tests/`, il piccolo servizio FastAPI che
espone il runner: dipende da `src.vision.runner` e quindi eredita lo stesso
vincolo su Python 3.13.
```bash
uv run --python 3.13 --extra vision --extra dev pytest src/vision_worker/tests
```
### Il worker di visione (`Dockerfile.vision`)
Il worker gira in un container separato dal server FastAPI principale, così
che l'immagine dell'API resti leggera e un aggiornamento di VisionSuite non
richieda di riavviare il traffico di produzione. Il container non pubblica
porte verso l'esterno: lo raggiunge solo il server, all'indirizzo interno
`http://vision:8100` sulla rete `tmflow-net`.
Ogni misura riporta il commit di VisionSuite che l'ha prodotta
(`engine_version`), perché una stazione che misura con un motore diverso da
quello atteso deve poter essere identificata. Il container, però, non ha
accesso al repository Git del progetto principale — `vendor/visionsuite` è un
submodule, e la copia `.git` che lo collega al repository ospitante non
viene inclusa nel contesto di build — quindi la versione non può essere
scoperta al volo dentro l'immagine. Va invece **stampata al momento del
build**, leggendo il commit dalla macchina che esegue `docker compose build`,
dove il repository Git è presente per intero:
```bash
VISION_ENGINE_VERSION=$(git -C vendor/visionsuite rev-parse HEAD) \
docker compose -f docker-compose.dev.yml build vision
```
La variabile viene passata come build argument (`ARG VISION_ENGINE_VERSION`
in `Dockerfile.vision`) e fissata nell'immagine come variabile d'ambiente, in
modo che il worker la trovi già pronta a ogni avvio senza doverla ricalcolare.
Fuori da un container, in un checkout di sviluppo locale, la stessa funzione
ricade su `git rev-parse` se la variabile non è impostata — comodo per
lavorare sul runner senza Docker. Ma se un'immagine viene costruita senza
passare `VISION_ENGINE_VERSION`, quel ripiego non ha nulla su cui appoggiarsi:
il `.git` del submodule non arriva nel contesto di build, e il worker
risponde con un errore esplicito su `/health` invece di indovinare una
versione o restituire `"unknown"`. La build va quindi sempre lanciata con la
variabile impostata, sia in sviluppo sia in produzione, con lo stesso comando
mostrato sopra (sostituendo `docker-compose.dev.yml` con `docker-compose.yml`
in produzione).
Stato corrente su `V3.0.0`: **360 pass, 0 fail** (212 backend + 148 frontend).
Alcuni test frontend non renderizzano niente e leggono i sorgenti, perché guardano
@@ -430,6 +475,7 @@ Copia `.env.example` in `.env` e configura:
| `CLIENT_SECRET_KEY` | Chiave segreta Flask (sessioni, CSRF) |
| `API_SERVER_URL` | URL del backend visto dal client (es. `http://server:8000`) |
| `STATION_CODE` | **Per-tablet** — codice stazione (es. `ST-001`). Senza, il client mostra errore configurazione. |
| `VISION_WORKER_URL` | Indirizzo interno del worker di visione (default: `http://vision:8100`, mai esposto fuori da `tmflow-net`) |
| `UPLOAD_DIR` | Percorso upload file (default: `uploads`, project root) |
| `MAX_UPLOAD_SIZE_MB` | Limite dimensione upload (default 50) |
| `RATE_LIMIT_LOGIN` | Login req/min/IP (default 5) |