docs(spec): design dell'integrazione VisionSuite
Otto decisioni dal brainstorming, con il motivo di ognuna: motore su entrambi i lati con versione in lockstep, agente sottile, server che non importa mai VisionSuite, device a livello PC, esecuzione decisa dalla stazione, verdetto tenuto fuori dal grafo, sorgente immagine astratta. Modello dati, API, errori e le quattro domande che restano aperte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014BBnuACZSCJqXrMYC3LUMU
This commit is contained in:
@@ -0,0 +1,399 @@
|
||||
# Integrazione di VisionSuite in TieMeasureFlow — design
|
||||
|
||||
> Progetto: TieMeasureFlow V3.0.0 → V3.1.0
|
||||
> Data: 2026-08-16
|
||||
> Stato: design approvato, piano di implementazione da scrivere
|
||||
|
||||
## Perché
|
||||
|
||||
TieMeasureFlow oggi registra misure prese a mano, con il calibro USB o con il
|
||||
tastierino. Il passo successivo che il cliente chiede è misurare con una camera:
|
||||
acquisire l'immagine di un pezzo e ricavarne le quote, oppure confrontarne il
|
||||
profilo con il disegno DXF di riferimento.
|
||||
|
||||
La visione artificiale non va scritta qui. Tielogic ha già
|
||||
[VisionSuite](ssh://git@git.tielogic.xyz:222/Adriano/visionsuite.git), un
|
||||
monorepo di moduli riusabili il cui README indica esplicitamente TieMeasureFlow
|
||||
fra i consumatori previsti. Questo documento descrive come agganciarlo.
|
||||
|
||||
Il terreno è in parte già preparato. La migrazione `007_task_type` ha introdotto
|
||||
il tipo di task dichiarato e l'enum contiene fin da allora due valori che nessuno
|
||||
produce né consuma ancora:
|
||||
|
||||
```python
|
||||
TASK_TYPES = ("note", "measure", "drawing", "xf_compare", "camera_measure")
|
||||
MEASURING_TASK_TYPES = ("measure", "camera_measure")
|
||||
```
|
||||
|
||||
Questo lavoro li riempie.
|
||||
|
||||
## Che cosa entra e che cosa resta fuori
|
||||
|
||||
**Entra:** l'acquisizione da camera, l'esecuzione di un programma di visione su
|
||||
quell'immagine — pattern matching più misure, oppure pattern matching più
|
||||
confronto DXF — e l'ingresso dei risultati nel modello di misura che già esiste.
|
||||
|
||||
**Resta fuori, per ora:** i semafori USB. Sono previsti dall'architettura — è
|
||||
l'agente di stazione a possederli, insieme alle camere — ma non fanno parte di
|
||||
questa consegna. Il confine è disegnato perché ci entrino senza ristrutturazioni.
|
||||
|
||||
## Le otto decisioni
|
||||
|
||||
Sono le scelte prese in fase di brainstorming, con il motivo per cui sono state
|
||||
prese. Chi legge fra sei mesi ha bisogno del motivo più della scelta.
|
||||
|
||||
### 1. La visione gira sia sul server sia sulla stazione
|
||||
|
||||
Il motore è un componente unico distribuito in due posti: accanto al server e
|
||||
dentro l'agente di stazione. Questo dà la resilienza — se cade il collegamento,
|
||||
la stazione continua a misurare — e permette al server di elaborare in fase di
|
||||
produzione, di comporre ricette per conto del Maker e di rifare analisi su
|
||||
immagini archiviate.
|
||||
|
||||
È coerente con la decisione **D-3**, chiusa il 28/07: dati sul server, app di
|
||||
stazione su ogni PC.
|
||||
|
||||
### 2. Server e stazione montano la stessa identica versione di VisionSuite
|
||||
|
||||
Non è una raccomandazione di build: è un vincolo metrologico. Se il server monta
|
||||
un commit e una stazione ne monta un altro, lo stesso pezzo con lo stesso grafo
|
||||
può dare due numeri diversi, e nessuno se ne accorge finché non arriva una
|
||||
contestazione.
|
||||
|
||||
Da qui tre obblighi che il design deve garantire:
|
||||
|
||||
- VisionSuite entra come **sottomodulo git su un commit preciso**, lo stesso per
|
||||
entrambi — la forma che il README di VisionSuite prescrive
|
||||
- ogni misura porta scritto **con quale versione del motore** è stata prodotta
|
||||
- una stazione che monta una versione diversa da quella del server **si rifiuta
|
||||
di misurare** e lo dichiara, invece di produrre numeri plausibili e sbagliati
|
||||
|
||||
### 3. L'agente di stazione è sottile
|
||||
|
||||
L'agente parla ai device, consegna immagini e — più avanti — piloterà i semafori.
|
||||
Non contiene decisioni di visione.
|
||||
|
||||
La conseguenza pratica è l'impacchettamento: un agente che non contiene
|
||||
VisionSuite non contiene nemmeno PyTorch. Distribuirlo come eseguibile su Windows
|
||||
o come container su Linux diventa realistico, e la scelta su driver e formato può
|
||||
restare tardiva — che è bene, perché dipende dalla **D-4**, ancora aperta.
|
||||
|
||||
L'agente incorpora il motore solo quando la stazione è configurata per elaborare
|
||||
in locale, e in quel caso resta comunque un guscio: riceve un grafo, lo esegue,
|
||||
restituisce le uscite.
|
||||
|
||||
### 4. Il server non importa mai VisionSuite
|
||||
|
||||
La visione sul lato server vive in un **servizio separato** nello stesso compose,
|
||||
il *vision worker*. Il server FastAPI continua a fare quello che fa —
|
||||
autenticazione, ricette, misure, statistica — e resta leggero.
|
||||
|
||||
Quattro ragioni concrete: l'immagine dell'API non passa da poche centinaia di
|
||||
megabyte a cinque gigabyte; un aggiornamento di VisionSuite non richiede di
|
||||
riavviare l'API in produzione; un'elaborazione che va in crash non porta giù le
|
||||
richieste degli altri tablet; il giorno in cui la visione chiede una GPU o una
|
||||
macchina propria si sposta senza toccare il resto.
|
||||
|
||||
Il confine va difeso: se un domani il server importasse VisionSuite per una
|
||||
scorciatoia, il vantaggio si perde in silenzio.
|
||||
|
||||
### 5. La configurazione dei device sta sul PC
|
||||
|
||||
Le camere — e domani i semafori — appartengono alla macchina, non alla ricetta.
|
||||
Il server ne tiene uno **specchio** in sola lettura, popolato dall'agente quando
|
||||
si connette, perché il Maker deve poter scegliere un device da un elenco stando
|
||||
alla scrivania.
|
||||
|
||||
Un device che l'agente non dichiara più è **offline**, non cancellato: una
|
||||
ricetta che lo referenzia deve continuare a esistere e a spiegare perché non può
|
||||
girare.
|
||||
|
||||
### 6. Dove si esegue lo decide la configurazione della stazione
|
||||
|
||||
Ogni PC dichiara se elabora in locale o delega al server, nella stessa
|
||||
configurazione dove già stanno i device. Il default è il server.
|
||||
|
||||
Le macchine capaci si prendono il carico e non mandano immagini in rete; quelle
|
||||
deboli delegano. Se il server non risponde e la stazione ha il motore, la
|
||||
stazione ripiega in locale e lo segna nel risultato. Un impianto misto si governa
|
||||
senza toccare le ricette.
|
||||
|
||||
La ricetta non dice mai dove gira: legare una scelta di impianto a un documento
|
||||
di produzione significa dover rimettere mano alle ricette per spostare il carico.
|
||||
|
||||
### 7. Il verdetto lo dà TieMeasureFlow, non il grafo
|
||||
|
||||
Gli strumenti di quota di `vs-task` hanno anche loro `nominal`, `plus`, `minus` e
|
||||
un `outcome`. Quell'esito viene ignorato e quei limiti restano vuoti: il grafo
|
||||
produce numeri, `measurement_service` decide se sono buoni.
|
||||
|
||||
Due giudici sullo stesso valore è il modo più rapido per ottenere un CSV che si
|
||||
contraddice e una contestazione che non si sa come chiudere.
|
||||
|
||||
La conseguenza è che tutto il resto continua a funzionare senza modifiche:
|
||||
pass/warning/fail, gate del fuori tolleranza con autorizzazione del capoturno,
|
||||
Cp/Cpk, carta di controllo, colonne del CSV di produzione. Una quota da camera è
|
||||
una misura come le altre.
|
||||
|
||||
### 8. La sorgente immagine è un'astrazione a tre facce
|
||||
|
||||
Il motore riceve un'immagine e non sa da dove venga. Le tre sorgenti sono: dal
|
||||
vivo da un device tramite l'agente, un'immagine di riferimento salvata sul
|
||||
server, un file caricato dal Maker.
|
||||
|
||||
Non è un'astrazione nostra: è quella che VisionSuite si è già data — *«espongono
|
||||
una sorgente, non una telecamera: è questo che permette di collaudare un impianto
|
||||
a cinque telecamere senza avere le cinque telecamere sul tavolo, e di scrivere
|
||||
prove che danno sempre lo stesso risultato»*.
|
||||
|
||||
Sblocca tre cose che altrimenti non ci sarebbero:
|
||||
|
||||
- il Maker compone alla scrivania, senza tenere occupata una stazione né avere il
|
||||
pezzo in mano
|
||||
- una ricetta si prova prima di andare in produzione, invece di scoprirne i
|
||||
difetti misurando
|
||||
- diventa possibile una **prova di non regressione**: salvando accanto
|
||||
all'immagine il risultato atteso, si riesegue l'intero parco ricette dopo un
|
||||
aggiornamento di VisionSuite e si vede quali quote si sono mosse
|
||||
|
||||
Quest'ultima è la rete di sicurezza che rende sostenibile aggiornare il
|
||||
sottomodulo, con un motore che sta su due host.
|
||||
|
||||
## Architettura
|
||||
|
||||
### I componenti
|
||||
|
||||
| Componente | Nuovo | Dove vive | Responsabilità |
|
||||
|---|---|---|---|
|
||||
| `vendor/visionsuite` | sì | sottomodulo git | i pacchetti `vs-core`, `vs-camera`, `vs-pm2d`, `vs-measure`, `vs-dxf`, su un commit fisso |
|
||||
| Vision runner | sì | libreria condivisa | immagine + grafo + calibrazione → quote e artefatti. Unica implementazione, due host |
|
||||
| Vision worker | sì | container nel compose | espone il runner dietro una API interna. Il container pesante |
|
||||
| Agente di stazione | sì | PC di reparto | possiede i device, acquisisce, apre la connessione verso il server, incorpora il runner se configurato |
|
||||
| Backend FastAPI | modificato | container esistente | tipi di task, grafo sul task, registro device, inoltro, ingresso risultati. **Nessun import di VisionSuite** |
|
||||
| Frontend Flask | modificato | container esistente | Maker: device, fotogramma, aggancio quote. Measure: schermata del task con camera |
|
||||
|
||||
L'aggancio del sottomodulo segue la forma prescritta dal README di VisionSuite:
|
||||
dipendenza dai soli pacchetti che servono, dichiarati in path editable.
|
||||
|
||||
```toml
|
||||
[tool.uv.sources]
|
||||
visionsuite = { path = "vendor/visionsuite/packages/vs-core", editable = true }
|
||||
vs-camera = { path = "vendor/visionsuite/packages/vs-camera", editable = true }
|
||||
```
|
||||
|
||||
I due host non montano lo stesso insieme, ed è il punto dell'intera struttura di
|
||||
VisionSuite:
|
||||
|
||||
| Pacchetto | Worker | Agente | Perché |
|
||||
|---|---|---|---|
|
||||
| `vs-core` | sì | sì | strutture di base e calibrazione, servono a entrambi |
|
||||
| `vs-pm2d` | sì | solo se elabora in locale | pattern matching |
|
||||
| `vs-measure` | sì | solo se elabora in locale | primitive e quote |
|
||||
| `vs-dxf` | sì | solo se elabora in locale | confronto col disegno. È quello che porta PyTorch |
|
||||
| `vs-camera` | **no** | sì | il worker non tocca hardware: riceve immagini già acquisite |
|
||||
|
||||
Un agente su una stazione che delega al server monta quindi due soli pacchetti,
|
||||
`vs-core` e `vs-camera`, e resta leggero. È la differenza fra un eseguibile
|
||||
distribuibile e un'installazione da cinque gigabyte su ogni PC di reparto.
|
||||
|
||||
I nomi di importazione non sono uniformi — `vs-core` si importa come
|
||||
`visionsuite`, `vs-pm2d` come `pm2d`, `vs-dxf` come `dxf_compare` — ed è una
|
||||
stortura nota e dichiarata a monte, non un errore da correggere qui.
|
||||
|
||||
### Il flusso di una misura, esecuzione sul server
|
||||
|
||||
```
|
||||
operatore apre il task
|
||||
→ client chiede al server di eseguire
|
||||
→ server chiede all'agente un fotogramma sul device dichiarato
|
||||
→ agente acquisisce e carica l'immagine
|
||||
→ server passa immagine e grafo al worker
|
||||
→ worker restituisce le uscite
|
||||
→ server applica pass/fail, gate del fuori tolleranza, salva in measurements
|
||||
→ client mostra esito e immagine con le zone dove ha guardato
|
||||
```
|
||||
|
||||
### Il flusso con esecuzione in locale
|
||||
|
||||
Il giro si accorcia: il server manda il grafo all'agente, l'agente acquisisce ed
|
||||
esegue, e rimanda uscite più immagine. **Da lì in poi il server fa le stesse
|
||||
identiche cose.** Il verdetto, il gate e il salvataggio non cambiano mai posto:
|
||||
cambia chi calcola i numeri, non chi decide se sono buoni.
|
||||
|
||||
È questo che rende le due strade equivalenti a valle, e che permette di cambiare
|
||||
la configurazione di una stazione senza toccare né il frontend né le ricette.
|
||||
|
||||
## Modello dati
|
||||
|
||||
### Il grafo di visione
|
||||
|
||||
`recipe_tasks` prende **`vision_json`** (JSON, nullable): il grafo di `vs-task`
|
||||
serializzato, opaco al server.
|
||||
|
||||
Il versionamento arriva gratis. Il copy-on-write delle ricette copia già i task
|
||||
in profondità, quindi una modifica al grafo su una ricetta con misure produce una
|
||||
versione nuova come qualunque altra modifica; in `recipe_service` è un campo in
|
||||
più nella copia, non una logica nuova.
|
||||
|
||||
Non si riusa `annotations_json`, che sta lì accanto: le annotazioni sono un
|
||||
disegno per l'operatore, il grafo è un programma da eseguire. Hanno cicli di vita
|
||||
e regole di validazione diversi, e mescolarli significa non poter validare né
|
||||
l'uno né l'altro.
|
||||
|
||||
### Il legame fra quota del grafo e quota della ricetta
|
||||
|
||||
`recipe_subtasks` prende **`vision_output`** (String, nullable): la coppia
|
||||
«identificativo dello strumento, nome della sua uscita».
|
||||
|
||||
È la stessa forma di legame che `vs-task` usa internamente, e per la stessa
|
||||
ragione dichiarata nel suo README: *«Il legame è un ID, non un puntatore»* — un
|
||||
riferimento in memoria non si scrive su disco.
|
||||
|
||||
Tutto il resto della subtask — nominale, UTL, UWL, LWL, LTL, unità — resta com'è
|
||||
e continua a significare quello che significa oggi.
|
||||
|
||||
### Le misure
|
||||
|
||||
`input_method` passa da `("usb_caliper", "manual")` a
|
||||
`("usb_caliper", "manual", "camera")`.
|
||||
|
||||
Il contorno della visione **non** va su `measurements`. Quella tabella la leggono
|
||||
la statistica e l'export a ogni giro, e appesantirla la rovina. Ma soprattutto
|
||||
sarebbe sbagliato di modello: una sola acquisizione produce N quote, e immagine,
|
||||
overlay, device e versione del motore sono gli stessi per tutte.
|
||||
|
||||
Nasce quindi **`vision_results`**, una riga per **esecuzione**:
|
||||
|
||||
| Campo | Perché c'è |
|
||||
|---|---|
|
||||
| `image_path`, `overlay_path` | l'immagine, e quella con sopra le zone dove ha guardato |
|
||||
| `engine_version` | il commit di VisionSuite che ha prodotto i numeri |
|
||||
| `executed_on` | `server` o `station` |
|
||||
| `station_id`, `device_id` | da quale macchina e quale camera |
|
||||
| `calibration_snapshot` | la taratura in vigore in quel momento, **copiata** |
|
||||
| `graph_snapshot` | il grafo effettivamente eseguito, **copiato** |
|
||||
| `duration_ms`, `executed_at` | quanto è costata, quando |
|
||||
|
||||
Su `measurements` una sola colonna nuova: `vision_result_id`, FK nullable.
|
||||
|
||||
Calibrazione e grafo si copiano invece di essere referenziati per la stessa
|
||||
ragione: una taratura rifatta sei mesi dopo non deve riscrivere il significato di
|
||||
una misura già presa.
|
||||
|
||||
### Il registro dei device
|
||||
|
||||
**`station_devices`**: `station_id`, `code` (l'identificativo locale), `kind`
|
||||
(`camera` oggi, `light` domani), `label`, `capabilities` (JSON), `calibration`
|
||||
(JSON) con `calibration_taken_at`, `last_seen_at`.
|
||||
|
||||
Si popola dall'agente. Il server lo riceve e non lo modifica.
|
||||
|
||||
### Le immagini di riferimento
|
||||
|
||||
**`vision_reference_images`**: `task_id`, `path`, e la provenienza per intero —
|
||||
`station_id`, `device_id`, snapshot della calibrazione, `engine_version`,
|
||||
`acquired_at`, una nota — più `expected_json` per l'atteso della prova di non
|
||||
regressione.
|
||||
|
||||
La provenienza non è rimandabile. Senza, si riesegue il grafo mesi dopo, escono
|
||||
numeri diversi, e non c'è modo di distinguere un motore aggiornato da un'ottica
|
||||
spostata.
|
||||
|
||||
### Migrazione 011
|
||||
|
||||
Cinque cose: `vision_json` su `recipe_tasks`, `vision_output` su
|
||||
`recipe_subtasks`, `camera` in `input_method_enum`, `vision_result_id` su
|
||||
`measurements`, e le tre tabelle nuove.
|
||||
|
||||
Più una sesta, **da fare adesso o mai**: `task_type_enum` contiene `xf_compare`,
|
||||
refuso per `dxf_compare`. Oggi nessuna riga usa quel valore e correggerlo costa
|
||||
una riga. Dal primo task salvato in poi diventa una migrazione di dati, e il
|
||||
refuso finisce nelle API pubbliche.
|
||||
|
||||
Come le migrazioni 008 e 010, va scritta in *batch mode* per la compatibilità
|
||||
SQLite dei test e verificata in resa MySQL con `alembic upgrade X:Y --sql` prima
|
||||
del deploy: è la procedura che il progetto si è già dato.
|
||||
|
||||
## API
|
||||
|
||||
L'agente apre lui la connessione. **Il server non bussa mai a un PC**, e questo
|
||||
evita di dover aprire porte sulle macchine di reparto — che è materia della D-4.
|
||||
|
||||
| Verso | Endpoint | Cosa fa |
|
||||
|---|---|---|
|
||||
| agente → server | `WS /api/stations/agent` | si autentica con la chiave di stazione, dichiara i device, resta in ascolto |
|
||||
| server → agente | comando `acquire` | «scatta sul device X»; l'agente carica con `POST /api/vision/frames` |
|
||||
| server → agente | comando `execute` | esecuzione in locale: manda il grafo, riceve le uscite |
|
||||
| client → server | `POST /api/vision/execute` | esegui il task per la produzione in corso; **il server sceglie dove** |
|
||||
| client → server | `POST /api/vision/preview` | authoring: esegui su una sorgente qualsiasi, non salvare nulla |
|
||||
| client → server | `GET /api/stations/{code}/devices` | l'elenco per il Maker |
|
||||
| client → server | `POST /api/vision/reference-images` | salva un fotogramma come riferimento |
|
||||
| server → worker | `POST /run` | interno, mai esposto: immagine e grafo → uscite e overlay |
|
||||
|
||||
Un solo endpoint decide dove si esegue, ed è `execute`. Il client non sa e non
|
||||
deve sapere se ha misurato il server o la stazione: chiede una misura e riceve un
|
||||
esito.
|
||||
|
||||
## Errori e casi limite
|
||||
|
||||
| Caso | Comportamento |
|
||||
|---|---|
|
||||
| Versione del motore diversa fra stazione e server | la stazione si rifiuta di misurare e lo dichiara. Mai produrre numeri con un motore non concorde |
|
||||
| Agente non raggiungibile | il task di visione non è eseguibile e lo dice. Da decidere con il cliente se la stazione debba consentire il ripiego sul calibro |
|
||||
| Server non raggiungibile, stazione con motore | la stazione esegue in locale e lo segna in `executed_on` |
|
||||
| Device dichiarato dalla ricetta ma offline | la ricetta resta valida, il task spiega perché non può girare |
|
||||
| Il grafo non produce un'uscita attesa da una subtask | la misura non si salva, l'errore nomina l'uscita mancante |
|
||||
| Elaborazione in crash o oltre il tempo | il worker isola il guasto, l'API resta in piedi, il task riporta l'errore |
|
||||
| Device senza calibrazione | le quote in millimetri non si producono. Mai convertire con un fattore implicito |
|
||||
|
||||
## Prove
|
||||
|
||||
Ai tre file di test che il progetto ha già e che leggono i sorgenti invece di
|
||||
renderizzare — `test_offline.py`, `test_layout_shell.py`,
|
||||
`test_template_js_syntax.py` — se ne aggiunge la stessa specie:
|
||||
|
||||
- **una prova che il server non importi VisionSuite.** È il confine su cui poggia
|
||||
la decisione 4, e senza qualcuno che lo controlli si perde in silenzio
|
||||
- **una prova che il commit del sottomodulo dichiarato coincida** con quello che
|
||||
server e agente montano
|
||||
|
||||
Per il resto, il runner si prova su immagini di riferimento salvate — che è
|
||||
precisamente ciò per cui l'astrazione della sorgente esiste. Nessuna prova
|
||||
richiede una camera collegata.
|
||||
|
||||
## Quello che questo design non risolve
|
||||
|
||||
- **Il dimensionamento del server.** Il pattern matching è pesante e, con
|
||||
l'esecuzione lato server come default, la CPU è contesa fra tutte le stazioni.
|
||||
Va aggiunto alla **D-4**, che finora chiedeva solo quale macchina e quanto
|
||||
disco: ora chiede anche quanti core e quanta RAM.
|
||||
- **La conservazione delle immagini.** Due politiche distinte, entrambe da
|
||||
portare in D-4: le immagini di riferimento sono poche e vivono quanto la
|
||||
ricetta; quelle di produzione sono una per pezzo e crescono senza limite, e
|
||||
vogliono una scadenza o una regola del tipo «si conservano solo quelle dei
|
||||
pezzi fuori tolleranza».
|
||||
- **Che cosa fa la stazione se la rete cade** e non è configurata per elaborare
|
||||
in locale: si blocca, o consente il calibro e segna il task come non
|
||||
eseguibile? È una domanda per il cliente.
|
||||
- **L'impacchettamento dell'agente.** Le macchine possono essere Linux o Windows
|
||||
e i driver possono stare sulla macchina o nel container. La decisione è
|
||||
volutamente tardiva; il design la rende tale tenendo l'agente sottile e neutro.
|
||||
|
||||
## Da verificare prima di cominciare
|
||||
|
||||
VisionSuite è stato lavorato fino al 15/08/2026 e il suo ultimo commit è
|
||||
*«Rassegna delle firme pubbliche prima che si congelino»*: le API si stanno
|
||||
stabilizzando proprio ora. Prima di fissare il commit del sottomodulo:
|
||||
|
||||
- **il suo README è indietro rispetto al codice.** Dichiara `vs-measure` «da
|
||||
scrivere» quando invece esiste, con circa 4.865 righe e tredici file di test, e
|
||||
dichiara `vs-dxf` non avviabile quando l'import di `common.calibration` non c'è
|
||||
più. Va riletto lo stato reale, non quello documentato.
|
||||
- **il test di calibrazione dato per rosso** — la focale recuperata a 373 contro
|
||||
456 attesi, con tolleranza al 5% — va verificato sullo stato attuale. Il README
|
||||
di VisionSuite avverte di guardarlo *prima* di costruirci sopra misure in
|
||||
millimetri, che è esattamente ciò che questo lavoro fa. Le calibrazioni sono
|
||||
state toccate il 13/08 e lo stato potrebbe essere cambiato.
|
||||
- **il peso dell'ambiente.** `vs-dxf` dipende da PyTorch e l'installazione
|
||||
completa arriva a circa 5 GB. È la ragione per cui il worker è un container a
|
||||
parte, e va confermato che il solo sottoinsieme necessario pesi meno.
|
||||
Reference in New Issue
Block a user