Skip to content

feat(generate): restore model-family tuning, Krea-2 style reference and output filters - #9809

Open
joshistoast wants to merge 6 commits into
mainfrom
feat/parity-model-tuning
Open

joshistoast wants to merge 6 commits into
mainfrom
feat/parity-model-tuning

Conversation

@joshistoast

@joshistoast joshistoast commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Model-family tuning controls and two output options from the legacy frontend were missing from webv2 Generate and Canvas.

  • FLUX.1 DyPE: preset (off, auto, area, 4K optimized, manual) with scale and exponent under Manual, sent to the FLUX denoise node only when set.
  • Z-Image: an optional timestep shift override and the seed-variance enhancer (strength and randomized channels), inserted between the positive conditioning and its collector exactly as legacy wired it.
  • Qwen-Image: timestep shift override and encoder quantization (none, int8, nf4) applied to both text encoders.
  • Krea-2 style reference: a new reference-image kind with a 0–2 style strength, compiled to krea2_style_reference fed by the (cropped) image and the model VAE and connected to denoising; strength 0 bypasses it. Krea-2 now advertises one reference image on the backend.
  • NSFW checker and invisible watermark: two project settings (Settings → Project, off by default) that chain img_nsfw / img_watermark after the final image of every built-in Generate and Canvas family, keeping the output flags and metadata on the last node.

Every control appears only for its model family; graphs at defaults are structurally unchanged. New settings are emitted in metadata and restored by Recall All and Remix.

Related Issues / Discussions

Follows up the legacy parity audit behind #9803.

QA Instructions

From invokeai/frontend/webv2 (Node 22.14, pnpm 10.12): pnpm check:release passes. Python: pytest tests/backend/architectures tests/app/invocations/test_frontend_graph_node_types.py passes.

The mock backend's representative profile has only SD 1.x main models, so the per-family controls are covered by browser tests (GenerateRenderSection, GenerateAdvancedFields) and compile tests. On the mock: open a project → Settings → Project: toggle NSFW checker and Invisible watermark; the Generate graph preview then ends … → img_nsfw → img_watermark. With a real install: select a FLUX.1 model → Render → DyPE = Manual shows λs/λt; Qwen-Image → Advanced → Encoder quantization; Z-Image → Shift override and Seed variance; Krea-2 → add a reference image → Style strength. Generate, then Recall All restores each.

Review

No material findings. Reviewers confirmed every field against the backend invocations and the legacy builders, including that DyPE scale/exponent are read only under the manual preset. Follow-ups applied from review: tests for the shift override being turned off, watermark-only Canvas outpaint to a staging destination, the project setting flowing through the normal invoke path, and recall of a style reference whose image is missing; a single "Shift" label; docs naming.

Limitation: core_metadata at defaults now records z_image_shift: 'auto', qwen_image_shift: 'auto', qwen_image_quantization: 'none' and the seed-variance keys, matching legacy; execution nodes are unchanged.

Compatibility / Rollout

New generate settings and the two project settings are optional with defaults and normalize on load; no project-document migration. The Krea-2 architecture definition now reports max_reference_images=1 (capabilities fixture regenerated). No API changes.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…nd output filters

FLUX.1 DyPE, Z-Image shift and seed variance, and Qwen-Image shift and encoder quantization reach their denoise and encoder nodes, with metadata and recall.
Krea-2 declares a single style reference; the Generate panel compiles it to krea2_style_reference for Generate and every Canvas mode.
Project settings add the NSFW checker and invisible watermark, chained after every locally decoded output with the result flags and metadata on the final node.
…hift field

The shift override uses the sibling slider pattern so its label is not repeated; its off switch is tested to hand the shift back to the node.
Canvas outpaint, the project invoke path, and a Krea-2 record without a reference image are covered; the defaults test names the node-type contract as the real guard.
@github-actions github-actions Bot added python PRs that change python files backend PRs that change backend files frontend PRs that change frontend files python-tests PRs that change python tests docs PRs that change docs labels Oct 11, 2026
@Pfannkuchensack

Copy link
Copy Markdown
Member

Thanks for this. I ran the PR head (013a70e831) against a real backend on an RTX 4090 with Z-Image Turbo, FLUX.1 dev, Krea-2 Turbo and Qwen-Image 2512 installed.

These compiled to the expected graphs:

  • Z-Image shift override and seed variance
  • FLUX.1 DyPE
  • a Krea-2 style reference in Generate
  • Qwen encoder quantization
  • the watermark filter

The runs I queued finished with metadata and board on the final node. Three things should be fixed before merge.

Should fix

1. Recall All / Remix doesn't restore DyPE "Off"

getFluxDypeFields (graph.ts:606) returns {} for off, and the same object is spread into core_metadata. So a webv2 image generated with DyPE off has no dype_preset. getMetadataModelTuning (imageRecall.ts:460) only patches when the key is present, and model defaults don't reset the preset.

Repro:

  1. Generate a FLUX.1 image with DyPE Off.
  2. Set DyPE to 4K optimized.
  3. Run Recall All on that image.

The seed comes back, but DyPE stays on 4K optimized.

Legacy always records dype_preset, including 'off' (webv1 buildFLUXGraph.ts:371), and Z-Image and Qwen here always record a shift. graph.test.ts:1313 currently asserts that the key is omitted, while flux-1.mdx:99 says Off is restored. Suggested fix: record dype_preset: 'off' in the metadata only, and leave the denoise node untouched.

The shift has the same gap:

  • Legacy Qwen images write qwen_image_shift: null for auto.
  • Images from before this PR have no z_image_shift or qwen_image_shift key.

