Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
be5c91e
feat: add job progress reporting to queue infrastructure
Irozuku Jul 1, 2026
33bb5b7
feat: report progress from training, dataset, prediction and converte…
Irozuku Jul 1, 2026
0774f8f
feat: show per-job progress bar and phase in the job queue widget
Irozuku Jul 1, 2026
5fa8db5
test: cover job progress reporting and widget progress bar
Irozuku Jul 1, 2026
1093709
feat: add COMPONENT_PATH config for downloadable components
Irozuku Jul 1, 2026
54c66ba
feat: add DownloadableMixin and HuggingFace download helpers
Irozuku Jul 1, 2026
c0c8f37
test: harden downloadable mixin tests and add _repo_dir docstring
Irozuku Jul 1, 2026
21d3864
feat: expose requires_download and download_size_bytes in model metadata
Irozuku Jul 1, 2026
06d3c58
feat: track component download status in the registry
Irozuku Jul 1, 2026
139e096
docs: document downloaded flag in component registry
Irozuku Jul 1, 2026
45a007d
test: verify components API exposes download fields
Irozuku Jul 1, 2026
c2a8d3a
feat: add ComponentDownloadJob
Irozuku Jul 1, 2026
e077f08
fix: narrow component download job registry lookup exception
Irozuku Jul 1, 2026
831cf2e
feat: add component download, delete and status endpoints
Irozuku Jul 1, 2026
5df76f6
fix: return 409 on delete of non-downloadable component and cover dow…
Irozuku Jul 1, 2026
e891688
feat: block use of undownloaded components at run creation and training
Irozuku Jul 1, 2026
46f90a9
fix: return 422 for unknown model in run gate and restore registry st…
Irozuku Jul 1, 2026
82c9270
feat: make Opus-MT translation models downloadable
Irozuku Jul 1, 2026
5c2354c
docs: correct Opus-MT loading note for local downloads
Irozuku Jul 1, 2026
3878e3c
feat: add frontend component download API and types
Irozuku Jul 1, 2026
7ca82c5
feat: add download metadata fields to component metadata type
Irozuku Jul 1, 2026
95d2f7c
feat: add component download control with progress and usage gating
Irozuku Jul 1, 2026
513ae7a
fix: stop component download poller on unmount and handle delete errors
Irozuku Jul 1, 2026
5c9caac
fix: persist Opus-MT tokenizer with runs so prediction survives downl…
Irozuku Jul 1, 2026
2b2ffae
feat: block undownloaded models in the models list with inline download
Irozuku Jul 2, 2026
06ec7ed
feat: block undownloaded components in ComponentSelector cards with i…
Irozuku Jul 2, 2026
4da81af
feat: expose download metadata and gate generative models
Irozuku Jul 2, 2026
f284a7e
feat: support single-file (allow_patterns) downloads in HFDownloadabl…
Irozuku Jul 2, 2026
a07686c
feat: split GGUF text models into per-checkpoint downloadable components
Irozuku Jul 2, 2026
6108b45
fix: keep generative session unknown-model response at 400 and update…
Irozuku Jul 2, 2026
a59d251
feat: allow changing a generative session's model with a download gate
Irozuku Jul 2, 2026
adb6781
feat: split Mixtral into per-quantization downloadable components
Irozuku Jul 2, 2026
e90d4ca
feat: add session-level model switcher for generative sessions
Irozuku Jul 2, 2026
ddc682d
fix: reconcile component download status against filesystem on read
Irozuku Jul 2, 2026
70b0e61
fix: keep download controls mounted and add inline delete for models
Irozuku Jul 2, 2026
823bf0b
feat: notify on successful component download deletion
Irozuku Jul 2, 2026
e89a65b
refactor: remove session info button from GenerativeChat component
Irozuku Jul 2, 2026
b0388c9
feat: gate generative sessions on model download and log model switches
Irozuku Jul 2, 2026
a54c780
feat: make text classification transformers downloadable
Irozuku Jul 2, 2026
4f3a4a3
feat: make translation transformers downloadable
Irozuku Jul 2, 2026
9099ccc
feat: make torchvision image classifiers downloadable
Irozuku Jul 2, 2026
3886d58
fix: reconcile model download gate against the filesystem
Irozuku Jul 2, 2026
592385e
feat: split Stable Diffusion 2 into downloadable per-checkpoint models
Irozuku Jul 2, 2026
1ac6267
feat: split SDXL and make SDXL-Turbo downloadable per checkpoint
Irozuku Jul 2, 2026
4f64bda
feat: split PixArt-Sigma and Tongyi Z-Image into downloadable checkpo…
Irozuku Jul 2, 2026
0f72825
feat: split Stable Diffusion 3 into downloadable per-checkpoint models
Irozuku Jul 2, 2026
beef665
feat: make ControlNet models downloadable with per-repo local loading
Irozuku Jul 2, 2026
ec3203a
feat: download ControlNet preprocessors into the component folder
Irozuku Jul 2, 2026
2ea09b1
feat: add detailed per-checkpoint descriptions with repo links
Irozuku Jul 3, 2026
c63b9c3
fix: update model download state in place to avoid list scroll reset
Irozuku Jul 3, 2026
eb36dfb
feat: add collector for nested downloadable components
Irozuku Jul 3, 2026
25fa0bc
feat: block runs and sessions on undownloaded nested components
Irozuku Jul 3, 2026
990697b
feat: add endpoint to resolve required nested downloads
Irozuku Jul 3, 2026
70ea125
feat: add dummy downloadable classifier for nested download testing
Irozuku Jul 3, 2026
9c00e43
feat: show inline download control in nested component selectors
Irozuku Jul 3, 2026
0a679d7
fix: sync download state across all controls for a component
Irozuku Jul 3, 2026
9b51b65
fix: allow selecting a component nested two levels deep
Irozuku Jul 3, 2026
a1ef348
fix: add root crumb so nested config can return to the top model
Irozuku Jul 3, 2026
89790c7
feat: disable Next when a nested component is not downloaded
Irozuku Jul 3, 2026
3ad94d9
fix: keep download button at normal color on undownloaded cards
Irozuku Jul 3, 2026
5c409ab
feat: allow selecting undownloaded components to preview their descri…
Irozuku Jul 3, 2026
67c2899
refactor: simplify download control and use a plain status icon in th…
Irozuku Jul 3, 2026
999fda1
feat: show model description and download size on hover in the side bar
Irozuku Jul 3, 2026
ed784a3
fix: correct model descriptions to reflect explicit downloads
Irozuku Jul 3, 2026
d6afec3
fix: refresh models side bar when any download finishes
Irozuku Jul 3, 2026
8813305
fix: keep model row disabled while its download is in progress
Irozuku Jul 3, 2026
640212b
fix: avoid scroll reset when a model download finishes or is deleted
Irozuku Jul 3, 2026
0ba2b4b
fix: render the model delete control as an icon button
Irozuku Jul 3, 2026
38552a5
feat: add a tooltip to the model delete icon button
Irozuku Jul 3, 2026
5932951
fix: disable train and retrain when the run model is not downloaded
Irozuku Jul 3, 2026
b2e8686
fix: skip undownloaded models in Run All
Irozuku Jul 3, 2026
89c16cb
fix: block generative session while its model is still downloading
Irozuku Jul 3, 2026
383ea0a
fix: stop reporting freshly queued jobs as failed
Irozuku Jul 3, 2026
90ffb3f
chore: remove dummy downloadable classifier used for nested download …
Irozuku Jul 3, 2026
e71d100
test: drop dummy model name from download icon test fixture
Irozuku Jul 3, 2026
1c0e4b2
Merge branch 'develop' into feat/component-download
Irozuku Jul 9, 2026
962ef33
fix: set accurate model download sizes to match the actual download
Irozuku Jul 9, 2026
b50f17f
fix: adapt Opus En-Roa and Roa-En models to the download system
Irozuku Jul 9, 2026
cdc8e0d
feat: skip alternate framework weights when downloading HF models
Irozuku Jul 9, 2026
f51d755
fix: force classic transfer path for model downloads
Irozuku Jul 9, 2026
12ea673
fix: resolve PixArt-Sigma model source before loading
Irozuku Jul 9, 2026
9263176
fix: load PixArt-Sigma 512 from its transformer plus the 1024 pipeline
Irozuku Jul 9, 2026
82d0069
refactor: merge PixArt-Sigma 512 and 1024 into one component
Irozuku Jul 9, 2026
0327586
feat: confirm before deleting a component download
Irozuku Jul 9, 2026
6199d2b
fix: stop delete confirmation clicks from triggering the row underneath
Irozuku Jul 9, 2026
47da57f
feat: show download size on the delete button
Irozuku Jul 9, 2026
1918286
fix: close model hover popover when clicking a row action
Irozuku Jul 10, 2026
d5bd97a
fix: disable train in the comparison table when the model is not down…
Irozuku Jul 10, 2026
1d1db7a
fix: improve hover feedback for not downloaded models in ModelListItem
Irozuku Jul 10, 2026
0f50088
test: fix download, opus, and job fixtures broken by download changes
Irozuku Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ build/
develop-eggs/
dist/
downloads/
!DashAI/back/dependencies/downloads/
!tests/back/downloads/
eggs/
.eggs/
lib/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Add model_name to parameter_history

