Skip to content

FEAT: prebuilt pypiserver mirror image as the offline compose package source#5048

Open
qinrui777 wants to merge 37 commits into
xorbitsai:mainfrom
qinrui777:feat/add-dc-and-pypiserver
Open

FEAT: prebuilt pypiserver mirror image as the offline compose package source#5048
qinrui777 wants to merge 37 commits into
xorbitsai:mainfrom
qinrui777:feat/add-dc-and-pypiserver

Conversation

@qinrui777

@qinrui777 qinrui777 commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Ships xprobe/xinference-pypiserver: a prebuilt PyPI mirror image carrying every wheel the
xinference runtime may install into per-model virtual environments, and makes it the default
package source of the offline Docker Compose profile introduced in #5174. Air-gapped users
get working vllm / sglang / transformers / … model launches with zero wheel preparation.

This is a rework of the original approach in this PR. Testing showed that pointing
PIP_INDEX_URL / PIP_TRUSTED_HOST at the mirror does not reach the model-venv install
chain: per-model venv installs go through uv (which ignores PIP_* variables), and
xinference's pip-config inheritance (get_pip_config_args) only reads global.* keys from
pip config files, where PIP_INDEX_URL surfaces as :env:.index-url and is never inherited.
In a measured launch, uv installed packages while the mirror's TX byte counter stayed flat —
all traffic went to public PyPI. The compose/env-var half of this PR is therefore replaced by
the pip.conf + UV_* wiring that #5174 already merged (verified to reach uv:
settings(index_url=http://…:8080/simple/, …) in the worker log, mirror TX counter grows).

Changes

  • Runtime: resolve direct wheel URLs from the private index (rewrite_direct_url_packages_for_index).
    Direct-URL requirements (the sgl_kernel GitHub wheels) bypass any index, breaking sglang
    in air-gapped setups even when the wheel is mirrored. When settings.index_url is explicitly
    configured, such requirements are rewritten to name==version (keeping local segments, e.g.
    sgl_kernel==0.3.21+cu130) and resolved from the mirror. Online deployments are unchanged.
  • Runtime: drop support for CUDA below 13.0 in the virtualenv engine dependencies
    (PYTORCH_CUDA_WHEEL_URLS now cu130-only; the sgl_kernel ; cuda_version < "13.0" fallbacks
    are removed; the GLM-4.6/4.7 sgl_kernel URLs are aligned to v0.3.21 to match the code).
  • Image build pipeline (xinference/deploy/docker/pypiserver/):
    • generate_package_lists.py loads ENGINE_VIRTUALENV_PACKAGES and the marker-filter logic
      straight from the xinference sources (no install), so the mirror cannot drift from what the
      runtime installs — this replaces the hand-maintained expansions in list-packages.sh.
    • download_packages.py locks the torch family to the runtime image's version
      (TORCH_VERSION=2.11.*), locks each engine set with uv pip compile, and fetches the
      fully-pinned locks with pip download --no-deps — one coherent resolution per engine
      instead of a per-line pip download loop that baked up to 12 versions of the same package.
      vllm/sglang pin their own torch stacks and are locked unconstrained (their venvs install a
      self-consistent stack). Per-model pins keep their exact versions. Git sources and
      sdist-only downloads are built into wheels so the runtime never compiles.
    • A selfcheck build stage re-resolves every engine set, model pin and direct wheel against
      the baked mirror alone (--disable-fallback); any gap fails the image build instead of
      failing an air-gapped user at model-launch time.
  • CI (build-pypiserver-image.yaml): native amd64/arm64 builds merged into a multi-arch
    manifest. Pushes (including :latest) only happen on release tags of xorbitsai/inference,
    gated by check-release-permission; workflow_dispatch builds run the full download +
    selfcheck without publishing.
  • Compose/docs: the offline profile defaults to the baked image (--disable-fallback);
    the previous stock-pypiserver + ./wheels setup moves to a docker-compose.byo-wheels.yml
    override; docs updated accordingly.

Verification

  • pytest xinference/core/tests/test_virtualenv_package_rewrite.py — unit tests for the
    direct-URL rewrite (PEP 508 forms, markers, local versions, non-wheel passthrough).
  • Generator run for both platforms: sglang set contains sglang>=0.5.6 + numpy>=2.4.1 with
    the arch-matching sgl_kernel wheel split into the direct-URL list; httpx==0.24.0
    correctly classified as a pin, not a URL.
  • Full CI build (workflow_dispatch semantics, no publishing) on both architectures:
    amd64 562 files / 11.37 GiB, arm64 561 files / 11.32 GiB, and the offline selfcheck
    passed on both — all 6 engine sets, 150 model pins and 3 direct wheels resolve from the
    mirror alone ("mirror is self-sufficient"). ~20 legacy model pins took the recorded
    unconstrained-fallback path, as designed (old pins conflict with the modern locks;
    per-model venv isolation makes the extra versions legitimate).
    Locked engine stacks: vllm 0.25.1 (torch 2.11.0+cu130, matching the runtime image),
    sglang 0.5.9 + sgl-kernel 0.3.21 (torch 2.9.1+cu130, sglang's own pin),
    transformers/sentence_transformers aligned to torch 2.11.0+cu130.
  • End-to-end on a 2×RTX 3090 Ti host (CUDA 13, Docker Compose v5.3), with the image built
    from this branch's pipeline and the runtime patched with this branch's code:
    • docker compose --profile offline up -d: worker log shows
      settings(index_url=http://xinference-pypiserver:8080/simple, extra_index_url=..., trusted_host=...)
      — pip.conf inheritance reaches the per-model uv installer, and the vLLM engine's
      hardcoded public extra indexes are overridden by the private one.
    • transformers engine (qwen3 0.6b): venv install traffic measured on the mirror
      container's NIC (+92 KiB for the forced extra wheels), inference OK.
    • sglang engine: the requirement list shows the GitHub direct wheel rewritten to
      sgl_kernel==0.3.21+cu130, resolved from the mirror; mirror NIC counter grew by
      ~390 MB during the venv install; sgl-kernel 0.3.21 + torch 2.11.0+cu130 landed in the
      venv. (Model load afterwards crashes on nightly-main because that image ships an
      incompatible sglang 0.5.6 / transformers 5.5.0 pair in its parent env — reproducible
      online, unrelated to this PR; will be reported separately.)
    • Air-gap override (docker-compose.airgap.yml, internal network, no external routing;
      curl https://pypi.org from the container fails): model launch + venv install +
      inference all succeed fully offline through the mirror.

Not run: the readthedocs render of the updated compose docs page.

@XprobeBot XprobeBot added this to the v2.x milestone Jun 18, 2026
@qinrui777 qinrui777 requested a review from qinxuye June 18, 2026 03:19

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a Docker Compose setup for both online and offline modes, including a local PyPI server (pypiserver) to host offline wheels. Key feedback includes fixing the pypiserver command in docker-compose.yaml to ensure wheels are served, using --chown in the Dockerfile to avoid permission errors, correcting invalid CUDA index URLs, and resolving script robustness issues in list-packages.sh (such as potential hangs, pipeline failures, and incorrect relative paths).

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread docker-compose.yaml Outdated
Comment thread xinference/deploy/docker/pypiserver/Dockerfile.pypiserver Outdated
Comment thread xinference/deploy/docker/pypiserver/Dockerfile.pypiserver Outdated
Comment thread xinference/deploy/docker/pypiserver/list-packages.sh Outdated
Comment thread xinference/deploy/docker/pypiserver/list-packages.sh Outdated
Comment thread xinference/deploy/docker/pypiserver/list-packages.sh Outdated
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

@qinxuye qinxuye left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One issue remains around the offline package mirror build.

Comment thread xinference/deploy/docker/pypiserver/Dockerfile.pypiserver Outdated

@rogercloud rogercloud left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Requesting changes for the release gating and offline package integrity issues below.

Comment thread .github/workflows/build-pypiserver-image.yaml
Comment thread xinference/deploy/docker/pypiserver/Dockerfile.pypiserver Outdated
Comment thread docker-compose.yaml Outdated

@rogercloud rogercloud left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Three design-level concerns about whether the offline mirror is actually usable in a true air-gapped deployment. These are independent of the silent-failure / release-gating points already raised.

Comment thread xinference/deploy/docker/pypiserver/Dockerfile.pypiserver Outdated
Comment thread docker-compose.yaml Outdated
Comment thread xinference/deploy/docker/pypiserver/list-packages.sh Outdated
@OliverBryant OliverBryant self-assigned this Jul 15, 2026
@OliverBryant OliverBryant self-requested a review July 15, 2026 10:39
Brings in the merged offline Docker Compose deployment (xorbitsai#5174) that this
PR's pypiserver image now plugs into.
…deps

What: remove the cu128/cu129 entries from PYTORCH_CUDA_WHEEL_URLS, drop the
'sgl_kernel ; cuda_version < "13.0"' fallback from the sglang engine
packages and from the GLM-4.6/GLM-4.7 model specs, and align the
sgl_kernel direct wheel URLs in llm_family.json to v0.3.21 so the JSON
matches ENGINE_VIRTUALENV_PACKAGES.

Why: the xinference runtime image ships CUDA 13.0 / torch cu130, and every
additional CUDA generation multiplies the wheel matrix the offline mirror
must carry (a full torch + nvidia-* stack per version). The cu<13 fallback
entries were already drifting (JSON pinned sgl_kernel v0.3.20 while the
code pinned v0.3.21), showing this matrix was not being maintained.

Goal: a single supported CUDA target (>= 13.0) so the prebuilt offline
mirror can be provably complete while staying reasonably small. Users on
older CUDA stay on older release images.
What: add rewrite_direct_url_packages_for_index() to core/utils and call
it in the worker's virtual-env preparation whenever settings.index_url is
explicitly configured (via VirtualEnvSettings or pip.conf through
inherit_pip_config). Direct wheel-URL requirements are rewritten to
name==version parsed from the wheel filename, keeping local version
segments (e.g. sgl_kernel==0.3.21+cu130). git+ sources, sdist URLs and
unparsable entries pass through unchanged. Unit tests cover the PEP 508
forms, marker preservation, percent-encoding and passthrough cases.

Why: uv fetches direct-URL requirements from the URL itself, bypassing
every configured index. In an air-gapped deployment the sgl_kernel GitHub
release wheels are unreachable, so sglang model launches failed even when
the mirror carried the exact wheel.

Goal: engines that rely on direct wheel URLs become installable fully
offline through the private index. Online deployments are unaffected:
index_url has no default, so the rewrite never triggers there.
What: add generate_package_lists.py, which loads ENGINE_VIRTUALENV_PACKAGES,
the engine extra-index tables and filter_virtualenv_packages_by_markers
straight from the xinference sources via importlib (no install needed; only
pydantic/packaging/orjson required) and emits, per target platform/CUDA:
one resolvable requirement set per engine, the per-model concrete pins,
direct wheel URLs and git sources, plus a generation manifest. The mlx
engine bucket is excluded by default (not usable in the Linux runtime
image). This replaces the hand-written expansions in list-packages.sh.

Why: the shell script duplicated the engine dependency lists by hand and
had already drifted from virtual_env_manager.py (missing the
numpy>=2.4.1 constraint, missing the sgl_kernel direct wheels). Drift in
an offline mirror is only discovered at the worst possible moment: at
model launch on a machine with no internet.

Goal: one source of truth. The mirror's contents are derived mechanically
from the same definitions and filtering code the runtime executes, so the
image can only go stale in lockstep with the code it was built from.
…ffline selfcheck

What: replace the per-line 'pip download' loop in Dockerfile.pypiserver
with a staged pipeline (download_packages.py):
- lock the torch family to the runtime image's version (TORCH_VERSION,
  default 2.11.* to match xprobe/xinference), then lock each engine set
  with 'uv pip compile' and fetch the fully-pinned locks with
  'pip download --no-deps'. vllm/sglang hard-pin their own torch stack per
  release, so they are locked unconstrained - their venvs install a
  self-consistent stack anyway;
- fetch per-model pins with transitive deps, constrained to the already
  locked versions, falling back to an unconstrained fetch (recorded in the
  report) only on genuine conflicts - availability beats minimization;
- fetch direct wheel URLs WITH their dependencies, build wheels from git
  sources, and build wheels from sdist-only downloads so the runtime never
  compiles anything.
A dedicated 'selfcheck' build stage then starts pypiserver with
--disable-fallback on the freshly built mirror and re-resolves every
engine set, every model pin and every direct wheel against it ALONE; any
gap fails the docker build. The final stage pins pypiserver/pypiserver
v2.3.2 and bakes '--disable-fallback' into the image CMD, with the
generation manifest and download report under /data/manifest/ for
debugging.

Why: resolving every spec independently produced a 9.7 GB image with up to
12 baked versions of the same package (e.g. transformers), 17 sdists that
would need compiling at install time, and - because each snapshot kept
only one version per unpinned spec - an sglang set that was outright
unresolvable offline (its numpy constraint conflicted with the single
snapshotted sglang). Nothing verified the published mirror was actually
self-sufficient; users discovered gaps at model launch on an air-gapped
host, and pypiserver's default fallback silently masked those gaps on
connected hosts by redirecting to public PyPI.

Goal: a coherent, smaller mirror where offline installability is proven at
image build time instead of discovered at the user's model launch, and
where an incomplete mirror can never be published or silently fall back to
the internet.
What: rework the workflow to build the image natively on amd64/arm64
runners and merge the results into multi-arch :<tag> and :latest
manifests. Pushing (including :latest) only happens for release tags on
xorbitsai/inference, behind check-release-permission; workflow_dispatch
and fork runs execute the full download + offline selfcheck without
publishing. Free runner disk before building, sanitize branch names into
valid image tags, and fall back to a placeholder Docker Hub org when the
secrets are absent so build-only runs work on forks.

Why: the previous workflow pushed :latest on every run, letting a
non-release build overwrite the production tag; default runners do not
have enough disk for ~10 GB of wheels plus intermediate layers; and there
was no way to validate the image pipeline from a PR without publishing.

Goal: publishing cadence and permission gating aligned with
docker-cd.yaml (release tags drive :latest), while the expensive
build+selfcheck remains runnable anywhere as a pure validation step.
What: point the offline profile's pypiserver service at
xprobe/xinference-pypiserver (wheels baked in) with --disable-fallback,
and drop the ./wheels bind mount from the base compose file - an empty
host directory would shadow the baked packages. The previous stock
pypiserver + ./wheels setup moves to a docker-compose.byo-wheels.yml
override for users who curate their own wheel set. Update
offline.env.example (pin the mirror to the same release tag as
XINFERENCE_IMAGE), pip.conf comments (a configured index also reroutes
direct wheel URLs such as sgl_kernel), the compose docs (no wheel
preparation step; CUDA >= 13.0 note; bring-your-own-wheels section) and
add a README for the pypiserver build directory.

Why: the offline profile merged in xorbitsai#5174 wired the install chain
correctly but left the package source as an empty ./wheels directory the
user had to populate by hand - in practice that means chasing transitive
dependencies and CUDA wheel variants, and getting it wrong only surfaces
at model launch. pypiserver's default fallback additionally hid mirror
gaps on connected hosts by silently redirecting to public PyPI.

Goal: offline deployment that works out of the box - transfer two images,
uncomment pip.conf, 'docker compose --profile offline up -d' - while
keeping the do-it-yourself wheel directory available as an explicit
override.
What: remove the repository-root docker-compose.yaml, the README
'Docker Compose Setup' section and list-packages.sh from the earlier
iterations of this PR.

Why: the root compose file and README section duplicated the
xinference/deploy/docker compose deployment merged in xorbitsai#5174, and their
PIP_INDEX_URL/PIP_TRUSTED_HOST approach does not reach the per-model
uv installer (model venv installs silently kept going to the public
internet). list-packages.sh hand-copied the engine dependency lists and
is replaced by generate_package_lists.py, which loads them from the
xinference sources directly.

Goal: a single offline deployment story - the deploy/docker compose
files backed by the prebuilt mirror image - with no second, broken
configuration path left for users to find.
@OliverBryant OliverBryant changed the title feat: add pypiserver image, docker-compose setup, and CI pipeline FEAT: prebuilt pypiserver mirror image as the offline compose package source Jul 16, 2026
@XprobeBot XprobeBot added the gpu label Jul 16, 2026

@rogercloud rogercloud left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

This PR hardens air-gapped deployments against silent egress to public PyPI/GitHub during per-model venv installs. It rewrites direct wheel-URL requirements (e.g. sgl_kernel GitHub-release wheels) into name==version specs so they resolve from a configured index, ships a prebuilt xprobe/xinference-pypiserver mirror image whose package lists are generated directly from xinference source (a solid anti-drift choice), and adds a build-time selfcheck gate that fails the image build if the mirror cannot self-sufficiently resolve everything. It also trims the CUDA wheel matrix (drops cu128/cu129 torch index entries and the sgl_kernel ; cuda_version < "13.0" fallback) to bound the mirror's size.

Approach verdict: acceptable-with-reservations

The mirror-image + build-time selfcheck gate is a sound architecture, and generating the package lists straight from xinference source rather than a hand-maintained copy is genuinely good design. However, three changes that are motivated as offline-only concerns are actually wired into the unconditional / already-online install path, and the most important one (Finding A below) is a real regression for a large, common class of online users — recommend fixing that before merge.

Findings B and C are also MAJOR; D and E are MINOR test-coverage gaps. See inline comments for details, and the Simplification note at the bottom.

Verification performed

  • Ran xinference/core/tests/test_virtualenv_package_rewrite.py: 7/7 pass.
  • Full editable install with dev deps succeeded in a review worktree; no other test files exercise the changed code paths directly.
  • Did not run the Docker image build itself (180-min CI-only step requiring registry access) — relying on the author's reported CI results for that portion.
  • Coverage gaps: the three new pypiserver build scripts (selfcheck.py, download_packages.py, generate_package_lists.py) have no unit tests (Finding E); the CUDA-marker behavior change from dropping CUDA<13.0 has no regression test (Finding D).

Simplification opportunity

xinference/deploy/docker/pypiserver/download_packages.py:402 — the hand-rolled wheel_re regex (used only for the multi_version_packages report stat) can be replaced with packaging.utils.parse_wheel_filename. This file already imports and uses packaging.utils.canonicalize_name on the same names for the same purpose, so canonicalization is already the expected behavior here (no behavior mismatch), and the risk is confined to a reporting statistic. Needs a .whl-suffix check plus try/except InvalidWheelFilename to preserve the current silent-skip-on-non-wheel behavior (e.g. leftover .tar.gz sdist artifacts in the same directory).

net: -3 to -5 lines possible

(Two similar suggestions — applying the same swap to rewrite_direct_url_packages_for_index in utils.py:427 and to wheel_url_to_spec in selfcheck.py:50 — were considered and dropped: parse_wheel_filename PEP-503-canonicalizes names (sgl_kernelsgl-kernel), which would break 5+ existing tests asserting underscore-preserving output, and raises on unparsable filenames rather than returning None.)

Out of scope (verified but not this PR's issue)

CI publishes :latest on any tag push to main, not only version-format tags (check-release-permission only checks who/where, never the tag's format). Confirmed real, but this is a pre-existing repo-wide convention also present in .github/workflows/docker-cd.yaml — not introduced by this PR.

Comment thread xinference/core/worker.py
packages, model_engine, cuda_version
)

if settings.index_url:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MAJOR] Finding A — Rewrite gate breaks online installs for any user with a pip mirror configured

This gate is a bare if settings.index_url:, which cannot distinguish "an offline mirror was explicitly configured for this feature" from "the user merely has a normal pip mirror in pip.conf" (Tsinghua/Aliyun/USTC, or a corporate Nexus/Artifactory proxy).

inherit_pip_config defaults to True (xinference/model/core.py:136) and unconditionally copies pip config list's global.index-url into settings.index_url for every model install (worker.py:2548-2553 via get_pip_config_args() in xinference/utils.py:42-81) — online or not.

Concrete failure: a fully-online user with a standard China mirror (or any corporate pip proxy) launches sglang. Pre-PR, the sgl_kernel GitHub release wheel (xinference/core/virtual_env_manager.py:36-37, carrying a +cu130 local-version segment) is fetched directly and works. Post-PR, rewrite_direct_url_packages_for_index (xinference/core/utils.py:427) rewrites it to sgl_kernel==0.3.21+cu130, resolved against the user's PyPI-proxy mirror — but PyPI disallows local-version-segment uploads, so no PyPI-compatible mirror can ever serve this. Per the comment at worker.py:2604-2611, uv treats an unreachable/unsatisfiable index entry as fatal, not a soft fallback to the original URL. Net: an install that worked before this PR now hard-fails for these users.

Suggested fix: gate the rewrite on an explicit offline/mirror-only signal (e.g. a distinct flag set by the docker offline profile) rather than the mere presence of settings.index_url.

for entry in json.loads((args.manifest_dir / "pins.json").read_text()):
gate(f"pin:{entry['spec']}", [entry["spec"]])

for url in (args.manifest_dir / "urls.txt").read_text().splitlines():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MAJOR] Finding B — Git-sourced package dependencies are excluded from the selfcheck gate

selfcheck.py's main() only re-resolves engines/*.in (line 124), pins.json (line 133), and urls.txt wheel URLs via wheel_url_to_spec (line 136) — it never reads or checks any git-sourced requirements list, even though download_packages.py:304-365 does build git-sourced deps into wheels and bakes them into the mirror (reading git.txt).

rewrite_direct_url_packages_for_index in xinference/core/utils.py intentionally leaves git+/name @ git+ specs unchanged (documented in its own docstring). At runtime, worker.py:2651-2676 passes the raw git spec straight to uv/pip; an explicit git/URL requirement is always fetched from that URL directly, regardless of a configured index. These specs are live in shipped model specs, not just test fixtures: xinference/model/image/model_spec.json:1234,1679,1714 and xinference/model/llm/llm_family.json:1854,2012.

So an air-gapped launch of such a model passes this build-time selfcheck, then fails at actual model-launch time trying to git clone from GitHub with no network — exactly the failure this selfcheck mechanism exists to catch before it reaches a user, silently missed for this one category.

Suggested fix: add a git-source check here (realistically: verify the wheel download_packages.py built for each git dep exists and installs standalone, since a live git-ref reachability check is impossible offline), or explicitly document git-sourced-dependency models as unsupported by the offline profile.

@@ -35,7 +35,6 @@
"sglang>=0.5.6",
'https://github.com/sgl-project/whl/releases/download/v0.3.21/sgl_kernel-0.3.21+cu130-cp310-abi3-manylinux2014_x86_64.whl ; cuda_version == "13.0" and platform_machine == "x86_64"',
'https://github.com/sgl-project/whl/releases/download/v0.3.21/sgl_kernel-0.3.21+cu130-cp310-abi3-manylinux2014_aarch64.whl ; cuda_version == "13.0" and platform_machine == "aarch64"',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MAJOR] Finding C — CUDA <13.0 drop applies unconditionally to the online path, with silent degradation

Removing the sgl_kernel ; cuda_version < "13.0" fallback here (and dropping cu128/cu129 from PYTORCH_CUDA_WHEEL_URLS below at line 84) is applied unconditionally in _prepare_virtual_env (xinference/core/worker.py:2574-2650) — not gated to offline/mirror mode.

Two distinct effects:

  1. Torch-index part: a CUDA 12.8/12.9 online user now gets PYTORCH_CUDA_WHEEL_URLS.get(cuda_version)None → no auto extra-index-url — same as any previously-unmapped CUDA version (cu121 was already unmapped). This is a reduction in supported versions, not a new failure mode.
  2. sgl_kernel part (sharper regression): with the < "13.0" fallback gone, a CUDA 12.x online sglang install now resolves zero sgl_kernel entries (both remaining entries require cuda_version == "13.0"), silently, with no install-time error — the failure only surfaces later as a runtime import/missing-kernel error inside sglang.

The commit that made this change frames it as an offline-mirror wheel-matrix concern, but the code affects every deployment including fully online ones, with no breaking-change surfacing beyond a docker-compose docs note.

Suggested fix: document this as a product-level breaking change (release notes / CLI warning), and consider raising an explicit, actionable error when sgl_kernel is unavailable for CUDA<13.0 instead of silently installing sglang without its kernel.

assert rewrite_direct_url_packages_for_index(packages) == packages


def test_filter_then_rewrite_sglang_cu130():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MINOR] Finding D — No test coverage for the new CUDA-version-marker filtering behavior

This is the only marker-filtering test (test_filter_then_rewrite_sglang_cu130), and it hardcodes cuda_version="13.0". No test anywhere in the repo exercises filter_virtualenv_packages_by_markers with a CUDA 12.x or 13.1+ value, so the behavior change from Finding C (sgl_kernel silently dropped for CUDA<13.0) has zero regression coverage.

Suggested fix: add a test asserting that for cuda_version="12.4" (or similar) the sglang package list contains no sgl_kernel entry — documents the intentional change and guards the marker-evaluation logic against future regressions.

sys.exit(f"FATAL: pypiserver at {index_url} did not become healthy")


def wheel_url_to_spec(url: str) -> Optional[str]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MINOR] Finding E — New image-build pipeline scripts have no unit test coverage

