mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-10-05 17:35:33 -03:00
P5a of docs/plans/paid-model-price-tracking.md: one surface that answers "what got cheaper / became free", without a permanent button (the grid filter was dropped by owner decision, so the panel carries the actions itself). - price_alert_since records when an alert started, so the panel can say "dropped 3 d ago" and count what is new since the user last looked; it is set on the first sight of an already-cheap version, preserved while the alert stands, and cleared when the price rises back above the threshold - get_price_alerts() compares the threshold at read time (editing it takes effect immediately, no refresh needed) and returns both kinds in one list; model_type=None covers every type, which the shared update DB makes a single query. "became free" needs no price data, so it is reported even while price tracking is off - GET /api/lm/price-alerts, registered once in MiscRoutes rather than per model type, decorating rows best-effort with the local model name and file path from the scanner indexes (a cold cache just omits them) - a third tab in the notification bell: segments for under-threshold and became-free, the three states (tracking off / nothing matching / stale), and per-row actions (CivitAI always, Open when the model is local) - two non-permanent entry points share one helper: the controls-bar updates dropdown and the global context menu, whose label carries the unread count - unread state stays client-side (localStorage watermark); the count is fetched once on init and only when price tracking is enabled - the per-type frontend client method is removed as dead code; the per-type backend route stays for the companion extension
185 lines
7.6 KiB
Python
185 lines
7.6 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"),
|
|
# App-wide and registered once: the alerts panel spans every model type, and
|
|
# the update DB is shared, so there is nothing per-type about it.
|
|
RouteDefinition("GET", "/api/lm/price-alerts", "get_price_alerts"),
|
|
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/browse-directory", "browse_directory"),
|
|
RouteDefinition("POST", "/api/lm/validate-path", "validate_path"),
|
|
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"
|
|
),
|
|
# Sidecar storage layout migration (GET supported for the extension)
|
|
RouteDefinition(
|
|
"POST", "/api/lm/sidecars/migrate", "migrate_sidecars"
|
|
),
|
|
RouteDefinition(
|
|
"GET", "/api/lm/sidecars/migrate", "migrate_sidecars"
|
|
),
|
|
RouteDefinition(
|
|
"POST", "/api/lm/sidecars/open-location", "open_sidecar_location"
|
|
),
|
|
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)
|