Revision ID: a7d2c9e4f1b0
Revises: f1a2b3c4d5e6
Create Date: 2026-07-02 00:00:00.000000

"""

from typing import Sequence, Union

import sqlalchemy as sa

from alembic import op

revision: str = "a7d2c9e4f1b0"
down_revision: Union[str, None] = "f1a2b3c4d5e6"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
with op.batch_alter_table("parameter_history", schema=None) as batch_op:
batch_op.add_column(sa.Column("model_name", sa.String(), nullable=True))


def downgrade() -> None:
with op.batch_alter_table("parameter_history", schema=None) as batch_op:
batch_op.drop_column("model_name")
190 changes: 189 additions & 1 deletion DashAI/back/api/api_v1/endpoints/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import logging
from typing import TYPE_CHECKING, Any, Dict, List, Union

from fastapi import APIRouter, Depends, Header, Query, status
from fastapi import APIRouter, Depends, Header, Query, Response, status
from fastapi.exceptions import HTTPException
from fastapi.responses import StreamingResponse
from kink import di, inject
from pydantic import BaseModel
from typing_extensions import Annotated

from DashAI.back.core.utils import MultilingualString
Expand Down Expand Up @@ -230,12 +231,199 @@ async def get_components(
components_with_related_type,
)

# Reconcile the download state of download required components against the
# filesystem before returning. Downloads happen in the worker process, so
# the in memory registry flag can be stale; a fresh check (a cheap folder
# stat per downloadable component) keeps the list truthful.
for comp_name, component_dict in selected_components.items():
if getattr(component_dict.get("class"), "REQUIRES_DOWNLOAD", False):
component_registry.refresh_download_status(comp_name)

return [
_filter_by_language(_delete_class(component_dict), accept_language)
for component_dict in selected_components.values()
]


@router.get("/{name}/download")
@inject
async def get_component_download_status(
name: str,
component_registry: "ComponentRegistry" = Depends(lambda: di["component_registry"]),
):
"""Return the reconciled download status of a component.

