183 lines
5.8 KiB
Python
183 lines
5.8 KiB
Python
"""Registry and dispatch helpers for backend custom-node model adapters."""
|
|
|
|
from typing import Any, Callable, Dict, List, Optional
|
|
|
|
from ..contracts import (
|
|
CustomNodeMetadata,
|
|
ModelReference,
|
|
ResolvedModel,
|
|
)
|
|
from .base import CustomNodeModelAdapter
|
|
from .lora_manager import ADAPTER as LORA_MANAGER_ADAPTER
|
|
from .rgthree_power_lora_loader import (
|
|
ADAPTER as RGTHREE_POWER_LORA_LOADER_ADAPTER,
|
|
)
|
|
|
|
CUSTOM_NODE_MODEL_ADAPTERS = (
|
|
LORA_MANAGER_ADAPTER,
|
|
RGTHREE_POWER_LORA_LOADER_ADAPTER,
|
|
)
|
|
_ADAPTERS_BY_ID = {
|
|
adapter.adapter_id: adapter for adapter in CUSTOM_NODE_MODEL_ADAPTERS
|
|
}
|
|
_ADAPTERS_BY_NODE_TYPE = {
|
|
node_type: adapter
|
|
for adapter in CUSTOM_NODE_MODEL_ADAPTERS
|
|
for node_type in adapter.node_types
|
|
}
|
|
|
|
|
|
def get_custom_node_model_adapter(
|
|
node_or_type: Any,
|
|
) -> Optional[CustomNodeModelAdapter]:
|
|
"""Return the adapter registered for a node dictionary or node type."""
|
|
node_type = (
|
|
node_or_type.get("type", "")
|
|
if isinstance(node_or_type, dict)
|
|
else str(node_or_type or "")
|
|
)
|
|
return _ADAPTERS_BY_NODE_TYPE.get(node_type)
|
|
|
|
|
|
def get_custom_node_adapter_for_reference(
|
|
reference: Optional[ModelReference],
|
|
) -> Optional[CustomNodeModelAdapter]:
|
|
"""Resolve an adapter from normalized or legacy reference metadata."""
|
|
if reference is None:
|
|
return None
|
|
adapter_id = reference.extra_value("custom_node_adapter")
|
|
if adapter_id in _ADAPTERS_BY_ID:
|
|
return _ADAPTERS_BY_ID[adapter_id]
|
|
adapter = get_custom_node_model_adapter(reference.node_type)
|
|
if adapter:
|
|
return adapter
|
|
if CustomNodeMetadata.from_mapping(reference.extra).is_legacy_lora_manager:
|
|
return LORA_MANAGER_ADAPTER
|
|
return None
|
|
|
|
|
|
def get_custom_node_category_hints() -> Dict[str, str]:
|
|
"""Return node-type category hints contributed by adapters."""
|
|
return {
|
|
node_type: adapter.category_hint
|
|
for adapter in CUSTOM_NODE_MODEL_ADAPTERS
|
|
if adapter.category_hint
|
|
for node_type in adapter.node_types
|
|
}
|
|
|
|
|
|
def get_custom_node_widget_categories() -> Dict[str, Dict[int, str]]:
|
|
"""Return fixed serialized widget categories contributed by adapters."""
|
|
return {
|
|
node_type: dict(adapter.widget_categories)
|
|
for adapter in CUSTOM_NODE_MODEL_ADAPTERS
|
|
if adapter.widget_categories
|
|
for node_type in adapter.node_types
|
|
}
|
|
|
|
|
|
def get_custom_node_resolution_metadata(
|
|
reference: Optional[ModelReference],
|
|
) -> CustomNodeMetadata:
|
|
"""Normalize adapter metadata carried into workflow update resolutions."""
|
|
adapter = get_custom_node_adapter_for_reference(reference)
|
|
if not adapter or reference is None:
|
|
return CustomNodeMetadata()
|
|
metadata = {
|
|
"custom_node_adapter": adapter.adapter_id,
|
|
"custom_node_original_identity": (
|
|
reference.extra_value("custom_node_original_identity")
|
|
or reference.extra_value("original_lora_name")
|
|
or reference.extra_value("name")
|
|
or reference.original_path
|
|
),
|
|
}
|
|
legacy_v2_flag = reference.extra_value("is_lora_v2")
|
|
if legacy_v2_flag is not None:
|
|
metadata["is_lora_v2"] = legacy_v2_flag
|
|
legacy_flag = reference.extra_value("is_legacy_lora_manager")
|
|
if legacy_flag is not None:
|
|
metadata["is_legacy_lora_manager"] = legacy_flag
|
|
return CustomNodeMetadata.from_mapping(metadata)
|
|
|
|
|
|
def analyze_custom_node_references(
|
|
node: Dict[str, Any],
|
|
available_models: Optional[List[ResolvedModel]] = None,
|
|
*,
|
|
is_active: bool,
|
|
get_widget_name_hint: Callable[[Dict[str, Any], int], str],
|
|
) -> Optional[List[ModelReference]]:
|
|
"""Dispatch custom serialized model extraction when an adapter handles it."""
|
|
adapter = get_custom_node_model_adapter(node)
|
|
if not adapter or not adapter.analyze_references:
|
|
return None
|
|
raw_references = adapter.analyze_references(
|
|
node,
|
|
available_models,
|
|
is_active=is_active,
|
|
get_widget_name_hint=get_widget_name_hint,
|
|
)
|
|
if raw_references is None:
|
|
return None
|
|
return [
|
|
reference
|
|
if isinstance(reference, ModelReference)
|
|
else ModelReference.from_mapping(reference)
|
|
for reference in raw_references
|
|
]
|
|
|
|
|
|
def custom_node_has_potential_model_reference(
|
|
node: Dict[str, Any],
|
|
) -> bool:
|
|
"""Return whether a registered adapter finds a model reference."""
|
|
adapter = get_custom_node_model_adapter(node)
|
|
return bool(
|
|
adapter
|
|
and adapter.has_potential_reference
|
|
and adapter.has_potential_reference(node)
|
|
)
|
|
|
|
|
|
def update_custom_node_model_path(
|
|
node: Dict[str, Any],
|
|
widget_index: int,
|
|
resolved_model: Optional[ResolvedModel],
|
|
metadata: CustomNodeMetadata,
|
|
) -> Optional[bool]:
|
|
"""Dispatch a custom workflow update, returning None when not handled."""
|
|
adapter = get_custom_node_model_adapter(node)
|
|
if not adapter or not adapter.update_model_path:
|
|
return None
|
|
return adapter.update_model_path(
|
|
node,
|
|
widget_index,
|
|
resolved_model,
|
|
metadata,
|
|
)
|
|
|
|
|
|
def should_skip_existing_custom_node_reference(
|
|
reference: ModelReference,
|
|
) -> bool:
|
|
"""Return whether an existing adapter reference needs no resolution."""
|
|
adapter = get_custom_node_adapter_for_reference(reference)
|
|
return bool(
|
|
adapter
|
|
and adapter.should_skip_existing
|
|
and adapter.should_skip_existing(reference)
|
|
)
|
|
|
|
|
|
def adapt_custom_node_loaded_model(
|
|
reference: ModelReference,
|
|
model_name: str,
|
|
strength: Any,
|
|
) -> tuple[str, Any]:
|
|
"""Apply adapter-specific display data for Loaded Models."""
|
|
adapter = get_custom_node_adapter_for_reference(reference)
|
|
if not adapter or not adapter.adapt_loaded_model:
|
|
return model_name, strength
|
|
return adapter.adapt_loaded_model(reference, model_name, strength)
|