getTimestepShiftPatch (imageRecall.ts:437) returns undefined for both, so a current override survives Recall All. Treating null as auto would fix the first case. A missing key on an image of the same family arguably should mean auto too.

The recall tests all start from default tuning (createValues()), so a value left over from the current settings can't show up. A round trip into non-default current values would catch this.

2. The watermark makes masked Canvas output fully opaque

addOutputPostprocessors runs after the mask tail (compileCanvasGraph.ts:672). Output only masked regions is on by default (canvasCompositing.ts:58), so the final node is apply_mask_to_image, which carries the mask in alpha. InvisibleWatermark.add_watermark (invokeai/backend/image_util/invisible_watermark.py) does convert("RGB") → convert("RGBA"), which sets alpha to 255 everywhere. I checked by calling it on a 512² RGBA image with only the center opaque: the 196,608 transparent pixels went to 0.

With the watermark on, inpaint and outpaint therefore stage the whole opaque bbox instead of only the masked region. Legacy chains the filter the same way, so the bug is inherited, but this PR puts it on webv2's default Canvas path. The simplest fix is to keep the source alpha in add_watermark. Applying the watermark before masking would also work.

3. The reference-image type name differs from legacy

webv2 uses krea2_style_reference_image (types.ts:144, settings.ts:275). Legacy uses krea2_reference_image with the same { image, styleStrength } shape (webv1 controlLayers/store/types.ts:445); every other webv2 reference type matches its legacy name. As a result, legacy recall of a webv2 Krea-2 image fails to parse ref_images. Renaming now costs nothing. After a release, persisted project documents and image metadata would need an alias.

Smaller issues

  • A Qwen-Image shift of 0 crashes the node. The field allows 0 (GenerateRenderSection.tsx:450) and normalization accepts it, but qwen_image_denoise.py:319 does math.log(self.shift) and fails with "math domain error". Legacy behaves the same, but a minimum above 0 would avoid a failed queue item. Z-Image doesn't crash on 0, but gets a degenerate schedule.
  • The filter copy overstates where the filters apply. generate-panel.mdx:75 says "apply to every image the project generates", and en.json:1956/1960 say "every generated image". Upscale, workflow runs and external models are not filtered. Since this is a safety setting, the copy should say Generate and Canvas.
  • Switching to Krea-2 with several references: getCompatibleReferenceImages converts all of them. Validation then blocks Invoke with "Generate supports at most 1 reference images…" (baseGenerationPolicies.ts:1600; the singular case also needs fixing). Legacy kept the first reference and warned. Truncating, or a clearer message, would help.
  • The filter flags are optional in the feature contract (contracts.ts:8, :10). Workbench passes its ProjectSettings structurally, so renaming useNsfwChecker or useWatermark there would turn the filters off without a type error. Making the fields required only touches test fixtures.
  • Copy and i18n:
    • The shift help text "Lightning LoRAs expect 3" (en.json:2506) also shows for Z-Image.
    • Z-Image reuses the krea2SeedVariance* keys.
    • generate-panel.mdx:55 mentions "CFG (Low)" for Wan. That isn't a UI label, and Wan has no Local Models page.
  • Encoder quantization: the quantized path bypasses the model cache and loads the Qwen VL encoder from disk on every generation, twice with CFG (qwen_image_text_encoder.py:386). The help text and docs mention only the VRAM saving and should mention this cost as well.
  • Tests:
    • The commit mapping of the Z-Image seed-variance fields isn't covered. It is a copy of the Krea-2 adapter, so a wrong key would pass.
    • The comment at graph.test.ts:1450 says the snapshot guards the default recipe, but byBase now merges the tuned compilation too.
  • Nit: the "take over canvas_output" logic now exists three times (twice in compileCanvasGraph.ts, once in graphBuilder.ts). The renamed node is always called canvas_decode, even when it is img_resize or invokeai_img_blend.

Verified fine

  • Z-Image: the enhancer wiring matches legacy (seed → enhancer, pos_cond → enhancer → pos_cond_collect). The shift reaches z_image_denoise and qwen_image_denoise only when set.
  • DyPE: scale and exponent are sent only under Manual, and the presets match the backend Literal.
  • Krea-2 style reference: it gets the cropped image and the same size as the denoise node. A Generate run completed at 1024². Canvas uses the processing size for both nodes.
  • Filters: the final node keeps canvas_output, is_intermediate, board and metadata. Nothing is applied twice, and the external and PiD paths are fine.
  • Settings and capabilities: the new settings normalize on load without a migration. max_reference_images=1 is read only by webv2, through the capabilities route.
  • Tests: CI is green. Locally, the touched unit tests (914) and tests/backend/architectures plus test_frontend_graph_node_types.py (704) pass.

Not exercised: an actual NSFW-checker run and a real Canvas inpaint. Finding 2 rests on the backend function and the graph order.

@blacksmith-sh

This comment has been minimized.

… masks transparent

FLUX records DyPE off, and recall restores defaults a same-family record omits; the watermark keeps source alpha.
Krea-2 references use the legacy type name, model switches turn off references over the limit, and shift stays within 0.1-10.
Filter flags are required, Canvas output takeovers share one helper, and filter, shift and quantization copy is corrected.
…d locale growth

Request counts, chunk sets and timing medians are unchanged; only byte fields move.
About 43 KB of editor script comes from this branch's model-family tuning and filters, and the rest from its new strings.

Copy link
Copy Markdown
Collaborator Author

@Pfannkuchensack ready for a re-review

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend PRs that change backend files docs PRs that change docs frontend PRs that change frontend files python PRs that change python files python-tests PRs that change python tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants