unsloth/studio/backend/hub/schemas/downloads.py
Daniel Han a3975d6500 Stop adopting an unknown scoped download, leaking raced blobs and resurrecting deleted clips
Three items from the latest review round.

A scoped download job carries a deliberate file subset, and every file set of one repo rides
the same "@scope" slot. A client that adopts a live job from the backend had no file list to
compare against: the active-downloads response never carried one, so an adopted job's set was
unknown and any later scoped request for the same repo read as "already started". Selecting a
different checkpoint then waited on the wrong transfer and tried to load a file nobody
fetched. The response now publishes the scoped file list, adoption records it, and an unknown
set no longer satisfies a scoped request.

A gallery record can be deleted while its blob is still downloading. The delete revokes the
URL present at that moment, so the fetch that lands afterwards inserted a fresh object URL for
a record no card renders and nothing can revoke: a full MP4, tens to hundreds of MB, pinned
for the rest of the session, and once per raced fetch. Both galleries now discard a blob whose
record went away, with an epoch covering the video page's Clear all.

The video backend keeps the last completed job until the next one starts, and the Video page
merges that record on mount to cover a job that finished after the gallery fetch. Deleting the
clip left the record in place, so every reload prepended a ghost card whose file request 404s
until another generation replaced it. Deleting the clip, or clearing the gallery, now clears
the matching terminal record, and the page skips a record it deleted itself.
2026-07-27 03:26:10 +00:00

180 lines
5.5 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
"""Pydantic schemas for the Hub download manager (/api/hub/downloads/*)."""
from pydantic import BaseModel, Field
from typing import List, Literal, Optional
DownloadJobState = Literal["idle", "running", "cancelling", "cancelled", "complete", "error"]
class DownloadModelRequest(BaseModel):
"""Body for POST /api/hub/download.
The HuggingFace token travels in the internal Hub token header.
"""
repo_id: str = Field(
...,
description = "HuggingFace repo ID, e.g. 'unsloth/Qwen3-4B-GGUF'",
)
gguf_variant: Optional[str] = Field(
None,
description = "Quantization label (e.g. 'Q4_K_M'). Required for GGUF repos.",
)
use_xet: bool = Field(
True,
description = "Use Xet parallel chunked transport. Default True; set False for HTTP Range-resume.",
)
scope_id: Optional[str] = Field(
None,
description = "Marks a partial-by-design download of `files` only (e.g. 'diffusion', "
"whose loader reads a scoped subset of a repo). Keyed separately from the full "
"snapshot of the same repo, so neither one's manifest describes the other.",
)
files: List[str] = Field(
default_factory = list,
description = "Exact files to fetch. Required with scope_id, ignored without it.",
)
class CancelDownloadRequest(BaseModel):
repo_id: str = Field(..., description = "HuggingFace repo ID")
gguf_variant: Optional[str] = Field(
None,
description = "GGUF variant label; omit for safetensors snapshots",
)
generation: Optional[int] = Field(
None,
description = "Download generation tag from a prior start; passing it scopes the cancel to that exact run.",
)
class DownloadJobStatus(BaseModel):
"""Live state of a background download job."""
state: DownloadJobState = Field(
...,
description = "Current download job state.",
)
error: Optional[str] = Field(None, description = "Error message if state == 'error'")
generation: int = Field(
0,
description = "Current run generation; an adopting client stores it so a later cancel is scoped to this exact run.",
)
class DownloadStartResponse(BaseModel):
job_key: str
state: str
accepted: bool
generation: int
class CancelDownloadResponse(BaseModel):
job_key: str
state: str
class ActiveDownload(BaseModel):
"""One in-flight download for a repo. ``variant`` is null for safetensors."""
repo_id: Optional[str] = None
variant: Optional[str] = None
transport: Optional[str] = None
state: str
files: Optional[List[str]] = Field(
None,
description = (
"For a SCOPED job (variant '@name'), the exact file list it is fetching; null for a "
"full-snapshot or variant download. Every file set of one repo shares the scope slot, "
"so an adopting client needs this to tell whether a live job is its own transfer or a "
"sibling checkpoint's."
),
)
generation: int = Field(
0,
description = "Current run generation; an adopting client stores it so a later cancel is scoped to this exact run.",
)
class ActiveDownloadsResponse(BaseModel):
downloads: List[ActiveDownload]
class TransportCapability(BaseModel):
available: bool
reason: Optional[str] = None
class TransportCapabilities(BaseModel):
http: TransportCapability
xet: TransportCapability
class TransportStatusResponse(BaseModel):
has_partial: bool
last_transport: Optional[str] = None
resumable: bool
class DownloadProgressResponse(BaseModel):
downloaded_bytes: int
# Finalized-blob bytes only (no ``.incomplete``). Registry-loss completion
# fallbacks key off this so a partial isn't mistaken for a finished download.
completed_bytes: int = 0
complete_on_disk: bool = Field(
False,
description = (
"True only when the backend verified a usable completed snapshot/variant on disk."
),
)
expected_bytes: int
progress: float
cache_path: Optional[str] = None
class DownloadDatasetRequest(BaseModel):
"""Body for POST /api/hub/datasets/download.
The HuggingFace token travels in the internal Hub token header.
"""
repo_id: str = Field(..., description = "HuggingFace dataset repo ID")
use_xet: bool = Field(
True,
description = "Use Xet parallel chunked transport. Default True; set False for HTTP Range-resume.",
)
class CancelDatasetDownloadRequest(BaseModel):
repo_id: str = Field(..., description = "HuggingFace dataset repo ID")
generation: Optional[int] = Field(None, description = "Download generation")
class DatasetDownloadJobStatus(BaseModel):
"""Live state of a background dataset download job."""
state: DownloadJobState = Field(
...,
description = "Current dataset download job state.",
)
error: Optional[str] = Field(None, description = "Error message if state == 'error'")
generation: int = Field(
0,
description = "Current run generation; an adopting client stores it so a later cancel is scoped to this exact run.",
)
class DatasetDownloadStartResponse(BaseModel):
repo_id: str
state: str
accepted: bool
generation: int
class CancelDatasetDownloadResponse(BaseModel):
repo_id: str
state: str