Skip to content

build(deps): CPU-only torch in the image — 9.50 GB → 2.95 GB (P2-5) - #408

Merged
jwvanderstam merged 2 commits into
mainfrom
feat/p2-5-cpu-torch
Oct 2, 2026
Merged

jwvanderstam merged 2 commits into
mainfrom
feat/p2-5-cpu-torch

Conversation

@jwvanderstam

Copy link
Copy Markdown
Owner

Independent of #403–#407: it touches the lock, CI and docs, none of which those PRs change. Merge order doesn't matter, apart from a likely trivial CHANGELOG/ROADMAP rebase.

What changes

sentence-transformers pulled PyPI's CUDA torch into the app image, together with fifteen nvidia-* packages, three cuda-* and triton. The app container never has a GPU; Ollama owns it.

  • requirements.in names the PyTorch CPU index (--extra-index-url https://download.pytorch.org/whl/cpu). PEP 440 orders 2.13.0+cpu above 2.13.0, so the resolver picks the CPU build without a torch pin.
  • The locks were recompiled on Linux with the documented command. Nothing else moved: torch became 2.13.0+cpu, the 19 GPU packages left, and the dev lock gained only the index line.
  • The image and CI install the same wheels, so the lock still describes what runs.

Measured

Image (built from the same commit, 2026-09-29) Size
main, CUDA torch 9.50 GB
this branch, CPU torch 2.95 GB (−69%)

The CPU image passes, by hand, every assertion docker-smoke makes:

  • uid 65532, no shell;
  • native imports, with torch 2.13.0+cpu and torch.version.cuda is None;
  • it boots against Postgres, and /api/health returns 200 with the database healthy.

The cross-encoder reranker, the one torch consumer, loaded and completed its warm-up on the CPU build.

Caught before it shipped: torch had silently left the vulnerability scan

pip-audit -r requirements.txt skips torch 2.13.0+cpu ("Dependency not found on PyPI and could not be audited") and still passes. CI now does three things instead:

  • Strips the local tag. A +cpu build is compiled from the public release's source and carries its advisories.
  • Audits the pins as written (--disable-pip --no-deps). The lock is the full closure, so nothing is lost, and resolving would pick the +cpu wheel again through the index line.
  • Adds --strict, so a package that can't be audited fails the step instead of disappearing from it.

Verified locally: the strict audit passes on the stripped lock and fails on the raw one, and pip-audit does report advisories for an old torch pinned the same way.

Guards

  • docker-smoke asserts torch.version.cuda is None inside the image.
  • tests/unit/test_lock_is_cpu_only.py fails if the lock regains a CUDA torch or any nvidia-*, cuda-* or triton package. This matters because a CPU index that lags a release would make the resolver fall back to CUDA silently. All three checks fail on main's lock.

Verification

ruff, mypy, bandit: clean. Fast suite: 3185 passed, 0 failed. The workflow YAML parses.

Not verifiable before it happens

  • How Dependabot handles the +cpu pin on its next bump. It supports pip-compile with index options, but local version labels are a known weak spot. The worst case is that torch bumps arrive ungrouped or need a manual recompile, not that the image regresses, because the lock test would catch a CUDA fallback.
  • The extra index serves some common packages too. pip picks the highest version across both indexes. The lock pins every version, so the practical exposure is which host a pinned wheel comes from.

🤖 Generated with Claude Code

sentence-transformers pulled PyPI's CUDA torch, with fifteen nvidia-*
packages, three cuda-* and triton, into an image whose app container never
has a GPU (Ollama owns it).

- requirements.in names the PyTorch CPU index. PEP 440 orders 2.13.0+cpu
  above 2.13.0, so the resolver prefers it without a torch pin; the Linux
  recompile changed nothing else. Image and CI install the same wheels.
- pip-audit -r skips 2.13.0+cpu (PyPI does not list it) and still passes, so
  torch had silently left the scan. CI now audits the lock with the local tag
  stripped, as pinned, with --strict so an unauditable package fails.
- docker-smoke asserts torch.version.cuda is None; test_lock_is_cpu_only
  fails if the lock regains a CUDA torch.

Same-commit image sizes recorded in DEPLOYMENT.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 2, 2026 11:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The exact +cpu pin breaks the documented macOS host installation path.

Review effort: Balanced
Findings: None

What changed in this PR

Reduces the application image size by switching PyTorch to its CPU-only build and strengthening dependency auditing.

Changes:

  • Uses the PyTorch CPU index and removes CUDA dependencies.
  • Adds CI guards for CPU-only Torch and strict vulnerability auditing.
  • Documents the resulting image-size reduction.
File Description
requirements.in Configures the CPU wheel index.
requirements.txt Locks CPU-only Torch and removes CUDA packages.
requirements-dev.txt Propagates the CPU index.
.github/​workflows/​tests.yml Strengthens auditing and image checks.
tests/​unit/​test_lock_is_cpu_only.py Guards the CPU-only lock.
docs/​DEPLOYMENT.md Records operational impact.
docs/​ROADMAP.md Marks P2-5 complete.
CHANGELOG.md Announces the dependency change.
.claude/​rules/​file-map.md Registers the new test.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@sonarqubecloud

sonarqubecloud Bot commented Oct 2, 2026

Copy link
Copy Markdown

@jwvanderstam
jwvanderstam merged commit bb1425f into main Oct 2, 2026
16 checks passed
@jwvanderstam
jwvanderstam deleted the feat/p2-5-cpu-torch branch October 2, 2026 12:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants