mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-22 05:32:12 -03:00
101 lines
7.6 KiB
Markdown
101 lines
7.6 KiB
Markdown
# Base model route architecture
|
||
|
||
The model routing stack now splits HTTP wiring, orchestration logic, and
|
||
business rules into discrete layers. The goal is to make it obvious where a
|
||
new collaborator should live and which contract it must honour. The diagram
|
||
below captures the end-to-end flow for a typical request:
|
||
|
||
```mermaid
|
||
graph TD
|
||
subgraph HTTP
|
||
A[ModelRouteRegistrar] -->|binds| B[BaseModelRoutes handler proxy]
|
||
end
|
||
subgraph Application
|
||
B --> C[ModelHandlerSet]
|
||
C --> D1[Handlers]
|
||
D1 --> E1[Use cases]
|
||
E1 --> F1[Services / scanners]
|
||
end
|
||
subgraph Side Effects
|
||
F1 --> G1[Cache & metadata]
|
||
F1 --> G2[Filesystem]
|
||
F1 --> G3[WebSocket state]
|
||
end
|
||
```
|
||
|
||
Every box maps to a concrete module:
|
||
|
||
| Layer | Module(s) | Responsibility |
|
||
| --- | --- | --- |
|
||
| Registrar | `py/routes/model_route_registrar.py` | Declarative list of routes shared by every model type and helper methods for binding them to an `aiohttp` application. |
|
||
| Route controller | `py/routes/base_model_routes.py` | Constructs the handler graph, injects shared services, exposes proxies that surface `503 Service not ready` when the model service has not been attached. |
|
||
| Handler set | `py/routes/handlers/model_handlers.py` | Thin HTTP adapters grouped by concern (page rendering, listings, mutations, queries, downloads, CivitAI integration, move operations, auto-organize). |
|
||
| Use cases | `py/services/use_cases/*.py` | Encapsulate long-running flows (`DownloadModelUseCase`, `BulkMetadataRefreshUseCase`, `AutoOrganizeUseCase`). They normalise validation errors and concurrency constraints before returning control to the handlers. |
|
||
| Services | `py/services/*.py` | Existing services and scanners that mutate caches, write metadata, move files, and broadcast WebSocket updates. |
|
||
|
||
## Handler responsibilities & contracts
|
||
|
||
`ModelHandlerSet` flattens the handler objects into the exact callables used by
|
||
the registrar. The table below highlights the separation of concerns within
|
||
the set and the invariants that must hold after each handler returns.
|
||
|
||
| Handler | Key endpoints | Collaborators | Contracts |
|
||
| --- | --- | --- | --- |
|
||
| `ModelPageView` | `/{prefix}` | `SettingsManager`, `server_i18n`, Jinja environment, `service.scanner` | Template is rendered with `is_initializing` flag when caches are cold; i18n filter is registered exactly once per environment instance. |
|
||
| `ModelListingHandler` | `/api/lm/{prefix}/list` | `service.get_paginated_data`, `service.format_response` | Listings respect pagination query parameters and cap `page_size` at 100; every item is formatted before response. |
|
||
| `ModelManagementHandler` | Mutations (delete, exclude, metadata, preview, tags, rename, bulk delete, duplicate verification) | `ModelLifecycleService`, `MetadataSyncService`, `PreviewAssetService`, `TagUpdateService`, scanner cache/index | Cache state mirrors filesystem changes: deletes prune cache & hash index, preview replacements synchronise metadata and cache NSFW levels, metadata saves trigger cache resort when names change. |
|
||
| `ModelQueryHandler` | Read-only queries (top tags, folders, duplicates, metadata, URLs) | Service query helpers & scanner cache | Outputs always wrapped in `{"success": True}` when no error; duplicate/filename grouping omits empty entries; invalid parameters (e.g. missing `model_root`) return HTTP 400. |
|
||
| `ModelDownloadHandler` | `/api/lm/download-model`, `/download-model-get`, `/download-progress/{id}`, `/cancel-download-get` | `DownloadModelUseCase`, `DownloadCoordinator`, `WebSocketManager` | Payload validation errors become HTTP 400 without mutating download progress cache; early-access failures surface as HTTP 401; successful downloads cache progress snapshots that back both WebSocket broadcasts and polling endpoints. |
|
||
| `ModelCivitaiHandler` | CivitAI metadata routes | `MetadataSyncService`, metadata provider factory, `BulkMetadataRefreshUseCase` | `fetch_all_civitai` streams progress via `WebSocketBroadcastCallback`; version lookups validate model type before returning; local availability fields derive from hash lookups without mutating cache state. |
|
||
| `ModelMoveHandler` | `move_model`, `move_models_bulk` | `ModelMoveService` | Moves execute atomically per request; bulk operations aggregate success/failure per file set. |
|
||
| `ModelAutoOrganizeHandler` | `/api/lm/{prefix}/auto-organize` (GET/POST), `/auto-organize-progress` | `AutoOrganizeUseCase`, `WebSocketProgressCallback`, `WebSocketManager` | Enforces single-flight execution using the shared lock; progress broadcasts remain available to polling clients until explicitly cleared; conflicts return HTTP 409 with a descriptive error. |
|
||
|
||
## Use case boundaries
|
||
|
||
Each use case exposes a narrow asynchronous API that hides the underlying
|
||
services. Their error mapping is essential for predictable HTTP responses.
|
||
|
||
| Use case | Entry point | Dependencies | Guarantees |
|
||
| --- | --- | --- | --- |
|
||
| `DownloadModelUseCase` | `execute(payload)` | `DownloadCoordinator.schedule_download` | Translates `ValueError` into `DownloadModelValidationError` for HTTP 400, recognises early-access errors (`"401"` in message) and surfaces them as `DownloadModelEarlyAccessError`, forwards success dictionaries untouched. |
|
||
| `AutoOrganizeUseCase` | `execute(file_paths, progress_callback)` | `ModelFileService.auto_organize_models`, `WebSocketManager` lock | Guarded by `ws_manager` lock + status checks; raises `AutoOrganizeInProgressError` before invoking the file service when another run is already active. |
|
||
| `BulkMetadataRefreshUseCase` | `execute_with_error_handling(progress_callback)` | `MetadataSyncService`, `SettingsManager`, `WebSocketBroadcastCallback` | Iterates through cached models, applies metadata sync, emits progress snapshots that handlers broadcast unchanged. |
|
||
|
||
## Maintaining legacy contracts
|
||
|
||
The refactor preserves the invariants called out in the previous architecture
|
||
notes. The most critical ones are reiterated here to emphasise the
|
||
collaboration points:
|
||
|
||
1. **Cache mutations** – Delete, exclude, rename, and bulk delete operations are
|
||
channelled through `ModelManagementHandler`. The handler delegates to
|
||
`ModelLifecycleService` or `MetadataSyncService`, and the scanner cache is
|
||
mutated in-place before the handler returns. The accompanying tests assert
|
||
that `scanner._cache.raw_data` and `scanner._hash_index` stay in sync after
|
||
each mutation.
|
||
2. **Preview updates** – `PreviewAssetService.replace_preview` writes the new
|
||
asset, `MetadataSyncService` persists the JSON metadata, and
|
||
`scanner.update_preview_in_cache` mirrors the change. The handler returns
|
||
the static URL produced by `config.get_preview_static_url`, keeping browser
|
||
clients in lockstep with disk state.
|
||
3. **Download progress** – `DownloadCoordinator.schedule_download` generates the
|
||
download identifier, registers a WebSocket progress callback, and caches the
|
||
latest numeric progress via `WebSocketManager`. Both `download_model`
|
||
responses and `/download-progress/{id}` polling read from the same cache to
|
||
guarantee consistent progress reporting across transports.
|
||
|
||
## Extending the stack
|
||
|
||
To add a new shared route:
|
||
|
||
1. Declare it in `COMMON_ROUTE_DEFINITIONS` using a unique handler name.
|
||
2. Implement the corresponding coroutine on one of the handlers inside
|
||
`ModelHandlerSet` (or introduce a new handler class when the concern does not
|
||
fit existing ones).
|
||
3. Inject additional dependencies in `BaseModelRoutes._create_handler_set` by
|
||
wiring services or use cases through the constructor parameters.
|
||
|
||
Model-specific routes should continue to be registered inside the subclass
|
||
implementation of `setup_specific_routes`, reusing the shared registrar where
|
||
possible.
|