From f6663444f5bcd95b428efa76afc09dc8d663e014 Mon Sep 17 00:00:00 2001 From: Towsty Date: Sat, 29 Aug 2026 07:05:01 -0500 Subject: [PATCH] Add Krea as a second Image v2 Generate engine beside Flux. Generate can queue krea_v2_generate.json without sharing Klein encoders or falling back if Krea assets are missing. Co-authored-by: Cursor --- docs/image-v2.md | 51 +++--- nuxt.config.ts | 4 +- pages/index.vue | 171 ++++++++++++------- server/api/v2/generate.post.ts | 42 ++++- server/assets/krea_v2_generate.json | 81 +++++++++ server/utils/generationPresets.ts | 9 +- server/utils/imageChainV2.ts | 22 ++- server/utils/imageWorkflowV2.ts | 252 +++++++++++++++++++++++++++- server/utils/studioQueue.ts | 2 + utils/imageV2.ts | 28 ++++ utils/libraryGeneration.ts | 13 +- 11 files changed, 567 insertions(+), 108 deletions(-) create mode 100644 server/assets/krea_v2_generate.json diff --git a/docs/image-v2.md b/docs/image-v2.md index 2572d90..ac704e7 100644 --- a/docs/image-v2.md +++ b/docs/image-v2.md @@ -6,12 +6,12 @@ Image edit (v1) and Image v2 are siblings. v1 is unchanged. Do not mute-fix `wor |---|---|---| | Tab | Image edit | Image v2 | | Route | `POST /api/edit` | `POST /api/v2/generate` and `POST /v2/generate` | -| Graphs | One file, prune unused branch | `klein_v2_edit.json`, `klein_v2_compose.json`, `klein_v2_refine.json`, or `klein_v2_generate.json` | +| Graphs | One file, prune unused branch | `klein_v2_edit.json`, `klein_v2_compose.json`, `klein_v2_refine.json`, `klein_v2_generate.json`, or `krea_v2_generate.json` | | Second still | Optional; switches branch | Compose only; required. Mismatch is a 400 | | Mask | — | Refine only; required. Missing mask is a 400, not an Edit fallback | | Input still | Required | Edit / Compose / Refine required. Generate ignores stills | -| LoRAs | Optional user picker | SNOFS + Consistency, four strengths. Generate defaults Consistency off | -| Defaults | 20 steps, CFG 1 | 24 steps, CFG 4 (turbo: 8 / 1) | +| LoRAs | Optional user picker | Concept LoRA + Consistency (Flux). Generate Consistency off. Krea Generate has no Consistency | +| Defaults | 20 steps, CFG 1 | Flux: 24 steps, CFG 4 (turbo: 8 / 1). Krea Generate: 8 steps, CFG 1 | Poll v2 jobs the same way as v1: `GET /api/generate/{id}/stream` and `GET /api/generate/{id}`. @@ -20,7 +20,7 @@ Poll v2 jobs the same way as v1: `GET /api/generate/{id}/stream` and `GET /api/g - **edit** — one image + text. Task is `scene`. Sending `image_b` is rejected. Use this to change lighting, background, or the whole frame from a single still. - **compose** — two images + text. `image_b` is required. No silent one-image fallback. Use this to lock a person from A and pull a face or outfit from B. - **refine** — one canvas + a painted mask + text. `image_a` and `mask` are required. `image_b` is ignored. Prompt only what should change inside the mask. Strength is denoise in that region (not CFG). Use this for face / hand / chest fixes without opening Comfy. -- **generate** — text only. No `image_a`, `image_b`, or mask. Size is width × height (768 / 1024 / 1280), not megapixels from a still. Use this to make a new Klein still from a prompt. +- **generate** — text only. No `image_a`, `image_b`, or mask. Size is width × height (768 / 1024 / 1280), not megapixels from a still. Engine is Flux (`klein_v2_generate.json`) or Krea (`krea_v2_generate.json`). Edit / Compose / Refine stay Flux. Compose tasks: @@ -35,7 +35,8 @@ Dummy tests: - Compose + two unrelated photos + “keep everything the same” must change the output. If it matches old v1 gens, still B is not connected. - Refine + last good gen + mask on the left breast + “red X painted on the left breast” at strength 0.4. Pass = X on the breast, face/pose/background stay. Fail = whole image regenerates, or nothing changes. -- Generate + “a red cube on a white table, studio light” and no stills. Pass = a new image of that. Fail = missing-image error, or the output equals the last Edit/Compose still. +- Generate + Flux + “a red cube on a white table, studio light” and no stills. Pass = a new image of that. Fail = missing-image error, or the output equals the last Edit/Compose still. +- Generate + Krea + the same prompt. Pass = `krea_v2_generate.json` is queued. Fail = Flux graph runs under a Krea label, or a missing Krea model falls back to Flux. ## Nodes the mapper patches @@ -45,7 +46,7 @@ Edit and Compose: |------|------| | `1` | Load Image A (`inputs.image`) | | `2` / `23` | Scale to MP, lanczos (`megapixels`) | -| `7` | SNOFS LoRA (`lora_name`, `strength_model`, `strength_clip`) | +| `7` | Concept LoRA (`lora_name`, `strength_model`, `strength_clip`) | | `8` | Consistency LoRA (`lora_name`, `strength_model`, `strength_clip`) | | `9` | Positive `CLIPTextEncode.text` (role header + user prompt on Compose) | | `10` | Negative `CLIPTextEncode.text` | @@ -75,16 +76,29 @@ Refine only (`klein_v2_refine.json`): | `17` | `BasicScheduler.denoise` — this is Strength, not CFG | | `19` | Sampler starts from the masked canvas latent, not an empty Flux2 latent | -Generate only (`klein_v2_generate.json`): +Generate Flux (`klein_v2_generate.json`): | Node | Role | |------|------| | `14` | `EmptyFlux2LatentImage` at request width × height | | `17` | `Flux2Scheduler` steps + the same size | -| `7` | SNOFS LoRA | +| `7` | Concept LoRA (Klein file) | | `8` | Consistency LoRA — removed at runtime when both strengths are 0 | | `9` / `10` | Prompt / negative from the API (no leftover widget text) | +Generate Krea (`krea_v2_generate.json`): + +| Node | Role | +|------|------| +| `4` | `UNETLoader` — `krea2_turbo_mxfp8`, then `nvfp4`, then `fp8_scaled` | +| `5` | `CLIPLoader` type `krea2` — `qwen3vl_4b_fp8_scaled.safetensors` | +| `6` | `VAELoader` — `qwen_image_vae.safetensors` | +| `14` | `EmptyLatentImage` at request width × height | +| `15` | `KSampler` euler / simple, default 8 steps, CFG 1 | +| `9` / `10` | Prompt / negative from the API | + +Krea does not load Klein UNET, Klein CLIP, Klein VAE, Klein Consistency, or the Klein concept LoRA. Missing Krea diffusion / text encoder / VAE fails the job. Concept LoRA on Krea is off unless `NUXT_KREA_CONCEPT_LORA` is set. + There is no `LoadImage`. If the executed Generate graph has a required LoadImage, the job fails. v1 node 145 (muted T2I with a baked prompt) is not used. If `image_b` is sent on Edit/Compose and the executed graph has fewer than two `LoadImage` nodes, the job fails. If Refine runs without a mask input or without denoise on the scheduler, the job fails. @@ -93,10 +107,10 @@ If `image_b` is sent on Edit/Compose and the executed graph has fewer than two ` | Slider | Default | |--------|---------| -| SNOFS Model | 0.65 | -| SNOFS CLIP | 0.35 | -| Consistency Model | 0.70 | -| Consistency CLIP | 0.70 | +| Concept LoRA — Model | 0.65 on Flux. 0 on Krea | +| Concept LoRA — CLIP | 0.35 on Flux. 0 on Krea | +| Consistency — Model | 0.70 on Flux Edit/Compose/Refine. Off on Generate | +| Consistency — CLIP | 0.70 on Flux Edit/Compose/Refine. Off on Generate | | Steps | 24 (turbo 8) | | CFG | 4 (turbo 1) | | Scale to MP | 1.0 lanczos | @@ -108,17 +122,16 @@ If `image_b` is sent on Edit/Compose and the executed graph has fewer than two ` | Preset | Strength | SNOFS model / CLIP | Consistency | Use for | |--------|----------|--------------------|-------------|---------| | Face | 0.25–0.35 (chip 0.28) | 0.55 / 0.25 | 0.75 / 0.80 | Glasses, cheeks, jaw. Keep the rest of the canvas. | -| Hand / chest | 0.35–0.45 (chip 0.40) | 0.65 / 0.30 | 0.70 / 0.70 | Hands, breasts, local contact. Dummy test uses 0.40. | +| Hand / chest | 0.35–0.45 (chip 0.40) | 0.65 / 0.30 | 0.70 / 0.70 | Hands or chest, local contact. Dummy test uses 0.40. | | Heavy | 0.55 | current sliders | current sliders | Stubborn region that barely moved at 0.40. | Do not raise Strength to rewrite the whole frame. If the face or background moves, the mask is too big or Strength is too high. -### NSFW identity vs outfit swap - -- **identity / face_lock** — keep Consistency at 0.70 / 0.70 so the face from A (identity) or B (face_lock) holds. Do not drop SNOFS CLIP below ~0.30 or the skin/body read falls apart. -- **outfit** — keep SNOFS Model ~0.65 so cloth reads; do not raise it past ~0.85 or it starts rewriting the body from A. Consistency stays at 0.70 so the person in A does not become the person in B. +- **identity / face_lock** — keep Consistency at 0.70 / 0.70 so the face from A (identity) or B (face_lock) holds. Do not drop Concept LoRA CLIP below ~0.30 or the skin/body read falls apart. +- **outfit** — keep Concept LoRA Model ~0.65 so cloth reads; do not raise it past ~0.85 or it starts rewriting the body from A. Consistency stays at 0.70 so the person in A does not become the person in B. - **scene / edit** — start at the table defaults. Turbo is for drafts only. - **refine** — prompt only the masked change. Face vs hand/chest chips set Strength and LoRAs together. -- **generate** — SFW scene = SNOFS 0 / 0. SNOF T2I = SNOFS 0.65 / 0.35. Consistency stays 0. Turbo is 8 steps / CFG 1. +- **generate / Flux** — Concept LoRA defaults 0.65 / 0.35. Consistency stays 0. Turbo is 8 steps / CFG 1. +- **generate / Krea** — 8 steps, CFG 1, no Consistency. Concept LoRA stays 0 unless a Krea concept file is configured. -Presets on this tab store sliders + mode + task (+ Strength when Refine, + size when Generate). They do not load v1 LoRA stacks or a cached latent. +Presets on this tab store sliders + mode + task + engine (+ Strength when Refine, + size when Generate). They do not load v1 LoRA stacks or a cached latent. Missing engine on an old preset is Flux. diff --git a/nuxt.config.ts b/nuxt.config.ts index 37388f4..a4ea7b3 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -53,12 +53,14 @@ export default defineNuxtConfig({ authPassword: process.env.AUTH_PASSWORD || '', purgeComfyOutputs: process.env.PURGE_COMFY_OUTPUTS !== 'false', comfyFilenamePrefix: process.env.COMFY_FILENAME_PREFIX || 'video/MiniMax_H3', + kreaConceptLora: process.env.NUXT_KREA_CONCEPT_LORA || '', public: { authEnabled: process.env.NUXT_PUBLIC_AUTH_ENABLED === 'true', authMode: process.env.NUXT_PUBLIC_AUTH_MODE || process.env.AUTH_MODE || '', instanceName: process.env.NUXT_PUBLIC_INSTANCE_NAME || 'AIGen', // NUXT_PUBLIC_STUDIO=xaigen on the xAIGen Coolify app (LTX Pink Cherry + xAIGen-prefixed LoRAs) - studio: process.env.NUXT_PUBLIC_STUDIO || '' + studio: process.env.NUXT_PUBLIC_STUDIO || '', + kreaConceptLora: process.env.NUXT_PUBLIC_KREA_CONCEPT_LORA || process.env.NUXT_KREA_CONCEPT_LORA || '' } }, nitro: { diff --git a/pages/index.vue b/pages/index.vue index 4f57496..281f969 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -66,7 +66,7 @@

Input

{{ studioMode === 'editv2' ? (v2Mode === 'generate' - ? 'Generate: no reference image. Prompt only. Klein 9B Base text-to-image.' + ? 'Generate: no reference image. Prompt only.' : v2Mode === 'refine' ? 'Refine: paint a mask on Still A. Prompt only what should change in the painted area. Strength is denoise in that region.' : v2Mode === 'compose' @@ -710,38 +710,44 @@ />

-

Klein v2 · 9B Base · euler. Edit, Compose, Refine, and Generate are separate graphs. Shares the desktop GPU with video.

-
-

Size · near 1 MP

-
- +

Edit, Compose, Refine, and Generate are separate graphs. Shares the desktop GPU with video.

+
+
+

Engine

+
+ + +
+

Flux is the current edit and compose engine. Krea is a separate photograph engine.

-
- - +
+

Size · near 1 MP

+
+ +
@@ -788,19 +794,36 @@
-
-