feat(regional): build universal adapter execution foundation
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Select decayed reduced-global authority from denoising timesteps."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Define model-neutral regional activation geometry and batch alignment."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
from .spatial_views import SpatialBatchLayout
|
||||
|
||||
|
||||
class RegionalActivationLayout(StrEnum):
|
||||
"""Classify the explicit spatial organization of one adapter activation."""
|
||||
|
||||
DIRECT_CONVOLUTION_1D = "direct_convolution_1d"
|
||||
DIRECT_CONVOLUTION_2D = "direct_convolution_2d"
|
||||
DIRECT_CONVOLUTION_3D = "direct_convolution_3d"
|
||||
FLATTENED_SPATIAL_TOKENS = "flattened_spatial_tokens"
|
||||
CONSUMER_SPATIALIZED = "consumer_spatialized"
|
||||
|
||||
|
||||
class RegionalTemporalOwnership(StrEnum):
|
||||
"""Declare how a two-dimensional authored mask owns temporal activations."""
|
||||
|
||||
NONE = "none"
|
||||
REPEAT_SPATIAL_MASK = "repeat_spatial_mask"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalActivationBatchAlignment:
|
||||
"""Retain CFG, latent-batch, and optional view-major alignment evidence."""
|
||||
|
||||
latent_batch_size: int
|
||||
chunk_count: int
|
||||
spatial_layout: SpatialBatchLayout | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require positive counts and one layout over the complete base batch."""
|
||||
|
||||
_positive_integer(self.latent_batch_size, name="latent_batch_size")
|
||||
_positive_integer(self.chunk_count, name="chunk_count")
|
||||
if self.spatial_layout is not None:
|
||||
if not isinstance(self.spatial_layout, SpatialBatchLayout):
|
||||
raise TypeError(
|
||||
"Regional activation spatial_layout must be a SpatialBatchLayout."
|
||||
)
|
||||
if self.spatial_layout.input_batch_size != self.base_batch_size:
|
||||
raise ValueError(
|
||||
"Regional activation spatial layout input batch must match "
|
||||
"CFG chunks times latent batch size."
|
||||
)
|
||||
|
||||
@property
|
||||
def base_batch_size(self) -> int:
|
||||
"""Return the model batch before spatial-view expansion."""
|
||||
|
||||
return self.latent_batch_size * self.chunk_count
|
||||
|
||||
@property
|
||||
def invocation_batch_size(self) -> int:
|
||||
"""Return the active model batch after optional view expansion."""
|
||||
|
||||
if self.spatial_layout is None:
|
||||
return self.base_batch_size
|
||||
return self.spatial_layout.expanded_batch_size
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalActivationGeometry:
|
||||
"""Describe one exact rank activation and its authored-mask correspondence."""
|
||||
|
||||
layout: RegionalActivationLayout
|
||||
invocation_shape: tuple[int, ...]
|
||||
feature_axis: int
|
||||
spatial_height: int
|
||||
spatial_width: int
|
||||
batch_alignment: RegionalActivationBatchAlignment
|
||||
temporal_axis: int | None = None
|
||||
temporal_ownership: RegionalTemporalOwnership = RegionalTemporalOwnership.NONE
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Reject ambiguous axes, batches, tokens, and temporal ownership."""
|
||||
|
||||
if not isinstance(self.layout, RegionalActivationLayout):
|
||||
raise TypeError("Regional activation layout has an invalid type.")
|
||||
if not isinstance(self.invocation_shape, tuple) or not self.invocation_shape:
|
||||
raise ValueError("Regional activation shape must be a nonempty tuple.")
|
||||
for dimension in self.invocation_shape:
|
||||
_positive_integer(dimension, name="shape dimension")
|
||||
if not isinstance(self.batch_alignment, RegionalActivationBatchAlignment):
|
||||
raise TypeError("Regional activation requires batch alignment evidence.")
|
||||
_positive_integer(self.spatial_height, name="spatial_height")
|
||||
_positive_integer(self.spatial_width, name="spatial_width")
|
||||
if self.invocation_shape[0] != self.batch_alignment.invocation_batch_size:
|
||||
raise ValueError(
|
||||
"Regional activation leading batch must match its alignment."
|
||||
)
|
||||
spatial_layout = self.batch_alignment.spatial_layout
|
||||
if spatial_layout is not None:
|
||||
mismatched_views = tuple(
|
||||
index
|
||||
for index, view in enumerate(spatial_layout.views)
|
||||
if (view.model_height, view.model_width)
|
||||
!= (self.spatial_height, self.spatial_width)
|
||||
)
|
||||
if mismatched_views:
|
||||
raise ValueError(
|
||||
"Regional activation H/W must match every published spatial "
|
||||
f"view; mismatched view indices {mismatched_views}."
|
||||
)
|
||||
rank = len(self.invocation_shape)
|
||||
_axis(self.feature_axis, rank=rank, name="feature_axis")
|
||||
if self.feature_axis == 0:
|
||||
raise ValueError(
|
||||
"Regional activation feature axis cannot be the batch axis."
|
||||
)
|
||||
if not isinstance(self.temporal_ownership, RegionalTemporalOwnership):
|
||||
raise TypeError(
|
||||
"Regional activation temporal ownership has an invalid type."
|
||||
)
|
||||
self._validate_layout()
|
||||
|
||||
def _validate_layout(self) -> None:
|
||||
"""Match the declared layout to its exact conventional tensor shape."""
|
||||
|
||||
batch = self.batch_alignment.invocation_batch_size
|
||||
features = self.invocation_shape[self.feature_axis]
|
||||
if self.layout is RegionalActivationLayout.DIRECT_CONVOLUTION_1D:
|
||||
self._require_shape((batch, features, self.spatial_width), feature_axis=1)
|
||||
if self.spatial_height != 1:
|
||||
raise ValueError("Direct Conv1d regional geometry requires height one.")
|
||||
self._require_no_temporal_axis()
|
||||
return
|
||||
if self.layout is RegionalActivationLayout.DIRECT_CONVOLUTION_2D:
|
||||
self._require_shape(
|
||||
(batch, features, self.spatial_height, self.spatial_width),
|
||||
feature_axis=1,
|
||||
)
|
||||
self._require_no_temporal_axis()
|
||||
return
|
||||
if self.layout is RegionalActivationLayout.DIRECT_CONVOLUTION_3D:
|
||||
if self.feature_axis != 1 or len(self.invocation_shape) != 5:
|
||||
raise ValueError("Direct Conv3d regional geometry requires B/C/D/H/W.")
|
||||
if self.temporal_axis != 2:
|
||||
raise ValueError(
|
||||
"Direct Conv3d regional geometry requires temporal axis 2."
|
||||
)
|
||||
if self.invocation_shape[3:] != (
|
||||
self.spatial_height,
|
||||
self.spatial_width,
|
||||
):
|
||||
raise ValueError("Direct Conv3d regional H/W must match its tensor.")
|
||||
if (
|
||||
self.temporal_ownership
|
||||
is not RegionalTemporalOwnership.REPEAT_SPATIAL_MASK
|
||||
):
|
||||
raise ValueError(
|
||||
"Direct Conv3d regional geometry requires explicit repeated "
|
||||
"spatial-mask temporal ownership."
|
||||
)
|
||||
return
|
||||
if self.layout in (
|
||||
RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS,
|
||||
RegionalActivationLayout.CONSUMER_SPATIALIZED,
|
||||
):
|
||||
self._require_shape(
|
||||
(
|
||||
batch,
|
||||
self.spatial_height * self.spatial_width,
|
||||
features,
|
||||
),
|
||||
feature_axis=2,
|
||||
)
|
||||
self._require_no_temporal_axis()
|
||||
return
|
||||
raise AssertionError(f"Unhandled regional activation layout: {self.layout}")
|
||||
|
||||
def _require_shape(
|
||||
self,
|
||||
expected: tuple[int, ...],
|
||||
*,
|
||||
feature_axis: int,
|
||||
) -> None:
|
||||
"""Require one conventional shape and feature-axis location."""
|
||||
|
||||
if self.feature_axis != feature_axis or self.invocation_shape != expected:
|
||||
raise ValueError(
|
||||
f"{self.layout.value} regional geometry expected shape {expected} "
|
||||
f"with feature axis {feature_axis}; observed "
|
||||
f"{self.invocation_shape} and axis {self.feature_axis}."
|
||||
)
|
||||
|
||||
def _require_no_temporal_axis(self) -> None:
|
||||
"""Reject temporal claims from non-temporal image activation layouts."""
|
||||
|
||||
if self.temporal_axis is not None:
|
||||
raise ValueError(
|
||||
"Non-temporal regional geometry cannot declare a temporal axis."
|
||||
)
|
||||
if self.temporal_ownership is not RegionalTemporalOwnership.NONE:
|
||||
raise ValueError(
|
||||
"Non-temporal regional geometry cannot claim temporal ownership."
|
||||
)
|
||||
|
||||
@property
|
||||
def temporal_size(self) -> int | None:
|
||||
"""Return the explicit temporal size when this activation owns one."""
|
||||
|
||||
if self.temporal_axis is None:
|
||||
return None
|
||||
return self.invocation_shape[self.temporal_axis]
|
||||
|
||||
def broadcast_mask_shape(self, region_count: int) -> tuple[int, ...]:
|
||||
"""Return the exact region-major multiplier shape for this activation."""
|
||||
|
||||
_positive_integer(region_count, name="region_count")
|
||||
shape = list(self.invocation_shape)
|
||||
shape[self.feature_axis] = 1
|
||||
return (region_count, *shape)
|
||||
|
||||
|
||||
def _positive_integer(value: object, *, name: str) -> None:
|
||||
"""Require one strictly positive non-boolean integer."""
|
||||
|
||||
if isinstance(value, bool) or not isinstance(value, int):
|
||||
raise TypeError(f"Regional activation {name} must be an integer.")
|
||||
if value < 1:
|
||||
raise ValueError(f"Regional activation {name} must be positive.")
|
||||
|
||||
|
||||
def _axis(value: object, *, rank: int, name: str) -> None:
|
||||
"""Require one non-negative axis inside the invocation rank."""
|
||||
|
||||
if isinstance(value, bool) or not isinstance(value, int):
|
||||
raise TypeError(f"Regional activation {name} must be an integer.")
|
||||
if not 0 <= value < rank:
|
||||
raise ValueError(f"Regional activation {name} is outside the tensor rank.")
|
||||
@@ -0,0 +1,347 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Define model-neutral resolved regional ordinary-LoRA operation contracts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
from .regional_lora_plan import RegionalLoraAdapterPlan
|
||||
|
||||
|
||||
class ResolvedLoraOperationClass(StrEnum):
|
||||
"""Classify normalized low-rank tensor organization before module binding."""
|
||||
|
||||
MATRIX_PAIR = "matrix_pair"
|
||||
CONVOLUTION_1D = "convolution_1d"
|
||||
CONVOLUTION_2D = "convolution_2d"
|
||||
CONVOLUTION_3D = "convolution_3d"
|
||||
UNSUPPORTED = "unsupported"
|
||||
|
||||
|
||||
class RegionalLoraGeometryClass(StrEnum):
|
||||
"""Declare the activation geometry an execution contract requires."""
|
||||
|
||||
TARGET_OPERATION = "target_operation"
|
||||
DIRECT_CONVOLUTION_1D = "direct_convolution_1d"
|
||||
DIRECT_CONVOLUTION_2D = "direct_convolution_2d"
|
||||
DIRECT_CONVOLUTION_3D = "direct_convolution_3d"
|
||||
UNSUPPORTED = "unsupported"
|
||||
|
||||
|
||||
class RegionalLoraExecutionContract(StrEnum):
|
||||
"""Declare exact rank-activation execution or explicit rejection."""
|
||||
|
||||
MATRIX_OR_RESHAPED_CONVOLUTION = "matrix_or_reshaped_convolution"
|
||||
DIRECT_CONVOLUTION = "direct_convolution"
|
||||
UNSUPPORTED = "unsupported"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalLoraTensorShape:
|
||||
"""Retain one immutable positive tensor shape without tensor ownership."""
|
||||
|
||||
dimensions: tuple[int, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require a nonempty tuple of strictly positive integer dimensions."""
|
||||
|
||||
if not isinstance(self.dimensions, tuple) or not self.dimensions:
|
||||
raise ValueError("Regional LoRA tensor shape must be a nonempty tuple.")
|
||||
if any(
|
||||
isinstance(dimension, bool)
|
||||
or not isinstance(dimension, int)
|
||||
or dimension <= 0
|
||||
for dimension in self.dimensions
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional LoRA tensor shape dimensions must be positive integers."
|
||||
)
|
||||
|
||||
@property
|
||||
def rank(self) -> int:
|
||||
"""Return the tensor dimensionality."""
|
||||
|
||||
return len(self.dimensions)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ResolvedRegionalLoraTarget:
|
||||
"""Retain one model-relative parameter path and optional Comfy tensor slice."""
|
||||
|
||||
model_target: str
|
||||
parameter_name: str
|
||||
offset: tuple[int, ...] | None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require an explicit module target, parameter, and valid optional slice."""
|
||||
|
||||
if not isinstance(self.model_target, str) or not self.model_target.strip():
|
||||
raise ValueError("Resolved regional LoRA model target must be nonempty.")
|
||||
if not isinstance(self.parameter_name, str) or not self.parameter_name.strip():
|
||||
raise ValueError("Resolved regional LoRA parameter name must be nonempty.")
|
||||
if self.offset is not None and (
|
||||
not isinstance(self.offset, tuple)
|
||||
or not self.offset
|
||||
or any(
|
||||
isinstance(part, bool) or not isinstance(part, int) or part < 0
|
||||
for part in self.offset
|
||||
)
|
||||
):
|
||||
raise ValueError(
|
||||
"Resolved regional LoRA target offset must contain "
|
||||
"non-negative integers."
|
||||
)
|
||||
|
||||
@property
|
||||
def parameter_path(self) -> str:
|
||||
"""Return the complete model-relative parameter path."""
|
||||
|
||||
return f"{self.model_target}.{self.parameter_name}"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ResolvedRegionalLoraOperation:
|
||||
"""Describe one supported operation or one explicit target rejection."""
|
||||
|
||||
adapter: RegionalLoraAdapterPlan
|
||||
target_index: int
|
||||
target: ResolvedRegionalLoraTarget
|
||||
normalized_operation_type: str
|
||||
operation_class: ResolvedLoraOperationClass
|
||||
down_shape: RegionalLoraTensorShape | None
|
||||
up_shape: RegionalLoraTensorShape | None
|
||||
middle_shape: RegionalLoraTensorShape | None
|
||||
reshape_shape: RegionalLoraTensorShape | None
|
||||
rank: int | None
|
||||
intrinsic_scale: float | None
|
||||
required_geometry: RegionalLoraGeometryClass
|
||||
execution_contract: RegionalLoraExecutionContract
|
||||
rejection_reason: str | None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Reject mutable, incomplete, or mathematically inconsistent metadata."""
|
||||
|
||||
if not isinstance(self.adapter, RegionalLoraAdapterPlan):
|
||||
raise TypeError("Resolved regional LoRA operation requires an adapter.")
|
||||
_require_non_negative_index(self.target_index, name="target_index")
|
||||
if not isinstance(self.target, ResolvedRegionalLoraTarget):
|
||||
raise TypeError("Resolved regional LoRA operation requires a target.")
|
||||
if (
|
||||
not isinstance(self.normalized_operation_type, str)
|
||||
or not self.normalized_operation_type.strip()
|
||||
):
|
||||
raise ValueError(
|
||||
"Normalized regional LoRA operation type must be nonempty."
|
||||
)
|
||||
if not isinstance(self.operation_class, ResolvedLoraOperationClass):
|
||||
raise TypeError("Resolved regional LoRA operation class is invalid.")
|
||||
if not isinstance(self.required_geometry, RegionalLoraGeometryClass):
|
||||
raise TypeError("Resolved regional LoRA geometry class is invalid.")
|
||||
if not isinstance(self.execution_contract, RegionalLoraExecutionContract):
|
||||
raise TypeError("Resolved regional LoRA execution contract is invalid.")
|
||||
if self.execution_contract is RegionalLoraExecutionContract.UNSUPPORTED:
|
||||
self._validate_rejection()
|
||||
return
|
||||
self._validate_supported()
|
||||
|
||||
def _validate_rejection(self) -> None:
|
||||
"""Require one reason and no misleading supported-operation metadata."""
|
||||
|
||||
if self.operation_class is not ResolvedLoraOperationClass.UNSUPPORTED:
|
||||
raise ValueError(
|
||||
"Rejected regional LoRA operation class must be unsupported."
|
||||
)
|
||||
if self.required_geometry is not RegionalLoraGeometryClass.UNSUPPORTED:
|
||||
raise ValueError("Rejected regional LoRA geometry must be unsupported.")
|
||||
if (
|
||||
not isinstance(self.rejection_reason, str)
|
||||
or not self.rejection_reason.strip()
|
||||
):
|
||||
raise ValueError("Rejected regional LoRA operation requires a reason.")
|
||||
if any(
|
||||
value is not None
|
||||
for value in (
|
||||
self.down_shape,
|
||||
self.up_shape,
|
||||
self.middle_shape,
|
||||
self.reshape_shape,
|
||||
self.rank,
|
||||
self.intrinsic_scale,
|
||||
)
|
||||
):
|
||||
raise ValueError(
|
||||
"Rejected regional LoRA operation cannot claim executable metadata."
|
||||
)
|
||||
|
||||
def _validate_supported(self) -> None:
|
||||
"""Require complete shape, scale, geometry, and execution consistency."""
|
||||
|
||||
if self.operation_class is ResolvedLoraOperationClass.UNSUPPORTED:
|
||||
raise ValueError("Supported regional LoRA operation cannot be unsupported.")
|
||||
if self.required_geometry is RegionalLoraGeometryClass.UNSUPPORTED:
|
||||
raise ValueError("Supported regional LoRA geometry cannot be unsupported.")
|
||||
if self.rejection_reason is not None:
|
||||
raise ValueError(
|
||||
"Supported regional LoRA operation cannot have a rejection."
|
||||
)
|
||||
if self.down_shape is None or self.up_shape is None:
|
||||
raise ValueError(
|
||||
"Supported regional LoRA operation requires down/up shapes."
|
||||
)
|
||||
if self.down_shape.rank < 2 or self.up_shape.rank < 2:
|
||||
raise ValueError(
|
||||
"Regional LoRA down/up shapes need at least two dimensions."
|
||||
)
|
||||
if (
|
||||
isinstance(self.rank, bool)
|
||||
or not isinstance(self.rank, int)
|
||||
or self.rank <= 0
|
||||
):
|
||||
raise ValueError("Supported regional LoRA rank must be a positive integer.")
|
||||
if not isinstance(self.intrinsic_scale, float) or not math.isfinite(
|
||||
self.intrinsic_scale
|
||||
):
|
||||
raise ValueError("Regional LoRA intrinsic scale must be finite.")
|
||||
if self.down_shape.dimensions[0] != self.rank:
|
||||
raise ValueError("Regional LoRA down shape must begin with its rank.")
|
||||
if (
|
||||
len(self.up_shape.dimensions) < 2
|
||||
or self.up_shape.dimensions[1] != self.rank
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional LoRA up shape must contain its rank at index one."
|
||||
)
|
||||
if self.middle_shape is not None:
|
||||
if self.middle_shape.rank < 2:
|
||||
raise ValueError(
|
||||
"Regional LoRA middle shape needs at least two dimensions."
|
||||
)
|
||||
if (
|
||||
self.middle_shape.dimensions[0] != self.rank
|
||||
or self.middle_shape.dimensions[1] != self.rank
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional LoRA middle shape must preserve rank channels."
|
||||
)
|
||||
self._validate_operation_shape_contract()
|
||||
|
||||
def _validate_operation_shape_contract(self) -> None:
|
||||
"""Match operation, tensor dimensionality, geometry, and execution policy."""
|
||||
|
||||
if self.operation_class is ResolvedLoraOperationClass.MATRIX_PAIR:
|
||||
if self.down_shape is None or self.up_shape is None:
|
||||
raise AssertionError(
|
||||
"Supported shape validation requires down/up shapes."
|
||||
)
|
||||
if self.down_shape.rank != 2 or self.up_shape.rank != 2:
|
||||
raise ValueError("Matrix-pair regional LoRA tensors must be rank two.")
|
||||
if self.middle_shape is not None:
|
||||
raise ValueError(
|
||||
"Matrix-pair regional LoRA cannot contain middle weights."
|
||||
)
|
||||
if self.required_geometry is not RegionalLoraGeometryClass.TARGET_OPERATION:
|
||||
raise ValueError("Matrix-pair regional LoRA requires target geometry.")
|
||||
if (
|
||||
self.execution_contract
|
||||
is not RegionalLoraExecutionContract.MATRIX_OR_RESHAPED_CONVOLUTION
|
||||
):
|
||||
raise ValueError("Matrix-pair regional LoRA execution is inconsistent.")
|
||||
return
|
||||
spatial_rank = {
|
||||
ResolvedLoraOperationClass.CONVOLUTION_1D: 3,
|
||||
ResolvedLoraOperationClass.CONVOLUTION_2D: 4,
|
||||
ResolvedLoraOperationClass.CONVOLUTION_3D: 5,
|
||||
}[self.operation_class]
|
||||
expected_geometry = {
|
||||
3: RegionalLoraGeometryClass.DIRECT_CONVOLUTION_1D,
|
||||
4: RegionalLoraGeometryClass.DIRECT_CONVOLUTION_2D,
|
||||
5: RegionalLoraGeometryClass.DIRECT_CONVOLUTION_3D,
|
||||
}[spatial_rank]
|
||||
shapes = tuple(
|
||||
shape
|
||||
for shape in (self.down_shape, self.up_shape, self.middle_shape)
|
||||
if shape is not None
|
||||
)
|
||||
if any(shape.rank not in (2, spatial_rank) for shape in shapes):
|
||||
raise ValueError("Convolutional regional LoRA tensor rank is inconsistent.")
|
||||
if not any(shape.rank == spatial_rank for shape in shapes):
|
||||
raise ValueError("Convolutional regional LoRA needs one spatial tensor.")
|
||||
if self.required_geometry is not expected_geometry:
|
||||
raise ValueError("Convolutional regional LoRA geometry is inconsistent.")
|
||||
if (
|
||||
self.execution_contract
|
||||
is not RegionalLoraExecutionContract.DIRECT_CONVOLUTION
|
||||
):
|
||||
raise ValueError("Convolutional regional LoRA execution is inconsistent.")
|
||||
|
||||
@property
|
||||
def authored_strength(self) -> float:
|
||||
"""Return the authored model strength from the authoritative plan owner."""
|
||||
|
||||
return self.adapter.model_strength
|
||||
|
||||
@property
|
||||
def supported(self) -> bool:
|
||||
"""Report whether this target has an executable ordinary-LoRA contract."""
|
||||
|
||||
return self.execution_contract is not RegionalLoraExecutionContract.UNSUPPORTED
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ResolvedRegionalLoraOperationSet:
|
||||
"""Retain canonical adapter/target order across supported and rejected entries."""
|
||||
|
||||
entries: tuple[ResolvedRegionalLoraOperation, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require immutable entries in contiguous per-adapter target order."""
|
||||
|
||||
if not isinstance(self.entries, tuple):
|
||||
raise TypeError("Resolved regional LoRA entries must be a tuple.")
|
||||
if any(
|
||||
not isinstance(entry, ResolvedRegionalLoraOperation)
|
||||
for entry in self.entries
|
||||
):
|
||||
raise TypeError("Resolved regional LoRA set contains an invalid entry.")
|
||||
observed = tuple(
|
||||
(entry.adapter.composition_index, entry.target_index)
|
||||
for entry in self.entries
|
||||
)
|
||||
if observed != tuple(sorted(observed)):
|
||||
raise ValueError(
|
||||
"Resolved regional LoRA entries must retain declared order."
|
||||
)
|
||||
indices_by_adapter: dict[int, list[int]] = {}
|
||||
for composition_index, target_index in observed:
|
||||
indices_by_adapter.setdefault(composition_index, []).append(target_index)
|
||||
if any(
|
||||
target_indices != list(range(len(target_indices)))
|
||||
for target_indices in indices_by_adapter.values()
|
||||
):
|
||||
raise ValueError(
|
||||
"Resolved regional LoRA target indices must be contiguous per adapter."
|
||||
)
|
||||
|
||||
@property
|
||||
def supported(self) -> tuple[ResolvedRegionalLoraOperation, ...]:
|
||||
"""Return supported entries without changing canonical relative order."""
|
||||
|
||||
return tuple(entry for entry in self.entries if entry.supported)
|
||||
|
||||
@property
|
||||
def rejected(self) -> tuple[ResolvedRegionalLoraOperation, ...]:
|
||||
"""Return rejected entries without changing canonical relative order."""
|
||||
|
||||
return tuple(entry for entry in self.entries if not entry.supported)
|
||||
|
||||
|
||||
def _require_non_negative_index(value: object, *, name: str) -> None:
|
||||
"""Require one non-negative non-boolean integer index."""
|
||||
|
||||
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
||||
raise ValueError(f"Resolved regional LoRA {name} must be non-negative.")
|
||||
@@ -0,0 +1,194 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Project canonical masks into exact regional activation multiplier shapes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
|
||||
from ..domain.regional_activation_geometry import (
|
||||
RegionalActivationGeometry,
|
||||
RegionalActivationLayout,
|
||||
)
|
||||
from ..domain.regional_mask_bank import RegionalMaskBank
|
||||
from ..domain.spatial_views import (
|
||||
SpatialBatchLayout,
|
||||
SpatialView,
|
||||
SpatialViewKind,
|
||||
)
|
||||
from .regional_mask_projection import (
|
||||
RegionalMaskForm,
|
||||
RegionalMaskProjectionMode,
|
||||
RegionalMaskProjector,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalActivationMaskBatch:
|
||||
"""Retain one finite region-major multiplier broadcast over rank activations."""
|
||||
|
||||
multipliers: torch.Tensor
|
||||
geometry: RegionalActivationGeometry
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require exact geometry shape and bounded finite floating multipliers."""
|
||||
|
||||
if not isinstance(self.multipliers, torch.Tensor):
|
||||
raise TypeError("Regional activation multipliers must be a tensor.")
|
||||
if not isinstance(self.geometry, RegionalActivationGeometry):
|
||||
raise TypeError("Regional activation multipliers require geometry.")
|
||||
if not self.multipliers.is_floating_point():
|
||||
raise TypeError("Regional activation multipliers must use floating point.")
|
||||
if self.multipliers.ndim != len(self.geometry.invocation_shape) + 1:
|
||||
raise ValueError("Regional activation multiplier rank is inconsistent.")
|
||||
expected = self.geometry.broadcast_mask_shape(int(self.multipliers.shape[0]))
|
||||
if tuple(self.multipliers.shape) != expected:
|
||||
raise ValueError(
|
||||
"Regional activation multiplier shape must match its geometry."
|
||||
)
|
||||
if not bool(torch.isfinite(self.multipliers).all()):
|
||||
raise ValueError("Regional activation multipliers must be finite.")
|
||||
if not bool(((self.multipliers >= 0.0) & (self.multipliers <= 1.0)).all()):
|
||||
raise ValueError("Regional activation multipliers must stay within [0, 1].")
|
||||
|
||||
|
||||
class RegionalActivationMaskProjector:
|
||||
"""Own geometry-shaped mask projection around existing crop/interpolation."""
|
||||
|
||||
def __init__(self, projector: RegionalMaskProjector | None = None) -> None:
|
||||
"""Retain the canonical mask crop and interpolation authority."""
|
||||
|
||||
self._projector = projector or RegionalMaskProjector()
|
||||
|
||||
def project(
|
||||
self,
|
||||
*,
|
||||
bank: RegionalMaskBank,
|
||||
geometry: RegionalActivationGeometry,
|
||||
form: RegionalMaskForm,
|
||||
mode: RegionalMaskProjectionMode,
|
||||
device: torch.device,
|
||||
dtype: torch.dtype,
|
||||
) -> RegionalActivationMaskBatch:
|
||||
"""Return masks in region/view/chunk/latent activation order."""
|
||||
|
||||
_validate_inputs(bank, geometry, form, mode, device, dtype)
|
||||
layout = geometry.batch_alignment.spatial_layout or _full_layout(
|
||||
bank,
|
||||
input_batch_size=geometry.batch_alignment.base_batch_size,
|
||||
)
|
||||
projected_views = tuple(
|
||||
self._projector.project_query_grid(
|
||||
bank=bank,
|
||||
layout=layout,
|
||||
view_index=view_index,
|
||||
query_height=geometry.spatial_height,
|
||||
query_width=geometry.spatial_width,
|
||||
form=form,
|
||||
mode=mode,
|
||||
)
|
||||
for view_index in range(layout.view_count)
|
||||
)
|
||||
masks = torch.cat(
|
||||
tuple(
|
||||
projected.unsqueeze(1).expand(
|
||||
-1,
|
||||
layout.input_batch_size,
|
||||
-1,
|
||||
-1,
|
||||
)
|
||||
for projected in projected_views
|
||||
),
|
||||
dim=1,
|
||||
).to(device=device, dtype=dtype)
|
||||
multipliers = _reshape_for_activation(masks, geometry)
|
||||
return RegionalActivationMaskBatch(multipliers, geometry)
|
||||
|
||||
|
||||
def _reshape_for_activation(
|
||||
masks: torch.Tensor,
|
||||
geometry: RegionalActivationGeometry,
|
||||
) -> torch.Tensor:
|
||||
"""Place projected H/W masks on the declared operation's non-feature axes."""
|
||||
|
||||
regions, batch, height, width = (int(value) for value in masks.shape)
|
||||
if geometry.layout is RegionalActivationLayout.DIRECT_CONVOLUTION_1D:
|
||||
if height != 1:
|
||||
raise ValueError("Conv1d regional masks require projected height one.")
|
||||
return masks.reshape(regions, batch, 1, width)
|
||||
if geometry.layout is RegionalActivationLayout.DIRECT_CONVOLUTION_2D:
|
||||
return masks.reshape(regions, batch, 1, height, width)
|
||||
if geometry.layout is RegionalActivationLayout.DIRECT_CONVOLUTION_3D:
|
||||
temporal_size = geometry.temporal_size
|
||||
if temporal_size is None:
|
||||
raise ValueError("Conv3d regional masks require explicit temporal size.")
|
||||
return masks.reshape(regions, batch, 1, 1, height, width).expand(
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
temporal_size,
|
||||
-1,
|
||||
-1,
|
||||
)
|
||||
if geometry.layout in (
|
||||
RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS,
|
||||
RegionalActivationLayout.CONSUMER_SPATIALIZED,
|
||||
):
|
||||
return masks.flatten(start_dim=2).unsqueeze(-1)
|
||||
raise AssertionError(f"Unhandled regional activation layout: {geometry.layout}")
|
||||
|
||||
|
||||
def _full_layout(
|
||||
bank: RegionalMaskBank,
|
||||
*,
|
||||
input_batch_size: int,
|
||||
) -> SpatialBatchLayout:
|
||||
"""Represent one full-canvas invocation through the shared layout contract."""
|
||||
|
||||
return SpatialBatchLayout(
|
||||
bank.canvas_width,
|
||||
bank.canvas_height,
|
||||
(
|
||||
SpatialView(
|
||||
SpatialViewKind.FULL,
|
||||
0,
|
||||
0,
|
||||
bank.canvas_width,
|
||||
bank.canvas_height,
|
||||
bank.canvas_width,
|
||||
bank.canvas_height,
|
||||
),
|
||||
),
|
||||
input_batch_size,
|
||||
)
|
||||
|
||||
|
||||
def _validate_inputs(
|
||||
bank: object,
|
||||
geometry: object,
|
||||
form: object,
|
||||
mode: object,
|
||||
device: object,
|
||||
dtype: object,
|
||||
) -> None:
|
||||
"""Validate typed projection inputs without moving or mutating source masks."""
|
||||
|
||||
if not isinstance(bank, RegionalMaskBank):
|
||||
raise TypeError("Regional activation projection requires a mask bank.")
|
||||
if not isinstance(geometry, RegionalActivationGeometry):
|
||||
raise TypeError("Regional activation projection requires geometry.")
|
||||
if not isinstance(form, RegionalMaskForm):
|
||||
raise TypeError("Regional activation mask form has an invalid type.")
|
||||
if not isinstance(mode, RegionalMaskProjectionMode):
|
||||
raise TypeError("Regional activation projection mode has an invalid type.")
|
||||
if not isinstance(device, torch.device):
|
||||
raise TypeError("Regional activation mask device must be a torch.device.")
|
||||
if not isinstance(dtype, torch.dtype) or not dtype.is_floating_point:
|
||||
raise TypeError("Regional activation mask dtype must be floating point.")
|
||||
|
||||
|
||||
REGIONAL_ACTIVATION_MASK_PROJECTOR = RegionalActivationMaskProjector()
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Fuse tiled predictions with scheduled reduced-global scene authority."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Install one preflighted batch of exact clone-local model object patches."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from inspect import Parameter, signature
|
||||
from typing import cast
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ExactModelObjectReplacement:
|
||||
"""Describe one exact expected object and its clone-local replacement."""
|
||||
|
||||
path: str
|
||||
expected_object: object
|
||||
replacement: object
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require one canonical nonempty dotted model path."""
|
||||
|
||||
if (
|
||||
not isinstance(self.path, str)
|
||||
or not self.path
|
||||
or any(not segment for segment in self.path.split("."))
|
||||
):
|
||||
raise ValueError("MODEL object replacement requires a dotted path.")
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ModelObjectPatchBatchMutation:
|
||||
"""Preflight every exact replacement before changing pending patch state."""
|
||||
|
||||
replacements: tuple[ExactModelObjectReplacement, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Reject empty, invalid, duplicate, or hierarchically overlapping paths."""
|
||||
|
||||
if not isinstance(self.replacements, tuple) or not self.replacements:
|
||||
raise ValueError("MODEL object patch batch cannot be empty.")
|
||||
if any(
|
||||
not isinstance(replacement, ExactModelObjectReplacement)
|
||||
for replacement in self.replacements
|
||||
):
|
||||
raise TypeError("MODEL object patch batch contains an invalid value.")
|
||||
paths = tuple(replacement.path for replacement in self.replacements)
|
||||
if len(paths) != len(set(paths)):
|
||||
raise ValueError("MODEL object patch batch paths must be unique.")
|
||||
overlaps = _overlapping_paths(paths)
|
||||
if overlaps:
|
||||
raise ValueError(
|
||||
f"MODEL object patch batch paths cannot overlap: {overlaps!r}."
|
||||
)
|
||||
|
||||
def apply(self, model: object) -> None:
|
||||
"""Validate the complete clone surface, then install the entire batch."""
|
||||
|
||||
getter = _required_method(model, "get_model_object", ("name",))
|
||||
adder = _required_method(model, "add_object_patch", ("name", "obj"))
|
||||
object_patches = _required_dictionary(model, "object_patches")
|
||||
backups = _required_dictionary(model, "object_patches_backup")
|
||||
paths = tuple(replacement.path for replacement in self.replacements)
|
||||
exact_collisions = tuple(
|
||||
path for path in paths if path in object_patches or path in backups
|
||||
)
|
||||
if exact_collisions:
|
||||
raise ValueError(
|
||||
"MODEL object patch batch collides with existing patch state: "
|
||||
f"{exact_collisions!r}."
|
||||
)
|
||||
existing_paths = tuple(
|
||||
path for path in (*object_patches, *backups) if isinstance(path, str)
|
||||
)
|
||||
overlaps = _overlapping_paths((*paths, *existing_paths), cross_only=paths)
|
||||
if overlaps:
|
||||
raise ValueError(
|
||||
"MODEL object patch batch collides with existing patch state: "
|
||||
f"{overlaps!r}."
|
||||
)
|
||||
for replacement in self.replacements:
|
||||
current = getter(replacement.path)
|
||||
if current is not replacement.expected_object:
|
||||
raise ValueError(
|
||||
f"MODEL object path '{replacement.path}' does not match its "
|
||||
"expected object."
|
||||
)
|
||||
original_patches = object_patches.copy()
|
||||
try:
|
||||
for replacement in self.replacements:
|
||||
adder(replacement.path, replacement.replacement)
|
||||
except BaseException:
|
||||
object_patches.clear()
|
||||
object_patches.update(original_patches)
|
||||
raise
|
||||
|
||||
|
||||
def _required_method(
|
||||
model: object,
|
||||
name: str,
|
||||
parameters: tuple[str, ...],
|
||||
) -> Callable[..., object]:
|
||||
"""Return one exact installed positional ModelPatcher method."""
|
||||
|
||||
method = getattr(model, name, None)
|
||||
if not callable(method):
|
||||
raise TypeError(f"MODEL must expose callable {name}({', '.join(parameters)}).")
|
||||
try:
|
||||
observed = tuple(signature(method).parameters.values())
|
||||
except (TypeError, ValueError) as error:
|
||||
raise TypeError(f"MODEL {name} signature cannot be inspected.") from error
|
||||
if len(observed) != len(parameters) or any(
|
||||
parameter.name != expected
|
||||
or parameter.kind
|
||||
not in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
|
||||
for parameter, expected in zip(observed, parameters, strict=True)
|
||||
):
|
||||
raise TypeError(f"MODEL {name} has an unsupported signature.")
|
||||
return cast(Callable[..., object], method)
|
||||
|
||||
|
||||
def _required_dictionary(model: object, name: str) -> dict[object, object]:
|
||||
"""Return one exact mutable ModelPatcher state dictionary."""
|
||||
|
||||
value = getattr(model, name, None)
|
||||
if not isinstance(value, dict):
|
||||
raise TypeError(f"MODEL {name} must be a dictionary.")
|
||||
return value
|
||||
|
||||
|
||||
def _overlapping_paths(
|
||||
paths: tuple[str, ...],
|
||||
*,
|
||||
cross_only: tuple[str, ...] | None = None,
|
||||
) -> tuple[tuple[str, str], ...]:
|
||||
"""Return equal or ancestor-related path pairs in deterministic order."""
|
||||
|
||||
owned = set(cross_only) if cross_only is not None else None
|
||||
pairs: list[tuple[str, str]] = []
|
||||
for index, left in enumerate(paths):
|
||||
for right in paths[index + 1 :]:
|
||||
if owned is not None and (left in owned) == (right in owned):
|
||||
continue
|
||||
if (
|
||||
left == right
|
||||
or left.startswith(f"{right}.")
|
||||
or right.startswith(f"{left}.")
|
||||
):
|
||||
ordered = sorted((left, right))
|
||||
pair = (ordered[0], ordered[1])
|
||||
if pair not in pairs:
|
||||
pairs.append(pair)
|
||||
return tuple(sorted(pairs))
|
||||
@@ -1 +1,5 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Provide focused regional model-side LoRA runtime adapters."""
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Adapt existing CFG, latent-batch, and spatial-view alignment authorities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from ...domain.regional_activation_geometry import RegionalActivationBatchAlignment
|
||||
from ...domain.regional_attention_batch import BatchedRegionalAttentionContexts
|
||||
from ...domain.spatial_views import SpatialBatchLayout
|
||||
|
||||
|
||||
class RegionalActivationBatchAlignmentResolver:
|
||||
"""Compose published alignment values without recreating their policies."""
|
||||
|
||||
def resolve(
|
||||
self,
|
||||
contexts: BatchedRegionalAttentionContexts,
|
||||
*,
|
||||
spatial_layout: SpatialBatchLayout | None = None,
|
||||
) -> RegionalActivationBatchAlignment:
|
||||
"""Return one activation alignment from exact existing authorities."""
|
||||
|
||||
if not isinstance(contexts, BatchedRegionalAttentionContexts):
|
||||
raise TypeError(
|
||||
"Regional activation alignment requires batched regional contexts."
|
||||
)
|
||||
return RegionalActivationBatchAlignment(
|
||||
latent_batch_size=contexts.latent_batch_size,
|
||||
chunk_count=len(contexts.chunks),
|
||||
spatial_layout=spatial_layout,
|
||||
)
|
||||
|
||||
|
||||
REGIONAL_ACTIVATION_BATCH_ALIGNMENT_RESOLVER = (
|
||||
RegionalActivationBatchAlignmentResolver()
|
||||
)
|
||||
@@ -0,0 +1,160 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Provide regional activation geometry from explicit operation invocations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
import torch
|
||||
|
||||
from ...domain.regional_activation_geometry import (
|
||||
RegionalActivationBatchAlignment,
|
||||
RegionalActivationGeometry,
|
||||
RegionalActivationLayout,
|
||||
RegionalTemporalOwnership,
|
||||
)
|
||||
|
||||
|
||||
class RegionalActivationGeometryProvider(Protocol):
|
||||
"""Resolve one exact regional activation without inferring hidden axes."""
|
||||
|
||||
def resolve(
|
||||
self,
|
||||
activation: torch.Tensor,
|
||||
*,
|
||||
batch_alignment: RegionalActivationBatchAlignment,
|
||||
) -> RegionalActivationGeometry:
|
||||
"""Return validated geometry for one observed activation tensor."""
|
||||
|
||||
...
|
||||
|
||||
|
||||
class DirectConvolutionGeometryProvider:
|
||||
"""Describe channel-first Conv1d/2d/3d rank activations."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
temporal_ownership: RegionalTemporalOwnership = (
|
||||
RegionalTemporalOwnership.NONE
|
||||
),
|
||||
) -> None:
|
||||
"""Retain only an explicit temporal ownership policy."""
|
||||
|
||||
if not isinstance(temporal_ownership, RegionalTemporalOwnership):
|
||||
raise TypeError(
|
||||
"Direct convolution temporal ownership has an invalid type."
|
||||
)
|
||||
self._temporal_ownership = temporal_ownership
|
||||
|
||||
def resolve(
|
||||
self,
|
||||
activation: torch.Tensor,
|
||||
*,
|
||||
batch_alignment: RegionalActivationBatchAlignment,
|
||||
) -> RegionalActivationGeometry:
|
||||
"""Describe one exact channel-first convolution result by tensor rank."""
|
||||
|
||||
_validate_activation(activation, batch_alignment=batch_alignment)
|
||||
if activation.ndim == 3:
|
||||
return RegionalActivationGeometry(
|
||||
RegionalActivationLayout.DIRECT_CONVOLUTION_1D,
|
||||
tuple(activation.shape),
|
||||
1,
|
||||
1,
|
||||
int(activation.shape[2]),
|
||||
batch_alignment,
|
||||
)
|
||||
if activation.ndim == 4:
|
||||
return RegionalActivationGeometry(
|
||||
RegionalActivationLayout.DIRECT_CONVOLUTION_2D,
|
||||
tuple(activation.shape),
|
||||
1,
|
||||
int(activation.shape[2]),
|
||||
int(activation.shape[3]),
|
||||
batch_alignment,
|
||||
)
|
||||
if activation.ndim == 5:
|
||||
return RegionalActivationGeometry(
|
||||
RegionalActivationLayout.DIRECT_CONVOLUTION_3D,
|
||||
tuple(activation.shape),
|
||||
1,
|
||||
int(activation.shape[3]),
|
||||
int(activation.shape[4]),
|
||||
batch_alignment,
|
||||
temporal_axis=2,
|
||||
temporal_ownership=self._temporal_ownership,
|
||||
)
|
||||
raise ValueError(
|
||||
"Direct regional convolution activation must use B/C/L, B/C/H/W, "
|
||||
"or B/C/D/H/W layout."
|
||||
)
|
||||
|
||||
|
||||
class SpatialTokenGeometryProvider:
|
||||
"""Describe flattened spatial tokens using caller-published height and width."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
spatial_height: int,
|
||||
spatial_width: int,
|
||||
consumer_spatialized: bool = False,
|
||||
) -> None:
|
||||
"""Retain the explicit producer/consumer spatial grid."""
|
||||
|
||||
if type(spatial_height) is not int or spatial_height < 1:
|
||||
raise ValueError("Spatial token height must be a positive integer.")
|
||||
if type(spatial_width) is not int or spatial_width < 1:
|
||||
raise ValueError("Spatial token width must be a positive integer.")
|
||||
if not isinstance(consumer_spatialized, bool):
|
||||
raise TypeError("Consumer-spatialized selection must be boolean.")
|
||||
self._height = spatial_height
|
||||
self._width = spatial_width
|
||||
self._layout = (
|
||||
RegionalActivationLayout.CONSUMER_SPATIALIZED
|
||||
if consumer_spatialized
|
||||
else RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS
|
||||
)
|
||||
|
||||
def resolve(
|
||||
self,
|
||||
activation: torch.Tensor,
|
||||
*,
|
||||
batch_alignment: RegionalActivationBatchAlignment,
|
||||
) -> RegionalActivationGeometry:
|
||||
"""Match one B/S/C activation to its explicitly published grid."""
|
||||
|
||||
_validate_activation(activation, batch_alignment=batch_alignment)
|
||||
if activation.ndim != 3:
|
||||
raise ValueError("Spatial-token regional activation must use B/S/C layout.")
|
||||
return RegionalActivationGeometry(
|
||||
self._layout,
|
||||
tuple(activation.shape),
|
||||
2,
|
||||
self._height,
|
||||
self._width,
|
||||
batch_alignment,
|
||||
)
|
||||
|
||||
|
||||
def _validate_activation(
|
||||
activation: object,
|
||||
*,
|
||||
batch_alignment: RegionalActivationBatchAlignment,
|
||||
) -> None:
|
||||
"""Require one finite floating activation and typed alignment evidence."""
|
||||
|
||||
if not isinstance(activation, torch.Tensor):
|
||||
raise TypeError("Regional activation must be a torch.Tensor.")
|
||||
if not activation.is_floating_point():
|
||||
raise TypeError("Regional activation must use a floating-point dtype.")
|
||||
if not isinstance(batch_alignment, RegionalActivationBatchAlignment):
|
||||
raise TypeError("Regional activation requires typed batch alignment.")
|
||||
if activation.ndim < 1 or int(activation.shape[0]) != (
|
||||
batch_alignment.invocation_batch_size
|
||||
):
|
||||
raise ValueError("Regional activation batch must match its alignment.")
|
||||
@@ -123,7 +123,7 @@ class AnimaRegionalLoraPlanAdmissionService:
|
||||
issues: list[AnimaRegionalLoraPlanAdmissionIssue] = []
|
||||
for adapter_plan, weights in zip(
|
||||
plan.adapters,
|
||||
adaptation.adapter_weights,
|
||||
(payload.model_side_weights for payload in adaptation.adapter_payloads),
|
||||
strict=True,
|
||||
):
|
||||
try:
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Classify Comfy normalized operations and their source-key evidence."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
|
||||
from comfy.weight_adapter.base import WeightAdapterBase
|
||||
from comfy.weight_adapter.lora import LoRAAdapter
|
||||
|
||||
from ...domain.regional_lora_plan import RegionalLoraAdapterPlan
|
||||
from .comfy_adapter_resolution import (
|
||||
ComfyAdapterResolutionIssue,
|
||||
ComfyAdapterResolutionIssueCode,
|
||||
ComfyAdapterSourceEntry,
|
||||
ComfyAdapterSourceScope,
|
||||
ComfyAdapterTargetPath,
|
||||
ComfyNormalizedAdapterTarget,
|
||||
)
|
||||
|
||||
|
||||
def normalized_targets(
|
||||
adapter: RegionalLoraAdapterPlan,
|
||||
model_weights: Mapping[object, object],
|
||||
) -> tuple[
|
||||
tuple[ComfyNormalizedAdapterTarget, ...],
|
||||
tuple[ComfyAdapterResolutionIssue, ...],
|
||||
]:
|
||||
"""Classify normalized model operations while preserving their identities."""
|
||||
|
||||
targets: list[ComfyNormalizedAdapterTarget] = []
|
||||
issues: list[ComfyAdapterResolutionIssue] = []
|
||||
for raw_path, operation in model_weights.items():
|
||||
path = _target_path(raw_path)
|
||||
if path is None:
|
||||
issues.append(
|
||||
issue(
|
||||
adapter,
|
||||
ComfyAdapterResolutionIssueCode.INVALID_TARGET_PATH,
|
||||
f"Comfy returned invalid model target path {raw_path!r}.",
|
||||
)
|
||||
)
|
||||
continue
|
||||
source_keys = _operation_source_keys(operation)
|
||||
is_ordinary = (
|
||||
isinstance(operation, LoRAAdapter) and operation.weights[4] is None
|
||||
)
|
||||
if isinstance(operation, LoRAAdapter) and operation.weights[4] is not None:
|
||||
issues.append(
|
||||
issue(
|
||||
adapter,
|
||||
ComfyAdapterResolutionIssueCode.DORA_UNSUPPORTED,
|
||||
f"Target {path.parameter_key!r} uses unsupported DoRA scaling.",
|
||||
)
|
||||
)
|
||||
elif not isinstance(operation, LoRAAdapter):
|
||||
issues.append(
|
||||
issue(
|
||||
adapter,
|
||||
ComfyAdapterResolutionIssueCode.UNSUPPORTED_OPERATION,
|
||||
f"Target {path.parameter_key!r} uses unsupported "
|
||||
f"{type(operation).__name__} operation.",
|
||||
)
|
||||
)
|
||||
targets.append(
|
||||
ComfyNormalizedAdapterTarget(
|
||||
path=path,
|
||||
operation=operation,
|
||||
operation_type=type(operation).__name__,
|
||||
source_keys=source_keys,
|
||||
ordinary_additive_lora=is_ordinary,
|
||||
)
|
||||
)
|
||||
return tuple(targets), tuple(issues)
|
||||
|
||||
|
||||
def classify_raw_sources(
|
||||
raw_weights: Mapping[str, object],
|
||||
*,
|
||||
model_weights: Mapping[object, object],
|
||||
clip_weights: Mapping[object, object],
|
||||
vae_weights: Mapping[object, object],
|
||||
) -> tuple[ComfyAdapterSourceEntry, ...]:
|
||||
"""Classify every raw key from Comfy's normalized payload evidence."""
|
||||
|
||||
model_keys = _normalized_source_keys(model_weights, raw_weights)
|
||||
clip_keys = _normalized_source_keys(clip_weights, raw_weights)
|
||||
vae_keys = _normalized_source_keys(vae_weights, raw_weights)
|
||||
return tuple(
|
||||
ComfyAdapterSourceEntry(
|
||||
source_key=source_key,
|
||||
scope=(
|
||||
ComfyAdapterSourceScope.MODEL
|
||||
if source_key in model_keys
|
||||
else ComfyAdapterSourceScope.TEXT_ENCODER
|
||||
if source_key in clip_keys
|
||||
else ComfyAdapterSourceScope.VAE
|
||||
if source_key in vae_keys
|
||||
else ComfyAdapterSourceScope.UNUSED
|
||||
),
|
||||
)
|
||||
for source_key in raw_weights
|
||||
)
|
||||
|
||||
|
||||
def classify_initialized_sources(
|
||||
*,
|
||||
model_weights: Mapping[object, object],
|
||||
clip_weights: Mapping[object, object],
|
||||
) -> tuple[ComfyAdapterSourceEntry, ...]:
|
||||
"""Classify source keys exposed by already-normalized host operations."""
|
||||
|
||||
model_keys = _operation_mapping_source_keys(model_weights)
|
||||
clip_keys = _operation_mapping_source_keys(clip_weights)
|
||||
return tuple(
|
||||
ComfyAdapterSourceEntry(source_key, ComfyAdapterSourceScope.MODEL)
|
||||
for source_key in sorted(model_keys)
|
||||
) + tuple(
|
||||
ComfyAdapterSourceEntry(source_key, ComfyAdapterSourceScope.TEXT_ENCODER)
|
||||
for source_key in sorted(clip_keys - model_keys)
|
||||
)
|
||||
|
||||
|
||||
def source_keys(value: object | None) -> tuple[str, ...]:
|
||||
"""Return ordered string source keys when a failed payload exposes them."""
|
||||
|
||||
if not isinstance(value, Mapping):
|
||||
return ()
|
||||
return tuple(key for key in value if isinstance(key, str))
|
||||
|
||||
|
||||
def issue(
|
||||
adapter: RegionalLoraAdapterPlan,
|
||||
code: ComfyAdapterResolutionIssueCode,
|
||||
message: str,
|
||||
) -> ComfyAdapterResolutionIssue:
|
||||
"""Create one consistently adapter-scoped issue."""
|
||||
|
||||
return ComfyAdapterResolutionIssue(
|
||||
composition_index=adapter.composition_index,
|
||||
adapter_identity=adapter.adapter_identity.value,
|
||||
code=code,
|
||||
message=message,
|
||||
)
|
||||
|
||||
|
||||
def _normalized_source_keys(
|
||||
normalized: Mapping[object, object],
|
||||
raw_weights: Mapping[str, object],
|
||||
) -> set[str]:
|
||||
"""Recover consumed keys from adapter metadata or retained value identity."""
|
||||
|
||||
exposed = _operation_mapping_source_keys(normalized)
|
||||
normalized_values = tuple(normalized.values())
|
||||
return exposed | {
|
||||
source_key
|
||||
for source_key, source_value in raw_weights.items()
|
||||
if any(_contains_identity(value, source_value) for value in normalized_values)
|
||||
}
|
||||
|
||||
|
||||
def _operation_mapping_source_keys(weights: Mapping[object, object]) -> set[str]:
|
||||
"""Collect source keys exposed by installed normalized adapter objects."""
|
||||
|
||||
return {
|
||||
source_key
|
||||
for operation in weights.values()
|
||||
for source_key in _operation_source_keys(operation)
|
||||
}
|
||||
|
||||
|
||||
def _operation_source_keys(operation: object) -> tuple[str, ...]:
|
||||
"""Read exact source keys exposed by one installed Comfy adapter."""
|
||||
|
||||
if not isinstance(operation, WeightAdapterBase):
|
||||
return ()
|
||||
return tuple(sorted(operation.loaded_keys))
|
||||
|
||||
|
||||
def _contains_identity(container: object, sought: object) -> bool:
|
||||
"""Find a retained source value without tensor equality or device movement."""
|
||||
|
||||
if container is sought:
|
||||
return True
|
||||
if isinstance(container, (tuple, list)):
|
||||
return any(_contains_identity(item, sought) for item in container)
|
||||
if isinstance(container, WeightAdapterBase):
|
||||
return _contains_identity(container.weights, sought)
|
||||
return False
|
||||
|
||||
|
||||
def _target_path(value: object) -> ComfyAdapterTargetPath | None:
|
||||
"""Retain string and sliced target paths exactly as Comfy emits them."""
|
||||
|
||||
if isinstance(value, str):
|
||||
return ComfyAdapterTargetPath(value, None)
|
||||
if (
|
||||
isinstance(value, tuple)
|
||||
and len(value) == 2
|
||||
and isinstance(value[0], str)
|
||||
and isinstance(value[1], tuple)
|
||||
and all(
|
||||
isinstance(part, int) and not isinstance(part, bool) for part in value[1]
|
||||
)
|
||||
):
|
||||
return ComfyAdapterTargetPath(value[0], value[1])
|
||||
return None
|
||||
@@ -0,0 +1,88 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Define immutable evidence returned by Comfy regional adapter resolution."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
from ...domain.regional_lora_plan import RegionalLoraAdapterPlan
|
||||
from ..regional_lora_host_payload import RegionalLoraHostPayload
|
||||
|
||||
|
||||
class ComfyAdapterSourceScope(StrEnum):
|
||||
"""Classify one source entry using installed host-resolution evidence."""
|
||||
|
||||
MODEL = "model"
|
||||
TEXT_ENCODER = "text_encoder"
|
||||
VAE = "vae"
|
||||
UNRESOLVED = "unresolved"
|
||||
UNUSED = "unused"
|
||||
|
||||
|
||||
class ComfyAdapterResolutionIssueCode(StrEnum):
|
||||
"""Identify one adapter-scoped failure without discarding other results."""
|
||||
|
||||
RESOLUTION_FAILED = "resolution_failed"
|
||||
INVALID_TARGET_PATH = "invalid_target_path"
|
||||
UNSUPPORTED_OPERATION = "unsupported_operation"
|
||||
DORA_UNSUPPORTED = "dora_unsupported"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ComfyAdapterTargetPath:
|
||||
"""Retain Comfy's exact model parameter key and optional tensor slice."""
|
||||
|
||||
parameter_key: str
|
||||
offset: tuple[int, ...] | None
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ComfyAdapterSourceEntry:
|
||||
"""Retain one raw source key and its host-proven resolution scope."""
|
||||
|
||||
source_key: str
|
||||
scope: ComfyAdapterSourceScope
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ComfyNormalizedAdapterTarget:
|
||||
"""Retain one exact normalized Comfy operation by identity."""
|
||||
|
||||
path: ComfyAdapterTargetPath
|
||||
operation: object
|
||||
operation_type: str
|
||||
source_keys: tuple[str, ...]
|
||||
ordinary_additive_lora: bool
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ComfyAdapterResolutionIssue:
|
||||
"""Describe one failure in the context of its canonical adapter use."""
|
||||
|
||||
composition_index: int
|
||||
adapter_identity: str
|
||||
code: ComfyAdapterResolutionIssueCode
|
||||
message: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ComfyRegionalAdapterResolution:
|
||||
"""Retain all resolution evidence for one ordered regional adapter use."""
|
||||
|
||||
adapter: RegionalLoraAdapterPlan
|
||||
payload: RegionalLoraHostPayload
|
||||
model_targets: tuple[ComfyNormalizedAdapterTarget, ...]
|
||||
source_entries: tuple[ComfyAdapterSourceEntry, ...]
|
||||
issues: tuple[ComfyAdapterResolutionIssue, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ComfyRegionalLoraResolution:
|
||||
"""Retain ordered adapter results and every aggregated issue."""
|
||||
|
||||
adapters: tuple[ComfyRegionalAdapterResolution, ...]
|
||||
issues: tuple[ComfyAdapterResolutionIssue, ...]
|
||||
@@ -0,0 +1,209 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Resolve regional WeightHook payloads through installed Comfy contracts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Mapping
|
||||
from typing import cast
|
||||
|
||||
import comfy.lora
|
||||
import comfy.model_patcher
|
||||
|
||||
from ...domain.regional_lora_plan import RegionalLoraAdapterPlan
|
||||
from ..regional_lora_host_payload import RegionalLoraHostPayload
|
||||
from ..regional_lora_plan_adapter import RegionalLoraPlanAdaptation
|
||||
from .comfy_adapter_evidence import (
|
||||
classify_initialized_sources,
|
||||
classify_raw_sources,
|
||||
issue,
|
||||
normalized_targets,
|
||||
source_keys,
|
||||
)
|
||||
from .comfy_adapter_resolution import (
|
||||
ComfyAdapterResolutionIssueCode,
|
||||
ComfyAdapterSourceEntry,
|
||||
ComfyAdapterSourceScope,
|
||||
ComfyRegionalAdapterResolution,
|
||||
ComfyRegionalLoraResolution,
|
||||
)
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ComfyRegionalAdapterResolver:
|
||||
"""Resolve regional model LoRAs without model, hook, or tensor mutation."""
|
||||
|
||||
def resolve(
|
||||
self,
|
||||
adaptation: RegionalLoraPlanAdaptation,
|
||||
*,
|
||||
model: object,
|
||||
clip_model: object | None = None,
|
||||
vae_key_map: Mapping[str, object] | None = None,
|
||||
) -> ComfyRegionalLoraResolution:
|
||||
"""Resolve every ordered payload and aggregate adapter-scoped failures."""
|
||||
|
||||
base_model = _base_model(model)
|
||||
results = tuple(
|
||||
self._resolve_adapter(
|
||||
adapter,
|
||||
payload,
|
||||
model=base_model,
|
||||
clip_model=clip_model,
|
||||
vae_key_map=vae_key_map,
|
||||
)
|
||||
for adapter, payload in zip(
|
||||
adaptation.plan.adapters,
|
||||
adaptation.adapter_payloads,
|
||||
strict=True,
|
||||
)
|
||||
)
|
||||
return ComfyRegionalLoraResolution(
|
||||
adapters=results,
|
||||
issues=tuple(issue for result in results for issue in result.issues),
|
||||
)
|
||||
|
||||
def _resolve_adapter(
|
||||
self,
|
||||
adapter: RegionalLoraAdapterPlan,
|
||||
payload: RegionalLoraHostPayload,
|
||||
*,
|
||||
model: object,
|
||||
clip_model: object | None,
|
||||
vae_key_map: Mapping[str, object] | None,
|
||||
) -> ComfyRegionalAdapterResolution:
|
||||
"""Resolve one payload while converting exceptions into scoped evidence."""
|
||||
|
||||
try:
|
||||
return self._resolve_adapter_or_raise(
|
||||
adapter,
|
||||
payload,
|
||||
model=model,
|
||||
clip_model=clip_model,
|
||||
vae_key_map=vae_key_map,
|
||||
)
|
||||
except Exception as error:
|
||||
LOGGER.exception(
|
||||
"Comfy regional adapter resolution failed",
|
||||
extra={
|
||||
"adapter_identity": adapter.adapter_identity.value,
|
||||
"composition_index": adapter.composition_index,
|
||||
"region_index": adapter.region_index,
|
||||
"branch": adapter.branch.value,
|
||||
},
|
||||
)
|
||||
resolution_issue = issue(
|
||||
adapter,
|
||||
ComfyAdapterResolutionIssueCode.RESOLUTION_FAILED,
|
||||
f"Comfy regional adapter resolution failed: {error}",
|
||||
)
|
||||
return ComfyRegionalAdapterResolution(
|
||||
adapter=adapter,
|
||||
payload=payload,
|
||||
model_targets=(),
|
||||
source_entries=tuple(
|
||||
ComfyAdapterSourceEntry(
|
||||
source_key=source_key,
|
||||
scope=ComfyAdapterSourceScope.UNRESOLVED,
|
||||
)
|
||||
for source_key in source_keys(payload.raw_weights)
|
||||
),
|
||||
issues=(resolution_issue,),
|
||||
)
|
||||
|
||||
def _resolve_adapter_or_raise(
|
||||
self,
|
||||
adapter: RegionalLoraAdapterPlan,
|
||||
payload: RegionalLoraHostPayload,
|
||||
*,
|
||||
model: object,
|
||||
clip_model: object | None,
|
||||
vae_key_map: Mapping[str, object] | None,
|
||||
) -> ComfyRegionalAdapterResolution:
|
||||
"""Use only Comfy key maps and decoding for one valid host payload."""
|
||||
|
||||
if payload.needs_resolution:
|
||||
raw_weights = _require_source_mapping(payload.raw_weights)
|
||||
model_weights = comfy.lora.load_lora(
|
||||
raw_weights,
|
||||
comfy.lora.model_lora_keys_unet(model, {}),
|
||||
log_missing=False,
|
||||
)
|
||||
clip_weights = (
|
||||
comfy.lora.load_lora(
|
||||
raw_weights,
|
||||
comfy.lora.model_lora_keys_clip(clip_model, {}),
|
||||
log_missing=False,
|
||||
)
|
||||
if clip_model is not None
|
||||
else {}
|
||||
)
|
||||
vae_weights = (
|
||||
comfy.lora.load_lora(
|
||||
raw_weights,
|
||||
dict(vae_key_map),
|
||||
log_missing=False,
|
||||
)
|
||||
if vae_key_map is not None
|
||||
else {}
|
||||
)
|
||||
source_entries = classify_raw_sources(
|
||||
raw_weights,
|
||||
model_weights=model_weights,
|
||||
clip_weights=clip_weights,
|
||||
vae_weights=vae_weights,
|
||||
)
|
||||
else:
|
||||
model_weights = _require_mapping(payload.model_weights)
|
||||
clip_weights = _optional_mapping(payload.clip_weights)
|
||||
source_entries = classify_initialized_sources(
|
||||
model_weights=model_weights,
|
||||
clip_weights=clip_weights,
|
||||
)
|
||||
|
||||
targets, issues = normalized_targets(adapter, model_weights)
|
||||
return ComfyRegionalAdapterResolution(
|
||||
adapter=adapter,
|
||||
payload=payload,
|
||||
model_targets=targets,
|
||||
source_entries=source_entries,
|
||||
issues=issues,
|
||||
)
|
||||
|
||||
|
||||
def _require_mapping(value: object | None) -> Mapping[object, object]:
|
||||
"""Require the mapping contract consumed and emitted by Comfy's resolver."""
|
||||
|
||||
if not isinstance(value, Mapping):
|
||||
raise TypeError("Regional LoRA host payload weights must be a mapping.")
|
||||
return value
|
||||
|
||||
|
||||
def _base_model(model: object) -> object:
|
||||
"""Return the exact base model owned by one Comfy MODEL object."""
|
||||
|
||||
if not isinstance(model, comfy.model_patcher.ModelPatcher):
|
||||
raise TypeError("Regional LoRA resolution requires a Comfy ModelPatcher.")
|
||||
return model.model
|
||||
|
||||
|
||||
def _require_source_mapping(value: object | None) -> Mapping[str, object]:
|
||||
"""Require the string-keyed raw source contract consumed by Comfy."""
|
||||
|
||||
weights = _require_mapping(value)
|
||||
if any(not isinstance(key, str) for key in weights):
|
||||
raise TypeError("Regional LoRA raw source keys must be strings.")
|
||||
return cast(Mapping[str, object], weights)
|
||||
|
||||
|
||||
def _optional_mapping(value: object | None) -> Mapping[object, object]:
|
||||
"""Accept an absent initialized side payload as one empty mapping."""
|
||||
|
||||
return {} if value is None else _require_mapping(value)
|
||||
|
||||
|
||||
COMFY_REGIONAL_ADAPTER_RESOLVER = ComfyRegionalAdapterResolver()
|
||||
@@ -0,0 +1,337 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Execute exact generic regional convolution LoRA rank-space deltas."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from collections.abc import Callable
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as functional
|
||||
|
||||
from ...domain.regional_activation_geometry import RegionalActivationLayout
|
||||
from ...masking.regional_activation_mask_projection import RegionalActivationMaskBatch
|
||||
from .convolution_execution_plan import (
|
||||
RegionalConvolutionExecutionPlan,
|
||||
RegionalConvolutionTargetUse,
|
||||
)
|
||||
|
||||
|
||||
class RegionalConvolutionExecutor:
|
||||
"""Call one original convolution and add regional deltas in declared order."""
|
||||
|
||||
def execute(
|
||||
self,
|
||||
original: Callable[..., object],
|
||||
inputs: torch.Tensor,
|
||||
*args: object,
|
||||
plan: RegionalConvolutionExecutionPlan,
|
||||
masks: RegionalActivationMaskBatch,
|
||||
schedule_strengths: tuple[float, ...],
|
||||
**kwargs: object,
|
||||
) -> torch.Tensor:
|
||||
"""Apply each active mask between exact down/middle and up convolutions."""
|
||||
|
||||
expected_spatial = self._validate_call(
|
||||
inputs,
|
||||
plan,
|
||||
masks,
|
||||
schedule_strengths,
|
||||
)
|
||||
original_output = original(inputs, *args, **kwargs)
|
||||
if not isinstance(original_output, torch.Tensor):
|
||||
raise TypeError(
|
||||
"Regional convolution original operation must return a tensor."
|
||||
)
|
||||
parameters = plan.uses[0].parameters
|
||||
expected_output = (
|
||||
int(inputs.shape[0]),
|
||||
parameters.output_channels,
|
||||
*expected_spatial,
|
||||
)
|
||||
if (
|
||||
tuple(original_output.shape) != expected_output
|
||||
or original_output.device != inputs.device
|
||||
or original_output.dtype != inputs.dtype
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional convolution original output must match the observed "
|
||||
"operation shape and input execution type."
|
||||
)
|
||||
result = original_output
|
||||
for index, use in enumerate(plan.uses):
|
||||
scale = use.base_strength * schedule_strengths[index]
|
||||
if scale == 0.0:
|
||||
continue
|
||||
multiplier = masks.multipliers[use.region_index]
|
||||
if not bool(torch.count_nonzero(multiplier)):
|
||||
continue
|
||||
result = result + self._delta(
|
||||
inputs,
|
||||
use=use,
|
||||
multiplier=multiplier,
|
||||
scale=scale,
|
||||
)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _delta(
|
||||
inputs: torch.Tensor,
|
||||
*,
|
||||
use: RegionalConvolutionTargetUse,
|
||||
multiplier: torch.Tensor,
|
||||
scale: float,
|
||||
) -> torch.Tensor:
|
||||
"""Return one exact down/optional-middle/mask/up convolution delta."""
|
||||
|
||||
parameters = use.parameters
|
||||
weights = use.preparation.weights(device=inputs.device, dtype=inputs.dtype)
|
||||
down_kernel = (
|
||||
(1,) * parameters.dimension
|
||||
if weights.middle is not None
|
||||
else parameters.kernel_size
|
||||
)
|
||||
down = _convolution_weight(
|
||||
weights.down,
|
||||
dimension=parameters.dimension,
|
||||
output_channels=int(weights.down.shape[0]),
|
||||
input_channels=parameters.input_channels // parameters.groups,
|
||||
kernel_size=down_kernel,
|
||||
)
|
||||
if parameters.groups > 1:
|
||||
down = down.repeat(parameters.groups, 1, *((1,) * parameters.dimension))
|
||||
rank_values = _convolution(
|
||||
inputs,
|
||||
down,
|
||||
stride=parameters.stride,
|
||||
padding=parameters.padding,
|
||||
dilation=parameters.dilation,
|
||||
groups=parameters.groups,
|
||||
)
|
||||
if weights.middle is not None:
|
||||
middle = weights.middle
|
||||
if parameters.groups > 1:
|
||||
middle = middle.repeat(
|
||||
parameters.groups,
|
||||
1,
|
||||
*((1,) * parameters.dimension),
|
||||
)
|
||||
rank_values = _convolution(
|
||||
rank_values,
|
||||
middle,
|
||||
stride=(1,) * parameters.dimension,
|
||||
padding=(0,) * parameters.dimension,
|
||||
dilation=(1,) * parameters.dimension,
|
||||
groups=parameters.groups,
|
||||
)
|
||||
if tuple(multiplier.shape) != (
|
||||
int(rank_values.shape[0]),
|
||||
1,
|
||||
*tuple(int(value) for value in rank_values.shape[2:]),
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional convolution mask must match the exact rank activation."
|
||||
)
|
||||
rank_values = rank_values * multiplier * scale
|
||||
rank = int(weights.down.shape[0])
|
||||
up = _convolution_weight(
|
||||
weights.up,
|
||||
dimension=parameters.dimension,
|
||||
output_channels=parameters.output_channels,
|
||||
input_channels=rank,
|
||||
kernel_size=(1,) * parameters.dimension,
|
||||
)
|
||||
return _convolution(
|
||||
rank_values,
|
||||
up,
|
||||
stride=(1,) * parameters.dimension,
|
||||
padding=(0,) * parameters.dimension,
|
||||
dilation=(1,) * parameters.dimension,
|
||||
groups=parameters.groups,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _validate_call(
|
||||
inputs: object,
|
||||
plan: object,
|
||||
masks: object,
|
||||
schedule_strengths: object,
|
||||
) -> tuple[int, ...]:
|
||||
"""Preflight invocation, output geometry, masks, and schedules."""
|
||||
|
||||
if not isinstance(inputs, torch.Tensor) or not inputs.is_floating_point():
|
||||
raise TypeError("Regional convolution inputs must be a floating tensor.")
|
||||
if not isinstance(plan, RegionalConvolutionExecutionPlan):
|
||||
raise TypeError("Regional convolution execution requires a typed plan.")
|
||||
parameters = plan.uses[0].parameters
|
||||
if inputs.ndim != parameters.dimension + 2:
|
||||
raise ValueError("Regional convolution input dimensionality is invalid.")
|
||||
if int(inputs.shape[1]) != parameters.input_channels:
|
||||
raise ValueError("Regional convolution input channel count is invalid.")
|
||||
if not isinstance(masks, RegionalActivationMaskBatch):
|
||||
raise TypeError("Regional convolution execution requires activation masks.")
|
||||
expected_layout = {
|
||||
1: RegionalActivationLayout.DIRECT_CONVOLUTION_1D,
|
||||
2: RegionalActivationLayout.DIRECT_CONVOLUTION_2D,
|
||||
3: RegionalActivationLayout.DIRECT_CONVOLUTION_3D,
|
||||
}[parameters.dimension]
|
||||
if masks.geometry.layout is not expected_layout:
|
||||
raise ValueError(
|
||||
"Regional convolution mask geometry has the wrong dimension."
|
||||
)
|
||||
if (
|
||||
masks.multipliers.device != inputs.device
|
||||
or masks.multipliers.dtype != inputs.dtype
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional convolution masks must match input device and dtype."
|
||||
)
|
||||
if not isinstance(schedule_strengths, tuple) or len(schedule_strengths) != len(
|
||||
plan.uses
|
||||
):
|
||||
raise ValueError("Regional convolution schedules must align to uses.")
|
||||
if any(
|
||||
isinstance(strength, bool)
|
||||
or not isinstance(strength, int | float)
|
||||
or not math.isfinite(float(strength))
|
||||
for strength in schedule_strengths
|
||||
):
|
||||
raise TypeError("Regional convolution schedule strengths must be finite.")
|
||||
if any(
|
||||
use.region_index >= int(masks.multipliers.shape[0]) for use in plan.uses
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional convolution use references an unavailable region."
|
||||
)
|
||||
expected_spatial = _rank_spatial_shape(
|
||||
tuple(int(value) for value in inputs.shape[2:]),
|
||||
plan.uses[0],
|
||||
)
|
||||
expected_rank_channels = (
|
||||
int(plan.uses[0].preparation.down.shape[0]) * parameters.groups
|
||||
)
|
||||
if masks.geometry.invocation_shape != (
|
||||
int(inputs.shape[0]),
|
||||
expected_rank_channels,
|
||||
*expected_spatial,
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional convolution mask geometry must describe the exact rank "
|
||||
"activation shape."
|
||||
)
|
||||
multiplier_spatial = tuple(int(value) for value in masks.multipliers.shape[3:])
|
||||
if parameters.dimension == 1:
|
||||
multiplier_spatial = tuple(
|
||||
int(value) for value in masks.multipliers.shape[3:]
|
||||
)
|
||||
if tuple(masks.multipliers.shape[1:3]) != (int(inputs.shape[0]), 1):
|
||||
raise ValueError("Regional convolution mask batch is invalid.")
|
||||
if multiplier_spatial != expected_spatial:
|
||||
raise ValueError("Regional convolution mask spatial shape is invalid.")
|
||||
return expected_spatial
|
||||
|
||||
|
||||
def _rank_spatial_shape(
|
||||
input_spatial: tuple[int, ...],
|
||||
use: RegionalConvolutionTargetUse,
|
||||
) -> tuple[int, ...]:
|
||||
"""Return exact rank-activation spatial dimensions before original execution."""
|
||||
|
||||
parameters = use.parameters
|
||||
down = use.preparation.down
|
||||
if down.ndim == 2 or use.preparation.middle is None:
|
||||
down_kernel = parameters.kernel_size
|
||||
else:
|
||||
down_kernel = tuple(int(value) for value in down.shape[2:])
|
||||
spatial = tuple(
|
||||
_convolution_output_size(
|
||||
size,
|
||||
kernel,
|
||||
stride,
|
||||
padding,
|
||||
dilation,
|
||||
)
|
||||
for size, kernel, stride, padding, dilation in zip(
|
||||
input_spatial,
|
||||
down_kernel,
|
||||
parameters.stride,
|
||||
parameters.padding,
|
||||
parameters.dilation,
|
||||
strict=True,
|
||||
)
|
||||
)
|
||||
middle = use.preparation.middle
|
||||
if middle is not None:
|
||||
middle_kernel = tuple(int(value) for value in middle.shape[2:])
|
||||
spatial = tuple(
|
||||
_convolution_output_size(size, kernel, 1, 0, 1)
|
||||
for size, kernel in zip(spatial, middle_kernel, strict=True)
|
||||
)
|
||||
if any(size < 1 for size in spatial):
|
||||
raise ValueError("Regional convolution rank activation would be empty.")
|
||||
return spatial
|
||||
|
||||
|
||||
def _convolution_output_size(
|
||||
size: int,
|
||||
kernel: int,
|
||||
stride: int,
|
||||
padding: int,
|
||||
dilation: int,
|
||||
) -> int:
|
||||
"""Return Torch's ordinary convolution output size for one axis."""
|
||||
|
||||
return ((size + 2 * padding - dilation * (kernel - 1) - 1) // stride) + 1
|
||||
|
||||
|
||||
def _convolution_weight(
|
||||
weight: torch.Tensor,
|
||||
*,
|
||||
dimension: int,
|
||||
output_channels: int,
|
||||
input_channels: int,
|
||||
kernel_size: tuple[int, ...],
|
||||
) -> torch.Tensor:
|
||||
"""Return the exact convolution view of matrix or convolution weights."""
|
||||
|
||||
expected = (output_channels, input_channels, *kernel_size)
|
||||
if weight.ndim == 2:
|
||||
return weight.reshape(expected)
|
||||
if tuple(weight.shape) != expected:
|
||||
raise ValueError("Regional convolution prepared weight shape is incompatible.")
|
||||
if weight.ndim != dimension + 2:
|
||||
raise ValueError("Regional convolution prepared weight rank is incompatible.")
|
||||
return weight
|
||||
|
||||
|
||||
def _convolution(
|
||||
inputs: torch.Tensor,
|
||||
weight: torch.Tensor,
|
||||
*,
|
||||
stride: tuple[int, ...],
|
||||
padding: tuple[int, ...],
|
||||
dilation: tuple[int, ...],
|
||||
groups: int,
|
||||
) -> torch.Tensor:
|
||||
"""Dispatch one exact dimensional convolution without model-family policy."""
|
||||
|
||||
function = {
|
||||
1: functional.conv1d,
|
||||
2: functional.conv2d,
|
||||
3: functional.conv3d,
|
||||
}[inputs.ndim - 2]
|
||||
return function(
|
||||
inputs,
|
||||
weight,
|
||||
bias=None,
|
||||
stride=stride,
|
||||
padding=padding,
|
||||
dilation=dilation,
|
||||
groups=groups,
|
||||
)
|
||||
|
||||
|
||||
REGIONAL_CONVOLUTION_EXECUTOR = RegionalConvolutionExecutor()
|
||||
@@ -0,0 +1,270 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Adapt bound Comfy convolution LoRAs to exact generic execution plans."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
from comfy.weight_adapter.lora import LoRAAdapter
|
||||
from torch import nn
|
||||
|
||||
from .convolution_preparation import RegionalConvolutionPreparation
|
||||
from .target_binding import (
|
||||
BoundRegionalLoraModuleClass,
|
||||
BoundRegionalLoraOperation,
|
||||
BoundRegionalLoraSpatialCapability,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalConvolutionParameters:
|
||||
"""Retain the observed original operation parameters used by the down path."""
|
||||
|
||||
dimension: int
|
||||
stride: tuple[int, ...]
|
||||
padding: tuple[int, ...]
|
||||
dilation: tuple[int, ...]
|
||||
groups: int
|
||||
input_channels: int
|
||||
output_channels: int
|
||||
kernel_size: tuple[int, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require complete positive convolution metadata for one dimension."""
|
||||
|
||||
if self.dimension not in (1, 2, 3):
|
||||
raise ValueError("Regional convolution dimension must be 1, 2, or 3.")
|
||||
for name, values, allow_zero in (
|
||||
("stride", self.stride, False),
|
||||
("padding", self.padding, True),
|
||||
("dilation", self.dilation, False),
|
||||
):
|
||||
if len(values) != self.dimension or any(
|
||||
isinstance(value, bool)
|
||||
or not isinstance(value, int)
|
||||
or value < (0 if allow_zero else 1)
|
||||
for value in values
|
||||
):
|
||||
raise ValueError(f"Regional convolution {name} is invalid.")
|
||||
if (
|
||||
isinstance(self.groups, bool)
|
||||
or not isinstance(self.groups, int)
|
||||
or self.groups < 1
|
||||
):
|
||||
raise ValueError("Regional convolution groups must be positive.")
|
||||
for name, value in (
|
||||
("input_channels", self.input_channels),
|
||||
("output_channels", self.output_channels),
|
||||
):
|
||||
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
|
||||
raise ValueError(f"Regional convolution {name} must be positive.")
|
||||
if len(self.kernel_size) != self.dimension or any(
|
||||
isinstance(value, bool) or not isinstance(value, int) or value < 1
|
||||
for value in self.kernel_size
|
||||
):
|
||||
raise ValueError("Regional convolution kernel size is invalid.")
|
||||
if self.input_channels % self.groups or self.output_channels % self.groups:
|
||||
raise ValueError("Regional convolution channels must divide over groups.")
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalConvolutionTargetUse:
|
||||
"""Retain one ordered convolution adapter use and its exact tensor owner."""
|
||||
|
||||
composition_index: int
|
||||
region_index: int
|
||||
operation_identity: tuple[int, int, int | None]
|
||||
preparation: RegionalConvolutionPreparation
|
||||
parameters: RegionalConvolutionParameters
|
||||
base_strength: float
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require canonical indices, identities, owners, and finite strength."""
|
||||
|
||||
for name, value in (
|
||||
("composition_index", self.composition_index),
|
||||
("region_index", self.region_index),
|
||||
):
|
||||
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
||||
raise ValueError(
|
||||
f"Regional convolution {name} must be a non-negative integer."
|
||||
)
|
||||
if not isinstance(self.preparation, RegionalConvolutionPreparation):
|
||||
raise TypeError("Regional convolution use requires preparation.")
|
||||
if not isinstance(self.parameters, RegionalConvolutionParameters):
|
||||
raise TypeError("Regional convolution use requires operation parameters.")
|
||||
if not isinstance(self.base_strength, float) or not math.isfinite(
|
||||
self.base_strength
|
||||
):
|
||||
raise TypeError("Regional convolution base strength must be finite.")
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RegionalConvolutionExecutionPlan:
|
||||
"""Retain one target module's declared-order convolution adapter uses."""
|
||||
|
||||
uses: tuple[RegionalConvolutionTargetUse, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require one ordered set sharing exact operation parameters and channels."""
|
||||
|
||||
if not isinstance(self.uses, tuple) or not self.uses:
|
||||
raise ValueError("Regional convolution execution plan cannot be empty.")
|
||||
if any(not isinstance(use, RegionalConvolutionTargetUse) for use in self.uses):
|
||||
raise TypeError("Regional convolution plan contains an invalid use.")
|
||||
indices = tuple(use.composition_index for use in self.uses)
|
||||
if indices != tuple(sorted(indices)):
|
||||
raise ValueError("Regional convolution uses must follow composition order.")
|
||||
parameters = self.uses[0].parameters
|
||||
if any(use.parameters != parameters for use in self.uses):
|
||||
raise ValueError(
|
||||
"Regional convolution uses must share operation parameters."
|
||||
)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Release every locally retained prepared convolution tensor set."""
|
||||
|
||||
for use in self.uses:
|
||||
use.preparation.clear()
|
||||
|
||||
|
||||
class RegionalConvolutionExecutionPlanFactory:
|
||||
"""Build one exact convolution plan from U2/U3/U4 binding evidence."""
|
||||
|
||||
def build(
|
||||
self,
|
||||
bindings: tuple[BoundRegionalLoraOperation, ...],
|
||||
) -> RegionalConvolutionExecutionPlan:
|
||||
"""Adapt exact tensors and observed module parameters without mutation."""
|
||||
|
||||
if not isinstance(bindings, tuple) or not bindings:
|
||||
raise ValueError("Regional convolution plan requires bound operations.")
|
||||
return RegionalConvolutionExecutionPlan(
|
||||
tuple(self._use(item) for item in bindings)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _use(binding: BoundRegionalLoraOperation) -> RegionalConvolutionTargetUse:
|
||||
"""Adapt one direct bound convolution operation."""
|
||||
|
||||
expected_classes = {
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_1D: (nn.Conv1d, 1),
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_2D: (nn.Conv2d, 2),
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_3D: (nn.Conv3d, 3),
|
||||
}
|
||||
if not isinstance(binding, BoundRegionalLoraOperation) or not binding.bound:
|
||||
raise ValueError(
|
||||
"Regional convolution execution requires a bound operation."
|
||||
)
|
||||
if binding.module_class not in expected_classes:
|
||||
raise ValueError(
|
||||
"Regional convolution execution requires a convolution target."
|
||||
)
|
||||
if binding.spatial_capability is not BoundRegionalLoraSpatialCapability.DIRECT:
|
||||
raise ValueError(
|
||||
"Regional convolution execution requires direct capability."
|
||||
)
|
||||
module_type, dimension = expected_classes[binding.module_class]
|
||||
if not isinstance(binding.module, module_type):
|
||||
raise TypeError("Regional convolution bound module class is inconsistent.")
|
||||
operation = binding.normalized_target.operation
|
||||
if not isinstance(operation, LoRAAdapter):
|
||||
raise TypeError("Regional convolution operation must be LoRAAdapter.")
|
||||
up, down, _alpha, middle, dora, reshape = operation.weights
|
||||
if not isinstance(up, torch.Tensor) or not isinstance(down, torch.Tensor):
|
||||
raise TypeError("Regional convolution down/up weights must be tensors.")
|
||||
if middle is not None and not isinstance(middle, torch.Tensor):
|
||||
raise TypeError("Regional convolution middle weight must be a tensor.")
|
||||
if dora is not None or reshape is not None:
|
||||
raise ValueError(
|
||||
"Regional convolution ordinary execution rejects DoRA/reshape."
|
||||
)
|
||||
descriptor = binding.descriptor
|
||||
if descriptor.intrinsic_scale is None:
|
||||
raise ValueError("Regional convolution descriptor lacks intrinsic scale.")
|
||||
module = binding.module
|
||||
assert isinstance(module, nn.Conv1d | nn.Conv2d | nn.Conv3d)
|
||||
if module.padding_mode != "zeros":
|
||||
raise ValueError(
|
||||
"Regional convolution execution requires zero padding mode."
|
||||
)
|
||||
parameters = RegionalConvolutionParameters(
|
||||
dimension,
|
||||
tuple(int(value) for value in module.stride),
|
||||
tuple(int(value) for value in module.padding),
|
||||
tuple(int(value) for value in module.dilation),
|
||||
module.groups,
|
||||
module.in_channels,
|
||||
module.out_channels,
|
||||
tuple(int(value) for value in module.kernel_size),
|
||||
)
|
||||
middle_tensor = middle if isinstance(middle, torch.Tensor) else None
|
||||
_validate_adapter_shapes(
|
||||
down,
|
||||
middle_tensor,
|
||||
up,
|
||||
parameters=parameters,
|
||||
)
|
||||
return RegionalConvolutionTargetUse(
|
||||
descriptor.adapter.composition_index,
|
||||
descriptor.adapter.region_index,
|
||||
(id(down), id(up), None if middle_tensor is None else id(middle_tensor)),
|
||||
RegionalConvolutionPreparation(down, middle_tensor, up),
|
||||
parameters,
|
||||
float(descriptor.adapter.model_strength * descriptor.intrinsic_scale),
|
||||
)
|
||||
|
||||
|
||||
REGIONAL_CONVOLUTION_EXECUTION_PLAN_FACTORY = RegionalConvolutionExecutionPlanFactory()
|
||||
|
||||
|
||||
def _validate_adapter_shapes(
|
||||
down: torch.Tensor,
|
||||
middle: torch.Tensor | None,
|
||||
up: torch.Tensor,
|
||||
*,
|
||||
parameters: RegionalConvolutionParameters,
|
||||
) -> None:
|
||||
"""Require exact matrix/conv down, optional middle, and pointwise up shapes."""
|
||||
|
||||
rank = int(down.shape[0])
|
||||
input_per_group = parameters.input_channels // parameters.groups
|
||||
expected_matrix_input = input_per_group * math.prod(parameters.kernel_size)
|
||||
if down.ndim == 2:
|
||||
if tuple(down.shape) != (rank, expected_matrix_input):
|
||||
raise ValueError("Regional convolution matrix down shape is incompatible.")
|
||||
down_kernel = parameters.kernel_size
|
||||
elif down.ndim == parameters.dimension + 2:
|
||||
if int(down.shape[1]) != input_per_group:
|
||||
raise ValueError("Regional convolution down channels are incompatible.")
|
||||
down_kernel = tuple(int(value) for value in down.shape[2:])
|
||||
else:
|
||||
raise ValueError("Regional convolution down dimensionality is incompatible.")
|
||||
if middle is None:
|
||||
if down_kernel != parameters.kernel_size:
|
||||
raise ValueError("Regional convolution down kernel must match the module.")
|
||||
else:
|
||||
if down_kernel != (1,) * parameters.dimension:
|
||||
raise ValueError("LoCon down convolution must use a pointwise kernel.")
|
||||
if tuple(middle.shape) != (
|
||||
rank,
|
||||
rank,
|
||||
*parameters.kernel_size,
|
||||
):
|
||||
raise ValueError("LoCon middle convolution shape is incompatible.")
|
||||
pointwise = (1,) * parameters.dimension
|
||||
if up.ndim == 2:
|
||||
valid_up = tuple(up.shape) == (parameters.output_channels, rank)
|
||||
else:
|
||||
valid_up = tuple(up.shape) == (
|
||||
parameters.output_channels,
|
||||
rank,
|
||||
*pointwise,
|
||||
)
|
||||
if not valid_up:
|
||||
raise ValueError("Regional convolution up shape must be pointwise.")
|
||||
@@ -0,0 +1,96 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prepare immutable regional convolution LoRA tensors per device and dtype."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from threading import Lock
|
||||
|
||||
import torch
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalConvolutionPreparedWeights:
|
||||
"""Retain one exact prepared down/optional-middle/up tensor set."""
|
||||
|
||||
down: torch.Tensor
|
||||
middle: torch.Tensor | None
|
||||
up: torch.Tensor
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RegionalConvolutionPreparation:
|
||||
"""Prepare one admitted convolution target once per execution type."""
|
||||
|
||||
down: torch.Tensor
|
||||
middle: torch.Tensor | None
|
||||
up: torch.Tensor
|
||||
_prepared: dict[
|
||||
tuple[torch.device, torch.dtype], RegionalConvolutionPreparedWeights
|
||||
] = field(default_factory=dict, init=False, repr=False)
|
||||
_lock: Lock = field(default_factory=Lock, init=False, repr=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require finite CPU floating tensors without copying their contents."""
|
||||
|
||||
for name, tensor in (
|
||||
("down", self.down),
|
||||
("up", self.up),
|
||||
("middle", self.middle),
|
||||
):
|
||||
if tensor is None and name == "middle":
|
||||
continue
|
||||
if not isinstance(tensor, torch.Tensor):
|
||||
raise TypeError(f"Regional convolution {name} weight must be a tensor.")
|
||||
if tensor.device.type != "cpu" or not tensor.is_floating_point():
|
||||
raise TypeError(
|
||||
f"Regional convolution {name} weight must be floating CPU data."
|
||||
)
|
||||
if tensor.ndim < 2 or not bool(torch.isfinite(tensor).all()):
|
||||
raise ValueError(
|
||||
f"Regional convolution {name} weight must be finite "
|
||||
"rank-two-or-higher data."
|
||||
)
|
||||
|
||||
def weights(
|
||||
self,
|
||||
*,
|
||||
device: torch.device,
|
||||
dtype: torch.dtype,
|
||||
) -> RegionalConvolutionPreparedWeights:
|
||||
"""Return detached full-quality tensors prepared once for one execution type."""
|
||||
|
||||
if not isinstance(dtype, torch.dtype) or not dtype.is_floating_point:
|
||||
raise TypeError("Regional convolution execution dtype must be floating.")
|
||||
key = (torch.device(device), dtype)
|
||||
existing = self._prepared.get(key)
|
||||
if existing is not None:
|
||||
return existing
|
||||
with self._lock:
|
||||
existing = self._prepared.get(key)
|
||||
if existing is not None:
|
||||
return existing
|
||||
prepared = RegionalConvolutionPreparedWeights(
|
||||
self.down.detach().to(device=key[0], dtype=dtype, copy=True),
|
||||
(
|
||||
None
|
||||
if self.middle is None
|
||||
else self.middle.detach().to(
|
||||
device=key[0],
|
||||
dtype=dtype,
|
||||
copy=True,
|
||||
)
|
||||
),
|
||||
self.up.detach().to(device=key[0], dtype=dtype, copy=True),
|
||||
)
|
||||
self._prepared[key] = prepared
|
||||
return prepared
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Release every locally retained device/dtype tensor set."""
|
||||
|
||||
with self._lock:
|
||||
self._prepared.clear()
|
||||
@@ -0,0 +1,107 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Resolve read-only effective ModelPatcher objects through active object patches."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass
|
||||
|
||||
import comfy.model_patcher
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class EffectiveModelObject:
|
||||
"""Retain one resolved object and every active patch prefix affecting it."""
|
||||
|
||||
value: object
|
||||
active_object_patch_paths: tuple[str, ...]
|
||||
|
||||
|
||||
class EffectiveModelGraphError(ValueError):
|
||||
"""Report an absent or ambiguous effective graph path."""
|
||||
|
||||
|
||||
class EffectiveModelGraphResolver:
|
||||
"""Read effective graph objects without applying, ejecting, or mutating patches."""
|
||||
|
||||
def resolve(
|
||||
self,
|
||||
patcher: comfy.model_patcher.ModelPatcher,
|
||||
path: str,
|
||||
) -> EffectiveModelObject:
|
||||
"""Resolve one path through the most specific unambiguous active patch."""
|
||||
|
||||
if not isinstance(patcher, comfy.model_patcher.ModelPatcher):
|
||||
raise TypeError("Effective model graph resolution requires a ModelPatcher.")
|
||||
if (
|
||||
not isinstance(path, str)
|
||||
or not path
|
||||
or any(not segment for segment in path.split("."))
|
||||
):
|
||||
raise ValueError(
|
||||
"Effective model graph path must be a nonempty dotted path."
|
||||
)
|
||||
object_patches = _mapping(patcher.object_patches, name="object_patches")
|
||||
applicable = tuple(
|
||||
patch_path
|
||||
for patch_path in object_patches
|
||||
if isinstance(patch_path, str)
|
||||
and (path == patch_path or path.startswith(f"{patch_path}."))
|
||||
)
|
||||
nested = tuple(
|
||||
patch_path
|
||||
for patch_path in applicable
|
||||
if any(
|
||||
patch_path != other and patch_path.startswith(f"{other}.")
|
||||
for other in applicable
|
||||
)
|
||||
)
|
||||
if nested:
|
||||
paths = ", ".join(sorted(applicable))
|
||||
raise EffectiveModelGraphError(
|
||||
f"Effective model graph path {path!r} has overlapping object "
|
||||
f"patch prefixes: {paths}."
|
||||
)
|
||||
if applicable:
|
||||
patch_path = max(applicable, key=len)
|
||||
value = object_patches[patch_path]
|
||||
suffix = path.removeprefix(patch_path).removeprefix(".")
|
||||
if suffix:
|
||||
value = _traverse(value, suffix, full_path=path)
|
||||
return EffectiveModelObject(value, tuple(sorted(applicable)))
|
||||
try:
|
||||
value = patcher.get_model_object(path)
|
||||
except AttributeError as error:
|
||||
raise EffectiveModelGraphError(
|
||||
f"Effective model graph path {path!r} does not exist."
|
||||
) from error
|
||||
return EffectiveModelObject(value, ())
|
||||
|
||||
|
||||
def _traverse(value: object, suffix: str, *, full_path: str) -> object:
|
||||
"""Traverse one suffix from an unapplied object-patch replacement."""
|
||||
|
||||
current = value
|
||||
for segment in suffix.split("."):
|
||||
try:
|
||||
current = getattr(current, segment)
|
||||
except AttributeError as error:
|
||||
raise EffectiveModelGraphError(
|
||||
f"Effective model graph path {full_path!r} does not exist in its "
|
||||
"active object-patch replacement."
|
||||
) from error
|
||||
return current
|
||||
|
||||
|
||||
def _mapping(value: object, *, name: str) -> Mapping[object, object]:
|
||||
"""Require the installed ModelPatcher mapping contract."""
|
||||
|
||||
if not isinstance(value, Mapping):
|
||||
raise TypeError(f"ModelPatcher {name} must be a mapping.")
|
||||
return value
|
||||
|
||||
|
||||
EFFECTIVE_MODEL_GRAPH_RESOLVER = EffectiveModelGraphResolver()
|
||||
@@ -0,0 +1,309 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Preserve Comfy host state around clone-local regional operation execution."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from typing import Any
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
|
||||
from .convolution_execution import (
|
||||
REGIONAL_CONVOLUTION_EXECUTOR,
|
||||
RegionalConvolutionExecutor,
|
||||
)
|
||||
from .convolution_execution_plan import RegionalConvolutionExecutionPlan
|
||||
from .linear_execution import REGIONAL_LINEAR_EXECUTOR, RegionalLinearExecutor
|
||||
from .linear_execution_plan import RegionalLinearExecutionPlan
|
||||
from .operation_invocation import (
|
||||
REGIONAL_OPERATION_INVOCATION_CONTEXT,
|
||||
RegionalOperationInvocationContext,
|
||||
)
|
||||
|
||||
_COMFY_TRANSIENT_ATTRIBUTES = frozenset(
|
||||
{
|
||||
"comfy_cast_weights",
|
||||
"comfy_force_cast_weights",
|
||||
"comfy_patched_weights",
|
||||
"prev_comfy_cast_weights",
|
||||
"seed_key",
|
||||
"weight_function",
|
||||
"bias_function",
|
||||
"_pin_state",
|
||||
"_prefetch",
|
||||
"_v",
|
||||
"_v_signature",
|
||||
}
|
||||
)
|
||||
_COMFY_PARAMETER_SUFFIXES = (
|
||||
"_comfy_model_dtype",
|
||||
"_function",
|
||||
"_lowvram_function",
|
||||
)
|
||||
|
||||
|
||||
class RegionalHostBackedOperation(nn.Module):
|
||||
"""Mirror public host state while retaining one private exact-class shell."""
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
"""Resolve registered state locally and original operation state privately."""
|
||||
|
||||
try:
|
||||
return super().__getattr__(name)
|
||||
except AttributeError as error:
|
||||
backing = self.__dict__.get("_host_backing")
|
||||
if isinstance(backing, RegionalHostOperationBacking) and hasattr(
|
||||
backing.module, name
|
||||
):
|
||||
return getattr(backing.module, name)
|
||||
raise error
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
"""Mirror Comfy writes and public operation state onto the exact shell."""
|
||||
|
||||
backing = self.__dict__.get("_host_backing")
|
||||
if isinstance(backing, RegionalHostOperationBacking) and (
|
||||
backing.owns_public_attribute(name)
|
||||
):
|
||||
backing.replace_public_attribute(self, name, value)
|
||||
return
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __delattr__(self, name: str) -> None:
|
||||
"""Delete transient host state from both public views."""
|
||||
|
||||
backing = self.__dict__.get("_host_backing")
|
||||
if isinstance(backing, RegionalHostOperationBacking) and (
|
||||
backing.owns_public_attribute(name)
|
||||
):
|
||||
if hasattr(backing.module, name):
|
||||
delattr(backing.module, name)
|
||||
if name in self.__dict__:
|
||||
super().__delattr__(name)
|
||||
return
|
||||
super().__delattr__(name)
|
||||
|
||||
|
||||
class RegionalHostOperationBacking:
|
||||
"""Own one source-isolated exact-class shell and its public state mirror."""
|
||||
|
||||
def __init__(self, module: nn.Module) -> None:
|
||||
"""Copy registration maps without copying parameter storage."""
|
||||
|
||||
if not isinstance(module, nn.Module):
|
||||
raise TypeError("Regional host backing requires a PyTorch module.")
|
||||
self._module = self._execution_shell(module)
|
||||
self._direct_state_names: frozenset[str] = frozenset()
|
||||
self._public_attribute_names = frozenset(
|
||||
name for name in module.__dict__ if name not in _MODULE_INTERNAL_STATE
|
||||
)
|
||||
|
||||
@property
|
||||
def module(self) -> nn.Module:
|
||||
"""Return the private original exact-class execution shell."""
|
||||
|
||||
return self._module
|
||||
|
||||
def install_direct_state(self, replacement: nn.Module) -> None:
|
||||
"""Expose parameters and buffers at their unchanged public model paths."""
|
||||
|
||||
if "weight" in self._module._parameters:
|
||||
self._module._parameters.setdefault("bias", None)
|
||||
parameter_names = tuple(self._module._parameters)
|
||||
buffer_names = tuple(self._module._buffers)
|
||||
overlap = frozenset(parameter_names).intersection(buffer_names)
|
||||
if overlap:
|
||||
raise ValueError(
|
||||
"Regional host operation exposes duplicate parameter/buffer names: "
|
||||
f"{sorted(overlap)!r}."
|
||||
)
|
||||
replacement._parameters = self._module._parameters
|
||||
replacement._buffers = self._module._buffers
|
||||
replacement._non_persistent_buffers_set = (
|
||||
self._module._non_persistent_buffers_set
|
||||
)
|
||||
self._module._parameters = replacement._parameters
|
||||
self._module._buffers = replacement._buffers
|
||||
self._module._non_persistent_buffers_set = (
|
||||
replacement._non_persistent_buffers_set
|
||||
)
|
||||
self._direct_state_names = frozenset((*parameter_names, *buffer_names))
|
||||
replacement.training = self._module.training
|
||||
|
||||
def owns_public_attribute(self, name: str) -> bool:
|
||||
"""Report operation or Comfy host state mirrored to the exact shell."""
|
||||
|
||||
if (
|
||||
name in self._direct_state_names
|
||||
or name in self._public_attribute_names
|
||||
or name in _COMFY_TRANSIENT_ATTRIBUTES
|
||||
):
|
||||
return True
|
||||
return any(
|
||||
name == f"{parameter_name}{suffix}"
|
||||
for parameter_name in ("weight", "bias")
|
||||
for suffix in _COMFY_PARAMETER_SUFFIXES
|
||||
)
|
||||
|
||||
def replace_public_attribute(
|
||||
self,
|
||||
replacement: nn.Module,
|
||||
name: str,
|
||||
value: Any,
|
||||
) -> None:
|
||||
"""Keep Comfy's installed module and exact shell synchronized."""
|
||||
|
||||
setattr(self._module, name, value)
|
||||
nn.Module.__setattr__(replacement, name, value)
|
||||
|
||||
@staticmethod
|
||||
def _execution_shell(module: nn.Module) -> nn.Module:
|
||||
"""Return an exact-class shell isolated from source registration mutation."""
|
||||
|
||||
shell = copy(module)
|
||||
shell._parameters = {
|
||||
name: None if parameter is None else copy(parameter)
|
||||
for name, parameter in module._parameters.items()
|
||||
}
|
||||
shell._buffers = {
|
||||
name: None if buffer is None else copy(buffer)
|
||||
for name, buffer in module._buffers.items()
|
||||
}
|
||||
shell._non_persistent_buffers_set = module._non_persistent_buffers_set.copy()
|
||||
shell._modules = module._modules.copy()
|
||||
return shell
|
||||
|
||||
|
||||
class RegionalLinearOperationPatch(RegionalHostBackedOperation):
|
||||
"""Execute one host Linear plus task-local regional low-rank deltas."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
module_path: str,
|
||||
original: nn.Module,
|
||||
plan: RegionalLinearExecutionPlan,
|
||||
*,
|
||||
context: RegionalOperationInvocationContext = (
|
||||
REGIONAL_OPERATION_INVOCATION_CONTEXT
|
||||
),
|
||||
executor: RegionalLinearExecutor = REGIONAL_LINEAR_EXECUTOR,
|
||||
) -> None:
|
||||
"""Bind one static plan while retaining task-local runtime inputs."""
|
||||
|
||||
super().__init__()
|
||||
_validate_patch_inputs(module_path, original, plan, context)
|
||||
if not isinstance(executor, RegionalLinearExecutor):
|
||||
raise TypeError("Regional Linear operation requires a typed executor.")
|
||||
self._host_backing = RegionalHostOperationBacking(original)
|
||||
self._host_backing.install_direct_state(self)
|
||||
self._module_path = module_path
|
||||
self._plan = plan
|
||||
self._context = context
|
||||
self._executor = executor
|
||||
|
||||
def forward(self, inputs: torch.Tensor, *args: object, **kwargs: object) -> object:
|
||||
"""Delegate unchanged when inactive or execute the explicit regional call."""
|
||||
|
||||
invocation = self._context.current_for(self._module_path)
|
||||
if invocation is None:
|
||||
return self._host_backing.module(inputs, *args, **kwargs)
|
||||
return self._executor.execute(
|
||||
self._host_backing.module,
|
||||
inputs,
|
||||
*args,
|
||||
plan=self._plan,
|
||||
masks=invocation.masks,
|
||||
schedule_strengths=invocation.schedule_strengths,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class RegionalConvolutionOperationPatch(RegionalHostBackedOperation):
|
||||
"""Execute one host convolution plus task-local regional LoRA deltas."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
module_path: str,
|
||||
original: nn.Module,
|
||||
plan: RegionalConvolutionExecutionPlan,
|
||||
*,
|
||||
context: RegionalOperationInvocationContext = (
|
||||
REGIONAL_OPERATION_INVOCATION_CONTEXT
|
||||
),
|
||||
executor: RegionalConvolutionExecutor = REGIONAL_CONVOLUTION_EXECUTOR,
|
||||
) -> None:
|
||||
"""Bind one static convolution plan and host-compatible shell."""
|
||||
|
||||
super().__init__()
|
||||
_validate_patch_inputs(module_path, original, plan, context)
|
||||
if not isinstance(executor, RegionalConvolutionExecutor):
|
||||
raise TypeError("Regional convolution operation requires a typed executor.")
|
||||
self._host_backing = RegionalHostOperationBacking(original)
|
||||
self._host_backing.install_direct_state(self)
|
||||
self._module_path = module_path
|
||||
self._plan = plan
|
||||
self._context = context
|
||||
self._executor = executor
|
||||
|
||||
def forward(self, inputs: torch.Tensor, *args: object, **kwargs: object) -> object:
|
||||
"""Delegate unchanged when inactive or execute the regional convolution."""
|
||||
|
||||
invocation = self._context.current_for(self._module_path)
|
||||
if invocation is None:
|
||||
return self._host_backing.module(inputs, *args, **kwargs)
|
||||
return self._executor.execute(
|
||||
self._host_backing.module,
|
||||
inputs,
|
||||
*args,
|
||||
plan=self._plan,
|
||||
masks=invocation.masks,
|
||||
schedule_strengths=invocation.schedule_strengths,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def _validate_patch_inputs(
|
||||
module_path: object,
|
||||
original: object,
|
||||
plan: object,
|
||||
context: object,
|
||||
) -> None:
|
||||
"""Validate one static operation shell before retaining host state."""
|
||||
|
||||
if not isinstance(module_path, str) or not module_path.strip():
|
||||
raise ValueError("Regional operation module path must be nonempty.")
|
||||
if not isinstance(original, nn.Module):
|
||||
raise TypeError("Regional operation original must be a PyTorch module.")
|
||||
if not isinstance(
|
||||
plan,
|
||||
RegionalLinearExecutionPlan | RegionalConvolutionExecutionPlan,
|
||||
):
|
||||
raise TypeError("Regional operation requires a typed execution plan.")
|
||||
if not isinstance(context, RegionalOperationInvocationContext):
|
||||
raise TypeError("Regional operation requires an invocation context.")
|
||||
|
||||
|
||||
_MODULE_INTERNAL_STATE = frozenset(
|
||||
{
|
||||
"_parameters",
|
||||
"_buffers",
|
||||
"_non_persistent_buffers_set",
|
||||
"_modules",
|
||||
"_backward_pre_hooks",
|
||||
"_backward_hooks",
|
||||
"_is_full_backward_hook",
|
||||
"_forward_hooks",
|
||||
"_forward_hooks_with_kwargs",
|
||||
"_forward_hooks_always_called",
|
||||
"_forward_pre_hooks",
|
||||
"_forward_pre_hooks_with_kwargs",
|
||||
"_state_dict_hooks",
|
||||
"_state_dict_pre_hooks",
|
||||
"_load_state_dict_pre_hooks",
|
||||
"_load_state_dict_post_hooks",
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,206 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Execute generic regional ordinary-LoRA deltas around one original Linear call."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from collections.abc import Callable
|
||||
|
||||
import torch
|
||||
|
||||
from ...domain.regional_activation_geometry import RegionalActivationLayout
|
||||
from ...masking.regional_activation_mask_projection import RegionalActivationMaskBatch
|
||||
from .delta_execution import REGIONAL_LORA_DELTA_EXECUTOR, RegionalLoraDeltaExecutor
|
||||
from .linear_execution_plan import RegionalLinearExecutionPlan
|
||||
|
||||
|
||||
class RegionalLinearExecutor:
|
||||
"""Call the original once and add exact active adapter groups in order."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
delta_executor: RegionalLoraDeltaExecutor = REGIONAL_LORA_DELTA_EXECUTOR,
|
||||
) -> None:
|
||||
"""Retain the existing model-neutral projection and accumulation owner."""
|
||||
|
||||
if not isinstance(delta_executor, RegionalLoraDeltaExecutor):
|
||||
raise TypeError("Regional Linear execution requires a delta executor.")
|
||||
self._delta_executor = delta_executor
|
||||
|
||||
def execute(
|
||||
self,
|
||||
original: Callable[..., object],
|
||||
inputs: torch.Tensor,
|
||||
*args: object,
|
||||
plan: RegionalLinearExecutionPlan,
|
||||
masks: RegionalActivationMaskBatch,
|
||||
schedule_strengths: tuple[float, ...],
|
||||
**kwargs: object,
|
||||
) -> torch.Tensor:
|
||||
"""Execute one original operation and ordered regional low-rank additions."""
|
||||
|
||||
self._validate_call(inputs, plan, masks, schedule_strengths)
|
||||
original_output = original(inputs, *args, **kwargs)
|
||||
if not isinstance(original_output, torch.Tensor):
|
||||
raise TypeError("Regional Linear original operation must return a tensor.")
|
||||
expected_output_shape = (
|
||||
*inputs.shape[:-1],
|
||||
plan.groups[0].preparation.target.output_features,
|
||||
)
|
||||
if (
|
||||
tuple(original_output.shape) != expected_output_shape
|
||||
or original_output.device != inputs.device
|
||||
or original_output.dtype != inputs.dtype
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional Linear original output must match its target shape and "
|
||||
"input execution type."
|
||||
)
|
||||
multipliers = self._group_multipliers(
|
||||
plan,
|
||||
masks,
|
||||
schedule_strengths=schedule_strengths,
|
||||
)
|
||||
active = tuple(
|
||||
index
|
||||
for index, multiplier in enumerate(multipliers)
|
||||
if multiplier is not None
|
||||
)
|
||||
if not active:
|
||||
return original_output
|
||||
if len(active) == 1:
|
||||
index = active[0]
|
||||
multiplier = multipliers[index]
|
||||
if multiplier is None:
|
||||
raise AssertionError("Active Regional Linear multiplier disappeared.")
|
||||
return self._delta_executor.add_masked_delta(
|
||||
original_output,
|
||||
inputs,
|
||||
preparation=plan.groups[index].preparation,
|
||||
multiplier=multiplier,
|
||||
)
|
||||
deltas: list[torch.Tensor | None] = [None] * len(plan.groups)
|
||||
for indices, preparation in plan.rank_batches:
|
||||
selected = tuple(
|
||||
index for index in indices if multipliers[index] is not None
|
||||
)
|
||||
if not selected:
|
||||
continue
|
||||
if len(selected) == 1:
|
||||
index = selected[0]
|
||||
multiplier = multipliers[index]
|
||||
if multiplier is None:
|
||||
raise AssertionError(
|
||||
"Active Regional Linear multiplier disappeared."
|
||||
)
|
||||
deltas[index] = self._delta_executor.masked_delta(
|
||||
inputs,
|
||||
preparation=plan.groups[index].preparation,
|
||||
multiplier=multiplier,
|
||||
)
|
||||
continue
|
||||
selected_preparation = plan.preparation_for(
|
||||
selected_indices=selected,
|
||||
complete_indices=indices,
|
||||
complete=preparation,
|
||||
)
|
||||
batch = self._delta_executor.compatible_batch(
|
||||
inputs,
|
||||
preparation=selected_preparation,
|
||||
multipliers=tuple(
|
||||
multiplier
|
||||
for index in selected
|
||||
if (multiplier := multipliers[index]) is not None
|
||||
),
|
||||
)
|
||||
for local_index, group_index in enumerate(selected):
|
||||
deltas[group_index] = batch[..., local_index, :]
|
||||
return self._delta_executor.accumulate_ordered(
|
||||
original_output,
|
||||
tuple(delta for delta in deltas if delta is not None),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _group_multipliers(
|
||||
plan: RegionalLinearExecutionPlan,
|
||||
masks: RegionalActivationMaskBatch,
|
||||
*,
|
||||
schedule_strengths: tuple[float, ...],
|
||||
) -> tuple[torch.Tensor | None, ...]:
|
||||
"""Combine contiguous repeated uses in declared floating addition order."""
|
||||
|
||||
strengths = {
|
||||
use.composition_index: schedule_strengths[index]
|
||||
for index, use in enumerate(plan.uses)
|
||||
}
|
||||
combined: list[torch.Tensor | None] = []
|
||||
for group in plan.groups:
|
||||
multiplier: torch.Tensor | None = None
|
||||
for use in group.uses:
|
||||
scale = use.base_strength * strengths[use.composition_index]
|
||||
if scale == 0.0:
|
||||
continue
|
||||
region = masks.multipliers[use.region_index].squeeze(-1)
|
||||
contribution = region * scale
|
||||
multiplier = (
|
||||
contribution if multiplier is None else multiplier + contribution
|
||||
)
|
||||
if multiplier is not None and not bool(torch.count_nonzero(multiplier)):
|
||||
multiplier = None
|
||||
combined.append(multiplier)
|
||||
return tuple(combined)
|
||||
|
||||
@staticmethod
|
||||
def _validate_call(
|
||||
inputs: object,
|
||||
plan: object,
|
||||
masks: object,
|
||||
schedule_strengths: object,
|
||||
) -> None:
|
||||
"""Validate the explicit call contract before invoking the original."""
|
||||
|
||||
if not isinstance(inputs, torch.Tensor) or not inputs.is_floating_point():
|
||||
raise TypeError("Regional Linear inputs must be a floating tensor.")
|
||||
if not isinstance(plan, RegionalLinearExecutionPlan):
|
||||
raise TypeError("Regional Linear execution requires a typed plan.")
|
||||
if not isinstance(masks, RegionalActivationMaskBatch):
|
||||
raise TypeError("Regional Linear execution requires activation masks.")
|
||||
if masks.geometry.layout not in (
|
||||
RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS,
|
||||
RegionalActivationLayout.CONSUMER_SPATIALIZED,
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional Linear execution requires spatial-token geometry."
|
||||
)
|
||||
if tuple(inputs.shape) != masks.geometry.invocation_shape:
|
||||
raise ValueError("Regional Linear input must match activation geometry.")
|
||||
if (
|
||||
masks.multipliers.device != inputs.device
|
||||
or masks.multipliers.dtype != inputs.dtype
|
||||
):
|
||||
raise ValueError("Regional Linear masks must match input device and dtype.")
|
||||
if not isinstance(schedule_strengths, tuple) or len(schedule_strengths) != len(
|
||||
plan.uses
|
||||
):
|
||||
raise ValueError("Regional Linear schedule strengths must align to uses.")
|
||||
if any(
|
||||
isinstance(strength, bool)
|
||||
or not isinstance(strength, int | float)
|
||||
or not math.isfinite(float(strength))
|
||||
for strength in schedule_strengths
|
||||
):
|
||||
raise TypeError("Regional Linear schedule strengths must be finite.")
|
||||
if int(inputs.shape[-1]) != plan.groups[0].preparation.target.input_features:
|
||||
raise ValueError("Regional Linear input feature count is invalid.")
|
||||
if any(
|
||||
use.region_index >= int(masks.multipliers.shape[0]) for use in plan.uses
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional Linear use references an unavailable region mask."
|
||||
)
|
||||
|
||||
|
||||
REGIONAL_LINEAR_EXECUTOR = RegionalLinearExecutor()
|
||||
@@ -0,0 +1,270 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build immutable generic Linear execution plans from bound Comfy operations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
import comfy.model_patcher
|
||||
import torch
|
||||
from comfy.weight_adapter.lora import LoRAAdapter
|
||||
|
||||
from ...domain.regional_lora_plan import RegionalLoraAdapterIdentity
|
||||
from .execution_cache import ModelCloneLineage, RegionalLoraExecutionCache
|
||||
from .preparation import (
|
||||
RegionalLoraCompatibleBatchPreparation,
|
||||
RegionalLoraTargetPreparation,
|
||||
)
|
||||
from .standard_adapter import StandardLoraTarget
|
||||
from .target_binding import (
|
||||
BoundRegionalLoraModuleClass,
|
||||
BoundRegionalLoraOperation,
|
||||
BoundRegionalLoraSpatialCapability,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalLinearOperationKey:
|
||||
"""Identify one exact adapter tensor pair at one bound parameter target."""
|
||||
|
||||
adapter_identity: RegionalLoraAdapterIdentity
|
||||
parameter_path: str
|
||||
down_identity: int
|
||||
up_identity: int
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalLinearTargetUse:
|
||||
"""Retain one ordered regional use and its immutable prepared-weight owner."""
|
||||
|
||||
composition_index: int
|
||||
region_index: int
|
||||
operation_key: RegionalLinearOperationKey
|
||||
preparation: RegionalLoraTargetPreparation
|
||||
base_strength: float
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require canonical indices, key, preparation, and finite base strength."""
|
||||
|
||||
for name, value in (
|
||||
("composition_index", self.composition_index),
|
||||
("region_index", self.region_index),
|
||||
):
|
||||
if isinstance(value, bool) or not isinstance(value, int):
|
||||
raise TypeError(f"Regional Linear {name} must be an integer.")
|
||||
if value < 0:
|
||||
raise ValueError(f"Regional Linear {name} must be non-negative.")
|
||||
if not isinstance(self.operation_key, RegionalLinearOperationKey):
|
||||
raise TypeError("Regional Linear use requires an operation key.")
|
||||
if not isinstance(self.preparation, RegionalLoraTargetPreparation):
|
||||
raise TypeError("Regional Linear use requires target preparation.")
|
||||
if not isinstance(self.base_strength, float) or not math.isfinite(
|
||||
self.base_strength
|
||||
):
|
||||
raise TypeError("Regional Linear base strength must be finite.")
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalLinearTargetGroup:
|
||||
"""Combine only contiguous repeated uses of one exact adapter target."""
|
||||
|
||||
uses: tuple[RegionalLinearTargetUse, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require one nonempty contiguous exact-operation group."""
|
||||
|
||||
if not isinstance(self.uses, tuple) or not self.uses:
|
||||
raise ValueError("Regional Linear target group cannot be empty.")
|
||||
first = self.uses[0]
|
||||
if any(use.operation_key != first.operation_key for use in self.uses):
|
||||
raise ValueError("Regional Linear group uses must share one operation.")
|
||||
|
||||
@property
|
||||
def preparation(self) -> RegionalLoraTargetPreparation:
|
||||
"""Return the shared cache-compatible target preparation."""
|
||||
|
||||
return self.uses[0].preparation
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class RegionalLinearExecutionPlan:
|
||||
"""Retain declared-order groups and reusable compatible-rank preparations."""
|
||||
|
||||
uses: tuple[RegionalLinearTargetUse, ...]
|
||||
groups: tuple[RegionalLinearTargetGroup, ...] = field(init=False)
|
||||
rank_batches: tuple[
|
||||
tuple[tuple[int, ...], RegionalLoraCompatibleBatchPreparation], ...
|
||||
] = field(init=False)
|
||||
_active_batches: dict[tuple[int, ...], RegionalLoraCompatibleBatchPreparation] = (
|
||||
field(default_factory=dict, init=False, repr=False)
|
||||
)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Canonicalize declared order once without changing adapter semantics."""
|
||||
|
||||
if not isinstance(self.uses, tuple) or not self.uses:
|
||||
raise ValueError("Regional Linear execution plan cannot be empty.")
|
||||
if any(not isinstance(use, RegionalLinearTargetUse) for use in self.uses):
|
||||
raise TypeError("Regional Linear execution plan contains an invalid use.")
|
||||
composition = tuple(use.composition_index for use in self.uses)
|
||||
if composition != tuple(sorted(composition)):
|
||||
raise ValueError("Regional Linear uses must follow composition order.")
|
||||
groups = _contiguous_groups(self.uses)
|
||||
object.__setattr__(self, "groups", groups)
|
||||
by_contract: dict[tuple[int, int, int], list[int]] = {}
|
||||
for index, group in enumerate(groups):
|
||||
target = group.preparation.target
|
||||
contract = (target.rank, target.input_features, target.output_features)
|
||||
by_contract.setdefault(contract, []).append(index)
|
||||
batches = tuple(
|
||||
(
|
||||
tuple(indices),
|
||||
RegionalLoraCompatibleBatchPreparation(
|
||||
tuple(groups[index].preparation for index in indices)
|
||||
),
|
||||
)
|
||||
for indices in by_contract.values()
|
||||
)
|
||||
object.__setattr__(self, "rank_batches", batches)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Release locally retained prepared weights and compatible batches."""
|
||||
|
||||
for group in self.groups:
|
||||
group.preparation.clear()
|
||||
for _indices, preparation in self.rank_batches:
|
||||
preparation.clear()
|
||||
for preparation in self._active_batches.values():
|
||||
preparation.clear()
|
||||
self._active_batches.clear()
|
||||
|
||||
def preparation_for(
|
||||
self,
|
||||
*,
|
||||
selected_indices: tuple[int, ...],
|
||||
complete_indices: tuple[int, ...],
|
||||
complete: RegionalLoraCompatibleBatchPreparation,
|
||||
) -> RegionalLoraCompatibleBatchPreparation:
|
||||
"""Reuse one complete batch or cache an exact active compatible subset."""
|
||||
|
||||
if selected_indices == complete_indices:
|
||||
return complete
|
||||
existing = self._active_batches.get(selected_indices)
|
||||
if existing is not None:
|
||||
return existing
|
||||
preparation = RegionalLoraCompatibleBatchPreparation(
|
||||
tuple(self.groups[index].preparation for index in selected_indices)
|
||||
)
|
||||
self._active_batches[selected_indices] = preparation
|
||||
return preparation
|
||||
|
||||
|
||||
class RegionalLinearExecutionPlanFactory:
|
||||
"""Adapt exact U2/U3/U4 evidence to existing neutral preparation owners."""
|
||||
|
||||
def build(
|
||||
self,
|
||||
bindings: tuple[BoundRegionalLoraOperation, ...],
|
||||
*,
|
||||
model: comfy.model_patcher.ModelPatcher,
|
||||
cache: RegionalLoraExecutionCache,
|
||||
) -> RegionalLinearExecutionPlan:
|
||||
"""Return one plan without copying, moving, or mutating adapter tensors."""
|
||||
|
||||
if not isinstance(bindings, tuple) or not bindings:
|
||||
raise ValueError("Regional Linear plan requires bound operations.")
|
||||
if not isinstance(model, comfy.model_patcher.ModelPatcher):
|
||||
raise TypeError("Regional Linear plan requires a Comfy ModelPatcher.")
|
||||
if not isinstance(cache, RegionalLoraExecutionCache):
|
||||
raise TypeError("Regional Linear plan requires an execution cache.")
|
||||
lineage = ModelCloneLineage.from_model(model)
|
||||
return RegionalLinearExecutionPlan(
|
||||
tuple(
|
||||
self._use(binding, lineage=lineage, cache=cache) for binding in bindings
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _use(
|
||||
binding: BoundRegionalLoraOperation,
|
||||
*,
|
||||
lineage: ModelCloneLineage,
|
||||
cache: RegionalLoraExecutionCache,
|
||||
) -> RegionalLinearTargetUse:
|
||||
"""Adapt one exact ordinary matrix pair to a neutral preparation."""
|
||||
|
||||
if not isinstance(binding, BoundRegionalLoraOperation) or not binding.bound:
|
||||
raise ValueError("Regional Linear execution requires a bound operation.")
|
||||
if binding.module_class is not BoundRegionalLoraModuleClass.LINEAR:
|
||||
raise ValueError("Regional Linear execution requires a Linear target.")
|
||||
if binding.spatial_capability is not (
|
||||
BoundRegionalLoraSpatialCapability.CONSUMER_SPATIALIZED
|
||||
):
|
||||
raise ValueError(
|
||||
"Regional Linear execution requires proven consumer spatialization."
|
||||
)
|
||||
operation = binding.normalized_target.operation
|
||||
if not isinstance(operation, LoRAAdapter):
|
||||
raise TypeError("Regional Linear normalized operation must be LoRAAdapter.")
|
||||
up, down, _alpha, middle, dora, reshape = operation.weights
|
||||
if (
|
||||
not isinstance(down, torch.Tensor)
|
||||
or not isinstance(up, torch.Tensor)
|
||||
or down.ndim != 2
|
||||
or up.ndim != 2
|
||||
or middle is not None
|
||||
or dora is not None
|
||||
or reshape is not None
|
||||
):
|
||||
raise ValueError("Regional Linear operation must be one matrix LoRA pair.")
|
||||
descriptor = binding.descriptor
|
||||
if descriptor.rank is None or descriptor.intrinsic_scale is None:
|
||||
raise ValueError("Regional Linear descriptor lacks executable metadata.")
|
||||
target = StandardLoraTarget(
|
||||
target=descriptor.target.parameter_path,
|
||||
down=down,
|
||||
up=up,
|
||||
rank=descriptor.rank,
|
||||
input_features=int(down.shape[1]),
|
||||
output_features=int(up.shape[0]),
|
||||
intrinsic_scale=descriptor.intrinsic_scale,
|
||||
)
|
||||
adapter_identity = descriptor.adapter.adapter_identity
|
||||
return RegionalLinearTargetUse(
|
||||
descriptor.adapter.composition_index,
|
||||
descriptor.adapter.region_index,
|
||||
RegionalLinearOperationKey(
|
||||
adapter_identity,
|
||||
descriptor.target.parameter_path,
|
||||
id(down),
|
||||
id(up),
|
||||
),
|
||||
RegionalLoraTargetPreparation(
|
||||
adapter_identity,
|
||||
lineage,
|
||||
target,
|
||||
cache,
|
||||
),
|
||||
float(descriptor.adapter.model_strength * descriptor.intrinsic_scale),
|
||||
)
|
||||
|
||||
|
||||
def _contiguous_groups(
|
||||
uses: tuple[RegionalLinearTargetUse, ...],
|
||||
) -> tuple[RegionalLinearTargetGroup, ...]:
|
||||
"""Combine adjacent exact operations while preserving all other boundaries."""
|
||||
|
||||
groups: list[list[RegionalLinearTargetUse]] = []
|
||||
for use in uses:
|
||||
if groups and groups[-1][-1].operation_key == use.operation_key:
|
||||
groups[-1].append(use)
|
||||
else:
|
||||
groups.append([use])
|
||||
return tuple(RegionalLinearTargetGroup(tuple(group)) for group in groups)
|
||||
|
||||
|
||||
REGIONAL_LINEAR_EXECUTION_PLAN_FACTORY = RegionalLinearExecutionPlanFactory()
|
||||
@@ -0,0 +1,61 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Release generic regional operation device state at Comfy detach."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol
|
||||
|
||||
from comfy.patcher_extension import CallbacksMP
|
||||
|
||||
from ..model_patcher_mutations import ModelKeyedCallbackMutation
|
||||
from .execution_cache import RegionalLoraExecutionCache
|
||||
|
||||
REGIONAL_OPERATION_DETACH_CALLBACK_KEY = "simple_syrup.regional_operation_plans"
|
||||
|
||||
|
||||
class RegionalOperationPlan(Protocol):
|
||||
"""Expose the device-preparation release contract of one static plan."""
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Release every locally retained prepared tensor."""
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalOperationCacheLifecycle:
|
||||
"""Own complete release for one derived generic regional operation model."""
|
||||
|
||||
plans: tuple[RegionalOperationPlan, ...]
|
||||
cache: RegionalLoraExecutionCache
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require nonempty identity-unique plans and one shared cache owner."""
|
||||
|
||||
if not isinstance(self.plans, tuple) or not self.plans:
|
||||
raise ValueError("Regional operation cache plans cannot be empty.")
|
||||
if any(not callable(getattr(plan, "clear", None)) for plan in self.plans):
|
||||
raise TypeError("Regional operation cache plan must expose clear().")
|
||||
if len({id(plan) for plan in self.plans}) != len(self.plans):
|
||||
raise ValueError("Regional operation cache plans must be unique.")
|
||||
if not isinstance(self.cache, RegionalLoraExecutionCache):
|
||||
raise TypeError("Regional operation cache lifecycle requires a cache.")
|
||||
|
||||
def mutation(self) -> ModelKeyedCallbackMutation:
|
||||
"""Return the single collision-safe Comfy detach registration."""
|
||||
|
||||
return ModelKeyedCallbackMutation(
|
||||
CallbacksMP.ON_DETACH,
|
||||
REGIONAL_OPERATION_DETACH_CALLBACK_KEY,
|
||||
self.release,
|
||||
)
|
||||
|
||||
def release(self, model: object, unpatch_all: bool) -> None:
|
||||
"""Release local and shared prepared tensors on every detach form."""
|
||||
|
||||
del model, unpatch_all
|
||||
for plan in self.plans:
|
||||
plan.clear()
|
||||
self.cache.clear()
|
||||
@@ -0,0 +1,94 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Install generic regional operation patches and detach lifecycle atomically."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from comfy.patcher_extension import CallbacksMP
|
||||
|
||||
from ..model_object_patch_batch import ModelObjectPatchBatchMutation
|
||||
from ..model_patcher_mutations import ModelKeyedCallbackMutation
|
||||
from .operation_cache_lifecycle import (
|
||||
REGIONAL_OPERATION_DETACH_CALLBACK_KEY,
|
||||
RegionalOperationCacheLifecycle,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalOperationInstallationMutation:
|
||||
"""Own rollback across exact object patches and detach callback registration."""
|
||||
|
||||
object_patches: ModelObjectPatchBatchMutation
|
||||
cache_lifecycle: RegionalOperationCacheLifecycle
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require exact focused collaborators before touching patcher state."""
|
||||
|
||||
if not isinstance(self.object_patches, ModelObjectPatchBatchMutation):
|
||||
raise TypeError("Regional installation requires an object-patch batch.")
|
||||
if not isinstance(self.cache_lifecycle, RegionalOperationCacheLifecycle):
|
||||
raise TypeError("Regional installation requires a cache lifecycle.")
|
||||
|
||||
def apply(self, model: object) -> None:
|
||||
"""Preflight both owners, install both, and rollback either on failure."""
|
||||
|
||||
callback_mutation = self.cache_lifecycle.mutation()
|
||||
_preflight_callback(model, callback_mutation)
|
||||
object_patches = getattr(model, "object_patches", None)
|
||||
callbacks = getattr(model, "callbacks", None)
|
||||
if not isinstance(object_patches, dict):
|
||||
raise TypeError("MODEL object_patches must be a dictionary.")
|
||||
if not isinstance(callbacks, dict):
|
||||
raise TypeError("MODEL callbacks must be a dictionary.")
|
||||
original_patches = object_patches.copy()
|
||||
original_callbacks = _copy_callback_state(callbacks)
|
||||
try:
|
||||
self.object_patches.apply(model)
|
||||
callback_mutation.apply(model)
|
||||
except BaseException:
|
||||
object_patches.clear()
|
||||
object_patches.update(original_patches)
|
||||
callbacks.clear()
|
||||
callbacks.update(original_callbacks)
|
||||
raise
|
||||
|
||||
|
||||
def _preflight_callback(
|
||||
model: object,
|
||||
mutation: ModelKeyedCallbackMutation,
|
||||
) -> None:
|
||||
"""Validate callback collision before installing any object replacement."""
|
||||
|
||||
getter = getattr(model, "get_callbacks", None)
|
||||
adder = getattr(model, "add_callback_with_key", None)
|
||||
if not callable(getter) or not callable(adder):
|
||||
raise TypeError("MODEL must expose keyed callback registration.")
|
||||
existing = getter(CallbacksMP.ON_DETACH, REGIONAL_OPERATION_DETACH_CALLBACK_KEY)
|
||||
if not isinstance(existing, list):
|
||||
raise TypeError("MODEL detach callback state must be a list.")
|
||||
if any(not callable(callback) for callback in existing):
|
||||
raise TypeError("MODEL detach callbacks must contain only callables.")
|
||||
if existing:
|
||||
raise ValueError("Regional operation detach callback is already installed.")
|
||||
if mutation.callback_type != CallbacksMP.ON_DETACH:
|
||||
raise ValueError("Regional installation callback type is invalid.")
|
||||
|
||||
|
||||
def _copy_callback_state(
|
||||
callbacks: dict[object, object],
|
||||
) -> dict[object, object]:
|
||||
"""Copy Comfy's callback dictionaries and owned callback lists."""
|
||||
|
||||
copied: dict[object, object] = {}
|
||||
for event, keyed in callbacks.items():
|
||||
if not isinstance(keyed, dict):
|
||||
raise TypeError("MODEL callback event state must be a dictionary.")
|
||||
copied[event] = {
|
||||
key: value.copy() if isinstance(value, list) else value
|
||||
for key, value in keyed.items()
|
||||
}
|
||||
return copied
|
||||
@@ -0,0 +1,73 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Publish task-local runtime values for installed regional operations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator, Mapping
|
||||
from contextlib import contextmanager
|
||||
from contextvars import ContextVar
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ...masking.regional_activation_mask_projection import RegionalActivationMaskBatch
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalOperationInvocation:
|
||||
"""Retain masks and ordered schedule strengths for one target module call."""
|
||||
|
||||
masks: RegionalActivationMaskBatch
|
||||
schedule_strengths: tuple[float, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require typed masks and an explicit immutable strength sequence."""
|
||||
|
||||
if not isinstance(self.masks, RegionalActivationMaskBatch):
|
||||
raise TypeError("Regional operation invocation requires activation masks.")
|
||||
if not isinstance(self.schedule_strengths, tuple):
|
||||
raise TypeError("Regional operation schedule strengths must be a tuple.")
|
||||
|
||||
|
||||
class RegionalOperationInvocationContext:
|
||||
"""Own target-addressed regional operation values for one nested model call."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Create one empty task-local invocation map."""
|
||||
|
||||
self._current: ContextVar[Mapping[str, RegionalOperationInvocation] | None] = (
|
||||
ContextVar("simple_syrup_regional_operation_invocations", default=None)
|
||||
)
|
||||
|
||||
def current_for(self, module_path: str) -> RegionalOperationInvocation | None:
|
||||
"""Return the active target invocation or no value outside its scope."""
|
||||
|
||||
current = self._current.get()
|
||||
return None if current is None else current.get(module_path)
|
||||
|
||||
@contextmanager
|
||||
def activate(
|
||||
self,
|
||||
invocations: Mapping[str, RegionalOperationInvocation],
|
||||
) -> Iterator[None]:
|
||||
"""Publish one immutable-by-contract target map for a nested execution."""
|
||||
|
||||
if not isinstance(invocations, Mapping):
|
||||
raise TypeError("Regional operation invocations must be a mapping.")
|
||||
copied = dict(invocations)
|
||||
if any(not isinstance(path, str) or not path for path in copied):
|
||||
raise ValueError("Regional operation invocation paths must be nonempty.")
|
||||
if any(
|
||||
not isinstance(value, RegionalOperationInvocation)
|
||||
for value in copied.values()
|
||||
):
|
||||
raise TypeError("Regional operation invocation values are invalid.")
|
||||
token = self._current.set(copied)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self._current.reset(token)
|
||||
|
||||
|
||||
REGIONAL_OPERATION_INVOCATION_CONTEXT = RegionalOperationInvocationContext()
|
||||
@@ -0,0 +1,204 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Assemble admitted bound targets into clone-local operation mutations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
|
||||
import comfy.model_patcher
|
||||
from comfy.patcher_extension import CallbacksMP
|
||||
from torch import nn
|
||||
|
||||
from ..model_object_patch_batch import (
|
||||
ExactModelObjectReplacement,
|
||||
ModelObjectPatchBatchMutation,
|
||||
)
|
||||
from ..patcher_lifecycle import ModelMutation
|
||||
from .convolution_execution_plan import (
|
||||
REGIONAL_CONVOLUTION_EXECUTION_PLAN_FACTORY,
|
||||
RegionalConvolutionExecutionPlanFactory,
|
||||
)
|
||||
from .execution_cache import RegionalLoraExecutionCache
|
||||
from .host_operation_backing import (
|
||||
RegionalConvolutionOperationPatch,
|
||||
RegionalLinearOperationPatch,
|
||||
)
|
||||
from .linear_execution_plan import (
|
||||
REGIONAL_LINEAR_EXECUTION_PLAN_FACTORY,
|
||||
RegionalLinearExecutionPlanFactory,
|
||||
)
|
||||
from .operation_cache_lifecycle import (
|
||||
REGIONAL_OPERATION_DETACH_CALLBACK_KEY,
|
||||
RegionalOperationCacheLifecycle,
|
||||
RegionalOperationPlan,
|
||||
)
|
||||
from .operation_installation_mutation import RegionalOperationInstallationMutation
|
||||
from .operation_invocation import (
|
||||
REGIONAL_OPERATION_INVOCATION_CONTEXT,
|
||||
RegionalOperationInvocationContext,
|
||||
)
|
||||
from .target_binding import (
|
||||
BoundRegionalLoraModuleClass,
|
||||
BoundRegionalLoraOperation,
|
||||
RegionalLoraBindingResult,
|
||||
)
|
||||
|
||||
|
||||
class RegionalOperationMutationAssembler:
|
||||
"""Own complete target grouping, static plans, and mutation construction."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
linear_factory: RegionalLinearExecutionPlanFactory = (
|
||||
REGIONAL_LINEAR_EXECUTION_PLAN_FACTORY
|
||||
),
|
||||
convolution_factory: RegionalConvolutionExecutionPlanFactory = (
|
||||
REGIONAL_CONVOLUTION_EXECUTION_PLAN_FACTORY
|
||||
),
|
||||
context: RegionalOperationInvocationContext = (
|
||||
REGIONAL_OPERATION_INVOCATION_CONTEXT
|
||||
),
|
||||
) -> None:
|
||||
"""Retain focused plan and task-local invocation collaborators."""
|
||||
|
||||
self._linear_factory = linear_factory
|
||||
self._convolution_factory = convolution_factory
|
||||
self._context = context
|
||||
|
||||
def assemble(
|
||||
self,
|
||||
binding: RegionalLoraBindingResult,
|
||||
*,
|
||||
model: comfy.model_patcher.ModelPatcher,
|
||||
cache: RegionalLoraExecutionCache,
|
||||
) -> tuple[ModelMutation, ...]:
|
||||
"""Return one exact batch and one cache-lifecycle callback mutation."""
|
||||
|
||||
self._validate_inputs(binding, model=model, cache=cache)
|
||||
grouped = _group_by_module(binding.entries)
|
||||
replacements: list[ExactModelObjectReplacement] = []
|
||||
plans: list[RegionalOperationPlan] = []
|
||||
for module_path, entries in grouped.items():
|
||||
module = entries[0].module
|
||||
if not isinstance(module, nn.Module):
|
||||
raise AssertionError("Admissible binding lost its target module.")
|
||||
module_class = entries[0].module_class
|
||||
if module_class is BoundRegionalLoraModuleClass.LINEAR:
|
||||
linear_plan = self._linear_factory.build(
|
||||
entries,
|
||||
model=model,
|
||||
cache=cache,
|
||||
)
|
||||
linear_replacement = RegionalLinearOperationPatch(
|
||||
module_path,
|
||||
module,
|
||||
linear_plan,
|
||||
context=self._context,
|
||||
)
|
||||
plan: RegionalOperationPlan = linear_plan
|
||||
replacement: object = linear_replacement
|
||||
elif module_class in (
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_1D,
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_2D,
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_3D,
|
||||
):
|
||||
convolution_plan = self._convolution_factory.build(entries)
|
||||
convolution_replacement = RegionalConvolutionOperationPatch(
|
||||
module_path,
|
||||
module,
|
||||
convolution_plan,
|
||||
context=self._context,
|
||||
)
|
||||
plan = convolution_plan
|
||||
replacement = convolution_replacement
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Regional operation module {module_path!r} is unsupported."
|
||||
)
|
||||
plans.append(plan)
|
||||
replacements.append(
|
||||
ExactModelObjectReplacement(module_path, module, replacement)
|
||||
)
|
||||
|
||||
cache_lifecycle = RegionalOperationCacheLifecycle(tuple(plans), cache)
|
||||
return (
|
||||
RegionalOperationInstallationMutation(
|
||||
ModelObjectPatchBatchMutation(tuple(replacements)),
|
||||
cache_lifecycle,
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _validate_inputs(
|
||||
binding: object,
|
||||
*,
|
||||
model: object,
|
||||
cache: object,
|
||||
) -> None:
|
||||
"""Reject incomplete, already-installed, injected, or conflicting state."""
|
||||
|
||||
if not isinstance(binding, RegionalLoraBindingResult):
|
||||
raise TypeError("Regional mutation assembly requires binding evidence.")
|
||||
if not binding.admissible or not binding.entries:
|
||||
raise ValueError("Regional mutation assembly requires complete admission.")
|
||||
if not isinstance(model, comfy.model_patcher.ModelPatcher):
|
||||
raise TypeError("Regional mutation assembly requires a ModelPatcher.")
|
||||
if not isinstance(cache, RegionalLoraExecutionCache):
|
||||
raise TypeError("Regional mutation assembly requires an execution cache.")
|
||||
if model.is_injected:
|
||||
raise ValueError("Regional operation assembly requires an ejected MODEL.")
|
||||
injections = model.injections
|
||||
if not isinstance(injections, Mapping):
|
||||
raise TypeError("MODEL injections must be a mapping.")
|
||||
if any(values for values in injections.values()):
|
||||
raise ValueError(
|
||||
"Regional operation targets cannot compose with existing MODEL "
|
||||
"injections before their target ownership is proven."
|
||||
)
|
||||
callbacks = model.get_callbacks(
|
||||
CallbacksMP.ON_DETACH,
|
||||
REGIONAL_OPERATION_DETACH_CALLBACK_KEY,
|
||||
)
|
||||
if not isinstance(callbacks, list):
|
||||
raise TypeError("MODEL detach callback state must be a list.")
|
||||
if callbacks:
|
||||
raise ValueError("Regional operation detach callback is already installed.")
|
||||
active_paths = tuple(
|
||||
path
|
||||
for entry in binding.entries
|
||||
for path in entry.active_object_patch_paths
|
||||
)
|
||||
if active_paths:
|
||||
raise ValueError(
|
||||
"Regional operation targets overlap existing MODEL object patches: "
|
||||
f"{tuple(dict.fromkeys(active_paths))!r}."
|
||||
)
|
||||
|
||||
|
||||
def _group_by_module(
|
||||
entries: tuple[BoundRegionalLoraOperation, ...],
|
||||
) -> Mapping[str, tuple[BoundRegionalLoraOperation, ...]]:
|
||||
"""Group declared-order adapter uses by one exact observed target module."""
|
||||
|
||||
grouped: dict[str, list[BoundRegionalLoraOperation]] = {}
|
||||
identities: dict[str, tuple[object, BoundRegionalLoraModuleClass]] = {}
|
||||
for entry in entries:
|
||||
path = entry.descriptor.target.model_target
|
||||
identity = (entry.module, entry.module_class)
|
||||
existing = identities.get(path)
|
||||
if existing is not None and (
|
||||
existing[0] is not identity[0] or existing[1] is not identity[1]
|
||||
):
|
||||
raise ValueError(
|
||||
f"Regional operation path {path!r} has inconsistent ownership."
|
||||
)
|
||||
identities[path] = identity
|
||||
grouped.setdefault(path, []).append(entry)
|
||||
return {path: tuple(values) for path, values in grouped.items()}
|
||||
|
||||
|
||||
REGIONAL_OPERATION_MUTATION_ASSEMBLER = RegionalOperationMutationAssembler()
|
||||
@@ -0,0 +1,297 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Translate normalized Comfy LoRA targets into model-neutral descriptors."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
from comfy.weight_adapter.lora import LoRAAdapter
|
||||
|
||||
from ...domain.regional_lora_plan import RegionalLoraAdapterPlan
|
||||
from ...domain.resolved_regional_lora import (
|
||||
RegionalLoraExecutionContract,
|
||||
RegionalLoraGeometryClass,
|
||||
RegionalLoraTensorShape,
|
||||
ResolvedLoraOperationClass,
|
||||
ResolvedRegionalLoraOperation,
|
||||
ResolvedRegionalLoraOperationSet,
|
||||
ResolvedRegionalLoraTarget,
|
||||
)
|
||||
from .comfy_adapter_resolution import (
|
||||
ComfyNormalizedAdapterTarget,
|
||||
ComfyRegionalLoraResolution,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class _OrdinaryLoraMetadata:
|
||||
"""Retain validated descriptor metadata during one translation."""
|
||||
|
||||
operation_class: ResolvedLoraOperationClass
|
||||
down_shape: RegionalLoraTensorShape
|
||||
up_shape: RegionalLoraTensorShape
|
||||
middle_shape: RegionalLoraTensorShape | None
|
||||
reshape_shape: RegionalLoraTensorShape | None
|
||||
rank: int
|
||||
intrinsic_scale: float
|
||||
required_geometry: RegionalLoraGeometryClass
|
||||
execution_contract: RegionalLoraExecutionContract
|
||||
|
||||
|
||||
class ComfyResolvedOperationTranslator:
|
||||
"""Translate U2 results without model binding, tensor movement, or mutation."""
|
||||
|
||||
def translate(
|
||||
self,
|
||||
resolution: ComfyRegionalLoraResolution,
|
||||
) -> ResolvedRegionalLoraOperationSet:
|
||||
"""Return one descriptor for every normalized target in declared order."""
|
||||
|
||||
return ResolvedRegionalLoraOperationSet(
|
||||
tuple(
|
||||
self._translate_target(adapter_result.adapter, target_index, target)
|
||||
for adapter_result in resolution.adapters
|
||||
for target_index, target in enumerate(adapter_result.model_targets)
|
||||
)
|
||||
)
|
||||
|
||||
def _translate_target(
|
||||
self,
|
||||
adapter: RegionalLoraAdapterPlan,
|
||||
target_index: int,
|
||||
target: ComfyNormalizedAdapterTarget,
|
||||
) -> ResolvedRegionalLoraOperation:
|
||||
"""Translate one target or retain its exact rejection reason."""
|
||||
|
||||
try:
|
||||
resolved_target = _resolved_target(target)
|
||||
metadata = _ordinary_lora_metadata(target)
|
||||
return ResolvedRegionalLoraOperation(
|
||||
adapter=adapter,
|
||||
target_index=target_index,
|
||||
target=resolved_target,
|
||||
normalized_operation_type=target.operation_type,
|
||||
operation_class=metadata.operation_class,
|
||||
down_shape=metadata.down_shape,
|
||||
up_shape=metadata.up_shape,
|
||||
middle_shape=metadata.middle_shape,
|
||||
reshape_shape=metadata.reshape_shape,
|
||||
rank=metadata.rank,
|
||||
intrinsic_scale=metadata.intrinsic_scale,
|
||||
required_geometry=metadata.required_geometry,
|
||||
execution_contract=metadata.execution_contract,
|
||||
rejection_reason=None,
|
||||
)
|
||||
except (IndexError, TypeError, ValueError) as error:
|
||||
return _rejection(
|
||||
adapter=adapter,
|
||||
target_index=target_index,
|
||||
target=_fallback_target(target),
|
||||
normalized_operation_type=target.operation_type,
|
||||
reason=str(error),
|
||||
)
|
||||
|
||||
|
||||
def _ordinary_lora_metadata(
|
||||
target: ComfyNormalizedAdapterTarget,
|
||||
) -> _OrdinaryLoraMetadata:
|
||||
"""Validate and classify one exact installed Comfy normalized operation."""
|
||||
|
||||
if not isinstance(target.operation, LoRAAdapter):
|
||||
raise ValueError(
|
||||
f"Normalized operation {target.operation_type} is not an ordinary LoRA."
|
||||
)
|
||||
weights = target.operation.weights
|
||||
if not isinstance(weights, (tuple, list)) or len(weights) != 6:
|
||||
raise ValueError("Normalized LoRA operation must contain six weight fields.")
|
||||
up, down, alpha, middle, dora_scale, reshape = weights
|
||||
if dora_scale is not None:
|
||||
raise ValueError("DoRA scaling is unsupported.")
|
||||
if not target.ordinary_additive_lora:
|
||||
raise ValueError("Normalized LoRA target is not ordinary additive LoRA.")
|
||||
up_tensor = _require_tensor(up, name="up")
|
||||
down_tensor = _require_tensor(down, name="down")
|
||||
middle_tensor = (
|
||||
_require_tensor(middle, name="middle") if middle is not None else None
|
||||
)
|
||||
up_shape = _shape(up_tensor)
|
||||
down_shape = _shape(down_tensor)
|
||||
middle_shape = _shape(middle_tensor) if middle_tensor is not None else None
|
||||
rank = down_shape.dimensions[0]
|
||||
intrinsic_scale = _intrinsic_scale(alpha, rank=rank)
|
||||
reshape_shape = _reshape_shape(reshape)
|
||||
operation_class, geometry, execution = _operation_contract(
|
||||
down_shape,
|
||||
up_shape,
|
||||
middle_shape,
|
||||
)
|
||||
return _OrdinaryLoraMetadata(
|
||||
operation_class=operation_class,
|
||||
down_shape=down_shape,
|
||||
up_shape=up_shape,
|
||||
middle_shape=middle_shape,
|
||||
reshape_shape=reshape_shape,
|
||||
rank=rank,
|
||||
intrinsic_scale=intrinsic_scale,
|
||||
required_geometry=geometry,
|
||||
execution_contract=execution,
|
||||
)
|
||||
|
||||
|
||||
def _operation_contract(
|
||||
down_shape: RegionalLoraTensorShape,
|
||||
up_shape: RegionalLoraTensorShape,
|
||||
middle_shape: RegionalLoraTensorShape | None,
|
||||
) -> tuple[
|
||||
ResolvedLoraOperationClass,
|
||||
RegionalLoraGeometryClass,
|
||||
RegionalLoraExecutionContract,
|
||||
]:
|
||||
"""Classify tensor organization without claiming a target module class."""
|
||||
|
||||
shapes = tuple(
|
||||
shape for shape in (down_shape, up_shape, middle_shape) if shape is not None
|
||||
)
|
||||
maximum_rank = max(shape.rank for shape in shapes)
|
||||
if maximum_rank == 2 and middle_shape is None:
|
||||
return (
|
||||
ResolvedLoraOperationClass.MATRIX_PAIR,
|
||||
RegionalLoraGeometryClass.TARGET_OPERATION,
|
||||
RegionalLoraExecutionContract.MATRIX_OR_RESHAPED_CONVOLUTION,
|
||||
)
|
||||
contracts = {
|
||||
3: (
|
||||
ResolvedLoraOperationClass.CONVOLUTION_1D,
|
||||
RegionalLoraGeometryClass.DIRECT_CONVOLUTION_1D,
|
||||
),
|
||||
4: (
|
||||
ResolvedLoraOperationClass.CONVOLUTION_2D,
|
||||
RegionalLoraGeometryClass.DIRECT_CONVOLUTION_2D,
|
||||
),
|
||||
5: (
|
||||
ResolvedLoraOperationClass.CONVOLUTION_3D,
|
||||
RegionalLoraGeometryClass.DIRECT_CONVOLUTION_3D,
|
||||
),
|
||||
}
|
||||
if maximum_rank not in contracts:
|
||||
raise ValueError(
|
||||
"Normalized LoRA tensor dimensionality must describe a matrix pair "
|
||||
"or Conv1d/2d/3d operation."
|
||||
)
|
||||
if any(shape.rank not in (2, maximum_rank) for shape in shapes):
|
||||
raise ValueError("Normalized convolutional LoRA tensor ranks are inconsistent.")
|
||||
operation_class, geometry = contracts[maximum_rank]
|
||||
return (
|
||||
operation_class,
|
||||
geometry,
|
||||
RegionalLoraExecutionContract.DIRECT_CONVOLUTION,
|
||||
)
|
||||
|
||||
|
||||
def _resolved_target(
|
||||
target: ComfyNormalizedAdapterTarget,
|
||||
) -> ResolvedRegionalLoraTarget:
|
||||
"""Split Comfy's parameter path without resolving it against a model."""
|
||||
|
||||
model_target, separator, parameter_name = target.path.parameter_key.rpartition(".")
|
||||
if not separator or not model_target or not parameter_name:
|
||||
raise ValueError(
|
||||
"Normalized regional LoRA path must identify a model target and parameter."
|
||||
)
|
||||
return ResolvedRegionalLoraTarget(
|
||||
model_target=model_target,
|
||||
parameter_name=parameter_name,
|
||||
offset=target.path.offset,
|
||||
)
|
||||
|
||||
|
||||
def _fallback_target(
|
||||
target: ComfyNormalizedAdapterTarget,
|
||||
) -> ResolvedRegionalLoraTarget:
|
||||
"""Retain a malformed path diagnostically inside a rejected descriptor."""
|
||||
|
||||
return ResolvedRegionalLoraTarget(
|
||||
model_target=target.path.parameter_key or "<empty-target>",
|
||||
parameter_name="<invalid-parameter>",
|
||||
offset=target.path.offset,
|
||||
)
|
||||
|
||||
|
||||
def _require_tensor(value: object, *, name: str) -> torch.Tensor:
|
||||
"""Require tensor metadata without moving, cloning, or reading its values."""
|
||||
|
||||
if not isinstance(value, torch.Tensor):
|
||||
raise TypeError(f"Normalized LoRA {name} weight must be a tensor.")
|
||||
return value
|
||||
|
||||
|
||||
def _shape(tensor: torch.Tensor) -> RegionalLoraTensorShape:
|
||||
"""Copy immutable integer shape metadata from one tensor."""
|
||||
|
||||
return RegionalLoraTensorShape(tuple(int(dimension) for dimension in tensor.shape))
|
||||
|
||||
|
||||
def _intrinsic_scale(alpha: object, *, rank: int) -> float:
|
||||
"""Apply Comfy's exact optional alpha-over-rank ordinary-LoRA scaling."""
|
||||
|
||||
if alpha is None:
|
||||
return 1.0
|
||||
if isinstance(alpha, bool) or not isinstance(alpha, (int, float)):
|
||||
raise TypeError("Normalized LoRA alpha must be a finite scalar or None.")
|
||||
alpha_value = float(alpha)
|
||||
if not math.isfinite(alpha_value):
|
||||
raise ValueError("Normalized LoRA alpha must be finite.")
|
||||
return alpha_value / rank
|
||||
|
||||
|
||||
def _reshape_shape(value: object) -> RegionalLoraTensorShape | None:
|
||||
"""Copy Comfy's optional authored target reshape metadata immutably."""
|
||||
|
||||
if value is None:
|
||||
return None
|
||||
if not isinstance(value, (tuple, list)):
|
||||
raise TypeError("Normalized LoRA reshape metadata must be a sequence.")
|
||||
dimensions: list[int] = []
|
||||
for dimension in value:
|
||||
if isinstance(dimension, bool) or not isinstance(dimension, int):
|
||||
raise TypeError(
|
||||
"Normalized LoRA reshape dimensions must be positive integers."
|
||||
)
|
||||
dimensions.append(dimension)
|
||||
return RegionalLoraTensorShape(tuple(dimensions))
|
||||
|
||||
|
||||
def _rejection(
|
||||
*,
|
||||
adapter: RegionalLoraAdapterPlan,
|
||||
target_index: int,
|
||||
target: ResolvedRegionalLoraTarget,
|
||||
normalized_operation_type: str,
|
||||
reason: str,
|
||||
) -> ResolvedRegionalLoraOperation:
|
||||
"""Return one complete rejection without partial executable metadata."""
|
||||
|
||||
return ResolvedRegionalLoraOperation(
|
||||
adapter=adapter,
|
||||
target_index=target_index,
|
||||
target=target,
|
||||
normalized_operation_type=normalized_operation_type,
|
||||
operation_class=ResolvedLoraOperationClass.UNSUPPORTED,
|
||||
down_shape=None,
|
||||
up_shape=None,
|
||||
middle_shape=None,
|
||||
reshape_shape=None,
|
||||
rank=None,
|
||||
intrinsic_scale=None,
|
||||
required_geometry=RegionalLoraGeometryClass.UNSUPPORTED,
|
||||
execution_contract=RegionalLoraExecutionContract.UNSUPPORTED,
|
||||
rejection_reason=reason,
|
||||
)
|
||||
|
||||
|
||||
COMFY_RESOLVED_OPERATION_TRANSLATOR = ComfyResolvedOperationTranslator()
|
||||
@@ -0,0 +1,380 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Bind resolved regional LoRA targets to one effective Comfy model graph."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import Mapping
|
||||
|
||||
import comfy.model_patcher
|
||||
|
||||
from ...domain.resolved_regional_lora import (
|
||||
ResolvedRegionalLoraOperation,
|
||||
ResolvedRegionalLoraOperationSet,
|
||||
)
|
||||
from .comfy_adapter_resolution import (
|
||||
ComfyNormalizedAdapterTarget,
|
||||
ComfyRegionalLoraResolution,
|
||||
)
|
||||
from .effective_model_graph import (
|
||||
EFFECTIVE_MODEL_GRAPH_RESOLVER,
|
||||
EffectiveModelGraphError,
|
||||
EffectiveModelGraphResolver,
|
||||
)
|
||||
from .target_binding import (
|
||||
BoundRegionalLoraModuleClass,
|
||||
BoundRegionalLoraOperation,
|
||||
BoundRegionalLoraSpatialCapability,
|
||||
RegionalLoraBindingIssue,
|
||||
RegionalLoraBindingIssueCode,
|
||||
RegionalLoraBindingResult,
|
||||
)
|
||||
from .target_shape_admission import (
|
||||
REGIONAL_LORA_TARGET_SHAPE_VALIDATOR,
|
||||
RegionalLoraTargetShapeValidator,
|
||||
)
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RegionalLoraTargetBinder:
|
||||
"""Bind every target atomically in evidence while never mutating the graph."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
graph_resolver: EffectiveModelGraphResolver = EFFECTIVE_MODEL_GRAPH_RESOLVER,
|
||||
shape_validator: RegionalLoraTargetShapeValidator = (
|
||||
REGIONAL_LORA_TARGET_SHAPE_VALIDATOR
|
||||
),
|
||||
) -> None:
|
||||
"""Retain focused effective-graph and shape-admission collaborators."""
|
||||
|
||||
self._graph_resolver = graph_resolver
|
||||
self._shape_validator = shape_validator
|
||||
|
||||
def bind(
|
||||
self,
|
||||
*,
|
||||
source: object,
|
||||
candidate: object,
|
||||
resolution: ComfyRegionalLoraResolution,
|
||||
operations: ResolvedRegionalLoraOperationSet,
|
||||
linear_spatial_capabilities: Mapping[
|
||||
str,
|
||||
BoundRegionalLoraSpatialCapability,
|
||||
]
|
||||
| None = None,
|
||||
) -> RegionalLoraBindingResult:
|
||||
"""Return complete ordered bindings or aggregate pre-mutation failures."""
|
||||
|
||||
capabilities = _linear_spatial_capabilities(linear_spatial_capabilities)
|
||||
lineage_issue = _lineage_issue(source, candidate)
|
||||
if lineage_issue is not None:
|
||||
return RegionalLoraBindingResult((), (lineage_issue,))
|
||||
assert isinstance(candidate, comfy.model_patcher.ModelPatcher)
|
||||
pairing_issues, pairs = _ordered_pairs(resolution, operations)
|
||||
duplicate_keys = _duplicate_ownership_keys(operations.entries)
|
||||
entries: list[BoundRegionalLoraOperation] = []
|
||||
for descriptor, normalized_target in pairs:
|
||||
entry = self._bind_target(
|
||||
candidate,
|
||||
descriptor,
|
||||
normalized_target,
|
||||
linear_spatial_capabilities=capabilities,
|
||||
duplicate=(
|
||||
descriptor.adapter.composition_index,
|
||||
descriptor.target.parameter_path,
|
||||
descriptor.target.offset,
|
||||
)
|
||||
in duplicate_keys,
|
||||
)
|
||||
entries.append(entry)
|
||||
resolution_issues = tuple(
|
||||
RegionalLoraBindingIssue(
|
||||
composition_index=issue.composition_index,
|
||||
target_index=None,
|
||||
parameter_path=None,
|
||||
code=RegionalLoraBindingIssueCode.RESOLUTION_ISSUE,
|
||||
message=issue.message,
|
||||
)
|
||||
for issue in resolution.issues
|
||||
)
|
||||
issues = (
|
||||
pairing_issues
|
||||
+ resolution_issues
|
||||
+ tuple(issue for entry in entries for issue in entry.issues)
|
||||
)
|
||||
return RegionalLoraBindingResult(tuple(entries), issues)
|
||||
|
||||
def _bind_target(
|
||||
self,
|
||||
candidate: comfy.model_patcher.ModelPatcher,
|
||||
descriptor: ResolvedRegionalLoraOperation,
|
||||
normalized_target: ComfyNormalizedAdapterTarget,
|
||||
*,
|
||||
linear_spatial_capabilities: Mapping[
|
||||
str,
|
||||
BoundRegionalLoraSpatialCapability,
|
||||
],
|
||||
duplicate: bool,
|
||||
) -> BoundRegionalLoraOperation:
|
||||
"""Bind one paired target and retain every local failure."""
|
||||
|
||||
issues: list[RegionalLoraBindingIssue] = []
|
||||
if duplicate:
|
||||
issues.append(
|
||||
_issue(
|
||||
descriptor,
|
||||
RegionalLoraBindingIssueCode.DUPLICATE_OWNERSHIP,
|
||||
"One adapter owns the same target path and offset more than once.",
|
||||
)
|
||||
)
|
||||
if not descriptor.supported:
|
||||
issues.append(
|
||||
_issue(
|
||||
descriptor,
|
||||
RegionalLoraBindingIssueCode.OPERATION_REJECTED,
|
||||
descriptor.rejection_reason or "Resolved operation is rejected.",
|
||||
)
|
||||
)
|
||||
return _unbound(descriptor, normalized_target, tuple(issues))
|
||||
try:
|
||||
effective_module = self._graph_resolver.resolve(
|
||||
candidate,
|
||||
descriptor.target.model_target,
|
||||
)
|
||||
effective_parameter = self._graph_resolver.resolve(
|
||||
candidate,
|
||||
descriptor.target.parameter_path,
|
||||
)
|
||||
except EffectiveModelGraphError as error:
|
||||
code = (
|
||||
RegionalLoraBindingIssueCode.OBJECT_PATCH_CONFLICT
|
||||
if "overlapping object patch" in str(error)
|
||||
else RegionalLoraBindingIssueCode.TARGET_NOT_FOUND
|
||||
)
|
||||
issues.append(_issue(descriptor, code, str(error)))
|
||||
return _unbound(descriptor, normalized_target, tuple(issues))
|
||||
admission = self._shape_validator.validate(
|
||||
descriptor,
|
||||
effective_module.value,
|
||||
effective_parameter.value,
|
||||
)
|
||||
if admission.issue is not None:
|
||||
issues.append(
|
||||
_issue(
|
||||
descriptor,
|
||||
_admission_issue_code(admission.issue),
|
||||
admission.issue,
|
||||
)
|
||||
)
|
||||
capability = _spatial_capability(
|
||||
descriptor.target.parameter_path,
|
||||
admission.module_class,
|
||||
linear_spatial_capabilities,
|
||||
)
|
||||
return BoundRegionalLoraOperation(
|
||||
descriptor=descriptor,
|
||||
normalized_target=normalized_target,
|
||||
module=effective_module.value,
|
||||
parameter=admission.parameter,
|
||||
module_class=admission.module_class,
|
||||
spatial_capability=capability,
|
||||
active_object_patch_paths=tuple(
|
||||
dict.fromkeys(
|
||||
effective_module.active_object_patch_paths
|
||||
+ effective_parameter.active_object_patch_paths
|
||||
)
|
||||
),
|
||||
issues=tuple(issues),
|
||||
)
|
||||
|
||||
|
||||
def _lineage_issue(
|
||||
source: object, candidate: object
|
||||
) -> RegionalLoraBindingIssue | None:
|
||||
"""Require the source itself or one direct Comfy clone sharing its model."""
|
||||
|
||||
if not isinstance(source, comfy.model_patcher.ModelPatcher) or not isinstance(
|
||||
candidate, comfy.model_patcher.ModelPatcher
|
||||
):
|
||||
return _global_issue(
|
||||
RegionalLoraBindingIssueCode.SOURCE_LINEAGE,
|
||||
"Regional LoRA target binding requires Comfy ModelPatchers.",
|
||||
)
|
||||
if candidate is source:
|
||||
return None
|
||||
if candidate.parent is not source or not source.is_clone(candidate):
|
||||
return _global_issue(
|
||||
RegionalLoraBindingIssueCode.SOURCE_LINEAGE,
|
||||
"Regional LoRA candidate must be the source or its direct Comfy clone.",
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def _ordered_pairs(
|
||||
resolution: ComfyRegionalLoraResolution,
|
||||
operations: ResolvedRegionalLoraOperationSet,
|
||||
) -> tuple[
|
||||
tuple[RegionalLoraBindingIssue, ...],
|
||||
tuple[tuple[ResolvedRegionalLoraOperation, ComfyNormalizedAdapterTarget], ...],
|
||||
]:
|
||||
"""Pair exact U2 identities with U3 descriptors without positional guessing."""
|
||||
|
||||
normalized = {
|
||||
(adapter.adapter.composition_index, target_index): target
|
||||
for adapter in resolution.adapters
|
||||
for target_index, target in enumerate(adapter.model_targets)
|
||||
}
|
||||
descriptors = {
|
||||
(entry.adapter.composition_index, entry.target_index): entry
|
||||
for entry in operations.entries
|
||||
}
|
||||
issues: list[RegionalLoraBindingIssue] = []
|
||||
missing_descriptors = sorted(normalized.keys() - descriptors.keys())
|
||||
missing_targets = sorted(descriptors.keys() - normalized.keys())
|
||||
for key in missing_descriptors:
|
||||
issues.append(
|
||||
_global_issue(
|
||||
RegionalLoraBindingIssueCode.RESOLUTION_MISMATCH,
|
||||
f"Normalized adapter/target {key} has no U3 descriptor.",
|
||||
)
|
||||
)
|
||||
for key in missing_targets:
|
||||
issues.append(
|
||||
_global_issue(
|
||||
RegionalLoraBindingIssueCode.RESOLUTION_MISMATCH,
|
||||
f"U3 adapter/target {key} has no normalized operation identity.",
|
||||
)
|
||||
)
|
||||
pairs = tuple(
|
||||
(
|
||||
descriptor,
|
||||
normalized[(descriptor.adapter.composition_index, descriptor.target_index)],
|
||||
)
|
||||
for descriptor in operations.entries
|
||||
if (descriptor.adapter.composition_index, descriptor.target_index) in normalized
|
||||
)
|
||||
return tuple(issues), pairs
|
||||
|
||||
|
||||
def _duplicate_ownership_keys(
|
||||
descriptors: tuple[ResolvedRegionalLoraOperation, ...],
|
||||
) -> set[tuple[int, str, tuple[int, ...] | None]]:
|
||||
"""Find repeated ownership within one adapter while allowing composition stacks."""
|
||||
|
||||
seen: set[tuple[int, str, tuple[int, ...] | None]] = set()
|
||||
duplicates: set[tuple[int, str, tuple[int, ...] | None]] = set()
|
||||
for descriptor in descriptors:
|
||||
key = (
|
||||
descriptor.adapter.composition_index,
|
||||
descriptor.target.parameter_path,
|
||||
descriptor.target.offset,
|
||||
)
|
||||
if key in seen:
|
||||
duplicates.add(key)
|
||||
seen.add(key)
|
||||
return duplicates
|
||||
|
||||
|
||||
def _spatial_capability(
|
||||
parameter_path: str,
|
||||
module_class: BoundRegionalLoraModuleClass,
|
||||
linear_capabilities: Mapping[str, BoundRegionalLoraSpatialCapability],
|
||||
) -> BoundRegionalLoraSpatialCapability:
|
||||
"""Publish only spatial capability directly proven by the observed operation."""
|
||||
|
||||
if module_class in (
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_1D,
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_2D,
|
||||
BoundRegionalLoraModuleClass.CONVOLUTION_3D,
|
||||
):
|
||||
return BoundRegionalLoraSpatialCapability.DIRECT
|
||||
if module_class is BoundRegionalLoraModuleClass.LINEAR:
|
||||
return linear_capabilities.get(
|
||||
parameter_path,
|
||||
BoundRegionalLoraSpatialCapability.GLOBAL_ONLY,
|
||||
)
|
||||
return BoundRegionalLoraSpatialCapability.UNSUPPORTED
|
||||
|
||||
|
||||
def _linear_spatial_capabilities(
|
||||
capabilities: Mapping[str, BoundRegionalLoraSpatialCapability] | None,
|
||||
) -> Mapping[str, BoundRegionalLoraSpatialCapability]:
|
||||
"""Validate explicit consumer-spatialized declarations without path policy."""
|
||||
|
||||
if capabilities is None:
|
||||
return {}
|
||||
for path, capability in capabilities.items():
|
||||
if not isinstance(path, str) or not path.strip():
|
||||
raise ValueError("Linear spatial capability paths must be nonempty.")
|
||||
if capability not in (
|
||||
BoundRegionalLoraSpatialCapability.CONSUMER_SPATIALIZED,
|
||||
BoundRegionalLoraSpatialCapability.GLOBAL_ONLY,
|
||||
):
|
||||
raise ValueError(
|
||||
"Linear target capability must be consumer-spatialized or global-only."
|
||||
)
|
||||
return capabilities
|
||||
|
||||
|
||||
def _admission_issue_code(message: str) -> RegionalLoraBindingIssueCode:
|
||||
"""Map focused admission text into stable binder issue categories."""
|
||||
|
||||
if "module type" in message:
|
||||
return RegionalLoraBindingIssueCode.MODULE_UNSUPPORTED
|
||||
if "no parameter" in message:
|
||||
return RegionalLoraBindingIssueCode.PARAMETER_NOT_FOUND
|
||||
if "parameter must" in message:
|
||||
return RegionalLoraBindingIssueCode.PARAMETER_INVALID
|
||||
return RegionalLoraBindingIssueCode.SHAPE_INCOMPATIBLE
|
||||
|
||||
|
||||
def _issue(
|
||||
descriptor: ResolvedRegionalLoraOperation,
|
||||
code: RegionalLoraBindingIssueCode,
|
||||
message: str,
|
||||
) -> RegionalLoraBindingIssue:
|
||||
"""Create one target-scoped issue."""
|
||||
|
||||
return RegionalLoraBindingIssue(
|
||||
composition_index=descriptor.adapter.composition_index,
|
||||
target_index=descriptor.target_index,
|
||||
parameter_path=descriptor.target.parameter_path,
|
||||
code=code,
|
||||
message=message,
|
||||
)
|
||||
|
||||
|
||||
def _global_issue(
|
||||
code: RegionalLoraBindingIssueCode,
|
||||
message: str,
|
||||
) -> RegionalLoraBindingIssue:
|
||||
"""Create one bind-wide issue without inventing target ownership."""
|
||||
|
||||
return RegionalLoraBindingIssue(None, None, None, code, message)
|
||||
|
||||
|
||||
def _unbound(
|
||||
descriptor: ResolvedRegionalLoraOperation,
|
||||
normalized_target: ComfyNormalizedAdapterTarget,
|
||||
issues: tuple[RegionalLoraBindingIssue, ...],
|
||||
) -> BoundRegionalLoraOperation:
|
||||
"""Return one unresolved target without partial module claims."""
|
||||
|
||||
return BoundRegionalLoraOperation(
|
||||
descriptor=descriptor,
|
||||
normalized_target=normalized_target,
|
||||
module=None,
|
||||
parameter=None,
|
||||
module_class=BoundRegionalLoraModuleClass.UNSUPPORTED,
|
||||
spatial_capability=BoundRegionalLoraSpatialCapability.UNSUPPORTED,
|
||||
active_object_patch_paths=(),
|
||||
issues=issues,
|
||||
)
|
||||
|
||||
|
||||
REGIONAL_LORA_TARGET_BINDER = RegionalLoraTargetBinder()
|
||||
@@ -0,0 +1,100 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Define immutable runtime evidence for regional LoRA target binding."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
from ...domain.resolved_regional_lora import ResolvedRegionalLoraOperation
|
||||
from .comfy_adapter_resolution import ComfyNormalizedAdapterTarget
|
||||
|
||||
|
||||
class BoundRegionalLoraModuleClass(StrEnum):
|
||||
"""Classify the observed executable module owning one target parameter."""
|
||||
|
||||
LINEAR = "linear"
|
||||
CONVOLUTION_1D = "convolution_1d"
|
||||
CONVOLUTION_2D = "convolution_2d"
|
||||
CONVOLUTION_3D = "convolution_3d"
|
||||
UNSUPPORTED = "unsupported"
|
||||
|
||||
|
||||
class BoundRegionalLoraSpatialCapability(StrEnum):
|
||||
"""Declare how one bound operation can receive regional spatial ownership."""
|
||||
|
||||
DIRECT = "direct"
|
||||
CONSUMER_SPATIALIZED = "consumer_spatialized"
|
||||
GLOBAL_ONLY = "global_only"
|
||||
UNSUPPORTED = "unsupported"
|
||||
|
||||
|
||||
class RegionalLoraBindingIssueCode(StrEnum):
|
||||
"""Identify one complete-bind failure without discarding sibling evidence."""
|
||||
|
||||
SOURCE_LINEAGE = "source_lineage"
|
||||
RESOLUTION_MISMATCH = "resolution_mismatch"
|
||||
RESOLUTION_ISSUE = "resolution_issue"
|
||||
DUPLICATE_OWNERSHIP = "duplicate_ownership"
|
||||
OBJECT_PATCH_CONFLICT = "object_patch_conflict"
|
||||
TARGET_NOT_FOUND = "target_not_found"
|
||||
MODULE_UNSUPPORTED = "module_unsupported"
|
||||
PARAMETER_NOT_FOUND = "parameter_not_found"
|
||||
PARAMETER_INVALID = "parameter_invalid"
|
||||
SHAPE_INCOMPATIBLE = "shape_incompatible"
|
||||
OPERATION_REJECTED = "operation_rejected"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalLoraBindingIssue:
|
||||
"""Describe one adapter/target-scoped binding failure."""
|
||||
|
||||
composition_index: int | None
|
||||
target_index: int | None
|
||||
parameter_path: str | None
|
||||
code: RegionalLoraBindingIssueCode
|
||||
message: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class BoundRegionalLoraOperation:
|
||||
"""Pair U2/U3 evidence with exact observed module and parameter identities."""
|
||||
|
||||
descriptor: ResolvedRegionalLoraOperation
|
||||
normalized_target: ComfyNormalizedAdapterTarget
|
||||
module: object | None
|
||||
parameter: object | None
|
||||
module_class: BoundRegionalLoraModuleClass
|
||||
spatial_capability: BoundRegionalLoraSpatialCapability
|
||||
active_object_patch_paths: tuple[str, ...]
|
||||
issues: tuple[RegionalLoraBindingIssue, ...]
|
||||
|
||||
@property
|
||||
def bound(self) -> bool:
|
||||
"""Report whether this target has one executable, issue-free binding."""
|
||||
|
||||
return (
|
||||
not self.issues
|
||||
and self.module is not None
|
||||
and self.parameter is not None
|
||||
and self.module_class is not BoundRegionalLoraModuleClass.UNSUPPORTED
|
||||
and self.spatial_capability
|
||||
is not BoundRegionalLoraSpatialCapability.UNSUPPORTED
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalLoraBindingResult:
|
||||
"""Retain all ordered target evidence and aggregate all binding failures."""
|
||||
|
||||
entries: tuple[BoundRegionalLoraOperation, ...]
|
||||
issues: tuple[RegionalLoraBindingIssue, ...]
|
||||
|
||||
@property
|
||||
def admissible(self) -> bool:
|
||||
"""Require every target to bind and every aggregate issue to be absent."""
|
||||
|
||||
return not self.issues and all(entry.bound for entry in self.entries)
|
||||
@@ -0,0 +1,169 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Validate resolved ordinary-LoRA shapes against observed Torch operations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
|
||||
from ...domain.resolved_regional_lora import (
|
||||
ResolvedLoraOperationClass,
|
||||
ResolvedRegionalLoraOperation,
|
||||
)
|
||||
from .target_binding import BoundRegionalLoraModuleClass
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class TargetShapeAdmission:
|
||||
"""Retain one observed module class, parameter, and shape failure if any."""
|
||||
|
||||
module_class: BoundRegionalLoraModuleClass
|
||||
parameter: object | None
|
||||
issue: str | None
|
||||
|
||||
|
||||
class RegionalLoraTargetShapeValidator:
|
||||
"""Validate target modules and parameter shapes without tensor movement."""
|
||||
|
||||
def validate(
|
||||
self,
|
||||
descriptor: ResolvedRegionalLoraOperation,
|
||||
module: object,
|
||||
parameter: object,
|
||||
) -> TargetShapeAdmission:
|
||||
"""Return complete shape admission for one observed target module."""
|
||||
|
||||
module_class = _module_class(module)
|
||||
if module_class is BoundRegionalLoraModuleClass.UNSUPPORTED:
|
||||
return TargetShapeAdmission(
|
||||
module_class,
|
||||
None,
|
||||
f"Target module type {type(module).__name__} is unsupported.",
|
||||
)
|
||||
if not isinstance(parameter, (torch.Tensor, nn.Parameter)):
|
||||
return TargetShapeAdmission(
|
||||
module_class,
|
||||
parameter,
|
||||
"Target parameter must be a Torch tensor or Parameter.",
|
||||
)
|
||||
target_shape, offset_issue = _target_shape(parameter, descriptor.target.offset)
|
||||
if offset_issue is not None:
|
||||
return TargetShapeAdmission(module_class, parameter, offset_issue)
|
||||
issue = _shape_issue(descriptor, module, target_shape)
|
||||
return TargetShapeAdmission(module_class, parameter, issue)
|
||||
|
||||
|
||||
def _module_class(module: object) -> BoundRegionalLoraModuleClass:
|
||||
"""Classify exact supported Torch operation families."""
|
||||
|
||||
if isinstance(module, nn.Linear):
|
||||
return BoundRegionalLoraModuleClass.LINEAR
|
||||
if isinstance(module, nn.Conv1d):
|
||||
return BoundRegionalLoraModuleClass.CONVOLUTION_1D
|
||||
if isinstance(module, nn.Conv2d):
|
||||
return BoundRegionalLoraModuleClass.CONVOLUTION_2D
|
||||
if isinstance(module, nn.Conv3d):
|
||||
return BoundRegionalLoraModuleClass.CONVOLUTION_3D
|
||||
return BoundRegionalLoraModuleClass.UNSUPPORTED
|
||||
|
||||
|
||||
def _target_shape(
|
||||
parameter: torch.Tensor,
|
||||
offset: tuple[int, ...] | None,
|
||||
) -> tuple[tuple[int, ...], str | None]:
|
||||
"""Return the exact sliced parameter shape described by Comfy's target path."""
|
||||
|
||||
shape = tuple(int(dimension) for dimension in parameter.shape)
|
||||
if offset is None:
|
||||
return shape, None
|
||||
if len(offset) != 3:
|
||||
return shape, "Comfy target offset must contain dimension, start, and length."
|
||||
dimension, start, length = offset
|
||||
if dimension >= len(shape):
|
||||
return shape, "Comfy target offset dimension exceeds parameter rank."
|
||||
if length <= 0 or start + length > shape[dimension]:
|
||||
return shape, "Comfy target offset exceeds the parameter dimension."
|
||||
sliced = list(shape)
|
||||
sliced[dimension] = length
|
||||
return tuple(sliced), None
|
||||
|
||||
|
||||
def _shape_issue(
|
||||
descriptor: ResolvedRegionalLoraOperation,
|
||||
module: object,
|
||||
target_shape: tuple[int, ...],
|
||||
) -> str | None:
|
||||
"""Validate low-rank composition against the observed operation shape."""
|
||||
|
||||
if descriptor.down_shape is None or descriptor.up_shape is None:
|
||||
return "Supported descriptor is missing down/up shapes."
|
||||
expected_target_shape = (
|
||||
descriptor.reshape_shape.dimensions
|
||||
if descriptor.reshape_shape is not None
|
||||
else target_shape
|
||||
)
|
||||
if descriptor.reshape_shape is not None:
|
||||
if len(expected_target_shape) != len(target_shape):
|
||||
return "Authored reshape rank must match the target parameter rank."
|
||||
if any(
|
||||
authored < observed
|
||||
for authored, observed in zip(
|
||||
expected_target_shape, target_shape, strict=True
|
||||
)
|
||||
):
|
||||
return "Authored reshape cannot shrink an observed target dimension."
|
||||
down = descriptor.down_shape.dimensions
|
||||
up = descriptor.up_shape.dimensions
|
||||
if up[0] != expected_target_shape[0]:
|
||||
return "LoRA up output channels do not match the target parameter."
|
||||
if isinstance(module, nn.Linear):
|
||||
if descriptor.operation_class is not ResolvedLoraOperationClass.MATRIX_PAIR:
|
||||
return "Linear target requires a matrix-pair LoRA descriptor."
|
||||
if len(expected_target_shape) != 2 or down[1] != expected_target_shape[1]:
|
||||
return "LoRA down input features do not match the linear parameter."
|
||||
return None
|
||||
if not isinstance(module, (nn.Conv1d, nn.Conv2d, nn.Conv3d)):
|
||||
return "Target module type is unsupported."
|
||||
convolution_dimension = module.weight.ndim - 2
|
||||
expected_class = {
|
||||
1: ResolvedLoraOperationClass.CONVOLUTION_1D,
|
||||
2: ResolvedLoraOperationClass.CONVOLUTION_2D,
|
||||
3: ResolvedLoraOperationClass.CONVOLUTION_3D,
|
||||
}[convolution_dimension]
|
||||
if descriptor.operation_class not in (
|
||||
ResolvedLoraOperationClass.MATRIX_PAIR,
|
||||
expected_class,
|
||||
):
|
||||
return "LoRA convolution dimensionality does not match the target module."
|
||||
flattened_input = math.prod(expected_target_shape[1:])
|
||||
if len(down) == 2:
|
||||
if down[1] != flattened_input:
|
||||
return "Matrix LoRA down input size does not match the convolution weight."
|
||||
elif descriptor.middle_shape is None:
|
||||
if tuple(down[1:]) != tuple(expected_target_shape[1:]):
|
||||
return "Convolutional LoRA down shape does not match the target weight."
|
||||
elif (
|
||||
down[1] != expected_target_shape[1]
|
||||
or any(kernel != 1 for kernel in down[2:])
|
||||
or tuple(descriptor.middle_shape.dimensions[2:])
|
||||
!= tuple(expected_target_shape[2:])
|
||||
):
|
||||
return "LoCon down/middle shapes do not compose to the target weight."
|
||||
spatial_rank = convolution_dimension + 2
|
||||
if len(up) not in (2, spatial_rank):
|
||||
return "Convolutional LoRA up tensor dimensionality is incompatible."
|
||||
if len(up) == spatial_rank and any(kernel != 1 for kernel in up[2:]):
|
||||
return "Convolutional LoRA up projection must use unit spatial kernels."
|
||||
middle = descriptor.middle_shape
|
||||
if middle is not None and middle.rank not in (2, spatial_rank):
|
||||
return "Convolutional LoRA middle dimensionality is incompatible."
|
||||
return None
|
||||
|
||||
|
||||
REGIONAL_LORA_TARGET_SHAPE_VALIDATOR = RegionalLoraTargetShapeValidator()
|
||||
@@ -0,0 +1,63 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Snapshot Comfy WeightHook payload state without resolving or mutating it."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from comfy.hooks import WeightHook
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RegionalLoraHostPayload:
|
||||
"""Retain one WeightHook's exact raw or initialized payload identities."""
|
||||
|
||||
needs_resolution: bool
|
||||
raw_weights: object | None
|
||||
model_weights: object | None
|
||||
clip_weights: object | None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require one unambiguous raw or initialized host representation."""
|
||||
|
||||
if not isinstance(self.needs_resolution, bool):
|
||||
raise TypeError("Regional LoRA payload resolution state must be boolean.")
|
||||
if self.needs_resolution:
|
||||
if self.raw_weights is None:
|
||||
raise ValueError(
|
||||
"Unresolved regional LoRA payload requires raw weights."
|
||||
)
|
||||
if self.model_weights is not None or self.clip_weights is not None:
|
||||
raise ValueError(
|
||||
"Unresolved regional LoRA payload cannot contain "
|
||||
"initialized weights."
|
||||
)
|
||||
elif self.raw_weights is not None:
|
||||
raise ValueError(
|
||||
"Initialized regional LoRA payload cannot retain raw weights."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_hook(cls, hook: WeightHook) -> RegionalLoraHostPayload:
|
||||
"""Copy payload references from one installed Comfy WeightHook."""
|
||||
|
||||
if not isinstance(hook, WeightHook):
|
||||
raise TypeError("Regional LoRA host payload requires a WeightHook.")
|
||||
if hook.need_weight_init:
|
||||
return cls(True, hook.weights, None, None)
|
||||
return cls(False, None, hook.weights, hook.weights_clip)
|
||||
|
||||
@classmethod
|
||||
def unresolved(cls, weights: object) -> RegionalLoraHostPayload:
|
||||
"""Declare one raw model-and-clip payload requiring Comfy resolution."""
|
||||
|
||||
return cls(True, weights, None, None)
|
||||
|
||||
@property
|
||||
def model_side_weights(self) -> object | None:
|
||||
"""Return the exact payload Comfy presents to the model side."""
|
||||
|
||||
return self.raw_weights if self.needs_resolution else self.model_weights
|
||||
@@ -24,6 +24,7 @@ from ..domain.regional_lora_plan import (
|
||||
RegionalLoraPlan,
|
||||
RegionalLoraScheduleBoundary,
|
||||
)
|
||||
from .regional_lora_host_payload import RegionalLoraHostPayload
|
||||
from .regional_model_hook_selection import (
|
||||
REGIONAL_MODEL_HOOK_SELECTOR,
|
||||
RegionalModelHookSelector,
|
||||
@@ -66,18 +67,23 @@ class RegionalLoraPlanAdaptation:
|
||||
"""Retain one immutable plan with its exact ordered hook weight payloads."""
|
||||
|
||||
plan: RegionalLoraPlan
|
||||
adapter_weights: tuple[object, ...]
|
||||
adapter_payloads: tuple[RegionalLoraHostPayload, ...]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""Require exact plan types and one payload per canonical adapter."""
|
||||
|
||||
if not isinstance(self.plan, RegionalLoraPlan):
|
||||
raise TypeError("Regional LoRA adaptation requires a plan.")
|
||||
if not isinstance(self.adapter_weights, tuple):
|
||||
raise TypeError("Regional LoRA adaptation weights must be a tuple.")
|
||||
if len(self.adapter_weights) != len(self.plan.adapters):
|
||||
if not isinstance(self.adapter_payloads, tuple):
|
||||
raise TypeError("Regional LoRA adaptation payloads must be a tuple.")
|
||||
if any(
|
||||
not isinstance(payload, RegionalLoraHostPayload)
|
||||
for payload in self.adapter_payloads
|
||||
):
|
||||
raise TypeError("Regional LoRA adaptation contains an invalid payload.")
|
||||
if len(self.adapter_payloads) != len(self.plan.adapters):
|
||||
raise ValueError(
|
||||
"Regional LoRA adaptation requires one weight payload per adapter."
|
||||
"Regional LoRA adaptation requires one host payload per adapter."
|
||||
)
|
||||
|
||||
|
||||
@@ -105,23 +111,23 @@ class RegionalLoraPlanAdapter:
|
||||
return RegionalLoraPlanAdaptation(EMPTY_REGIONAL_LORA_PLAN, ())
|
||||
|
||||
adapters: list[RegionalLoraAdapterPlan] = []
|
||||
adapter_weights: list[object] = []
|
||||
adapter_payloads: list[RegionalLoraHostPayload] = []
|
||||
for source_index, source in enumerate(source_values):
|
||||
if not isinstance(source, RegionalLoraHookSource):
|
||||
raise TypeError(
|
||||
f"Regional LoRA source {source_index} has an invalid type."
|
||||
)
|
||||
source_adapters, source_weights = self._adapt_source(
|
||||
source_adapters, source_payloads = self._adapt_source(
|
||||
source,
|
||||
model=model,
|
||||
first_composition_index=len(adapters),
|
||||
source_index=source_index,
|
||||
)
|
||||
adapters.extend(source_adapters)
|
||||
adapter_weights.extend(source_weights)
|
||||
adapter_payloads.extend(source_payloads)
|
||||
return RegionalLoraPlanAdaptation(
|
||||
RegionalLoraPlan(adapters=tuple(adapters)),
|
||||
tuple(adapter_weights),
|
||||
tuple(adapter_payloads),
|
||||
)
|
||||
|
||||
def _adapt_source(
|
||||
@@ -131,7 +137,10 @@ class RegionalLoraPlanAdapter:
|
||||
model: object,
|
||||
first_composition_index: int,
|
||||
source_index: int,
|
||||
) -> tuple[tuple[RegionalLoraAdapterPlan, ...], tuple[object, ...]]:
|
||||
) -> tuple[
|
||||
tuple[RegionalLoraAdapterPlan, ...],
|
||||
tuple[RegionalLoraHostPayload, ...],
|
||||
]:
|
||||
"""Adapt one exact HookGroup while retaining hook order and payloads."""
|
||||
|
||||
selection = self._hook_selector.select(
|
||||
@@ -162,7 +171,8 @@ class RegionalLoraPlanAdapter:
|
||||
for participant_index, participant in enumerate(selection.model_hooks)
|
||||
)
|
||||
return adapters, tuple(
|
||||
participant.hook.weights for participant in selection.model_hooks
|
||||
RegionalLoraHostPayload.from_hook(participant.hook)
|
||||
for participant in selection.model_hooks
|
||||
)
|
||||
|
||||
def _schedule(
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Define shared callable types for model-function wrapper composition."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Execute SEGS-guided tiled diffusion across a latent batch."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Execute tiled diffusion once per latent conditioning batch item."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Select one tiled diffusion runtime for one latent sampling request."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Adapt Anima diffusion calls to shared schedule and CFG invariants."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Supply exact Anima identity to shared diagnostic invariant execution."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Adapt Anima cross-attention to the shared invariant test contract."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Adapt Anima model derivation to shared lifecycle invariants."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Construct the exact allocation-free Anima lifecycle fixture."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build explicit compact Anima regional branch invocations for tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build minimal complete P0.7 evidence for focused owner tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build installed allocation-free Anima graphs for surface tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build exact synthetic P9.6 host rejection evidence for focused tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build shared diagnostic observations for backend-specific test adapters."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Define backend-neutral Attention Coupling invariant test values."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build canonical backend-neutral Attention Coupling invariant values."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build deterministic P9.1 evidence values from the validated P0.8 baseline."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build explicit regional-attention batch values for focused tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Build explicit static regional LoRA schedule state for focused tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the accepted P5/P6/P7 scheduled Anima conditioning graph."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the complete P5.9 public-node matrix and evidence owners."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify focused Anima Attention Coupling family policy."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove Attention Coupling projected state follows Comfy MODEL detach."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify focused common diagnostics for attention-only Anima execution."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the complete P7.8 combined public-node evidence owners."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify Anima global prompt sharing throughout active regions."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify bounded immutable Anima diagnostic snapshot caching."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove Comfy host weight state reaches regional Anima linear backing."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify fail-closed safetensors inventory for Anima LoRA pairs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify inactive regional LoRA schedules bypass only block-owned work."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify fixed input selection for the thin P0.7 coordinator."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the complete pinned ADAPTER_A characterization matrix."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify ordinary global Comfy graphs for pinned ADAPTER_A characterization."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify exact task-local reuse of repeated-adapter combined multipliers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify mandatory target, ordering, schedule, and digest evidence."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify strict P0.7 Comfy history decoding."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify atomic, resumable, complete P0.7 result persistence."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify complete authoritative Anima multiplier category classification."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the shared managed Anima model-modifier graph boundary."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove source-visible Anima discovery across installed clone replacements."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove P9.5 terminal history narrowing and exact acceptance policy."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove ordered durable P9.5 result publication and cleanup gating."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove exact P9.5 managed matrix and public graph construction."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify installed Sage override delegation and observation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove regional-LoRA device state follows Comfy MODEL detach lifecycle."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify isolated VRAM measurement, acceptance, and publication."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify isolated profile ordering and cleanup coordination."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify one-profile isolated fixture assembly and cleanup ownership."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify complete deterministic PyTorch aggregate decoding."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify pinned Anima performance artifact role and byte identity."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove the immutable P5.7 zero/one/four benchmark contract."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify exact denoiser-sequence measurement and failure boundaries."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify pinned P5.7 definition-to-adapter-use translation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify complete Anima operator-profile evidence publication."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove complete P5.7 median overhead and denoiser-count evaluation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the thin Anima performance matrix coordinator."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify shared Anima performance-suite assembly and cleanup."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove all-or-nothing regional Anima adapter-plan admission."""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -19,6 +23,7 @@ from simple_syrup.runtime.regional_lora.anima_plan_admission import (
|
||||
ANIMA_REGIONAL_LORA_PLAN_ADMISSION_SERVICE,
|
||||
AnimaRegionalLoraPlanAdmissionError,
|
||||
)
|
||||
from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload
|
||||
from simple_syrup.runtime.regional_lora_plan_adapter import (
|
||||
REGIONAL_LORA_PLAN_ADAPTER,
|
||||
RegionalLoraHookSource,
|
||||
@@ -95,7 +100,10 @@ def test_plan_admission_aggregates_every_adapter_format_and_target_failure() ->
|
||||
ANIMA_REGIONAL_LORA_PLAN_ADMISSION_SERVICE.admit(
|
||||
RegionalLoraPlanAdaptation(
|
||||
plan,
|
||||
({}, converted_model_patch, unsupported_target),
|
||||
tuple(
|
||||
RegionalLoraHostPayload.unresolved(weights)
|
||||
for weights in ({}, converted_model_patch, unsupported_target)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -117,7 +125,7 @@ def test_plan_admission_requires_one_weight_mapping_per_ordered_adapter() -> Non
|
||||
|
||||
plan = _plan((RegionalLoraScheduleBoundary(0.0, 100.0, 1.0, 0),))
|
||||
|
||||
with pytest.raises(ValueError, match="one weight payload"):
|
||||
with pytest.raises(ValueError, match="one host payload"):
|
||||
RegionalLoraPlanAdaptation(plan, ())
|
||||
with pytest.raises(TypeError, match="requires an adaptation"):
|
||||
ANIMA_REGIONAL_LORA_PLAN_ADMISSION_SERVICE.admit(cast(Any, plan))
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify generic Anima benchmark runtime derivation boundaries."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the scaling CLI publishes its complete evaluated result."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify deterministic P10.1 contexts, masks, and tensor inputs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify the complete immutable P10.1 scaling declaration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify per-repeat scaling cache, work, tensor, and backend evidence."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify scaling definitions translate into explicit runtime adapter uses."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify selected raw scaling traces and operator summaries."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify complete scaling acceptance and atomic JSON publication."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify interleaved scaling warmup, repeats, and environment coordination."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify complete scaling-suite assembly, alignment, and cleanup."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Verify one shared declared-to-observed scaling work contract."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
||||
# Copyright (C) 2026 Artificial Sweetener and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
"""Prove exact Anima regional-LoRA target ownership classification."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user