Parameters
----------
name : str
The component class name.

Returns
-------
dict
``{"downloaded": bool, "requires_download": bool}``.
"""
try:
component_class = component_registry[name]["class"]
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e
requires = bool(getattr(component_class, "REQUIRES_DOWNLOAD", False))
downloaded = component_registry.refresh_download_status(name)
return {"downloaded": downloaded, "requires_download": requires}


@router.post("/{name}/download", status_code=status.HTTP_201_CREATED)
@inject
async def download_component(
name: str,
component_registry: "ComponentRegistry" = Depends(lambda: di["component_registry"]),
job_queue=Depends(lambda: di["job_queue"]),
):
"""Enqueue a job to download the component's artifacts.

Parameters
----------
name : str
The component class name.

Returns
-------
dict
``{"id": job_id}`` of the enqueued download job.

Raises
------
HTTPException
404 if unknown; 409 if not downloadable or already downloaded.
"""
from DashAI.back.job.component_download_job import ComponentDownloadJob

try:
component_class = component_registry[name]["class"]
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e
if not getattr(component_class, "REQUIRES_DOWNLOAD", False):
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Component {name} does not require a download",
)
if component_registry.refresh_download_status(name):
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Component {name} is already downloaded",
)
job = ComponentDownloadJob(component_name=name)
job.set_status_as_delivered()
job_id = job_queue.put(job).id
return {"id": job_id}


@router.delete("/{name}/download", status_code=status.HTTP_204_NO_CONTENT)
@inject
async def delete_component_download(
name: str,
component_registry: "ComponentRegistry" = Depends(lambda: di["component_registry"]),
):
"""Delete a component's downloaded artifacts and reconcile its status.

Parameters
----------
name : str
The component class name.
"""
try:
component_class = component_registry[name]["class"]
except Exception as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) from e
if not getattr(component_class, "REQUIRES_DOWNLOAD", False):
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Component {name} does not support download and cannot be deleted",
)
component_class.delete()
component_registry.refresh_download_status(name)
return Response(status_code=status.HTTP_204_NO_CONTENT)


class RequiredDownloadsParams(BaseModel):
"""Request body for resolving nested download-required components.

Attributes
----------
model_name : str or None
The parent component being configured. When set and it still needs a
download, it is included in the result so the caller can gate on a
single list.
parameters : dict
The parameters dict as produced by the configuration UI.
"""

model_name: Union[str, None] = None
parameters: Dict[str, Any] = {}


@router.post("/downloads/required")
@inject
async def get_required_downloads(
params: RequiredDownloadsParams,
accept_language: str | None = Header(default=None),
component_registry: "ComponentRegistry" = Depends(lambda: di["component_registry"]),
) -> List[Dict[str, Any]]:
"""Return the components a configuration still needs downloaded.

Walks the ``parameters`` dict for nested components (a component selected as
another component's parameter) and, optionally, checks the parent
``model_name`` itself. Each component is reconciled against the filesystem so
the answer reflects downloads finished after startup.

Parameters
----------
params : RequiredDownloadsParams
The parent ``model_name`` (optional) and its ``parameters`` dict.
accept_language : str | None
The 'Accept-Language' header used to localize display names.
component_registry : ComponentRegistry
Registry that resolves component classes and download state.

Returns
-------
list[dict]
One entry per not-yet-downloaded component, each with ``name``,
``display_name``, ``parent``, and ``download_size_bytes``.
"""
from DashAI.back.dependencies.downloads.nested import missing_downloads

missing = missing_downloads(params.parameters, component_registry)

# Optionally fold in the parent model so callers can gate on one list.
if params.model_name and params.model_name in component_registry:
parent_class = component_registry[params.model_name]["class"]
if getattr(
parent_class, "REQUIRES_DOWNLOAD", False
) and not component_registry.refresh_download_status(params.model_name):
missing.insert(
0,
{
"name": params.model_name,
"parent": None,
"download_size_bytes": getattr(
parent_class, "DOWNLOAD_SIZE_BYTES", None
),
},
)

def _localized_name(name: str) -> str:
display = component_registry[name].get("display_name")
if isinstance(display, MultilingualString):
lang = (accept_language or "en").split("-")[0].lower()
return display.get(lang)
return display or name

return [
{**entry, "display_name": _localized_name(entry["name"])} for entry in missing
]


@router.get("/{id}/")
@inject
def get_component_by_id(
Expand Down
Loading
Loading