106 lines
4.4 KiB
Python
106 lines
4.4 KiB
Python
"""Non-blocking UI adapter for the backend model-discovery cache.
|
|
|
|
The UI never discovers models synchronously and never invents model IDs. The
|
|
adapter deliberately tolerates the small naming differences used by backend
|
|
revisions so the presentation layer can be merged independently from A9.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any, Callable, Optional
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class CachedModels:
|
|
provider: str
|
|
models: tuple[str, ...] = ()
|
|
fetched_at: str = ""
|
|
is_stale: bool = True
|
|
unavailable_reason: str = "Список моделей ещё не получен"
|
|
|
|
|
|
def _service() -> Any:
|
|
try:
|
|
from antigravity_provider.router.model_discovery import ModelDiscoveryService
|
|
|
|
getter = getattr(ModelDiscoveryService, "get", None)
|
|
return getter() if callable(getter) else ModelDiscoveryService()
|
|
except (ImportError, AttributeError, TypeError):
|
|
pass
|
|
try:
|
|
from antigravity_provider.router.model_discovery_service import ModelDiscoveryService
|
|
|
|
getter = getattr(ModelDiscoveryService, "get", None)
|
|
return getter() if callable(getter) else ModelDiscoveryService()
|
|
except (ImportError, AttributeError, TypeError):
|
|
return None
|
|
|
|
|
|
def _normalise(provider: str, raw: Any) -> CachedModels:
|
|
if raw is None:
|
|
return CachedModels(provider=provider)
|
|
if isinstance(raw, dict):
|
|
models = raw.get("models") or raw.get("discovered_models") or []
|
|
fetched_at = raw.get("fetched_at") or raw.get("discovered_at") or raw.get("updated_at") or raw.get("last_refresh_at") or ""
|
|
stale = raw.get("is_stale", raw.get("stale", False))
|
|
reason = raw.get("unavailable_reason") or raw.get("error") or ""
|
|
else:
|
|
models = getattr(raw, "models", None) or getattr(raw, "discovered_models", None) or []
|
|
fetched_at = (
|
|
getattr(raw, "fetched_at", None)
|
|
or getattr(raw, "updated_at", None)
|
|
or getattr(raw, "last_refresh_at", None)
|
|
or ""
|
|
)
|
|
stale = getattr(raw, "is_stale", getattr(raw, "stale", False))
|
|
reason = getattr(raw, "unavailable_reason", None) or getattr(raw, "error", None) or ""
|
|
clean = tuple(dict.fromkeys(str(model).strip() for model in models if str(model).strip()))
|
|
return CachedModels(
|
|
provider=provider,
|
|
models=clean,
|
|
fetched_at=str(fetched_at),
|
|
is_stale=bool(stale),
|
|
unavailable_reason=str(reason or ("" if clean else "Список моделей ещё не получен")),
|
|
)
|
|
|
|
|
|
def get_cached_models(provider: str) -> CachedModels:
|
|
"""Return cached models immediately; never performs provider I/O."""
|
|
service = _service()
|
|
if service is None:
|
|
return CachedModels(provider=provider, unavailable_reason="Служба обнаружения моделей ещё не подключена")
|
|
for name in ("get_cached", "get_cached_models", "get_snapshot", "get_provider_models"):
|
|
method = getattr(service, name, None)
|
|
if not callable(method):
|
|
continue
|
|
try:
|
|
return _normalise(provider, method(provider))
|
|
except Exception as exc:
|
|
return CachedModels(provider=provider, unavailable_reason=f"Кэш моделей недоступен: {exc}")
|
|
return CachedModels(provider=provider, unavailable_reason="Служба не предоставляет чтение кэша моделей")
|
|
|
|
|
|
def refresh_models_async(provider: str, on_complete: Callable[[CachedModels], None]) -> bool:
|
|
"""Request a backend background refresh without blocking the Tk thread."""
|
|
service = _service()
|
|
if service is None:
|
|
on_complete(get_cached_models(provider))
|
|
return False
|
|
for name in ("refresh_models_async", "refresh_models", "refresh_provider_async", "refresh_async", "discover_async"):
|
|
method = getattr(service, name, None)
|
|
if not callable(method):
|
|
continue
|
|
try:
|
|
result: Optional[Any] = method(provider, on_complete=lambda *_args: on_complete(get_cached_models(provider)))
|
|
return result is not False
|
|
except TypeError:
|
|
try:
|
|
result = method(provider)
|
|
return result is not False
|
|
except Exception:
|
|
break
|
|
except Exception:
|
|
break
|
|
on_complete(get_cached_models(provider))
|
|
return False
|