selfcheck.py (155 lines), download_packages.py (427 lines), and generate_package_lists.py (312 lines) are all new with non-trivial branching/parsing logic (lock/resolve/fallback handling, platform.machine monkeypatching for cross-arch resolution). No test file anywhere imports or exercises any of these three scripts.

wheel_url_to_spec here is independently written from the tested rewrite_direct_url_packages_for_index in xinference/core/utils.py, so the existing unit tests give only indirect, not-guaranteed confidence in this parser. build-pypiserver-image.yaml:32 sets a 180-minute timeout on the only job that exercises these scripts, and it triggers only on tag-push/workflow_dispatch, not on ordinary PRs.

Suggested fix (non-blocking follow-up): add fast unit tests for generate_package_lists.py's classification logic and this file's wheel_url_to_spec, independent of a full image build.

versions: Dict[str, Set[str]] = defaultdict(set)
total_size = 0
files = sorted(p for p in dest.iterdir() if p.is_file())
wheel_re = re.compile(r"^([A-Za-z0-9_.]+)-([0-9][^-]*)-")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shrink: hand-rolled wheel_re regex reinvents wheel-filename parsing (used only for the multi_version_packages report stat). This file already imports and uses packaging.utils.canonicalize_name on the same names for the same purpose, so canonicalization is already the expected behavior here — replace with packaging.utils.parse_wheel_filename, adding a .whl-suffix check plus try/except InvalidWheelFilename to preserve the current silent-skip-on-non-wheel behavior (e.g. leftover .tar.gz sdist artifacts in the same directory). net: -3 to -5 lines possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants