Skip to content

Commit 1a4c1a8

Browse files
committed
Clarify external noise integration rules
1 parent 3de1a0b commit 1a4c1a8

1 file changed

Lines changed: 58 additions & 8 deletions

File tree

docs/contributing/NEW_MODEL_INTEGRATION.md

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ class NewModelTextEncoderInvocation(BaseInvocation):
442442
class NewModelDenoiseInvocation(BaseInvocation):
443443
# Standard Fields
444444
latents: LatentsField | None = InputField(default=None)
445+
noise: LatentsField | None = InputField(default=None)
445446
positive_conditioning: ConditioningField = InputField()
446447
negative_conditioning: ConditioningField | None = InputField(default=None)
447448

@@ -453,6 +454,7 @@ class NewModelDenoiseInvocation(BaseInvocation):
453454
denoising_end: float = InputField(default=1.0, ge=0, le=1)
454455
steps: int = InputField(default=20, ge=1)
455456
cfg_scale: float = InputField(default=7.0)
457+
add_noise: bool = InputField(default=True)
456458

457459
# Image-to-Image / Inpainting
458460
denoise_mask: DenoiseMaskField | None = InputField(default=None)
@@ -461,16 +463,27 @@ class NewModelDenoiseInvocation(BaseInvocation):
461463
scheduler: Literal["euler", "heun", "lcm"] = InputField(default="euler")
462464

463465
def invoke(self, context: InvocationContext) -> LatentsOutput:
464-
# 1. Generate noise
465-
noise = get_noise_newmodel(seed, height, width, ...)
466-
467-
# 2. Pack latents (if needed)
468-
x = pack_newmodel(latents)
466+
# 1. Load or generate noise
467+
if self.noise is not None:
468+
noise = self._load_and_validate_noise(context)
469+
else:
470+
noise = get_noise_newmodel(seed, height, width, ...)
469471

470-
# 3. Compute schedule
472+
# 2. Compute schedule
471473
timesteps = get_schedule_newmodel(num_steps, denoising_start, denoising_end)
472474

473-
# 4. Denoising loop
475+
# 3. Prepare init latents and img2img preblend
476+
if latents is not None and self.add_noise:
477+
x = noise * timesteps[0] + latents * (1.0 - timesteps[0])
478+
elif latents is not None:
479+
x = latents
480+
else:
481+
x = noise
482+
483+
# 4. Pack latents (if needed)
484+
x = pack_newmodel(x)
485+
486+
# 5. Denoising loop
474487
x = denoise(
475488
model=transformer,
476489
x=x,
@@ -480,12 +493,19 @@ class NewModelDenoiseInvocation(BaseInvocation):
480493
inpaint_extension=inpaint_extension, # For inpainting
481494
)
482495

483-
# 5. Unpack latents
496+
# 6. Unpack latents
484497
latents = unpack_newmodel(x)
485498

486499
return LatentsOutput(latents=latents)
487500
```
488501

502+
If the architecture supports external noise, the denoise invocation should
503+
accept an optional `noise: LatentsField` input and preserve the existing
504+
seed-driven path when it is not connected. Validate external noise against
505+
the architecture's expected rank, channel count, and spatial shape before
506+
using it. Existing workflows must continue to work unchanged when `noise` is
507+
left disconnected.
508+
489509
### 4.4 VAE Encode Invocation
490510

491511
**File:** `invokeai/app/invocations/[newmodel]_vae_encode.py`
@@ -536,6 +556,9 @@ class NewModelVaeDecodeInvocation(BaseInvocation):
536556
- [ ] Model loader invocation (`[newmodel]_model_loader.py`)
537557
- [ ] Text encoder invocation (`[newmodel]_text_encoder.py`)
538558
- [ ] Denoise invocation (`[newmodel]_denoise.py`)
559+
- [ ] Add optional `noise: LatentsField` when the architecture supports
560+
external noise
561+
- [ ] Preserve the seed-driven fallback path when `noise` is not connected
539562
- [ ] VAE encode invocation (`[newmodel]_vae_encode.py`)
540563
- [ ] VAE decode invocation (`[newmodel]_vae_decode.py`)
541564
- [ ] Define output classes (e.g., `NewModelLoaderOutput`)
@@ -574,6 +597,11 @@ def get_noise_newmodel(
574597
dtype=dtype,
575598
)
576599

600+
# If the architecture supports external noise, also extend
601+
# invokeai/app/invocations/universal_noise.py when the tensor contract can be
602+
# represented there. Only create a dedicated noise invocation when
603+
# Universal Noise cannot express the architecture cleanly.
604+
577605
def pack_newmodel(x: torch.Tensor) -> torch.Tensor:
578606
"""Pack latents for transformer input.
579607
@@ -670,6 +698,13 @@ def denoise(
670698
return img
671699
```
672700

701+
If the architecture supports external noise, the denoise path should accept
702+
validated external noise without changing the legacy seed-driven behavior.
703+
Review img2img and inpaint preblend logic carefully when adding scheduler
704+
support. If the initial latent/noise mix is computed before
705+
`scheduler.set_timesteps()`, confirm that the preblend matches the
706+
scheduler's true first effective sigma or timestep.
707+
673708
### 5.3 Scheduler (if model-specific)
674709

675710
**File:** `invokeai/backend/[newmodel]/schedulers.py` or use existing
@@ -690,11 +725,16 @@ NEWMODEL_SCHEDULER_MAP = {
690725
### Backend Sampling and Denoise Checklist
691726

692727
- [ ] Noise generation (`get_noise_newmodel()`)
728+
- [ ] Extend `invokeai/app/invocations/universal_noise.py` when the
729+
architecture's noise tensor contract fits there
693730
- [ ] Pack/unpack functions (if transformer-based)
694731
- [ ] Schedule generation (`get_schedule_newmodel()`)
695732
- [ ] Position ID generation (if needed)
696733
- [ ] Implement denoise loop
734+
- [ ] Validate external noise shape and rank if the architecture supports it
697735
- [ ] Scheduler integration
736+
- [ ] Verify img2img and inpaint preblend parity with the scheduler's first
737+
effective timestep or sigma
698738
- [ ] Inpaint extension integration
699739
- [ ] Progress callbacks
700740

@@ -847,6 +887,11 @@ if (
847887
}
848888
```
849889

890+
If the architecture supports external noise, do not require generated
891+
workflows to connect it. Keep the denoise node backward compatible by
892+
leaving `noise` disconnected unless the workflow explicitly needs external
893+
noise.
894+
850895
### Frontend Graph Building Checklist
851896

852897
- [ ] Create graph builder (`buildNewModelGraph.ts`)
@@ -1259,6 +1304,11 @@ For a **minimal txt2img integration**, the following files are required:
12591304
3. `src/features/nodes/util/graph/generation/addInpaint.ts`
12601305
4. `src/features/nodes/util/graph/generation/addOutpaint.ts`
12611306

1307+
If the architecture supports external noise, also extend
1308+
`invokeai/app/invocations/universal_noise.py` when possible and keep the
1309+
denoise invocation's `noise` input optional so existing generated workflows
1310+
continue to work without modification.
1311+
12621312
---
12631313

12641314
## Reference: Existing Implementations

0 commit comments

Comments
 (0)