mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-07-06 09:21:16 -03:00
feat(ui): load provider model catalog asynchronously to avoid blocking page render
This commit is contained in:
@@ -38,7 +38,12 @@ from ...services.settings_manager import get_settings_manager
|
||||
from ...services.websocket_manager import ws_manager
|
||||
from ...services.downloader import get_downloader
|
||||
from ...services.errors import ResourceNotFoundError
|
||||
from ...services.llm_service import get_provider_model_ids, fetch_ollama_models
|
||||
from ...services.llm_service import (
|
||||
PROVIDER_PRESETS,
|
||||
fetch_ollama_models,
|
||||
get_all_provider_models,
|
||||
get_provider_model_ids,
|
||||
)
|
||||
from ...services.cache_health_monitor import CacheHealthMonitor, CacheHealthStatus
|
||||
from ...utils.models import BaseModelMetadata
|
||||
from ...utils.constants import (
|
||||
@@ -1625,6 +1630,20 @@ class SettingsHandler:
|
||||
def _is_dedicated_example_images_folder(self, folder_path: str) -> bool:
|
||||
return is_valid_example_images_root(folder_path)
|
||||
|
||||
async def get_provider_models(self, request: web.Request) -> web.Response:
|
||||
"""Return the model catalog for all preset providers.
|
||||
|
||||
This endpoint is called asynchronously by the settings UI so that
|
||||
page rendering never blocks on the remote model catalog fetch.
|
||||
"""
|
||||
catalog_provider_ids = [p for p in PROVIDER_PRESETS if p != "custom"]
|
||||
try:
|
||||
provider_models = await get_all_provider_models(catalog_provider_ids)
|
||||
return web.json_response({"success": True, "models": provider_models})
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to fetch provider models: %s", exc)
|
||||
return web.json_response({"success": False, "models": {}, "error": str(exc)})
|
||||
|
||||
|
||||
class UsageStatsHandler:
|
||||
def __init__(self, usage_stats_factory: UsageStatsFactory = UsageStats) -> None:
|
||||
@@ -3395,6 +3414,7 @@ class MiscHandlerSet:
|
||||
"get_settings_libraries": self.settings.get_libraries,
|
||||
"activate_library": self.settings.activate_library,
|
||||
"get_llm_models": self.settings.get_llm_models,
|
||||
"get_provider_models": self.settings.get_provider_models,
|
||||
"update_usage_stats": self.usage_stats.update_usage_stats,
|
||||
"get_usage_stats": self.usage_stats.get_usage_stats,
|
||||
"update_lora_code": self.lora_code.update_lora_code,
|
||||
|
||||
@@ -154,10 +154,13 @@ class ModelPageView:
|
||||
)
|
||||
self._template_env._i18n_filter_added = True # type: ignore[attr-defined]
|
||||
|
||||
from ...services.llm_service import PROVIDER_PRESETS, get_all_provider_models
|
||||
from ...services.llm_service import PROVIDER_PRESETS
|
||||
|
||||
catalog_provider_ids = [p for p in PROVIDER_PRESETS if p != "custom"]
|
||||
provider_models = await get_all_provider_models(catalog_provider_ids)
|
||||
# Provider presets are embedded directly (local, no await needed).
|
||||
# Provider model catalogs are fetched asynchronously by the
|
||||
# frontend via GET /api/lm/llm/provider-models so page rendering
|
||||
# never blocks on the remote model catalog (which can take up to
|
||||
# 30s on cold cache).
|
||||
|
||||
template_context = {
|
||||
"is_initializing": is_initializing,
|
||||
@@ -167,7 +170,7 @@ class ModelPageView:
|
||||
"t": self._server_i18n.get_translation,
|
||||
"version": self._get_app_version(),
|
||||
"provider_presets_json": json.dumps(PROVIDER_PRESETS),
|
||||
"provider_models_json": json.dumps(provider_models),
|
||||
"provider_models_json": "{}",
|
||||
}
|
||||
|
||||
if not is_initializing:
|
||||
|
||||
@@ -23,6 +23,7 @@ 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"),
|
||||
|
||||
@@ -789,6 +789,27 @@ export class SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
async _fetchProviderModelsAsync() {
|
||||
try {
|
||||
const resp = await fetch('/api/lm/llm/provider-models');
|
||||
if (!resp.ok) return;
|
||||
const data = await resp.json();
|
||||
if (data.success && data.models) {
|
||||
this._providerModels = data.models;
|
||||
// Refresh model combobox if the settings modal is still open.
|
||||
// Skip when provider is Ollama — it fetches its own live list
|
||||
// from the local Ollama API and we must not overwrite it.
|
||||
const llmProviderSelect = document.getElementById('llmProvider');
|
||||
const provider = llmProviderSelect ? llmProviderSelect.value : 'openai';
|
||||
if (this._llmModelCombobox && provider !== 'ollama') {
|
||||
this._llmModelCombobox.updatePresets(this._providerModels[provider] || []);
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
// Silently ignore — models stay empty until next modal open
|
||||
}
|
||||
}
|
||||
|
||||
async loadSettingsToUI() {
|
||||
// Set frontend settings from state
|
||||
const blurMatureContentCheckbox = document.getElementById('blurMatureContent');
|
||||
@@ -850,6 +871,12 @@ export class SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
// If the embedded provider models is empty (server did not block on
|
||||
// the remote catalog during page render), fetch asynchronously.
|
||||
if (!this._providerModels || Object.keys(this._providerModels).length === 0) {
|
||||
this._fetchProviderModelsAsync();
|
||||
}
|
||||
|
||||
const llmProviderSelect = document.getElementById('llmProvider');
|
||||
if (llmProviderSelect) {
|
||||
llmProviderSelect.value = state.global.settings.llm_provider || 'openai';
|
||||
|
||||
Reference in New Issue
Block a user