diff --git a/simple_syrup/domain/global_context_schedule.py b/simple_syrup/domain/global_context_schedule.py index ff287fb..6d35150 100644 --- a/simple_syrup/domain/global_context_schedule.py +++ b/simple_syrup/domain/global_context_schedule.py @@ -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 diff --git a/simple_syrup/domain/regional_activation_geometry.py b/simple_syrup/domain/regional_activation_geometry.py new file mode 100644 index 0000000..9a118c2 --- /dev/null +++ b/simple_syrup/domain/regional_activation_geometry.py @@ -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.") diff --git a/simple_syrup/domain/resolved_regional_lora.py b/simple_syrup/domain/resolved_regional_lora.py new file mode 100644 index 0000000..be9f017 --- /dev/null +++ b/simple_syrup/domain/resolved_regional_lora.py @@ -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.") diff --git a/simple_syrup/masking/regional_activation_mask_projection.py b/simple_syrup/masking/regional_activation_mask_projection.py new file mode 100644 index 0000000..a037154 --- /dev/null +++ b/simple_syrup/masking/regional_activation_mask_projection.py @@ -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() diff --git a/simple_syrup/runtime/contextual_model_wrapper.py b/simple_syrup/runtime/contextual_model_wrapper.py index 7c1c83d..e9f61cb 100644 --- a/simple_syrup/runtime/contextual_model_wrapper.py +++ b/simple_syrup/runtime/contextual_model_wrapper.py @@ -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 diff --git a/simple_syrup/runtime/model_object_patch_batch.py b/simple_syrup/runtime/model_object_patch_batch.py new file mode 100644 index 0000000..e136381 --- /dev/null +++ b/simple_syrup/runtime/model_object_patch_batch.py @@ -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)) diff --git a/simple_syrup/runtime/regional_lora/__init__.py b/simple_syrup/runtime/regional_lora/__init__.py index fffa873..c09b186 100644 --- a/simple_syrup/runtime/regional_lora/__init__.py +++ b/simple_syrup/runtime/regional_lora/__init__.py @@ -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.""" diff --git a/simple_syrup/runtime/regional_lora/activation_batch_alignment.py b/simple_syrup/runtime/regional_lora/activation_batch_alignment.py new file mode 100644 index 0000000..076d9ef --- /dev/null +++ b/simple_syrup/runtime/regional_lora/activation_batch_alignment.py @@ -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() +) diff --git a/simple_syrup/runtime/regional_lora/activation_geometry_providers.py b/simple_syrup/runtime/regional_lora/activation_geometry_providers.py new file mode 100644 index 0000000..7ab413c --- /dev/null +++ b/simple_syrup/runtime/regional_lora/activation_geometry_providers.py @@ -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.") diff --git a/simple_syrup/runtime/regional_lora/anima_plan_admission.py b/simple_syrup/runtime/regional_lora/anima_plan_admission.py index ac1a1e6..c858e20 100644 --- a/simple_syrup/runtime/regional_lora/anima_plan_admission.py +++ b/simple_syrup/runtime/regional_lora/anima_plan_admission.py @@ -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: diff --git a/simple_syrup/runtime/regional_lora/comfy_adapter_evidence.py b/simple_syrup/runtime/regional_lora/comfy_adapter_evidence.py new file mode 100644 index 0000000..2fc2a67 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/comfy_adapter_evidence.py @@ -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 diff --git a/simple_syrup/runtime/regional_lora/comfy_adapter_resolution.py b/simple_syrup/runtime/regional_lora/comfy_adapter_resolution.py new file mode 100644 index 0000000..f6437c4 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/comfy_adapter_resolution.py @@ -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, ...] diff --git a/simple_syrup/runtime/regional_lora/comfy_adapter_resolver.py b/simple_syrup/runtime/regional_lora/comfy_adapter_resolver.py new file mode 100644 index 0000000..7f62b9b --- /dev/null +++ b/simple_syrup/runtime/regional_lora/comfy_adapter_resolver.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/convolution_execution.py b/simple_syrup/runtime/regional_lora/convolution_execution.py new file mode 100644 index 0000000..2489745 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/convolution_execution.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/convolution_execution_plan.py b/simple_syrup/runtime/regional_lora/convolution_execution_plan.py new file mode 100644 index 0000000..012ef70 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/convolution_execution_plan.py @@ -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.") diff --git a/simple_syrup/runtime/regional_lora/convolution_preparation.py b/simple_syrup/runtime/regional_lora/convolution_preparation.py new file mode 100644 index 0000000..c63282c --- /dev/null +++ b/simple_syrup/runtime/regional_lora/convolution_preparation.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/effective_model_graph.py b/simple_syrup/runtime/regional_lora/effective_model_graph.py new file mode 100644 index 0000000..b5fd15f --- /dev/null +++ b/simple_syrup/runtime/regional_lora/effective_model_graph.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/host_operation_backing.py b/simple_syrup/runtime/regional_lora/host_operation_backing.py new file mode 100644 index 0000000..c2ec672 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/host_operation_backing.py @@ -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", + } +) diff --git a/simple_syrup/runtime/regional_lora/linear_execution.py b/simple_syrup/runtime/regional_lora/linear_execution.py new file mode 100644 index 0000000..82d28d5 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/linear_execution.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/linear_execution_plan.py b/simple_syrup/runtime/regional_lora/linear_execution_plan.py new file mode 100644 index 0000000..ecb454e --- /dev/null +++ b/simple_syrup/runtime/regional_lora/linear_execution_plan.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/operation_cache_lifecycle.py b/simple_syrup/runtime/regional_lora/operation_cache_lifecycle.py new file mode 100644 index 0000000..71fce98 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/operation_cache_lifecycle.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/operation_installation_mutation.py b/simple_syrup/runtime/regional_lora/operation_installation_mutation.py new file mode 100644 index 0000000..1c892f6 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/operation_installation_mutation.py @@ -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 diff --git a/simple_syrup/runtime/regional_lora/operation_invocation.py b/simple_syrup/runtime/regional_lora/operation_invocation.py new file mode 100644 index 0000000..20087fc --- /dev/null +++ b/simple_syrup/runtime/regional_lora/operation_invocation.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/operation_mutation_assembly.py b/simple_syrup/runtime/regional_lora/operation_mutation_assembly.py new file mode 100644 index 0000000..3dc7389 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/operation_mutation_assembly.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/resolved_operation_translator.py b/simple_syrup/runtime/regional_lora/resolved_operation_translator.py new file mode 100644 index 0000000..33e4698 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/resolved_operation_translator.py @@ -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 "", + parameter_name="", + 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() diff --git a/simple_syrup/runtime/regional_lora/target_binder.py b/simple_syrup/runtime/regional_lora/target_binder.py new file mode 100644 index 0000000..b53793f --- /dev/null +++ b/simple_syrup/runtime/regional_lora/target_binder.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora/target_binding.py b/simple_syrup/runtime/regional_lora/target_binding.py new file mode 100644 index 0000000..a0c182b --- /dev/null +++ b/simple_syrup/runtime/regional_lora/target_binding.py @@ -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) diff --git a/simple_syrup/runtime/regional_lora/target_shape_admission.py b/simple_syrup/runtime/regional_lora/target_shape_admission.py new file mode 100644 index 0000000..6520dc3 --- /dev/null +++ b/simple_syrup/runtime/regional_lora/target_shape_admission.py @@ -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() diff --git a/simple_syrup/runtime/regional_lora_host_payload.py b/simple_syrup/runtime/regional_lora_host_payload.py new file mode 100644 index 0000000..3ce06db --- /dev/null +++ b/simple_syrup/runtime/regional_lora_host_payload.py @@ -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 diff --git a/simple_syrup/runtime/regional_lora_plan_adapter.py b/simple_syrup/runtime/regional_lora_plan_adapter.py index 91a5563..bf7a54e 100644 --- a/simple_syrup/runtime/regional_lora_plan_adapter.py +++ b/simple_syrup/runtime/regional_lora_plan_adapter.py @@ -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( diff --git a/simple_syrup/runtime/sampling_model_types.py b/simple_syrup/runtime/sampling_model_types.py index af5bc82..7132176 100644 --- a/simple_syrup/runtime/sampling_model_types.py +++ b/simple_syrup/runtime/sampling_model_types.py @@ -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 diff --git a/simple_syrup/services/segs_guided_tiled_diffusion_sampling_service.py b/simple_syrup/services/segs_guided_tiled_diffusion_sampling_service.py index 66e721d..b55c3ac 100644 --- a/simple_syrup/services/segs_guided_tiled_diffusion_sampling_service.py +++ b/simple_syrup/services/segs_guided_tiled_diffusion_sampling_service.py @@ -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 diff --git a/simple_syrup/services/tiled_diffusion_conditioning_batch_service.py b/simple_syrup/services/tiled_diffusion_conditioning_batch_service.py index cf38a78..8253aa9 100644 --- a/simple_syrup/services/tiled_diffusion_conditioning_batch_service.py +++ b/simple_syrup/services/tiled_diffusion_conditioning_batch_service.py @@ -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 diff --git a/simple_syrup/services/tiled_diffusion_item_sampling_service.py b/simple_syrup/services/tiled_diffusion_item_sampling_service.py index 8d0fd5c..4c9cb1c 100644 --- a/simple_syrup/services/tiled_diffusion_item_sampling_service.py +++ b/simple_syrup/services/tiled_diffusion_item_sampling_service.py @@ -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 diff --git a/tests/anima_attention_coupling_call_harness.py b/tests/anima_attention_coupling_call_harness.py index 2c00a9d..4d9af5c 100644 --- a/tests/anima_attention_coupling_call_harness.py +++ b/tests/anima_attention_coupling_call_harness.py @@ -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 diff --git a/tests/anima_attention_coupling_diagnostics_harness.py b/tests/anima_attention_coupling_diagnostics_harness.py index 20fe681..3e36d0e 100644 --- a/tests/anima_attention_coupling_diagnostics_harness.py +++ b/tests/anima_attention_coupling_diagnostics_harness.py @@ -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 diff --git a/tests/anima_attention_coupling_invariant_harness.py b/tests/anima_attention_coupling_invariant_harness.py index 9e761ca..23f8d56 100644 --- a/tests/anima_attention_coupling_invariant_harness.py +++ b/tests/anima_attention_coupling_invariant_harness.py @@ -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 diff --git a/tests/anima_attention_coupling_lifecycle_harness.py b/tests/anima_attention_coupling_lifecycle_harness.py index 4e2b02a..c6b4551 100644 --- a/tests/anima_attention_coupling_lifecycle_harness.py +++ b/tests/anima_attention_coupling_lifecycle_harness.py @@ -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 diff --git a/tests/anima_attention_coupling_lifecycle_values.py b/tests/anima_attention_coupling_lifecycle_values.py index 7e759e7..f77ab38 100644 --- a/tests/anima_attention_coupling_lifecycle_values.py +++ b/tests/anima_attention_coupling_lifecycle_values.py @@ -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 diff --git a/tests/anima_branch_test_values.py b/tests/anima_branch_test_values.py index b9b2dff..70ace26 100644 --- a/tests/anima_branch_test_values.py +++ b/tests/anima_branch_test_values.py @@ -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 diff --git a/tests/anima_lora_characterization_fixtures.py b/tests/anima_lora_characterization_fixtures.py index 91e7116..bac165d 100644 --- a/tests/anima_lora_characterization_fixtures.py +++ b/tests/anima_lora_characterization_fixtures.py @@ -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 diff --git a/tests/anima_module_surface_fixtures.py b/tests/anima_module_surface_fixtures.py index 3e05ac0..ddbee97 100644 --- a/tests/anima_module_surface_fixtures.py +++ b/tests/anima_module_surface_fixtures.py @@ -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 diff --git a/tests/anima_nondiffusion_rejection_values.py b/tests/anima_nondiffusion_rejection_values.py index a83ca1b..c47c8e8 100644 --- a/tests/anima_nondiffusion_rejection_values.py +++ b/tests/anima_nondiffusion_rejection_values.py @@ -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 diff --git a/tests/attention_coupling_diagnostics_harness.py b/tests/attention_coupling_diagnostics_harness.py index e478fc1..03c6e5f 100644 --- a/tests/attention_coupling_diagnostics_harness.py +++ b/tests/attention_coupling_diagnostics_harness.py @@ -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 diff --git a/tests/attention_coupling_invariant_contract.py b/tests/attention_coupling_invariant_contract.py index 71145a2..382d985 100644 --- a/tests/attention_coupling_invariant_contract.py +++ b/tests/attention_coupling_invariant_contract.py @@ -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 diff --git a/tests/attention_coupling_invariant_values.py b/tests/attention_coupling_invariant_values.py index 0ef4759..b4bf820 100644 --- a/tests/attention_coupling_invariant_values.py +++ b/tests/attention_coupling_invariant_values.py @@ -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 diff --git a/tests/prompt_control_attention_coupling_values.py b/tests/prompt_control_attention_coupling_values.py index c2aeec5..a57fff2 100644 --- a/tests/prompt_control_attention_coupling_values.py +++ b/tests/prompt_control_attention_coupling_values.py @@ -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 diff --git a/tests/regional_attention_test_values.py b/tests/regional_attention_test_values.py index 6a61c33..b3dfb30 100644 --- a/tests/regional_attention_test_values.py +++ b/tests/regional_attention_test_values.py @@ -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 diff --git a/tests/regional_lora_test_values.py b/tests/regional_lora_test_values.py index de931d4..cfcb73a 100644 --- a/tests/regional_lora_test_values.py +++ b/tests/regional_lora_test_values.py @@ -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 diff --git a/tests/test_anima_attention_coupling_conditioning.py b/tests/test_anima_attention_coupling_conditioning.py index 7b3f3e0..b8237c0 100644 --- a/tests/test_anima_attention_coupling_conditioning.py +++ b/tests/test_anima_attention_coupling_conditioning.py @@ -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 diff --git a/tests/test_anima_attention_coupling_integration.py b/tests/test_anima_attention_coupling_integration.py index 279bb87..5564616 100644 --- a/tests/test_anima_attention_coupling_integration.py +++ b/tests/test_anima_attention_coupling_integration.py @@ -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 diff --git a/tests/test_anima_attention_coupling_model_family.py b/tests/test_anima_attention_coupling_model_family.py index 27d17ec..ab7b09f 100644 --- a/tests/test_anima_attention_coupling_model_family.py +++ b/tests/test_anima_attention_coupling_model_family.py @@ -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 diff --git a/tests/test_anima_attention_device_cache_lifecycle.py b/tests/test_anima_attention_device_cache_lifecycle.py index ad6ae49..49db1f1 100644 --- a/tests/test_anima_attention_device_cache_lifecycle.py +++ b/tests/test_anima_attention_device_cache_lifecycle.py @@ -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 diff --git a/tests/test_anima_attention_only_diagnostics.py b/tests/test_anima_attention_only_diagnostics.py index 8a00e96..ec93912 100644 --- a/tests/test_anima_attention_only_diagnostics.py +++ b/tests/test_anima_attention_only_diagnostics.py @@ -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 diff --git a/tests/test_anima_contextual_attention_coupling_integration.py b/tests/test_anima_contextual_attention_coupling_integration.py index df828ec..67f9b4a 100644 --- a/tests/test_anima_contextual_attention_coupling_integration.py +++ b/tests/test_anima_contextual_attention_coupling_integration.py @@ -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 diff --git a/tests/test_anima_cross_attention_weights.py b/tests/test_anima_cross_attention_weights.py index d1dfdf2..1039b66 100644 --- a/tests/test_anima_cross_attention_weights.py +++ b/tests/test_anima_cross_attention_weights.py @@ -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 diff --git a/tests/test_anima_diagnostics_cache.py b/tests/test_anima_diagnostics_cache.py index ba71d73..2b6cb19 100644 --- a/tests/test_anima_diagnostics_cache.py +++ b/tests/test_anima_diagnostics_cache.py @@ -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 diff --git a/tests/test_anima_host_weight_interop.py b/tests/test_anima_host_weight_interop.py index 446beb3..87709d6 100644 --- a/tests/test_anima_host_weight_interop.py +++ b/tests/test_anima_host_weight_interop.py @@ -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 diff --git a/tests/test_anima_lora_artifact_inventory.py b/tests/test_anima_lora_artifact_inventory.py index 83c4e12..01679ae 100644 --- a/tests/test_anima_lora_artifact_inventory.py +++ b/tests/test_anima_lora_artifact_inventory.py @@ -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 diff --git a/tests/test_anima_lora_block_inactive_schedule.py b/tests/test_anima_lora_block_inactive_schedule.py index 6dd1c5e..8eefaf8 100644 --- a/tests/test_anima_lora_block_inactive_schedule.py +++ b/tests/test_anima_lora_block_inactive_schedule.py @@ -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 diff --git a/tests/test_anima_lora_characterization_cli.py b/tests/test_anima_lora_characterization_cli.py index 8cb2682..f905c90 100644 --- a/tests/test_anima_lora_characterization_cli.py +++ b/tests/test_anima_lora_characterization_cli.py @@ -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 diff --git a/tests/test_anima_lora_characterization_matrix.py b/tests/test_anima_lora_characterization_matrix.py index 806e251..efb3fe5 100644 --- a/tests/test_anima_lora_characterization_matrix.py +++ b/tests/test_anima_lora_characterization_matrix.py @@ -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 diff --git a/tests/test_anima_lora_characterization_workflow.py b/tests/test_anima_lora_characterization_workflow.py index f5b9a7b..a2d13bd 100644 --- a/tests/test_anima_lora_characterization_workflow.py +++ b/tests/test_anima_lora_characterization_workflow.py @@ -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 diff --git a/tests/test_anima_lora_combined_multiplier_cache.py b/tests/test_anima_lora_combined_multiplier_cache.py index 676e525..5f5d73d 100644 --- a/tests/test_anima_lora_combined_multiplier_cache.py +++ b/tests/test_anima_lora_combined_multiplier_cache.py @@ -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 diff --git a/tests/test_anima_lora_evidence_validation.py b/tests/test_anima_lora_evidence_validation.py index 2ae838a..37cb1ec 100644 --- a/tests/test_anima_lora_evidence_validation.py +++ b/tests/test_anima_lora_evidence_validation.py @@ -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 diff --git a/tests/test_anima_lora_history_outputs.py b/tests/test_anima_lora_history_outputs.py index 4a06beb..c0fc1b0 100644 --- a/tests/test_anima_lora_history_outputs.py +++ b/tests/test_anima_lora_history_outputs.py @@ -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 diff --git a/tests/test_anima_lora_results.py b/tests/test_anima_lora_results.py index f670d6a..7a656d3 100644 --- a/tests/test_anima_lora_results.py +++ b/tests/test_anima_lora_results.py @@ -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 diff --git a/tests/test_anima_lora_weight_categories.py b/tests/test_anima_lora_weight_categories.py index 538f963..ced53c7 100644 --- a/tests/test_anima_lora_weight_categories.py +++ b/tests/test_anima_lora_weight_categories.py @@ -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 diff --git a/tests/test_anima_model_modifier_workflow.py b/tests/test_anima_model_modifier_workflow.py index a5566a0..471f192 100644 --- a/tests/test_anima_model_modifier_workflow.py +++ b/tests/test_anima_model_modifier_workflow.py @@ -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 diff --git a/tests/test_anima_model_patcher_surface.py b/tests/test_anima_model_patcher_surface.py index 33d17b2..b0677f0 100644 --- a/tests/test_anima_model_patcher_surface.py +++ b/tests/test_anima_model_patcher_surface.py @@ -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 diff --git a/tests/test_anima_regional_lora_admission_history.py b/tests/test_anima_regional_lora_admission_history.py index 0dbaf83..1c786c8 100644 --- a/tests/test_anima_regional_lora_admission_history.py +++ b/tests/test_anima_regional_lora_admission_history.py @@ -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 diff --git a/tests/test_anima_regional_lora_admission_results.py b/tests/test_anima_regional_lora_admission_results.py index b2122f3..3bdab41 100644 --- a/tests/test_anima_regional_lora_admission_results.py +++ b/tests/test_anima_regional_lora_admission_results.py @@ -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 diff --git a/tests/test_anima_regional_lora_admission_workflow.py b/tests/test_anima_regional_lora_admission_workflow.py index 13b9745..c70396b 100644 --- a/tests/test_anima_regional_lora_admission_workflow.py +++ b/tests/test_anima_regional_lora_admission_workflow.py @@ -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 diff --git a/tests/test_anima_regional_lora_attention_backend.py b/tests/test_anima_regional_lora_attention_backend.py index 5d81cb9..e249d15 100644 --- a/tests/test_anima_regional_lora_attention_backend.py +++ b/tests/test_anima_regional_lora_attention_backend.py @@ -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 diff --git a/tests/test_anima_regional_lora_device_cache_lifecycle.py b/tests/test_anima_regional_lora_device_cache_lifecycle.py index 6cd2b26..97535c6 100644 --- a/tests/test_anima_regional_lora_device_cache_lifecycle.py +++ b/tests/test_anima_regional_lora_device_cache_lifecycle.py @@ -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 diff --git a/tests/test_anima_regional_lora_isolated_measurement.py b/tests/test_anima_regional_lora_isolated_measurement.py index 0308d9d..f2a604d 100644 --- a/tests/test_anima_regional_lora_isolated_measurement.py +++ b/tests/test_anima_regional_lora_isolated_measurement.py @@ -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 diff --git a/tests/test_anima_regional_lora_isolated_runner.py b/tests/test_anima_regional_lora_isolated_runner.py index 410aa4c..de4c689 100644 --- a/tests/test_anima_regional_lora_isolated_runner.py +++ b/tests/test_anima_regional_lora_isolated_runner.py @@ -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 diff --git a/tests/test_anima_regional_lora_isolated_suite.py b/tests/test_anima_regional_lora_isolated_suite.py index 3ec0817..40815d7 100644 --- a/tests/test_anima_regional_lora_isolated_suite.py +++ b/tests/test_anima_regional_lora_isolated_suite.py @@ -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 diff --git a/tests/test_anima_regional_lora_operator_capture.py b/tests/test_anima_regional_lora_operator_capture.py index 039352f..32b38d9 100644 --- a/tests/test_anima_regional_lora_operator_capture.py +++ b/tests/test_anima_regional_lora_operator_capture.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_artifacts.py b/tests/test_anima_regional_lora_performance_artifacts.py index f1923f8..67d134f 100644 --- a/tests/test_anima_regional_lora_performance_artifacts.py +++ b/tests/test_anima_regional_lora_performance_artifacts.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_manifest.py b/tests/test_anima_regional_lora_performance_manifest.py index a376675..1afd301 100644 --- a/tests/test_anima_regional_lora_performance_manifest.py +++ b/tests/test_anima_regional_lora_performance_manifest.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_measurement.py b/tests/test_anima_regional_lora_performance_measurement.py index 522092a..af67bd1 100644 --- a/tests/test_anima_regional_lora_performance_measurement.py +++ b/tests/test_anima_regional_lora_performance_measurement.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_profile.py b/tests/test_anima_regional_lora_performance_profile.py index fd3fe3e..51ee777 100644 --- a/tests/test_anima_regional_lora_performance_profile.py +++ b/tests/test_anima_regional_lora_performance_profile.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_profiling.py b/tests/test_anima_regional_lora_performance_profiling.py index c00cc0d..c592a8f 100644 --- a/tests/test_anima_regional_lora_performance_profiling.py +++ b/tests/test_anima_regional_lora_performance_profiling.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_results.py b/tests/test_anima_regional_lora_performance_results.py index 6caaccf..cb3cb03 100644 --- a/tests/test_anima_regional_lora_performance_results.py +++ b/tests/test_anima_regional_lora_performance_results.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_runner.py b/tests/test_anima_regional_lora_performance_runner.py index c17829e..a0db067 100644 --- a/tests/test_anima_regional_lora_performance_runner.py +++ b/tests/test_anima_regional_lora_performance_runner.py @@ -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 diff --git a/tests/test_anima_regional_lora_performance_suite.py b/tests/test_anima_regional_lora_performance_suite.py index e582565..d27008b 100644 --- a/tests/test_anima_regional_lora_performance_suite.py +++ b/tests/test_anima_regional_lora_performance_suite.py @@ -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 diff --git a/tests/test_anima_regional_lora_plan_admission.py b/tests/test_anima_regional_lora_plan_admission.py index 61d0956..7da8f11 100644 --- a/tests/test_anima_regional_lora_plan_admission.py +++ b/tests/test_anima_regional_lora_plan_admission.py @@ -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)) diff --git a/tests/test_anima_regional_lora_runtime_profile.py b/tests/test_anima_regional_lora_runtime_profile.py index 2e711bd..b136f8d 100644 --- a/tests/test_anima_regional_lora_runtime_profile.py +++ b/tests/test_anima_regional_lora_runtime_profile.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_cli.py b/tests/test_anima_regional_lora_scaling_cli.py index 48268de..dc3e8a6 100644 --- a/tests/test_anima_regional_lora_scaling_cli.py +++ b/tests/test_anima_regional_lora_scaling_cli.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_fixture.py b/tests/test_anima_regional_lora_scaling_fixture.py index c15975c..3790cf1 100644 --- a/tests/test_anima_regional_lora_scaling_fixture.py +++ b/tests/test_anima_regional_lora_scaling_fixture.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_manifest.py b/tests/test_anima_regional_lora_scaling_manifest.py index 93c1a1f..ace3d56 100644 --- a/tests/test_anima_regional_lora_scaling_manifest.py +++ b/tests/test_anima_regional_lora_scaling_manifest.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_measurement.py b/tests/test_anima_regional_lora_scaling_measurement.py index 5a5bf0b..c1ea478 100644 --- a/tests/test_anima_regional_lora_scaling_measurement.py +++ b/tests/test_anima_regional_lora_scaling_measurement.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_profile.py b/tests/test_anima_regional_lora_scaling_profile.py index 0c8ac9d..006402c 100644 --- a/tests/test_anima_regional_lora_scaling_profile.py +++ b/tests/test_anima_regional_lora_scaling_profile.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_profiling.py b/tests/test_anima_regional_lora_scaling_profiling.py index a9ba8f1..e9de936 100644 --- a/tests/test_anima_regional_lora_scaling_profiling.py +++ b/tests/test_anima_regional_lora_scaling_profiling.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_results.py b/tests/test_anima_regional_lora_scaling_results.py index 8a5b1f0..49fe671 100644 --- a/tests/test_anima_regional_lora_scaling_results.py +++ b/tests/test_anima_regional_lora_scaling_results.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_runner.py b/tests/test_anima_regional_lora_scaling_runner.py index 25dccf4..5fc372c 100644 --- a/tests/test_anima_regional_lora_scaling_runner.py +++ b/tests/test_anima_regional_lora_scaling_runner.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_suite.py b/tests/test_anima_regional_lora_scaling_suite.py index cafe2db..28f341c 100644 --- a/tests/test_anima_regional_lora_scaling_suite.py +++ b/tests/test_anima_regional_lora_scaling_suite.py @@ -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 diff --git a/tests/test_anima_regional_lora_scaling_work_contract.py b/tests/test_anima_regional_lora_scaling_work_contract.py index 2b90e5b..af09957 100644 --- a/tests/test_anima_regional_lora_scaling_work_contract.py +++ b/tests/test_anima_regional_lora_scaling_work_contract.py @@ -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 diff --git a/tests/test_anima_regional_lora_target_ownership.py b/tests/test_anima_regional_lora_target_ownership.py index d1b0413..cb4c794 100644 --- a/tests/test_anima_regional_lora_target_ownership.py +++ b/tests/test_anima_regional_lora_target_ownership.py @@ -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 diff --git a/tests/test_anima_regional_lora_targets.py b/tests/test_anima_regional_lora_targets.py index c90e205..ebb782f 100644 --- a/tests/test_anima_regional_lora_targets.py +++ b/tests/test_anima_regional_lora_targets.py @@ -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 standard adapter decoding and exhaustive Anima target admission.""" from __future__ import annotations diff --git a/tests/test_anima_regional_lora_vram_cli.py b/tests/test_anima_regional_lora_vram_cli.py index 7847a9a..fd23422 100644 --- a/tests/test_anima_regional_lora_vram_cli.py +++ b/tests/test_anima_regional_lora_vram_cli.py @@ -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 isolated VRAM CLI coordinates existing authorities only.""" from __future__ import annotations diff --git a/tests/test_anima_regional_nondiffusion_rejection_matrix.py b/tests/test_anima_regional_nondiffusion_rejection_matrix.py index daeec36..5205e58 100644 --- a/tests/test_anima_regional_nondiffusion_rejection_matrix.py +++ b/tests/test_anima_regional_nondiffusion_rejection_matrix.py @@ -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 exact P9.6 matrix and shared public graph construction.""" from __future__ import annotations diff --git a/tests/test_anima_regional_nondiffusion_rejection_results.py b/tests/test_anima_regional_nondiffusion_rejection_results.py index dcf7740..ae920c0 100644 --- a/tests/test_anima_regional_nondiffusion_rejection_results.py +++ b/tests/test_anima_regional_nondiffusion_rejection_results.py @@ -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.6 rejection-sidecar publication.""" from __future__ import annotations diff --git a/tests/test_anima_regional_nondiffusion_rejection_validation.py b/tests/test_anima_regional_nondiffusion_rejection_validation.py index 9b59299..b8b77da 100644 --- a/tests/test_anima_regional_nondiffusion_rejection_validation.py +++ b/tests/test_anima_regional_nondiffusion_rejection_validation.py @@ -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.6 history narrowing and owner-rejection policy.""" from __future__ import annotations diff --git a/tests/test_anima_regional_output_capture.py b/tests/test_anima_regional_output_capture.py index db7cf4b..fb6d96f 100644 --- a/tests/test_anima_regional_output_capture.py +++ b/tests/test_anima_regional_output_capture.py @@ -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 labeled regional Anima output workflows and result persistence.""" from __future__ import annotations diff --git a/tests/test_anima_regional_profile_probe.py b/tests/test_anima_regional_profile_probe.py index 0b8790b..3d126de 100644 --- a/tests/test_anima_regional_profile_probe.py +++ b/tests/test_anima_regional_profile_probe.py @@ -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 benchmark-only regional Anima visual-profile boundaries.""" from __future__ import annotations diff --git a/tests/test_anima_regression_oracle_artifact_validation.py b/tests/test_anima_regression_oracle_artifact_validation.py new file mode 100644 index 0000000..4f09f4a --- /dev/null +++ b/tests/test_anima_regression_oracle_artifact_validation.py @@ -0,0 +1,161 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify accepted Anima artifact validation fails closed.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path + +import pytest + +from tools.anima_regression_oracle.artifact_validation import ( + AcceptedArtifactValidator, +) +from tools.anima_regression_oracle.manifest import ( + AcceptedImage, + AnimaRegressionManifest, + CompletedJsonEvidence, + OracleCommand, + PerformanceEvidence, +) + + +def test_validator_requires_hashes_cleanup_counts_targets_and_performance( + tmp_path: Path, +) -> None: + """Accept only a complete baseline across every evidence authority.""" + + manifest = _manifest(tmp_path) + + observations = AcceptedArtifactValidator().validate(manifest) + + assert [item.identity for item in observations] == [ + "accepted-image", + "managed-result", + "accepted-performance-gate", + ] + + +@pytest.mark.parametrize( + ("mutation", "message"), + [ + ("image", "Accepted image hash changed"), + ("cleanup", "Managed cleanup failed"), + ("target", "target_count=448"), + ("performance", "performance result does not pass"), + ], +) +def test_validator_rejects_changed_accepted_evidence( + tmp_path: Path, + mutation: str, + message: str, +) -> None: + """Report the exact blocking evidence dimension that regressed.""" + + manifest = _manifest(tmp_path) + if mutation == "image": + manifest.images[0].path.write_bytes(b"changed") + elif mutation == "cleanup": + _write_json( + manifest.managed_results[0].path, + {"status": "completed", "cleanup_verified": False}, + ) + elif mutation == "target": + _write_json( + manifest.managed_results[0].detail_paths[0], + {"model_call_count": 30, "target_count": 447}, + ) + else: + payload = json.loads(manifest.performance.path.read_text(encoding="utf-8")) + payload["passed"] = False + _write_json(manifest.performance.path, payload) + + with pytest.raises(ValueError, match=message): + AcceptedArtifactValidator().validate(manifest) + + +def _manifest(root: Path) -> AnimaRegressionManifest: + """Write the smallest complete accepted-evidence fixture.""" + + image = root / "accepted.png" + image.write_bytes(b"accepted-image") + result = root / "result.json" + detail = root / "history.json" + performance = root / "performance.json" + _write_json( + result, + { + "status": "completed", + "cleanup_verified": True, + "observations": [{"case_id": "one"}], + "image_sha256": hashlib.sha256(image.read_bytes()).hexdigest(), + }, + ) + _write_json(detail, {"model_call_count": 30, "target_count": 448}) + _write_json( + performance, + { + "passed": True, + "environment": {"adapter_target_count": 448}, + "profiles": [ + { + "profile_id": "attention-only", + "maximum_overhead_percent": 0.0, + "passed": True, + }, + { + "profile_id": "regional-lora-1", + "maximum_overhead_percent": 15.0, + "passed": True, + }, + { + "profile_id": "regional-lora-4", + "maximum_overhead_percent": 35.0, + "passed": True, + }, + ], + }, + ) + empty = OracleCommand("empty", ("empty",)) + return AnimaRegressionManifest( + images=( + AcceptedImage( + "accepted-image", image, hashlib.sha256(image.read_bytes()).hexdigest() + ), + ), + managed_results=( + CompletedJsonEvidence( + "managed-result", + result, + detail_paths=(detail,), + observation_count=1, + required_integer_occurrences=( + ("model_call_count", 30, 1), + ("target_count", 448, 1), + ), + required_image_hashes=(hashlib.sha256(image.read_bytes()).hexdigest(),), + ), + ), + performance=PerformanceEvidence( + performance, + 448, + ( + ("attention-only", 0.0), + ("regional-lora-1", 15.0), + ("regional-lora-4", 35.0), + ), + ), + focused_command=empty, + repository_commands=(), + managed_rerun_commands=(), + ) + + +def _write_json(path: Path, payload: object) -> None: + """Write one deterministic fixture object.""" + + path.write_text(json.dumps(payload), encoding="utf-8") diff --git a/tests/test_anima_regression_oracle_cli.py b/tests/test_anima_regression_oracle_cli.py new file mode 100644 index 0000000..7c934e8 --- /dev/null +++ b/tests/test_anima_regression_oracle_cli.py @@ -0,0 +1,29 @@ +# 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 Anima oracle's direct command-line boundary.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + + +def test_oracle_script_supports_direct_filename_execution(tmp_path: Path) -> None: + """Load repository-owned packages even when launched outside the repository.""" + + script = Path(__file__).parents[1] / "tools" / "run_anima_regression_oracle.py" + + result = subprocess.run( + [sys.executable, str(script), "--help"], + cwd=tmp_path, + check=False, + capture_output=True, + text=True, + timeout=30, + ) + + assert result.returncode == 0, result.stderr + assert "Run the blocking accepted Anima regression oracle" in result.stdout diff --git a/tests/test_anima_regression_oracle_execution.py b/tests/test_anima_regression_oracle_execution.py new file mode 100644 index 0000000..c92ed78 --- /dev/null +++ b/tests/test_anima_regression_oracle_execution.py @@ -0,0 +1,49 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify ordered fail-fast Anima oracle command execution.""" + +from __future__ import annotations + +import sys +from pathlib import Path + +from tools.anima_regression_oracle.execution import OracleCommandExecutor +from tools.anima_regression_oracle.manifest import OracleCommand + + +def test_executor_preserves_logs_and_stops_after_first_failure(tmp_path: Path) -> None: + """Retain the failed gate without running later work.""" + + commands = ( + OracleCommand( + "passes", + (sys.executable, "-c", "print('passed output')"), + ), + OracleCommand( + "fails", + ( + sys.executable, + "-c", + "import sys; print('failed output', file=sys.stderr); sys.exit(7)", + ), + ), + OracleCommand("must-not-run", (sys.executable, "-c", "print('wrong')")), + ) + + observations = OracleCommandExecutor().execute( + commands, + repo_root=Path.cwd(), + output_root=tmp_path, + ) + + assert [item.identity for item in observations] == ["passes", "fails"] + assert [item.return_code for item in observations] == [0, 7] + assert (tmp_path / observations[0].stdout_file).read_text( + encoding="utf-8" + ).strip() == "passed output" + assert (tmp_path / observations[1].stderr_file).read_text( + encoding="utf-8" + ).strip() == "failed output" + assert not any(tmp_path.glob("*must-not-run*")) diff --git a/tests/test_anima_regression_oracle_manifest.py b/tests/test_anima_regression_oracle_manifest.py new file mode 100644 index 0000000..090c7f0 --- /dev/null +++ b/tests/test_anima_regression_oracle_manifest.py @@ -0,0 +1,77 @@ +# 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 immutable U1 Anima regression-oracle definition.""" + +from pathlib import Path + +from tools.anima_regression_oracle.manifest import default_manifest + + +def test_manifest_pins_visual_managed_performance_and_gate_contracts() -> None: + """Keep accepted evidence and executable gates complete and ordered.""" + + manifest = default_manifest(Path.cwd()) + + assert [image.identity for image in manifest.images] == [ + "character_a-phased-composition", + "adapter_a-one-side-phased-composition", + "global-style-character-control", + "global-adapter_a-050-regional-character_a", + "global-adapter_a-100-regional-character_a", + ] + assert [evidence.observation_count for evidence in manifest.managed_results] == [ + None, + None, + 3, + 8, + 8, + 6, + 1, + ] + assert manifest.performance.adapter_target_count == 448 + assert manifest.performance.profile_limits == ( + ("attention-only", 0.0), + ("regional-lora-1", 15.0), + ("regional-lora-4", 35.0), + ) + assert manifest.focused_command.arguments[1:5] == ( + "-m", + "pytest", + "-n", + "auto", + ) + assert [command.identity for command in manifest.repository_commands] == [ + "ruff-format-check", + "ruff-check", + "strict-mypy", + "full-python-suite", + ] + assert [command.identity for command in manifest.managed_rerun_commands] == [ + "character_a-managed-proof", + "adapter_a-managed-proof", + "global-style-character-proof", + "full-managed-matrix", + "tiled-managed-matrix", + "contextual-managed-matrix", + "performance-gate", + ] + assert all( + command.arguments[1] == "-m" for command in manifest.managed_rerun_commands + ) + + +def test_manifest_names_focused_tests_explicitly() -> None: + """Prevent broad filename discovery from silently changing U1 coverage.""" + + arguments = default_manifest(Path.cwd()).focused_command.arguments + tests = tuple(argument for argument in arguments if argument.startswith("tests/")) + + assert len(tests) == 32 + assert "tests/test_anima_module_surface.py" in tests + assert "tests/test_anima_multi_lora_fidelity.py" in tests + assert "tests/test_anima_composition_phase.py" in tests + assert "tests/test_anima_tiled_attention_coupling_integration.py" in tests + assert "tests/test_anima_contextual_attention_coupling_integration.py" in tests + assert "tests/test_anima_regional_lora_performance_results.py" in tests diff --git a/tests/test_anima_regression_oracle_results.py b/tests/test_anima_regression_oracle_results.py new file mode 100644 index 0000000..a120cf3 --- /dev/null +++ b/tests/test_anima_regression_oracle_results.py @@ -0,0 +1,51 @@ +# 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 terminal Anima oracle result publication.""" + +from __future__ import annotations + +import json +from pathlib import Path + +from tools.anima_regression_oracle.artifact_validation import ArtifactObservation +from tools.anima_regression_oracle.execution import CommandObservation +from tools.anima_regression_oracle.results import AnimaRegressionResultRecorder + + +def test_result_is_completed_only_for_complete_passing_command_set( + tmp_path: Path, +) -> None: + """Publish a complete ordered result with no leftover journal.""" + + recorder = AnimaRegressionResultRecorder(tmp_path / "passing") + + result = recorder.publish( + level="focused", + artifacts=(ArtifactObservation("image", "image.png", "a" * 64),), + commands=(CommandObservation("focused", ("python",), 0, 1.0, "out", "err"),), + expected_command_count=1, + ) + + payload = json.loads(result.read_text(encoding="utf-8")) + assert payload["status"] == "completed" + assert payload["expected_command_count"] == 1 + assert payload["artifact_observations"][0]["identity"] == "image" + assert payload["command_observations"][0]["identity"] == "focused" + assert not result.with_suffix(".json.tmp").exists() + + +def test_result_retains_failure_or_incomplete_execution(tmp_path: Path) -> None: + """Never publish completion from a failed or truncated gate set.""" + + recorder = AnimaRegressionResultRecorder(tmp_path / "failed") + + result = recorder.publish( + level="full", + artifacts=(), + commands=(CommandObservation("focused", ("python",), 1, 1.0, "out", "err"),), + expected_command_count=5, + ) + + assert json.loads(result.read_text(encoding="utf-8"))["status"] == "failed" diff --git a/tests/test_anima_self_attention_work.py b/tests/test_anima_self_attention_work.py index 4e4dee1..2596a2a 100644 --- a/tests/test_anima_self_attention_work.py +++ b/tests/test_anima_self_attention_work.py @@ -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 compact Anima self-attention work after composition is established.""" from __future__ import annotations diff --git a/tests/test_anima_tiled_attention_coupling_integration.py b/tests/test_anima_tiled_attention_coupling_integration.py index c58adba..4110889 100644 --- a/tests/test_anima_tiled_attention_coupling_integration.py +++ b/tests/test_anima_tiled_attention_coupling_integration.py @@ -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 P6.9 tiled public-node matrix and evidence owners.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_backend_invariants.py b/tests/test_attention_coupling_backend_invariants.py index a0a1549..ffe836a 100644 --- a/tests/test_attention_coupling_backend_invariants.py +++ b/tests/test_attention_coupling_backend_invariants.py @@ -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 + """Run shared Attention Coupling invariants against every backend harness.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_benchmark_manifest.py b/tests/test_attention_coupling_benchmark_manifest.py index 75fb141..e534dc9 100644 --- a/tests/test_attention_coupling_benchmark_manifest.py +++ b/tests/test_attention_coupling_benchmark_manifest.py @@ -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 fixed Anima regional benchmark manifest and scoring contract.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_benchmark_masks.py b/tests/test_attention_coupling_benchmark_masks.py index 663af3b..a7135a3 100644 --- a/tests/test_attention_coupling_benchmark_masks.py +++ b/tests/test_attention_coupling_benchmark_masks.py @@ -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 benchmark mask materialization.""" from pathlib import Path diff --git a/tests/test_attention_coupling_benchmark_probe.py b/tests/test_attention_coupling_benchmark_probe.py index 227af20..aeed621 100644 --- a/tests/test_attention_coupling_benchmark_probe.py +++ b/tests/test_attention_coupling_benchmark_probe.py @@ -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 benchmark-only Comfy model-call and resource probe.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_benchmark_results.py b/tests/test_attention_coupling_benchmark_results.py index e3bb1b5..e490f14 100644 --- a/tests/test_attention_coupling_benchmark_results.py +++ b/tests/test_attention_coupling_benchmark_results.py @@ -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 benchmark history parsing and complete result persistence.""" import json diff --git a/tests/test_attention_coupling_benchmark_workflow.py b/tests/test_attention_coupling_benchmark_workflow.py index 463d67a..92c740a 100644 --- a/tests/test_attention_coupling_benchmark_workflow.py +++ b/tests/test_attention_coupling_benchmark_workflow.py @@ -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 API-format workflow construction for every benchmark spatial mode.""" import pytest diff --git a/tests/test_attention_coupling_interop_preflight.py b/tests/test_attention_coupling_interop_preflight.py index 3baab33..41a2c3b 100644 --- a/tests/test_attention_coupling_interop_preflight.py +++ b/tests/test_attention_coupling_interop_preflight.py @@ -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 modifier admission precedes every expensive preparation owner.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_model_family_selector.py b/tests/test_attention_coupling_model_family_selector.py index f1cbd19..58df01d 100644 --- a/tests/test_attention_coupling_model_family_selector.py +++ b/tests/test_attention_coupling_model_family_selector.py @@ -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 selection from admitted Attention Coupling backend capabilities.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_model_preparation_routing.py b/tests/test_attention_coupling_model_preparation_routing.py index ec7bb22..0a96ac8 100644 --- a/tests/test_attention_coupling_model_preparation_routing.py +++ b/tests/test_attention_coupling_model_preparation_routing.py @@ -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 central capability admission routes shared model preparation.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_model_preparation_service.py b/tests/test_attention_coupling_model_preparation_service.py index dbff171..d9d9e5f 100644 --- a/tests/test_attention_coupling_model_preparation_service.py +++ b/tests/test_attention_coupling_model_preparation_service.py @@ -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 model-neutral prepare-once Attention Coupling orchestration.""" from __future__ import annotations diff --git a/tests/test_attention_coupling_preparation_service.py b/tests/test_attention_coupling_preparation_service.py index 37123ff..fed2eda 100644 --- a/tests/test_attention_coupling_preparation_service.py +++ b/tests/test_attention_coupling_preparation_service.py @@ -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 base-only Attention Coupling sampler preparation and metadata admission.""" from __future__ import annotations diff --git a/tests/test_clip_patcher_mutations.py b/tests/test_clip_patcher_mutations.py index d5ee281..f8d2aa6 100644 --- a/tests/test_clip_patcher_mutations.py +++ b/tests/test_clip_patcher_mutations.py @@ -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 + """Characterize concrete CLIP patcher mutation behavior.""" from __future__ import annotations diff --git a/tests/test_clip_schedule_snapshot_probe.py b/tests/test_clip_schedule_snapshot_probe.py index 3798fd9..4a95dc3 100644 --- a/tests/test_clip_schedule_snapshot_probe.py +++ b/tests/test_clip_schedule_snapshot_probe.py @@ -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 read-only benchmark snapshots of CLIP hook schedule state.""" from __future__ import annotations diff --git a/tests/test_comfy_api.py b/tests/test_comfy_api.py index 5fcb7a5..f7de8ed 100644 --- a/tests/test_comfy_api.py +++ b/tests/test_comfy_api.py @@ -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 loopback-only Comfy API access.""" from __future__ import annotations diff --git a/tests/test_comfy_conditioning_model_loader.py b/tests/test_comfy_conditioning_model_loader.py index 9aae0d7..38a31b5 100644 --- a/tests/test_comfy_conditioning_model_loader.py +++ b/tests/test_comfy_conditioning_model_loader.py @@ -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 official Comfy model loading before Anima conditioning processing.""" from __future__ import annotations diff --git a/tests/test_comfy_integration_artifacts.py b/tests/test_comfy_integration_artifacts.py index 82dcfb8..b93b36e 100644 --- a/tests/test_comfy_integration_artifacts.py +++ b/tests/test_comfy_integration_artifacts.py @@ -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 + """Test atomic managed-run evidence persistence and finalization.""" from __future__ import annotations diff --git a/tests/test_comfy_integration_baseline_workflow.py b/tests/test_comfy_integration_baseline_workflow.py index 7aa0b6e..c7d03c6 100644 --- a/tests/test_comfy_integration_baseline_workflow.py +++ b/tests/test_comfy_integration_baseline_workflow.py @@ -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 + """Test the production-node incoming Anima baseline graph.""" from __future__ import annotations diff --git a/tests/test_comfy_integration_history_output.py b/tests/test_comfy_integration_history_output.py index a7e9faf..f9fade3 100644 --- a/tests/test_comfy_integration_history_output.py +++ b/tests/test_comfy_integration_history_output.py @@ -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 + """Test strict saved-image extraction from completed Comfy history.""" from __future__ import annotations diff --git a/tests/test_comfy_integration_loopback_port.py b/tests/test_comfy_integration_loopback_port.py index 1476eb4..6f882b3 100644 --- a/tests/test_comfy_integration_loopback_port.py +++ b/tests/test_comfy_integration_loopback_port.py @@ -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 + """Test isolated loopback-port selection and validation.""" from __future__ import annotations diff --git a/tests/test_comfy_integration_managed_server.py b/tests/test_comfy_integration_managed_server.py index 9b0feb0..7906d60 100644 --- a/tests/test_comfy_integration_managed_server.py +++ b/tests/test_comfy_integration_managed_server.py @@ -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 + """Test context-managed startup, readiness, and exact cleanup coordination.""" from __future__ import annotations diff --git a/tests/test_comfy_integration_readiness.py b/tests/test_comfy_integration_readiness.py index a1fe52a..46da9c0 100644 --- a/tests/test_comfy_integration_readiness.py +++ b/tests/test_comfy_integration_readiness.py @@ -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 + """Test bounded server readiness and process-liveness policy.""" from __future__ import annotations diff --git a/tests/test_comfy_integration_server_process.py b/tests/test_comfy_integration_server_process.py index 51f2a21..c0ce21f 100644 --- a/tests/test_comfy_integration_server_process.py +++ b/tests/test_comfy_integration_server_process.py @@ -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 + """Test exact Windows Comfy command and owned-process cleanup.""" from __future__ import annotations diff --git a/tests/test_comfy_regional_adapter_resolver.py b/tests/test_comfy_regional_adapter_resolver.py new file mode 100644 index 0000000..dabf764 --- /dev/null +++ b/tests/test_comfy_regional_adapter_resolver.py @@ -0,0 +1,334 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify Comfy-owned regional adapter resolution and complete accounting.""" + +from __future__ import annotations + +from collections.abc import Mapping + +import comfy.lora +import comfy.model_patcher +import pytest +import torch +from comfy.weight_adapter.lora import LoRAAdapter + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraPlan, + RegionalLoraScheduleBoundary, +) +from simple_syrup.runtime.regional_lora.comfy_adapter_resolution import ( + ComfyAdapterResolutionIssueCode, + ComfyAdapterSourceScope, +) +from simple_syrup.runtime.regional_lora.comfy_adapter_resolver import ( + ComfyRegionalAdapterResolver, +) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload +from simple_syrup.runtime.regional_lora_plan_adapter import ( + RegionalLoraPlanAdaptation, +) + + +def test_resolver_uses_comfy_maps_and_accounts_for_every_raw_source( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Delegate naming to Comfy and classify model, clip, VAE, and unused keys.""" + + raw_weights = { + "model.up": object(), + "model.down": object(), + "clip.up": object(), + "clip.down": object(), + "vae.diff": object(), + "unused": object(), + } + model_operation = _lora("model.up", "model.down") + clip_operation = _lora("clip.up", "clip.down") + model_path = ("diffusion_model.layer.weight", (0, 4, 8)) + calls: list[tuple[object, Mapping[object, object], bool]] = [] + base_model = torch.nn.Linear(1, 1) + model = _patcher(base_model) + clip_model = object() + model_map = {"model": model_path} + clip_map = {"clip": "text_model.layer.weight"} + vae_map = {"vae": "decoder.layer.weight"} + + monkeypatch.setattr( + comfy.lora, + "model_lora_keys_unet", + lambda observed, key_map: model_map if observed is base_model else key_map, + ) + monkeypatch.setattr( + comfy.lora, + "model_lora_keys_clip", + lambda observed, key_map: clip_map if observed is clip_model else key_map, + ) + + def load_lora( + weights: Mapping[str, object], + key_map: Mapping[object, object], + *, + log_missing: bool, + ) -> Mapping[object, object]: + calls.append((weights, key_map, log_missing)) + if key_map is model_map: + return {model_path: model_operation} + if key_map is clip_map: + return {"text_model.layer.weight": clip_operation} + return {"decoder.layer.weight": ("diff", (raw_weights["vae.diff"],))} + + monkeypatch.setattr(comfy.lora, "load_lora", load_lora) + adaptation = _adaptation((RegionalLoraHostPayload.unresolved(raw_weights),)) + + result = ComfyRegionalAdapterResolver().resolve( + adaptation, + model=model, + clip_model=clip_model, + vae_key_map=vae_map, + ) + + assert [call[0] is raw_weights for call in calls] == [True, True, True] + assert [call[1] for call in calls] == [model_map, clip_map, vae_map] + assert [call[2] for call in calls] == [False, False, False] + target = result.adapters[0].model_targets[0] + assert target.path.parameter_key == "diffusion_model.layer.weight" + assert target.path.offset == (0, 4, 8) + assert target.operation is model_operation + assert target.source_keys == ("model.down", "model.up") + assert target.ordinary_additive_lora is True + assert result.adapters[0].payload is adaptation.adapter_payloads[0] + assert { + entry.source_key: entry.scope for entry in result.adapters[0].source_entries + } == { + "model.up": ComfyAdapterSourceScope.MODEL, + "model.down": ComfyAdapterSourceScope.MODEL, + "clip.up": ComfyAdapterSourceScope.TEXT_ENCODER, + "clip.down": ComfyAdapterSourceScope.TEXT_ENCODER, + "vae.diff": ComfyAdapterSourceScope.VAE, + "unused": ComfyAdapterSourceScope.UNUSED, + } + assert result.issues == () + assert adaptation.adapter_payloads[0].raw_weights is raw_weights + + +def test_resolver_consumes_initialized_payloads_by_exact_identity( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Bypass key mapping and decoding after Comfy has initialized a hook.""" + + model_operation = _lora("model.up", "model.down") + clip_operation = _lora("clip.up", "clip.down") + model_weights = {"diffusion_model.layer.weight": model_operation} + clip_weights = {"text_model.layer.weight": clip_operation} + payload = RegionalLoraHostPayload(False, None, model_weights, clip_weights) + monkeypatch.setattr( + comfy.lora, + "load_lora", + lambda *args, **kwargs: pytest.fail("Initialized payload was decoded again."), + ) + monkeypatch.setattr( + comfy.lora, + "model_lora_keys_unet", + lambda *args, **kwargs: pytest.fail("Initialized payload was remapped."), + ) + + result = ComfyRegionalAdapterResolver().resolve( + _adaptation((payload,)), + model=_patcher(torch.nn.Linear(1, 1)), + ) + + target = result.adapters[0].model_targets[0] + assert target.operation is model_operation + assert result.adapters[0].payload.model_weights is model_weights + assert result.adapters[0].payload.clip_weights is clip_weights + assert [ + (entry.source_key, entry.scope) for entry in result.adapters[0].source_entries + ] == [ + ("model.down", ComfyAdapterSourceScope.MODEL), + ("model.up", ComfyAdapterSourceScope.MODEL), + ("clip.down", ComfyAdapterSourceScope.TEXT_ENCODER), + ("clip.up", ComfyAdapterSourceScope.TEXT_ENCODER), + ] + + +def test_resolver_preserves_normalized_lora_metadata_by_identity() -> None: + """Retain alpha, middle, reshape, and tensor identities for U3 translation.""" + + up = torch.ones((2, 1)) + down = torch.ones((1, 2)) + middle = torch.ones((1, 1, 1, 1)) + reshape = [2, 2] + operation = LoRAAdapter( + {"up", "down", "middle", "reshape"}, + (up, down, 0.75, middle, None, reshape), + ) + payload = RegionalLoraHostPayload( + False, + None, + {"diffusion_model.layer.weight": operation}, + None, + ) + + result = ComfyRegionalAdapterResolver().resolve( + _adaptation((payload,)), + model=_patcher(torch.nn.Linear(1, 1)), + ) + + retained = result.adapters[0].model_targets[0].operation + assert retained is operation + assert isinstance(retained, LoRAAdapter) + assert retained.weights[0] is up + assert retained.weights[1] is down + assert retained.weights[2] == 0.75 + assert retained.weights[3] is middle + assert retained.weights[4] is None + assert retained.weights[5] is reshape + + +def test_resolver_reports_dora_and_every_other_operation_explicitly() -> None: + """Admit only ordinary additive LoRA while retaining unsupported operations.""" + + dora = _lora("dora.up", "dora.down", dora_scale=torch.ones(1)) + diff = ("diff", (torch.ones(1),)) + payload = RegionalLoraHostPayload( + False, + None, + { + "diffusion_model.dora.weight": dora, + "diffusion_model.diff.weight": diff, + }, + None, + ) + + result = ComfyRegionalAdapterResolver().resolve( + _adaptation((payload,)), + model=_patcher(torch.nn.Linear(1, 1)), + ) + + assert [target.operation for target in result.adapters[0].model_targets] == [ + dora, + diff, + ] + assert [ + target.ordinary_additive_lora for target in result.adapters[0].model_targets + ] == [ + False, + False, + ] + assert [issue.code for issue in result.issues] == [ + ComfyAdapterResolutionIssueCode.DORA_UNSUPPORTED, + ComfyAdapterResolutionIssueCode.UNSUPPORTED_OPERATION, + ] + + +def test_resolver_aggregates_failures_without_hiding_other_adapters( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Return all ordered adapter failures instead of stopping at the first one.""" + + invalid = RegionalLoraHostPayload.unresolved(object()) + unsupported = RegionalLoraHostPayload( + False, + None, + {"diffusion_model.layer.weight": ("set", (object(),))}, + None, + ) + monkeypatch.setattr(comfy.lora, "model_lora_keys_unet", lambda model, key_map: {}) + + result = ComfyRegionalAdapterResolver().resolve( + _adaptation((invalid, unsupported)), + model=_patcher(torch.nn.Linear(1, 1)), + ) + + assert len(result.adapters) == 2 + assert [issue.composition_index for issue in result.issues] == [0, 1] + assert [issue.code for issue in result.issues] == [ + ComfyAdapterResolutionIssueCode.RESOLUTION_FAILED, + ComfyAdapterResolutionIssueCode.UNSUPPORTED_OPERATION, + ] + assert [issue.adapter_identity for issue in result.issues] == [ + "adapter-0.safetensors", + "adapter-1.safetensors", + ] + + +def test_resolver_reports_invalid_comfy_target_paths() -> None: + """Reject malformed host target paths without discarding valid siblings.""" + + operation = _lora("up", "down") + payload = RegionalLoraHostPayload( + False, + None, + {3: operation, "diffusion_model.valid.weight": operation}, + None, + ) + + result = ComfyRegionalAdapterResolver().resolve( + _adaptation((payload,)), + model=_patcher(torch.nn.Linear(1, 1)), + ) + + assert [ + target.path.parameter_key for target in result.adapters[0].model_targets + ] == ["diffusion_model.valid.weight"] + assert [issue.code for issue in result.issues] == [ + ComfyAdapterResolutionIssueCode.INVALID_TARGET_PATH + ] + + +def test_resolver_rejects_non_comfy_model_boundaries() -> None: + """Require the same MODEL object contract supplied by Comfy workflows.""" + + with pytest.raises(TypeError, match="requires a Comfy ModelPatcher"): + ComfyRegionalAdapterResolver().resolve( + _adaptation(()), + model=object(), + ) + + +def _adaptation( + payloads: tuple[RegionalLoraHostPayload, ...], +) -> RegionalLoraPlanAdaptation: + """Return one valid ordered adaptation for resolver characterization.""" + + adapters = tuple( + RegionalLoraAdapterPlan( + adapter_identity=RegionalLoraAdapterIdentity( + f"adapter-{index}.safetensors" + ), + composition_index=index, + region_index=index, + branch=RegionalLoraBranch.POSITIVE, + model_strength=0.8, + schedule=(RegionalLoraScheduleBoundary(0.0, 100.0, 1.0, 0),), + ) + for index in range(len(payloads)) + ) + return RegionalLoraPlanAdaptation(RegionalLoraPlan(adapters), payloads) + + +def _patcher(model: object) -> comfy.model_patcher.ModelPatcher: + """Wrap one test base model in the installed Comfy MODEL boundary.""" + + return comfy.model_patcher.ModelPatcher( + model, torch.device("cpu"), torch.device("cpu") + ) + + +def _lora( + up_key: str, + down_key: str, + *, + dora_scale: torch.Tensor | None = None, +) -> LoRAAdapter: + """Return one installed Comfy ordinary or DoRA normalized adapter.""" + + return LoRAAdapter( + {up_key, down_key}, + (torch.ones((2, 1)), torch.ones((1, 2)), 1.0, None, dora_scale, None), + ) diff --git a/tests/test_comfy_regional_conditioning_processing.py b/tests/test_comfy_regional_conditioning_processing.py index fa5232a..1fdd790 100644 --- a/tests/test_comfy_regional_conditioning_processing.py +++ b/tests/test_comfy_regional_conditioning_processing.py @@ -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 contexts use installed Comfy conditioning processing.""" from __future__ import annotations diff --git a/tests/test_conditioning_batch_snapshot_probe.py b/tests/test_conditioning_batch_snapshot_probe.py index 863c852..eb1c934 100644 --- a/tests/test_conditioning_batch_snapshot_probe.py +++ b/tests/test_conditioning_batch_snapshot_probe.py @@ -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 ordered benchmark snapshots for mixed conditioning values.""" from __future__ import annotations diff --git a/tests/test_conditioning_schedule.py b/tests/test_conditioning_schedule.py index d978988..8cdbd14 100644 --- a/tests/test_conditioning_schedule.py +++ b/tests/test_conditioning_schedule.py @@ -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 immutable authored and converted conditioning schedule contracts.""" from __future__ import annotations diff --git a/tests/test_conditioning_schedule_comfy_equivalence.py b/tests/test_conditioning_schedule_comfy_equivalence.py index 847acb8..3b987d6 100644 --- a/tests/test_conditioning_schedule_comfy_equivalence.py +++ b/tests/test_conditioning_schedule_comfy_equivalence.py @@ -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 + """Characterize installed Comfy conditioning schedule admission exactly.""" from __future__ import annotations diff --git a/tests/test_contextual_attention_coupling_sampling_service.py b/tests/test_contextual_attention_coupling_sampling_service.py index ce4965d..7ea9123 100644 --- a/tests/test_contextual_attention_coupling_sampling_service.py +++ b/tests/test_contextual_attention_coupling_sampling_service.py @@ -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 Attention Coupling composes through Contextual Diffusion authority.""" from __future__ import annotations diff --git a/tests/test_contextual_diffusion_sampling_boundary.py b/tests/test_contextual_diffusion_sampling_boundary.py index 7812d19..c5d3e12 100644 --- a/tests/test_contextual_diffusion_sampling_boundary.py +++ b/tests/test_contextual_diffusion_sampling_boundary.py @@ -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 Contextual Diffusion Comfy sampling boundary.""" from __future__ import annotations diff --git a/tests/test_contextual_integration_diagnostics.py b/tests/test_contextual_integration_diagnostics.py index ce20a63..69cf2a9 100644 --- a/tests/test_contextual_integration_diagnostics.py +++ b/tests/test_contextual_integration_diagnostics.py @@ -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 P7.8 managed structured diagnostic validation.""" from __future__ import annotations diff --git a/tests/test_contextual_model_wrapper.py b/tests/test_contextual_model_wrapper.py index 01691c8..83b272f 100644 --- a/tests/test_contextual_model_wrapper.py +++ b/tests/test_contextual_model_wrapper.py @@ -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 Contextual Diffusion wrapper delegation and fusion behavior.""" from __future__ import annotations diff --git a/tests/test_effective_model_graph.py b/tests/test_effective_model_graph.py new file mode 100644 index 0000000..1085917 --- /dev/null +++ b/tests/test_effective_model_graph.py @@ -0,0 +1,129 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify read-only effective model graph resolution through object patches.""" + +from __future__ import annotations + +from types import SimpleNamespace + +import comfy.model_patcher +import pytest +import torch +from torch import nn + +from simple_syrup.runtime.regional_lora.effective_model_graph import ( + EffectiveModelGraphError, + EffectiveModelGraphResolver, +) + + +def test_resolver_reads_base_graph_without_mutation() -> None: + """Return exact nested base objects when no active patch owns the path.""" + + patcher = _patcher() + layer = patcher.model.diffusion_model.layer + before = _snapshot(patcher) + + result = EffectiveModelGraphResolver().resolve( + patcher, + "diffusion_model.layer.weight", + ) + + assert result.value is layer.weight + assert result.active_object_patch_paths == () + assert _snapshot(patcher) == before + + +def test_resolver_traverses_one_unapplied_parent_object_patch() -> None: + """Observe a pending replacement's children without applying the patch.""" + + patcher = _patcher() + original = patcher.model.diffusion_model.layer + replacement = nn.Linear(4, 6, bias=False) + patcher.add_object_patch("diffusion_model.layer", replacement) + + module = EffectiveModelGraphResolver().resolve( + patcher, + "diffusion_model.layer", + ) + parameter = EffectiveModelGraphResolver().resolve( + patcher, + "diffusion_model.layer.weight", + ) + + assert module.value is replacement + assert parameter.value is replacement.weight + assert module.active_object_patch_paths == ("diffusion_model.layer",) + assert parameter.active_object_patch_paths == ("diffusion_model.layer",) + assert patcher.model.diffusion_model.layer is original + + +def test_resolver_uses_exact_parameter_patch() -> None: + """Observe exact pending parameter ownership independently of its module.""" + + patcher = _patcher() + replacement = nn.Parameter(torch.ones((6, 4)), requires_grad=False) + patcher.add_object_patch("diffusion_model.layer.weight", replacement) + + result = EffectiveModelGraphResolver().resolve( + patcher, + "diffusion_model.layer.weight", + ) + + assert result.value is replacement + assert result.active_object_patch_paths == ("diffusion_model.layer.weight",) + + +def test_resolver_rejects_overlapping_parent_and_child_patches() -> None: + """Reject insertion-order-dependent effective graph ownership.""" + + patcher = _patcher() + patcher.add_object_patch("diffusion_model.layer", nn.Linear(4, 6, bias=False)) + patcher.add_object_patch( + "diffusion_model.layer.weight", + nn.Parameter(torch.ones((6, 4)), requires_grad=False), + ) + + with pytest.raises(EffectiveModelGraphError, match="overlapping object patch"): + EffectiveModelGraphResolver().resolve( + patcher, + "diffusion_model.layer.weight", + ) + + +@pytest.mark.parametrize("path", ["", ".layer", "layer.", "layer..weight"]) +def test_resolver_rejects_invalid_or_missing_paths(path: str) -> None: + """Fail clearly for malformed and absent effective graph paths.""" + + patcher = _patcher() + with pytest.raises((ValueError, EffectiveModelGraphError)): + EffectiveModelGraphResolver().resolve(patcher, path) + with pytest.raises(EffectiveModelGraphError, match="does not exist"): + EffectiveModelGraphResolver().resolve(patcher, "diffusion_model.missing") + + +def _patcher() -> comfy.model_patcher.ModelPatcher: + """Return one real Comfy patcher around a minimal nested graph.""" + + model = nn.Module() + model.diffusion_model = nn.Module() + model.diffusion_model.layer = nn.Linear(4, 6, bias=False) + return comfy.model_patcher.ModelPatcher( + model, + torch.device("cpu"), + torch.device("cpu"), + ) + + +def _snapshot(patcher: comfy.model_patcher.ModelPatcher) -> SimpleNamespace: + """Snapshot graph and patch mapping identity without copying tensors.""" + + return SimpleNamespace( + model=patcher.model, + layer=patcher.model.diffusion_model.layer, + weight=patcher.model.diffusion_model.layer.weight, + object_patches=dict(patcher.object_patches), + backups=dict(patcher.object_patches_backup), + ) diff --git a/tests/test_global_context_schedule.py b/tests/test_global_context_schedule.py index cf131dd..e2f1ade 100644 --- a/tests/test_global_context_schedule.py +++ b/tests/test_global_context_schedule.py @@ -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 + """Characterize pure Contextual Diffusion global-authority scheduling.""" from __future__ import annotations diff --git a/tests/test_global_lora_visual_proof.py b/tests/test_global_lora_visual_proof.py index fa88d07..eeecf33 100644 --- a/tests/test_global_lora_visual_proof.py +++ b/tests/test_global_lora_visual_proof.py @@ -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 fixed global and regional ADAPTER_A visual proof matrix.""" from __future__ import annotations diff --git a/tests/test_global_regional_lora_image_validation.py b/tests/test_global_regional_lora_image_validation.py index eadfe5d..525327b 100644 --- a/tests/test_global_regional_lora_image_validation.py +++ b/tests/test_global_regional_lora_image_validation.py @@ -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 P9.3 same-server transition image acceptance.""" from __future__ import annotations diff --git a/tests/test_global_regional_lora_integration.py b/tests/test_global_regional_lora_integration.py index ec33788..3e3f016 100644 --- a/tests/test_global_regional_lora_integration.py +++ b/tests/test_global_regional_lora_integration.py @@ -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 closed P9.3 managed matrix and result contract.""" from __future__ import annotations diff --git a/tests/test_global_style_character_proof.py b/tests/test_global_style_character_proof.py index f6b34e7..765b279 100644 --- a/tests/test_global_style_character_proof.py +++ b/tests/test_global_style_character_proof.py @@ -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 requested global-style and regional-character proof matrix.""" from __future__ import annotations diff --git a/tests/test_lora_execution_probe.py b/tests/test_lora_execution_probe.py index c3d36a4..2cce686 100644 --- a/tests/test_lora_execution_probe.py +++ b/tests/test_lora_execution_probe.py @@ -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 benchmark-only LoRA execution observation nodes.""" from __future__ import annotations diff --git a/tests/test_lora_patch_snapshot.py b/tests/test_lora_patch_snapshot.py index b8323af..f1104ab 100644 --- a/tests/test_lora_patch_snapshot.py +++ b/tests/test_lora_patch_snapshot.py @@ -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 stable normalization of Comfy static and scheduled LoRA patches.""" from __future__ import annotations diff --git a/tests/test_model_keyed_callback_mutation.py b/tests/test_model_keyed_callback_mutation.py index 21a308e..61b7731 100644 --- a/tests/test_model_keyed_callback_mutation.py +++ b/tests/test_model_keyed_callback_mutation.py @@ -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 collision-safe Comfy MODEL callback registration.""" from __future__ import annotations diff --git a/tests/test_model_modifier_snapshot_probe.py b/tests/test_model_modifier_snapshot_probe.py index db641d8..9ef3dbd 100644 --- a/tests/test_model_modifier_snapshot_probe.py +++ b/tests/test_model_modifier_snapshot_probe.py @@ -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 benchmark-only MODEL modifier state snapshots.""" from __future__ import annotations diff --git a/tests/test_model_object_patch_batch.py b/tests/test_model_object_patch_batch.py new file mode 100644 index 0000000..8371d63 --- /dev/null +++ b/tests/test_model_object_patch_batch.py @@ -0,0 +1,176 @@ +# 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 exact-object mutation batches against installed Comfy.""" + +from __future__ import annotations + +from typing import Any + +import pytest +import torch +from torch import nn + +from simple_syrup.runtime.model_object_patch_batch import ( + ExactModelObjectReplacement, + ModelObjectPatchBatchMutation, +) +from simple_syrup.runtime.patcher_lifecycle import PATCHER_LIFECYCLE + + +def test_batch_preflights_every_identity_before_installing_any_path() -> None: + """A late identity failure cannot leave one earlier target installed.""" + + source = _patcher() + first = source.get_model_object("first") + mutation = ModelObjectPatchBatchMutation( + ( + ExactModelObjectReplacement("first", first, nn.Identity()), + ExactModelObjectReplacement("second", object(), nn.Identity()), + ) + ) + + with pytest.raises(ValueError, match="second.*expected object"): + PATCHER_LIFECYCLE.derive_model( + source, + (mutation,), + operation="atomic late identity regression", + ) + + assert source.object_patches == {} + + +@pytest.mark.parametrize("failure", [RuntimeError("failure"), KeyboardInterrupt()]) +def test_batch_rolls_back_pending_state_on_exception_or_interruption( + failure: BaseException, +) -> None: + """Restore the exact pending map after any BaseException from Comfy's adder.""" + + class FailingSurface: + """Expose a valid exact surface whose second mutation fails.""" + + def __init__(self) -> None: + """Initialize two expected objects and one foreign pending patch.""" + + self.values = {"first": object(), "second": object()} + self.object_patches: dict[object, object] = {"foreign": object()} + self.object_patches_backup: dict[object, object] = {} + self.calls = 0 + + def get_model_object(self, name: str) -> object: + """Return one exact expected object.""" + + return self.values[name] + + def add_object_patch(self, name: str, obj: object) -> None: + """Install once, then emulate host failure or interruption.""" + + self.calls += 1 + if self.calls == 2: + raise failure + self.object_patches[name] = obj + + model = FailingSurface() + before = model.object_patches.copy() + mutation = ModelObjectPatchBatchMutation( + tuple( + ExactModelObjectReplacement(path, model.values[path], object()) + for path in ("first", "second") + ) + ) + + with pytest.raises(type(failure)): + mutation.apply(model) + + assert model.object_patches == before + + +def test_real_clone_patch_unpatch_and_reentry_restore_exact_graph() -> None: + """Apply and eject one batch repeatedly without changing the source graph.""" + + source = _patcher() + first = source.get_model_object("first") + second = source.get_model_object("second") + first_replacement = nn.Identity() + second_replacement = nn.Identity() + derived = PATCHER_LIFECYCLE.derive_model( + source, + ( + ModelObjectPatchBatchMutation( + ( + ExactModelObjectReplacement("first", first, first_replacement), + ExactModelObjectReplacement("second", second, second_replacement), + ) + ), + ), + operation="exact object reentry regression", + ) + + assert derived.parent is source + assert source.object_patches == {} + for _iteration in range(2): + derived.patch_model(load_weights=False) + assert derived.model.first is first_replacement + assert derived.model.second is second_replacement + derived.unpatch_model(unpatch_weights=False) + assert derived.model.first is first + assert derived.model.second is second + assert derived.object_patches_backup == {} + + +def test_batch_rejects_ancestor_collision_before_mutation() -> None: + """Prevent a module replacement from overlapping a nested active patch.""" + + source = _patcher() + derived = source.clone() + derived.object_patches["first.weight"] = object() + mutation = ModelObjectPatchBatchMutation( + ( + ExactModelObjectReplacement( + "first", + source.get_model_object("first"), + nn.Identity(), + ), + ) + ) + + with pytest.raises(ValueError, match="collides with existing"): + mutation.apply(derived) + + assert tuple(derived.object_patches) == ("first.weight",) + + +def test_batch_rejects_exact_collision_before_mutation() -> None: + """Reject one exact pending path even when no distinct ancestor pair exists.""" + + source = _patcher() + derived = source.clone() + existing = object() + derived.object_patches["first"] = existing + mutation = ModelObjectPatchBatchMutation( + ( + ExactModelObjectReplacement( + "first", + source.get_model_object("first"), + nn.Identity(), + ), + ) + ) + + with pytest.raises(ValueError, match="collides with existing"): + mutation.apply(derived) + + assert derived.object_patches == {"first": existing} + + +def _patcher() -> Any: + """Return one real Comfy patcher around two exact module paths.""" + + from comfy.model_patcher import ModelPatcher + + root = nn.Module() + root.first = nn.Linear(2, 2) + root.second = nn.Linear(2, 2) + device = torch.device("cpu") + return ModelPatcher(root, device, device) diff --git a/tests/test_model_patcher_mutations.py b/tests/test_model_patcher_mutations.py index fb29e6b..c0f7d3d 100644 --- a/tests/test_model_patcher_mutations.py +++ b/tests/test_model_patcher_mutations.py @@ -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 + """Characterize concrete MODEL patcher mutation behavior.""" from __future__ import annotations diff --git a/tests/test_prompt_control_attention_coupling_cli.py b/tests/test_prompt_control_attention_coupling_cli.py index 00b6686..dba5b1b 100644 --- a/tests/test_prompt_control_attention_coupling_cli.py +++ b/tests/test_prompt_control_attention_coupling_cli.py @@ -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 P9.1 managed coordinator remains a thin explicit entrypoint.""" from pathlib import Path diff --git a/tests/test_prompt_control_attention_coupling_history.py b/tests/test_prompt_control_attention_coupling_history.py index 44b8bb2..4582455 100644 --- a/tests/test_prompt_control_attention_coupling_history.py +++ b/tests/test_prompt_control_attention_coupling_history.py @@ -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 P9.1 history output decoding.""" from __future__ import annotations diff --git a/tests/test_prompt_control_attention_coupling_matrix.py b/tests/test_prompt_control_attention_coupling_matrix.py index 5a20d17..7b48649 100644 --- a/tests/test_prompt_control_attention_coupling_matrix.py +++ b/tests/test_prompt_control_attention_coupling_matrix.py @@ -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 immutable P9.1 matrix and validated P0.8 dependency.""" from tools.prompt_control_attention_coupling_integration.baseline import load_baseline diff --git a/tests/test_prompt_control_attention_coupling_results.py b/tests/test_prompt_control_attention_coupling_results.py index 78868f3..ac33909 100644 --- a/tests/test_prompt_control_attention_coupling_results.py +++ b/tests/test_prompt_control_attention_coupling_results.py @@ -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 P9.1 result journaling, artifacts, and cleanup gate.""" from __future__ import annotations diff --git a/tests/test_prompt_control_attention_coupling_validation.py b/tests/test_prompt_control_attention_coupling_validation.py index 47dde1d..e76f1be 100644 --- a/tests/test_prompt_control_attention_coupling_validation.py +++ b/tests/test_prompt_control_attention_coupling_validation.py @@ -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 P9.1 accepts exact baseline-derived managed evidence only.""" from __future__ import annotations diff --git a/tests/test_prompt_control_attention_coupling_workflow.py b/tests/test_prompt_control_attention_coupling_workflow.py index e950b0a..d4cfde4 100644 --- a/tests/test_prompt_control_attention_coupling_workflow.py +++ b/tests/test_prompt_control_attention_coupling_workflow.py @@ -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 public graph construction for every P9.1 case.""" from __future__ import annotations diff --git a/tests/test_prompt_control_characterization_cases.py b/tests/test_prompt_control_characterization_cases.py index 41b1966..ed2be4e 100644 --- a/tests/test_prompt_control_characterization_cases.py +++ b/tests/test_prompt_control_characterization_cases.py @@ -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 immutable P0.8 Prompt Control case matrix.""" from tools.prompt_control_characterization.cases import cases diff --git a/tests/test_prompt_control_characterization_cli.py b/tests/test_prompt_control_characterization_cli.py index ff7e70e..b66c3ad 100644 --- a/tests/test_prompt_control_characterization_cli.py +++ b/tests/test_prompt_control_characterization_cli.py @@ -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 Prompt Control coordinator remains a thin module entrypoint.""" from pathlib import Path diff --git a/tests/test_prompt_control_characterization_workflow.py b/tests/test_prompt_control_characterization_workflow.py index d41dafc..dc38bf6 100644 --- a/tests/test_prompt_control_characterization_workflow.py +++ b/tests/test_prompt_control_characterization_workflow.py @@ -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 public-node Prompt Control characterization graph construction.""" from tools.prompt_control_characterization.cases import cases diff --git a/tests/test_prompt_control_evidence_validation.py b/tests/test_prompt_control_evidence_validation.py index 333504d..7e3fd32 100644 --- a/tests/test_prompt_control_evidence_validation.py +++ b/tests/test_prompt_control_evidence_validation.py @@ -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 Prompt Control characterization acceptance policy.""" from __future__ import annotations diff --git a/tests/test_prompt_control_expansion_probe.py b/tests/test_prompt_control_expansion_probe.py index f193acf..135077f 100644 --- a/tests/test_prompt_control_expansion_probe.py +++ b/tests/test_prompt_control_expansion_probe.py @@ -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 canonical Prompt Control lazy graph expansion observation.""" import pytest diff --git a/tests/test_prompt_control_history_outputs.py b/tests/test_prompt_control_history_outputs.py index 4a881cd..e4ea3f0 100644 --- a/tests/test_prompt_control_history_outputs.py +++ b/tests/test_prompt_control_history_outputs.py @@ -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 decoding of Prompt Control Comfy history outputs.""" import pytest diff --git a/tests/test_prompt_control_regional_hook_identities.py b/tests/test_prompt_control_regional_hook_identities.py index c93453d..c3a395c 100644 --- a/tests/test_prompt_control_regional_hook_identities.py +++ b/tests/test_prompt_control_regional_hook_identities.py @@ -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 stable identities extracted from Prompt Control lazy hook graphs.""" from __future__ import annotations diff --git a/tests/test_prompt_control_results.py b/tests/test_prompt_control_results.py index f3ce009..ca7f6f1 100644 --- a/tests/test_prompt_control_results.py +++ b/tests/test_prompt_control_results.py @@ -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 durable Prompt Control result completeness and resume identity.""" from pathlib import Path diff --git a/tests/test_prompt_control_runtime_probe.py b/tests/test_prompt_control_runtime_probe.py index 9a29d59..cb04d9b 100644 --- a/tests/test_prompt_control_runtime_probe.py +++ b/tests/test_prompt_control_runtime_probe.py @@ -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 sampling-time Prompt Control UUID and WeightHook observation.""" from __future__ import annotations diff --git a/tests/test_prompt_control_snapshot_probe.py b/tests/test_prompt_control_snapshot_probe.py index 386df6b..c541c1d 100644 --- a/tests/test_prompt_control_snapshot_probe.py +++ b/tests/test_prompt_control_snapshot_probe.py @@ -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 pre-sampling Prompt Control conditioning and HookGroup snapshots.""" from __future__ import annotations diff --git a/tests/test_prompt_control_source_identity.py b/tests/test_prompt_control_source_identity.py index 6fc3e94..c64ac79 100644 --- a/tests/test_prompt_control_source_identity.py +++ b/tests/test_prompt_control_source_identity.py @@ -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 pinned installed Prompt Control source identity owner.""" from pathlib import Path diff --git a/tests/test_regional_activation_batch_alignment.py b/tests/test_regional_activation_batch_alignment.py new file mode 100644 index 0000000..68ad452 --- /dev/null +++ b/tests/test_regional_activation_batch_alignment.py @@ -0,0 +1,93 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify activation batches adapt existing regional alignment authorities.""" + +from __future__ import annotations + +import pytest +import torch + +from simple_syrup.domain.regional_attention import RegionalAttentionBranch +from simple_syrup.domain.regional_attention_batch import ( + BatchedRegionalAttentionContexts, + RegionalAttentionChunkBatch, +) +from simple_syrup.domain.spatial_views import ( + SpatialBatchLayout, + SpatialView, + SpatialViewKind, +) +from simple_syrup.runtime.regional_lora.activation_batch_alignment import ( + RegionalActivationBatchAlignmentResolver, +) + + +def test_resolver_composes_real_cfg_latent_and_tiled_authorities() -> None: + """Adapt exact chunk and view batches without recreating their ordering.""" + + contexts = _contexts(latent_batch_size=2) + layout = SpatialBatchLayout( + 8, + 4, + ( + SpatialView(SpatialViewKind.TILE, 0, 0, 4, 4, 4, 4), + SpatialView(SpatialViewKind.TILE, 4, 0, 4, 4, 4, 4), + ), + input_batch_size=4, + ) + + alignment = RegionalActivationBatchAlignmentResolver().resolve( + contexts, + spatial_layout=layout, + ) + + assert alignment.latent_batch_size == 2 + assert alignment.chunk_count == 2 + assert alignment.base_batch_size == 4 + assert alignment.invocation_batch_size == 8 + assert alignment.spatial_layout is layout + + +def test_resolver_rejects_layout_that_disagrees_with_context_batch() -> None: + """Refuse a view layout built over a different CFG/latent base batch.""" + + contexts = _contexts(latent_batch_size=2) + layout = SpatialBatchLayout( + 4, + 4, + (SpatialView(SpatialViewKind.FULL, 0, 0, 4, 4, 4, 4),), + input_batch_size=2, + ) + + with pytest.raises(ValueError, match="input batch"): + RegionalActivationBatchAlignmentResolver().resolve( + contexts, + spatial_layout=layout, + ) + + +def _contexts(*, latent_batch_size: int) -> BatchedRegionalAttentionContexts: + """Return one actual two-branch chunk-major alignment value.""" + + chunks = ( + RegionalAttentionChunkBatch( + 0, + RegionalAttentionBranch.POSITIVE, + 0, + latent_batch_size, + ), + RegionalAttentionChunkBatch( + 1, + RegionalAttentionBranch.NEGATIVE, + latent_batch_size, + latent_batch_size * 2, + ), + ) + return BatchedRegionalAttentionContexts( + latent_batch_size, + chunks, + torch.zeros((latent_batch_size * 2, 1, 2)), + (), + ) diff --git a/tests/test_regional_activation_geometry.py b/tests/test_regional_activation_geometry.py new file mode 100644 index 0000000..3dd5a9f --- /dev/null +++ b/tests/test_regional_activation_geometry.py @@ -0,0 +1,223 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify model-neutral regional activation geometry invariants.""" + +from __future__ import annotations + +import pytest + +from simple_syrup.domain.regional_activation_geometry import ( + RegionalActivationBatchAlignment, + RegionalActivationGeometry, + RegionalActivationLayout, + RegionalTemporalOwnership, +) +from simple_syrup.domain.spatial_views import ( + SpatialBatchLayout, + SpatialView, + SpatialViewKind, +) + + +def test_batch_alignment_composes_cfg_latent_and_view_major_counts() -> None: + """Use existing layout evidence to derive one exact invocation batch.""" + + layout = SpatialBatchLayout( + canvas_width=8, + canvas_height=6, + views=( + SpatialView(SpatialViewKind.TILE, 0, 0, 4, 6, 4, 6), + SpatialView(SpatialViewKind.TILE, 4, 0, 4, 6, 4, 6), + ), + input_batch_size=4, + ) + + alignment = RegionalActivationBatchAlignment( + latent_batch_size=2, + chunk_count=2, + spatial_layout=layout, + ) + + assert alignment.base_batch_size == 4 + assert alignment.invocation_batch_size == 8 + + +@pytest.mark.parametrize( + ("layout", "shape", "height", "width", "mask_shape"), + [ + ( + RegionalActivationLayout.DIRECT_CONVOLUTION_1D, + (4, 8, 12), + 1, + 12, + (3, 4, 1, 12), + ), + ( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (4, 8, 6, 12), + 6, + 12, + (3, 4, 1, 6, 12), + ), + ( + RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS, + (4, 72, 8), + 6, + 12, + (3, 4, 72, 1), + ), + ( + RegionalActivationLayout.CONSUMER_SPATIALIZED, + (4, 72, 8), + 6, + 12, + (3, 4, 72, 1), + ), + ], +) +def test_geometry_admits_explicit_image_layouts( + layout: RegionalActivationLayout, + shape: tuple[int, ...], + height: int, + width: int, + mask_shape: tuple[int, ...], +) -> None: + """Admit exact image layouts without inferring spatial dimensions.""" + + feature_axis = 1 if "convolution" in layout.value else 2 + geometry = RegionalActivationGeometry( + layout=layout, + invocation_shape=shape, + feature_axis=feature_axis, + spatial_height=height, + spatial_width=width, + batch_alignment=RegionalActivationBatchAlignment(2, 2), + ) + + assert geometry.broadcast_mask_shape(3) == mask_shape + assert geometry.temporal_size is None + + +def test_geometry_admits_conv3d_only_with_explicit_temporal_ownership() -> None: + """Require the caller to own repetition of authored masks over time.""" + + geometry = RegionalActivationGeometry( + layout=RegionalActivationLayout.DIRECT_CONVOLUTION_3D, + invocation_shape=(2, 8, 3, 6, 12), + feature_axis=1, + spatial_height=6, + spatial_width=12, + temporal_axis=2, + temporal_ownership=RegionalTemporalOwnership.REPEAT_SPATIAL_MASK, + batch_alignment=RegionalActivationBatchAlignment(2, 1), + ) + + assert geometry.temporal_size == 3 + assert geometry.broadcast_mask_shape(4) == (4, 2, 1, 3, 6, 12) + + +@pytest.mark.parametrize( + ("case", "message"), + [ + ("layout-batch", "input batch"), + ("invocation-batch", "leading batch"), + ("token-count", "expected shape"), + ("conv-height", "height one"), + ("conv3d-ownership", "explicit repeated"), + ("temporal-on-image", "cannot declare a temporal axis"), + ("view-shape", "mismatched view indices"), + ], +) +def test_geometry_rejects_ambiguous_or_divergent_layouts( + case: str, + message: str, +) -> None: + """Reject every geometry claim not proven by explicit invocation evidence.""" + + with pytest.raises(ValueError, match=message): + _invalid_case(case) + + +def _invalid_case(case: str) -> object: + """Construct one invalid value after parametrized test collection.""" + + if case == "layout-batch": + return RegionalActivationBatchAlignment( + 2, + 2, + _full_layout(input_batch_size=2), + ) + if case == "invocation-batch": + return RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (3, 8, 6, 12), + 1, + 6, + 12, + RegionalActivationBatchAlignment(2, 2), + ) + if case == "token-count": + return RegionalActivationGeometry( + RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS, + (4, 71, 8), + 2, + 6, + 12, + RegionalActivationBatchAlignment(2, 2), + ) + if case == "conv-height": + return RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_1D, + (4, 8, 12), + 1, + 2, + 12, + RegionalActivationBatchAlignment(2, 2), + ) + if case == "conv3d-ownership": + return RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_3D, + (4, 8, 3, 6, 12), + 1, + 6, + 12, + RegionalActivationBatchAlignment(2, 2), + temporal_axis=2, + ) + if case == "temporal-on-image": + return RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (4, 8, 6, 12), + 1, + 6, + 12, + RegionalActivationBatchAlignment(2, 2), + temporal_axis=2, + ) + if case == "view-shape": + return RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (2, 8, 3, 8), + 1, + 3, + 8, + RegionalActivationBatchAlignment( + 2, + 1, + _full_layout(input_batch_size=2), + ), + ) + raise AssertionError(f"Unknown regional activation invalid case: {case}") + + +def _full_layout(*, input_batch_size: int) -> SpatialBatchLayout: + """Return one full-canvas layout with an explicit input batch.""" + + return SpatialBatchLayout( + 12, + 6, + (SpatialView(SpatialViewKind.FULL, 0, 0, 12, 6, 12, 6),), + input_batch_size, + ) diff --git a/tests/test_regional_activation_geometry_providers.py b/tests/test_regional_activation_geometry_providers.py new file mode 100644 index 0000000..b4458e8 --- /dev/null +++ b/tests/test_regional_activation_geometry_providers.py @@ -0,0 +1,118 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify explicit runtime providers for regional activation geometry.""" + +from __future__ import annotations + +import pytest +import torch + +from simple_syrup.domain.regional_activation_geometry import ( + RegionalActivationBatchAlignment, + RegionalActivationLayout, + RegionalTemporalOwnership, +) +from simple_syrup.runtime.regional_lora.activation_geometry_providers import ( + DirectConvolutionGeometryProvider, + SpatialTokenGeometryProvider, +) + + +@pytest.mark.parametrize( + ("shape", "layout", "height", "width"), + [ + ((2, 4, 9), RegionalActivationLayout.DIRECT_CONVOLUTION_1D, 1, 9), + ((2, 4, 5, 9), RegionalActivationLayout.DIRECT_CONVOLUTION_2D, 5, 9), + ], +) +def test_direct_provider_reads_exact_image_tensor_axes( + shape: tuple[int, ...], + layout: RegionalActivationLayout, + height: int, + width: int, +) -> None: + """Describe only the conventional dimensions observed on the tensor.""" + + geometry = DirectConvolutionGeometryProvider().resolve( + torch.ones(shape), + batch_alignment=RegionalActivationBatchAlignment(2, 1), + ) + + assert geometry.layout is layout + assert geometry.spatial_height == height + assert geometry.spatial_width == width + + +def test_direct_provider_requires_explicit_conv3d_temporal_ownership() -> None: + """Admit temporal convolution only when authored-mask repetition is declared.""" + + activation = torch.ones((2, 4, 3, 5, 9)) + with pytest.raises(ValueError, match="explicit repeated"): + DirectConvolutionGeometryProvider().resolve( + activation, + batch_alignment=RegionalActivationBatchAlignment(2, 1), + ) + + geometry = DirectConvolutionGeometryProvider( + temporal_ownership=RegionalTemporalOwnership.REPEAT_SPATIAL_MASK + ).resolve( + activation, + batch_alignment=RegionalActivationBatchAlignment(2, 1), + ) + + assert geometry.temporal_size == 3 + + +@pytest.mark.parametrize("consumer_spatialized", [False, True]) +def test_token_provider_requires_published_rectangular_grid( + consumer_spatialized: bool, +) -> None: + """Accept a non-square token grid only from explicit H/W publication.""" + + geometry = SpatialTokenGeometryProvider( + spatial_height=3, + spatial_width=8, + consumer_spatialized=consumer_spatialized, + ).resolve( + torch.ones((2, 24, 6)), + batch_alignment=RegionalActivationBatchAlignment(2, 1), + ) + + expected = ( + RegionalActivationLayout.CONSUMER_SPATIALIZED + if consumer_spatialized + else RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS + ) + assert geometry.layout is expected + + +def test_token_provider_does_not_infer_grid_from_token_count() -> None: + """Reject a mismatched published grid even when token count has square factors.""" + + with pytest.raises(ValueError, match="expected shape"): + SpatialTokenGeometryProvider(spatial_height=4, spatial_width=4).resolve( + torch.ones((2, 24, 6)), + batch_alignment=RegionalActivationBatchAlignment(2, 1), + ) + + +@pytest.mark.parametrize( + "activation", + [ + torch.ones((1, 4, 5, 5)), + torch.ones((2, 4, 5, 5), dtype=torch.int64), + torch.ones((2, 4)), + ], +) +def test_direct_provider_rejects_batch_dtype_and_rank_mismatch( + activation: torch.Tensor, +) -> None: + """Fail before publishing incomplete or incompatible invocation geometry.""" + + with pytest.raises((TypeError, ValueError)): + DirectConvolutionGeometryProvider().resolve( + activation, + batch_alignment=RegionalActivationBatchAlignment(2, 1), + ) diff --git a/tests/test_regional_activation_mask_projection.py b/tests/test_regional_activation_mask_projection.py new file mode 100644 index 0000000..b79a88c --- /dev/null +++ b/tests/test_regional_activation_mask_projection.py @@ -0,0 +1,217 @@ +# 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 canonical-mask projection into regional rank activations.""" + +from __future__ import annotations + +import torch + +from simple_syrup.domain.regional_activation_geometry import ( + RegionalActivationBatchAlignment, + RegionalActivationGeometry, + RegionalActivationLayout, + RegionalTemporalOwnership, +) +from simple_syrup.domain.regional_mask_bank import RegionalMaskBank +from simple_syrup.domain.spatial_views import ( + SpatialBatchLayout, + SpatialView, + SpatialViewKind, +) +from simple_syrup.masking.regional_activation_mask_projection import ( + RegionalActivationMaskBatch, + RegionalActivationMaskProjector, +) +from simple_syrup.masking.regional_mask_projection import ( + RegionalMaskForm, + RegionalMaskProjectionMode, +) + + +def test_full_token_projection_preserves_soft_overlap_and_uncovered_pixels() -> None: + """Flatten exact soft full-canvas masks without normalizing overlap or gaps.""" + + masks = torch.tensor( + [ + [[1.0, 0.5, 0.0], [1.0, 0.5, 0.0]], + [[0.0, 0.75, 0.0], [0.0, 0.75, 0.0]], + ] + ) + bank = _bank(masks) + geometry = RegionalActivationGeometry( + RegionalActivationLayout.FLATTENED_SPATIAL_TOKENS, + (2, 6, 4), + 2, + 2, + 3, + RegionalActivationBatchAlignment(1, 2), + ) + + projected = _project(bank, geometry, RegionalMaskProjectionMode.SOFT) + + expected = masks.flatten(start_dim=1).unsqueeze(1).expand(-1, 2, -1).unsqueeze(-1) + torch.testing.assert_close(projected.multipliers, expected) + assert float(projected.multipliers[:, :, 1].sum()) == 2.5 + assert projected.multipliers[:, :, 2].eq(0.0).all() + + +def test_tiled_projection_uses_view_chunk_then_latent_order() -> None: + """Repeat each cropped tile over the layout's authoritative base batch.""" + + masks = torch.tensor([[[0.25, 0.25, 0.75, 0.75]]]).expand(1, 2, 4).clone() + bank = _bank(masks) + layout = SpatialBatchLayout( + 4, + 2, + ( + SpatialView(SpatialViewKind.TILE, 0, 0, 2, 2, 2, 2), + SpatialView(SpatialViewKind.TILE, 2, 0, 2, 2, 2, 2), + ), + input_batch_size=4, + ) + geometry = RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (8, 3, 2, 2), + 1, + 2, + 2, + RegionalActivationBatchAlignment(2, 2, layout), + ) + + projected = _project(bank, geometry, RegionalMaskProjectionMode.HARD_PRESERVING) + + torch.testing.assert_close( + projected.multipliers[0, :, 0, 0, 0], + torch.tensor([0.25] * 4 + [0.75] * 4), + ) + + +def test_contextual_projection_resizes_complete_source_to_model_grid() -> None: + """Use the existing contextual full-source view as the crop/resize authority.""" + + masks = torch.arange(24, dtype=torch.float32).reshape(1, 4, 6) / 23.0 + bank = _bank(masks) + layout = SpatialBatchLayout( + 6, + 4, + ( + SpatialView( + SpatialViewKind.CONTEXTUAL_GLOBAL, + 0, + 0, + 6, + 4, + 3, + 2, + ), + ), + input_batch_size=1, + ) + geometry = RegionalActivationGeometry( + RegionalActivationLayout.CONSUMER_SPATIALIZED, + (1, 6, 5), + 2, + 2, + 3, + RegionalActivationBatchAlignment(1, 1, layout), + ) + + projected = _project( + bank, + geometry, + RegionalMaskProjectionMode.CONTINUOUS_COVERAGE, + ) + + assert projected.multipliers.shape == (1, 1, 6, 1) + assert projected.multipliers.min() >= 0.0 + assert projected.multipliers.max() <= 1.0 + + +def test_conv3d_projection_repeats_spatial_ownership_over_declared_time() -> None: + """Repeat one authored image mask only under explicit temporal ownership.""" + + bank = _bank(torch.tensor([[[1.0, 0.0], [0.5, 0.25]]])) + geometry = RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_3D, + (1, 4, 3, 2, 2), + 1, + 2, + 2, + RegionalActivationBatchAlignment(1, 1), + temporal_axis=2, + temporal_ownership=RegionalTemporalOwnership.REPEAT_SPATIAL_MASK, + ) + + projected = _project(bank, geometry, RegionalMaskProjectionMode.SOFT) + + assert projected.multipliers.shape == (1, 1, 1, 3, 2, 2) + torch.testing.assert_close( + projected.multipliers[:, :, :, 0], + projected.multipliers[:, :, :, 2], + ) + + +def test_projection_does_not_mutate_canonical_masks() -> None: + """Leave tensor identity, version, dtype, device, and contents unchanged.""" + + masks = torch.rand((2, 3, 5)) + bank = _bank(masks) + before = _fingerprint(bank) + geometry = RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (1, 4, 2, 4), + 1, + 2, + 4, + RegionalActivationBatchAlignment(1, 1), + ) + + _project(bank, geometry, RegionalMaskProjectionMode.CONTINUOUS_COVERAGE) + + assert _fingerprint(bank) == before + + +def _project( + bank: RegionalMaskBank, + geometry: RegionalActivationGeometry, + mode: RegionalMaskProjectionMode, +) -> RegionalActivationMaskBatch: + """Project one test case through the complete typed boundary.""" + + return RegionalActivationMaskProjector().project( + bank=bank, + geometry=geometry, + form=RegionalMaskForm.CONDITIONING, + mode=mode, + device=torch.device("cpu"), + dtype=torch.float32, + ) + + +def _bank(masks: torch.Tensor) -> RegionalMaskBank: + """Create independent planning and conditioning masks.""" + + return RegionalMaskBank( + masks.clone(), + masks.clone(), + int(masks.shape[2]), + int(masks.shape[1]), + ) + + +def _fingerprint(bank: RegionalMaskBank) -> tuple[tuple[object, ...], ...]: + """Capture source identity and state without copying its contents.""" + + return tuple( + ( + tensor.data_ptr(), + tensor._version, + tuple(tensor.shape), + tensor.dtype, + tensor.device, + float(tensor.sum()), + ) + for tensor in (bank.planning_masks, bank.conditioning_masks) + ) diff --git a/tests/test_regional_attention.py b/tests/test_regional_attention.py index 1c25f00..078cfe5 100644 --- a/tests/test_regional_attention.py +++ b/tests/test_regional_attention.py @@ -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 immutable raw and processed Attention Coupling plan contracts.""" from __future__ import annotations diff --git a/tests/test_regional_attention_batching.py b/tests/test_regional_attention_batching.py index fd73964..e5a5c57 100644 --- a/tests/test_regional_attention_batching.py +++ b/tests/test_regional_attention_batching.py @@ -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 CFG-chunk and latent-batch regional context alignment.""" from __future__ import annotations diff --git a/tests/test_regional_attention_diagnostics.py b/tests/test_regional_attention_diagnostics.py index af8f61c..755bd97 100644 --- a/tests/test_regional_attention_diagnostics.py +++ b/tests/test_regional_attention_diagnostics.py @@ -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 backend-neutral regional execution diagnostics.""" from __future__ import annotations diff --git a/tests/test_regional_attention_execution_context.py b/tests/test_regional_attention_execution_context.py index f6292a8..4104651 100644 --- a/tests/test_regional_attention_execution_context.py +++ b/tests/test_regional_attention_execution_context.py @@ -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 model-neutral regional attention context lifetime ownership.""" from __future__ import annotations diff --git a/tests/test_regional_attention_model_call.py b/tests/test_regional_attention_model_call.py index e453c1b..335b46b 100644 --- a/tests/test_regional_attention_model_call.py +++ b/tests/test_regional_attention_model_call.py @@ -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-call regional CFG, UUID, schedule, and batch resolution.""" from __future__ import annotations diff --git a/tests/test_regional_attention_model_call_values.py b/tests/test_regional_attention_model_call_values.py index 5b572fa..ee14e95 100644 --- a/tests/test_regional_attention_model_call_values.py +++ b/tests/test_regional_attention_model_call_values.py @@ -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 model-call sigma and conditioning UUID normalization.""" from __future__ import annotations diff --git a/tests/test_regional_attention_query_masks.py b/tests/test_regional_attention_query_masks.py index 3f39b91..7d7e3cf 100644 --- a/tests/test_regional_attention_query_masks.py +++ b/tests/test_regional_attention_query_masks.py @@ -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 backend-neutral regional query-mask batching.""" from __future__ import annotations diff --git a/tests/test_regional_attention_selection.py b/tests/test_regional_attention_selection.py index 355b54b..73482c2 100644 --- a/tests/test_regional_attention_selection.py +++ b/tests/test_regional_attention_selection.py @@ -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 current-sigma regional conditioning entry selection.""" from __future__ import annotations diff --git a/tests/test_regional_attention_weights.py b/tests/test_regional_attention_weights.py index d3a4a89..05ea8cf 100644 --- a/tests/test_regional_attention_weights.py +++ b/tests/test_regional_attention_weights.py @@ -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 normalized regional attention weighting and output composition.""" from __future__ import annotations diff --git a/tests/test_regional_attention_weights_comfy_equivalence.py b/tests/test_regional_attention_weights_comfy_equivalence.py index 0c066d9..8da040b 100644 --- a/tests/test_regional_attention_weights_comfy_equivalence.py +++ b/tests/test_regional_attention_weights_comfy_equivalence.py @@ -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 + """Compare regional attention weighting with installed Comfy default conditions.""" from __future__ import annotations diff --git a/tests/test_regional_cache_execution_interop.py b/tests/test_regional_cache_execution_interop.py index 36bfdd2..dee3b20 100644 --- a/tests/test_regional_cache_execution_interop.py +++ b/tests/test_regional_cache_execution_interop.py @@ -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 cache admission against regional execution identity changes.""" from __future__ import annotations diff --git a/tests/test_regional_capability_admission_service.py b/tests/test_regional_capability_admission_service.py index 20627c2..9853f1a 100644 --- a/tests/test_regional_capability_admission_service.py +++ b/tests/test_regional_capability_admission_service.py @@ -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 regional requests are admitted against sampler and model policy.""" from __future__ import annotations diff --git a/tests/test_regional_conditioning_output.py b/tests/test_regional_conditioning_output.py index 57c262c..3565695 100644 --- a/tests/test_regional_conditioning_output.py +++ b/tests/test_regional_conditioning_output.py @@ -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-equivalent within-region active-entry output combination.""" from __future__ import annotations diff --git a/tests/test_regional_context_validation.py b/tests/test_regional_context_validation.py index 698d2c0..fb2f547 100644 --- a/tests/test_regional_context_validation.py +++ b/tests/test_regional_context_validation.py @@ -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 separate Anima and standard-UNet processed-context policies.""" from __future__ import annotations diff --git a/tests/test_regional_convolution_execution.py b/tests/test_regional_convolution_execution.py new file mode 100644 index 0000000..02151a4 --- /dev/null +++ b/tests/test_regional_convolution_execution.py @@ -0,0 +1,618 @@ +# 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 generic regional convolution execution against references.""" + +from __future__ import annotations + +import pytest +import torch +import torch.nn.functional as functional +from comfy.weight_adapter.lora import LoRAAdapter +from torch import nn + +from simple_syrup.domain.regional_activation_geometry import ( + RegionalActivationBatchAlignment, + RegionalActivationGeometry, + RegionalActivationLayout, + RegionalTemporalOwnership, +) +from simple_syrup.domain.spatial_views import ( + SpatialBatchLayout, + SpatialView, + SpatialViewKind, +) +from simple_syrup.masking.regional_activation_mask_projection import ( + RegionalActivationMaskBatch, +) +from simple_syrup.runtime.regional_lora.convolution_execution import ( + RegionalConvolutionExecutor, +) +from simple_syrup.runtime.regional_lora.convolution_execution_plan import ( + RegionalConvolutionExecutionPlan, + RegionalConvolutionParameters, + RegionalConvolutionTargetUse, +) +from simple_syrup.runtime.regional_lora.convolution_preparation import ( + RegionalConvolutionPreparation, +) + + +@pytest.mark.parametrize("dimension", [1, 2, 3]) +def test_direct_convolution_dimensions_match_rank_space_reference( + dimension: int, +) -> None: + """Match exact Conv1d/2d/3d down-mask-up math with spatial kernels.""" + + fixture = _fixture(dimension=dimension, kernel_size=3, padding=1) + mask = torch.linspace(0.0, 1.0, fixture.rank_element_count).reshape( + 1, + *fixture.rank_spatial, + ) + + result = fixture.execute(mask, schedule=0.6) + + rank = _conv( + fixture.inputs, + fixture.down, + stride=fixture.parameters.stride, + padding=fixture.parameters.padding, + dilation=fixture.parameters.dilation, + groups=fixture.parameters.groups, + ) + expected = fixture.original(fixture.inputs) + _conv( + rank * mask.unsqueeze(1) * 0.8 * 0.6, + fixture.up, + stride=(1,) * dimension, + padding=(0,) * dimension, + dilation=(1,) * dimension, + groups=1, + ) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-6) + + +def test_pointwise_convolution_all_one_matches_global_delta() -> None: + """Match ordinary complete-image convolution LoRA at full coverage.""" + + fixture = _fixture(dimension=2, kernel_size=1, padding=0) + + result = fixture.execute(torch.ones((1, *fixture.rank_spatial))) + + expected = fixture.original(fixture.inputs) + _conv( + _conv( + fixture.inputs, + fixture.down, + stride=fixture.parameters.stride, + padding=fixture.parameters.padding, + dilation=fixture.parameters.dilation, + groups=1, + ) + * 0.8, + fixture.up, + stride=(1, 1), + padding=(0, 0), + dilation=(1, 1), + groups=1, + ) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-6) + + +def test_locon_middle_applies_mask_after_spatial_middle() -> None: + """Project pointwise down through middle kernel before regional masking.""" + + fixture = _fixture(dimension=2, kernel_size=3, padding=1, use_middle=True) + mask = torch.linspace(0.2, 0.9, fixture.rank_element_count).reshape( + 1, + *fixture.rank_spatial, + ) + + result = fixture.execute(mask) + + pointwise = _conv( + fixture.inputs, + fixture.down, + stride=fixture.parameters.stride, + padding=fixture.parameters.padding, + dilation=fixture.parameters.dilation, + groups=1, + ) + assert fixture.middle is not None + rank = _conv( + pointwise, + fixture.middle, + stride=(1, 1), + padding=(0, 0), + dilation=(1, 1), + groups=1, + ) + expected = fixture.original(fixture.inputs) + _conv( + rank * mask.unsqueeze(1) * 0.8, + fixture.up, + stride=(1, 1), + padding=(0, 0), + dilation=(1, 1), + groups=1, + ) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-6) + + +def test_stride_changes_rank_mask_and_output_resolution_exactly() -> None: + """Require masks at down-convolution output resolution, not input resolution.""" + + fixture = _fixture(dimension=2, kernel_size=3, padding=1, stride=2) + assert fixture.inputs.shape[-2:] != fixture.rank_spatial + + result = fixture.execute(torch.ones((1, *fixture.rank_spatial))) + + assert tuple(result.shape[-2:]) == fixture.rank_spatial + with pytest.raises(ValueError, match="exact rank activation shape"): + fixture.execute(torch.ones((1, *fixture.inputs.shape[-2:]))) + + +def test_zero_mask_and_schedule_skip_preparation_but_call_original_once() -> None: + """Avoid device preparation for inactive convolution adapters.""" + + fixture = _fixture(dimension=2, kernel_size=3, padding=1) + original = _CountedOriginal(fixture.original) + + result = fixture.execute( + torch.zeros((1, *fixture.rank_spatial)), + original=original, + ) + + assert torch.equal(result, fixture.original(fixture.inputs)) + assert original.calls == 1 + assert fixture.use.preparation._prepared == {} + + +def test_multiple_adapters_preserve_declared_addition_order() -> None: + """Add distinct convolution targets in declared schedule order.""" + + fixture = _fixture(dimension=2, kernel_size=3, padding=1) + second_down = fixture.down * -0.5 + second_up = fixture.up * 0.25 + second = RegionalConvolutionTargetUse( + 1, + 1, + (id(second_down), id(second_up), None), + RegionalConvolutionPreparation(second_down, None, second_up), + fixture.parameters, + 0.4, + ) + plan = RegionalConvolutionExecutionPlan((fixture.use, second)) + masks = torch.stack( + ( + torch.full(fixture.rank_spatial, 0.25), + torch.full(fixture.rank_spatial, 0.75), + ) + ) + + result = fixture.execute(masks, schedules=(0.5, 0.75), plan=plan) + + expected = fixture.original(fixture.inputs) + for down, up, mask, scale in ( + (fixture.down, fixture.up, masks[0], 0.8 * 0.5), + (second_down, second_up, masks[1], 0.4 * 0.75), + ): + rank = _conv( + fixture.inputs, + down, + stride=fixture.parameters.stride, + padding=fixture.parameters.padding, + dilation=fixture.parameters.dilation, + groups=1, + ) + expected = expected + _conv( + rank * mask.unsqueeze(0).unsqueeze(0) * scale, + up, + stride=(1, 1), + padding=(0, 0), + dilation=(1, 1), + groups=1, + ) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-6) + + +def test_grouped_convolution_matches_grouped_merged_weight_reference() -> None: + """Repeat rank bases per group so all-one execution equals merged weight math.""" + + original = nn.Conv2d(4, 6, 3, padding=1, groups=2, bias=True) + inputs = torch.linspace(-1.0, 1.0, 2 * 4 * 5 * 5).reshape(2, 4, 5, 5) + down = torch.linspace(-0.2, 0.3, 2 * 2 * 3 * 3).reshape(2, 2, 3, 3) + up = torch.linspace(-0.4, 0.25, 6 * 2).reshape(6, 2, 1, 1) + parameters = RegionalConvolutionParameters( + 2, + (1, 1), + (1, 1), + (1, 1), + 2, + 4, + 6, + (3, 3), + ) + use = RegionalConvolutionTargetUse( + 0, + 0, + (id(down), id(up), None), + RegionalConvolutionPreparation(down, None, up), + parameters, + 0.75, + ) + plan = RegionalConvolutionExecutionPlan((use,)) + masks = _mask_batch(torch.ones((1, 5, 5)), dimension=2, input_batch=2, features=4) + + result = RegionalConvolutionExecutor().execute( + original, + inputs, + plan=plan, + masks=masks, + schedule_strengths=(1.0,), + ) + + adapter = LoRAAdapter( + {"up", "down"}, + (up, down, None, None, None, None), + ) + patched = adapter.calculate_weight( + original.weight.detach().clone(), + "diffusion_model.grouped.weight", + 0.75, + 1.0, + None, + lambda value: value, + intermediate_dtype=torch.float32, + ) + expected = functional.conv2d( + inputs, + patched, + original.bias, + stride=1, + padding=1, + groups=2, + ) + torch.testing.assert_close(result, expected, rtol=1e-5, atol=1e-6) + + +def test_view_major_tiled_batch_keeps_each_tile_mask_on_its_rows() -> None: + """Apply distinct view masks over existing view/chunk/latent batch order.""" + + fixture = _fixture(dimension=2, kernel_size=1, padding=0) + fixture.inputs = fixture.inputs.repeat(2, 1, 1, 1) + fixture.rank_spatial = tuple(int(value) for value in fixture.inputs.shape[2:]) + view_values = torch.cat( + ( + torch.full((2, *fixture.rank_spatial), 0.25), + torch.full((2, *fixture.rank_spatial), 0.75), + ), + dim=0, + ) + masks = RegionalActivationMaskBatch( + view_values.unsqueeze(0).unsqueeze(2), + RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (4, 2, *fixture.rank_spatial), + 1, + fixture.rank_spatial[0], + fixture.rank_spatial[1], + RegionalActivationBatchAlignment(2, 1, _two_view_layout()), + ), + ) + + result = RegionalConvolutionExecutor().execute( + fixture.original, + fixture.inputs, + plan=fixture.plan, + masks=masks, + schedule_strengths=(1.0,), + ) + + rank = _conv( + fixture.inputs, + fixture.down, + stride=(1, 1), + padding=(0, 0), + dilation=(1, 1), + groups=1, + ) + expected = fixture.original(fixture.inputs) + _conv( + rank * view_values.unsqueeze(1) * 0.8, + fixture.up, + stride=(1, 1), + padding=(0, 0), + dilation=(1, 1), + groups=1, + ) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-6) + + +@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable") +@pytest.mark.parametrize("dtype", [torch.float16, torch.bfloat16]) +def test_cuda_low_precision_matches_explicit_reference(dtype: torch.dtype) -> None: + """Preserve full-rank convolution LoRA quality in FP16 and BF16.""" + + fixture = _fixture( + dimension=2, + kernel_size=3, + padding=1, + device=torch.device("cuda"), + dtype=dtype, + ) + mask = torch.linspace( + 0.0, + 1.0, + fixture.rank_element_count, + device="cuda", + dtype=dtype, + ).reshape(1, *fixture.rank_spatial) + + result = fixture.execute(mask) + + rank = _conv( + fixture.inputs, + fixture.down.to("cuda", dtype), + stride=fixture.parameters.stride, + padding=fixture.parameters.padding, + dilation=fixture.parameters.dilation, + groups=1, + ) + expected = fixture.original(fixture.inputs) + _conv( + rank * mask.unsqueeze(1) * 0.8, + fixture.up.to("cuda", dtype), + stride=(1, 1), + padding=(0, 0), + dilation=(1, 1), + groups=1, + ) + torch.testing.assert_close(result, expected, rtol=0.02, atol=0.02) + + +class _Fixture: + """Retain one deterministic dimensional convolution execution case.""" + + def __init__( + self, + *, + dimension: int, + kernel_size: int, + padding: int, + stride: int, + use_middle: bool, + device: torch.device, + dtype: torch.dtype, + ) -> None: + """Create exact original and low-rank convolution tensors.""" + + convolution = (nn.Conv1d, nn.Conv2d, nn.Conv3d)[dimension - 1] + spatial = (7,) * dimension + self.original = convolution( + 2, + 3, + kernel_size, + stride=stride, + padding=padding, + bias=True, + device=device, + dtype=dtype, + ) + self.inputs = torch.linspace( + -1.0, + 1.0, + 2 * 2 * (7**dimension), + device=device, + dtype=dtype, + ).reshape(2, 2, *spatial) + rank = 2 + down_kernel = (1,) * dimension if use_middle else (kernel_size,) * dimension + self.down = torch.linspace( + -0.3, + 0.4, + rank * 2 * (down_kernel[0] ** dimension), + ).reshape(rank, 2, *down_kernel) + self.middle = ( + torch.linspace(-0.2, 0.25, rank * rank * (kernel_size**dimension)).reshape( + rank, + rank, + *((kernel_size,) * dimension), + ) + if use_middle + else None + ) + self.up = torch.linspace(-0.4, 0.35, 3 * rank).reshape( + 3, + rank, + *((1,) * dimension), + ) + self.parameters = RegionalConvolutionParameters( + dimension, + (stride,) * dimension, + (padding,) * dimension, + (1,) * dimension, + 1, + 2, + 3, + (kernel_size,) * dimension, + ) + self.use = RegionalConvolutionTargetUse( + 0, + 0, + ( + id(self.down), + id(self.up), + None if self.middle is None else id(self.middle), + ), + RegionalConvolutionPreparation(self.down, self.middle, self.up), + self.parameters, + 0.8, + ) + self.plan = RegionalConvolutionExecutionPlan((self.use,)) + self.rank_spatial = _reference_rank_spatial(self.inputs, self.use) + self.rank_element_count = int(torch.tensor(self.rank_spatial).prod()) + + def execute( + self, + masks: torch.Tensor, + *, + schedule: float = 1.0, + schedules: tuple[float, ...] | None = None, + plan: RegionalConvolutionExecutionPlan | None = None, + original: object | None = None, + ) -> torch.Tensor: + """Execute this fixture with explicitly shaped rank masks.""" + + selected_plan = plan or self.plan + mask_batch = _mask_batch( + masks.to(self.inputs), + dimension=self.parameters.dimension, + input_batch=int(self.inputs.shape[0]), + features=2, + ) + selected_original = self.original if original is None else original + assert callable(selected_original) + return RegionalConvolutionExecutor().execute( + selected_original, + self.inputs, + plan=selected_plan, + masks=mask_batch, + schedule_strengths=schedules or (schedule,), + ) + + +class _CountedOriginal: + """Count original convolution calls while preserving output behavior.""" + + def __init__(self, original: nn.Module) -> None: + """Retain the original module and zero calls.""" + + self._original = original + self.calls = 0 + + def __call__(self, inputs: torch.Tensor) -> torch.Tensor: + """Count and forward one exact original call.""" + + self.calls += 1 + result = self._original(inputs) + assert isinstance(result, torch.Tensor) + return result + + +def _fixture( + *, + dimension: int, + kernel_size: int, + padding: int, + stride: int = 1, + use_middle: bool = False, + device: torch.device | None = None, + dtype: torch.dtype = torch.float32, +) -> _Fixture: + """Return one deterministic convolution execution fixture.""" + + return _Fixture( + dimension=dimension, + kernel_size=kernel_size, + padding=padding, + stride=stride, + use_middle=use_middle, + device=device or torch.device("cpu"), + dtype=dtype, + ) + + +def _mask_batch( + values: torch.Tensor, + *, + dimension: int, + input_batch: int, + features: int, +) -> RegionalActivationMaskBatch: + """Wrap region/spatial masks in direct convolution geometry.""" + + if values.ndim == dimension: + values = values.unsqueeze(0) + regions = int(values.shape[0]) + spatial = tuple(int(value) for value in values.shape[1:]) + invocation = (input_batch, features, *spatial) + layout = { + 1: RegionalActivationLayout.DIRECT_CONVOLUTION_1D, + 2: RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + 3: RegionalActivationLayout.DIRECT_CONVOLUTION_3D, + }[dimension] + geometry = RegionalActivationGeometry( + layout, + invocation, + 1, + 1 if dimension == 1 else spatial[-2], + spatial[-1], + RegionalActivationBatchAlignment(input_batch, 1), + temporal_axis=2 if dimension == 3 else None, + temporal_ownership=( + RegionalTemporalOwnership.REPEAT_SPATIAL_MASK + if dimension == 3 + else RegionalTemporalOwnership.NONE + ), + ) + expanded = values.reshape(regions, 1, 1, *spatial).expand( + -1, + input_batch, + -1, + *spatial, + ) + return RegionalActivationMaskBatch(expanded, geometry) + + +def _reference_rank_spatial( + inputs: torch.Tensor, + use: RegionalConvolutionTargetUse, +) -> tuple[int, ...]: + """Observe the exact explicit down/middle rank activation shape.""" + + rank = _conv( + inputs, + use.preparation.down.to(inputs), + stride=use.parameters.stride, + padding=use.parameters.padding, + dilation=use.parameters.dilation, + groups=use.parameters.groups, + ) + if use.preparation.middle is not None: + rank = _conv( + rank, + use.preparation.middle.to(inputs), + stride=(1,) * use.parameters.dimension, + padding=(0,) * use.parameters.dimension, + dilation=(1,) * use.parameters.dimension, + groups=1, + ) + return tuple(int(value) for value in rank.shape[2:]) + + +def _conv( + inputs: torch.Tensor, + weight: torch.Tensor, + *, + stride: tuple[int, ...], + padding: tuple[int, ...], + dilation: tuple[int, ...], + groups: int, +) -> torch.Tensor: + """Dispatch one explicit dimensional reference convolution.""" + + function = (functional.conv1d, functional.conv2d, functional.conv3d)[ + inputs.ndim - 3 + ] + return function(inputs, weight, None, stride, padding, dilation, groups) + + +def _two_view_layout() -> SpatialBatchLayout: + """Return two equal view-major tiles over a four-entry active batch.""" + + return SpatialBatchLayout( + 14, + 7, + ( + SpatialView(SpatialViewKind.TILE, 0, 0, 7, 7, 7, 7), + SpatialView(SpatialViewKind.TILE, 7, 0, 7, 7, 7, 7), + ), + input_batch_size=2, + ) diff --git a/tests/test_regional_convolution_execution_plan.py b/tests/test_regional_convolution_execution_plan.py new file mode 100644 index 0000000..ecdf0e0 --- /dev/null +++ b/tests/test_regional_convolution_execution_plan.py @@ -0,0 +1,184 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify convolution plans adapt exact bound Comfy operation evidence.""" + +from __future__ import annotations + +from dataclasses import replace +from typing import Literal + +import comfy.model_patcher +import pytest +import torch +from comfy.weight_adapter.lora import LoRAAdapter +from torch import nn + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraScheduleBoundary, +) +from simple_syrup.runtime.regional_lora.comfy_adapter_resolution import ( + ComfyAdapterTargetPath, + ComfyNormalizedAdapterTarget, + ComfyRegionalAdapterResolution, + ComfyRegionalLoraResolution, +) +from simple_syrup.runtime.regional_lora.convolution_execution_plan import ( + RegionalConvolutionExecutionPlanFactory, +) +from simple_syrup.runtime.regional_lora.resolved_operation_translator import ( + ComfyResolvedOperationTranslator, +) +from simple_syrup.runtime.regional_lora.target_binder import RegionalLoraTargetBinder +from simple_syrup.runtime.regional_lora.target_binding import ( + RegionalLoraBindingResult, +) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload + + +@pytest.mark.parametrize("dimension", [1, 2, 3]) +def test_factory_preserves_bound_dimension_parameters_and_tensor_identity( + dimension: int, +) -> None: + """Adapt real bound Conv1d/2d/3d evidence without copying normalized tensors.""" + + patcher, module = _patcher(dimension) + target = _target(dimension) + binding = _binding(patcher, target) + + plan = RegionalConvolutionExecutionPlanFactory().build(binding.entries) + + operation = target.operation + assert isinstance(operation, LoRAAdapter) + up, down, *_rest = operation.weights + assert plan.uses[0].preparation.down is down + assert plan.uses[0].preparation.up is up + assert plan.uses[0].parameters.dimension == dimension + assert plan.uses[0].parameters.stride == tuple(module.stride) + assert plan.uses[0].parameters.padding == tuple(module.padding) + assert plan.uses[0].parameters.dilation == tuple(module.dilation) + assert plan.uses[0].parameters.kernel_size == tuple(module.kernel_size) + + +def test_factory_rejects_nonzero_padding_mode_before_execution() -> None: + """Fail before sampling when functional down cannot match the original boundary.""" + + patcher, _module = _patcher(2, padding_mode="reflect") + binding = _binding(patcher, _target(2)) + + with pytest.raises(ValueError, match="zero padding mode"): + RegionalConvolutionExecutionPlanFactory().build(binding.entries) + + +def test_factory_rejects_malformed_pointwise_up_before_execution() -> None: + """Reject a bound-evidence mutation that cannot be an exact pointwise up path.""" + + patcher, _module = _patcher(2) + target = _target(2) + binding = _binding(patcher, target) + entry = binding.entries[0] + operation = target.operation + assert isinstance(operation, LoRAAdapter) + _up, down, alpha, middle, dora, reshape_shape = operation.weights + malformed = LoRAAdapter( + {"up", "down"}, + (torch.ones((3, 2, 2, 2)), down, alpha, middle, dora, reshape_shape), + ) + normalized = replace(target, operation=malformed) + altered = replace(entry, normalized_target=normalized) + + with pytest.raises(ValueError, match="pointwise"): + RegionalConvolutionExecutionPlanFactory().build((altered,)) + + +def _patcher( + dimension: int, + *, + padding_mode: Literal["zeros", "reflect", "replicate", "circular"] = "zeros", +) -> tuple[comfy.model_patcher.ModelPatcher, nn.Conv1d | nn.Conv2d | nn.Conv3d]: + """Return one real patcher around an observed dimensional convolution.""" + + convolution = (nn.Conv1d, nn.Conv2d, nn.Conv3d)[dimension - 1] + module = convolution( + 2, + 3, + 3, + stride=2, + padding=1, + dilation=1, + padding_mode=padding_mode, + bias=False, + ) + root = nn.Module() + root.diffusion_model = nn.Module() + root.diffusion_model.layer = module + return ( + comfy.model_patcher.ModelPatcher( + root, + torch.device("cpu"), + torch.device("cpu"), + ), + module, + ) + + +def _target(dimension: int) -> ComfyNormalizedAdapterTarget: + """Return one exact normalized direct dimensional LoRA target.""" + + spatial = (3,) * dimension + return ComfyNormalizedAdapterTarget( + ComfyAdapterTargetPath("diffusion_model.layer.weight", None), + LoRAAdapter( + {"up", "down"}, + ( + torch.ones((3, 2, *((1,) * dimension))), + torch.ones((2, 2, *spatial)), + None, + None, + None, + None, + ), + ), + "LoRAAdapter", + ("down", "up"), + True, + ) + + +def _binding( + patcher: comfy.model_patcher.ModelPatcher, + target: ComfyNormalizedAdapterTarget, +) -> RegionalLoraBindingResult: + """Resolve, translate, and bind one convolution target through U2-U4 values.""" + + adapter = RegionalLoraAdapterPlan( + RegionalLoraAdapterIdentity("conv.safetensors"), + 0, + 0, + RegionalLoraBranch.POSITIVE, + 0.8, + (RegionalLoraScheduleBoundary(0.0, 1.0, 1.0, 0),), + ) + resolution = ComfyRegionalLoraResolution( + ( + ComfyRegionalAdapterResolution( + adapter, + RegionalLoraHostPayload(False, None, {}, None), + (target,), + (), + (), + ), + ), + (), + ) + operations = ComfyResolvedOperationTranslator().translate(resolution) + return RegionalLoraTargetBinder().bind( + source=patcher, + candidate=patcher, + resolution=resolution, + operations=operations, + ) diff --git a/tests/test_regional_diagnostics_capture_probe.py b/tests/test_regional_diagnostics_capture_probe.py index 468982d..6aaefe4 100644 --- a/tests/test_regional_diagnostics_capture_probe.py +++ b/tests/test_regional_diagnostics_capture_probe.py @@ -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 benchmark-only structured regional diagnostic capture.""" from __future__ import annotations diff --git a/tests/test_regional_features.py b/tests/test_regional_features.py index 495e3c6..9930f4c 100644 --- a/tests/test_regional_features.py +++ b/tests/test_regional_features.py @@ -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 immutable regional feature requests, capabilities, and admissions.""" from __future__ import annotations diff --git a/tests/test_regional_hook_fixture_probe.py b/tests/test_regional_hook_fixture_probe.py index 5694b9a..dd03a52 100644 --- a/tests/test_regional_hook_fixture_probe.py +++ b/tests/test_regional_hook_fixture_probe.py @@ -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 benchmark-only regional HookGroup rejection fixtures.""" from __future__ import annotations diff --git a/tests/test_regional_host_operation_backing.py b/tests/test_regional_host_operation_backing.py new file mode 100644 index 0000000..936aab9 --- /dev/null +++ b/tests/test_regional_host_operation_backing.py @@ -0,0 +1,274 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Prove generic regional operation shells preserve Comfy host behavior.""" + +from __future__ import annotations + +from uuid import UUID + +import comfy.ops +import torch +from torch import nn + +from simple_syrup.domain.regional_activation_geometry import ( + RegionalActivationBatchAlignment, + RegionalActivationGeometry, + RegionalActivationLayout, +) +from simple_syrup.domain.regional_lora_plan import RegionalLoraAdapterIdentity +from simple_syrup.masking.regional_activation_mask_projection import ( + RegionalActivationMaskBatch, +) +from simple_syrup.runtime.regional_lora.convolution_execution_plan import ( + RegionalConvolutionExecutionPlan, + RegionalConvolutionParameters, + RegionalConvolutionTargetUse, +) +from simple_syrup.runtime.regional_lora.convolution_preparation import ( + RegionalConvolutionPreparation, +) +from simple_syrup.runtime.regional_lora.execution_cache import ( + ModelCloneLineage, + RegionalLoraExecutionCache, +) +from simple_syrup.runtime.regional_lora.host_operation_backing import ( + RegionalConvolutionOperationPatch, + RegionalLinearOperationPatch, +) +from simple_syrup.runtime.regional_lora.linear_execution_plan import ( + RegionalLinearExecutionPlan, + RegionalLinearOperationKey, + RegionalLinearTargetUse, +) +from simple_syrup.runtime.regional_lora.operation_invocation import ( + RegionalOperationInvocation, + RegionalOperationInvocationContext, +) +from simple_syrup.runtime.regional_lora.preparation import ( + RegionalLoraTargetPreparation, +) +from simple_syrup.runtime.regional_lora.standard_adapter import StandardLoraTarget + + +def test_linear_inactive_path_matches_exact_original_and_preserves_source() -> None: + """Delegate through the exact host class without mutating source registrations.""" + + original = comfy.ops.disable_weight_init.Linear(2, 2, bias=False) + original.weight = nn.Parameter(torch.eye(2), requires_grad=False) + original.register_buffer("calibration", torch.tensor([3.0])) + original.eval() + source_weight = original.weight + context = RegionalOperationInvocationContext() + patch = RegionalLinearOperationPatch( + "diffusion_model.linear", + original, + _linear_plan(), + context=context, + ) + inputs = torch.tensor([[[1.0, 2.0], [3.0, 4.0]]]) + + torch.testing.assert_close(patch(inputs), original(inputs)) + assert type(patch._host_backing.module) is type(original) + assert patch.weight is patch._host_backing.module.weight + assert patch.weight is not source_weight + assert patch.training is False + assert patch._host_backing.module.training is False + assert patch.calibration is patch._host_backing.module.calibration + assert "calibration" in dict(patch.named_buffers()) + patch.to(dtype=torch.float64) + assert patch.weight.dtype is torch.float64 + assert source_weight.dtype is torch.float32 + + +def test_linear_active_path_applies_only_task_local_regional_delta() -> None: + """Use the installed plan only inside its exact target invocation scope.""" + + original = comfy.ops.disable_weight_init.Linear(2, 2, bias=False) + original.weight = nn.Parameter(torch.eye(2), requires_grad=False) + context = RegionalOperationInvocationContext() + patch = RegionalLinearOperationPatch( + "diffusion_model.linear", + original, + _linear_plan(), + context=context, + ) + inputs = torch.tensor([[[1.0, 2.0], [3.0, 4.0]]]) + masks = _linear_masks(inputs.shape, torch.tensor([[[[1.0], [0.0]]]])) + + with context.activate( + {"diffusion_model.linear": RegionalOperationInvocation(masks, (1.0,))} + ): + output = patch(inputs) + + expected = inputs.clone() + expected[:, 0] += inputs[:, 0] + torch.testing.assert_close(output, expected) + torch.testing.assert_close(patch(inputs), inputs) + + +def test_host_weight_functions_and_lowvram_writes_reach_exact_shell() -> None: + """Mirror Comfy cast, global patch, low-VRAM, and transient deletion state.""" + + original = comfy.ops.disable_weight_init.Linear(2, 2, bias=False) + original.weight = nn.Parameter(torch.eye(2), requires_grad=False) + patch = RegionalLinearOperationPatch( + "diffusion_model.linear", + original, + _linear_plan(), + ) + inputs = torch.tensor([[1.0, 2.0]]) + patch.weight_function = [lambda weight: weight * 2.0] + patch.bias_function = [] + patch.comfy_cast_weights = True + patch.weight_lowvram_function = object() + patch.prev_comfy_cast_weights = False + + torch.testing.assert_close(patch(inputs), inputs * 2.0) + backing = patch._host_backing.module + assert backing.weight_function is patch.weight_function + assert backing.bias_function is patch.bias_function + assert backing.weight_lowvram_function is patch.weight_lowvram_function + assert backing.__dict__["prev_comfy_cast_weights"] is False + del patch.prev_comfy_cast_weights + assert not hasattr(patch, "prev_comfy_cast_weights") + assert not hasattr(backing, "prev_comfy_cast_weights") + + +def test_convolution_inactive_and_active_paths_preserve_host_semantics() -> None: + """Retain Conv2d parameters and add one direct regional convolution delta.""" + + original = comfy.ops.disable_weight_init.Conv2d( + 1, + 1, + 1, + bias=False, + ) + original.weight = nn.Parameter(torch.ones((1, 1, 1, 1)), requires_grad=False) + context = RegionalOperationInvocationContext() + patch = RegionalConvolutionOperationPatch( + "diffusion_model.conv", + original, + _convolution_plan(), + context=context, + ) + inputs = torch.arange(4, dtype=torch.float32).reshape(1, 1, 2, 2) + geometry = RegionalActivationGeometry( + RegionalActivationLayout.DIRECT_CONVOLUTION_2D, + (1, 1, 2, 2), + 1, + 2, + 2, + RegionalActivationBatchAlignment(1, 1), + ) + masks = RegionalActivationMaskBatch( + torch.tensor([[[[[1.0, 0.0], [0.0, 0.0]]]]]), + geometry, + ) + + torch.testing.assert_close(patch(inputs), inputs) + with context.activate( + {"diffusion_model.conv": RegionalOperationInvocation(masks, (1.0,))} + ): + output = patch(inputs) + + expected = inputs.clone() + expected[:, :, 0, 0] *= 2.0 + torch.testing.assert_close(output, expected) + assert type(patch._host_backing.module) is type(original) + assert patch.stride == original.stride + assert patch.padding == original.padding + assert patch.groups == original.groups + + +def _linear_plan() -> RegionalLinearExecutionPlan: + """Return one identity ordinary-LoRA plan for a two-feature Linear.""" + + identity = RegionalLoraAdapterIdentity("identity.safetensors") + target = StandardLoraTarget( + "diffusion_model.linear.weight", + torch.eye(2), + torch.eye(2), + 2, + 2, + 2, + 1.0, + ) + preparation = RegionalLoraTargetPreparation( + identity, + ModelCloneLineage(_uuid(1), _uuid(2)), + target, + RegionalLoraExecutionCache(), + ) + return RegionalLinearExecutionPlan( + ( + RegionalLinearTargetUse( + 0, + 0, + RegionalLinearOperationKey( + identity, + target.target, + id(target.down), + id(target.up), + ), + preparation, + 1.0, + ), + ) + ) + + +def _convolution_plan() -> RegionalConvolutionExecutionPlan: + """Return one identity 1x1 Conv2d ordinary-LoRA plan.""" + + parameters = RegionalConvolutionParameters( + 2, + (1, 1), + (0, 0), + (1, 1), + 1, + 1, + 1, + (1, 1), + ) + preparation = RegionalConvolutionPreparation( + torch.ones((1, 1, 1, 1)), + None, + torch.ones((1, 1, 1, 1)), + ) + return RegionalConvolutionExecutionPlan( + ( + RegionalConvolutionTargetUse( + 0, + 0, + (id(preparation.down), id(preparation.up), None), + preparation, + parameters, + 1.0, + ), + ) + ) + + +def _linear_masks( + shape: torch.Size, + multipliers: torch.Tensor, +) -> RegionalActivationMaskBatch: + """Return consumer-spatialized masks for one B/S/C input.""" + + geometry = RegionalActivationGeometry( + RegionalActivationLayout.CONSUMER_SPATIALIZED, + tuple(shape), + 2, + 1, + int(shape[1]), + RegionalActivationBatchAlignment(int(shape[0]), 1), + ) + return RegionalActivationMaskBatch(multipliers, geometry) + + +def _uuid(value: int) -> UUID: + """Return one deterministic UUID without leaking construction into fixtures.""" + + return UUID(int=value) diff --git a/tests/test_regional_linear_execution.py b/tests/test_regional_linear_execution.py new file mode 100644 index 0000000..39612c6 --- /dev/null +++ b/tests/test_regional_linear_execution.py @@ -0,0 +1,424 @@ +# 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 model-neutral regional Linear execution semantics.""" + +from __future__ import annotations + +from typing import cast +from uuid import uuid4 + +import pytest +import torch +import torch.nn.functional as functional +from comfy.weight_adapter.lora import LoRAAdapter +from torch import nn + +from simple_syrup.domain.regional_activation_geometry import ( + RegionalActivationBatchAlignment, + RegionalActivationGeometry, + RegionalActivationLayout, +) +from simple_syrup.domain.regional_lora_plan import RegionalLoraAdapterIdentity +from simple_syrup.masking.regional_activation_mask_projection import ( + RegionalActivationMaskBatch, +) +from simple_syrup.runtime.regional_lora.execution_cache import ( + ModelCloneLineage, + RegionalLoraExecutionCache, +) +from simple_syrup.runtime.regional_lora.linear_execution import RegionalLinearExecutor +from simple_syrup.runtime.regional_lora.linear_execution_plan import ( + RegionalLinearExecutionPlan, + RegionalLinearOperationKey, + RegionalLinearTargetUse, +) +from simple_syrup.runtime.regional_lora.preparation import ( + RegionalLoraTargetPreparation, +) +from simple_syrup.runtime.regional_lora.standard_adapter import StandardLoraTarget + + +def test_zero_mask_returns_exact_original_and_performs_no_projection() -> None: + """Call the original once and avoid preparing inactive adapter weights.""" + + fixture = _fixture() + plan = _plan((_use(fixture, composition=0, region=0),)) + original = _Original(fixture.base) + masks = _masks(torch.zeros((1, 2, 6)), features=4) + + result = RegionalLinearExecutor().execute( + original, + fixture.inputs, + "marker", + plan=plan, + masks=masks, + schedule_strengths=(1.0,), + expected="value", + ) + + expected = fixture.base(fixture.inputs) + assert torch.equal(result, expected) + assert original.calls == [("marker", "value")] + assert fixture.cache.size == 0 + + +def test_all_one_mask_matches_ordinary_global_lora_math() -> None: + """Match the unmerged full-rank additive LoRA reference over the whole image.""" + + fixture = _fixture() + plan = _plan((_use(fixture, composition=0, region=0, strength=0.75),)) + + result = _execute(fixture, plan, torch.ones((1, 2, 6)), (0.5,)) + + scale = 0.75 * 0.5 * fixture.target.intrinsic_scale + expected = fixture.base(fixture.inputs) + _delta(fixture, fixture.inputs) * scale + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-7) + + +def test_all_one_mask_matches_comfy_calculated_weight() -> None: + """Match Comfy's installed ordinary-LoRA weight calculation at full coverage.""" + + fixture = _fixture() + strength = 0.75 + schedule = 0.5 + plan = _plan((_use(fixture, composition=0, region=0, strength=strength),)) + + result = _execute(fixture, plan, torch.ones((1, 2, 6)), (schedule,)) + + adapter = LoRAAdapter( + {"up", "down"}, + ( + fixture.target.up, + fixture.target.down, + fixture.target.rank * fixture.target.intrinsic_scale, + None, + None, + None, + ), + ) + patched_weight = adapter.calculate_weight( + fixture.base.weight.detach().clone(), + "diffusion_model.layer.weight", + strength * schedule, + 1.0, + None, + lambda value: value, + intermediate_dtype=torch.float32, + ) + expected = functional.linear(fixture.inputs, patched_weight, fixture.base.bias) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-7) + + +def test_complementary_regions_reconstruct_global_application() -> None: + """Partition one adapter spatially without changing its complete-image result.""" + + fixture = _fixture() + uses = ( + _use(fixture, composition=0, region=0), + _use(fixture, composition=1, region=1), + ) + plan = _plan(uses) + left = torch.zeros((2, 6)) + left[:, :3] = 1.0 + masks = torch.stack((left, 1.0 - left)) + + result = _execute(fixture, plan, masks, (1.0, 1.0)) + + expected = fixture.base(fixture.inputs) + _delta(fixture, fixture.inputs) * ( + fixture.target.intrinsic_scale + ) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-7) + assert len(plan.groups) == 1 + + +def test_fractional_schedule_matches_rank_space_reference() -> None: + """Apply authored soft coverage and schedule scale before the up projection.""" + + fixture = _fixture() + plan = _plan((_use(fixture, composition=0, region=0, strength=0.8),)) + mask = torch.linspace(0.0, 1.0, 12).reshape(1, 2, 6) + + result = _execute(fixture, plan, mask, (0.25,)) + + rank = functional.linear(fixture.inputs, fixture.target.down) + multiplier = mask[0] * 0.8 * 0.25 * fixture.target.intrinsic_scale + expected = fixture.base(fixture.inputs) + functional.linear( + rank * multiplier.unsqueeze(-1), + fixture.target.up, + ) + torch.testing.assert_close(result, expected, rtol=0, atol=0) + + +def test_different_adapters_preserve_declared_addition_order() -> None: + """Accumulate non-contiguous targets in exact composition order.""" + + fixture = _fixture() + second = _target( + "second", + torch.tensor([[0.3, -0.2, 0.1, 0.4], [-0.1, 0.2, 0.5, -0.3]]), + torch.tensor([[0.2, 0.1], [-0.4, 0.2], [0.3, -0.2]]), + ) + uses = ( + _use(fixture, composition=0, region=0), + _use(fixture, composition=1, region=1, target=second), + _use(fixture, composition=2, region=0), + ) + plan = _plan(uses) + masks = torch.stack( + ( + torch.full((2, 6), 0.25), + torch.full((2, 6), 0.75), + ) + ) + + result = _execute(fixture, plan, masks, (1.0, 0.5, 0.25)) + + expected = fixture.base(fixture.inputs) + for target, mask, schedule in ( + (fixture.target, masks[0], 1.0), + (second, masks[1], 0.5), + (fixture.target, masks[0], 0.25), + ): + rank = functional.linear(fixture.inputs, target.down) + expected = expected + functional.linear( + rank * (mask * schedule * target.intrinsic_scale).unsqueeze(-1), + target.up, + ) + torch.testing.assert_close(result, expected, rtol=1e-6, atol=1e-7) + assert len(plan.groups) == 3 + + +def test_repeated_adapter_combination_matches_separate_ordered_uses() -> None: + """Combine adjacent exact targets without changing fractional spatial math.""" + + fixture = _fixture() + uses = ( + _use(fixture, composition=0, region=0, strength=0.5), + _use(fixture, composition=1, region=1, strength=0.25), + ) + plan = _plan(uses) + masks = torch.stack((torch.full((2, 6), 0.4), torch.full((2, 6), 0.7))) + + result = _execute(fixture, plan, masks, (0.8, 0.6)) + + rank = functional.linear(fixture.inputs, fixture.target.down) + combined = ( + masks[0] * 0.5 * 0.8 + masks[1] * 0.25 * 0.6 + ) * fixture.target.intrinsic_scale + expected = fixture.base(fixture.inputs) + functional.linear( + rank * combined.unsqueeze(-1), + fixture.target.up, + ) + torch.testing.assert_close(result, expected, rtol=0, atol=0) + assert len(plan.groups) == 1 + assert fixture.cache.size == 1 + + +def test_failure_and_clear_release_local_preparation_state() -> None: + """Validate before preparation and release successful preparation explicitly.""" + + fixture = _fixture() + plan = _plan((_use(fixture, composition=0, region=0),)) + wrong = _masks(torch.ones((1, 1, 6)), features=4) + with pytest.raises(ValueError, match="input must match"): + RegionalLinearExecutor().execute( + fixture.base, + fixture.inputs, + plan=plan, + masks=wrong, + schedule_strengths=(1.0,), + ) + assert fixture.cache.size == 0 + + _execute(fixture, plan, torch.ones((1, 2, 6)), (1.0,)) + assert fixture.cache.size == 1 + plan.clear() + assert all(not preparation._prepared for preparation in _preparations(plan)) + + +@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable") +@pytest.mark.parametrize("dtype", [torch.float16, torch.bfloat16]) +def test_cuda_low_precision_matches_explicit_execution_dtype_reference( + dtype: torch.dtype, +) -> None: + """Preserve full-rank FP16/BF16 adapter math on the installed GPU.""" + + fixture = _fixture(device=torch.device("cuda"), dtype=dtype) + plan = _plan((_use(fixture, composition=0, region=0),)) + mask = torch.linspace(0.0, 1.0, 12, device="cuda", dtype=dtype).reshape(1, 2, 6) + + result = _execute(fixture, plan, mask, (1.0,)) + + rank = functional.linear(fixture.inputs, fixture.target.down.to("cuda", dtype)) + expected = fixture.base(fixture.inputs) + functional.linear( + rank * (mask[0] * fixture.target.intrinsic_scale).unsqueeze(-1), + fixture.target.up.to("cuda", dtype), + ) + torch.testing.assert_close(result, expected, rtol=0.01, atol=0.01) + + +class _Fixture: + """Retain deterministic Linear execution values for one device and dtype.""" + + def __init__(self, device: torch.device, dtype: torch.dtype) -> None: + """Create base, input, adapter, lineage, and cache state.""" + + self.base = nn.Linear(4, 3, bias=True, device=device, dtype=dtype) + with torch.no_grad(): + self.base.weight.copy_( + torch.tensor( + [ + [0.1, 0.2, -0.3, 0.4], + [0.5, -0.2, 0.1, 0.3], + [-0.4, 0.2, 0.6, -0.1], + ], + device=device, + dtype=dtype, + ) + ) + self.base.bias.copy_( + torch.tensor([0.1, -0.2, 0.3], device=device, dtype=dtype) + ) + self.inputs = torch.linspace( + -1.0, + 1.0, + 48, + device=device, + dtype=dtype, + ).reshape(2, 6, 4) + self.target = _target( + "first", + torch.tensor([[0.2, -0.1, 0.3, 0.4], [-0.3, 0.5, 0.2, -0.2]]), + torch.tensor([[0.4, -0.2], [0.1, 0.3], [-0.5, 0.2]]), + ) + self.lineage = ModelCloneLineage(uuid4(), uuid4()) + self.cache = RegionalLoraExecutionCache() + + +class _Original: + """Record forwarded arguments around one real base Linear operation.""" + + def __init__(self, base: nn.Linear) -> None: + """Retain the base and an empty call journal.""" + + self._base = base + self.calls: list[tuple[object, object]] = [] + + def __call__( + self, + inputs: torch.Tensor, + marker: object, + *, + expected: object, + ) -> torch.Tensor: + """Record positional/keyword forwarding and execute the base.""" + + self.calls.append((marker, expected)) + return cast(torch.Tensor, self._base(inputs)) + + +def _fixture( + *, + device: torch.device | None = None, + dtype: torch.dtype = torch.float32, +) -> _Fixture: + """Return one deterministic execution fixture.""" + + return _Fixture(device or torch.device("cpu"), dtype) + + +def _target( + name: str, + down: torch.Tensor, + up: torch.Tensor, +) -> StandardLoraTarget: + """Return one immutable CPU ordinary-LoRA target.""" + + return StandardLoraTarget(name, down, up, 2, 4, 3, 0.5) + + +def _use( + fixture: _Fixture, + *, + composition: int, + region: int, + strength: float = 1.0, + target: StandardLoraTarget | None = None, +) -> RegionalLinearTargetUse: + """Return one ordered use sharing exact tensor identities where requested.""" + + selected = target or fixture.target + identity = RegionalLoraAdapterIdentity(f"{selected.target}.safetensors") + return RegionalLinearTargetUse( + composition, + region, + RegionalLinearOperationKey( + identity, + "diffusion_model.layer.weight", + id(selected.down), + id(selected.up), + ), + RegionalLoraTargetPreparation( + identity, + fixture.lineage, + selected, + fixture.cache, + ), + strength * selected.intrinsic_scale, + ) + + +def _plan(uses: tuple[RegionalLinearTargetUse, ...]) -> RegionalLinearExecutionPlan: + """Build one generic execution plan from ordered uses.""" + + return RegionalLinearExecutionPlan(uses) + + +def _masks(values: torch.Tensor, *, features: int) -> RegionalActivationMaskBatch: + """Wrap region/B/S values in explicit consumer-spatialized geometry.""" + + _, batch, tokens = values.shape + geometry = RegionalActivationGeometry( + RegionalActivationLayout.CONSUMER_SPATIALIZED, + (batch, tokens, features), + 2, + 2, + tokens // 2, + RegionalActivationBatchAlignment(batch, 1), + ) + return RegionalActivationMaskBatch(values.unsqueeze(-1), geometry) + + +def _execute( + fixture: _Fixture, + plan: RegionalLinearExecutionPlan, + masks: torch.Tensor, + schedules: tuple[float, ...], +) -> torch.Tensor: + """Run one complete generic Linear execution case.""" + + return RegionalLinearExecutor().execute( + fixture.base, + fixture.inputs, + plan=plan, + masks=_masks(masks.to(fixture.inputs), features=4), + schedule_strengths=schedules, + ) + + +def _delta(fixture: _Fixture, inputs: torch.Tensor) -> torch.Tensor: + """Return the explicit unscaled ordinary-LoRA delta.""" + + down = fixture.target.down.to(inputs) + up = fixture.target.up.to(inputs) + return functional.linear(functional.linear(inputs, down), up) + + +def _preparations( + plan: RegionalLinearExecutionPlan, +) -> tuple[RegionalLoraTargetPreparation, ...]: + """Return every group-local preparation for lifecycle assertions.""" + + return tuple(group.preparation for group in plan.groups) diff --git a/tests/test_regional_linear_execution_plan.py b/tests/test_regional_linear_execution_plan.py new file mode 100644 index 0000000..7fb7819 --- /dev/null +++ b/tests/test_regional_linear_execution_plan.py @@ -0,0 +1,158 @@ +# 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 Linear plans adapt exact bound Comfy identities.""" + +from __future__ import annotations + +import comfy.model_patcher +import pytest +import torch +from comfy.weight_adapter.lora import LoRAAdapter +from torch import nn + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraScheduleBoundary, +) +from simple_syrup.runtime.regional_lora.comfy_adapter_resolution import ( + ComfyAdapterTargetPath, + ComfyNormalizedAdapterTarget, + ComfyRegionalAdapterResolution, + ComfyRegionalLoraResolution, +) +from simple_syrup.runtime.regional_lora.execution_cache import ( + RegionalLoraExecutionCache, +) +from simple_syrup.runtime.regional_lora.linear_execution_plan import ( + RegionalLinearExecutionPlanFactory, +) +from simple_syrup.runtime.regional_lora.resolved_operation_translator import ( + ComfyResolvedOperationTranslator, +) +from simple_syrup.runtime.regional_lora.target_binder import RegionalLoraTargetBinder +from simple_syrup.runtime.regional_lora.target_binding import ( + BoundRegionalLoraSpatialCapability, +) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload + + +def test_factory_preserves_exact_tensors_scale_and_model_lineage() -> None: + """Adapt U2/U3/U4 evidence without copying its normalized tensor pair.""" + + model = _patcher() + target = _target() + resolution = _resolution(target) + operations = ComfyResolvedOperationTranslator().translate(resolution) + path = target.path.parameter_key + binding = RegionalLoraTargetBinder().bind( + source=model, + candidate=model, + resolution=resolution, + operations=operations, + linear_spatial_capabilities={ + path: BoundRegionalLoraSpatialCapability.CONSUMER_SPATIALIZED + }, + ) + + plan = RegionalLinearExecutionPlanFactory().build( + binding.entries, + model=model, + cache=RegionalLoraExecutionCache(), + ) + + operation = target.operation + assert isinstance(operation, LoRAAdapter) + up, down, *_rest = operation.weights + assert plan.uses[0].preparation.target.down is down + assert plan.uses[0].preparation.target.up is up + assert ( + plan.uses[0].preparation.model_lineage.clone_base_uuid == model.clone_base_uuid + ) + assert plan.uses[0].preparation.model_lineage.patches_uuid == model.patches_uuid + assert plan.uses[0].base_strength == pytest.approx(0.8 * (1.0 / 2.0)) + + +def test_factory_rejects_global_only_linear_binding() -> None: + """Require U5 consumer geometry before admitting generic regional execution.""" + + model = _patcher() + target = _target() + resolution = _resolution(target) + operations = ComfyResolvedOperationTranslator().translate(resolution) + binding = RegionalLoraTargetBinder().bind( + source=model, + candidate=model, + resolution=resolution, + operations=operations, + ) + + with pytest.raises(ValueError, match="consumer spatialization"): + RegionalLinearExecutionPlanFactory().build( + binding.entries, + model=model, + cache=RegionalLoraExecutionCache(), + ) + + +def _patcher() -> comfy.model_patcher.ModelPatcher: + """Return one real Comfy patcher over a minimal target graph.""" + + root = nn.Module() + root.diffusion_model = nn.Module() + root.diffusion_model.layer = nn.Linear(4, 6, bias=False) + return comfy.model_patcher.ModelPatcher( + root, + torch.device("cpu"), + torch.device("cpu"), + ) + + +def _target() -> ComfyNormalizedAdapterTarget: + """Return one exact normalized matrix pair with alpha metadata.""" + + return ComfyNormalizedAdapterTarget( + ComfyAdapterTargetPath("diffusion_model.layer.weight", None), + LoRAAdapter( + {"up", "down"}, + ( + torch.ones((6, 2)), + torch.ones((2, 4)), + 1.0, + None, + None, + None, + ), + ), + "LoRAAdapter", + ("down", "up"), + True, + ) + + +def _resolution(target: ComfyNormalizedAdapterTarget) -> ComfyRegionalLoraResolution: + """Return one complete U2 resolution around the exact target.""" + + adapter = RegionalLoraAdapterPlan( + RegionalLoraAdapterIdentity("adapter.safetensors"), + 0, + 0, + RegionalLoraBranch.POSITIVE, + 0.8, + (RegionalLoraScheduleBoundary(0.0, 1.0, 1.0, 0),), + ) + return ComfyRegionalLoraResolution( + ( + ComfyRegionalAdapterResolution( + adapter, + RegionalLoraHostPayload(False, None, {}, None), + (target,), + (), + (), + ), + ), + (), + ) diff --git a/tests/test_regional_lora_conditioning_adapter.py b/tests/test_regional_lora_conditioning_adapter.py index bf84918..ecdd6f0 100644 --- a/tests/test_regional_lora_conditioning_adapter.py +++ b/tests/test_regional_lora_conditioning_adapter.py @@ -78,11 +78,16 @@ def test_adapter_extracts_ordered_positive_and_negative_regional_stacks() -> Non RegionalLoraBranch.POSITIVE, RegionalLoraBranch.NEGATIVE, ] - assert adaptation.adapter_weights == tuple( + expected_weights = tuple( hook.weights for group in (positive_hooks, negative_hooks) for hook in group.get_type(comfy.hooks.EnumHookType.Weight) ) + assert ( + tuple(payload.raw_weights for payload in adaptation.adapter_payloads) + == expected_weights + ) + assert all(payload.needs_resolution for payload in adaptation.adapter_payloads) def test_adapter_accepts_cloned_schedule_entries_and_rejects_mixed_groups() -> None: @@ -152,7 +157,9 @@ def test_adapter_retains_encoded_text_loras_without_model_admission() -> None: "pc-ADAPTER_A-0.8-0.0" ] model_hook = regional_model_hooks.get_type(comfy.hooks.EnumHookType.Weight)[0] - assert adaptation.adapter_weights == (model_hook.weights,) + assert len(adaptation.adapter_payloads) == 1 + assert adaptation.adapter_payloads[0].needs_resolution is True + assert adaptation.adapter_payloads[0].raw_weights is model_hook.weights assert ( global_text_hooks.get_type(comfy.hooks.EnumHookType.Weight)[0]._strength_model == 0.0 @@ -191,7 +198,7 @@ def test_adapter_accepts_different_text_only_hooks_across_schedule_entries() -> adaptation = RegionalLoraConditioningAdapter().adapt(plan, model=_Model()) assert adaptation.plan.adapters == () - assert adaptation.adapter_weights == () + assert adaptation.adapter_payloads == () def test_adapter_rejects_global_hooks_and_opaque_regional_identity() -> None: diff --git a/tests/test_regional_lora_execution_cache.py b/tests/test_regional_lora_execution_cache.py index b221a82..c6e35ba 100644 --- a/tests/test_regional_lora_execution_cache.py +++ b/tests/test_regional_lora_execution_cache.py @@ -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 safe regional LoRA prepared-weight cache identity and reuse.""" from __future__ import annotations diff --git a/tests/test_regional_lora_hook_identity.py b/tests/test_regional_lora_hook_identity.py index 34750f8..a5ec3e9 100644 --- a/tests/test_regional_lora_hook_identity.py +++ b/tests/test_regional_lora_hook_identity.py @@ -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 clone-local stable identity labeling for regional LoRA hooks.""" from __future__ import annotations diff --git a/tests/test_regional_lora_host_payload.py b/tests/test_regional_lora_host_payload.py new file mode 100644 index 0000000..9e4b88f --- /dev/null +++ b/tests/test_regional_lora_host_payload.py @@ -0,0 +1,84 @@ +# 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 snapshots of Comfy WeightHook payload state.""" + +from __future__ import annotations + +from typing import Any, cast + +import comfy.hooks +import pytest + +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload + + +def test_payload_preserves_unresolved_hook_weight_identity() -> None: + """Retain raw source weights when Comfy has not initialized the hook.""" + + raw_weights = {"diffusion_model.layer.lora_A.weight": object()} + hook = comfy.hooks.create_hook_lora(raw_weights, 0.8, 0.0).get_type( + comfy.hooks.EnumHookType.Weight + )[0] + + payload = RegionalLoraHostPayload.from_hook(hook) + + assert payload.needs_resolution is True + assert payload.raw_weights is raw_weights + assert payload.model_weights is None + assert payload.clip_weights is None + assert payload.model_side_weights is raw_weights + + +def test_payload_preserves_initialized_model_and_clip_weight_identities() -> None: + """Retain both exact initialized mappings without conflating their owners.""" + + hook = comfy.hooks.create_hook_lora({}, 0.8, 0.6).get_type( + comfy.hooks.EnumHookType.Weight + )[0] + model_weights = {"diffusion_model.layer.weight": object()} + clip_weights = {"text_model.layer.weight": object()} + hook.need_weight_init = False + hook.weights = model_weights + hook.weights_clip = clip_weights + + payload = RegionalLoraHostPayload.from_hook(hook) + + assert payload.needs_resolution is False + assert payload.raw_weights is None + assert payload.model_weights is model_weights + assert payload.clip_weights is clip_weights + assert payload.model_side_weights is model_weights + + +@pytest.mark.parametrize( + ("payload", "message"), + [ + ((True, None, None, None), "requires raw weights"), + ((True, object(), object(), None), "cannot contain initialized"), + ((False, object(), None, None), "cannot retain raw weights"), + ], +) +def test_payload_rejects_ambiguous_host_state( + payload: tuple[bool, object | None, object | None, object | None], + message: str, +) -> None: + """Reject snapshots that cannot represent a real WeightHook state.""" + + with pytest.raises(ValueError, match=message): + RegionalLoraHostPayload(*payload) + + +def test_payload_rejects_non_boolean_resolution_state() -> None: + """Keep raw-versus-initialized state explicit and strictly typed.""" + + with pytest.raises(TypeError, match="must be boolean"): + RegionalLoraHostPayload(cast(Any, 1), object(), None, None) + + +def test_payload_factory_rejects_non_weight_hooks() -> None: + """Prevent arbitrary host objects from entering the snapshot boundary.""" + + with pytest.raises(TypeError, match="requires a WeightHook"): + RegionalLoraHostPayload.from_hook(cast(Any, object())) diff --git a/tests/test_regional_lora_host_payloads.py b/tests/test_regional_lora_host_payloads.py index 1f393d1..8f6349c 100644 --- a/tests/test_regional_lora_host_payloads.py +++ b/tests/test_regional_lora_host_payloads.py @@ -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 + """Characterize installed Comfy WeightHook payload factory boundaries.""" from __future__ import annotations diff --git a/tests/test_regional_lora_plan.py b/tests/test_regional_lora_plan.py index 3bb0994..01595ee 100644 --- a/tests/test_regional_lora_plan.py +++ b/tests/test_regional_lora_plan.py @@ -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 immutable regional LoRA planning from ordered Comfy HookGroups.""" from __future__ import annotations @@ -126,8 +130,12 @@ def test_hook_adapter_preserves_identity_order_ownership_strength_and_schedules( ) assert _hook_snapshot(positive_hooks) == positive_snapshot assert _hook_snapshot(negative_hooks) == negative_snapshot - assert adaptation.adapter_weights[0] is positive_weight_hooks[0].weights - assert adaptation.adapter_weights[1] is negative_weight_hook.weights + assert adaptation.adapter_payloads[0].needs_resolution is True + assert ( + adaptation.adapter_payloads[0].raw_weights is positive_weight_hooks[0].weights + ) + assert adaptation.adapter_payloads[1].needs_resolution is True + assert adaptation.adapter_payloads[1].raw_weights is negative_weight_hook.weights def test_hook_adapter_accepts_empty_source_sequence() -> None: diff --git a/tests/test_regional_lora_schedule_comfy_characterization.py b/tests/test_regional_lora_schedule_comfy_characterization.py index 8ec53e2..2cf6bd2 100644 --- a/tests/test_regional_lora_schedule_comfy_characterization.py +++ b/tests/test_regional_lora_schedule_comfy_characterization.py @@ -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 + """Characterize installed Comfy WeightHook schedule behavior exactly.""" from __future__ import annotations diff --git a/tests/test_regional_lora_schedule_resolution.py b/tests/test_regional_lora_schedule_resolution.py index e8aef92..cc3bc6c 100644 --- a/tests/test_regional_lora_schedule_resolution.py +++ b/tests/test_regional_lora_schedule_resolution.py @@ -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 schedule resolution matches installed Comfy state.""" from __future__ import annotations diff --git a/tests/test_regional_lora_target_binder.py b/tests/test_regional_lora_target_binder.py new file mode 100644 index 0000000..5af64ff --- /dev/null +++ b/tests/test_regional_lora_target_binder.py @@ -0,0 +1,432 @@ +# 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 regional LoRA target binding against real Comfy patchers.""" + +from __future__ import annotations + +from types import SimpleNamespace +from typing import cast + +import comfy.model_patcher +import pytest +import torch +from comfy.weight_adapter.lora import LoRAAdapter +from torch import nn + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraScheduleBoundary, +) +from simple_syrup.runtime.regional_lora.comfy_adapter_resolution import ( + ComfyAdapterResolutionIssue, + ComfyAdapterResolutionIssueCode, + ComfyAdapterTargetPath, + ComfyNormalizedAdapterTarget, + ComfyRegionalAdapterResolution, + ComfyRegionalLoraResolution, +) +from simple_syrup.runtime.regional_lora.resolved_operation_translator import ( + ComfyResolvedOperationTranslator, +) +from simple_syrup.runtime.regional_lora.target_binder import RegionalLoraTargetBinder +from simple_syrup.runtime.regional_lora.target_binding import ( + BoundRegionalLoraModuleClass, + BoundRegionalLoraSpatialCapability, + RegionalLoraBindingIssueCode, +) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload + + +def test_binder_retains_exact_identities_and_never_mutates_source() -> None: + """Bind a Linear target by identity while preserving all patcher state.""" + + source = _patcher() + resolution = _resolution( + ((_adapter(0), (_linear_target("diffusion_model.layer.weight"),)),) + ) + operations = ComfyResolvedOperationTranslator().translate(resolution) + before = _snapshot(source) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=resolution, + operations=operations, + ) + + assert result.admissible + assert result.entries[0].descriptor is operations.entries[0] + assert ( + result.entries[0].normalized_target is resolution.adapters[0].model_targets[0] + ) + model = cast(_TestModel, source.model) + assert result.entries[0].module is model.diffusion_model.layer + assert result.entries[0].parameter is model.diffusion_model.layer.weight + assert result.entries[0].module_class is BoundRegionalLoraModuleClass.LINEAR + assert ( + result.entries[0].spatial_capability + is BoundRegionalLoraSpatialCapability.GLOBAL_ONLY + ) + assert _snapshot(source) == before + + +def test_binder_accepts_direct_clone_and_explicit_linear_capability() -> None: + """Bind one direct clone and retain consumer-owned spatialization evidence.""" + + source = _patcher() + candidate = source.clone() + path = "diffusion_model.layer.weight" + resolution = _resolution(((_adapter(0), (_linear_target(path),)),)) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=candidate, + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + linear_spatial_capabilities={ + path: BoundRegionalLoraSpatialCapability.CONSUMER_SPATIALIZED + }, + ) + + assert result.admissible + assert ( + result.entries[0].spatial_capability + is BoundRegionalLoraSpatialCapability.CONSUMER_SPATIALIZED + ) + + +def test_binder_classifies_convolution_as_direct() -> None: + """Derive direct spatial capability from the observed Conv2d target.""" + + source = _patcher() + source.model.diffusion_model.conv = nn.Conv2d(4, 6, 3, bias=False) + resolution = _resolution( + ((_adapter(0), (_conv_target("diffusion_model.conv.weight"),)),) + ) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + ) + + assert result.admissible + assert result.entries[0].module_class is BoundRegionalLoraModuleClass.CONVOLUTION_2D + assert ( + result.entries[0].spatial_capability + is BoundRegionalLoraSpatialCapability.DIRECT + ) + + +def test_binder_reads_pending_object_patch_without_applying_it() -> None: + """Bind through the effective replacement graph while leaving it pending.""" + + source = _patcher() + original = source.model.diffusion_model.layer + replacement = nn.Linear(4, 6, bias=False) + source.add_object_patch("diffusion_model.layer", replacement) + resolution = _resolution( + ((_adapter(0), (_linear_target("diffusion_model.layer.weight"),)),) + ) + before = _snapshot(source) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + ) + + assert result.admissible + assert result.entries[0].module is replacement + assert result.entries[0].parameter is replacement.weight + assert result.entries[0].active_object_patch_paths == ("diffusion_model.layer",) + assert source.model.diffusion_model.layer is original + assert _snapshot(source) == before + + +def test_binder_aggregates_target_resolution_and_rejection_failures() -> None: + """Return all independent failures without partial graph mutation.""" + + source = _patcher() + source.model.diffusion_model.unsupported = nn.Embedding(4, 6) + source.model.diffusion_model.bad_shape = nn.Linear(4, 6, bias=False) + source.model.diffusion_model.dora = nn.Linear(4, 6, bias=False) + targets = ( + _linear_target("diffusion_model.missing.weight"), + _linear_target("diffusion_model.unsupported.weight"), + _linear_target("diffusion_model.bad_shape.weight", output_size=7), + _linear_target("diffusion_model.dora.weight", dora=True), + ) + issue = ComfyAdapterResolutionIssue( + 0, + "adapter-0.safetensors", + ComfyAdapterResolutionIssueCode.UNSUPPORTED_OPERATION, + "host characterization issue", + ) + resolution = _resolution(((_adapter(0), targets),), issues=(issue,)) + before = _snapshot(source) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + ) + + assert {entry.issues[0].code for entry in result.entries} == { + RegionalLoraBindingIssueCode.TARGET_NOT_FOUND, + RegionalLoraBindingIssueCode.MODULE_UNSUPPORTED, + RegionalLoraBindingIssueCode.SHAPE_INCOMPATIBLE, + RegionalLoraBindingIssueCode.OPERATION_REJECTED, + } + assert RegionalLoraBindingIssueCode.RESOLUTION_ISSUE in { + issue.code for issue in result.issues + } + assert not result.admissible + assert _snapshot(source) == before + + +def test_binder_rejects_duplicate_ownership_within_one_adapter() -> None: + """Reject repeated target ownership while retaining both pieces of evidence.""" + + source = _patcher() + target = _linear_target("diffusion_model.layer.weight") + resolution = _resolution(((_adapter(0), (target, target)),)) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + ) + + assert len(result.entries) == 2 + assert all( + entry.issues[0].code is RegionalLoraBindingIssueCode.DUPLICATE_OWNERSHIP + for entry in result.entries + ) + + +def test_binder_allows_multiple_adapters_to_compose_on_one_parameter() -> None: + """Permit ordered adapter composition without mistaking it for duplication.""" + + source = _patcher() + resolution = _resolution( + ( + (_adapter(0), (_linear_target("diffusion_model.layer.weight"),)), + (_adapter(1), (_linear_target("diffusion_model.layer.weight"),)), + ) + ) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + ) + + assert result.admissible + assert len(result.entries) == 2 + + +def test_binder_reports_u2_u3_identity_mismatch() -> None: + """Refuse positional guessing when normalized and translated identities differ.""" + + source = _patcher() + one = _resolution( + ((_adapter(0), (_linear_target("diffusion_model.layer.weight"),)),) + ) + two = _resolution( + ( + ( + _adapter(0), + ( + _linear_target("diffusion_model.layer.weight"), + _linear_target("diffusion_model.layer.weight"), + ), + ), + ) + ) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=two, + operations=ComfyResolvedOperationTranslator().translate(one), + ) + + assert RegionalLoraBindingIssueCode.RESOLUTION_MISMATCH in { + issue.code for issue in result.issues + } + assert len(result.entries) == 1 + + +def test_binder_rejects_unrelated_patcher_before_target_access() -> None: + """Reject unrelated graph lineage without returning partial target entries.""" + + source = _patcher() + resolution = _resolution( + ((_adapter(0), (_linear_target("diffusion_model.layer.weight"),)),) + ) + + result = RegionalLoraTargetBinder().bind( + source=source, + candidate=_patcher(), + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + ) + + assert result.entries == () + assert result.issues[0].code is RegionalLoraBindingIssueCode.SOURCE_LINEAGE + + +def test_binder_rejects_invalid_linear_capability_declaration() -> None: + """Accept only explicit Linear capabilities owned by actual consumers.""" + + source = _patcher() + resolution = _resolution( + ((_adapter(0), (_linear_target("diffusion_model.layer.weight"),)),) + ) + + with pytest.raises(ValueError, match="consumer-spatialized or global-only"): + RegionalLoraTargetBinder().bind( + source=source, + candidate=source, + resolution=resolution, + operations=ComfyResolvedOperationTranslator().translate(resolution), + linear_spatial_capabilities={ + "diffusion_model.layer.weight": ( + BoundRegionalLoraSpatialCapability.DIRECT + ) + }, + ) + + +def _patcher() -> comfy.model_patcher.ModelPatcher: + """Return a real Comfy patcher with a minimal nested diffusion graph.""" + + model = _TestModel() + return comfy.model_patcher.ModelPatcher( + model, torch.device("cpu"), torch.device("cpu") + ) + + +def _snapshot(patcher: comfy.model_patcher.ModelPatcher) -> SimpleNamespace: + """Capture all graph identities and pending patch state used by the binder.""" + + model = cast(_TestModel, patcher.model) + return SimpleNamespace( + model=model, + diffusion_model=model.diffusion_model, + layer=model.diffusion_model.layer, + weight=model.diffusion_model.layer.weight, + object_patches=dict(patcher.object_patches), + backups=dict(patcher.object_patches_backup), + ) + + +class _TestDiffusionModel(nn.Module): + """Provide typed target modules for binder tests.""" + + def __init__(self) -> None: + """Create one Linear target and permit focused test additions.""" + + super().__init__() + self.layer = nn.Linear(4, 6, bias=False) + + +class _TestModel(nn.Module): + """Provide the typed Comfy model root used by binder tests.""" + + def __init__(self) -> None: + """Expose one diffusion-model owner below the model root.""" + + super().__init__() + self.diffusion_model = _TestDiffusionModel() + + +def _resolution( + adapters: tuple[ + tuple[RegionalLoraAdapterPlan, tuple[ComfyNormalizedAdapterTarget, ...]], ... + ], + *, + issues: tuple[ComfyAdapterResolutionIssue, ...] = (), +) -> ComfyRegionalLoraResolution: + """Build ordered normalized evidence around the supplied exact targets.""" + + return ComfyRegionalLoraResolution( + tuple( + ComfyRegionalAdapterResolution( + adapter, + RegionalLoraHostPayload(False, None, {}, None), + targets, + (), + (), + ) + for adapter, targets in adapters + ), + issues, + ) + + +def _linear_target( + path: str, + *, + output_size: int = 6, + dora: bool = False, +) -> ComfyNormalizedAdapterTarget: + """Create one exact normalized matrix-pair target.""" + + operation = LoRAAdapter( + {"up", "down"}, + ( + torch.ones((output_size, 2)), + torch.ones((2, 4)), + None, + None, + torch.ones(output_size) if dora else None, + None, + ), + ) + return ComfyNormalizedAdapterTarget( + ComfyAdapterTargetPath(path, None), + operation, + "LoRAAdapter", + ("down", "up"), + not dora, + ) + + +def _conv_target(path: str) -> ComfyNormalizedAdapterTarget: + """Create one exact normalized Conv2d LoRA target.""" + + operation = LoRAAdapter( + {"up", "down"}, + (torch.ones((6, 2)), torch.ones((2, 4, 3, 3)), None, None, None, None), + ) + return ComfyNormalizedAdapterTarget( + ComfyAdapterTargetPath(path, None), + operation, + "LoRAAdapter", + ("down", "up"), + True, + ) + + +def _adapter(composition_index: int) -> RegionalLoraAdapterPlan: + """Create one valid ordered regional adapter plan.""" + + return RegionalLoraAdapterPlan( + RegionalLoraAdapterIdentity(f"adapter-{composition_index}.safetensors"), + composition_index, + composition_index, + RegionalLoraBranch.POSITIVE, + 0.8, + (RegionalLoraScheduleBoundary(0.0, 100.0, 1.0, 0),), + ) diff --git a/tests/test_regional_lora_target_shape_admission.py b/tests/test_regional_lora_target_shape_admission.py new file mode 100644 index 0000000..c392b87 --- /dev/null +++ b/tests/test_regional_lora_target_shape_admission.py @@ -0,0 +1,291 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify regional LoRA descriptor compatibility with real Torch operations.""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest +import torch +from torch import nn + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraScheduleBoundary, +) +from simple_syrup.domain.resolved_regional_lora import ( + RegionalLoraExecutionContract, + RegionalLoraGeometryClass, + RegionalLoraTensorShape, + ResolvedLoraOperationClass, + ResolvedRegionalLoraOperation, + ResolvedRegionalLoraTarget, +) +from simple_syrup.runtime.regional_lora.target_binding import ( + BoundRegionalLoraModuleClass, +) +from simple_syrup.runtime.regional_lora.target_shape_admission import ( + RegionalLoraTargetShapeValidator, +) + + +def test_validator_admits_linear_matrix_pair() -> None: + """Match matrix input/output features and exact parameter identity.""" + + module = nn.Linear(4, 6, bias=False) + + result = RegionalLoraTargetShapeValidator().validate( + _matrix_descriptor(input_size=4, output_size=6), + module, + module.weight, + ) + + assert result.module_class is BoundRegionalLoraModuleClass.LINEAR + assert result.parameter is module.weight + assert result.issue is None + + +@pytest.mark.parametrize( + ("module", "operation_class"), + [ + (nn.Conv1d(4, 6, 3, bias=False), ResolvedLoraOperationClass.CONVOLUTION_1D), + (nn.Conv2d(4, 6, 3, bias=False), ResolvedLoraOperationClass.CONVOLUTION_2D), + (nn.Conv3d(4, 6, 3, bias=False), ResolvedLoraOperationClass.CONVOLUTION_3D), + ], +) +def test_validator_admits_explicit_convolution_dimensions( + module: nn.Conv1d | nn.Conv2d | nn.Conv3d, + operation_class: ResolvedLoraOperationClass, +) -> None: + """Match direct convolution channels, kernels, rank, and module dimension.""" + + weight = module.weight + descriptor = _convolution_descriptor( + operation_class=operation_class, + target_shape=tuple(weight.shape), + ) + + result = RegionalLoraTargetShapeValidator().validate( + descriptor, + module, + weight, + ) + + assert result.issue is None + + +def test_validator_admits_matrix_pair_for_grouped_convolution() -> None: + """Use the observed grouped weight shape for Comfy's matrix conv form.""" + + module = nn.Conv2d(4, 6, 3, groups=2, bias=False) + descriptor = _matrix_descriptor( + input_size=2 * 3 * 3, + output_size=6, + ) + + result = RegionalLoraTargetShapeValidator().validate( + descriptor, + module, + module.weight, + ) + + assert result.issue is None + + +def test_validator_admits_locon_down_middle_composition() -> None: + """Match 1x1 down plus spatial middle against the target kernel.""" + + module = nn.Conv2d(4, 6, 3, bias=False) + descriptor = _convolution_descriptor( + operation_class=ResolvedLoraOperationClass.CONVOLUTION_2D, + target_shape=tuple(module.weight.shape), + use_middle=True, + ) + + result = RegionalLoraTargetShapeValidator().validate( + descriptor, + module, + module.weight, + ) + + assert result.issue is None + + +@pytest.mark.parametrize( + ("case", "message"), + [ + ("linear-input", "input features"), + ("linear-output", "output channels"), + ("convolution-dimension", "dimensionality"), + ("module", "module type"), + ("parameter", "Torch tensor"), + ], +) +def test_validator_rejects_module_parameter_and_shape_mismatch( + case: str, + message: str, +) -> None: + """Return explicit incompatibility without changing the observed module.""" + + descriptor, module, parameter = _invalid_case(case) + result = RegionalLoraTargetShapeValidator().validate( + descriptor, + module, + parameter, + ) + + assert result.issue is not None + assert message in result.issue + + +def _invalid_case( + case: str, +) -> tuple[ResolvedRegionalLoraOperation, nn.Module, object]: + """Construct one focused invalid admission case after module import.""" + + if case == "linear-input": + return ( + _matrix_descriptor(input_size=5, output_size=6), + nn.Linear(4, 6, bias=False), + torch.ones((6, 4)), + ) + if case == "linear-output": + return ( + _matrix_descriptor(input_size=4, output_size=7), + nn.Linear(4, 6, bias=False), + torch.ones((6, 4)), + ) + if case == "convolution-dimension": + return ( + _convolution_descriptor( + operation_class=ResolvedLoraOperationClass.CONVOLUTION_1D, + target_shape=(6, 4, 3), + ), + nn.Conv2d(4, 6, 3, bias=False), + torch.ones((6, 4, 3, 3)), + ) + if case == "module": + return ( + _matrix_descriptor(input_size=4, output_size=6), + nn.ReLU(), + torch.ones((6, 4)), + ) + if case == "parameter": + return ( + _matrix_descriptor(input_size=4, output_size=6), + nn.Linear(4, 6, bias=False), + object(), + ) + raise AssertionError(f"Unknown invalid admission case: {case}") + + +def test_validator_applies_comfy_offset_to_target_shape() -> None: + """Validate one exact sliced target shape without narrowing the parameter.""" + + module = nn.Linear(4, 12, bias=False) + descriptor = replace( + _matrix_descriptor(input_size=4, output_size=6), + target=ResolvedRegionalLoraTarget("diffusion_model.layer", "weight", (0, 3, 6)), + ) + before = ( + module.weight.data_ptr(), + module.weight._version, + tuple(module.weight.shape), + ) + + result = RegionalLoraTargetShapeValidator().validate( + descriptor, + module, + module.weight, + ) + + assert result.issue is None + assert before == ( + module.weight.data_ptr(), + module.weight._version, + tuple(module.weight.shape), + ) + + +def _matrix_descriptor( + *, + input_size: int, + output_size: int, +) -> ResolvedRegionalLoraOperation: + """Return one valid matrix-pair descriptor.""" + + return _descriptor( + operation_class=ResolvedLoraOperationClass.MATRIX_PAIR, + down_shape=RegionalLoraTensorShape((2, input_size)), + up_shape=RegionalLoraTensorShape((output_size, 2)), + middle_shape=None, + geometry=RegionalLoraGeometryClass.TARGET_OPERATION, + execution=RegionalLoraExecutionContract.MATRIX_OR_RESHAPED_CONVOLUTION, + ) + + +def _convolution_descriptor( + *, + operation_class: ResolvedLoraOperationClass, + target_shape: tuple[int, ...], + use_middle: bool = False, +) -> ResolvedRegionalLoraOperation: + """Return one valid direct convolution descriptor.""" + + spatial = target_shape[2:] + down_spatial = (1,) * len(spatial) if use_middle else spatial + middle = RegionalLoraTensorShape((2, 2, *spatial)) if use_middle else None + geometry = { + 1: RegionalLoraGeometryClass.DIRECT_CONVOLUTION_1D, + 2: RegionalLoraGeometryClass.DIRECT_CONVOLUTION_2D, + 3: RegionalLoraGeometryClass.DIRECT_CONVOLUTION_3D, + }[len(spatial)] + return _descriptor( + operation_class=operation_class, + down_shape=RegionalLoraTensorShape((2, target_shape[1], *down_spatial)), + up_shape=RegionalLoraTensorShape((target_shape[0], 2)), + middle_shape=middle, + geometry=geometry, + execution=RegionalLoraExecutionContract.DIRECT_CONVOLUTION, + ) + + +def _descriptor( + *, + operation_class: ResolvedLoraOperationClass, + down_shape: RegionalLoraTensorShape, + up_shape: RegionalLoraTensorShape, + middle_shape: RegionalLoraTensorShape | None, + geometry: RegionalLoraGeometryClass, + execution: RegionalLoraExecutionContract, +) -> ResolvedRegionalLoraOperation: + """Return one shared valid descriptor shell.""" + + return ResolvedRegionalLoraOperation( + adapter=RegionalLoraAdapterPlan( + RegionalLoraAdapterIdentity("adapter.safetensors"), + 0, + 0, + RegionalLoraBranch.POSITIVE, + 1.0, + (RegionalLoraScheduleBoundary(0.0, 1.0, 1.0, 0),), + ), + target_index=0, + target=ResolvedRegionalLoraTarget("diffusion_model.layer", "weight", None), + normalized_operation_type="LoRAAdapter", + operation_class=operation_class, + down_shape=down_shape, + up_shape=up_shape, + middle_shape=middle_shape, + reshape_shape=None, + rank=2, + intrinsic_scale=1.0, + required_geometry=geometry, + execution_contract=execution, + rejection_reason=None, + ) diff --git a/tests/test_regional_mask_activation.py b/tests/test_regional_mask_activation.py index 5ef39e1..d2b0dbb 100644 --- a/tests/test_regional_mask_activation.py +++ b/tests/test_regional_mask_activation.py @@ -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 regional activity pruning and exact fast-path classification.""" from __future__ import annotations diff --git a/tests/test_regional_mask_bank.py b/tests/test_regional_mask_bank.py index e95a5b6..9b28229 100644 --- a/tests/test_regional_mask_bank.py +++ b/tests/test_regional_mask_bank.py @@ -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 immutable full-canvas regional mask authority.""" from __future__ import annotations diff --git a/tests/test_regional_mask_projection.py b/tests/test_regional_mask_projection.py index a1b8f87..05a52b0 100644 --- a/tests/test_regional_mask_projection.py +++ b/tests/test_regional_mask_projection.py @@ -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 canonical regional masks project by explicit spatial policy.""" from __future__ import annotations diff --git a/tests/test_regional_model_capabilities.py b/tests/test_regional_model_capabilities.py index a76ea5d..9f04a50 100644 --- a/tests/test_regional_model_capabilities.py +++ b/tests/test_regional_model_capabilities.py @@ -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 defensive regional model capability detection and reporting.""" from __future__ import annotations diff --git a/tests/test_regional_model_hook_selection.py b/tests/test_regional_model_hook_selection.py index a6ac32e..867a93b 100644 --- a/tests/test_regional_model_hook_selection.py +++ b/tests/test_regional_model_hook_selection.py @@ -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 authoritative model participation selection from Comfy HookGroups.""" from __future__ import annotations diff --git a/tests/test_regional_model_modifier_characterization.py b/tests/test_regional_model_modifier_characterization.py index ca43076..095fbc8 100644 --- a/tests/test_regional_model_modifier_characterization.py +++ b/tests/test_regional_model_modifier_characterization.py @@ -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 + """Characterize installed cache, NegPiP, and attention-modifier surfaces.""" from __future__ import annotations diff --git a/tests/test_regional_model_patch_interop.py b/tests/test_regional_model_patch_interop.py index 9ea3f39..87aaaea 100644 --- a/tests/test_regional_model_patch_interop.py +++ b/tests/test_regional_model_patch_interop.py @@ -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 centralized MODEL modifier admission for Attention Coupling.""" from __future__ import annotations diff --git a/tests/test_regional_model_patch_stack.py b/tests/test_regional_model_patch_stack.py index fab4e98..2619c47 100644 --- a/tests/test_regional_model_patch_stack.py +++ b/tests/test_regional_model_patch_stack.py @@ -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 fixed regional Attention Coupling MODEL patch-stack order.""" from __future__ import annotations diff --git a/tests/test_regional_operation_cache_lifecycle.py b/tests/test_regional_operation_cache_lifecycle.py new file mode 100644 index 0000000..d84371d --- /dev/null +++ b/tests/test_regional_operation_cache_lifecycle.py @@ -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 + +"""Verify generic regional operation device state follows Comfy detach.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +import torch +from comfy.patcher_extension import CallbacksMP + +from simple_syrup.runtime.regional_lora.execution_cache import ( + RegionalLoraExecutionCache, +) +from simple_syrup.runtime.regional_lora.operation_cache_lifecycle import ( + REGIONAL_OPERATION_DETACH_CALLBACK_KEY, + RegionalOperationCacheLifecycle, +) + + +@dataclass +class _Plan: + """Record every local preparation release.""" + + clears: int = 0 + + def clear(self) -> None: + """Record one detach release.""" + + self.clears += 1 + + +def test_detach_callback_releases_every_plan_and_shared_cache() -> None: + """Release all generic owners on complete and clone-switch detach forms.""" + + first = _Plan() + second = _Plan() + cache = RegionalLoraExecutionCache() + lifecycle = RegionalOperationCacheLifecycle((first, second), cache) + model = _patcher() + lifecycle.mutation().apply(model) + + model.detach(unpatch_all=False) + model.detach(unpatch_all=True) + + assert (first.clears, second.clears) == (2, 2) + assert cache.size == 0 + assert model.get_callbacks( + CallbacksMP.ON_DETACH, + REGIONAL_OPERATION_DETACH_CALLBACK_KEY, + ) == [lifecycle.release] + + +def _patcher() -> Any: + """Return one real Comfy patcher for callback execution.""" + + from comfy.model_patcher import ModelPatcher + + device = torch.device("cpu") + return ModelPatcher(torch.nn.Linear(2, 2), device, device) diff --git a/tests/test_regional_operation_installation_mutation.py b/tests/test_regional_operation_installation_mutation.py new file mode 100644 index 0000000..a0114c3 --- /dev/null +++ b/tests/test_regional_operation_installation_mutation.py @@ -0,0 +1,114 @@ +# 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 generic operation installation across Comfy state owners.""" + +from __future__ import annotations + +from typing import Any + +import pytest +import torch +from torch import nn + +from simple_syrup.runtime.model_object_patch_batch import ( + ExactModelObjectReplacement, + ModelObjectPatchBatchMutation, +) +from simple_syrup.runtime.regional_lora.execution_cache import ( + RegionalLoraExecutionCache, +) +from simple_syrup.runtime.regional_lora.operation_cache_lifecycle import ( + RegionalOperationCacheLifecycle, +) +from simple_syrup.runtime.regional_lora.operation_installation_mutation import ( + RegionalOperationInstallationMutation, +) + + +class _Plan: + """Expose one no-op release contract.""" + + def clear(self) -> None: + """Release no retained data.""" + + +@pytest.mark.parametrize("failure", [RuntimeError("failure"), KeyboardInterrupt()]) +def test_callback_failure_rolls_back_object_patches_and_callbacks( + failure: BaseException, +) -> None: + """Restore both state owners after host failure or interruption.""" + + class FailingCallbackPatcher: + """Wrap one real patcher and fail only callback installation.""" + + def __init__(self) -> None: + """Expose the exact state and methods consumed by the mutation.""" + + self.real = _patcher() + self.object_patches = self.real.object_patches + self.object_patches_backup = self.real.object_patches_backup + self.callbacks = self.real.callbacks + + def get_model_object(self, name: str) -> object: + """Delegate exact object lookup.""" + + return self.real.get_model_object(name) + + def add_object_patch(self, name: str, obj: object) -> None: + """Delegate one pending exact object patch.""" + + self.real.add_object_patch(name, obj) + + def get_callbacks(self, call_type: str, key: str) -> list[object]: + """Delegate keyed callback lookup.""" + + callbacks = self.real.get_callbacks(call_type, key) + assert isinstance(callbacks, list) + return callbacks + + def add_callback_with_key( + self, + call_type: str, + key: str, + callback: object, + ) -> None: + """Emulate failure after object-patch installation.""" + + del call_type, key, callback + raise failure + + model = FailingCallbackPatcher() + before_callbacks = { + event: {key: values.copy() for key, values in keyed.items()} + for event, keyed in model.callbacks.items() + } + expected = model.get_model_object("linear") + mutation = RegionalOperationInstallationMutation( + ModelObjectPatchBatchMutation( + ( + ExactModelObjectReplacement( + "linear", + expected, + nn.Identity(), + ), + ) + ), + RegionalOperationCacheLifecycle((_Plan(),), RegionalLoraExecutionCache()), + ) + + with pytest.raises(type(failure)): + mutation.apply(model) + + assert model.object_patches == {} + assert model.callbacks == before_callbacks + + +def _patcher() -> Any: + """Return one real Comfy patcher around an exact module path.""" + + from comfy.model_patcher import ModelPatcher + + device = torch.device("cpu") + return ModelPatcher(nn.ModuleDict({"linear": nn.Linear(2, 2)}), device, device) diff --git a/tests/test_regional_operation_mutation_assembly.py b/tests/test_regional_operation_mutation_assembly.py new file mode 100644 index 0000000..ae21d93 --- /dev/null +++ b/tests/test_regional_operation_mutation_assembly.py @@ -0,0 +1,386 @@ +# 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 clone-local generic operation mutation assembly.""" + +from __future__ import annotations + +from typing import Any + +import comfy.model_patcher +import comfy.ops +import comfy_aimdo.host_buffer # type: ignore[import-untyped] +import pytest +import torch +from comfy.patcher_extension import PatcherInjection, WrappersMP +from comfy.weight_adapter.lora import LoRAAdapter +from torch import nn + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraScheduleBoundary, +) +from simple_syrup.runtime.patcher_lifecycle import PATCHER_LIFECYCLE +from simple_syrup.runtime.regional_lora.comfy_adapter_resolution import ( + ComfyAdapterTargetPath, + ComfyNormalizedAdapterTarget, + ComfyRegionalAdapterResolution, + ComfyRegionalLoraResolution, +) +from simple_syrup.runtime.regional_lora.execution_cache import ( + RegionalLoraExecutionCache, +) +from simple_syrup.runtime.regional_lora.host_operation_backing import ( + RegionalLinearOperationPatch, +) +from simple_syrup.runtime.regional_lora.operation_mutation_assembly import ( + RegionalOperationMutationAssembler, +) +from simple_syrup.runtime.regional_lora.resolved_operation_translator import ( + ComfyResolvedOperationTranslator, +) +from simple_syrup.runtime.regional_lora.target_binder import RegionalLoraTargetBinder +from simple_syrup.runtime.regional_lora.target_binding import ( + BoundRegionalLoraSpatialCapability, + RegionalLoraBindingResult, +) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload + + +def test_assembler_preserves_existing_wrapper_and_installs_one_direct_clone() -> None: + """Build one grouped target replacement without changing source state.""" + + source = _patcher() + + def wrapper(*args: object) -> tuple[object, ...]: + """Return characterized wrapper arguments.""" + + return args + + source.add_wrapper_with_key(WrappersMP.DIFFUSION_MODEL, "external", wrapper) + binding = _binding(source) + mutations = RegionalOperationMutationAssembler().assemble( + binding, + model=source, + cache=RegionalLoraExecutionCache(), + ) + original = source.model.diffusion_model.linear + derived = PATCHER_LIFECYCLE.derive_model( + source, + mutations, + operation="universal regional operation assembly regression", + ) + + assert derived.parent is source + assert source.object_patches == {} + assert derived.get_wrappers(WrappersMP.DIFFUSION_MODEL, "external") == [wrapper] + assert tuple(derived.object_patches) == ("diffusion_model.linear",) + replacement = derived.object_patches["diffusion_model.linear"] + assert isinstance(replacement, RegionalLinearOperationPatch) + derived.patch_model(load_weights=False) + try: + assert derived.model.diffusion_model.linear is replacement + finally: + derived.unpatch_model(unpatch_weights=False) + assert derived.model.diffusion_model.linear is original + + +def test_assembler_rejects_existing_injection_before_creating_mutations() -> None: + """Fail before clone mutation while injection ownership is unresolved.""" + + source = _patcher() + source.set_injections( + "external", + [PatcherInjection(lambda model: None, lambda model: None)], + ) + + with pytest.raises(ValueError, match="existing MODEL injections"): + RegionalOperationMutationAssembler().assemble( + _binding(source), + model=source, + cache=RegionalLoraExecutionCache(), + ) + + assert source.object_patches == {} + + +def test_assembler_rejects_incomplete_binding_without_partial_plan() -> None: + """Require complete admission before constructing any replacement shell.""" + + source = _patcher() + complete = _binding(source) + issue = complete.entries[0].issues + incomplete = RegionalLoraBindingResult(complete.entries, issue or (object(),)) # type: ignore[arg-type] + + with pytest.raises(ValueError, match="complete admission"): + RegionalOperationMutationAssembler().assemble( + incomplete, + model=source, + cache=RegionalLoraExecutionCache(), + ) + + +def test_real_lowvram_load_unload_and_reentry_preserve_public_path() -> None: + """Exercise Comfy's host writes and graph restoration around a replacement.""" + + source = _patcher(comfy_linear=True) + binding = _binding(source) + derived = PATCHER_LIFECYCLE.derive_model( + source, + RegionalOperationMutationAssembler().assemble( + binding, + model=source, + cache=RegionalLoraExecutionCache(), + ), + operation="regional operation low-VRAM regression", + ) + original = source.model.diffusion_model.linear + derived.patch_model( + device_to=torch.device("cpu"), + lowvram_model_memory=1, + load_weights=True, + ) + replacement = derived.model.diffusion_model.linear + try: + assert isinstance(replacement, RegionalLinearOperationPatch) + assert replacement.comfy_cast_weights is True + assert replacement._host_backing.module.__dict__["comfy_cast_weights"] is True + assert ( + replacement.weight_function + is replacement._host_backing.module.weight_function + ) + torch.testing.assert_close( + replacement(torch.ones((1, 2))), + torch.ones((1, 2)), + ) + finally: + derived.unpatch_model(torch.device("cpu"), unpatch_weights=True) + + assert derived.model.diffusion_model.linear is original + assert derived.object_patches_backup == {} + derived.patch_model(load_weights=False) + try: + assert isinstance( + derived.model.diffusion_model.linear, + RegionalLinearOperationPatch, + ) + finally: + derived.unpatch_model(unpatch_weights=False) + + +def test_real_full_load_and_detach_release_shared_prepared_cache() -> None: + """Load normally, prepare one device entry, then release it on detach.""" + + source = _patcher(comfy_linear=True) + cache = RegionalLoraExecutionCache() + derived = PATCHER_LIFECYCLE.derive_model( + source, + RegionalOperationMutationAssembler().assemble( + _binding(source), + model=source, + cache=cache, + ), + operation="regional operation full-load regression", + ) + original = source.model.diffusion_model.linear + derived.patch_model( + device_to=torch.device("cpu"), + lowvram_model_memory=0, + load_weights=True, + ) + replacement = derived.model.diffusion_model.linear + assert isinstance(replacement, RegionalLinearOperationPatch) + preparation = replacement._plan.groups[0].preparation + first = preparation.weights(device=torch.device("cpu"), dtype=torch.float32) + assert cache.size == 1 + + derived.detach(unpatch_all=True) + + assert derived.model.diffusion_model.linear is original + assert cache.size == 0 + second = preparation.weights(device=torch.device("cpu"), dtype=torch.float32) + assert second is not first + assert cache.size == 1 + + +def test_global_weight_patch_does_not_mutate_source_parameter_storage() -> None: + """Keep global LoRA-style weight updates inside the derived patch window.""" + + source = _patcher(comfy_linear=True) + source_weight = source.model.diffusion_model.linear.weight + source_before = source_weight.detach().clone() + binding = _binding(source) + derived = PATCHER_LIFECYCLE.derive_model( + source, + RegionalOperationMutationAssembler().assemble( + binding, + model=source, + cache=RegionalLoraExecutionCache(), + ), + operation="regional operation global-patch regression", + ) + derived.add_patches( + { + "diffusion_model.linear.weight": ( + "diff", + (torch.ones_like(source_weight),), + ) + } + ) + + derived.patch_model(device_to=torch.device("cpu"), load_weights=True) + try: + replacement = derived.model.diffusion_model.linear + assert isinstance(replacement, RegionalLinearOperationPatch) + assert not torch.equal(replacement.weight, source_before) + torch.testing.assert_close(source_weight, source_before) + finally: + derived.unpatch_model(torch.device("cpu"), unpatch_weights=True) + + assert derived.model.diffusion_model.linear is source.model.diffusion_model.linear + torch.testing.assert_close(source_weight, source_before) + + +def test_dynamic_patcher_clone_retains_dynamic_owner_and_pending_replacement( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Exercise installed dynamic clone state without requiring native host buffers.""" + + class HostBufferStub: + """Stand in only for unavailable native allocator construction.""" + + def __init__(self, *args: object, **kwargs: object) -> None: + """Accept the installed constructor surface without allocating.""" + + del args, kwargs + + monkeypatch.setattr(comfy_aimdo.host_buffer, "HostBuffer", HostBufferStub) + source = _dynamic_patcher() + derived = PATCHER_LIFECYCLE.derive_model( + source, + RegionalOperationMutationAssembler().assemble( + _binding(source), + model=source, + cache=RegionalLoraExecutionCache(), + ), + operation="regional operation dynamic regression", + ) + + try: + assert type(derived) is comfy.model_patcher.ModelPatcherDynamic + assert derived.is_dynamic() + assert derived.parent is source + assert derived.model is source.model + assert derived.model.dynamic_pins is source.model.dynamic_pins + assert tuple(derived.object_patches) == ("diffusion_model.linear",) + assert source.object_patches == {} + original = source.model.diffusion_model.linear + derived.patch_model(load_weights=False) + assert isinstance( + derived.model.diffusion_model.linear, + RegionalLinearOperationPatch, + ) + derived.unpatch_model(unpatch_weights=False) + assert derived.model.diffusion_model.linear is original + finally: + derived.detach(unpatch_all=False) + source.detach(unpatch_all=False) + + +def _binding( + patcher: comfy.model_patcher.ModelPatcher, +) -> RegionalLoraBindingResult: + """Return one complete consumer-spatialized Linear binding.""" + + target = ComfyNormalizedAdapterTarget( + ComfyAdapterTargetPath("diffusion_model.linear.weight", None), + LoRAAdapter( + {"up", "down"}, + ( + torch.eye(2), + torch.eye(2), + None, + None, + None, + None, + ), + ), + "LoRAAdapter", + ("down", "up"), + True, + ) + plan = RegionalLoraAdapterPlan( + RegionalLoraAdapterIdentity("assembly.safetensors"), + 0, + 0, + RegionalLoraBranch.POSITIVE, + 1.0, + (RegionalLoraScheduleBoundary(0.0, 1.0, 1.0, 0),), + ) + resolution = ComfyRegionalLoraResolution( + ( + ComfyRegionalAdapterResolution( + plan, + RegionalLoraHostPayload(False, None, {}, None), + (target,), + (), + (), + ), + ), + (), + ) + operations = ComfyResolvedOperationTranslator().translate(resolution) + return RegionalLoraTargetBinder().bind( + source=patcher, + candidate=patcher, + resolution=resolution, + operations=operations, + linear_spatial_capabilities={ + "diffusion_model.linear.weight": ( + BoundRegionalLoraSpatialCapability.CONSUMER_SPATIALIZED + ) + }, + ) + + +def _patcher(*, comfy_linear: bool = False) -> Any: + """Return one real Comfy patcher around a two-feature target.""" + + linear: nn.Module + if comfy_linear: + linear = comfy.ops.disable_weight_init.Linear(2, 2, bias=False) + linear.weight = nn.Parameter(torch.eye(2), requires_grad=False) + else: + linear = nn.Linear(2, 2, bias=False) + linear.weight = nn.Parameter(torch.eye(2), requires_grad=False) + root = nn.Module() + root.diffusion_model = nn.Module() + root.diffusion_model.linear = linear + device = torch.device("cpu") + return comfy.model_patcher.ModelPatcher(root, device, device) + + +def _dynamic_patcher() -> comfy.model_patcher.ModelPatcherDynamic: + """Return one installed dynamic GPU patcher without loading any weights.""" + + root = nn.Module() + root.diffusion_model = nn.Module() + linear = comfy.ops.disable_weight_init.Linear( + 2, + 2, + bias=False, + device="cpu", + ) + linear.weight = nn.Parameter( + torch.eye(2), + requires_grad=False, + ) + root.diffusion_model.linear = linear + return comfy.model_patcher.ModelPatcherDynamic( + root, + torch.device("cuda:0"), + torch.device("cpu"), + ) diff --git a/tests/test_regional_patch_interop_evidence.py b/tests/test_regional_patch_interop_evidence.py index 9cbd8f6..4301d18 100644 --- a/tests/test_regional_patch_interop_evidence.py +++ b/tests/test_regional_patch_interop_evidence.py @@ -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 P9.7 terminal-history decoding and exact evidence validation.""" from __future__ import annotations diff --git a/tests/test_regional_patch_interop_image_evidence.py b/tests/test_regional_patch_interop_image_evidence.py index 0d8ce75..4d86486 100644 --- a/tests/test_regional_patch_interop_image_evidence.py +++ b/tests/test_regional_patch_interop_image_evidence.py @@ -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 decoded-pixel acceptance for exact P9.7 modifier paths.""" from __future__ import annotations diff --git a/tests/test_regional_patch_interop_results.py b/tests/test_regional_patch_interop_results.py index 378ac2b..591f758 100644 --- a/tests/test_regional_patch_interop_results.py +++ b/tests/test_regional_patch_interop_results.py @@ -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 ordered P9.7 image, rejection, log, and result publication.""" from __future__ import annotations diff --git a/tests/test_regional_patch_interop_workflow.py b/tests/test_regional_patch_interop_workflow.py index 70e5b95..663d38d 100644 --- a/tests/test_regional_patch_interop_workflow.py +++ b/tests/test_regional_patch_interop_workflow.py @@ -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 closed P9.7 public modifier workflow matrix.""" from __future__ import annotations diff --git a/tests/test_regional_sampling_layout_characterization.py b/tests/test_regional_sampling_layout_characterization.py index 7bd884f..a1b782b 100644 --- a/tests/test_regional_sampling_layout_characterization.py +++ b/tests/test_regional_sampling_layout_characterization.py @@ -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 + """Characterize Regional Conditioning across sampling layouts and tile batches.""" from collections.abc import Callable diff --git a/tests/test_regional_strategy_comparison_history.py b/tests/test_regional_strategy_comparison_history.py index 95f2505..9ff8196 100644 --- a/tests/test_regional_strategy_comparison_history.py +++ b/tests/test_regional_strategy_comparison_history.py @@ -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 typed P10.2 history extraction.""" from tools.comfy_api import JsonObject diff --git a/tests/test_regional_strategy_comparison_input_artifacts.py b/tests/test_regional_strategy_comparison_input_artifacts.py index 7651c48..67a3243 100644 --- a/tests/test_regional_strategy_comparison_input_artifacts.py +++ b/tests/test_regional_strategy_comparison_input_artifacts.py @@ -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 P10.2 Comfy input-artifact ownership.""" import hashlib diff --git a/tests/test_regional_strategy_comparison_matrix.py b/tests/test_regional_strategy_comparison_matrix.py index f9c4137..9d291cf 100644 --- a/tests/test_regional_strategy_comparison_matrix.py +++ b/tests/test_regional_strategy_comparison_matrix.py @@ -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 closed P10.2 strategy comparison matrix.""" from tools.regional_strategy_comparison.matrix import ( diff --git a/tests/test_regional_strategy_comparison_results.py b/tests/test_regional_strategy_comparison_results.py index 226ae0b..d562a74 100644 --- a/tests/test_regional_strategy_comparison_results.py +++ b/tests/test_regional_strategy_comparison_results.py @@ -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 P10.2 result persistence and acceptance.""" import hashlib diff --git a/tests/test_regional_strategy_comparison_validation.py b/tests/test_regional_strategy_comparison_validation.py index 09c1970..350abc7 100644 --- a/tests/test_regional_strategy_comparison_validation.py +++ b/tests/test_regional_strategy_comparison_validation.py @@ -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 P10.2 cross-case execution acceptance.""" import pytest diff --git a/tests/test_regional_strategy_comparison_workflow.py b/tests/test_regional_strategy_comparison_workflow.py index be0123c..01ab257 100644 --- a/tests/test_regional_strategy_comparison_workflow.py +++ b/tests/test_regional_strategy_comparison_workflow.py @@ -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 public graph construction for P10.2 comparison cases.""" from tools.regional_strategy_comparison.matrix import cases diff --git a/tests/test_regional_tiled_diffusion_sampling_service.py b/tests/test_regional_tiled_diffusion_sampling_service.py index 1ebe8bf..7c68bbd 100644 --- a/tests/test_regional_tiled_diffusion_sampling_service.py +++ b/tests/test_regional_tiled_diffusion_sampling_service.py @@ -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 regional tiled diffusion planning and batch execution.""" from __future__ import annotations diff --git a/tests/test_regional_visual_benchmark_blind_scoring.py b/tests/test_regional_visual_benchmark_blind_scoring.py index edcf588..4fea8bc 100644 --- a/tests/test_regional_visual_benchmark_blind_scoring.py +++ b/tests/test_regional_visual_benchmark_blind_scoring.py @@ -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 P10.3 opaque staging, score sealing, and unblinding.""" import hashlib diff --git a/tests/test_regional_visual_benchmark_corpus.py b/tests/test_regional_visual_benchmark_corpus.py index 60c8cb0..8cd3a70 100644 --- a/tests/test_regional_visual_benchmark_corpus.py +++ b/tests/test_regional_visual_benchmark_corpus.py @@ -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 P10.3 corpus persistence and source pairing.""" from dataclasses import replace diff --git a/tests/test_regional_visual_benchmark_execution.py b/tests/test_regional_visual_benchmark_execution.py index 63e4d08..eeba02d 100644 --- a/tests/test_regional_visual_benchmark_execution.py +++ b/tests/test_regional_visual_benchmark_execution.py @@ -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 + """Characterize bounded P10.3 server segmentation and exact journal resume.""" from __future__ import annotations diff --git a/tests/test_regional_visual_benchmark_input_artifacts.py b/tests/test_regional_visual_benchmark_input_artifacts.py index 2ea2970..9f4acd4 100644 --- a/tests/test_regional_visual_benchmark_input_artifacts.py +++ b/tests/test_regional_visual_benchmark_input_artifacts.py @@ -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 P10.3 Comfy input ownership and cleanup.""" from pathlib import Path diff --git a/tests/test_regional_visual_benchmark_matrix.py b/tests/test_regional_visual_benchmark_matrix.py index 762bbc8..3b5ac61 100644 --- a/tests/test_regional_visual_benchmark_matrix.py +++ b/tests/test_regional_visual_benchmark_matrix.py @@ -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 corrected P10.3 visual matrix.""" from collections import Counter diff --git a/tests/test_regional_visual_benchmark_run_artifacts.py b/tests/test_regional_visual_benchmark_run_artifacts.py index 6ace523..bf0b8b6 100644 --- a/tests/test_regional_visual_benchmark_run_artifacts.py +++ b/tests/test_regional_visual_benchmark_run_artifacts.py @@ -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 durable segmented-run identity and exact P10.3 resume validation.""" from __future__ import annotations diff --git a/tests/test_regional_visual_benchmark_runner.py b/tests/test_regional_visual_benchmark_runner.py index f91f0cc..d21850b 100644 --- a/tests/test_regional_visual_benchmark_runner.py +++ b/tests/test_regional_visual_benchmark_runner.py @@ -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 P10.3 runner graph coverage and scoring CLI lifecycle.""" import json diff --git a/tests/test_regional_visual_benchmark_workflow.py b/tests/test_regional_visual_benchmark_workflow.py index e294337..8073827 100644 --- a/tests/test_regional_visual_benchmark_workflow.py +++ b/tests/test_regional_visual_benchmark_workflow.py @@ -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 P10.3 public workflow construction.""" import pytest diff --git a/tests/test_resolved_regional_lora.py b/tests/test_resolved_regional_lora.py new file mode 100644 index 0000000..85637a6 --- /dev/null +++ b/tests/test_resolved_regional_lora.py @@ -0,0 +1,317 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Verify immutable model-neutral resolved regional LoRA contracts.""" + +from __future__ import annotations + +import ast +from collections.abc import Callable +from dataclasses import FrozenInstanceError, replace +from pathlib import Path +from typing import Any, cast + +import pytest + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraScheduleBoundary, +) +from simple_syrup.domain.resolved_regional_lora import ( + RegionalLoraExecutionContract, + RegionalLoraGeometryClass, + RegionalLoraTensorShape, + ResolvedLoraOperationClass, + ResolvedRegionalLoraOperation, + ResolvedRegionalLoraOperationSet, + ResolvedRegionalLoraTarget, +) + + +def test_matrix_pair_retains_plan_target_shapes_scale_and_strength() -> None: + """Represent a matrix pair without guessing its eventual module class.""" + + adapter = _adapter(0) + operation = _matrix_operation(adapter=adapter, target_index=0) + + assert operation.adapter is adapter + assert operation.target.parameter_path == "diffusion_model.layer.weight" + assert operation.target.offset is None + assert operation.down_shape == RegionalLoraTensorShape((4, 16)) + assert operation.up_shape == RegionalLoraTensorShape((32, 4)) + assert operation.rank == 4 + assert operation.intrinsic_scale == 0.5 + assert operation.authored_strength == 0.8 + assert operation.required_geometry is RegionalLoraGeometryClass.TARGET_OPERATION + assert operation.supported is True + + +@pytest.mark.parametrize( + ("operation_class", "tensor_rank", "geometry"), + [ + ( + ResolvedLoraOperationClass.CONVOLUTION_1D, + 3, + RegionalLoraGeometryClass.DIRECT_CONVOLUTION_1D, + ), + ( + ResolvedLoraOperationClass.CONVOLUTION_2D, + 4, + RegionalLoraGeometryClass.DIRECT_CONVOLUTION_2D, + ), + ( + ResolvedLoraOperationClass.CONVOLUTION_3D, + 5, + RegionalLoraGeometryClass.DIRECT_CONVOLUTION_3D, + ), + ], +) +def test_convolution_contracts_represent_direct_and_mixed_tensor_shapes( + operation_class: ResolvedLoraOperationClass, + tensor_rank: int, + geometry: RegionalLoraGeometryClass, +) -> None: + """Represent direct convolution while allowing Comfy's 2D up projection.""" + + spatial = (3,) * (tensor_rank - 2) + operation = ResolvedRegionalLoraOperation( + adapter=_adapter(0), + target_index=0, + target=_target(), + normalized_operation_type="LoRAAdapter", + operation_class=operation_class, + down_shape=RegionalLoraTensorShape((4, 16, *spatial)), + up_shape=RegionalLoraTensorShape((32, 4)), + middle_shape=RegionalLoraTensorShape((4, 4, *spatial)), + reshape_shape=None, + rank=4, + intrinsic_scale=1.0, + required_geometry=geometry, + execution_contract=RegionalLoraExecutionContract.DIRECT_CONVOLUTION, + rejection_reason=None, + ) + + assert operation.supported is True + assert operation.middle_shape is not None + assert operation.middle_shape.rank == tensor_rank + + +def test_rejection_requires_reason_and_cannot_claim_executable_metadata() -> None: + """Keep unsupported targets explicit without misleading partial math.""" + + rejected = _rejection(adapter=_adapter(0), target_index=0) + + assert rejected.supported is False + assert rejected.rejection_reason == "DoRA scaling is unsupported." + assert rejected.down_shape is None + + with pytest.raises(ValueError, match="requires a reason"): + replace(rejected, rejection_reason="") + with pytest.raises(ValueError, match="cannot claim executable metadata"): + replace(rejected, rank=4) + + +@pytest.mark.parametrize( + ("dimensions", "message"), + [ + ((), "nonempty tuple"), + ((0, 2), "positive integers"), + ((-1, 2), "positive integers"), + ((cast(Any, True), 2), "positive integers"), + ((cast(Any, 1.5), 2), "positive integers"), + ], +) +def test_tensor_shape_rejects_mutable_or_invalid_dimensions( + dimensions: tuple[int, ...], + message: str, +) -> None: + """Require immutable positive integer shape metadata.""" + + with pytest.raises(ValueError, match=message): + RegionalLoraTensorShape(dimensions) + with pytest.raises(ValueError, match="nonempty tuple"): + RegionalLoraTensorShape(cast(Any, [2, 2])) + + +@pytest.mark.parametrize( + ("mutation", "message"), + [ + (lambda operation: replace(operation, rank=3), "down shape must begin"), + ( + lambda operation: replace( + operation, + up_shape=RegionalLoraTensorShape((32, 3)), + ), + "rank at index one", + ), + ( + lambda operation: replace(operation, intrinsic_scale=float("nan")), + "intrinsic scale must be finite", + ), + ( + lambda operation: replace( + operation, + intrinsic_scale=cast(Any, 1), + ), + "intrinsic scale must be finite", + ), + ( + lambda operation: replace( + operation, + middle_shape=RegionalLoraTensorShape((4,)), + ), + "at least two", + ), + ( + lambda operation: replace( + operation, + middle_shape=RegionalLoraTensorShape((4, 4, 1, 1)), + ), + "cannot contain middle", + ), + ( + lambda operation: replace( + operation, + required_geometry=RegionalLoraGeometryClass.DIRECT_CONVOLUTION_2D, + ), + "requires target geometry", + ), + ], +) +def test_supported_operation_rejects_inconsistent_math( + mutation: Callable[ + [ResolvedRegionalLoraOperation], + ResolvedRegionalLoraOperation, + ], + message: str, +) -> None: + """Reject inconsistent rank, scale, middle, and geometry metadata.""" + + with pytest.raises(ValueError, match=message): + mutation(_matrix_operation(adapter=_adapter(0), target_index=0)) + + +def test_operation_set_preserves_canonical_order_and_partitions_results() -> None: + """Keep adapter composition and per-adapter target indices contiguous.""" + + first = _matrix_operation(adapter=_adapter(0), target_index=0) + second = _rejection(adapter=_adapter(0), target_index=1) + third = _matrix_operation(adapter=_adapter(1), target_index=0) + operation_set = ResolvedRegionalLoraOperationSet((first, second, third)) + + assert operation_set.supported == (first, third) + assert operation_set.rejected == (second,) + + with pytest.raises(ValueError, match="retain declared order"): + ResolvedRegionalLoraOperationSet((third, first)) + with pytest.raises(ValueError, match="contiguous per adapter"): + ResolvedRegionalLoraOperationSet((replace(first, target_index=1),)) + with pytest.raises(TypeError, match="must be a tuple"): + ResolvedRegionalLoraOperationSet(cast(Any, [first])) + + +def test_domain_values_are_frozen_and_import_no_runtime_frameworks() -> None: + """Keep mutation and Comfy/Torch dependencies outside the domain owner.""" + + operation = _matrix_operation(adapter=_adapter(0), target_index=0) + with pytest.raises(FrozenInstanceError): + operation.rank = 8 # type: ignore[misc] + + source = ( + Path(__file__).parents[1] + / "simple_syrup" + / "domain" + / "resolved_regional_lora.py" + ) + tree = ast.parse(source.read_text(encoding="utf-8")) + imported_roots = { + alias.name.split(".", 1)[0] + for node in ast.walk(tree) + if isinstance(node, ast.Import) + for alias in node.names + } | { + (node.module or "").lstrip(".").split(".", 1)[0] + for node in ast.walk(tree) + if isinstance(node, ast.ImportFrom) + } + assert imported_roots.isdisjoint({"comfy", "torch"}) + + +def _matrix_operation( + *, + adapter: RegionalLoraAdapterPlan, + target_index: int, +) -> ResolvedRegionalLoraOperation: + """Return one valid matrix-pair descriptor.""" + + return ResolvedRegionalLoraOperation( + adapter=adapter, + target_index=target_index, + target=_target(), + normalized_operation_type="LoRAAdapter", + operation_class=ResolvedLoraOperationClass.MATRIX_PAIR, + down_shape=RegionalLoraTensorShape((4, 16)), + up_shape=RegionalLoraTensorShape((32, 4)), + middle_shape=None, + reshape_shape=None, + rank=4, + intrinsic_scale=0.5, + required_geometry=RegionalLoraGeometryClass.TARGET_OPERATION, + execution_contract=( + RegionalLoraExecutionContract.MATRIX_OR_RESHAPED_CONVOLUTION + ), + rejection_reason=None, + ) + + +def _rejection( + *, + adapter: RegionalLoraAdapterPlan, + target_index: int, +) -> ResolvedRegionalLoraOperation: + """Return one valid explicit rejection descriptor.""" + + return ResolvedRegionalLoraOperation( + adapter=adapter, + target_index=target_index, + target=_target(), + normalized_operation_type="LoRAAdapter", + 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="DoRA scaling is unsupported.", + ) + + +def _target() -> ResolvedRegionalLoraTarget: + """Return one valid model-relative target.""" + + return ResolvedRegionalLoraTarget( + model_target="diffusion_model.layer", + parameter_name="weight", + offset=None, + ) + + +def _adapter(composition_index: int) -> RegionalLoraAdapterPlan: + """Return one valid authored adapter plan.""" + + return RegionalLoraAdapterPlan( + adapter_identity=RegionalLoraAdapterIdentity( + f"adapter-{composition_index}.safetensors" + ), + composition_index=composition_index, + region_index=composition_index, + branch=RegionalLoraBranch.POSITIVE, + model_strength=0.8, + schedule=(RegionalLoraScheduleBoundary(0.0, 100.0, 1.0, 0),), + ) diff --git a/tests/test_resolved_regional_lora_translation.py b/tests/test_resolved_regional_lora_translation.py new file mode 100644 index 0000000..4bd767f --- /dev/null +++ b/tests/test_resolved_regional_lora_translation.py @@ -0,0 +1,310 @@ +# 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 translation from Comfy targets to domain descriptors.""" + +from __future__ import annotations + +from typing import cast + +import pytest +import torch +from comfy.weight_adapter.lora import LoRAAdapter + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraScheduleBoundary, +) +from simple_syrup.domain.resolved_regional_lora import ( + RegionalLoraExecutionContract, + RegionalLoraGeometryClass, + ResolvedLoraOperationClass, +) +from simple_syrup.runtime.regional_lora.comfy_adapter_resolution import ( + ComfyAdapterTargetPath, + ComfyNormalizedAdapterTarget, + ComfyRegionalAdapterResolution, + ComfyRegionalLoraResolution, +) +from simple_syrup.runtime.regional_lora.resolved_operation_translator import ( + ComfyResolvedOperationTranslator, +) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload + + +def test_translator_retains_matrix_metadata_order_and_authored_plan() -> None: + """Translate every target without copying or reordering authored plan state.""" + + first_adapter = _adapter(0) + second_adapter = _adapter(1) + first = _target( + "diffusion_model.first.weight", + up=torch.ones((32, 4)), + down=torch.ones((4, 16)), + alpha=2.0, + reshape=[32, 16], + ) + second = _target( + "diffusion_model.second.weight", + up=torch.ones((24, 8)), + down=torch.ones((8, 12)), + alpha=None, + offset=(0, 4, 8), + ) + + translated = ComfyResolvedOperationTranslator().translate( + _resolution(((first_adapter, (first,)), (second_adapter, (second,)))) + ) + + assert [entry.adapter for entry in translated.entries] == [ + first_adapter, + second_adapter, + ] + assert [entry.target_index for entry in translated.entries] == [0, 0] + assert [entry.target.parameter_path for entry in translated.entries] == [ + "diffusion_model.first.weight", + "diffusion_model.second.weight", + ] + assert translated.entries[0].rank == 4 + assert translated.entries[0].intrinsic_scale == 0.5 + assert translated.entries[0].reshape_shape is not None + assert translated.entries[0].reshape_shape.dimensions == (32, 16) + assert translated.entries[1].intrinsic_scale == 1.0 + assert translated.entries[1].target.offset == (0, 4, 8) + assert all( + entry.operation_class is ResolvedLoraOperationClass.MATRIX_PAIR + for entry in translated.entries + ) + + +@pytest.mark.parametrize( + ("tensor_rank", "operation_class", "geometry"), + [ + ( + 3, + ResolvedLoraOperationClass.CONVOLUTION_1D, + RegionalLoraGeometryClass.DIRECT_CONVOLUTION_1D, + ), + ( + 4, + ResolvedLoraOperationClass.CONVOLUTION_2D, + RegionalLoraGeometryClass.DIRECT_CONVOLUTION_2D, + ), + ( + 5, + ResolvedLoraOperationClass.CONVOLUTION_3D, + RegionalLoraGeometryClass.DIRECT_CONVOLUTION_3D, + ), + ], +) +def test_translator_classifies_explicit_convolution_and_middle_weights( + tensor_rank: int, + operation_class: ResolvedLoraOperationClass, + geometry: RegionalLoraGeometryClass, +) -> None: + """Classify direct convolution solely from normalized tensor organization.""" + + spatial = (3,) * (tensor_rank - 2) + target = _target( + "diffusion_model.conv.weight", + up=torch.ones((32, 4)), + down=torch.ones((4, 16, *spatial)), + middle=torch.ones((4, 4, *spatial)), + alpha=4.0, + ) + + entry = ( + ComfyResolvedOperationTranslator() + .translate(_resolution(((_adapter(0), (target,)),))) + .entries[0] + ) + + assert entry.operation_class is operation_class + assert entry.required_geometry is geometry + assert entry.execution_contract is RegionalLoraExecutionContract.DIRECT_CONVOLUTION + assert entry.intrinsic_scale == 1.0 + + +def test_translator_rejects_every_unsupported_target_without_omission() -> None: + """Retain DoRA, non-LoRA, malformed-shape, and malformed-path rejections.""" + + ordinary = _target( + "diffusion_model.valid.weight", + up=torch.ones((8, 2)), + down=torch.ones((2, 8)), + ) + dora = _target( + "diffusion_model.dora.weight", + up=torch.ones((8, 2)), + down=torch.ones((2, 8)), + dora_scale=torch.ones(8), + ) + non_lora = ComfyNormalizedAdapterTarget( + path=ComfyAdapterTargetPath("diffusion_model.diff.weight", None), + operation=("diff", (torch.ones(1),)), + operation_type="tuple", + source_keys=(), + ordinary_additive_lora=False, + ) + bad_rank = _target( + "diffusion_model.bad.weight", + up=torch.ones((8, 3)), + down=torch.ones((2, 8)), + ) + malformed_path = _target( + "weight", + up=torch.ones((8, 2)), + down=torch.ones((2, 8)), + ) + + result = ComfyResolvedOperationTranslator().translate( + _resolution( + ((_adapter(0), (ordinary, dora, non_lora, bad_rank, malformed_path)),) + ) + ) + + assert len(result.entries) == 5 + assert result.supported == (result.entries[0],) + assert result.rejected == result.entries[1:] + assert [entry.target_index for entry in result.entries] == [0, 1, 2, 3, 4] + assert [entry.rejection_reason for entry in result.rejected] == [ + "DoRA scaling is unsupported.", + "Normalized operation tuple is not an ordinary LoRA.", + "Regional LoRA up shape must contain its rank at index one.", + ("Normalized regional LoRA path must identify a model target and parameter."), + ] + + +@pytest.mark.parametrize( + ("alpha", "reshape", "message"), + [ + (float("inf"), None, "alpha must be finite"), + (cast(float, True), None, "finite scalar or None"), + (1.0, [8, 0], "positive integers"), + (1.0, [8, cast(int, True)], "positive integers"), + ], +) +def test_translator_rejects_invalid_alpha_and_reshape_metadata( + alpha: float, + reshape: list[int] | None, + message: str, +) -> None: + """Convert malformed normalized metadata into explicit target rejections.""" + + target = _target( + "diffusion_model.layer.weight", + up=torch.ones((8, 2)), + down=torch.ones((2, 8)), + alpha=alpha, + reshape=reshape, + ) + + entry = ( + ComfyResolvedOperationTranslator() + .translate(_resolution(((_adapter(0), (target,)),))) + .entries[0] + ) + + assert entry.supported is False + assert entry.rejection_reason is not None + assert message in entry.rejection_reason + + +def test_translator_reads_metadata_without_moving_or_mutating_tensors() -> None: + """Preserve tensor identity and state during translation.""" + + up = torch.ones((8, 2), dtype=torch.float16) + down = torch.ones((2, 8), dtype=torch.float16) + before = tuple( + ( + id(tensor), + tensor.data_ptr(), + tensor._version, + tensor.device, + tensor.dtype, + tensor.shape, + ) + for tensor in (up, down) + ) + target = _target("diffusion_model.layer.weight", up=up, down=down) + + result = ComfyResolvedOperationTranslator().translate( + _resolution(((_adapter(0), (target,)),)) + ) + + assert result.entries[0].supported is True + assert before == tuple( + ( + id(tensor), + tensor.data_ptr(), + tensor._version, + tensor.device, + tensor.dtype, + tensor.shape, + ) + for tensor in (up, down) + ) + + +def _resolution( + adapters: tuple[ + tuple[RegionalLoraAdapterPlan, tuple[ComfyNormalizedAdapterTarget, ...]], ... + ], +) -> ComfyRegionalLoraResolution: + """Return one ordered U2 resolution for translator characterization.""" + + results = tuple( + ComfyRegionalAdapterResolution( + adapter=adapter, + payload=RegionalLoraHostPayload(False, None, {}, None), + model_targets=targets, + source_entries=(), + issues=(), + ) + for adapter, targets in adapters + ) + return ComfyRegionalLoraResolution(adapters=results, issues=()) + + +def _target( + parameter_path: str, + *, + up: torch.Tensor, + down: torch.Tensor, + alpha: float | None = None, + middle: torch.Tensor | None = None, + dora_scale: torch.Tensor | None = None, + reshape: list[int] | None = None, + offset: tuple[int, ...] | None = None, +) -> ComfyNormalizedAdapterTarget: + """Return one installed normalized LoRA target with exact tensor references.""" + + operation = LoRAAdapter( + {"up", "down"}, + (up, down, alpha, middle, dora_scale, reshape), + ) + return ComfyNormalizedAdapterTarget( + path=ComfyAdapterTargetPath(parameter_path, offset), + operation=operation, + operation_type="LoRAAdapter", + source_keys=("down", "up"), + ordinary_additive_lora=dora_scale is None, + ) + + +def _adapter(composition_index: int) -> RegionalLoraAdapterPlan: + """Return one valid authored adapter plan.""" + + return RegionalLoraAdapterPlan( + adapter_identity=RegionalLoraAdapterIdentity( + f"adapter-{composition_index}.safetensors" + ), + composition_index=composition_index, + region_index=composition_index, + branch=RegionalLoraBranch.POSITIVE, + model_strength=0.8, + schedule=(RegionalLoraScheduleBoundary(0.0, 100.0, 1.0, 0),), + ) diff --git a/tests/test_run_managed_comfy_baseline.py b/tests/test_run_managed_comfy_baseline.py index 8adf548..730d690 100644 --- a/tests/test_run_managed_comfy_baseline.py +++ b/tests/test_run_managed_comfy_baseline.py @@ -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 + """Test the thin managed-baseline execution coordinator.""" from __future__ import annotations diff --git a/tests/test_run_sdxl_attention_coupling_integration.py b/tests/test_run_sdxl_attention_coupling_integration.py index ff12a04..6becd7d 100644 --- a/tests/test_run_sdxl_attention_coupling_integration.py +++ b/tests/test_run_sdxl_attention_coupling_integration.py @@ -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 managed SDXL integration coordinator.""" from __future__ import annotations diff --git a/tests/test_scheduled_clip_conditioning_metadata.py b/tests/test_scheduled_clip_conditioning_metadata.py index fe81cb6..96b689d 100644 --- a/tests/test_scheduled_clip_conditioning_metadata.py +++ b/tests/test_scheduled_clip_conditioning_metadata.py @@ -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 metadata preservation around installed scheduled CLIP encoding.""" from __future__ import annotations diff --git a/tests/test_sdxl_attention_coupling_results.py b/tests/test_sdxl_attention_coupling_results.py index 003c0a9..f56b7a3 100644 --- a/tests/test_sdxl_attention_coupling_results.py +++ b/tests/test_sdxl_attention_coupling_results.py @@ -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 SDXL history decoding and acceptance persistence.""" from __future__ import annotations diff --git a/tests/test_sdxl_attention_coupling_workflow.py b/tests/test_sdxl_attention_coupling_workflow.py index 8430689..5b5b3d2 100644 --- a/tests/test_sdxl_attention_coupling_workflow.py +++ b/tests/test_sdxl_attention_coupling_workflow.py @@ -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 public full and 1.5x SDXL managed workflow graph.""" from __future__ import annotations diff --git a/tests/test_sdxl_integration_checkpoint_link.py b/tests/test_sdxl_integration_checkpoint_link.py index 12a11f3..ead278b 100644 --- a/tests/test_sdxl_integration_checkpoint_link.py +++ b/tests/test_sdxl_integration_checkpoint_link.py @@ -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 temporary SDXL checkpoint link ownership.""" from __future__ import annotations diff --git a/tests/test_sdxl_integration_masks.py b/tests/test_sdxl_integration_masks.py index 971cc01..37a52b5 100644 --- a/tests/test_sdxl_integration_masks.py +++ b/tests/test_sdxl_integration_masks.py @@ -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 temporary Phase 8 SDXL mask ownership.""" from __future__ import annotations diff --git a/tests/test_segs_guided_tiled_diffusion_sampling_service.py b/tests/test_segs_guided_tiled_diffusion_sampling_service.py index 46da262..9facf16 100644 --- a/tests/test_segs_guided_tiled_diffusion_sampling_service.py +++ b/tests/test_segs_guided_tiled_diffusion_sampling_service.py @@ -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 SEGS-guided tiled diffusion batch planning and execution.""" from __future__ import annotations diff --git a/tests/test_spatial_model_arguments_validation.py b/tests/test_spatial_model_arguments_validation.py index 388ebb0..16c3845 100644 --- a/tests/test_spatial_model_arguments_validation.py +++ b/tests/test_spatial_model_arguments_validation.py @@ -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 spatial model calls fail closed before model execution.""" from __future__ import annotations diff --git a/tests/test_spatial_tensor_projection_characterization.py b/tests/test_spatial_tensor_projection_characterization.py index 27092a2..cfae12f 100644 --- a/tests/test_spatial_tensor_projection_characterization.py +++ b/tests/test_spatial_tensor_projection_characterization.py @@ -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 + """Characterize the focused final-axis spatial tensor projection owner.""" from __future__ import annotations diff --git a/tests/test_spatial_view_model_arguments.py b/tests/test_spatial_view_model_arguments.py index 9f0526b..5f54a99 100644 --- a/tests/test_spatial_view_model_arguments.py +++ b/tests/test_spatial_view_model_arguments.py @@ -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 apply-model argument transformation across spatial views.""" from __future__ import annotations diff --git a/tests/test_spatial_views.py b/tests/test_spatial_views.py index 2c4a7d5..0e49cbf 100644 --- a/tests/test_spatial_views.py +++ b/tests/test_spatial_views.py @@ -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 immutable spatial view and view-major layout invariants.""" from __future__ import annotations diff --git a/tests/test_tensor_snapshot_probe.py b/tests/test_tensor_snapshot_probe.py index 6273b12..97f4c75 100644 --- a/tests/test_tensor_snapshot_probe.py +++ b/tests/test_tensor_snapshot_probe.py @@ -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 benchmark tensor identity without source mutation.""" from __future__ import annotations diff --git a/tests/test_text_encoder_lora_fixture.py b/tests/test_text_encoder_lora_fixture.py index 1b0ddd9..b5cfd13 100644 --- a/tests/test_text_encoder_lora_fixture.py +++ b/tests/test_text_encoder_lora_fixture.py @@ -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 byte-level identity checks for the P9.4 LoRA fixture.""" from __future__ import annotations diff --git a/tests/test_text_encoder_lora_matrix.py b/tests/test_text_encoder_lora_matrix.py index e55d9f9..11f4071 100644 --- a/tests/test_text_encoder_lora_matrix.py +++ b/tests/test_text_encoder_lora_matrix.py @@ -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 closed P9.4 text-encoder LoRA matrix.""" from __future__ import annotations diff --git a/tests/test_text_encoder_lora_results.py b/tests/test_text_encoder_lora_results.py index a47899d..e134850 100644 --- a/tests/test_text_encoder_lora_results.py +++ b/tests/test_text_encoder_lora_results.py @@ -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 terminal P9.4 history, pixel, and model-call acceptance.""" from __future__ import annotations diff --git a/tests/test_text_encoder_lora_workflow.py b/tests/test_text_encoder_lora_workflow.py index 587187a..3e43658 100644 --- a/tests/test_text_encoder_lora_workflow.py +++ b/tests/test_text_encoder_lora_workflow.py @@ -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 public graph construction for P9.4 LoRA cases.""" from __future__ import annotations diff --git a/tests/test_tile_prediction_accumulation_characterization.py b/tests/test_tile_prediction_accumulation_characterization.py index d891b0f..c056ee6 100644 --- a/tests/test_tile_prediction_accumulation_characterization.py +++ b/tests/test_tile_prediction_accumulation_characterization.py @@ -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 + """Characterize prediction accumulation imported from the mixed tiled runtime.""" from __future__ import annotations diff --git a/tests/test_tiled_attention_coupling_sampling_service.py b/tests/test_tiled_attention_coupling_sampling_service.py index 215a97e..4b4e9f2 100644 --- a/tests/test_tiled_attention_coupling_sampling_service.py +++ b/tests/test_tiled_attention_coupling_sampling_service.py @@ -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 Attention Coupling composes through the tiled sampling authority.""" from __future__ import annotations diff --git a/tests/test_tiled_diffusion_conditioning_batch_service.py b/tests/test_tiled_diffusion_conditioning_batch_service.py index ccce248..1e7d940 100644 --- a/tests/test_tiled_diffusion_conditioning_batch_service.py +++ b/tests/test_tiled_diffusion_conditioning_batch_service.py @@ -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-item tiled diffusion conditioning batch execution.""" from __future__ import annotations diff --git a/tests/test_tiled_diffusion_item_sampling_service.py b/tests/test_tiled_diffusion_item_sampling_service.py index 26b4425..9bf5386 100644 --- a/tests/test_tiled_diffusion_item_sampling_service.py +++ b/tests/test_tiled_diffusion_item_sampling_service.py @@ -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-item tiled diffusion runtime selection and forwarding.""" from __future__ import annotations diff --git a/tests/test_tiled_diffusion_sampling_routing_service.py b/tests/test_tiled_diffusion_sampling_routing_service.py index bd9e134..5c366b8 100644 --- a/tests/test_tiled_diffusion_sampling_routing_service.py +++ b/tests/test_tiled_diffusion_sampling_routing_service.py @@ -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 top-level tiled diffusion route precedence and delegation.""" from __future__ import annotations diff --git a/tests/test_tiled_sampling_validation_characterization.py b/tests/test_tiled_sampling_validation_characterization.py index ec0485f..20a8931 100644 --- a/tests/test_tiled_sampling_validation_characterization.py +++ b/tests/test_tiled_sampling_validation_characterization.py @@ -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 + """Characterize validation imported from the mixed tiled-sampling runtime.""" from __future__ import annotations diff --git a/tests/test_tiled_spatial_batch_layout.py b/tests/test_tiled_spatial_batch_layout.py index 20af991..bff1348 100644 --- a/tests/test_tiled_spatial_batch_layout.py +++ b/tests/test_tiled_spatial_batch_layout.py @@ -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 SpatialBatchLayout construction from tiled plans.""" from __future__ import annotations diff --git a/tests/test_unet_attention_backend.py b/tests/test_unet_attention_backend.py index d4f6d02..047e790 100644 --- a/tests/test_unet_attention_backend.py +++ b/tests/test_unet_attention_backend.py @@ -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 collision-safe MODEL derivation for the standard-UNet backend.""" from __future__ import annotations diff --git a/tests/test_unet_attention_backend_integration.py b/tests/test_unet_attention_backend_integration.py index 34626ce..3f7b5ce 100644 --- a/tests/test_unet_attention_backend_integration.py +++ b/tests/test_unet_attention_backend_integration.py @@ -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 production standard-UNet backend through installed transformers.""" from __future__ import annotations diff --git a/tests/test_unet_attention_branch_batch.py b/tests/test_unet_attention_branch_batch.py index b12e640..8006ba2 100644 --- a/tests/test_unet_attention_branch_batch.py +++ b/tests/test_unet_attention_branch_batch.py @@ -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 compact standard-UNet attention branch packing and restoration.""" from __future__ import annotations diff --git a/tests/test_unet_attention_context_wrapper.py b/tests/test_unet_attention_context_wrapper.py index a997054..78ed668 100644 --- a/tests/test_unet_attention_context_wrapper.py +++ b/tests/test_unet_attention_context_wrapper.py @@ -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 CFG and schedule context publication for standard UNet.""" from __future__ import annotations diff --git a/tests/test_unet_attention_coupling_model_family.py b/tests/test_unet_attention_coupling_model_family.py index d52809c..91761bf 100644 --- a/tests/test_unet_attention_coupling_model_family.py +++ b/tests/test_unet_attention_coupling_model_family.py @@ -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 standard-UNet Attention Coupling family policy.""" from __future__ import annotations @@ -31,6 +35,7 @@ from simple_syrup.runtime.attention_coupling.unet_attention_state import ( from simple_syrup.runtime.attention_coupling.unet_context import ( STANDARD_UNET_REGIONAL_CONTEXT_VALIDATOR, ) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload from simple_syrup.runtime.regional_lora_plan_adapter import RegionalLoraPlanAdaptation from simple_syrup.services.unet_attention_coupling_model_family import ( StandardUnetAttentionCouplingModelFamily, @@ -125,7 +130,10 @@ def _regional_lora_adaptation() -> RegionalLoraPlanAdaptation: 1.0, (RegionalLoraScheduleBoundary(0.0, 1.0, 1.0, 0),), ) - return RegionalLoraPlanAdaptation(RegionalLoraPlan((adapter,)), (object(),)) + return RegionalLoraPlanAdaptation( + RegionalLoraPlan((adapter,)), + (RegionalLoraHostPayload.unresolved(object()),), + ) def _plan() -> ProcessedRegionalAttentionPlan: diff --git a/tests/test_unet_attention_diagnostics.py b/tests/test_unet_attention_diagnostics.py index d44ae2c..afd2204 100644 --- a/tests/test_unet_attention_diagnostics.py +++ b/tests/test_unet_attention_diagnostics.py @@ -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 standard-UNet structured resolution diagnostic emission.""" from __future__ import annotations diff --git a/tests/test_unet_attn2_comfy_contract.py b/tests/test_unet_attn2_comfy_contract.py index 77598aa..6ade0dd 100644 --- a/tests/test_unet_attn2_comfy_contract.py +++ b/tests/test_unet_attn2_comfy_contract.py @@ -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 + """Characterize installed Comfy's paired UNet attn2 callback contract.""" from __future__ import annotations diff --git a/tests/test_unet_attn2_execution.py b/tests/test_unet_attn2_execution.py index 8343068..ae5a694 100644 --- a/tests/test_unet_attn2_execution.py +++ b/tests/test_unet_attn2_execution.py @@ -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 standard-UNet attn2 branch execution and spatial blending.""" from __future__ import annotations diff --git a/tests/test_unet_attn2_geometry.py b/tests/test_unet_attn2_geometry.py index 44a60c3..e4d468e 100644 --- a/tests/test_unet_attn2_geometry.py +++ b/tests/test_unet_attn2_geometry.py @@ -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 standard-UNet attn2 query-geometry resolution.""" from __future__ import annotations diff --git a/tests/test_unet_attn2_patch.py b/tests/test_unet_attn2_patch.py index 71b8a6a..eb0f82f 100644 --- a/tests/test_unet_attn2_patch.py +++ b/tests/test_unet_attn2_patch.py @@ -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 paired Comfy callback adapter for standard-UNet coupling.""" from __future__ import annotations diff --git a/tests/test_unet_attn2_resolution_cache.py b/tests/test_unet_attn2_resolution_cache.py index 598b2cb..41d4bc1 100644 --- a/tests/test_unet_attn2_resolution_cache.py +++ b/tests/test_unet_attn2_resolution_cache.py @@ -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 task-local standard-UNet attn2 resolution caching.""" from __future__ import annotations diff --git a/tests/test_unet_attn2_resolution_contract.py b/tests/test_unet_attn2_resolution_contract.py index 658c880..03ad060 100644 --- a/tests/test_unet_attn2_resolution_contract.py +++ b/tests/test_unet_attn2_resolution_contract.py @@ -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 + """Characterize installed Comfy metadata for UNet attn2 resolution parsing.""" from __future__ import annotations diff --git a/tests/test_unet_dynamic_attention_integration.py b/tests/test_unet_dynamic_attention_integration.py index 018ad68..4cd11a7 100644 --- a/tests/test_unet_dynamic_attention_integration.py +++ b/tests/test_unet_dynamic_attention_integration.py @@ -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 dynamic regional state reaches the installed UNet attn2 callbacks.""" from __future__ import annotations diff --git a/tests/test_unet_dynamic_attn2_execution_resolver.py b/tests/test_unet_dynamic_attn2_execution_resolver.py index c6139c9..fe31e8e 100644 --- a/tests/test_unet_dynamic_attn2_execution_resolver.py +++ b/tests/test_unet_dynamic_attn2_execution_resolver.py @@ -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 dynamic per-resolution standard-UNet attn2 execution resolution.""" from __future__ import annotations diff --git a/tests/unet_attention_coupling_call_harness.py b/tests/unet_attention_coupling_call_harness.py index 0cc47e6..0eb914c 100644 --- a/tests/unet_attention_coupling_call_harness.py +++ b/tests/unet_attention_coupling_call_harness.py @@ -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 standard-UNet calls to shared schedule and CFG invariants.""" from __future__ import annotations diff --git a/tests/unet_attention_coupling_diagnostics_harness.py b/tests/unet_attention_coupling_diagnostics_harness.py index 32b8f42..221d7a8 100644 --- a/tests/unet_attention_coupling_diagnostics_harness.py +++ b/tests/unet_attention_coupling_diagnostics_harness.py @@ -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 UNet identity to shared diagnostic invariant execution.""" from __future__ import annotations diff --git a/tests/unet_attention_coupling_invariant_harness.py b/tests/unet_attention_coupling_invariant_harness.py index a0fc7b7..4f709eb 100644 --- a/tests/unet_attention_coupling_invariant_harness.py +++ b/tests/unet_attention_coupling_invariant_harness.py @@ -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 standard-UNet attn2 execution to the shared invariant contract.""" from __future__ import annotations diff --git a/tests/unet_attention_coupling_lifecycle_harness.py b/tests/unet_attention_coupling_lifecycle_harness.py index 242493e..3c53b8e 100644 --- a/tests/unet_attention_coupling_lifecycle_harness.py +++ b/tests/unet_attention_coupling_lifecycle_harness.py @@ -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 standard-UNet model derivation to shared lifecycle invariants.""" from __future__ import annotations diff --git a/tools/anima_attention_coupling_conditioning.py b/tools/anima_attention_coupling_conditioning.py index 648910d..ba92d3a 100644 --- a/tools/anima_attention_coupling_conditioning.py +++ b/tools/anima_attention_coupling_conditioning.py @@ -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 the accepted scheduled conditioning graph for Anima workflows.""" from __future__ import annotations diff --git a/tools/anima_attention_coupling_integration/__init__.py b/tools/anima_attention_coupling_integration/__init__.py index df852d4..2b8d349 100644 --- a/tools/anima_attention_coupling_integration/__init__.py +++ b/tools/anima_attention_coupling_integration/__init__.py @@ -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 the P5.9 public-node Anima integration evidence package.""" diff --git a/tools/anima_attention_coupling_integration/matrix.py b/tools/anima_attention_coupling_integration/matrix.py index 70b621a..ea24943 100644 --- a/tools/anima_attention_coupling_integration/matrix.py +++ b/tools/anima_attention_coupling_integration/matrix.py @@ -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 the immutable P5.9 public Attention Coupling workflow matrix.""" from __future__ import annotations diff --git a/tools/anima_attention_coupling_integration/results.py b/tools/anima_attention_coupling_integration/results.py index 21cb000..8a51fce 100644 --- a/tools/anima_attention_coupling_integration/results.py +++ b/tools/anima_attention_coupling_integration/results.py @@ -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 + """Validate and persist complete labeled P5.9 public-node evidence.""" from __future__ import annotations diff --git a/tools/anima_attention_coupling_integration/schema.py b/tools/anima_attention_coupling_integration/schema.py index 8680c94..d6134cb 100644 --- a/tools/anima_attention_coupling_integration/schema.py +++ b/tools/anima_attention_coupling_integration/schema.py @@ -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 + """Validate the exact live P5.8 public-node metadata contract.""" from __future__ import annotations diff --git a/tools/anima_attention_coupling_integration/workflow.py b/tools/anima_attention_coupling_integration/workflow.py index c0f6222..978d0bd 100644 --- a/tools/anima_attention_coupling_integration/workflow.py +++ b/tools/anima_attention_coupling_integration/workflow.py @@ -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 + """Bind P5.9 cases to the shared Anima workflow graph owner.""" from __future__ import annotations diff --git a/tools/anima_attention_coupling_prompts.py b/tools/anima_attention_coupling_prompts.py index f2cd6cd..b43baff 100644 --- a/tools/anima_attention_coupling_prompts.py +++ b/tools/anima_attention_coupling_prompts.py @@ -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 + """Own pinned prompts and LoRA declarations for managed Anima workflows.""" from __future__ import annotations diff --git a/tools/anima_attention_coupling_workflow.py b/tools/anima_attention_coupling_workflow.py index a65bb57..9b9a6ee 100644 --- a/tools/anima_attention_coupling_workflow.py +++ b/tools/anima_attention_coupling_workflow.py @@ -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 + """Assemble shared full, tiled, and Contextual Anima sampler graphs.""" from __future__ import annotations diff --git a/tools/anima_contextual_attention_coupling_integration/__init__.py b/tools/anima_contextual_attention_coupling_integration/__init__.py index e74967a..9226361 100644 --- a/tools/anima_contextual_attention_coupling_integration/__init__.py +++ b/tools/anima_contextual_attention_coupling_integration/__init__.py @@ -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 + """Own the P7.8 managed Contextual Attention Coupling integration.""" diff --git a/tools/anima_contextual_attention_coupling_integration/diagnostics.py b/tools/anima_contextual_attention_coupling_integration/diagnostics.py index 388f02e..617e2c8 100644 --- a/tools/anima_contextual_attention_coupling_integration/diagnostics.py +++ b/tools/anima_contextual_attention_coupling_integration/diagnostics.py @@ -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 + """Validate structured P7.8 local and reduced-global diagnostics.""" from __future__ import annotations diff --git a/tools/anima_contextual_attention_coupling_integration/matrix.py b/tools/anima_contextual_attention_coupling_integration/matrix.py index 51a27cf..2ddd2ef 100644 --- a/tools/anima_contextual_attention_coupling_integration/matrix.py +++ b/tools/anima_contextual_attention_coupling_integration/matrix.py @@ -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 the immutable P7.8 Contextual Attention Coupling matrix.""" from __future__ import annotations diff --git a/tools/anima_contextual_attention_coupling_integration/results.py b/tools/anima_contextual_attention_coupling_integration/results.py index 8e7e924..cc22610 100644 --- a/tools/anima_contextual_attention_coupling_integration/results.py +++ b/tools/anima_contextual_attention_coupling_integration/results.py @@ -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 + """Validate and persist complete labeled P7.8 managed-run evidence.""" from __future__ import annotations diff --git a/tools/anima_contextual_attention_coupling_integration/schema.py b/tools/anima_contextual_attention_coupling_integration/schema.py index a29646d..fdfa309 100644 --- a/tools/anima_contextual_attention_coupling_integration/schema.py +++ b/tools/anima_contextual_attention_coupling_integration/schema.py @@ -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 + """Validate the exact live P7.8 combined-node metadata contract.""" from __future__ import annotations diff --git a/tools/anima_contextual_attention_coupling_integration/workflow.py b/tools/anima_contextual_attention_coupling_integration/workflow.py index 1e3e8b6..c37ae0a 100644 --- a/tools/anima_contextual_attention_coupling_integration/workflow.py +++ b/tools/anima_contextual_attention_coupling_integration/workflow.py @@ -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 P7.8 API graphs through the shared Anima workflow owner.""" from __future__ import annotations diff --git a/tools/anima_latent_source_workflow.py b/tools/anima_latent_source_workflow.py index 71b484d..ec553ea 100644 --- a/tools/anima_latent_source_workflow.py +++ b/tools/anima_latent_source_workflow.py @@ -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 empty and coherent-upscale latent sources for Anima workflows.""" from __future__ import annotations diff --git a/tools/anima_lora_characterization/__init__.py b/tools/anima_lora_characterization/__init__.py index c575535..2b562ac 100644 --- a/tools/anima_lora_characterization/__init__.py +++ b/tools/anima_lora_characterization/__init__.py @@ -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 + """Characterize pinned global Anima LoRA behavior through ComfyUI.""" diff --git a/tools/anima_lora_characterization/artifact_inventory.py b/tools/anima_lora_characterization/artifact_inventory.py index ea204d7..bd3bd11 100644 --- a/tools/anima_lora_characterization/artifact_inventory.py +++ b/tools/anima_lora_characterization/artifact_inventory.py @@ -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 + """Inspect and validate the pinned ADAPTER_A safetensors target surface.""" from __future__ import annotations diff --git a/tools/anima_lora_characterization/evidence_validation.py b/tools/anima_lora_characterization/evidence_validation.py index 23f3933..294cdfc 100644 --- a/tools/anima_lora_characterization/evidence_validation.py +++ b/tools/anima_lora_characterization/evidence_validation.py @@ -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 + """Enforce complete ADAPTER_A target, order, schedule, and denoiser evidence.""" from __future__ import annotations diff --git a/tools/anima_lora_characterization/history_outputs.py b/tools/anima_lora_characterization/history_outputs.py index 9e23e92..cd574df 100644 --- a/tools/anima_lora_characterization/history_outputs.py +++ b/tools/anima_lora_characterization/history_outputs.py @@ -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 + """Decode P0.7 LoRA probe and image outputs from Comfy history.""" from __future__ import annotations diff --git a/tools/anima_lora_characterization/json_contract.py b/tools/anima_lora_characterization/json_contract.py index 4c3b954..aa3bae2 100644 --- a/tools/anima_lora_characterization/json_contract.py +++ b/tools/anima_lora_characterization/json_contract.py @@ -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 + """Narrow dynamic JSON values at P0.7 artifact boundaries.""" from __future__ import annotations diff --git a/tools/anima_lora_characterization/matrix.py b/tools/anima_lora_characterization/matrix.py index e8487b4..3a69da4 100644 --- a/tools/anima_lora_characterization/matrix.py +++ b/tools/anima_lora_characterization/matrix.py @@ -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 + """Own the immutable pinned ADAPTER_A global characterization matrix.""" from __future__ import annotations diff --git a/tools/anima_lora_characterization/results.py b/tools/anima_lora_characterization/results.py index 5324da6..80e2171 100644 --- a/tools/anima_lora_characterization/results.py +++ b/tools/anima_lora_characterization/results.py @@ -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 + """Journal and finalize complete validated ADAPTER_A characterization results.""" from __future__ import annotations diff --git a/tools/anima_lora_characterization/workflow.py b/tools/anima_lora_characterization/workflow.py index 5620f1a..9a5af4d 100644 --- a/tools/anima_lora_characterization/workflow.py +++ b/tools/anima_lora_characterization/workflow.py @@ -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 ordinary global Comfy workflows for pinned Anima LoRA runs.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_admission_integration/__init__.py b/tools/anima_regional_lora_admission_integration/__init__.py index 95015e4..93d5750 100644 --- a/tools/anima_regional_lora_admission_integration/__init__.py +++ b/tools/anima_regional_lora_admission_integration/__init__.py @@ -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 managed evidence for P9.5 regional LoRA admission.""" diff --git a/tools/anima_regional_lora_admission_integration/conditioning.py b/tools/anima_regional_lora_admission_integration/conditioning.py index 928ec55..9f94b0c 100644 --- a/tools/anima_regional_lora_admission_integration/conditioning.py +++ b/tools/anima_regional_lora_admission_integration/conditioning.py @@ -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 native regional-LoRA admission conditioning and HookGroup graphs.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_admission_integration/graph_contract.py b/tools/anima_regional_lora_admission_integration/graph_contract.py index 5a0c799..2755c3d 100644 --- a/tools/anima_regional_lora_admission_integration/graph_contract.py +++ b/tools/anima_regional_lora_admission_integration/graph_contract.py @@ -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 the shared fixed regional-LoRA admission graph contract.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_admission_integration/history.py b/tools/anima_regional_lora_admission_integration/history.py index 2504960..bb619f1 100644 --- a/tools/anima_regional_lora_admission_integration/history.py +++ b/tools/anima_regional_lora_admission_integration/history.py @@ -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 + """Narrow successful or rejected regional-LoRA admission history.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_admission_integration/matrix.py b/tools/anima_regional_lora_admission_integration/matrix.py index 446fe69..0c1881d 100644 --- a/tools/anima_regional_lora_admission_integration/matrix.py +++ b/tools/anima_regional_lora_admission_integration/matrix.py @@ -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 the closed P9.5 regional diffusion-WeightHook admission matrix.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_admission_integration/results.py b/tools/anima_regional_lora_admission_integration/results.py index 488897c..def9fe1 100644 --- a/tools/anima_regional_lora_admission_integration/results.py +++ b/tools/anima_regional_lora_admission_integration/results.py @@ -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 + """Persist complete labeled P9.5 managed admission evidence.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_admission_integration/validation.py b/tools/anima_regional_lora_admission_integration/validation.py index 20bb01e..a0572ce 100644 --- a/tools/anima_regional_lora_admission_integration/validation.py +++ b/tools/anima_regional_lora_admission_integration/validation.py @@ -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 + """Validate exact P9.5 supported execution and fail-closed classifications.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_admission_integration/workflow.py b/tools/anima_regional_lora_admission_integration/workflow.py index 07c0d8d..46bf2a3 100644 --- a/tools/anima_regional_lora_admission_integration/workflow.py +++ b/tools/anima_regional_lora_admission_integration/workflow.py @@ -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 + """Bind regional-LoRA admission cases to the shared full-context graph.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/__init__.py b/tools/anima_regional_lora_performance/__init__.py index e94d7a0..e22fdf4 100644 --- a/tools/anima_regional_lora_performance/__init__.py +++ b/tools/anima_regional_lora_performance/__init__.py @@ -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 + """Benchmark pinned full-context Anima regional LoRA execution.""" diff --git a/tools/anima_regional_lora_performance/artifacts.py b/tools/anima_regional_lora_performance/artifacts.py index b3ecf93..2135704 100644 --- a/tools/anima_regional_lora_performance/artifacts.py +++ b/tools/anima_regional_lora_performance/artifacts.py @@ -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 + """Resolve and verify pinned Anima performance artifacts.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/attention_backend.py b/tools/anima_regional_lora_performance/attention_backend.py index c38d911..6771bf9 100644 --- a/tools/anima_regional_lora_performance/attention_backend.py +++ b/tools/anima_regional_lora_performance/attention_backend.py @@ -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 and observe the installed Sage attention override boundary.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/environment.py b/tools/anima_regional_lora_performance/environment.py index f1b77c0..bffb4a7 100644 --- a/tools/anima_regional_lora_performance/environment.py +++ b/tools/anima_regional_lora_performance/environment.py @@ -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 + """Publish the installed Anima performance runtime identity.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/execution_fixture.py b/tools/anima_regional_lora_performance/execution_fixture.py index 97a985e..20a399d 100644 --- a/tools/anima_regional_lora_performance/execution_fixture.py +++ b/tools/anima_regional_lora_performance/execution_fixture.py @@ -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 Anima performance attention and tensor inputs.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/isolated_measurement.py b/tools/anima_regional_lora_performance/isolated_measurement.py index 1080e2a..aee2919 100644 --- a/tools/anima_regional_lora_performance/isolated_measurement.py +++ b/tools/anima_regional_lora_performance/isolated_measurement.py @@ -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 + """Measure, validate, and publish isolated P10.1 VRAM evidence.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/isolated_runner.py b/tools/anima_regional_lora_performance/isolated_runner.py index 8578929..bded141 100644 --- a/tools/anima_regional_lora_performance/isolated_runner.py +++ b/tools/anima_regional_lora_performance/isolated_runner.py @@ -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 + """Coordinate ordered isolated P10.1 VRAM profile measurement.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/isolated_suite.py b/tools/anima_regional_lora_performance/isolated_suite.py index f565003..3d7f55d 100644 --- a/tools/anima_regional_lora_performance/isolated_suite.py +++ b/tools/anima_regional_lora_performance/isolated_suite.py @@ -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 + """Prepare and release one isolated P10.1 scaling profile.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/manifest.py b/tools/anima_regional_lora_performance/manifest.py index ab233db..d3edaf0 100644 --- a/tools/anima_regional_lora_performance/manifest.py +++ b/tools/anima_regional_lora_performance/manifest.py @@ -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 the immutable pinned P5.7 performance benchmark contract.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_fixture.py b/tools/anima_regional_lora_performance/matrix_fixture.py index 77309c1..b2c71d2 100644 --- a/tools/anima_regional_lora_performance/matrix_fixture.py +++ b/tools/anima_regional_lora_performance/matrix_fixture.py @@ -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 identical-context tensors and canonical matrix masks.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_manifest.py b/tools/anima_regional_lora_performance/matrix_manifest.py index c2b972f..6ec1fd3 100644 --- a/tools/anima_regional_lora_performance/matrix_manifest.py +++ b/tools/anima_regional_lora_performance/matrix_manifest.py @@ -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 + """Declare the immutable P10.1 regional LoRA scaling matrix.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_measurement.py b/tools/anima_regional_lora_performance/matrix_measurement.py index da575bd..f9eb184 100644 --- a/tools/anima_regional_lora_performance/matrix_measurement.py +++ b/tools/anima_regional_lora_performance/matrix_measurement.py @@ -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 + """Measure one scaling repeat with production cache and work diagnostics.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_profile.py b/tools/anima_regional_lora_performance/matrix_profile.py index 7a75b61..08b2961 100644 --- a/tools/anima_regional_lora_performance/matrix_profile.py +++ b/tools/anima_regional_lora_performance/matrix_profile.py @@ -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 + """Translate one scaling position into explicit runtime adapter uses.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_profiling.py b/tools/anima_regional_lora_performance/matrix_profiling.py index c26241c..26e6735 100644 --- a/tools/anima_regional_lora_performance/matrix_profiling.py +++ b/tools/anima_regional_lora_performance/matrix_profiling.py @@ -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 + """Capture raw and summarized operators for selected scaling positions.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_results.py b/tools/anima_regional_lora_performance/matrix_results.py index fb22cb9..3790c5a 100644 --- a/tools/anima_regional_lora_performance/matrix_results.py +++ b/tools/anima_regional_lora_performance/matrix_results.py @@ -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 + """Evaluate and publish complete P10.1 scaling evidence.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_runner.py b/tools/anima_regional_lora_performance/matrix_runner.py index fcd2116..d7871a5 100644 --- a/tools/anima_regional_lora_performance/matrix_runner.py +++ b/tools/anima_regional_lora_performance/matrix_runner.py @@ -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 + """Coordinate interleaved warmup and repeats for the P10.1 scaling matrix.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/matrix_suite.py b/tools/anima_regional_lora_performance/matrix_suite.py index 629bdd5..97b9fda 100644 --- a/tools/anima_regional_lora_performance/matrix_suite.py +++ b/tools/anima_regional_lora_performance/matrix_suite.py @@ -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 + """Prepare and clean one resident P10.1 scaling suite.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/measurement.py b/tools/anima_regional_lora_performance/measurement.py index dbfbdcb..c0e0602 100644 --- a/tools/anima_regional_lora_performance/measurement.py +++ b/tools/anima_regional_lora_performance/measurement.py @@ -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 + """Measure one exact CUDA denoiser-call sequence.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/model_fixture.py b/tools/anima_regional_lora_performance/model_fixture.py index bc5fe68..cbcf82b 100644 --- a/tools/anima_regional_lora_performance/model_fixture.py +++ b/tools/anima_regional_lora_performance/model_fixture.py @@ -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 + """Load the exact installed Anima model and regional LoRA fixture.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/operator_capture.py b/tools/anima_regional_lora_performance/operator_capture.py index c3f60a7..68501d6 100644 --- a/tools/anima_regional_lora_performance/operator_capture.py +++ b/tools/anima_regional_lora_performance/operator_capture.py @@ -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 + """Decode complete PyTorch profiler aggregates into immutable evidence rows.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/profile.py b/tools/anima_regional_lora_performance/profile.py index 7635d42..cc329ec 100644 --- a/tools/anima_regional_lora_performance/profile.py +++ b/tools/anima_regional_lora_performance/profile.py @@ -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 + """Translate one pinned P5.7 definition into repeated ADAPTER_A adapter uses.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/profiling.py b/tools/anima_regional_lora_performance/profiling.py index ee61bc7..caba8e9 100644 --- a/tools/anima_regional_lora_performance/profiling.py +++ b/tools/anima_regional_lora_performance/profiling.py @@ -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 + """Capture complete PyTorch operator evidence for Anima performance profiles.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/results.py b/tools/anima_regional_lora_performance/results.py index 17d87f7..485f7ca 100644 --- a/tools/anima_regional_lora_performance/results.py +++ b/tools/anima_regional_lora_performance/results.py @@ -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 + """Evaluate and persist complete P5.7 performance evidence.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/runner.py b/tools/anima_regional_lora_performance/runner.py index 4708d78..a6ae3ad 100644 --- a/tools/anima_regional_lora_performance/runner.py +++ b/tools/anima_regional_lora_performance/runner.py @@ -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 + """Coordinate the real full-resolution Anima performance matrix.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/runtime_profile.py b/tools/anima_regional_lora_performance/runtime_profile.py index 633f91c..6ca9142 100644 --- a/tools/anima_regional_lora_performance/runtime_profile.py +++ b/tools/anima_regional_lora_performance/runtime_profile.py @@ -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 + """Derive one typed Anima benchmark runtime from explicit adapter uses.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/scaling_work_contract.py b/tools/anima_regional_lora_performance/scaling_work_contract.py index 53baf7d..da53507 100644 --- a/tools/anima_regional_lora_performance/scaling_work_contract.py +++ b/tools/anima_regional_lora_performance/scaling_work_contract.py @@ -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 + """Map declared scaling work to its immutable observed value.""" from __future__ import annotations diff --git a/tools/anima_regional_lora_performance/suite.py b/tools/anima_regional_lora_performance/suite.py index 42ca123..949586b 100644 --- a/tools/anima_regional_lora_performance/suite.py +++ b/tools/anima_regional_lora_performance/suite.py @@ -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 + """Prepare one verified resident Anima performance suite.""" from __future__ import annotations diff --git a/tools/anima_regional_nondiffusion_rejection_integration/__init__.py b/tools/anima_regional_nondiffusion_rejection_integration/__init__.py index f090f3d..c422b64 100644 --- a/tools/anima_regional_nondiffusion_rejection_integration/__init__.py +++ b/tools/anima_regional_nondiffusion_rejection_integration/__init__.py @@ -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 managed evidence for P9.6 target-owner rejection.""" diff --git a/tools/anima_regional_nondiffusion_rejection_integration/matrix.py b/tools/anima_regional_nondiffusion_rejection_integration/matrix.py index 59f12cc..914a964 100644 --- a/tools/anima_regional_nondiffusion_rejection_integration/matrix.py +++ b/tools/anima_regional_nondiffusion_rejection_integration/matrix.py @@ -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 the closed P9.6 non-diffusion regional-target rejection matrix.""" from __future__ import annotations diff --git a/tools/anima_regional_nondiffusion_rejection_integration/results.py b/tools/anima_regional_nondiffusion_rejection_integration/results.py index 2ffd173..8542ee7 100644 --- a/tools/anima_regional_nondiffusion_rejection_integration/results.py +++ b/tools/anima_regional_nondiffusion_rejection_integration/results.py @@ -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 + """Persist ordered labeled P9.6 non-diffusion rejection artifacts.""" from __future__ import annotations diff --git a/tools/anima_regional_nondiffusion_rejection_integration/validation.py b/tools/anima_regional_nondiffusion_rejection_integration/validation.py index 0d665c7..185d4e2 100644 --- a/tools/anima_regional_nondiffusion_rejection_integration/validation.py +++ b/tools/anima_regional_nondiffusion_rejection_integration/validation.py @@ -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 + """Validate exact P9.6 non-diffusion pre-sampling rejection evidence.""" from __future__ import annotations diff --git a/tools/anima_regional_output_capture/__init__.py b/tools/anima_regional_output_capture/__init__.py index b3cd96c..f019d0e 100644 --- a/tools/anima_regional_output_capture/__init__.py +++ b/tools/anima_regional_output_capture/__init__.py @@ -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 + """Capture labeled decoded evidence for regional Anima LoRA profiles.""" diff --git a/tools/anima_regional_output_capture/matrix.py b/tools/anima_regional_output_capture/matrix.py index 7f45ab7..57d103d 100644 --- a/tools/anima_regional_output_capture/matrix.py +++ b/tools/anima_regional_output_capture/matrix.py @@ -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 the fixed labeled Anima regional LoRA output matrix.""" from __future__ import annotations diff --git a/tools/anima_regional_output_capture/results.py b/tools/anima_regional_output_capture/results.py index 60d11e0..ff5f8b4 100644 --- a/tools/anima_regional_output_capture/results.py +++ b/tools/anima_regional_output_capture/results.py @@ -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 + """Persist labeled workflows, histories, images, and hashes for visual review.""" from __future__ import annotations diff --git a/tools/anima_regional_output_capture/workflow.py b/tools/anima_regional_output_capture/workflow.py index b7446e0..31958b1 100644 --- a/tools/anima_regional_output_capture/workflow.py +++ b/tools/anima_regional_output_capture/workflow.py @@ -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 fixed API workflows for labeled regional Anima output evidence.""" from __future__ import annotations diff --git a/tools/anima_regression_oracle/__init__.py b/tools/anima_regression_oracle/__init__.py new file mode 100644 index 0000000..ad3256b --- /dev/null +++ b/tools/anima_regression_oracle/__init__.py @@ -0,0 +1,9 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Expose the blocking Anima regression-oracle contract.""" + +from .manifest import AnimaRegressionManifest, default_manifest + +__all__ = ["AnimaRegressionManifest", "default_manifest"] diff --git a/tools/anima_regression_oracle/artifact_validation.py b/tools/anima_regression_oracle/artifact_validation.py new file mode 100644 index 0000000..942a2f0 --- /dev/null +++ b/tools/anima_regression_oracle/artifact_validation.py @@ -0,0 +1,188 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Validate immutable accepted Anima artifacts without regenerating them.""" + +from __future__ import annotations + +import hashlib +import json +from dataclasses import dataclass +from pathlib import Path + +from .manifest import AnimaRegressionManifest, CompletedJsonEvidence + + +@dataclass(frozen=True, slots=True) +class ArtifactObservation: + """Record one successfully validated accepted evidence identity.""" + + identity: str + path: str + sha256: str + + +class AcceptedArtifactValidator: + """Require exact accepted hashes and structural managed-run evidence.""" + + def validate( + self, manifest: AnimaRegressionManifest + ) -> tuple[ArtifactObservation, ...]: + """Validate every artifact in manifest order or fail with its identity.""" + + observations: list[ArtifactObservation] = [] + for image in manifest.images: + digest = _file_sha256(image.path) + if digest != image.sha256: + raise ValueError(f"Accepted image hash changed for {image.identity}.") + observations.append( + ArtifactObservation(image.identity, str(image.path), digest) + ) + for evidence in manifest.managed_results: + payload = _json_object(evidence.path) + details = tuple(_json_object(path) for path in evidence.detail_paths) + self._validate_completed(evidence, payload, details) + observations.append( + ArtifactObservation( + evidence.identity, str(evidence.path), _file_sha256(evidence.path) + ) + ) + self._validate_performance(manifest) + observations.append( + ArtifactObservation( + "accepted-performance-gate", + str(manifest.performance.path), + _file_sha256(manifest.performance.path), + ) + ) + return tuple(observations) + + @staticmethod + def _validate_completed( + evidence: CompletedJsonEvidence, + payload: dict[str, object], + details: tuple[dict[str, object], ...], + ) -> None: + """Require completion, cleanup, matrix shape, and pinned values.""" + + if payload.get("status") != "completed": + raise ValueError(f"Managed evidence is incomplete: {evidence.identity}.") + if not _cleanup_passed(payload): + raise ValueError(f"Managed cleanup failed: {evidence.identity}.") + if evidence.observation_count is not None: + observations = payload.get("observations") + if ( + not isinstance(observations, list) + or len(observations) != evidence.observation_count + ): + raise ValueError(f"Managed matrix changed: {evidence.identity}.") + values = tuple( + item + for document in (payload, *details) + for item in _walk_key_values(document) + ) + for key, required, minimum_count in evidence.required_integer_occurrences: + count = sum( + found_key == key and value == required for found_key, value in values + ) + if count < minimum_count: + raise ValueError( + f"Managed evidence lacks {minimum_count} occurrences of " + f"{key}={required}: {evidence.identity}." + ) + serialized = json.dumps((payload, *details), sort_keys=True) + for digest in evidence.required_image_hashes: + if digest not in serialized: + raise ValueError( + f"Managed evidence lacks image hash {digest}: {evidence.identity}." + ) + + @staticmethod + def _validate_performance(manifest: AnimaRegressionManifest) -> None: + """Require the exact accepted workload, limits, and passing profiles.""" + + evidence = manifest.performance + payload = _json_object(evidence.path) + if payload.get("passed") is not True: + raise ValueError("Accepted Anima performance result does not pass.") + environment = payload.get("environment") + if ( + not isinstance(environment, dict) + or environment.get("adapter_target_count") != evidence.adapter_target_count + ): + raise ValueError("Accepted Anima performance target surface changed.") + profiles = payload.get("profiles") + if not isinstance(profiles, list): + raise ValueError("Accepted Anima performance profiles are missing.") + observed: list[tuple[str, float, bool]] = [] + for profile in profiles: + if not isinstance(profile, dict): + raise TypeError("Anima performance profile must be an object.") + identity = profile.get("profile_id") + limit = profile.get("maximum_overhead_percent") + passed = profile.get("passed") + if ( + not isinstance(identity, str) + or not isinstance(limit, int | float) + or not isinstance(passed, bool) + ): + raise TypeError("Anima performance profile has invalid fields.") + observed.append((identity, float(limit), passed)) + expected = tuple( + (identity, limit, True) for identity, limit in evidence.profile_limits + ) + if tuple(observed) != expected: + raise ValueError("Accepted Anima performance limits or status changed.") + + +def _json_object(path: Path) -> dict[str, object]: + """Read one required UTF-8 JSON object.""" + + decoded: object = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(decoded, dict) or not all( + isinstance(key, str) for key in decoded + ): + raise TypeError(f"Accepted evidence must be a JSON object: {path}.") + return decoded + + +def _file_sha256(path: Path) -> str: + """Return the SHA-256 digest of one required file.""" + + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def _cleanup_passed(payload: dict[str, object]) -> bool: + """Accept the two existing explicit managed-cleanup schemas.""" + + if payload.get("cleanup_verified") is True: + return True + cleanup = payload.get("cleanup") + return ( + isinstance(cleanup, dict) + and bool(cleanup) + and all( + value is True or (key == "parent_running" and value is False) + for key, value in cleanup.items() + ) + ) + + +def _walk_key_values(value: object) -> tuple[tuple[str, object], ...]: + """Return every nested string-key value without interpreting its owner.""" + + found: list[tuple[str, object]] = [] + if isinstance(value, dict): + for key, child in value.items(): + if isinstance(key, str): + found.append((key, child)) + found.extend(_walk_key_values(child)) + elif isinstance(value, list): + for child in value: + found.extend(_walk_key_values(child)) + return tuple(found) diff --git a/tools/anima_regression_oracle/execution.py b/tools/anima_regression_oracle/execution.py new file mode 100644 index 0000000..a1b203e --- /dev/null +++ b/tools/anima_regression_oracle/execution.py @@ -0,0 +1,70 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Execute ordered Anima oracle commands without owning their policy.""" + +from __future__ import annotations + +import subprocess +import time +from dataclasses import dataclass +from pathlib import Path + +from .manifest import OracleCommand + + +@dataclass(frozen=True, slots=True) +class CommandObservation: + """Capture one completed oracle command.""" + + identity: str + arguments: tuple[str, ...] + return_code: int + duration_seconds: float + stdout_file: str + stderr_file: str + + +class OracleCommandExecutor: + """Run exact argument-list commands from the repository root.""" + + def execute( + self, + commands: tuple[OracleCommand, ...], + *, + repo_root: Path, + output_root: Path, + ) -> tuple[CommandObservation, ...]: + """Run commands in order and stop after preserving the first failure.""" + + observations: list[CommandObservation] = [] + for index, command in enumerate(commands): + stdout_path = output_root / f"{index:02d}-{command.identity}.stdout.log" + stderr_path = output_root / f"{index:02d}-{command.identity}.stderr.log" + started = time.perf_counter() + with ( + stdout_path.open("w", encoding="utf-8") as stdout, + stderr_path.open("w", encoding="utf-8") as stderr, + ): + completed = subprocess.run( + command.arguments, + cwd=repo_root, + check=False, + shell=False, + stdout=stdout, + stderr=stderr, + text=True, + ) + observation = CommandObservation( + command.identity, + command.arguments, + completed.returncode, + time.perf_counter() - started, + stdout_path.name, + stderr_path.name, + ) + observations.append(observation) + if completed.returncode != 0: + break + return tuple(observations) diff --git a/tools/anima_regression_oracle/manifest.py b/tools/anima_regression_oracle/manifest.py new file mode 100644 index 0000000..5612277 --- /dev/null +++ b/tools/anima_regression_oracle/manifest.py @@ -0,0 +1,281 @@ +# 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 accepted evidence and executable Anima regression gates.""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + + +@dataclass(frozen=True, slots=True) +class AcceptedImage: + """Identify one immutable accepted visual artifact.""" + + identity: str + path: Path + sha256: str + + +@dataclass(frozen=True, slots=True) +class CompletedJsonEvidence: + """Describe one completed managed result and its structural invariants.""" + + identity: str + path: Path + detail_paths: tuple[Path, ...] = () + observation_count: int | None = None + required_integer_occurrences: tuple[tuple[str, int, int], ...] = () + required_image_hashes: tuple[str, ...] = () + + +@dataclass(frozen=True, slots=True) +class PerformanceEvidence: + """Describe the accepted fixed-workload Anima performance result.""" + + path: Path + adapter_target_count: int + profile_limits: tuple[tuple[str, float], ...] + + +@dataclass(frozen=True, slots=True) +class OracleCommand: + """Name one ordered executable oracle gate.""" + + identity: str + arguments: tuple[str, ...] + + +@dataclass(frozen=True, slots=True) +class AnimaRegressionManifest: + """Hold the complete immutable U1 oracle definition.""" + + images: tuple[AcceptedImage, ...] + managed_results: tuple[CompletedJsonEvidence, ...] + performance: PerformanceEvidence + focused_command: OracleCommand + repository_commands: tuple[OracleCommand, ...] + managed_rerun_commands: tuple[OracleCommand, ...] + + +def default_manifest(repo_root: Path) -> AnimaRegressionManifest: + """Build the accepted Anima oracle for the authoritative Comfy workspace.""" + + python = str( + (repo_root / ".." / ".." / "venv" / "Scripts" / "python.exe").resolve() + ) + ruff = str((repo_root / ".." / ".." / "venv" / "Scripts" / "ruff.exe").resolve()) + mypy = str((repo_root / ".." / ".." / "venv" / "Scripts" / "mypy.exe").resolve()) + evidence_root = Path(r"\benchmark_artifacts\anima-regional-prompting-v1") + character_a_root = ( + evidence_root + / "user-prompt-character_a-ownership-proof" + / "20260812T192527Z-28deb131" + ) + adapter_a_root = ( + evidence_root / "user-prompt-adapter_a-ownership-proof" / "20260812T192754Z-89ebd7cb" + ) + style_character_root = ( + evidence_root / "global-style-character-proof" / "20260812T224908Z-892f5d8f" + ) + focused_tests = ( + "tests/test_anima_module_surface.py", + "tests/test_anima_lora_weight_categories.py", + "tests/test_anima_lora_linear.py", + "tests/test_anima_multi_lora_fidelity.py", + "tests/test_anima_multi_lora_composition.py", + "tests/test_anima_full_tile_lora_equivalence.py", + "tests/test_anima_global_lora_overlap.py", + "tests/test_anima_lora_block.py", + "tests/test_anima_lora_block_inactive_schedule.py", + "tests/test_anima_projection_batch.py", + "tests/test_anima_lora_combined_multiplier_cache.py", + "tests/test_anima_regional_lora_device_cache_lifecycle.py", + "tests/test_anima_single_adapter_mutations.py", + "tests/test_anima_regional_lora_target_ownership.py", + "tests/test_anima_composition_phase.py", + "tests/test_anima_cross_attention.py", + "tests/test_anima_self_attention_ownership.py", + "tests/test_anima_self_attention_coherence.py", + "tests/test_anima_regional_self_attention.py", + "tests/test_anima_branch_batch.py", + "tests/test_anima_query_masks.py", + "tests/test_anima_query_activity.py", + "tests/test_anima_regional_diagnostics.py", + "tests/test_anima_regional_permutation_diagnostics.py", + "tests/test_anima_attention_device_cache_lifecycle.py", + "tests/test_anima_attention_coupling_integration.py", + "tests/test_anima_tiled_attention_coupling_integration.py", + "tests/test_anima_contextual_attention_coupling_integration.py", + "tests/test_anima_regional_lora_admission_results.py", + "tests/test_anima_regional_lora_performance_manifest.py", + "tests/test_anima_regional_lora_performance_results.py", + "tests/test_anima_regional_lora_scaling_results.py", + ) + return AnimaRegressionManifest( + images=( + AcceptedImage( + "character_a-phased-composition", + character_a_root / "baseline.png", + "66b4851bc48f9fcafa9965209f34c1bb8add3ff675e73c1ff32e85ed89aea7a0", + ), + AcceptedImage( + "adapter_a-one-side-phased-composition", + adapter_a_root / "baseline.png", + "3783e8ec83c3088ddb00e4d4d60af44499a3fb73c0cbb3f39e10c38d68a03499", + ), + AcceptedImage( + "global-style-character-control", + style_character_root / "character_a-right-only.png", + "a5521ded7595322c1ccc39732fb4f30e9b6e29d5a454811ffb1e26d7916c9c17", + ), + AcceptedImage( + "global-adapter_a-050-regional-character_a", + style_character_root / "global-adapter_a-050-character_a-right.png", + "631d206299f3754891f968f01052678f25e22a1b7a3d6188f3081d237a2999a5", + ), + AcceptedImage( + "global-adapter_a-100-regional-character_a", + style_character_root / "global-adapter_a-100-character_a-right.png", + "beb9deb0c37547bcd0691d783be9ec37fa41e1360c0abf7203a814e550915d8f", + ), + ), + managed_results=( + CompletedJsonEvidence( + "character_a-phased-composition", + character_a_root / "run.json", + detail_paths=(character_a_root / "history.json",), + required_integer_occurrences=( + ("model_call_count", 30, 1), + ("target_count", 280, 1), + ), + required_image_hashes=( + "66b4851bc48f9fcafa9965209f34c1bb8add3ff675e73c1ff32e85ed89aea7a0", + ), + ), + CompletedJsonEvidence( + "adapter_a-one-side-phased-composition", + adapter_a_root / "run.json", + detail_paths=(adapter_a_root / "history.json",), + required_integer_occurrences=( + ("model_call_count", 30, 1), + ("target_count", 448, 1), + ), + required_image_hashes=( + "3783e8ec83c3088ddb00e4d4d60af44499a3fb73c0cbb3f39e10c38d68a03499", + ), + ), + CompletedJsonEvidence( + "global-style-regional-character", + style_character_root / "global-style-character-proof.json", + detail_paths=( + style_character_root / "character_a-right-only.history.json", + style_character_root / "global-adapter_a-050-character_a-right.history.json", + style_character_root / "global-adapter_a-100-character_a-right.history.json", + ), + observation_count=3, + required_integer_occurrences=( + ("model_call_count", 30, 3), + ("target_count", 280, 3), + ), + required_image_hashes=( + "a5521ded7595322c1ccc39732fb4f30e9b6e29d5a454811ffb1e26d7916c9c17", + "631d206299f3754891f968f01052678f25e22a1b7a3d6188f3081d237a2999a5", + "beb9deb0c37547bcd0691d783be9ec37fa41e1360c0abf7203a814e550915d8f", + ), + ), + CompletedJsonEvidence( + "full-managed-matrix", + evidence_root + / "p5.9" + / "20260810T205325Z-ab058756" + / "p5.9-result.json", + observation_count=8, + ), + CompletedJsonEvidence( + "tiled-managed-matrix", + evidence_root + / "p6.9" + / "20260810T224653Z-71544322" + / "p6.9-result.json", + observation_count=8, + ), + CompletedJsonEvidence( + "contextual-managed-matrix", + evidence_root + / "p7.8" + / "20260811T011406Z-cefad93b" + / "p7.8-result.json", + observation_count=6, + ), + CompletedJsonEvidence( + "contextual-sub-token-diagnostics", + evidence_root + / "p7.8" + / "20260811T014916Z-b49b6be3" + / "p7.8-result.json", + observation_count=1, + ), + ), + performance=PerformanceEvidence( + evidence_root / "p5.7" / "20260810T200643Z" / "result.json", + 448, + ( + ("attention-only", 0.0), + ("regional-lora-1", 15.0), + ("regional-lora-4", 35.0), + ), + ), + focused_command=OracleCommand( + "focused-anima-characterization", + (python, "-m", "pytest", "-n", "auto", "-q", *focused_tests), + ), + repository_commands=( + OracleCommand("ruff-format-check", (ruff, "format", "--check", ".")), + OracleCommand("ruff-check", (ruff, "check", ".")), + OracleCommand("strict-mypy", (mypy, "--strict", "simple_syrup", "tests")), + OracleCommand( + "full-python-suite", (python, "-m", "pytest", "-n", "auto", "-q") + ), + ), + managed_rerun_commands=( + OracleCommand( + "character_a-managed-proof", + (python, "-m", "tools.run_character_a_ownership_proof"), + ), + OracleCommand( + "adapter_a-managed-proof", + (python, "-m", "tools.run_adapter_a_ownership_proof"), + ), + OracleCommand( + "global-style-character-proof", + (python, "-m", "tools.run_global_style_character_proof"), + ), + OracleCommand( + "full-managed-matrix", + (python, "-m", "tools.run_anima_attention_coupling_integration"), + ), + OracleCommand( + "tiled-managed-matrix", + ( + python, + "-m", + "tools.run_anima_tiled_attention_coupling_integration", + ), + ), + OracleCommand( + "contextual-managed-matrix", + ( + python, + "-m", + "tools.run_anima_contextual_attention_coupling_integration", + ), + ), + OracleCommand( + "performance-gate", + (python, "-m", "tools.benchmark_anima_regional_lora"), + ), + ), + ) diff --git a/tools/anima_regression_oracle/results.py b/tools/anima_regression_oracle/results.py new file mode 100644 index 0000000..72e58cb --- /dev/null +++ b/tools/anima_regression_oracle/results.py @@ -0,0 +1,69 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Persist complete ordered Anima regression-oracle results atomically.""" + +from __future__ import annotations + +import json +from dataclasses import asdict +from datetime import UTC, datetime +from pathlib import Path + +from .artifact_validation import ArtifactObservation +from .execution import CommandObservation + + +class AnimaRegressionResultRecorder: + """Own the U1 oracle journal and terminal result publication.""" + + def __init__(self, root: Path) -> None: + """Create one previously absent result directory.""" + + self._root = root.resolve() + self._root.mkdir(parents=True, exist_ok=False) + self._started_at = _utc_now() + + @property + def root(self) -> Path: + """Return the exact owned result directory.""" + + return self._root + + def publish( + self, + *, + level: str, + artifacts: tuple[ArtifactObservation, ...], + commands: tuple[CommandObservation, ...], + expected_command_count: int, + ) -> Path: + """Publish success only when every requested command passed.""" + + passed = len(commands) == expected_command_count and all( + command.return_code == 0 for command in commands + ) + payload = { + "schema_version": 1, + "status": "completed" if passed else "failed", + "level": level, + "started_at_utc": self._started_at, + "completed_at_utc": _utc_now(), + "artifact_observations": [asdict(item) for item in artifacts], + "command_observations": [asdict(item) for item in commands], + "expected_command_count": expected_command_count, + } + destination = self._root / "anima-regression-oracle.json" + temporary = destination.with_suffix(".json.tmp") + temporary.write_text( + json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8" + ) + temporary.replace(destination) + return destination + + +def _utc_now() -> str: + """Return one stable UTC timestamp.""" + + return datetime.now(UTC).isoformat().replace("+00:00", "Z") diff --git a/tools/anima_tiled_attention_coupling_integration/__init__.py b/tools/anima_tiled_attention_coupling_integration/__init__.py index 27a8c16..cab754f 100644 --- a/tools/anima_tiled_attention_coupling_integration/__init__.py +++ b/tools/anima_tiled_attention_coupling_integration/__init__.py @@ -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 + """Managed tiled Anima Attention Coupling integration evidence owners.""" diff --git a/tools/anima_tiled_attention_coupling_integration/matrix.py b/tools/anima_tiled_attention_coupling_integration/matrix.py index 217b2b1..fd3bc7c 100644 --- a/tools/anima_tiled_attention_coupling_integration/matrix.py +++ b/tools/anima_tiled_attention_coupling_integration/matrix.py @@ -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 the immutable P6.9 tiled Attention Coupling workflow matrix.""" from __future__ import annotations diff --git a/tools/anima_tiled_attention_coupling_integration/results.py b/tools/anima_tiled_attention_coupling_integration/results.py index b3deb69..5f29b54 100644 --- a/tools/anima_tiled_attention_coupling_integration/results.py +++ b/tools/anima_tiled_attention_coupling_integration/results.py @@ -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 + """Validate and persist complete labeled P6.9 tiled-node evidence.""" from __future__ import annotations diff --git a/tools/anima_tiled_attention_coupling_integration/schema.py b/tools/anima_tiled_attention_coupling_integration/schema.py index 36497de..ba5c823 100644 --- a/tools/anima_tiled_attention_coupling_integration/schema.py +++ b/tools/anima_tiled_attention_coupling_integration/schema.py @@ -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 + """Validate the exact live P6.9 tiled public-node metadata contract.""" from __future__ import annotations diff --git a/tools/anima_tiled_attention_coupling_integration/workflow.py b/tools/anima_tiled_attention_coupling_integration/workflow.py index 9c86c71..8934594 100644 --- a/tools/anima_tiled_attention_coupling_integration/workflow.py +++ b/tools/anima_tiled_attention_coupling_integration/workflow.py @@ -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 P6.9 tiled API graphs through the shared Anima workflow owner.""" from __future__ import annotations diff --git a/tools/anima_workflow_graph.py b/tools/anima_workflow_graph.py index 53c3c2e..1e29061 100644 --- a/tools/anima_workflow_graph.py +++ b/tools/anima_workflow_graph.py @@ -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 + """Own deterministic Comfy API graph node construction for Anima workflows.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/__init__.py b/tools/attention_coupling_benchmark/__init__.py index 0725a9d..fedbb53 100644 --- a/tools/attention_coupling_benchmark/__init__.py +++ b/tools/attention_coupling_benchmark/__init__.py @@ -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 + """Own deterministic Attention Coupling benchmark support.""" diff --git a/tools/attention_coupling_benchmark/comfy_probe/__init__.py b/tools/attention_coupling_benchmark/comfy_probe/__init__.py index 2706914..75374ff 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/__init__.py +++ b/tools/attention_coupling_benchmark/comfy_probe/__init__.py @@ -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 + """Register focused benchmark-only Comfy v3 probe nodes.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile.py b/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile.py index 8fdba9b..dd536d7 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile.py +++ b/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile.py @@ -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 + """Derive fixed benchmark-only Anima regional LoRA model profiles.""" from __future__ import annotations @@ -53,6 +57,7 @@ from simple_syrup.runtime.regional_lora.anima_plan_admission import ( from simple_syrup.runtime.regional_lora.execution_cache import ( RegionalLoraExecutionCache, ) +from simple_syrup.runtime.regional_lora_host_payload import RegionalLoraHostPayload from simple_syrup.runtime.regional_lora_plan_adapter import ( RegionalLoraPlanAdaptation, ) @@ -109,7 +114,7 @@ class StaticAnimaRegionalProfileBuilder: plan = self._plan(paths, adapters, model=model) adaptation = RegionalLoraPlanAdaptation( plan, - weights, + tuple(RegionalLoraHostPayload.unresolved(value) for value in weights), ) admitted = ANIMA_REGIONAL_LORA_PLAN_ADMISSION_SERVICE.admit(adaptation) raw = build_raw_regional_attention_plan( diff --git a/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_node.py b/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_node.py index 2d34353..1a0fa7d 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_node.py +++ b/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_node.py @@ -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 + """Expose the benchmark-only static Anima regional profile node.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_spec.py b/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_spec.py index 886d2d1..6e8f78b 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_spec.py +++ b/tools/attention_coupling_benchmark/comfy_probe/anima_regional_profile_spec.py @@ -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 + """Decode benchmark-only regional Anima adapter assignments.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/clip_schedule_snapshot.py b/tools/attention_coupling_benchmark/comfy_probe/clip_schedule_snapshot.py index b1f7ca8..90172ef 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/clip_schedule_snapshot.py +++ b/tools/attention_coupling_benchmark/comfy_probe/clip_schedule_snapshot.py @@ -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 + """Expose read-only CLIP hook-schedule state as managed benchmark evidence.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/conditioning_batch_snapshot.py b/tools/attention_coupling_benchmark/comfy_probe/conditioning_batch_snapshot.py index bfa5f12..08acf40 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/conditioning_batch_snapshot.py +++ b/tools/attention_coupling_benchmark/comfy_probe/conditioning_batch_snapshot.py @@ -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 + """Expose ordered conditioning-batch tensor identities as managed evidence.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/lora_execution_probe.py b/tools/attention_coupling_benchmark/comfy_probe/lora_execution_probe.py index 31079ec..ad948a1 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/lora_execution_probe.py +++ b/tools/attention_coupling_benchmark/comfy_probe/lora_execution_probe.py @@ -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 + """Expose benchmark-only LoRA execution evidence through focused v3 nodes.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/lora_patch_snapshot.py b/tools/attention_coupling_benchmark/comfy_probe/lora_patch_snapshot.py index b97dd17..26ba694 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/lora_patch_snapshot.py +++ b/tools/attention_coupling_benchmark/comfy_probe/lora_patch_snapshot.py @@ -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 + """Normalize dynamic Comfy LoRA patch state into stable JSON evidence.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/model_modifier_snapshot.py b/tools/attention_coupling_benchmark/comfy_probe/model_modifier_snapshot.py index 9863c2f..97efcb5 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/model_modifier_snapshot.py +++ b/tools/attention_coupling_benchmark/comfy_probe/model_modifier_snapshot.py @@ -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 + """Publish read-only benchmark evidence for installed MODEL modifiers.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/prompt_control_expansion.py b/tools/attention_coupling_benchmark/comfy_probe/prompt_control_expansion.py index 7ea7387..eb2a009 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/prompt_control_expansion.py +++ b/tools/attention_coupling_benchmark/comfy_probe/prompt_control_expansion.py @@ -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 + """Capture canonical graphs returned by Prompt Control public lazy nodes.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/prompt_control_runtime.py b/tools/attention_coupling_benchmark/comfy_probe/prompt_control_runtime.py index f763cbc..586c0af 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/prompt_control_runtime.py +++ b/tools/attention_coupling_benchmark/comfy_probe/prompt_control_runtime.py @@ -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 + """Observe Prompt Control UUIDs and WeightHook transitions during sampling.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/prompt_control_snapshot.py b/tools/attention_coupling_benchmark/comfy_probe/prompt_control_snapshot.py index 00a0183..95b5d3a 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/prompt_control_snapshot.py +++ b/tools/attention_coupling_benchmark/comfy_probe/prompt_control_snapshot.py @@ -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 + """Expose Prompt Control conditioning and HookGroup metadata as JSON evidence.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/regional_diagnostics_capture.py b/tools/attention_coupling_benchmark/comfy_probe/regional_diagnostics_capture.py index 64b394c..b76c735 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/regional_diagnostics_capture.py +++ b/tools/attention_coupling_benchmark/comfy_probe/regional_diagnostics_capture.py @@ -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 + """Capture structured regional diagnostics for managed benchmark workflows.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/regional_hook_fixture.py b/tools/attention_coupling_benchmark/comfy_probe/regional_hook_fixture.py index 9824b75..f3d29ac 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/regional_hook_fixture.py +++ b/tools/attention_coupling_benchmark/comfy_probe/regional_hook_fixture.py @@ -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 exact rejection-only Comfy hook fixtures for managed evidence.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/sampling_metrics.py b/tools/attention_coupling_benchmark/comfy_probe/sampling_metrics.py index a97ec04..ff09695 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/sampling_metrics.py +++ b/tools/attention_coupling_benchmark/comfy_probe/sampling_metrics.py @@ -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 + """Measure model-call count, sampling runtime, and peak CUDA memory.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/comfy_probe/tensor_snapshot.py b/tools/attention_coupling_benchmark/comfy_probe/tensor_snapshot.py index 03cdfcc..70aa9c6 100644 --- a/tools/attention_coupling_benchmark/comfy_probe/tensor_snapshot.py +++ b/tools/attention_coupling_benchmark/comfy_probe/tensor_snapshot.py @@ -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 + """Record deterministic tensor identities without retaining tensor contents.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/manifest.py b/tools/attention_coupling_benchmark/manifest.py index 2f7df1a..86def6e 100644 --- a/tools/attention_coupling_benchmark/manifest.py +++ b/tools/attention_coupling_benchmark/manifest.py @@ -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 + """Load and semantically validate the Attention Coupling benchmark manifest.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/manifest_decode.py b/tools/attention_coupling_benchmark/manifest_decode.py index ed26155..1ea5bd4 100644 --- a/tools/attention_coupling_benchmark/manifest_decode.py +++ b/tools/attention_coupling_benchmark/manifest_decode.py @@ -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 + """Decode benchmark manifest JSON into strongly typed records.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/manifest_types.py b/tools/attention_coupling_benchmark/manifest_types.py index 46d7d97..d706315 100644 --- a/tools/attention_coupling_benchmark/manifest_types.py +++ b/tools/attention_coupling_benchmark/manifest_types.py @@ -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 immutable Attention Coupling benchmark manifest records.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/mask_artifacts.py b/tools/attention_coupling_benchmark/mask_artifacts.py index bc2ec50..6d55295 100644 --- a/tools/attention_coupling_benchmark/mask_artifacts.py +++ b/tools/attention_coupling_benchmark/mask_artifacts.py @@ -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 + """Materialize deterministic rectangular benchmark masks for ComfyUI.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/results.py b/tools/attention_coupling_benchmark/results.py index 1e0d5c8..639b225 100644 --- a/tools/attention_coupling_benchmark/results.py +++ b/tools/attention_coupling_benchmark/results.py @@ -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 + """Parse and persist complete Attention Coupling benchmark evidence.""" from __future__ import annotations diff --git a/tools/attention_coupling_benchmark/workflow.py b/tools/attention_coupling_benchmark/workflow.py index 4feefe3..be08d8d 100644 --- a/tools/attention_coupling_benchmark/workflow.py +++ b/tools/attention_coupling_benchmark/workflow.py @@ -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 API-format Comfy workflows for fixed regional benchmark runs.""" from __future__ import annotations diff --git a/tools/benchmark_anima_regional_lora.py b/tools/benchmark_anima_regional_lora.py index 15df01e..33de94d 100644 --- a/tools/benchmark_anima_regional_lora.py +++ b/tools/benchmark_anima_regional_lora.py @@ -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 + """Run the pinned full-context Anima regional LoRA performance gate.""" from __future__ import annotations diff --git a/tools/benchmark_anima_regional_lora_scaling.py b/tools/benchmark_anima_regional_lora_scaling.py index cf424b5..fc8c24d 100644 --- a/tools/benchmark_anima_regional_lora_scaling.py +++ b/tools/benchmark_anima_regional_lora_scaling.py @@ -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 + """Run the complete focused P10.1 Anima regional LoRA scaling matrix.""" from __future__ import annotations diff --git a/tools/benchmark_attention_coupling.py b/tools/benchmark_attention_coupling.py index bad5eef..d3b8b95 100644 --- a/tools/benchmark_attention_coupling.py +++ b/tools/benchmark_attention_coupling.py @@ -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 the deterministic Attention Coupling benchmark against ComfyUI.""" from __future__ import annotations diff --git a/tools/capture_anima_regional_outputs.py b/tools/capture_anima_regional_outputs.py index 8ffc079..319acfb 100644 --- a/tools/capture_anima_regional_outputs.py +++ b/tools/capture_anima_regional_outputs.py @@ -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 + """Capture labeled regional Anima LoRA images through an isolated ComfyUI.""" from __future__ import annotations diff --git a/tools/characterize_anima_lora.py b/tools/characterize_anima_lora.py index bc9f074..6852af2 100644 --- a/tools/characterize_anima_lora.py +++ b/tools/characterize_anima_lora.py @@ -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 + """Capture the fixed global ADAPTER_A LoRA matrix through an existing ComfyUI.""" from __future__ import annotations diff --git a/tools/characterize_comfy_regional_adapter_resolution.py b/tools/characterize_comfy_regional_adapter_resolution.py new file mode 100644 index 0000000..527a47c --- /dev/null +++ b/tools/characterize_comfy_regional_adapter_resolution.py @@ -0,0 +1,178 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Characterize real installed Comfy regional adapter resolution fixtures.""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +import tempfile +from collections import Counter +from collections.abc import Sequence +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +COMFY_ROOT = REPO_ROOT.parents[1] +for import_root in (REPO_ROOT, COMFY_ROOT): + if str(import_root) not in sys.path: + sys.path.insert(0, str(import_root)) + +import comfy.hooks # noqa: E402 +import comfy.utils # noqa: E402 + +from simple_syrup.runtime.regional_lora.comfy_adapter_resolver import ( # noqa: E402 + COMFY_REGIONAL_ADAPTER_RESOLVER, +) +from simple_syrup.runtime.regional_lora_host_payload import ( # noqa: E402 + RegionalLoraHostPayload, +) +from simple_syrup.runtime.regional_lora_plan_adapter import ( # noqa: E402 + RegionalLoraPlanAdaptation, +) +from tools.regional_lora_real_fixtures import ( # noqa: E402 + REGIONAL_LORA_REAL_FIXTURES, + RegionalLoraRealFixture, +) + + +def main(argv: Sequence[str] | None = None) -> int: + """Resolve selected real fixtures and print their immutable observations.""" + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "families", + nargs="*", + choices=tuple(REGIONAL_LORA_REAL_FIXTURES), + default=(), + ) + parser.add_argument( + "--output", + type=Path, + help="Atomically write the complete JSON evidence to this path.", + ) + arguments = parser.parse_args(argv) + families = tuple(arguments.families) or tuple(REGIONAL_LORA_REAL_FIXTURES) + serialized = json.dumps( + [_characterize(REGIONAL_LORA_REAL_FIXTURES[family]) for family in families], + indent=2, + sort_keys=True, + ) + print(serialized) + if arguments.output is not None: + _write_atomically(arguments.output, serialized) + return 0 + + +def _characterize(fixture: RegionalLoraRealFixture) -> dict[str, object]: + """Resolve one real fixture and verify source model and hook immutability.""" + + fixture.require_files() + patcher = fixture.load_model() + raw_weights = comfy.utils.load_torch_file(str(fixture.lora_path), safe_load=True) + hook = comfy.hooks.create_hook_lora(raw_weights, 1.0, 0.0).get_type( + comfy.hooks.EnumHookType.Weight + )[0] + payload = RegionalLoraHostPayload.from_hook(hook) + before_model = _model_fingerprint(patcher.model) + before_hook = _hook_fingerprint(hook) + adaptation = RegionalLoraPlanAdaptation(fixture.plan(), (payload,)) + + resolution = COMFY_REGIONAL_ADAPTER_RESOLVER.resolve( + adaptation, + model=patcher, + ) + result = resolution.adapters[0] + scopes = Counter(entry.scope.value for entry in result.source_entries) + observation = { + "family": fixture.family, + "model_path": str(fixture.model_path), + "lora_path": str(fixture.lora_path), + "model_state_entries": len(before_model), + "raw_source_entries": len(raw_weights), + "resolved_model_targets": len(result.model_targets), + "ordinary_additive_targets": sum( + target.ordinary_additive_lora for target in result.model_targets + ), + "source_scopes": dict(sorted(scopes.items())), + "issues": [ + {"code": issue.code.value, "message": issue.message} + for issue in resolution.issues + ], + "model_unchanged": before_model == _model_fingerprint(patcher.model), + "hook_unchanged": before_hook == _hook_fingerprint(hook), + "payload_identity_preserved": result.payload.raw_weights is raw_weights, + "sample_target_paths": [ + target.path.parameter_key for target in result.model_targets[:3] + ], + } + if resolution.issues: + raise RuntimeError( + f"{fixture.family} resolution reported issues: {resolution.issues}" + ) + if not all( + bool(observation[key]) + for key in ("model_unchanged", "hook_unchanged", "payload_identity_preserved") + ): + raise RuntimeError(f"{fixture.family} resolution mutated source state.") + return observation + + +def _model_fingerprint(model: object) -> tuple[tuple[object, ...], ...]: + """Fingerprint model tensor identities and versions without moving tensors.""" + + state_dict = model.state_dict() # type: ignore[attr-defined] + return tuple( + ( + key, + value.data_ptr(), + value._version, + tuple(value.shape), + str(value.dtype), + str(value.device), + ) + for key, value in state_dict.items() + ) + + +def _hook_fingerprint(hook: comfy.hooks.WeightHook) -> tuple[object, ...]: + """Fingerprint the host hook fields resolution must leave unchanged.""" + + return ( + hook.need_weight_init, + id(hook.weights), + id(hook.weights_clip), + hook._strength_model, + hook._strength_clip, + hook.hook_ref, + ) + + +def _write_atomically(destination: Path, content: str) -> None: + """Publish complete characterization evidence without partial results.""" + + destination.parent.mkdir(parents=True, exist_ok=True) + descriptor, temporary_name = tempfile.mkstemp( + dir=destination.parent, + prefix=f".{destination.name}.", + suffix=".tmp", + text=True, + ) + temporary = Path(temporary_name) + try: + with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as stream: + stream.write(content) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + temporary.replace(destination) + except BaseException: + temporary.unlink(missing_ok=True) + raise + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/characterize_prompt_control.py b/tools/characterize_prompt_control.py index 471b202..13d282e 100644 --- a/tools/characterize_prompt_control.py +++ b/tools/characterize_prompt_control.py @@ -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 + """Capture pinned Prompt Control schedules through an existing ComfyUI.""" from __future__ import annotations diff --git a/tools/characterize_regional_lora_target_binding.py b/tools/characterize_regional_lora_target_binding.py new file mode 100644 index 0000000..d04cf00 --- /dev/null +++ b/tools/characterize_regional_lora_target_binding.py @@ -0,0 +1,214 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Characterize real installed regional LoRA target binding without mutation.""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +import tempfile +from collections import Counter +from collections.abc import Sequence +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +COMFY_ROOT = REPO_ROOT.parents[1] +for import_root in (REPO_ROOT, COMFY_ROOT): + if str(import_root) not in sys.path: + sys.path.insert(0, str(import_root)) + +import comfy.hooks # noqa: E402 +import comfy.utils # noqa: E402 + +from simple_syrup.runtime.regional_lora.comfy_adapter_resolver import ( # noqa: E402 + COMFY_REGIONAL_ADAPTER_RESOLVER, +) +from simple_syrup.runtime.regional_lora.resolved_operation_translator import ( # noqa: E402 + COMFY_RESOLVED_OPERATION_TRANSLATOR, +) +from simple_syrup.runtime.regional_lora.target_binder import ( # noqa: E402 + REGIONAL_LORA_TARGET_BINDER, +) +from simple_syrup.runtime.regional_lora_host_payload import ( # noqa: E402 + RegionalLoraHostPayload, +) +from simple_syrup.runtime.regional_lora_plan_adapter import ( # noqa: E402 + RegionalLoraPlanAdaptation, +) +from tools.regional_lora_real_fixtures import ( # noqa: E402 + REGIONAL_LORA_REAL_FIXTURES, + RegionalLoraRealFixture, +) + + +def main(argv: Sequence[str] | None = None) -> int: + """Bind selected fixtures and atomically publish complete JSON evidence.""" + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "families", + nargs="*", + choices=tuple(REGIONAL_LORA_REAL_FIXTURES), + default=(), + ) + parser.add_argument("--output", type=Path) + arguments = parser.parse_args(argv) + families = tuple(arguments.families) or tuple(REGIONAL_LORA_REAL_FIXTURES) + serialized = json.dumps( + [_characterize(REGIONAL_LORA_REAL_FIXTURES[family]) for family in families], + indent=2, + sort_keys=True, + ) + print(serialized) + if arguments.output is not None: + _write_atomically(arguments.output, serialized) + return 0 + + +def _characterize(fixture: RegionalLoraRealFixture) -> dict[str, object]: + """Resolve, translate, and bind one fixture while proving immutability.""" + + patcher = fixture.load_model() + raw_weights = comfy.utils.load_torch_file(str(fixture.lora_path), safe_load=True) + hook = comfy.hooks.create_hook_lora(raw_weights, 1.0, 0.0).get_type( + comfy.hooks.EnumHookType.Weight + )[0] + payload = RegionalLoraHostPayload.from_hook(hook) + before_model = _model_fingerprint(patcher.model) + before_patcher = _patcher_fingerprint(patcher) + before_hook = _hook_fingerprint(hook) + resolution = COMFY_REGIONAL_ADAPTER_RESOLVER.resolve( + RegionalLoraPlanAdaptation(fixture.plan(), (payload,)), + model=patcher, + ) + operations = COMFY_RESOLVED_OPERATION_TRANSLATOR.translate(resolution) + binding = REGIONAL_LORA_TARGET_BINDER.bind( + source=patcher, + candidate=patcher, + resolution=resolution, + operations=operations, + ) + module_classes = Counter(entry.module_class.value for entry in binding.entries) + capabilities = Counter(entry.spatial_capability.value for entry in binding.entries) + observation = { + "family": fixture.family, + "model_path": str(fixture.model_path), + "lora_path": str(fixture.lora_path), + "normalized_targets": sum( + len(adapter.model_targets) for adapter in resolution.adapters + ), + "translated_operations": len(operations.entries), + "bound_targets": sum(entry.bound for entry in binding.entries), + "module_classes": dict(sorted(module_classes.items())), + "spatial_capabilities": dict(sorted(capabilities.items())), + "issues": [ + { + "code": issue.code.value, + "message": issue.message, + "parameter_path": issue.parameter_path, + "target_index": issue.target_index, + } + for issue in binding.issues + ], + "model_unchanged": before_model == _model_fingerprint(patcher.model), + "patcher_unchanged": before_patcher == _patcher_fingerprint(patcher), + "hook_unchanged": before_hook == _hook_fingerprint(hook), + "identity_preserved": all( + entry.normalized_target + is resolution.adapters[ + entry.descriptor.adapter.composition_index + ].model_targets[entry.descriptor.target_index] + for entry in binding.entries + ), + "sample_parameter_paths": [ + entry.descriptor.target.parameter_path for entry in binding.entries[:3] + ], + } + required_truths = ( + "model_unchanged", + "patcher_unchanged", + "hook_unchanged", + "identity_preserved", + ) + if binding.issues: + raise RuntimeError( + f"{fixture.family} binding reported issues: {binding.issues}" + ) + if len(binding.entries) != len(operations.entries): + raise RuntimeError(f"{fixture.family} binding omitted translated operations.") + if not all(bool(observation[key]) for key in required_truths): + raise RuntimeError(f"{fixture.family} binding changed or copied source state.") + return observation + + +def _model_fingerprint(model: object) -> tuple[tuple[object, ...], ...]: + """Fingerprint tensor identities, versions, shapes, dtypes, and devices.""" + + state_dict = model.state_dict() # type: ignore[attr-defined] + return tuple( + ( + key, + value.data_ptr(), + value._version, + tuple(value.shape), + str(value.dtype), + str(value.device), + ) + for key, value in state_dict.items() + ) + + +def _patcher_fingerprint(patcher: object) -> tuple[object, ...]: + """Fingerprint mutation surfaces binding must leave untouched.""" + + return ( + id(patcher), + id(patcher.model), # type: ignore[attr-defined] + tuple(patcher.object_patches.items()), # type: ignore[attr-defined] + tuple(patcher.object_patches_backup.items()), # type: ignore[attr-defined] + tuple(patcher.patches.items()), # type: ignore[attr-defined] + ) + + +def _hook_fingerprint(hook: comfy.hooks.WeightHook) -> tuple[object, ...]: + """Fingerprint the host hook fields binding must leave unchanged.""" + + return ( + hook.need_weight_init, + id(hook.weights), + id(hook.weights_clip), + hook._strength_model, + hook._strength_clip, + hook.hook_ref, + ) + + +def _write_atomically(destination: Path, content: str) -> None: + """Publish complete evidence without leaving partial output.""" + + destination.parent.mkdir(parents=True, exist_ok=True) + descriptor, temporary_name = tempfile.mkstemp( + dir=destination.parent, + prefix=f".{destination.name}.", + suffix=".tmp", + text=True, + ) + temporary = Path(temporary_name) + try: + with os.fdopen(descriptor, "w", encoding="utf-8", newline="\n") as stream: + stream.write(content) + stream.write("\n") + stream.flush() + os.fsync(stream.fileno()) + temporary.replace(destination) + except BaseException: + temporary.unlink(missing_ok=True) + raise + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/comfy_api.py b/tools/comfy_api.py index 1173d98..9620ae1 100644 --- a/tools/comfy_api.py +++ b/tools/comfy_api.py @@ -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 + """Provide bounded loopback-only access to an existing ComfyUI process.""" from __future__ import annotations diff --git a/tools/comfy_integration/__init__.py b/tools/comfy_integration/__init__.py index 27bc38c..d2e2197 100644 --- a/tools/comfy_integration/__init__.py +++ b/tools/comfy_integration/__init__.py @@ -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 + """Run focused real-Comfy integration workflows with owned lifecycle.""" diff --git a/tools/comfy_integration/artifacts.py b/tools/comfy_integration/artifacts.py index 3fe1191..c505ae5 100644 --- a/tools/comfy_integration/artifacts.py +++ b/tools/comfy_integration/artifacts.py @@ -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 + """Own durable artifacts for one managed Comfy integration execution.""" from __future__ import annotations diff --git a/tools/comfy_integration/baseline_workflow.py b/tools/comfy_integration/baseline_workflow.py index a8123cc..c5c3d73 100644 --- a/tools/comfy_integration/baseline_workflow.py +++ b/tools/comfy_integration/baseline_workflow.py @@ -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 the smallest real incoming Anima baseline workflow.""" from __future__ import annotations diff --git a/tools/comfy_integration/history_output.py b/tools/comfy_integration/history_output.py index adb419e..6d7be71 100644 --- a/tools/comfy_integration/history_output.py +++ b/tools/comfy_integration/history_output.py @@ -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 + """Validate completed Comfy history and extract one saved image reference.""" from __future__ import annotations diff --git a/tools/comfy_integration/loopback_port.py b/tools/comfy_integration/loopback_port.py index db2efc9..94f603a 100644 --- a/tools/comfy_integration/loopback_port.py +++ b/tools/comfy_integration/loopback_port.py @@ -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 and validate unused non-default loopback TCP ports.""" from __future__ import annotations diff --git a/tools/comfy_integration/managed_server.py b/tools/comfy_integration/managed_server.py index fe83b56..8729a4a 100644 --- a/tools/comfy_integration/managed_server.py +++ b/tools/comfy_integration/managed_server.py @@ -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 + """Coordinate one context-managed Comfy server from focused lifecycle owners.""" from __future__ import annotations diff --git a/tools/comfy_integration/readiness.py b/tools/comfy_integration/readiness.py index 5b341ca..78901b3 100644 --- a/tools/comfy_integration/readiness.py +++ b/tools/comfy_integration/readiness.py @@ -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 + """Own bounded readiness polling for a live managed Comfy process.""" from __future__ import annotations diff --git a/tools/comfy_integration/server_process.py b/tools/comfy_integration/server_process.py index 38b9b43..9e74286 100644 --- a/tools/comfy_integration/server_process.py +++ b/tools/comfy_integration/server_process.py @@ -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 + """Own one exact Windows ComfyUI process tree and its redirected logs.""" from __future__ import annotations diff --git a/tools/decoded_image_comparison.py b/tools/decoded_image_comparison.py index 5aef62a..57cd5ad 100644 --- a/tools/decoded_image_comparison.py +++ b/tools/decoded_image_comparison.py @@ -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 + """Compare decoded RGB image pixels independently of container metadata.""" from __future__ import annotations diff --git a/tools/global_lora_visual_proof/__init__.py b/tools/global_lora_visual_proof/__init__.py index ae74790..b9c2575 100644 --- a/tools/global_lora_visual_proof/__init__.py +++ b/tools/global_lora_visual_proof/__init__.py @@ -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 fixed-seed global and regional LoRA visual proof owners.""" diff --git a/tools/global_lora_visual_proof/matrix.py b/tools/global_lora_visual_proof/matrix.py index 9a0ea41..991ba3b 100644 --- a/tools/global_lora_visual_proof/matrix.py +++ b/tools/global_lora_visual_proof/matrix.py @@ -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 the fixed global/regional ADAPTER_A visual comparison matrix.""" from __future__ import annotations diff --git a/tools/global_lora_visual_proof/results.py b/tools/global_lora_visual_proof/results.py index 917c05f..9cc1cfb 100644 --- a/tools/global_lora_visual_proof/results.py +++ b/tools/global_lora_visual_proof/results.py @@ -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 + """Validate and label global/regional ADAPTER_A visual proof artifacts.""" from __future__ import annotations diff --git a/tools/global_regional_lora_integration/__init__.py b/tools/global_regional_lora_integration/__init__.py index 4ea12e4..fce7e3f 100644 --- a/tools/global_regional_lora_integration/__init__.py +++ b/tools/global_regional_lora_integration/__init__.py @@ -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 + """Own the P9.3 managed global/regional Anima LoRA evidence.""" diff --git a/tools/global_regional_lora_integration/image_validation.py b/tools/global_regional_lora_integration/image_validation.py index b183bda..2306213 100644 --- a/tools/global_regional_lora_integration/image_validation.py +++ b/tools/global_regional_lora_integration/image_validation.py @@ -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 + """Validate exact P9.3 same-server transition image relationships.""" from __future__ import annotations diff --git a/tools/global_regional_lora_integration/matrix.py b/tools/global_regional_lora_integration/matrix.py index 1c64bee..8233191 100644 --- a/tools/global_regional_lora_integration/matrix.py +++ b/tools/global_regional_lora_integration/matrix.py @@ -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 the closed P9.3 global/regional Anima LoRA matrix.""" from __future__ import annotations diff --git a/tools/global_regional_lora_integration/results.py b/tools/global_regional_lora_integration/results.py index 966a1e7..a4672f5 100644 --- a/tools/global_regional_lora_integration/results.py +++ b/tools/global_regional_lora_integration/results.py @@ -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 + """Validate and persist P9.3 managed global/regional LoRA evidence.""" from __future__ import annotations diff --git a/tools/global_style_character_proof/__init__.py b/tools/global_style_character_proof/__init__.py index 831aa46..0329e02 100644 --- a/tools/global_style_character_proof/__init__.py +++ b/tools/global_style_character_proof/__init__.py @@ -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 global-style and regional-character proof owners.""" diff --git a/tools/global_style_character_proof/matrix.py b/tools/global_style_character_proof/matrix.py index bc21c52..c402a3f 100644 --- a/tools/global_style_character_proof/matrix.py +++ b/tools/global_style_character_proof/matrix.py @@ -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 fixed CHARACTER_A-region and global-ADAPTER_A comparison cases.""" from __future__ import annotations diff --git a/tools/global_style_character_proof/results.py b/tools/global_style_character_proof/results.py index 52ef8e0..0126c43 100644 --- a/tools/global_style_character_proof/results.py +++ b/tools/global_style_character_proof/results.py @@ -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 + """Persist and label global-style plus regional-character visual evidence.""" from __future__ import annotations diff --git a/tools/measure_anima_regional_lora_vram.py b/tools/measure_anima_regional_lora_vram.py index 27ce852..2e47742 100644 --- a/tools/measure_anima_regional_lora_vram.py +++ b/tools/measure_anima_regional_lora_vram.py @@ -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 + """Measure isolated regional-LoRA profile VRAM through production execution.""" from __future__ import annotations diff --git a/tools/profile_anima_regional_lora.py b/tools/profile_anima_regional_lora.py index d6b14e5..cba0580 100644 --- a/tools/profile_anima_regional_lora.py +++ b/tools/profile_anima_regional_lora.py @@ -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 + """Profile the exact pinned Anima regional-LoRA execution profiles.""" from __future__ import annotations diff --git a/tools/profile_anima_regional_lora_scaling.py b/tools/profile_anima_regional_lora_scaling.py index cbe7026..83ca388 100644 --- a/tools/profile_anima_regional_lora_scaling.py +++ b/tools/profile_anima_regional_lora_scaling.py @@ -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 + """Capture selected P10.1 scaling operator traces and summaries.""" from __future__ import annotations diff --git a/tools/prompt_control_attention_coupling_integration/__init__.py b/tools/prompt_control_attention_coupling_integration/__init__.py index 2df135f..da305b6 100644 --- a/tools/prompt_control_attention_coupling_integration/__init__.py +++ b/tools/prompt_control_attention_coupling_integration/__init__.py @@ -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 + """Own the P9.1 managed Prompt Control Attention Coupling proof.""" diff --git a/tools/prompt_control_attention_coupling_integration/baseline.py b/tools/prompt_control_attention_coupling_integration/baseline.py index 6cf6998..cc9b914 100644 --- a/tools/prompt_control_attention_coupling_integration/baseline.py +++ b/tools/prompt_control_attention_coupling_integration/baseline.py @@ -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 + """Load and revalidate the authoritative P0.8 Prompt Control baseline.""" from __future__ import annotations diff --git a/tools/prompt_control_attention_coupling_integration/conditioning.py b/tools/prompt_control_attention_coupling_integration/conditioning.py index f0e3c25..9ae10b1 100644 --- a/tools/prompt_control_attention_coupling_integration/conditioning.py +++ b/tools/prompt_control_attention_coupling_integration/conditioning.py @@ -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 P9.1 regional Prompt Control conditioning graphs.""" from __future__ import annotations diff --git a/tools/prompt_control_attention_coupling_integration/history.py b/tools/prompt_control_attention_coupling_integration/history.py index 76b4cf8..37e2701 100644 --- a/tools/prompt_control_attention_coupling_integration/history.py +++ b/tools/prompt_control_attention_coupling_integration/history.py @@ -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 + """Decode exact P9.1 evidence records from terminal Comfy history.""" from __future__ import annotations diff --git a/tools/prompt_control_attention_coupling_integration/matrix.py b/tools/prompt_control_attention_coupling_integration/matrix.py index 9018c36..65a8ba0 100644 --- a/tools/prompt_control_attention_coupling_integration/matrix.py +++ b/tools/prompt_control_attention_coupling_integration/matrix.py @@ -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 + """Bind immutable P0.8 cases to fixed P9.1 sampling fixtures.""" from __future__ import annotations diff --git a/tools/prompt_control_attention_coupling_integration/results.py b/tools/prompt_control_attention_coupling_integration/results.py index 125e585..37eb152 100644 --- a/tools/prompt_control_attention_coupling_integration/results.py +++ b/tools/prompt_control_attention_coupling_integration/results.py @@ -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 + """Persist complete labeled P9.1 managed evidence and sidecars.""" from __future__ import annotations diff --git a/tools/prompt_control_attention_coupling_integration/validation.py b/tools/prompt_control_attention_coupling_integration/validation.py index 6af1c1b..bc059ca 100644 --- a/tools/prompt_control_attention_coupling_integration/validation.py +++ b/tools/prompt_control_attention_coupling_integration/validation.py @@ -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 + """Validate P9.1 conditioning, selection, LoRA, and performance evidence.""" from __future__ import annotations diff --git a/tools/prompt_control_attention_coupling_integration/workflow.py b/tools/prompt_control_attention_coupling_integration/workflow.py index 9dd4865..217d3da 100644 --- a/tools/prompt_control_attention_coupling_integration/workflow.py +++ b/tools/prompt_control_attention_coupling_integration/workflow.py @@ -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 + """Bind P9.1 conditioning to the shared full-context sampler graph.""" from __future__ import annotations diff --git a/tools/prompt_control_characterization/__init__.py b/tools/prompt_control_characterization/__init__.py index 7963f68..d7da0c9 100644 --- a/tools/prompt_control_characterization/__init__.py +++ b/tools/prompt_control_characterization/__init__.py @@ -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 + """Characterize pinned Prompt Control scheduling through public Comfy nodes.""" diff --git a/tools/prompt_control_characterization/cases.py b/tools/prompt_control_characterization/cases.py index 3eb5411..6285137 100644 --- a/tools/prompt_control_characterization/cases.py +++ b/tools/prompt_control_characterization/cases.py @@ -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 + """Own immutable Prompt Control conditioning and LoRA schedule cases.""" from __future__ import annotations diff --git a/tools/prompt_control_characterization/conditioning.py b/tools/prompt_control_characterization/conditioning.py index 005bf4d..987608b 100644 --- a/tools/prompt_control_characterization/conditioning.py +++ b/tools/prompt_control_characterization/conditioning.py @@ -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 + """Own exact Prompt Control text construction shared by managed evidence.""" from __future__ import annotations diff --git a/tools/prompt_control_characterization/evidence_validation.py b/tools/prompt_control_characterization/evidence_validation.py index 76cbfda..1f51b49 100644 --- a/tools/prompt_control_characterization/evidence_validation.py +++ b/tools/prompt_control_characterization/evidence_validation.py @@ -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 + """Enforce exact Prompt Control conditioning, hook, UUID, and runtime evidence.""" from __future__ import annotations diff --git a/tools/prompt_control_characterization/history_outputs.py b/tools/prompt_control_characterization/history_outputs.py index 4444439..8d62447 100644 --- a/tools/prompt_control_characterization/history_outputs.py +++ b/tools/prompt_control_characterization/history_outputs.py @@ -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 + """Decode Prompt Control snapshot and runtime evidence from Comfy history.""" from __future__ import annotations diff --git a/tools/prompt_control_characterization/results.py b/tools/prompt_control_characterization/results.py index 06122d5..7b8e1ef 100644 --- a/tools/prompt_control_characterization/results.py +++ b/tools/prompt_control_characterization/results.py @@ -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 + """Persist and finalize the complete pinned Prompt Control characterization.""" from __future__ import annotations diff --git a/tools/prompt_control_characterization/source_identity.py b/tools/prompt_control_characterization/source_identity.py index 1b829af..68fdd2e 100644 --- a/tools/prompt_control_characterization/source_identity.py +++ b/tools/prompt_control_characterization/source_identity.py @@ -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 immutable installed Prompt Control source identity.""" from __future__ import annotations diff --git a/tools/prompt_control_characterization/workflow.py b/tools/prompt_control_characterization/workflow.py index 3d304a6..9dcb27e 100644 --- a/tools/prompt_control_characterization/workflow.py +++ b/tools/prompt_control_characterization/workflow.py @@ -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 Prompt Control characterization graphs from public Comfy nodes.""" from __future__ import annotations diff --git a/tools/regional_lora_real_fixtures.py b/tools/regional_lora_real_fixtures.py new file mode 100644 index 0000000..1c59e3f --- /dev/null +++ b/tools/regional_lora_real_fixtures.py @@ -0,0 +1,102 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Own installed real-model fixtures used by regional LoRA characterization.""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +import comfy.sd + +from simple_syrup.domain.regional_lora_plan import ( + RegionalLoraAdapterIdentity, + RegionalLoraAdapterPlan, + RegionalLoraBranch, + RegionalLoraPlan, + RegionalLoraScheduleBoundary, +) + + +@dataclass(frozen=True, slots=True) +class RegionalLoraRealFixture: + """Describe one installed model/LoRA pair and its Comfy loading boundary.""" + + family: str + model_path: Path + lora_path: Path + standalone_diffusion_model: bool + + def require_files(self) -> None: + """Fail clearly when one machine-local fixture is unavailable.""" + + for path in (self.model_path, self.lora_path): + if not path.is_file(): + raise FileNotFoundError( + f"Required characterization artifact is missing: {path}" + ) + + def load_model(self) -> Any: + """Load the fixture through its installed Comfy model boundary.""" + + self.require_files() + if self.standalone_diffusion_model: + return comfy.sd.load_diffusion_model(str(self.model_path), model_options={}) + loaded = comfy.sd.load_checkpoint_guess_config( + str(self.model_path), + output_vae=False, + output_clip=False, + embedding_directory=[], + ) + return loaded[0] + + def plan(self) -> RegionalLoraPlan: + """Return one valid canonical adapter use for this fixture.""" + + return RegionalLoraPlan( + ( + RegionalLoraAdapterPlan( + adapter_identity=RegionalLoraAdapterIdentity(str(self.lora_path)), + composition_index=0, + region_index=0, + branch=RegionalLoraBranch.POSITIVE, + model_strength=1.0, + schedule=(RegionalLoraScheduleBoundary(0.0, 1.0, 1.0, 0),), + ), + ) + ) + + +REGIONAL_LORA_REAL_FIXTURES = { + "anima": RegionalLoraRealFixture( + family="anima", + model_path=Path( + r"\diffusion_models\Anima\diffusion-model.safetensors" + ), + lora_path=Path(r"\Loras\Anima\style\adapter-a.safetensors"), + standalone_diffusion_model=True, + ), + "sd15": RegionalLoraRealFixture( + family="sd15", + model_path=Path( + r"\stable-diffusion\SD 1.5\checkpoint-fixture.safetensors" + ), + lora_path=Path(r"\lorabka\SD 1.5\adapter-fixture.safetensors"), + standalone_diffusion_model=False, + ), + "sdxl": RegionalLoraRealFixture( + family="sdxl", + model_path=Path( + "\\stable-diffusion\\Illustrious\\" + "checkpoint_aXLIllustrious_v30WIP.safetensors" + ), + lora_path=Path( + "\\Loras\\Illustrious\\Style\\" + "ELDEN RING Background_illustriousXL_v2.safetensors" + ), + standalone_diffusion_model=False, + ), +} diff --git a/tools/regional_patch_interop_integration/__init__.py b/tools/regional_patch_interop_integration/__init__.py index 2dcb71a..2e5b69c 100644 --- a/tools/regional_patch_interop_integration/__init__.py +++ b/tools/regional_patch_interop_integration/__init__.py @@ -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 + """Own the managed P9.7 MODEL modifier interoperability matrix.""" diff --git a/tools/regional_patch_interop_integration/case_artifacts.py b/tools/regional_patch_interop_integration/case_artifacts.py index 85d7ce7..d4f377e 100644 --- a/tools/regional_patch_interop_integration/case_artifacts.py +++ b/tools/regional_patch_interop_integration/case_artifacts.py @@ -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 + """Persist one validated P9.7 image or rejection artifact.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/history.py b/tools/regional_patch_interop_integration/history.py index defaaa4..7b0e027 100644 --- a/tools/regional_patch_interop_integration/history.py +++ b/tools/regional_patch_interop_integration/history.py @@ -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 + """Decode successful or rejected P9.7 terminal Comfy history.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/image_evidence.py b/tools/regional_patch_interop_integration/image_evidence.py index b813de4..8f0bf9e 100644 --- a/tools/regional_patch_interop_integration/image_evidence.py +++ b/tools/regional_patch_interop_integration/image_evidence.py @@ -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 + """Validate decoded-pixel equality for exact P9.7 full-context paths.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/latent.py b/tools/regional_patch_interop_integration/latent.py index 906406c..b9fadac 100644 --- a/tools/regional_patch_interop_integration/latent.py +++ b/tools/regional_patch_interop_integration/latent.py @@ -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 a no-denoiser 1024-to-1536 latent for rejection graphs.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/log_evidence.py b/tools/regional_patch_interop_integration/log_evidence.py index e1c17d5..149f67f 100644 --- a/tools/regional_patch_interop_integration/log_evidence.py +++ b/tools/regional_patch_interop_integration/log_evidence.py @@ -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 + """Publish hashed managed-Comfy logs and focused P9.7 diagnostics.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/matrix.py b/tools/regional_patch_interop_integration/matrix.py index 9d3ee8b..4d5866a 100644 --- a/tools/regional_patch_interop_integration/matrix.py +++ b/tools/regional_patch_interop_integration/matrix.py @@ -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 the closed managed P9.7 modifier interoperability matrix.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/model_evidence.py b/tools/regional_patch_interop_integration/model_evidence.py index 0cc4e91..db2450d 100644 --- a/tools/regional_patch_interop_integration/model_evidence.py +++ b/tools/regional_patch_interop_integration/model_evidence.py @@ -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 + """Add the benchmark-only pass-through MODEL modifier evidence node.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/modifier.py b/tools/regional_patch_interop_integration/modifier.py index 545de1d..812305e 100644 --- a/tools/regional_patch_interop_integration/modifier.py +++ b/tools/regional_patch_interop_integration/modifier.py @@ -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 one public upstream MODEL modifier without conditioning policy.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/results.py b/tools/regional_patch_interop_integration/results.py index d7c9c35..9cecd51 100644 --- a/tools/regional_patch_interop_integration/results.py +++ b/tools/regional_patch_interop_integration/results.py @@ -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 + """Persist labeled P9.7 images, rejections, logs, and terminal results.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/source_identity.py b/tools/regional_patch_interop_integration/source_identity.py index a45c64b..324909b 100644 --- a/tools/regional_patch_interop_integration/source_identity.py +++ b/tools/regional_patch_interop_integration/source_identity.py @@ -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 + """Inspect repository revisions for managed P9.7 evidence.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/validation.py b/tools/regional_patch_interop_integration/validation.py index f436e85..040ce94 100644 --- a/tools/regional_patch_interop_integration/validation.py +++ b/tools/regional_patch_interop_integration/validation.py @@ -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 + """Validate exact P9.7 modifier, execution, and rejection evidence.""" from __future__ import annotations diff --git a/tools/regional_patch_interop_integration/workflow.py b/tools/regional_patch_interop_integration/workflow.py index ea93419..b710bf1 100644 --- a/tools/regional_patch_interop_integration/workflow.py +++ b/tools/regional_patch_interop_integration/workflow.py @@ -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 the public managed P9.7 accepted and rejected workflow graphs.""" from __future__ import annotations diff --git a/tools/regional_strategy_comparison/__init__.py b/tools/regional_strategy_comparison/__init__.py index 91b7498..1914555 100644 --- a/tools/regional_strategy_comparison/__init__.py +++ b/tools/regional_strategy_comparison/__init__.py @@ -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 the focused P10.2 regional strategy comparison harness.""" diff --git a/tools/regional_strategy_comparison/comparison.py b/tools/regional_strategy_comparison/comparison.py index b313daa..6c96e3f 100644 --- a/tools/regional_strategy_comparison/comparison.py +++ b/tools/regional_strategy_comparison/comparison.py @@ -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 + """Validate P10.2 cross-strategy and regional-LoRA execution invariants.""" from __future__ import annotations diff --git a/tools/regional_strategy_comparison/history.py b/tools/regional_strategy_comparison/history.py index 7bf5a55..ecbf2ca 100644 --- a/tools/regional_strategy_comparison/history.py +++ b/tools/regional_strategy_comparison/history.py @@ -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 + """Narrow P10.2 managed history into typed evidence.""" from __future__ import annotations diff --git a/tools/regional_strategy_comparison/input_artifacts.py b/tools/regional_strategy_comparison/input_artifacts.py index 48470fa..b120ab7 100644 --- a/tools/regional_strategy_comparison/input_artifacts.py +++ b/tools/regional_strategy_comparison/input_artifacts.py @@ -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 + """Own exact Comfy input artifacts for the P10.2 managed comparison.""" from __future__ import annotations diff --git a/tools/regional_strategy_comparison/matrix.py b/tools/regional_strategy_comparison/matrix.py index 54e32dc..cd2b24f 100644 --- a/tools/regional_strategy_comparison/matrix.py +++ b/tools/regional_strategy_comparison/matrix.py @@ -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 the immutable P10.2 managed comparison matrix.""" from __future__ import annotations diff --git a/tools/regional_strategy_comparison/results.py b/tools/regional_strategy_comparison/results.py index 53b68f1..73c6cba 100644 --- a/tools/regional_strategy_comparison/results.py +++ b/tools/regional_strategy_comparison/results.py @@ -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 + """Persist and validate complete labeled P10.2 comparison evidence.""" from __future__ import annotations diff --git a/tools/regional_strategy_comparison/workflow.py b/tools/regional_strategy_comparison/workflow.py index 5c17894..3bb239a 100644 --- a/tools/regional_strategy_comparison/workflow.py +++ b/tools/regional_strategy_comparison/workflow.py @@ -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 P10.2 public-node strategy comparison workflows.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/__init__.py b/tools/regional_visual_benchmark/__init__.py index 1ed35a3..6396a9e 100644 --- a/tools/regional_visual_benchmark/__init__.py +++ b/tools/regional_visual_benchmark/__init__.py @@ -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 the fixed P10.3 regional visual benchmark system.""" diff --git a/tools/regional_visual_benchmark/blind.py b/tools/regional_visual_benchmark/blind.py index 2a4b763..8364bb1 100644 --- a/tools/regional_visual_benchmark/blind.py +++ b/tools/regional_visual_benchmark/blind.py @@ -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 + """Stage a strategy-blind P10.3 image packet with protected mapping.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/corpus.py b/tools/regional_visual_benchmark/corpus.py index 52f6384..361cceb 100644 --- a/tools/regional_visual_benchmark/corpus.py +++ b/tools/regional_visual_benchmark/corpus.py @@ -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 + """Persist and validate the complete managed P10.3 visual corpus.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/execution.py b/tools/regional_visual_benchmark/execution.py index 6fc5c25..203dfd9 100644 --- a/tools/regional_visual_benchmark/execution.py +++ b/tools/regional_visual_benchmark/execution.py @@ -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 one resumable P10.3 corpus through bounded managed-Comfy segments.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/input_artifacts.py b/tools/regional_visual_benchmark/input_artifacts.py index 81ff67d..16b7cc9 100644 --- a/tools/regional_visual_benchmark/input_artifacts.py +++ b/tools/regional_visual_benchmark/input_artifacts.py @@ -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 + """Own exact Comfy input files for one P10.3 visual position.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/matrix.py b/tools/regional_visual_benchmark/matrix.py index 53b2250..69f715f 100644 --- a/tools/regional_visual_benchmark/matrix.py +++ b/tools/regional_visual_benchmark/matrix.py @@ -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 the immutable corrected P10.3 visual benchmark matrix.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/run_artifacts.py b/tools/regional_visual_benchmark/run_artifacts.py index 73c93c2..8ff9f3c 100644 --- a/tools/regional_visual_benchmark/run_artifacts.py +++ b/tools/regional_visual_benchmark/run_artifacts.py @@ -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 + """Own durable identity and segment evidence for one P10.3 corpus run.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/scoring.py b/tools/regional_visual_benchmark/scoring.py index b6f8c1d..7839e77 100644 --- a/tools/regional_visual_benchmark/scoring.py +++ b/tools/regional_visual_benchmark/scoring.py @@ -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 + """Validate and seal strategy-blind P10.3 human scores.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/unblind.py b/tools/regional_visual_benchmark/unblind.py index fc472f3..d547708 100644 --- a/tools/regional_visual_benchmark/unblind.py +++ b/tools/regional_visual_benchmark/unblind.py @@ -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 + """Join a sealed P10.3 score ledger to protected corpus identities.""" from __future__ import annotations diff --git a/tools/regional_visual_benchmark/workflow.py b/tools/regional_visual_benchmark/workflow.py index 357a7d1..25d82a4 100644 --- a/tools/regional_visual_benchmark/workflow.py +++ b/tools/regional_visual_benchmark/workflow.py @@ -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 public-node workflows for corrected P10.3 visual positions.""" from __future__ import annotations diff --git a/tools/run_anima_attention_coupling_integration.py b/tools/run_anima_attention_coupling_integration.py index b41fe35..fb30c8d 100644 --- a/tools/run_anima_attention_coupling_integration.py +++ b/tools/run_anima_attention_coupling_integration.py @@ -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 + """Run the P5.9 public Attention Coupling matrix in an isolated ComfyUI.""" from __future__ import annotations diff --git a/tools/run_anima_contextual_attention_coupling_integration.py b/tools/run_anima_contextual_attention_coupling_integration.py index 1b8ae75..20a0bfd 100644 --- a/tools/run_anima_contextual_attention_coupling_integration.py +++ b/tools/run_anima_contextual_attention_coupling_integration.py @@ -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 + """Run the P7.8 combined public-node matrix in isolated ComfyUI.""" from __future__ import annotations diff --git a/tools/run_anima_regional_lora_admission_integration.py b/tools/run_anima_regional_lora_admission_integration.py index 8cfef4b..f22cb8d 100644 --- a/tools/run_anima_regional_lora_admission_integration.py +++ b/tools/run_anima_regional_lora_admission_integration.py @@ -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 + """Run the P9.5 supported/rejected matrix in one managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_anima_regional_nondiffusion_rejection_integration.py b/tools/run_anima_regional_nondiffusion_rejection_integration.py index 3bd3520..10107a0 100644 --- a/tools/run_anima_regional_nondiffusion_rejection_integration.py +++ b/tools/run_anima_regional_nondiffusion_rejection_integration.py @@ -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 + """Run the P9.6 non-diffusion rejection matrix in one managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_anima_regression_oracle.py b/tools/run_anima_regression_oracle.py new file mode 100644 index 0000000..e2e2dbc --- /dev/null +++ b/tools/run_anima_regression_oracle.py @@ -0,0 +1,110 @@ +# SimpleSyrup - workflow-focused ComfyUI extensions for image generation +# Copyright (C) 2026 Artificial Sweetener and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later + +"""Run the blocking accepted Anima regression oracle.""" + +from __future__ import annotations + +import argparse +import logging +import sys +from collections.abc import Sequence +from datetime import UTC, datetime +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.anima_regression_oracle.artifact_validation import ( # noqa: E402 + AcceptedArtifactValidator, + ArtifactObservation, +) +from tools.anima_regression_oracle.execution import ( # noqa: E402 + CommandObservation, + OracleCommandExecutor, +) +from tools.anima_regression_oracle.manifest import ( # noqa: E402 + OracleCommand, + default_manifest, +) +from tools.anima_regression_oracle.results import ( # noqa: E402 + AnimaRegressionResultRecorder, +) + +LOGGER = logging.getLogger(__name__) +DEFAULT_OUTPUT_ROOT = Path( + r"\benchmark_artifacts\universal-regional-adapter\anima-oracle" +) + + +def main(argv: Sequence[str] | None = None) -> int: + """Validate accepted artifacts and execute the requested blocking gates.""" + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--level", + choices=("accepted", "focused", "full"), + default="focused", + help=( + "Select accepted artifacts only, focused Anima gates, or all " + "repository gates." + ), + ) + parser.add_argument("--output-root", type=Path, default=DEFAULT_OUTPUT_ROOT) + args = parser.parse_args(argv) + logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s") + recorder = AnimaRegressionResultRecorder(args.output_root / _run_id()) + manifest = default_manifest(REPO_ROOT) + commands = _commands( + args.level, manifest.focused_command, manifest.repository_commands + ) + artifacts: tuple[ArtifactObservation, ...] = () + observations: tuple[CommandObservation, ...] = () + try: + artifacts = AcceptedArtifactValidator().validate(manifest) + observations = OracleCommandExecutor().execute( + commands, + repo_root=REPO_ROOT, + output_root=recorder.root, + ) + except BaseException: + LOGGER.exception("Anima regression oracle failed before command completion.") + result = recorder.publish( + level=args.level, + artifacts=artifacts, + commands=observations, + expected_command_count=len(commands), + ) + passed = len(observations) == len(commands) and all( + item.return_code == 0 for item in observations + ) + if args.level == "accepted": + passed = passed and bool(artifacts) + LOGGER.info("Anima regression oracle result: %s", result) + return 0 if passed else 1 + + +def _commands( + level: str, + focused: OracleCommand, + repository: tuple[OracleCommand, ...], +) -> tuple[OracleCommand, ...]: + """Return the exact ordered commands for one declared verification level.""" + + if level == "accepted": + return () + if level == "focused": + return (focused,) + return (focused, *repository) + + +def _run_id() -> str: + """Return one collision-resistant UTC result identity.""" + + return datetime.now(UTC).strftime("%Y%m%dT%H%M%S.%fZ") + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/run_anima_tiled_attention_coupling_integration.py b/tools/run_anima_tiled_attention_coupling_integration.py index 756c085..7836ec2 100644 --- a/tools/run_anima_tiled_attention_coupling_integration.py +++ b/tools/run_anima_tiled_attention_coupling_integration.py @@ -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 + """Run the P6.9 tiled public Attention Coupling matrix in isolated ComfyUI.""" from __future__ import annotations diff --git a/tools/run_global_lora_visual_proof.py b/tools/run_global_lora_visual_proof.py index 98ce7f9..64d8d2c 100644 --- a/tools/run_global_lora_visual_proof.py +++ b/tools/run_global_lora_visual_proof.py @@ -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 + """Run the fixed-seed global and regional ADAPTER_A visual comparison.""" from __future__ import annotations diff --git a/tools/run_global_regional_lora_integration.py b/tools/run_global_regional_lora_integration.py index 405bcb9..2740934 100644 --- a/tools/run_global_regional_lora_integration.py +++ b/tools/run_global_regional_lora_integration.py @@ -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 + """Run the P9.3 global/regional Anima LoRA matrix in managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_global_style_character_proof.py b/tools/run_global_style_character_proof.py index a19e98c..6e074f3 100644 --- a/tools/run_global_style_character_proof.py +++ b/tools/run_global_style_character_proof.py @@ -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 + """Run global ADAPTER_A style with a right-region CHARACTER_A character LoRA.""" from __future__ import annotations diff --git a/tools/run_kyoko_ownership_proof.py b/tools/run_kyoko_ownership_proof.py index a6f3fd6..394e80c 100644 --- a/tools/run_kyoko_ownership_proof.py +++ b/tools/run_kyoko_ownership_proof.py @@ -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 + """Run one fixed-seed CHARACTER_A regional-composition proof in managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_managed_comfy_baseline.py b/tools/run_managed_comfy_baseline.py index 1297761..c8a6243 100644 --- a/tools/run_managed_comfy_baseline.py +++ b/tools/run_managed_comfy_baseline.py @@ -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 + """Run one real incoming Anima baseline in an isolated managed Comfy server.""" from __future__ import annotations diff --git a/tools/run_mjpa_ownership_proof.py b/tools/run_mjpa_ownership_proof.py index 4307e93..d0f4468 100644 --- a/tools/run_mjpa_ownership_proof.py +++ b/tools/run_mjpa_ownership_proof.py @@ -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 + """Run one fixed-seed ADAPTER_A regional-composition proof in managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_prompt_control_attention_coupling_integration.py b/tools/run_prompt_control_attention_coupling_integration.py index 35e0dd2..5ed6167 100644 --- a/tools/run_prompt_control_attention_coupling_integration.py +++ b/tools/run_prompt_control_attention_coupling_integration.py @@ -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 + """Run the P9.1 Prompt Control matrix in one isolated managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_regional_patch_interop_integration.py b/tools/run_regional_patch_interop_integration.py index 4066c9e..8726009 100644 --- a/tools/run_regional_patch_interop_integration.py +++ b/tools/run_regional_patch_interop_integration.py @@ -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 + """Run the closed P9.7 modifier interoperability matrix in managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_regional_strategy_comparison.py b/tools/run_regional_strategy_comparison.py index eb5bde6..cd01258 100644 --- a/tools/run_regional_strategy_comparison.py +++ b/tools/run_regional_strategy_comparison.py @@ -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 + """Run the P10.2 regional strategy comparison in an isolated ComfyUI.""" from __future__ import annotations diff --git a/tools/run_regional_visual_benchmark.py b/tools/run_regional_visual_benchmark.py index 538490e..9c7440b 100644 --- a/tools/run_regional_visual_benchmark.py +++ b/tools/run_regional_visual_benchmark.py @@ -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 + """Run or resume the complete corrected P10.3 visual corpus in managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_sdxl_attention_coupling_integration.py b/tools/run_sdxl_attention_coupling_integration.py index acc6830..0a6520c 100644 --- a/tools/run_sdxl_attention_coupling_integration.py +++ b/tools/run_sdxl_attention_coupling_integration.py @@ -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 + """Run the Phase 8 public SDXL Attention Coupling matrix in managed ComfyUI.""" from __future__ import annotations diff --git a/tools/run_text_encoder_lora_integration.py b/tools/run_text_encoder_lora_integration.py index 415bc54..6f48e34 100644 --- a/tools/run_text_encoder_lora_integration.py +++ b/tools/run_text_encoder_lora_integration.py @@ -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 + """Run the complete P9.4 text-encoder LoRA matrix in managed ComfyUI.""" from __future__ import annotations diff --git a/tools/score_regional_visual_benchmark.py b/tools/score_regional_visual_benchmark.py index 384172b..452c699 100644 --- a/tools/score_regional_visual_benchmark.py +++ b/tools/score_regional_visual_benchmark.py @@ -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 + """Record, seal, and unblind P10.3 visual scores.""" from __future__ import annotations diff --git a/tools/sdxl_attention_coupling_integration/__init__.py b/tools/sdxl_attention_coupling_integration/__init__.py index 63a1005..4a89cca 100644 --- a/tools/sdxl_attention_coupling_integration/__init__.py +++ b/tools/sdxl_attention_coupling_integration/__init__.py @@ -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 + """Own managed SDXL Attention Coupling integration evidence.""" diff --git a/tools/sdxl_attention_coupling_integration/checkpoint_link.py b/tools/sdxl_attention_coupling_integration/checkpoint_link.py index 53ceb5c..b2d643e 100644 --- a/tools/sdxl_attention_coupling_integration/checkpoint_link.py +++ b/tools/sdxl_attention_coupling_integration/checkpoint_link.py @@ -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 + """Own one temporary normal-install checkpoint hard link for integration.""" from __future__ import annotations diff --git a/tools/sdxl_attention_coupling_integration/history.py b/tools/sdxl_attention_coupling_integration/history.py index 7009395..65dbdad 100644 --- a/tools/sdxl_attention_coupling_integration/history.py +++ b/tools/sdxl_attention_coupling_integration/history.py @@ -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 + """Decode labeled images, metrics, and diagnostics from managed history.""" from __future__ import annotations diff --git a/tools/sdxl_attention_coupling_integration/masks.py b/tools/sdxl_attention_coupling_integration/masks.py index e4da942..e15f8f5 100644 --- a/tools/sdxl_attention_coupling_integration/masks.py +++ b/tools/sdxl_attention_coupling_integration/masks.py @@ -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 + """Own deterministic temporary split masks for the SDXL integration run.""" from __future__ import annotations diff --git a/tools/sdxl_attention_coupling_integration/matrix.py b/tools/sdxl_attention_coupling_integration/matrix.py index 97d385a..5022df5 100644 --- a/tools/sdxl_attention_coupling_integration/matrix.py +++ b/tools/sdxl_attention_coupling_integration/matrix.py @@ -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 the fixed Phase 8 SDXL managed-workflow matrix.""" from __future__ import annotations diff --git a/tools/sdxl_attention_coupling_integration/results.py b/tools/sdxl_attention_coupling_integration/results.py index b797cd5..b10309d 100644 --- a/tools/sdxl_attention_coupling_integration/results.py +++ b/tools/sdxl_attention_coupling_integration/results.py @@ -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 + """Validate and persist the complete labeled Phase 8 SDXL evidence.""" from __future__ import annotations diff --git a/tools/sdxl_attention_coupling_integration/workflow.py b/tools/sdxl_attention_coupling_integration/workflow.py index 96a1d8d..f55dff2 100644 --- a/tools/sdxl_attention_coupling_integration/workflow.py +++ b/tools/sdxl_attention_coupling_integration/workflow.py @@ -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 the public full, tiled, and Contextual SDXL workflow graph.""" from __future__ import annotations diff --git a/tools/text_encoder_lora_integration/__init__.py b/tools/text_encoder_lora_integration/__init__.py index 6dac1f5..adb8913 100644 --- a/tools/text_encoder_lora_integration/__init__.py +++ b/tools/text_encoder_lora_integration/__init__.py @@ -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 + """Managed P9.4 text-encoder LoRA interoperability evidence.""" diff --git a/tools/text_encoder_lora_integration/conditioning.py b/tools/text_encoder_lora_integration/conditioning.py index d645700..301abb1 100644 --- a/tools/text_encoder_lora_integration/conditioning.py +++ b/tools/text_encoder_lora_integration/conditioning.py @@ -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 native global and Prompt-Control regional CLIP-LoRA graphs.""" from __future__ import annotations diff --git a/tools/text_encoder_lora_integration/fixture.py b/tools/text_encoder_lora_integration/fixture.py index d8b3c70..9094d4f 100644 --- a/tools/text_encoder_lora_integration/fixture.py +++ b/tools/text_encoder_lora_integration/fixture.py @@ -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 + """Pin and validate the external P9.4 Anima text-encoder LoRA fixture.""" from __future__ import annotations diff --git a/tools/text_encoder_lora_integration/history.py b/tools/text_encoder_lora_integration/history.py index d902e93..f17044c 100644 --- a/tools/text_encoder_lora_integration/history.py +++ b/tools/text_encoder_lora_integration/history.py @@ -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 + """Extract typed P9.4 metrics and diagnostics from Comfy history.""" from __future__ import annotations diff --git a/tools/text_encoder_lora_integration/image_validation.py b/tools/text_encoder_lora_integration/image_validation.py index 996b3f8..d94be8d 100644 --- a/tools/text_encoder_lora_integration/image_validation.py +++ b/tools/text_encoder_lora_integration/image_validation.py @@ -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 + """Validate decoded image effects for every P9.4 LoRA comparison.""" from __future__ import annotations diff --git a/tools/text_encoder_lora_integration/matrix.py b/tools/text_encoder_lora_integration/matrix.py index 3d0d42a..a91063a 100644 --- a/tools/text_encoder_lora_integration/matrix.py +++ b/tools/text_encoder_lora_integration/matrix.py @@ -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 the immutable P9.4 text-encoder LoRA evidence matrix.""" from __future__ import annotations diff --git a/tools/text_encoder_lora_integration/results.py b/tools/text_encoder_lora_integration/results.py index 5ec56a9..c2f055a 100644 --- a/tools/text_encoder_lora_integration/results.py +++ b/tools/text_encoder_lora_integration/results.py @@ -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 + """Validate and persist complete P9.4 managed evidence.""" from __future__ import annotations diff --git a/tools/text_encoder_lora_integration/workflow.py b/tools/text_encoder_lora_integration/workflow.py index 28b5439..a993432 100644 --- a/tools/text_encoder_lora_integration/workflow.py +++ b/tools/text_encoder_lora_integration/workflow.py @@ -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 + """Bind P9.4 conditioning to full, tiled, and Contextual public samplers.""" from __future__ import annotations