chiudi_contorni unisce LINE/ARC aperti in LWPOLYLINE chiuse prima di
costruire la ricetta e per l'anteprima: con banda larga cut_open_ends
non lasciava piu' punti in banda.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Worker: POST /dxf/preview legge il DXF con read_xld_dxf (lo stesso lettore di
CompareDxf) e ritorna contorni in mm con y verso il basso, bbox, dimensioni,
entita e punti (tetto 20000, decimazione uniforme). Backend: POST
/api/vision/dxf-preview (Maker) che inoltra al worker; 422 inoltrato.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Decodifica + run_graph girano in asyncio.to_thread sotto asyncio.wait_for
(VISION_RUN_TIMEOUT_S, default 100s, sotto i 120s del backend) -> 504 del
worker allo scadere. /health resta leggero e risponde durante un calcolo
in corso. asyncio.Semaphore(VISION_MAX_CONCURRENCY, default 2): oltre ->
503 "worker busy". vision_service._call_worker inoltra 422/503/504 del
worker con il loro status e motivo invece di appiattirli su 502.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Worker: Image.open().convert() in try/except (422 "image is not
decodable"); il grafo deve essere un dict (422 altrimenti); run_graph
distingue costruzione (GraphBuildError -> 422) da esecuzione (500 con
motivo). Router: verifica dei magic bytes (PNG/JPEG/BMP/TIFF/WebP/GIF)
prima di chiamare il worker.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
runner.run_graph esclude bool e NaN/inf dagli output numerici, con il
motivo in RunOutcome.invalid_outputs. vision_service tratta una quota
mappata con valore None/assente/non finito come "non prodotta" (422),
invece di arrivare a float(None) e sollevare TypeError.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ruling R9: separa l'extra vision (runner: 4 pacchetti VisionSuite + numpy)
da vision-worker (fastapi, uvicorn, pillow, python-multipart, sopra vision).
Prima il worker risolveva solo perche' vs-pm2d le lista per conto suo; un
domani lo stub di stazione dovra' incorporare il runner senza trascinarsi
dietro un server web che non gli serve.
Aggiorna Dockerfile.vision e README.md al nuovo extra; il test del worker ora
pinna anche il valore numerico del diametro (non solo la chiave), agganciato
alla stessa tolleranza del test in-process del runner.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014BBnuACZSCJqXrMYC3LUMU
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