Repository navigation
Auto-chunk large wheels in add_wheels() / install_uploaded_wheels() - #2667
Draft
Elena Peña Tapia (ElePT) wants to merge 2 commits into
Draft
Elena Peña Tapia (ElePT) wants to merge 2 commits into
Elena Peña Tapia (ElePT) wants to merge 2 commits into
Conversation
A wheel larger than the gateway's per-request upload limit was refused with a 413, so users had to split and rejoin big wheels (e.g. CUDA torch) by hand. Make that automatic and verified, client-side. add_wheels() now splits any wheel over WHEEL_CHUNK_THRESHOLD_MB (default 40, overridable via WHEEL_CHUNK_THRESHOLD_MB_OVERRIDE) into ordered parts named <wheel>.whl.part-0001-of-000N, uploads each as its own request, and records a combined .wheel-chunks-manifest.json (per-wheel sha256 + ordered part list) in the wheels/ dir. install_uploaded_wheels() reassembles each chunked wheel into a temp dir, verifies it against the manifest sha256, and only then pip-installs it alongside any whole wheels; a missing part or checksum mismatch raises instead of installing a corrupt wheel. Behavior with no manifest is unchanged. The feature is transparent: add_wheels() returns logical wheel names, list_wheels() hides parts and the manifest and surfaces chunked wheels by their logical name, and remove_wheels() deletes a chunked wheel's parts and prunes the manifest. Re-uploading a wheel clears its stale parts first so a smaller rebuild cannot leave orphans. Also threads sub_path through the files download endpoint (client file_download, GatewayFilesClient.download, the download view/use-case, and get_public_file_stream in both storage backends), so the client can read the manifest back from wheels/ and downloads are symmetric with upload/list/delete. The chunk threshold lives in client constants mirroring MAX_ARTIFACT_FILE_SIZE_MB. Adds client runtime + add_wheels/list_wheels/remove_wheels tests, a storage get_public_file_stream(sub_path=) test, a release note, and documents the behavior and its caveats (MIME allowlist, transient /tmp use) in the BYO-wheels guide.
CI's client lint (mypy) caught two issues the local run missed: - _download_chunk_manifest called file_download with the wrong argument order (download_location and function were swapped into each other's positional slots). This was a real bug that the tests hid by mocking _download_chunk_manifest wholesale. Call it with keyword arguments and add two tests that exercise the real method (argument order + sub_path, and the no-manifest path). - _reassemble_one used iter(lambda: ..., b"") to stream blocks, which mypy cannot type. Replace with a plain while loop.
Elena Peña Tapia (ElePT)
marked this pull request as ready for review
November 4, 2026 10:41
David (Tansito)
marked this pull request as draft
November 4, 2026 23:13
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #2652 (bring your own wheels). A wheel larger than the gateway's per-request upload limit (
MAX_REQUEST_BODY_SIZE_MB, default 50MB) is refused with a 413, so users have had to hand-split large wheels (e.g. CUDAtorch-*+cu121*.whl) and rejoin them in the entrypoint. This makes that automatic and verified, almost entirely client-side.What changes
add_wheels()splits any wheel overWHEEL_CHUNK_THRESHOLD_MB(default 40, under the 50 limit; override withWHEEL_CHUNK_THRESHOLD_MB_OVERRIDE) into ordered parts named<wheel>.whl.part-0001-of-000N, uploads each as its own request, and writes a combined.wheel-chunks-manifest.json(per-wheel sha256 + ordered part list) into thewheels/dir. Smaller wheels upload whole exactly as before.install_uploaded_wheels()reassembles each chunked wheel into a temp dir (the mount is read-only), verifies it against the manifest sha256, and only thenpip installs it alongside any whole wheels. A missing part or checksum mismatch raises rather than installing a corrupt wheel. With no manifest present, behavior is byte-for-byte unchanged.add_wheels()returns logical wheel names;list_wheels()hides parts + manifest and surfaces chunked wheels by their logical name;remove_wheels()deletes a chunked wheel's parts and prunes the manifest. Re-uploading a wheel clears its stale parts first so a smaller rebuild can't leave orphans.Supporting change:
sub_pathon downloadReading the manifest back from
wheels/needs download-by-sub_path, which #2652 did not add (it addedsub_pathto upload/list/delete only). This threadssub_paththrough the whole download path — clientfile_download/GatewayFilesClient.download, the download view + use-case, andget_public_file_streamin both storage backends — making download symmetric with the others. This is the only gateway change.Part naming & manifest
Parts:
<wheel>.whl.part-0001-of-000N(only-/digits/.added; a part never ends in.whl, so the runtime's existing wheel scan skips it). Manifest:.wheel-chunks-manifest.json,{"schema":1,"chunked_wheels":[{wheel, sha256, size, part_size, part_count, parts:[…]}]}; the runtime rejects an unknown schema loudly.Caveats (documented in the guide)
application/octet-stream(parts) andapplication/json(manifest), which the default config does; a hardened allowlist could reject them./tmpspace during function start-up (bounded, cleaned up after install).Testing
test_wheels.py): reassemble+verify, sha mismatch raises before pip, missing part raises, mixed whole+chunked, unknown schema rejected, workdir cleaned up, lone part ignored — plus the existing no-manifest tests as a regression guard.test_serverless_client_functions.py): small→whole/no-manifest, at-threshold→whole, large→N parts + manifest with round-trip + sha256 check, re-upload cleans stale parts, partial-failure cleanup,list_wheelshides parts/manifest and shows logical names,remove_wheelsdeletes parts + prunes manifest.test_file_storage.py):get_public_file_stream(sub_path="wheels")round trip.blackclean;pylint10/10 on the client source. The gateway Django suite and gateway lint could not run in my environment — please confirm CI (tox -e py312, lint) before un-drafting.Checklist
🤖 Generated with Claude Code