fix(vision): dichiara le dipendenze del worker, pin numerico HTTP vs runner

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
This commit is contained in:
2026-08-16 18:28:06 +02:00
parent 71da162e1f
commit 05f2937611
5 changed files with 62 additions and 9 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
COPY pyproject.toml uv.lock ./
COPY vendor/ ./vendor/
RUN uv sync --extra vision --frozen --no-dev
RUN uv sync --extra vision-worker --frozen --no-dev
COPY src/vision/ ./src/vision/
COPY src/vision_worker/ ./src/vision_worker/
+16 -4
View File
@@ -403,12 +403,24 @@ 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.
`src/vision_worker/tests/` eredita lo stesso vincolo su Python 3.13, perché
il worker dipende da `src.vision.runner`, ma aggiunge dipendenze proprie -
FastAPI, Uvicorn, Pillow, python-multipart - dichiarate in un extra
separato, `vision-worker`, tenuto distinto da `vision` apposta: un
consumatore che incorpora solo il runner (come un futuro agente di stazione,
che non è un servizio web) non deve trascinarsi dietro un server che non gli
serve. `vision-worker` include comunque `vision`, quindi un solo extra basta
per avere un worker funzionante:
```bash
uv run --python 3.13 --extra vision --extra dev pytest src/vision_worker/tests
uv run --python 3.13 --extra vision-worker --extra dev pytest src/vision_worker/tests
```
Per eseguire entrambe le suite insieme, nominando esplicitamente entrambi
gli extra:
```bash
uv run --python 3.13 --extra vision --extra vision-worker --extra dev pytest src/vision/tests src/vision_worker/tests
```
### Il worker di visione (`Dockerfile.vision`)
+19 -2
View File
@@ -53,13 +53,30 @@ dev = [
"coverage>=7.0.0",
]
# Vision runner (VisionSuite submodule). vs-task/vs-measure/vs-pm2d require
# Python >=3.13 upstream; marker keeps the base 3.11 resolution untouched.
# Vision runner (VisionSuite submodule): what src/vision/runner.py needs to
# execute a graph. vs-task/vs-measure/vs-pm2d require Python >=3.13 upstream;
# marker keeps the base 3.11 resolution untouched. numpy is declared
# explicitly - runner.py imports it directly, and it must not depend on
# being pulled in as a side effect of vs-pm2d's own dependencies.
vision = [
"visionsuite; python_version>='3.13'",
"vs-task; python_version>='3.13'",
"vs-measure; python_version>='3.13'",
"vs-pm2d; python_version>='3.13'",
"numpy; python_version>='3.13'",
]
# Vision worker (src/vision_worker/): the FastAPI shell around the runner,
# on top of `vision`. Kept separate on purpose (Ruling R9) - VisionSuite's
# whole structure exists so a consumer installs only what it needs, and a
# later station agent embeds the same runner without being a web service.
# It must not drag fastapi/uvicorn onto shop-floor PCs.
vision-worker = [
"tiemeasureflow[vision]",
"fastapi>=0.110.0; python_version>='3.13'",
"uvicorn[standard]>=0.30.0; python_version>='3.13'",
"pillow>=10.0.0; python_version>='3.13'",
"python-multipart>=0.0.6; python_version>='3.13'",
]
[project.scripts]
+7 -1
View File
@@ -16,7 +16,7 @@ pytest.importorskip(
reason=(
"the vision tree requires Python >=3.13 (vs-task/vs-measure/vs-pm2d "
"declare it upstream); run it with "
"`uv run --python 3.13 --extra vision --extra dev pytest "
"`uv run --python 3.13 --extra vision-worker --extra dev pytest "
"src/vision_worker/tests`"
),
)
@@ -75,6 +75,12 @@ async def test_run_returns_the_named_outputs():
body = response.json()
assert body["failures"] == []
assert any(key.endswith(".diameter") for key in body["outputs"])
# Pinned to src/vision/tests/test_runner.py's in-process expectation
# (same graph, same image, same tolerance) - the whole point of a shared
# engine_version is that the number does not depend on which path
# computed it.
diameter = next(v for k, v in body["outputs"].items() if k.endswith(".diameter"))
assert diameter == pytest.approx(120.0, abs=4.0)
@pytest.mark.asyncio
Generated
+19 -1
View File
@@ -1975,6 +1975,18 @@ server = [
{ name = "weasyprint" },
]
vision = [
{ name = "numpy", marker = "python_full_version >= '3.13'" },
{ name = "visionsuite", marker = "python_full_version >= '3.13'" },
{ name = "vs-measure", marker = "python_full_version >= '3.13'" },
{ name = "vs-pm2d", marker = "python_full_version >= '3.13'" },
{ name = "vs-task", marker = "python_full_version >= '3.13'" },
]
vision-worker = [
{ name = "fastapi", marker = "python_full_version >= '3.13'" },
{ name = "numpy", marker = "python_full_version >= '3.13'" },
{ name = "pillow", marker = "python_full_version >= '3.13'" },
{ name = "python-multipart", marker = "python_full_version >= '3.13'" },
{ name = "uvicorn", extra = ["standard"], marker = "python_full_version >= '3.13'" },
{ name = "visionsuite", marker = "python_full_version >= '3.13'" },
{ name = "vs-measure", marker = "python_full_version >= '3.13'" },
{ name = "vs-pm2d", marker = "python_full_version >= '3.13'" },
@@ -1988,6 +2000,7 @@ requires-dist = [
{ name = "asyncmy", marker = "extra == 'server'", specifier = ">=0.2.0" },
{ name = "bcrypt", marker = "extra == 'server'", specifier = ">=4.0.0" },
{ name = "coverage", marker = "extra == 'dev'", specifier = ">=7.0.0" },
{ name = "fastapi", marker = "python_full_version >= '3.13' and extra == 'vision-worker'", specifier = ">=0.110.0" },
{ name = "fastapi", marker = "extra == 'server'", specifier = ">=0.110.0" },
{ name = "flask", marker = "extra == 'client'", specifier = ">=3.0.0" },
{ name = "flask-babel", marker = "extra == 'client'", specifier = ">=4.0.0" },
@@ -1997,7 +2010,9 @@ requires-dist = [
{ name = "httpx", marker = "extra == 'server'", specifier = ">=0.27.0" },
{ name = "jinja2", marker = "extra == 'server'", specifier = ">=3.1.0" },
{ name = "kaleido", marker = "extra == 'server'", specifier = ">=0.2.0" },
{ name = "numpy", marker = "python_full_version >= '3.13' and extra == 'vision'" },
{ name = "pdfplumber", marker = "extra == 'server'", specifier = ">=0.10.0" },
{ name = "pillow", marker = "python_full_version >= '3.13' and extra == 'vision-worker'", specifier = ">=10.0.0" },
{ name = "pillow", marker = "extra == 'server'", specifier = ">=10.0.0" },
{ name = "plotly", marker = "extra == 'server'", specifier = ">=5.0.0" },
{ name = "pydantic", specifier = ">=2.0.0" },
@@ -2005,10 +2020,13 @@ requires-dist = [
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" },
{ name = "python-dotenv", specifier = ">=1.0.0" },
{ name = "python-multipart", marker = "python_full_version >= '3.13' and extra == 'vision-worker'", specifier = ">=0.0.6" },
{ name = "python-multipart", marker = "extra == 'server'", specifier = ">=0.0.6" },
{ name = "requests", marker = "extra == 'client'", specifier = ">=2.31.0" },
{ name = "sqlalchemy", extras = ["asyncio"], marker = "extra == 'server'", specifier = ">=2.0.0" },
{ name = "tiemeasureflow", extras = ["vision"], marker = "extra == 'vision-worker'" },
{ name = "urllib3", marker = "extra == 'client'", specifier = ">=2.0.0" },
{ name = "uvicorn", extras = ["standard"], marker = "python_full_version >= '3.13' and extra == 'vision-worker'", specifier = ">=0.30.0" },
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'server'", specifier = ">=0.30.0" },
{ name = "visionsuite", marker = "python_full_version >= '3.13' and extra == 'vision'", editable = "vendor/visionsuite/packages/vs-core" },
{ name = "vs-measure", marker = "python_full_version >= '3.13' and extra == 'vision'", editable = "vendor/visionsuite/packages/vs-measure" },
@@ -2016,7 +2034,7 @@ requires-dist = [
{ name = "vs-task", marker = "python_full_version >= '3.13' and extra == 'vision'", editable = "vendor/visionsuite/packages/vs-task" },
{ name = "weasyprint", marker = "extra == 'server'", specifier = ">=62.0" },
]
provides-extras = ["server", "client", "dev", "vision"]
provides-extras = ["server", "client", "dev", "vision", "vision-worker"]
[[package]]
name = "tinycss2"