From 2a83efbbe5fb9c856e38fbdf4e4a04e6c8cd6e03 Mon Sep 17 00:00:00 2001 From: BlackVortexAI Date: Tue, 25 Aug 2026 07:18:35 +0200 Subject: [PATCH] Document Regional legacy removal windows --- py/nodes/bv_regional.py | 21 +++++++++++++++++++++ py/nodes/bv_regional_detailer.py | 7 +++++++ py/util/regional/detailer_v3.py | 2 ++ py/util/regional/document.py | 2 ++ py/util/regional/lora_v3.py | 4 ++++ py/util/regional/mask_renderer.py | 2 ++ 6 files changed, 38 insertions(+) diff --git a/py/nodes/bv_regional.py b/py/nodes/bv_regional.py index 4bf311d..35b529c 100644 --- a/py/nodes/bv_regional.py +++ b/py/nodes/bv_regional.py @@ -68,6 +68,8 @@ CATEGORY_MODEL_FLUX2_KLEIN = f"{CATEGORY_MODELS}/FLUX.2 Klein 9B" CATEGORY_MODEL_KREA2 = f"{CATEGORY_MODELS}/Krea 2" CATEGORY_MODEL_ANIMA = f"{CATEGORY_MODELS}/Anima" DEFAULT_JSON = json.dumps(default_document(), ensure_ascii=False, separators=(",", ":")) +# BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 LoRA bindings JSON/output. +# Remove with the lora_bindings output after saved V2 workflows have passed the compatibility window. DEFAULT_LORA_BINDINGS_JSON = json.dumps(default_bindings(), ensure_ascii=False, separators=(",", ":")) DEFAULT_LORA_V3_JSON = '{"version":3,"entries":[],"steps":[]}' DEFAULT_DETAILER_V3_JSON = '{"version":1,"jobs":[]}' @@ -108,6 +110,8 @@ class BVRegionalPromptNode: ), }, "optional": { + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Persisted V2 bindings payload. + # Remove with the lora_bindings output after workflow migration support expires. "lora_bindings_json": ( "STRING", {"default": DEFAULT_LORA_BINDINGS_JSON, "multiline": True, "dynamicPrompts": False}, @@ -129,6 +133,7 @@ class BVRegionalPromptNode: }, } + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Second output is V2 wiring. RETURN_TYPES = (REGIONAL, BINDINGS) RETURN_NAMES = ("regional", "lora_bindings") FUNCTION = "build" @@ -180,6 +185,8 @@ class BVLoraStackCollectorNode: def INPUT_TYPES(cls): return { "required": { + # BV-LEGACY(marked=2026-08-25, review-after=2026-10-25): V2 stack registry bridge. + # Remove only when named stacks expose the V3 provider contract without this adapter. "lora_registry": (REGISTRY, {}), "collector_id": ("STRING", {"default": "", "multiline": False}), } @@ -235,6 +242,8 @@ def _context_lora_scopes(regional): def _consumer_lora_scopes(regional, document, lora_registry=None, lora_bindings=None): + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Prefer the V3 context capability. + # Remove this sidecar fallback together with every lora_registry/lora_bindings consumer port. if lora_registry is not None or lora_bindings is not None: return resolve_scope_stacks(lora_registry, lora_bindings, document) return _context_lora_scopes(regional) @@ -500,6 +509,7 @@ class BVRegionalNativeConditioningNode: }, ), }, + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 LoRA sidecar inputs. "optional": {"lora_registry": (REGISTRY, {}), "lora_bindings": (BINDINGS, {})}, } @@ -544,6 +554,7 @@ class BVRegionalSDXLAttentionNode: {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}, ), }, + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 LoRA sidecar inputs. "optional": {"lora_registry": (REGISTRY, {}), "lora_bindings": (BINDINGS, {})}, } @@ -588,6 +599,7 @@ class BVRegionalZImageAttentionNode: "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), "end_percent": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), }, + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 LoRA sidecar inputs. "optional": {"lora_registry": (REGISTRY, {}), "lora_bindings": (BINDINGS, {})}, } @@ -624,6 +636,7 @@ class BVRegionalFlux2KleinAttentionNode: "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), "end_percent": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), }, + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 LoRA sidecar inputs. "optional": {"lora_registry": (REGISTRY, {}), "lora_bindings": (BINDINGS, {})}, } @@ -667,12 +680,15 @@ class BVRegionalKrea2AttentionNode: "attention_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), "end_percent": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), + # BV-LEGACY(marked=2026-08-25, review-after=2026-10-25): Compatibility execution mode. + # Remove only after token-gated Krea 2 matches supported published LoRA behavior. "regional_lora_mode": (["multipass_legacy", "token_gated_singlepass"], { "default": "token_gated_singlepass", "tooltip": "Legacy evaluates one masked model pass per distinct LoRA stack. " "Token-gated single-pass is experimental and changes results.", }), }, + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 LoRA sidecar inputs. "optional": {"lora_registry": (REGISTRY, {}), "lora_bindings": (BINDINGS, {})}, } @@ -709,6 +725,7 @@ class BVRegionalKrea2AttentionNode: positive, negative, slots, aspect_ratio = compile_krea2_attention( document, clip, hook_groups ) + # BV-LEGACY(marked=2026-08-25, review-after=2026-10-25): See the INPUT_TYPES removal gate above. if regional_lora_mode == "multipass_legacy": positive, negative = apply_attention_hook_passes( positive, negative, document, scope_stacks, hook_groups @@ -760,11 +777,14 @@ class BVRegionalAnimaConditioningNode: "base_ratio": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01}), "cross_inject_every_n_blocks": ("INT", {"default": 1, "min": 1, "max": 100, "step": 1}), "self_inject_every_n_blocks": ("INT", {"default": 1, "min": 1, "max": 100, "step": 1}), + # BV-LEGACY(marked=2026-08-25, review-after=2026-10-25): Compatibility execution mode. + # Remove only after token-gated Anima matches supported published LoRA behavior. "regional_lora_mode": (["multipass_legacy", "token_gated_singlepass"], { "default": "multipass_legacy", "tooltip": "Legacy preserves published Anima results. Single-pass is an experimental token-gated model-LoRA path.", }), }, + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 LoRA sidecar inputs. "optional": {"lora_registry": (REGISTRY, {}), "lora_bindings": (BINDINGS, {})}, } @@ -809,6 +829,7 @@ class BVRegionalAnimaConditioningNode: scope_stacks = resolve_stack_paths(_consumer_lora_scopes(regional, document, lora_registry, lora_bindings)) hook_groups = create_hook_groups(scope_stacks) positive, negative, regions, background = compile_anima_adapter(document, clip, hook_groups) + # BV-LEGACY(marked=2026-08-25, review-after=2026-10-25): See the INPUT_TYPES removal gate above. if regional_lora_mode == "multipass_legacy": positive, negative = apply_attention_hook_passes( positive, negative, document, scope_stacks, hook_groups diff --git a/py/nodes/bv_regional_detailer.py b/py/nodes/bv_regional_detailer.py index a032597..c55a60c 100644 --- a/py/nodes/bv_regional_detailer.py +++ b/py/nodes/bv_regional_detailer.py @@ -68,6 +68,8 @@ class BVRegionalDetailerPlanNode: "config_json": ("STRING", {"default": "", "multiline": True}), }, "optional": { + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): V2 detector sidecar input. + # Remove after migrated plans resolve detectors exclusively through V3 provider links. "detector_registry": (DETECTOR_REGISTRY, {}), "resource_provider": (RUNTIME_PROVIDER, {"forceInput": True}), **{ @@ -118,6 +120,7 @@ class BVRegionalDetailerPlanNode: detector_id = job.get("detector_id") if detector_id is not None: resolve_detector(detector_registry, detector_id) + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Preserve V2 executor lookup. plan["detector_registry"] = detector_registry summary = "\n".join( f"{index + 1}. {' + '.join(job['region_names'])}" @@ -161,6 +164,7 @@ class BVRegionalDetailerJobNode: raise ValueError("BV Detailer Loop Job Resolver requires a single IMAGE shaped B,H,W,C") job = detailer_job_at(detailer_plan, job_index) detector_id = job.get("detector_id") + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Execute unmigrated V1/V2 jobs. if "detector_binding" not in job: job["detector_binding"] = ( resolve_detector(detailer_plan.get("detector_registry"), detector_id) @@ -231,6 +235,8 @@ class BVDetectorRegistryNode: }, } + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): First output is the V2 registry. + # Remove it only with the detector_registry plan input; resource_provider is the V3 output. RETURN_TYPES = (DETECTOR_REGISTRY, "INT", "STRING", RUNTIME_PROVIDER) RETURN_NAMES = ("detector_registry", "detector_count", "registry_summary", "resource_provider") FUNCTION = "collect" @@ -252,6 +258,7 @@ class BVDetectorRegistryNode: } except json.JSONDecodeError as error: raise ValueError("detector registry configuration is invalid JSON") from error + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Accept config v1 without collector_id. if not isinstance(parsed, dict) or parsed.get("schema") != "bv.detector_registry_config" or parsed.get("version") not in {1, 2}: raise ValueError("detector registry configuration must be bv.detector_registry_config v1 or v2") collector_id = str(parsed.get("collector_id", "")).strip() if parsed.get("version") == 2 else "" diff --git a/py/util/regional/detailer_v3.py b/py/util/regional/detailer_v3.py index 1f78430..d46f2dd 100644 --- a/py/util/regional/detailer_v3.py +++ b/py/util/regional/detailer_v3.py @@ -280,6 +280,8 @@ def materialize_detailer_plan( return plan +# BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Legacy plan -> V3 resource bindings. +# Remove after saved plans carry explicit same-graph collector_id/resource_id pairs. def migrate_detailer_plan_v1( value: Any, config: Any, diff --git a/py/util/regional/document.py b/py/util/regional/document.py index cc9bc13..ff79023 100644 --- a/py/util/regional/document.py +++ b/py/util/regional/document.py @@ -249,6 +249,8 @@ def validate_document(document: Any, *, executable: bool = True) -> list[str]: return list(dict.fromkeys(issues)) +# BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Regional document v1 -> v2. +# Remove only after published/saved v1 workflows have passed the compatibility window. def migrate_document(document: Any) -> dict[str, Any]: migrated = copy.deepcopy(document) if not isinstance(migrated, dict) or migrated.get("schema") != "bv.regional": diff --git a/py/util/regional/lora_v3.py b/py/util/regional/lora_v3.py index a11da51..3f60daf 100644 --- a/py/util/regional/lora_v3.py +++ b/py/util/regional/lora_v3.py @@ -113,6 +113,8 @@ def validate_lora_capability(payload: dict[str, Any]) -> None: }}) +# BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): LoRA capability v1 -> v2. +# Remove together with v1 validation/registration after old workflows have migrated. def migrate_lora_capability_v1(payload: dict[str, Any]) -> dict[str, Any]: validate_lora_capability(payload) collector_id = payload["collector_id"] @@ -127,6 +129,8 @@ def migrate_lora_capability_v1(payload: dict[str, Any]) -> dict[str, Any]: return migrated +# BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): LoRA capability v2 -> v3. +# Remove together with v2 validation/registration after old workflows have migrated. def migrate_lora_capability_v2(payload: dict[str, Any]) -> dict[str, Any]: validate_lora_capability(payload) migrated = {"version": 3, "entries": payload["entries"], "scopes": {}} diff --git a/py/util/regional/mask_renderer.py b/py/util/regional/mask_renderer.py index d4c361c..774c4c9 100644 --- a/py/util/regional/mask_renderer.py +++ b/py/util/regional/mask_renderer.py @@ -101,6 +101,8 @@ def _feather(mask: torch.Tensor, amount: float, width: int, height: int) -> torc def render_region(region: dict[str, Any], width: int, height: int) -> torch.Tensor: layers: dict[str, list[dict[str, Any]]] = {} + # BV-LEGACY(marked=2026-08-25, remove-after=2026-10-25): Pre-layer geometry fallback. + # Remove after documents without layer_id are no longer accepted by document migration. legacy_layer = next((shape["id"] for shape in region["geometry"] if "layer_id" not in shape), "__legacy__") for shape in region["geometry"]: layer_id = shape.get("layer_id", legacy_layer)