mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-08-24 06:21:26 -03:00
fix(init): add missing /api/lm/init-status endpoint used by polling fallback
initialization.js falls back to polling /api/lm/init-status when the /ws/init-progress WebSocket cannot be established, but no route ever registered that path — each poll 404'd and the page never reloaded after the scan completed. Report the aggregate status of all four scanners and omit pageType so every initialization page accepts the update.
This commit is contained in:
@@ -649,9 +649,60 @@ class NodeRegistry:
|
|||||||
|
|
||||||
|
|
||||||
class HealthCheckHandler:
|
class HealthCheckHandler:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
scanner_getters: Mapping[str, Callable[[], Awaitable[Any]]] | None = None,
|
||||||
|
) -> None:
|
||||||
|
self._scanner_getters = scanner_getters or {
|
||||||
|
"lora": ServiceRegistry.get_lora_scanner,
|
||||||
|
"checkpoint": ServiceRegistry.get_checkpoint_scanner,
|
||||||
|
"embedding": ServiceRegistry.get_embedding_scanner,
|
||||||
|
"recipe": ServiceRegistry.get_recipe_scanner,
|
||||||
|
}
|
||||||
|
|
||||||
async def health_check(self, request: web.Request) -> web.Response:
|
async def health_check(self, request: web.Request) -> web.Response:
|
||||||
return web.json_response({"status": "ok"})
|
return web.json_response({"status": "ok"})
|
||||||
|
|
||||||
|
async def get_init_status(self, request: web.Request) -> web.Response:
|
||||||
|
"""Report aggregate scanner initialization status.
|
||||||
|
|
||||||
|
Used by the initialization page's polling fallback when the
|
||||||
|
/ws/init-progress WebSocket is unavailable. Omits pageType so every
|
||||||
|
page accepts the update and only reloads once all scanners are done.
|
||||||
|
"""
|
||||||
|
pending: list[str] = []
|
||||||
|
for name, getter in self._scanner_getters.items():
|
||||||
|
try:
|
||||||
|
scanner = await getter()
|
||||||
|
except Exception:
|
||||||
|
pending.append(name)
|
||||||
|
continue
|
||||||
|
cache_ready = getattr(scanner, "_cache", None) is not None
|
||||||
|
is_initializing = getattr(scanner, "is_initializing", None)
|
||||||
|
busy = (
|
||||||
|
is_initializing()
|
||||||
|
if callable(is_initializing)
|
||||||
|
else bool(getattr(scanner, "_is_initializing", False))
|
||||||
|
)
|
||||||
|
if busy or not cache_ready:
|
||||||
|
pending.append(name)
|
||||||
|
|
||||||
|
if pending:
|
||||||
|
return web.json_response(
|
||||||
|
{
|
||||||
|
"status": "initializing",
|
||||||
|
"stage": "processing",
|
||||||
|
"details": "Initializing: " + ", ".join(pending),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return web.json_response(
|
||||||
|
{
|
||||||
|
"status": "complete",
|
||||||
|
"progress": 100,
|
||||||
|
"details": "Initialization complete",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SupportersHandler:
|
class SupportersHandler:
|
||||||
"""Handler for supporters data."""
|
"""Handler for supporters data."""
|
||||||
@@ -3859,6 +3910,7 @@ class MiscHandlerSet:
|
|||||||
) -> Mapping[str, Callable[[web.Request], Awaitable[web.StreamResponse]]]:
|
) -> Mapping[str, Callable[[web.Request], Awaitable[web.StreamResponse]]]:
|
||||||
return {
|
return {
|
||||||
"health_check": self.health.health_check,
|
"health_check": self.health.health_check,
|
||||||
|
"get_init_status": self.health.get_init_status,
|
||||||
"get_settings": self.settings.get_settings,
|
"get_settings": self.settings.get_settings,
|
||||||
"update_settings": self.settings.update_settings,
|
"update_settings": self.settings.update_settings,
|
||||||
"get_doctor_diagnostics": self.doctor.get_doctor_diagnostics,
|
"get_doctor_diagnostics": self.doctor.get_doctor_diagnostics,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ MISC_ROUTE_DEFINITIONS: tuple[RouteDefinition, ...] = (
|
|||||||
RouteDefinition("GET", "/api/lm/settings/libraries", "get_settings_libraries"),
|
RouteDefinition("GET", "/api/lm/settings/libraries", "get_settings_libraries"),
|
||||||
RouteDefinition("POST", "/api/lm/settings/libraries/activate", "activate_library"),
|
RouteDefinition("POST", "/api/lm/settings/libraries/activate", "activate_library"),
|
||||||
RouteDefinition("GET", "/api/lm/health-check", "health_check"),
|
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/supporters", "get_supporters"),
|
||||||
RouteDefinition("GET", "/api/lm/wildcards/search", "search_wildcards"),
|
RouteDefinition("GET", "/api/lm/wildcards/search", "search_wildcards"),
|
||||||
RouteDefinition("POST", "/api/lm/wildcards/open-location", "open_wildcards_location"),
|
RouteDefinition("POST", "/api/lm/wildcards/open-location", "open_wildcards_location"),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from py.routes.handlers.misc_handlers import (
|
|||||||
BackupHandler,
|
BackupHandler,
|
||||||
DoctorHandler,
|
DoctorHandler,
|
||||||
FileSystemHandler,
|
FileSystemHandler,
|
||||||
|
HealthCheckHandler,
|
||||||
LoraCodeHandler,
|
LoraCodeHandler,
|
||||||
ModelLibraryHandler,
|
ModelLibraryHandler,
|
||||||
NodeRegistry,
|
NodeRegistry,
|
||||||
@@ -2010,3 +2011,54 @@ async def test_resolve_filename_conflicts_handles_scanner_error_gracefully():
|
|||||||
|
|
||||||
assert payload["success"] is True
|
assert payload["success"] is True
|
||||||
assert payload["count"] == 0
|
assert payload["count"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_init_status_reports_complete_when_all_scanners_ready():
|
||||||
|
async def ready_scanner():
|
||||||
|
return SimpleNamespace(_cache=object(), is_initializing=lambda: False)
|
||||||
|
|
||||||
|
handler = HealthCheckHandler(
|
||||||
|
scanner_getters={
|
||||||
|
"lora": ready_scanner,
|
||||||
|
"recipe": ready_scanner,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await handler.get_init_status(FakeRequest(method="GET")) # pyright: ignore[reportArgumentType]
|
||||||
|
payload = _json_payload(response)
|
||||||
|
|
||||||
|
assert payload["status"] == "complete"
|
||||||
|
assert payload["progress"] == 100
|
||||||
|
assert "pageType" not in payload
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_init_status_reports_pending_scanners():
|
||||||
|
async def ready_scanner():
|
||||||
|
return SimpleNamespace(_cache=object(), is_initializing=lambda: False)
|
||||||
|
|
||||||
|
async def initializing_scanner():
|
||||||
|
return SimpleNamespace(_cache=object(), is_initializing=lambda: True)
|
||||||
|
|
||||||
|
async def no_cache_scanner():
|
||||||
|
return SimpleNamespace(_cache=None, is_initializing=lambda: False)
|
||||||
|
|
||||||
|
async def failing_scanner():
|
||||||
|
raise RuntimeError("scanner unavailable")
|
||||||
|
|
||||||
|
handler = HealthCheckHandler(
|
||||||
|
scanner_getters={
|
||||||
|
"lora": ready_scanner,
|
||||||
|
"checkpoint": initializing_scanner,
|
||||||
|
"embedding": no_cache_scanner,
|
||||||
|
"recipe": failing_scanner,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await handler.get_init_status(FakeRequest(method="GET")) # pyright: ignore[reportArgumentType]
|
||||||
|
payload = _json_payload(response)
|
||||||
|
|
||||||
|
assert payload["status"] == "initializing"
|
||||||
|
assert "checkpoint" in payload["details"]
|
||||||
|
assert "embedding" in payload["details"]
|
||||||
|
assert "recipe" in payload["details"]
|
||||||
|
assert "lora" not in payload["details"]
|
||||||
|
|||||||
Reference in New Issue
Block a user