Studio Hub: default downloads to Xet transport (#6433)
* Studio Hub: default downloads to Xet transport Model and dataset downloads defaulted to HTTP; flip the default to Xet for faster parallel chunked transfers. - Frontend: DEFAULT_TRANSPORT_MODE is now Xet, so a user with no saved preference starts on Xet. effectiveTransportMode() already downgrades to HTTP and warns when hf_xet is unavailable, so this degrades gracefully. - Backend: DownloadModelRequest.use_xet and DownloadDatasetRequest.use_xet default to True, keeping the API in step with the UI. Set use_xet=False for sequential HTTP Range-resume. - Align the internal _spawn_download_worker default so no caller silently falls back to HTTP. Inference and training model loads were already Xet-first with an HTTP stall fallback, so this brings explicit downloads in line with the rest of Studio. * Studio Hub: gracefully fall back to HTTP when Xet is unavailable With Xet now the default, an omitted or explicit use_xet=True from a non-UI API caller would 400 on installs without hf_xet, since resolve_transport raises when the transport is unavailable. Add resolve_effective_use_xet(), which downgrades a Xet request to HTTP (with a warning) when hf_xet is missing, mirroring the frontend's own downgrade. Both the model and dataset flows now derive a single effective use_xet and feed it to resolve_transport and spawn_worker, so the recorded transport and the worker env can never disagree. The UI is unaffected: it already resolves availability and passes use_xet explicitly. * Add tests for resolve_effective_use_xet Xet to HTTP fallback * Trim comments for PR #6433 --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
parent
17000516f6
commit
93572648b6
6 changed files with 57 additions and 10 deletions
|
|
@ -25,8 +25,8 @@ class DownloadModelRequest(BaseModel):
|
|||
description = "Quantization label (e.g. 'Q4_K_M'). Required for GGUF repos.",
|
||||
)
|
||||
use_xet: bool = Field(
|
||||
False,
|
||||
description = "Enable Xet parallel chunked transport. Default False uses HTTP Range-resume.",
|
||||
True,
|
||||
description = "Use Xet parallel chunked transport. Default True; set False for HTTP Range-resume.",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -125,8 +125,8 @@ class DownloadDatasetRequest(BaseModel):
|
|||
|
||||
repo_id: str = Field(..., description = "HuggingFace dataset repo ID")
|
||||
use_xet: bool = Field(
|
||||
False,
|
||||
description = "Enable Xet parallel chunked transport. Default False uses HTTP Range-resume.",
|
||||
True,
|
||||
description = "Use Xet parallel chunked transport. Default True; set False for HTTP Range-resume.",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,8 @@ async def download_dataset_response(
|
|||
repo_id = await asyncio.to_thread(resolve_cached_repo_id_case, repo_id, repo_type = "dataset")
|
||||
key = _download_job_key(repo_id)
|
||||
|
||||
transport = download_lifecycle.resolve_transport(body.use_xet)
|
||||
use_xet = download_lifecycle.resolve_effective_use_xet(body.use_xet)
|
||||
transport = download_lifecycle.resolve_transport(use_xet)
|
||||
|
||||
claimed, claim_state = _registry.claim(
|
||||
key,
|
||||
|
|
@ -183,7 +184,7 @@ async def download_dataset_response(
|
|||
spawn = lambda: download_lifecycle.spawn_worker(
|
||||
["--repo-id", repo_id, "--dataset"],
|
||||
hf_token,
|
||||
use_xet = body.use_xet,
|
||||
use_xet = use_xet,
|
||||
),
|
||||
hf_token = hf_token,
|
||||
label = repo_id,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
|
|
@ -20,11 +21,27 @@ from hub.utils import inventory_scan as hf_cache_scan
|
|||
from hub.utils.hf_cache_state import EXIT_CANCELLED
|
||||
from hub.utils.state_dir import RepoType
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def backend_dir() -> Path:
|
||||
return Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
|
||||
def resolve_effective_use_xet(use_xet: bool) -> bool:
|
||||
"""Downgrade an Xet request to HTTP when hf_xet is unavailable, so a defaulted
|
||||
or explicit Xet request never hard-fails on installs without the Xet extra."""
|
||||
if not use_xet:
|
||||
return False
|
||||
reason = download_registry.download_transport_unavailable_reason(
|
||||
download_registry.TRANSPORT_XET
|
||||
)
|
||||
if reason is None:
|
||||
return True
|
||||
logger.warning("Xet transport unavailable, falling back to HTTP: %s", reason)
|
||||
return False
|
||||
|
||||
|
||||
def resolve_transport(use_xet: bool) -> str:
|
||||
transport = download_registry.TRANSPORT_XET if use_xet else download_registry.TRANSPORT_HTTP
|
||||
unavailable_reason = download_registry.download_transport_unavailable_reason(transport)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ def _spawn_download_worker(
|
|||
repo_id: str,
|
||||
variant: Optional[str],
|
||||
hf_token: Optional[str],
|
||||
use_xet: bool = False,
|
||||
use_xet: bool = True,
|
||||
protected_blob_hashes: Optional[frozenset[str]] = None,
|
||||
) -> subprocess.Popen:
|
||||
args = ["--repo-id", repo_id]
|
||||
|
|
@ -96,7 +96,8 @@ async def download_model_response(body: DownloadModelRequest, hf_token: Optional
|
|||
detail = f"Invalid gguf_variant: {variant!r}",
|
||||
)
|
||||
key = _download_job_key(repo_id, variant)
|
||||
transport = download_lifecycle.resolve_transport(body.use_xet)
|
||||
use_xet = download_lifecycle.resolve_effective_use_xet(body.use_xet)
|
||||
transport = download_lifecycle.resolve_transport(use_xet)
|
||||
variant_blob_hashes = frozenset()
|
||||
variant_progress_blob_hashes = frozenset()
|
||||
completed_baseline_bytes = 0
|
||||
|
|
@ -171,7 +172,7 @@ async def download_model_response(body: DownloadModelRequest, hf_token: Optional
|
|||
repo_id,
|
||||
variant,
|
||||
hf_token,
|
||||
use_xet = body.use_xet,
|
||||
use_xet = use_xet,
|
||||
protected_blob_hashes = protected_blob_hashes,
|
||||
),
|
||||
hf_token = hf_token,
|
||||
|
|
|
|||
27
studio/backend/hub/tests/test_download_lifecycle.py
Normal file
27
studio/backend/hub/tests/test_download_lifecycle.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
||||
|
||||
from hub.services import download_lifecycle
|
||||
|
||||
|
||||
def _set_xet_reason(monkeypatch, reason):
|
||||
monkeypatch.setattr(
|
||||
download_lifecycle.download_registry,
|
||||
"download_transport_unavailable_reason",
|
||||
lambda _transport: reason,
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_effective_use_xet_keeps_http_when_not_requested(monkeypatch):
|
||||
_set_xet_reason(monkeypatch, "should not be consulted")
|
||||
assert download_lifecycle.resolve_effective_use_xet(False) is False
|
||||
|
||||
|
||||
def test_resolve_effective_use_xet_keeps_xet_when_available(monkeypatch):
|
||||
_set_xet_reason(monkeypatch, None)
|
||||
assert download_lifecycle.resolve_effective_use_xet(True) is True
|
||||
|
||||
|
||||
def test_resolve_effective_use_xet_downgrades_when_xet_unavailable(monkeypatch):
|
||||
_set_xet_reason(monkeypatch, "Xet transport is unavailable because hf_xet is not installed.")
|
||||
assert download_lifecycle.resolve_effective_use_xet(True) is False
|
||||
|
|
@ -8,7 +8,8 @@ export const TRANSPORT = {
|
|||
|
||||
export const TRANSPORT_MODES = [TRANSPORT.HTTP, TRANSPORT.XET] as const;
|
||||
export type TransportMode = (typeof TRANSPORT_MODES)[number];
|
||||
export const DEFAULT_TRANSPORT_MODE: TransportMode = TRANSPORT.HTTP;
|
||||
// Xet by default; effectiveTransportMode() downgrades to HTTP if hf_xet is missing.
|
||||
export const DEFAULT_TRANSPORT_MODE: TransportMode = TRANSPORT.XET;
|
||||
|
||||
export function isTransportMode(value: unknown): value is TransportMode {
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue