mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-21 03:01:27 -03:00
38d4c59b4c
ModelScope became a linkable source, but downloading from it was impossible:
the URL picker only recognised huggingface.co, the file listing hit a
huggingface-only endpoint, the resolve URL was hardcoded, and the default
path template always wrote into a `huggingface/` directory.
Move the download knowledge into the providers so the handlers stay generic:
- `ModelSource` gains `list_files()`, `file_download_url()`,
`default_revision` and `default_subdir`. `HuggingFaceSource` keeps the Hub
tree API (`/api/models/{id}/tree/{rev}`, LFS-aware sizes, `main`).
`ModelScopeSource` uses `/api/v1/models/{id}/repo/files?Revision=master`
— which reports real byte sizes for LFS files, so no HEAD probe is needed,
and which only accepts `master` (an HF-imported repo still 404s on `main`)
— and downloads through `/models/{id}/resolve/{rev}/{path}`. That URL
redirects to a CDN target carrying a time-limited `auth_key`, so it is
rebuilt on every request and never cached, which is also what keeps
resumable Range requests working.
- `hf_handlers.py`/`HfHandler` become `model_source_handlers.py`/
`ModelSourceHandler` with `list_model_source_files` and
`download_model_source`. New routes `/api/lm/model-source-files` and
`/api/lm/download-model-source`; the old `/api/lm/hf-repo-files` and
`/api/lm/download-hf-model` paths stay as aliases, and a payload without
`platform` still means Hugging Face, so existing callers are unaffected.
- A downloaded sidecar now records `source_platform` + `source_url` (with the
`hf_url` alias only for Hugging Face) instead of always writing `hf_url`,
and `use_default_paths` files ModelScope downloads under
`modelscope/<owner>/<repo>`. The now-unused shared HF aiohttp session and
its shutdown hook are gone; providers open short-lived sessions.
- Frontend: `detectUrlType` returns the platform-neutral
`model-source-repo` / `model-source-file` plus an explicit `platform`, the
DownloadManager's `hf*` state and methods are renamed to `source*`, every
`source === 'huggingface'` check becomes `isExternalModelSource()`, and
batch groups are keyed by `platform:repo` so the same `owner/name` on two
sites renders as two groups. A bare `owner/name` still means Hugging Face.
- `is_valid_source_id()` centralises repo-id validation (exactly
`owner/name`, no traversal, no leading dot). This also fixes the old HF
download check that rejected any dot in the name, i.e. legitimate repos
such as `black-forest-labs/FLUX.1-dev`.
Verified against the live APIs: the example repo lists 8 weight files with
correct sizes, and a ranged GET of the built resolve URL returns 206 after
following the redirect to the CDN. Backend 2853 passed; frontend 1143 JS +
91 Vue passed. The nine locales carry the refreshed download copy in the
next commit.
170 lines
6.9 KiB
Python
170 lines
6.9 KiB
Python
"""Route registrar for miscellaneous endpoints.
|
|
|
|
This module mirrors the model route registrar architecture so that
|
|
miscellaneous endpoints share a consistent registration flow.
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any, Callable, Iterable, Mapping
|
|
|
|
from aiohttp import web
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class RouteDefinition:
|
|
"""Declarative definition for a HTTP route."""
|
|
|
|
method: str
|
|
path: str
|
|
handler_name: str
|
|
|
|
|
|
MISC_ROUTE_DEFINITIONS: tuple[RouteDefinition, ...] = (
|
|
RouteDefinition("GET", "/api/lm/settings", "get_settings"),
|
|
RouteDefinition("POST", "/api/lm/settings", "update_settings"),
|
|
RouteDefinition("GET", "/api/lm/llm/models", "get_llm_models"),
|
|
RouteDefinition("GET", "/api/lm/llm/provider-models", "get_provider_models"),
|
|
RouteDefinition("GET", "/api/lm/doctor/diagnostics", "get_doctor_diagnostics"),
|
|
RouteDefinition("POST", "/api/lm/doctor/repair-cache", "repair_doctor_cache"),
|
|
RouteDefinition("POST", "/api/lm/doctor/resolve-filename-conflicts", "resolve_doctor_filename_conflicts"),
|
|
RouteDefinition("POST", "/api/lm/doctor/export-bundle", "export_doctor_bundle"),
|
|
RouteDefinition("GET", "/api/lm/priority-tags", "get_priority_tags"),
|
|
RouteDefinition("GET", "/api/lm/settings/libraries", "get_settings_libraries"),
|
|
RouteDefinition("POST", "/api/lm/settings/libraries/activate", "activate_library"),
|
|
RouteDefinition("GET", "/api/lm/health-check", "health_check"),
|
|
RouteDefinition("GET", "/api/lm/init-status", "get_init_status"),
|
|
RouteDefinition("GET", "/api/lm/supporters", "get_supporters"),
|
|
RouteDefinition("GET", "/api/lm/wildcards/search", "search_wildcards"),
|
|
RouteDefinition("POST", "/api/lm/wildcards/open-location", "open_wildcards_location"),
|
|
RouteDefinition("POST", "/api/lm/open-file-location", "open_file_location"),
|
|
RouteDefinition("POST", "/api/lm/update-usage-stats", "update_usage_stats"),
|
|
RouteDefinition("GET", "/api/lm/get-usage-stats", "get_usage_stats"),
|
|
RouteDefinition("POST", "/api/lm/update-lora-code", "update_lora_code"),
|
|
RouteDefinition("GET", "/api/lm/update-lora-code", "get_update_lora_code"),
|
|
RouteDefinition("GET", "/api/lm/trained-words", "get_trained_words"),
|
|
RouteDefinition("GET", "/api/lm/model-example-files", "get_model_example_files"),
|
|
RouteDefinition("POST", "/api/lm/register-nodes", "register_nodes"),
|
|
RouteDefinition("POST", "/api/lm/update-node-widget", "update_node_widget"),
|
|
RouteDefinition("GET", "/api/lm/update-node-widget", "get_update_node_widget"),
|
|
RouteDefinition("GET", "/api/lm/get-registry", "get_registry"),
|
|
RouteDefinition("GET", "/api/lm/check-model-exists", "check_model_exists"),
|
|
RouteDefinition("GET", "/api/lm/check-models-exist", "check_models_exist"),
|
|
RouteDefinition(
|
|
"GET",
|
|
"/api/lm/model-version-download-status",
|
|
"get_model_version_download_status",
|
|
),
|
|
RouteDefinition(
|
|
"POST",
|
|
"/api/lm/model-version-download-status",
|
|
"set_model_version_download_status",
|
|
),
|
|
RouteDefinition(
|
|
"GET",
|
|
"/api/lm/set-model-version-download-status",
|
|
"set_model_version_download_status",
|
|
),
|
|
RouteDefinition("GET", "/api/lm/civitai/user-models", "get_civitai_user_models"),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/download-metadata-archive", "download_metadata_archive"
|
|
),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/remove-metadata-archive", "remove_metadata_archive"
|
|
),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/metadata-archive-status", "get_metadata_archive_status"
|
|
),
|
|
RouteDefinition("GET", "/api/lm/backup/status", "get_backup_status"),
|
|
RouteDefinition("POST", "/api/lm/backup/export", "export_backup"),
|
|
RouteDefinition("POST", "/api/lm/backup/import", "import_backup"),
|
|
RouteDefinition("POST", "/api/lm/backup/open-location", "open_backup_location"),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/model-versions-status", "get_model_versions_status"
|
|
),
|
|
RouteDefinition("POST", "/api/lm/settings/open-location", "open_settings_location"),
|
|
RouteDefinition("GET", "/api/lm/custom-words/search", "search_custom_words"),
|
|
RouteDefinition("GET", "/api/lm/example-workflows", "get_example_workflows"),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/example-workflows/{filename}", "get_example_workflow"
|
|
),
|
|
# Base model management routes
|
|
RouteDefinition("GET", "/api/lm/base-models", "get_base_models"),
|
|
RouteDefinition("POST", "/api/lm/base-models/refresh", "refresh_base_models"),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/base-models/categories", "get_base_model_categories"
|
|
),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/base-models/cache-status", "get_base_model_cache_status"
|
|
),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/delete-model-version", "delete_model_version"
|
|
),
|
|
# External model source endpoints (Hugging Face / ModelScope).
|
|
# The hf-* paths are the historical names, kept as aliases.
|
|
RouteDefinition(
|
|
"GET", "/api/lm/model-source-files", "list_model_source_files"
|
|
),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/hf-repo-files", "get_hf_repo_files"
|
|
),
|
|
# Download target routing decision (checkpoint vs diffusion model roots)
|
|
RouteDefinition(
|
|
"POST", "/api/lm/download/routing", "get_download_routing"
|
|
),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/download-model-source", "download_model_source"
|
|
),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/download-hf-model", "download_hf_model"
|
|
),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/set-hf-url", "set_hf_url"
|
|
),
|
|
# Supported external model sites (Hugging Face / ModelScope / TensorArt)
|
|
RouteDefinition(
|
|
"GET", "/api/lm/model-sources", "get_model_sources"
|
|
),
|
|
# Agent skill endpoints
|
|
RouteDefinition(
|
|
"GET", "/api/lm/agent/skills", "get_agent_skills"
|
|
),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/agent/execute/{skill_name}", "execute_agent_skill"
|
|
),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/agent/cancel", "cancel_agent_skill"
|
|
),
|
|
)
|
|
|
|
|
|
class MiscRouteRegistrar:
|
|
"""Bind miscellaneous route definitions to an aiohttp router."""
|
|
|
|
_METHOD_MAP = {
|
|
"GET": "add_get",
|
|
"POST": "add_post",
|
|
"PUT": "add_put",
|
|
"DELETE": "add_delete",
|
|
}
|
|
|
|
def __init__(self, app: web.Application) -> None:
|
|
self._app = app
|
|
|
|
def register_routes(
|
|
self,
|
|
handler_lookup: Mapping[str, Callable[[web.Request], object]],
|
|
*,
|
|
definitions: Iterable[RouteDefinition] = MISC_ROUTE_DEFINITIONS,
|
|
) -> None:
|
|
for definition in definitions:
|
|
self._bind(
|
|
definition.method,
|
|
definition.path,
|
|
handler_lookup[definition.handler_name],
|
|
)
|
|
|
|
def _bind(self, method: str, path: str, handler: Callable[..., Any]) -> None:
|
|
add_method_name = self._METHOD_MAP[method.upper()]
|
|
add_method = getattr(self._app.router, add_method_name)
|
|
add_method(path, handler)
|