@melite/models-qwen-img-edit-21 0.1.4-alpha → 0.1.5-pre-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ ---
2
+ description: Qwen Image Edit 2.1 — the cached-reference edit lane over the shared 2.1 checkpoint; up to 16 images, edits at the stated target resolution.
3
+ kind: package-library
4
+ ---
5
+
6
+ # qwen-img-edit-21
7
+
8
+ qwen-img-edit-21 registers Qwen Image Edit 2.1 (form `form_edit`, label "Qwen Image Edit 2.1", genre `image`): the instruction-edit lane over the SAME physical 2.1 checkpoint as the t2i sibling. The lane: UNETLoader → QwenImage21Cache (the reference-prefix KV cache wrapped around the base unet — no separate edit weights exist) → TextEncodeQwenImage21 conditioning on the anchor images through its `image_N` autogrow slots (positive AND negative from ONE node) → EmptyLatentImage at the stated target resolution → KSampler → decode and save under `<run>/edit`. This is not the 2511 lane (VAEEncode-of-source at source resolution); the branch key is the lane's `text_encode_node`.
9
+
10
+ ## Params and law
11
+
12
+ Declared surface: `input_images`, `image1`, `image2`, `image3`, `reference_images`, `input_mask` (validation-only aliases the lane admits as wire keys), `prompt`, `negative_prompt`, `width`, `height`, `seed`, `steps`, `cfg`, `sampler`, `scheduler`. Laws: `width` and `height` ride together — an EmptyLatentImage takes both axes or neither, bounds 256–1536; `prompt` is required (the instruction, not a description of the image); an anchor is required — the ordered `input_images` list or a legacy spelling (`image1` / `image2` / `image3` / `reference_images`). The image cap is 16 total, anchor included (`max_images`).
13
+
14
+ Defaults: 25 steps, `euler` / `simple`, cfg 1, 1024×1024. Variants: `int8` (about 7.3 GB unet, about 24 GB VRAM, DEFAULT — provisioning required; without the 2.1 files the engine refuses loud) and `bf16` (about 14.2 GB, about 48 GB). Recipes: `remove_background` (the official template's canned prompt verbatim), `upstream_solvers`, `default`.
15
+
16
+ ## Mod surface
17
+
18
+ The DECL boards `mask` only: the bare flavor whose region contract rides the crop_stitch detailer over the cached model; automatic inpainting with a named field refuses — the LanPaint sampler is 2511's lane. `./mods/loras` is the seam with an empty row set (no first-party 2.1 adapters); nothing is fetched from either subpath.
19
+
20
+ ## Model Experience
21
+
22
+ ### Request context and condition
23
+
24
+ A run needs the anchor image and the edit instruction; target resolution and the sampling block ride defaults. Reference sets beyond the anchor go in `input_images` order, up to the 16-image ceiling.
25
+
26
+ #### What the model sees
27
+
28
+ The anchor and reference images (up to 16), the instruction, the target resolution, and the sampling block; the cached lane above.
29
+
30
+ #### Token effect
31
+
32
+ Zero-direct: an engine graph, not a token stream.
33
+
34
+ #### KV Cache effect
35
+
36
+ Within a run, the cache is the lane's point: QwenImage21Cache prefixes reference KV around the base unet, so the edit shares context with its anchors. Across runs it stays independent.
37
+
38
+ ## Known Limitations and Deferred Work
39
+
40
+ - **16-image ceiling** — beyond `max_images` the kit refuses loud; larger reference sets need trimming.
41
+ - **Resolution pair rides together** — a lone width or height is refused (the range rule made law).
42
+ - **No LoRA rows** — the `./mods/loras` seam is empty until first-party adapters land.
43
+ - **Live behavior unobserved** — not verified against the running model.
44
+
45
+ Exports: the plugin root, `./package.json`, `./mods/loras`, `./mods/mask`.
package/lib/actions.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { loadTextPair, decodeSave, stackLoras, orderedInputImages, regionSegs, detailerSave, isSpell, laneSampler, laneScheduler } from "@melite/comfyui-card-kit";
2
2
  import { fireLoras } from "@melite/comfyui-card-kit/guard";
3
- import { requireText } from "@melite/comfyui-card-kit/facts";
3
+ import { parseDiffusionLaneFacts, parseEditCap } from "@melite/comfyui-card-kit/facts";
4
4
  import { EDIT_DIFFUSION_BOUNDS, diffusionParamRows } from "@melite/comfyui-card-kit/paramRows";
5
5
  /** The edit facts door: the resolved lane_inputs parsed into the
6
6
  * closed shape. A drifted encoder class refuses here, never
@@ -10,23 +10,11 @@ import { EDIT_DIFFUSION_BOUNDS, diffusionParamRows } from "@melite/comfyui-card-
10
10
  * @param raw the resolved lane_inputs (family ⊕ variant)
11
11
  * @returns the typed facts the segment factory reads */
12
12
  export function parseEditFacts(raw) {
13
- const text = (key) => requireText(raw, key, "qwen-img-edit-21 edit");
14
- const node = raw.text_encode_node;
15
- if (node !== "TextEncodeQwenImage21") {
16
- throw new Error(`qwen-img-edit-21 edit: lane_inputs.text_encode_node = ${JSON.stringify(node)} — this family's edit action builds on TextEncodeQwenImage21`);
17
- }
18
- const cap = raw.max_images;
19
- if (typeof cap !== "number" || !Number.isFinite(cap) || cap < 1) {
20
- throw new Error(`qwen-img-edit-21 edit: lane_inputs.max_images = ${JSON.stringify(cap)} — a number ≥ 1 is required`);
21
- }
22
- return {
23
- unet: text("unet"),
24
- text_encoder: text("text_encoder"),
25
- vae: text("vae"),
26
- clip_type: text("clip_type"),
27
- text_encode_node: node,
28
- max_images: Math.floor(cap),
29
- };
13
+ // The checks + refusal prose live in the kit (J3 consolidation,
14
+
15
+ // into the family's own typed shape (B4).
16
+ const f = parseDiffusionLaneFacts(raw, "qwen-img-edit-21 edit", { key: "text_encode_node", node: "TextEncodeQwenImage21" }, false);
17
+ return { unet: f.unet, text_encoder: f.text_encoder, vae: f.vae, clip_type: f.clip_type, text_encode_node: f.node, max_images: parseEditCap(raw, "qwen-img-edit-21 edit") };
30
18
  }
31
19
  /** The edit action: the cached-reference lane — loaders → lora
32
20
  * stack → text pair → per-slot LoadImage anchors →
@@ -51,6 +39,7 @@ export const edit = {
51
39
  { name: "image3", type: "string" },
52
40
  { name: "reference_images", type: "image_list" },
53
41
  ...diffusionParamRows(EDIT_DIFFUSION_BOUNDS),
42
+ { name: "match_input_resolution", type: "boolean" },
54
43
  { name: "use_stock_nodes", type: "boolean" },
55
44
  { name: "double_reference", type: "boolean" },
56
45
  ],
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Plugin } from "@deepseek-ai/cordis";
2
- import type { ModelDecl } from "@melite/comfyui-cordis/model-contract";
2
+ import type { ModelDecl } from '@melite/comfyui-card-kit/model-contract';
3
3
  export declare const DECL: ModelDecl;
4
4
  export declare const family: Plugin;
5
5
  export { edit, parseEditFacts, type EditFacts, type EditParams } from "./actions.js";
6
+ export { family as default };
6
7
  //# sourceMappingURL=index.d.ts.map
package/lib/index.js CHANGED
@@ -22,16 +22,6 @@ export const DECL = {
22
22
  "width": 1024,
23
23
  "height": 1024
24
24
  },
25
- "prompt_guide": {
26
- "title": "Qwen Image 2.1 Edit Prompting Guide",
27
- "sections": [
28
- "GuideSection(label='Edit', body='State the change and the anchor: what stays identical. One edit per instruction.', list=[])",
29
- "GuideSection(label='CFG', body='Flow-matching lane: Guidance stays low (default 1.0).', list=[])",
30
- ],
31
- "label": "CFG",
32
- "body": "Flow-matching lane: keep Guidance low (default 1.0)",
33
- "list": []
34
- },
35
25
  "genre": "image",
36
26
  // THE LANE SURFACE (fix shapes 1+2 — the model is the author of
37
27
  // what it accepts; the card's visible_when guard list dies). The
@@ -41,7 +31,11 @@ export const DECL = {
41
31
  // reference_images, input_mask — hidden carriers, but a wire key
42
32
  // the lane admits, so the surface owns them) and THIS lane's
43
33
  // target resolution pair — the 2.1 edit lane samples an
44
- // EmptyLatentImage at stated size, so width/height are 2.1's.
34
+ // EmptyLatentImage at stated size, so width/height are 2.1's —
35
+ // plus the card-owned match toggle (ON by default: output matches
36
+ // the anchor; OFF: the custom pair rides). The toggle is admitted
37
+ // on BOTH edit lanes (the 2511 lane holds it as a no-op — it
38
+ // always edits at source resolution).
45
39
  // use_stock_nodes/double_reference are 2511's edit-tuning pair
46
40
  // and are NOT here: with a declared surface the bench-write door
47
41
  // refuses them on this family in the field's voice.
@@ -56,6 +50,7 @@ export const DECL = {
56
50
  "negative_prompt",
57
51
  "width",
58
52
  "height",
53
+ "match_input_resolution",
59
54
  "seed",
60
55
  "steps",
61
56
  "cfg",
@@ -149,11 +144,16 @@ export const DECL = {
149
144
  "size_bytes": 675509688
150
145
  }
151
146
  ],
147
+ // THE 2.1 MATRIX ON THE EDIT LANE (the variant-axis law,
148
+
149
+ // weights): Variant = Base/Turbo, Quant = int8_convrot/bf16;
150
+ // the quant cell owns its tier's unet spelling
151
+ // (CellRow.lane_inputs — the cell-lane cascade).
152
152
  "variants": [
153
153
  {
154
- "id": "int8",
155
- "label": "INT8 ConvRot",
156
- "hint": "The official template pick — 7.3GB unet, int8 convrot (DEFAULT). Requires provisioning: the 2.1 files are NOT on this box by default; without them the engine validation refuses LOUD (the honest face — the 2511 weights are what ship).",
154
+ "id": "base",
155
+ "label": "Base",
156
+ "hint": "The 2.1 base checkpoint on the edit lane — 25 steps (the official template's own count), both download tiers below, the cache-wrapped edit compose. Requires provisioning: the 2.1 files are NOT on this box by default; without them the engine validation refuses LOUD (the honest face — the 2511 weights are what ship).",
157
157
  "group": "Variant",
158
158
  "route": "qwen-img-edit-21",
159
159
  "order": 0,
@@ -171,30 +171,65 @@ export const DECL = {
171
171
  "dest_sub": "diffusion_models",
172
172
  "size_hint_gb": 7.3
173
173
  }
174
+ },
175
+ {
176
+ "quant": "bf16",
177
+ "label": "BF16",
178
+ "route": "qwen-img-edit-21-bf16",
179
+ "vram_peak_gb": 48,
180
+ "lane_inputs": {
181
+ "unet": "qwen_image_2.1_bf16.safetensors",
182
+ "text_encoder": "qwen3vl_8b_bf16.safetensors"
183
+ },
184
+ "artifact": {
185
+ "source_url": "https://huggingface.co/Comfy-Org/Qwen-Image-2.1/resolve/main/diffusion_models/qwen_image_2.1_bf16.safetensors",
186
+ "size_bytes": 14230280616,
187
+ "sha256": "89f4158d066cc33906a199fca85634f766892dd78f49b6698dabf187ac86c4bc",
188
+ "dest_root": "COMFYUI_ROOT",
189
+ "dest_sub": "diffusion_models",
190
+ "size_hint_gb": 14.3
191
+ }
174
192
  }
175
193
  ]
176
194
  },
177
195
  {
178
- "id": "bf16",
179
- "label": "BF16",
180
- "hint": "Full precision unet + bf16 encoder — 14.2GB unet, highest fidelity, most VRAM. Requires provisioning: the 2.1 files are NOT on this box by default; without them the engine validation refuses LOUD (the honest face — the 2511 weights are what ship).",
196
+ "id": "turbo",
197
+ "label": "Turbo",
198
+ "hint": "The distilled turbo weights on the edit lane — 8 steps instead of the base 25 (upstream: 'Turbo uses 8 steps'), both download tiers below, same cache-wrapped edit compose. Requires provisioning: the 2.1 turbo files are NOT on this box by default; without them the engine validation refuses LOUD (the honest face — the 2511 weights are what ship).",
181
199
  "group": "Variant",
182
- "route": "qwen-img-edit-21-bf16",
200
+ "route": "qwen-img-edit-21-turbo",
183
201
  "order": 1,
184
- "lane_inputs": {
185
- "unet": "qwen_image_2.1_bf16.safetensors",
186
- "text_encoder": "qwen3vl_8b_bf16.safetensors"
187
- },
202
+ "resolver_defaults": { "steps": 8 },
188
203
  "cells": [
204
+ {
205
+ "quant": "int8_convrot",
206
+ "label": "INT8 ConvRot",
207
+ "route": "qwen-img-edit-21-turbo-int8",
208
+ "vram_peak_gb": 24,
209
+ "lane_inputs": {
210
+ "unet": "qwen_image_2.1_turbo_int8_convrot.safetensors"
211
+ },
212
+ "artifact": {
213
+ "source_url": "https://huggingface.co/Comfy-Org/Qwen-Image-2.1/resolve/main/diffusion_models/qwen_image_2.1_turbo_int8_convrot.safetensors",
214
+ "size_bytes": 7256787829,
215
+ "sha256": "8144a0182964aa8403ad6dbc011db837304167d235161b9aabaa8532e967314c",
216
+ "dest_root": "COMFYUI_ROOT",
217
+ "dest_sub": "diffusion_models",
218
+ "size_hint_gb": 7.3
219
+ }
220
+ },
189
221
  {
190
222
  "quant": "bf16",
191
223
  "label": "BF16",
192
- "route": "qwen-img-edit-21-bf16",
224
+ "route": "qwen-img-edit-21-turbo-bf16",
193
225
  "vram_peak_gb": 48,
226
+ "lane_inputs": {
227
+ "unet": "qwen_image_2.1_turbo_bf16.safetensors"
228
+ },
194
229
  "artifact": {
195
- "source_url": "https://huggingface.co/Comfy-Org/Qwen-Image-2.1/resolve/main/diffusion_models/qwen_image_2.1_bf16.safetensors",
230
+ "source_url": "https://huggingface.co/Comfy-Org/Qwen-Image-2.1/resolve/main/diffusion_models/qwen_image_2.1_turbo_bf16.safetensors",
196
231
  "size_bytes": 14230280616,
197
- "sha256": "89f4158d066cc33906a199fca85634f766892dd78f49b6698dabf187ac86c4bc",
232
+ "sha256": "8b74237a7f505773e2c66726fa696444b4c704585c93a347cff9044937f7914a",
198
233
  "dest_root": "COMFYUI_ROOT",
199
234
  "dest_sub": "diffusion_models",
200
235
  "size_hint_gb": 14.3
@@ -275,4 +310,9 @@ export const family = {
275
310
  // The family's offered actions (the atom vocabulary): the edit
276
311
  // action the image-edit card binds.
277
312
  export { edit, parseEditFacts } from "./actions.js";
313
+ // The Loader-row face: DSH loads plugin rows by bare specifier and
314
+ // unwraps `exports.default ?? exports` (vendor/cordis_plugins/loader
315
+ // unwrapExports) — the default export IS the row-loadable plugin. The
316
+ // named `family` stays the library face (engine loadFamilies).
317
+ export { family as default };
278
318
  //# sourceMappingURL=index.js.map
@@ -1,3 +1,3 @@
1
- import type { Context } from '@deepseek-ai/cordis';
1
+ import type { Context } from '@melite/comfyui-card-kit/card-definition';
2
2
  export declare const inject: string[];
3
3
  export declare function apply(ctx: Context): void;
@@ -1,3 +1,3 @@
1
- import type { Context } from '@deepseek-ai/cordis';
1
+ import type { Context } from '@melite/comfyui-card-kit/card-definition';
2
2
  export declare const inject: string[];
3
3
  export declare function apply(ctx: Context): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melite/models-qwen-img-edit-21",
3
- "version": "0.1.4-alpha",
3
+ "version": "0.1.5-pre-alpha",
4
4
  "license": "SSPL-1.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -19,8 +19,7 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@melite/comfyui-cordis": "0.1.4-alpha",
23
- "@melite/comfyui-card-kit": "0.1.4-alpha"
22
+ "@melite/comfyui-card-kit": "0.1.5-pre-alpha"
24
23
  },
25
24
  "devDependencies": {
26
25
  "@types/node": "^22.20.0",