From e3ce1c818ead094a6fe927b1395dd874cd0b4822 Mon Sep 17 00:00:00 2001 From: Daniel Han-Chen Date: Mon, 25 May 2026 16:57:28 +0000 Subject: [PATCH] Fix/adjust diffusion: round 33 P1 batch for PR #5754 Two round-33 reviewer findings: hub-floor consistency and the multipart upload filename validator gap. Dependencies: reverted the round-26 huggingface_hub>=1.3.0 floor in no-torch-runtime.txt and pyproject.toml (round 33 P1 #1-#5, 4/12 vote consensus). studio.txt forces huggingface_hub==0.36.2 to match the transformers==4.57.6 pin in extras-no-deps.txt, so the 1.3.0 floor was internally inconsistent. Reviewers reproduced the resolver conflict on a fresh install. Empirical justification (re-verified on the live B200 host before the revert): huggingface_hub 0.36.2 + transformers 4.57.6 + diffusers 0.37.1 imports Flux2KleinPipeline cleanly and runs end-to-end image generation. transformers 4.57.6 carries its own transformers.utils.hub.is_offline_mode and does not actually need huggingface_hub.is_offline_mode at import time. The original bump was guarding against the (never-realised) transformers 5.x path, which extras-no-deps explicitly pins away. Validation: multipart /seed/upload-unstructured-file now applies the same _no_control_chars and _reject_embedded_hf_token checks to file.filename that SeedInspectUploadRequest.filename already applies in the JSON variant (round 33 P1 #7). The filename is reflected back to the client, persisted in the per-file meta JSON, and echoed by error responses, so the JSON-side hardening must not be asymmetric with the multipart path. Skipped (consistent with prior rounds): * Find_spec vs full import (R33 P1 #6): preserves test compatibility with the huggingface_hub stub fixture. * React hooks set-state-in-effect lint (R33 P1 #8): codebase has 146 pre-existing violations of the same rule; studio-frontend-ci does not gate on lint. * Direct DiffusionBackend.load_model bypass (R33 P1 #9): the route is the only production entry point, and the backend helper now publishes its own diffusion-backend pending tag (round 32 P1 #3). Direct-caller hardening would require duplicating the lease check into load_model itself, which is out of scope for the route-layer security boundary. * One-segment Hub IDs (R33 P2 #10): strict 2-segment Hub id check is intentional; one-segment names are not valid Hub ids. * Cwd-relative shadow of Hub IDs (R33 P2 #11): documented side-channel tradeoff accepted in round 31 commit msg. 97 targeted backend tests pass. --- pyproject.toml | 13 +++++++++---- .../backend/requirements/no-torch-runtime.txt | 17 ++++++++++------- studio/backend/routes/data_recipe/seed.py | 14 ++++++++++++++ 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4ccf8583b7..6c37d50f80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,10 +81,15 @@ huggingfacenotorch = [ "datasets>=3.4.1,!=4.0.*,!=4.1.0,<4.4.0", "accelerate>=0.34.1", "peft>=0.18.0,!=0.11.0", - # Round 26 P1 #9: floor at 1.3.0 because the diffusion stack below - # pulls transformers 5.x which calls hub.is_offline_mode (hub 1.x). - # Keep <2.0 to avoid any future hub ABI break. - "huggingface_hub>=1.3.0,<2.0", + # Round 33 P1: reverted the round-26 hub>=1.3.0 floor. studio.txt + # forces hub==0.36.2 to match the transformers 4.57.6 pin in + # extras-no-deps.txt; the 1.3.0 floor here was internally + # inconsistent and reviewers reproduced the resolver conflict. + # Align with the colab-new extra's 0.34.0 floor (line 610). The + # transformers-5.x is_offline_mode concern that motivated the + # original bump never triggers because transformers is pinned at + # 4.57.6 on the supported install path. + "huggingface_hub>=0.34.0", "hf_transfer", # Studio Images page depends on Flux2KleinPipeline / # Flux2Pipeline, both shipped in diffusers>=0.37.0. Floor was diff --git a/studio/backend/requirements/no-torch-runtime.txt b/studio/backend/requirements/no-torch-runtime.txt index 76da097a71..5cf65c7eea 100644 --- a/studio/backend/requirements/no-torch-runtime.txt +++ b/studio/backend/requirements/no-torch-runtime.txt @@ -43,13 +43,16 @@ safetensors>=0.4.3 datasets>=3.4.1,!=4.0.*,!=4.1.0,<4.4.0 accelerate>=0.34.1 peft>=0.18.0,!=0.11.0 -# Round 26 P1 #8: floor at 1.3.0 because transformers 5.x (allowed by -# the range below) calls huggingface_hub.is_offline_mode, only present -# in hub 1.x. Under --no-deps the resolver does not enforce this -# transitively, so a pre-existing 0.36.2 used to be kept and the next -# `from transformers import AutoConfig` raised ImportError. Upper bound -# <2.0 keeps us off any future ABI break. -huggingface_hub>=1.3.0,<2.0 +# Round 33 P1: reverted the round-26 hub>=1.3.0 floor. Studio's +# install_python_stack later forces hub==0.36.2 via studio.txt +# (constraint by transformers==4.57.6 pinned in extras-no-deps.txt), +# so the 1.3.0 floor was internally inconsistent with the steady +# install state. extras-no-deps holds transformers at 4.x, so the +# transformers-5.x is_offline_mode concern that motivated the +# original bump never actually triggers on the supported install +# path. Verified live on B200: hub 0.36.2 + transformers 4.57.6 + +# diffusers 0.37.1 imports Flux2KleinPipeline cleanly and runs +# end-to-end image generation. hf_transfer # Floor 0.37.0 introduces Flux2KleinPipeline + Flux2Pipeline which the # Studio Images page imports for the default curated picker. diff --git a/studio/backend/routes/data_recipe/seed.py b/studio/backend/routes/data_recipe/seed.py index 91cf718e6e..27bb623deb 100644 --- a/studio/backend/routes/data_recipe/seed.py +++ b/studio/backend/routes/data_recipe/seed.py @@ -433,6 +433,20 @@ async def upload_unstructured_file( tracked_ids = [fid.strip() for fid in existing_file_ids.split(",") if fid.strip()] original_filename = file.filename or "upload" + # Round 33 P1 #7: file.filename is reflected back to the client, + # persisted in the meta JSON, and echoed by error paths. Mirror + # the SeedInspectUploadRequest.filename hardening so a multipart + # upload cannot smuggle control characters or URL-form HF tokens + # through the path the JSON variant already rejects. Import + # locally to avoid a routes -> models cycle. + from models.inference import _no_control_chars, _reject_embedded_hf_token + + try: + _no_control_chars(original_filename, "filename") + _reject_embedded_hf_token(original_filename, "filename") + except ValueError as exc: + raise HTTPException(status_code = 400, detail = str(exc)) from exc + ext = Path(original_filename).suffix.lower() if ext not in UNSTRUCTURED_ALLOWED_EXTS: raise HTTPException(