Files
2026-05-22 15:25:59 +08:00

177 lines
6.0 KiB
Python

from __future__ import annotations
import importlib
from .base import RoutingResult, validate_manual_backend
from ...model_profiles.registry import resolve_model_profile
from ...utils.model_info import get_lls_attr, parse_jsonish_info
_BACKENDS = {}
_BUILTINS_LOADED = False
def _ensure_builtin_backends():
global _BUILTINS_LOADED
if _BUILTINS_LOADED:
return
importlib.import_module(f"{__package__}.generic")
importlib.import_module(f"{__package__}.sdxl")
importlib.import_module(f"{__package__}.flux")
_BUILTINS_LOADED = True
def register_backend(backend):
_BACKENDS[str(backend.backend_name).strip().lower()] = backend
return backend
def get_backend(name: str):
_ensure_builtin_backends()
backend = _BACKENDS.get(str(name or "").strip().lower())
if backend is None:
raise RuntimeError(f"[LLS] Unknown pro edit backend '{name}'.")
return backend
def _resolve_profile_and_capabilities(model=None, model_info=None, edit_info=None):
raw_model_info = parse_jsonish_info(model_info)
raw_edit_info = parse_jsonish_info(edit_info)
merged_extra_info = dict(raw_model_info)
merged_extra_info.update(raw_edit_info)
profile = resolve_model_profile(
model=model,
model_info=model_info,
extra_info=merged_extra_info,
checkpoint_name=str(
merged_extra_info.get("checkpoint_name")
or merged_extra_info.get("model_name")
or get_lls_attr(model, "checkpoint_name", None)
or get_lls_attr(model, "model_name", "")
or ""
),
family=(
merged_extra_info.get("family")
or merged_extra_info.get("model_family")
or get_lls_attr(model, "family", None)
or None
),
)
capabilities = {
"model_family": profile["family"],
"model_name": str(
merged_extra_info.get("checkpoint_name")
or merged_extra_info.get("model_name")
or get_lls_attr(model, "checkpoint_name", None)
or get_lls_attr(model, "model_name", "")
or ""
),
"model_role": profile["role"],
"profile_id": profile["profile_id"],
"backend_type": profile["backend_type"],
"sampler_strategy": profile["sampler_strategy"],
"loader_strategy": profile["loader_strategy"],
"supports_inpaint_native": profile["supports_inpaint_native"],
"supports_image_edit_native": profile["supports_image_edit_native"],
"preferred_edit_backend": profile["preferred_edit_backend"],
}
return profile, capabilities
def _resolve_fallback_backend_name(family: str) -> str:
normalized = str(family or "").strip().upper()
if normalized.startswith("SDXL"):
return "sdxl"
if normalized.startswith("FLUX"):
return "flux"
return "generic"
def _build_routing_result(backend, *, routing_reason, capabilities, profile, execution_path):
return backend, RoutingResult(
backend.backend_name,
routing_reason,
capabilities,
profile,
execution_path,
)
def _resolve_profile_route(*, profile, capabilities, native_backend_name):
if native_backend_name is not None:
backend = get_backend(native_backend_name)
validate_manual_backend(native_backend_name, backend, profile)
return _build_routing_result(
backend,
routing_reason="profile.backend_type",
capabilities=capabilities,
profile=profile,
execution_path="native_edit",
)
fallback_backend_name = _resolve_fallback_backend_name(str(profile.get("family") or ""))
backend = get_backend(fallback_backend_name)
validate_manual_backend(fallback_backend_name, backend, profile)
return _build_routing_result(
backend,
routing_reason="profile.family_fallback",
capabilities=capabilities,
profile=profile,
execution_path="fallback_repair",
)
def resolve_backend(backend_mode: str, *, model=None, model_info=None, edit_info=None):
_ensure_builtin_backends()
mode = str(backend_mode or "auto").strip().lower() or "auto"
profile, capabilities = _resolve_profile_and_capabilities(
model=model,
model_info=model_info,
edit_info=edit_info,
)
backend_type = str(profile.get("backend_type") or "none").strip().lower()
native_backend_name = {
"sdxl_native": "sdxl",
"flux_edit": "flux",
}.get(backend_type)
if backend_type != "none" and native_backend_name is None:
raise RuntimeError(f"[LLS] Unsupported pro edit backend_type '{backend_type}'.")
if mode != "auto":
backend = get_backend(mode)
if backend.supports(profile):
routing_reason = "manual.override" if native_backend_name is not None else "manual.family_fallback"
execution_path = "native_edit" if native_backend_name is not None else "fallback_repair"
validate_manual_backend(mode, backend, profile)
return _build_routing_result(
backend,
routing_reason=routing_reason,
capabilities=capabilities,
profile=profile,
execution_path=execution_path,
)
backend, routed = _resolve_profile_route(
profile=profile,
capabilities=capabilities,
native_backend_name=native_backend_name,
)
fallback_reason = (
"manual.incompatible_native_fallback"
if routed.execution_path == "native_edit"
else "manual.incompatible_family_fallback"
)
return _build_routing_result(
backend,
routing_reason=fallback_reason,
capabilities=capabilities,
profile=profile,
execution_path=routed.execution_path,
)
return _resolve_profile_route(
profile=profile,
capabilities=capabilities,
native_backend_name=native_backend_name,
)