Keep the Klein Concept LoRA on xAIGen only.

AIGen hides the sliders, ignores snofs strengths, and strips the SNOFS node so that file cannot be queued from the public studio.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-29 07:08:33 -05:00
co-authored by Cursor
parent f6663444f5
commit ab5477c999
8 changed files with 108 additions and 68 deletions
+5 -5
View File
@@ -10,7 +10,7 @@ Image edit (v1) and Image v2 are siblings. v1 is unchanged. Do not mute-fix `wor
| Second still | Optional; switches branch | Compose only; required. Mismatch is a 400 | | 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 | | Mask | — | Refine only; required. Missing mask is a 400, not an Edit fallback |
| Input still | Required | Edit / Compose / Refine required. Generate ignores stills | | Input still | Required | Edit / Compose / Refine required. Generate ignores stills |
| LoRAs | Optional user picker | Concept LoRA + Consistency (Flux). Generate Consistency off. Krea Generate has no Consistency | | LoRAs | Optional user picker | Consistency (Flux). Concept LoRA on xAIGen only. 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 | | 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}`. Poll v2 jobs the same way as v1: `GET /api/generate/{id}/stream` and `GET /api/generate/{id}`.
@@ -97,7 +97,7 @@ Generate Krea (`krea_v2_generate.json`):
| `15` | `KSampler` euler / simple, default 8 steps, CFG 1 | | `15` | `KSampler` euler / simple, default 8 steps, CFG 1 |
| `9` / `10` | Prompt / negative from the API | | `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. 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 (`klein_snofs_v1_4`) is xAIGen only; AIGen bypasses that node. On Krea it stays off unless `NUXT_KREA_CONCEPT_LORA` is set on xAIGen.
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. 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.
@@ -107,8 +107,8 @@ If `image_b` is sent on Edit/Compose and the executed graph has fewer than two `
| Slider | Default | | Slider | Default |
|--------|---------| |--------|---------|
| Concept LoRA — Model | 0.65 on Flux. 0 on Krea | | Concept LoRA — Model | xAIGen Flux: 0.65. Off on AIGen. 0 on Krea |
| Concept LoRA — CLIP | 0.35 on Flux. 0 on Krea | | Concept LoRA — CLIP | xAIGen Flux: 0.35. Off on AIGen. 0 on Krea |
| Consistency — Model | 0.70 on Flux Edit/Compose/Refine. Off on Generate | | Consistency — Model | 0.70 on Flux Edit/Compose/Refine. Off on Generate |
| Consistency — CLIP | 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) | | Steps | 24 (turbo 8) |
@@ -131,7 +131,7 @@ Do not raise Strength to rewrite the whole frame. If the face or background move
- **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. - **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. - **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. - **refine** — prompt only the masked change. Face vs hand/chest chips set Strength and LoRAs together.
- **generate / Flux** — Concept LoRA defaults 0.65 / 0.35. Consistency stays 0. Turbo is 8 steps / CFG 1. - **generate / Flux** — On xAIGen, Concept LoRA defaults 0.65 / 0.35. On AIGen the node is not loaded. 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. - **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 + 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. 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.
+25 -10
View File
@@ -793,6 +793,7 @@
</div> </div>
</div> </div>
<div class="grid grid-cols-2 gap-3"> <div class="grid grid-cols-2 gap-3">
<template v-if="v2ConceptLoraEnabled">
<label class="block text-sm"> <label class="block text-sm">
<span class="mb-1 block text-zinc-400">Concept LoRA — Model</span> <span class="mb-1 block text-zinc-400">Concept LoRA — Model</span>
<input <input
@@ -818,6 +819,7 @@
> >
</label> </label>
<p v-if="v2Engine === 'krea' && !kreaConceptConfigured" class="col-span-2 text-xs text-zinc-500">No Krea concept LoRA is configured.</p> <p v-if="v2Engine === 'krea' && !kreaConceptConfigured" class="col-span-2 text-xs text-zinc-500">No Krea concept LoRA is configured.</p>
</template>
<label v-if="v2Mode !== 'generate' && v2Engine !== 'krea'" class="block text-sm"> <label v-if="v2Mode !== 'generate' && v2Engine !== 'krea'" class="block text-sm">
<span class="mb-1 block text-zinc-400">Consistency — Model</span> <span class="mb-1 block text-zinc-400">Consistency — Model</span>
<input v-model.number="v2ConsistencyModel" type="number" min="0" max="2" step="0.05" class="w-full rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm outline-none ring-amber-300/40 focus:ring-2"> <input v-model.number="v2ConsistencyModel" type="number" min="0" max="2" step="0.05" class="w-full rounded-xl border border-white/10 bg-zinc-950 px-3 py-2 text-sm outline-none ring-amber-300/40 focus:ring-2">
@@ -2275,8 +2277,8 @@ const v2Engine = ref<ImageV2Engine>('flux')
const v2Task = ref<ImageV2Task>('scene') const v2Task = ref<ImageV2Task>('scene')
const kreaConceptConfigured = computed(() => Boolean(String(runtimeConfig.public.kreaConceptLora || IMAGE_V2_KREA_CONCEPT_LORA || '').trim())) const kreaConceptConfigured = computed(() => Boolean(String(runtimeConfig.public.kreaConceptLora || IMAGE_V2_KREA_CONCEPT_LORA || '').trim()))
const v2Negative = ref('') const v2Negative = ref('')
const v2SnofsModel = ref(IMAGE_V2_SNOFS_MODEL) const v2SnofsModel = ref(isXaigenStudio(runtimeConfig.public) ? IMAGE_V2_SNOFS_MODEL : 0)
const v2SnofsClip = ref(IMAGE_V2_SNOFS_CLIP) const v2SnofsClip = ref(isXaigenStudio(runtimeConfig.public) ? IMAGE_V2_SNOFS_CLIP : 0)
const v2ConsistencyModel = ref(IMAGE_V2_CONSISTENCY_MODEL) const v2ConsistencyModel = ref(IMAGE_V2_CONSISTENCY_MODEL)
const v2ConsistencyClip = ref(IMAGE_V2_CONSISTENCY_CLIP) const v2ConsistencyClip = ref(IMAGE_V2_CONSISTENCY_CLIP)
const v2Steps = ref(IMAGE_V2_STEPS_DEFAULT) const v2Steps = ref(IMAGE_V2_STEPS_DEFAULT)
@@ -2327,6 +2329,10 @@ const showPrivacyToggles = computed(() => isXaigenStudio({
studio: runtimeConfig.public.studio, studio: runtimeConfig.public.studio,
instanceName: instanceName.value instanceName: instanceName.value
})) }))
const v2ConceptLoraEnabled = computed(() => isXaigenStudio({
studio: runtimeConfig.public.studio,
instanceName: instanceName.value
}))
const folders = ref<LibraryFolder[]>([]) const folders = ref<LibraryFolder[]>([])
const clips = ref<LibraryClip[]>([]) const clips = ref<LibraryClip[]>([])
const stills = ref<LibraryStill[]>([]) const stills = ref<LibraryStill[]>([])
@@ -3500,8 +3506,8 @@ function currentPresetSnapshot() {
engine: v2Mode.value === 'generate' ? v2Engine.value : 'flux', engine: v2Mode.value === 'generate' ? v2Engine.value : 'flux',
task: v2Mode.value === 'compose' ? v2Task.value : v2Mode.value === 'refine' ? 'refine' : v2Mode.value === 'generate' ? 't2i' : 'scene', task: v2Mode.value === 'compose' ? v2Task.value : v2Mode.value === 'refine' ? 'refine' : v2Mode.value === 'generate' ? 't2i' : 'scene',
negative: v2Negative.value, negative: v2Negative.value,
snofsModel: clampImageV2Strength(v2SnofsModel.value, IMAGE_V2_SNOFS_MODEL), snofsModel: v2ConceptLoraEnabled.value ? clampImageV2Strength(v2SnofsModel.value, IMAGE_V2_SNOFS_MODEL) : 0,
snofsClip: clampImageV2Strength(v2SnofsClip.value, IMAGE_V2_SNOFS_CLIP), snofsClip: v2ConceptLoraEnabled.value ? clampImageV2Strength(v2SnofsClip.value, IMAGE_V2_SNOFS_CLIP) : 0,
consistencyModel: v2Mode.value === 'generate' ? 0 : clampImageV2Strength(v2ConsistencyModel.value, IMAGE_V2_CONSISTENCY_MODEL), consistencyModel: v2Mode.value === 'generate' ? 0 : clampImageV2Strength(v2ConsistencyModel.value, IMAGE_V2_CONSISTENCY_MODEL),
consistencyClip: v2Mode.value === 'generate' ? 0 : clampImageV2Strength(v2ConsistencyClip.value, IMAGE_V2_CONSISTENCY_CLIP), consistencyClip: v2Mode.value === 'generate' ? 0 : clampImageV2Strength(v2ConsistencyClip.value, IMAGE_V2_CONSISTENCY_CLIP),
steps: clampImageSteps(v2Steps.value, IMAGE_V2_STEPS_DEFAULT), steps: clampImageSteps(v2Steps.value, IMAGE_V2_STEPS_DEFAULT),
@@ -3553,8 +3559,8 @@ function applyGenerationPreset(preset: GenerationPreset) {
v2Engine.value = v2Mode.value === 'generate' ? parseImageV2Engine(settings.engine, 'flux') : 'flux' v2Engine.value = v2Mode.value === 'generate' ? parseImageV2Engine(settings.engine, 'flux') : 'flux'
v2Task.value = v2Mode.value === 'compose' ? (settings.task || 'scene') : v2Mode.value === 'refine' ? 'refine' : v2Mode.value === 'generate' ? 't2i' : 'scene' v2Task.value = v2Mode.value === 'compose' ? (settings.task || 'scene') : v2Mode.value === 'refine' ? 'refine' : v2Mode.value === 'generate' ? 't2i' : 'scene'
if (typeof settings.negative === 'string') v2Negative.value = settings.negative if (typeof settings.negative === 'string') v2Negative.value = settings.negative
v2SnofsModel.value = clampImageV2Strength(settings.snofsModel, IMAGE_V2_SNOFS_MODEL) v2SnofsModel.value = v2ConceptLoraEnabled.value ? clampImageV2Strength(settings.snofsModel, IMAGE_V2_SNOFS_MODEL) : 0
v2SnofsClip.value = clampImageV2Strength(settings.snofsClip, IMAGE_V2_SNOFS_CLIP) v2SnofsClip.value = v2ConceptLoraEnabled.value ? clampImageV2Strength(settings.snofsClip, IMAGE_V2_SNOFS_CLIP) : 0
v2ConsistencyModel.value = v2Mode.value === 'generate' ? 0 : clampImageV2Strength(settings.consistencyModel, IMAGE_V2_CONSISTENCY_MODEL) v2ConsistencyModel.value = v2Mode.value === 'generate' ? 0 : clampImageV2Strength(settings.consistencyModel, IMAGE_V2_CONSISTENCY_MODEL)
v2ConsistencyClip.value = v2Mode.value === 'generate' ? 0 : clampImageV2Strength(settings.consistencyClip, IMAGE_V2_CONSISTENCY_CLIP) v2ConsistencyClip.value = v2Mode.value === 'generate' ? 0 : clampImageV2Strength(settings.consistencyClip, IMAGE_V2_CONSISTENCY_CLIP)
v2Steps.value = clampImageSteps(settings.steps, IMAGE_V2_STEPS_DEFAULT) v2Steps.value = clampImageSteps(settings.steps, IMAGE_V2_STEPS_DEFAULT)
@@ -4836,8 +4842,13 @@ function applyStillSettings(still: LibraryStill) {
v2Engine.value = mode === 'generate' ? parseImageV2Engine(settings.engine, 'flux') : 'flux' v2Engine.value = mode === 'generate' ? parseImageV2Engine(settings.engine, 'flux') : 'flux'
if (!settings) return if (!settings) return
if (typeof settings.negative === 'string') v2Negative.value = settings.negative if (typeof settings.negative === 'string') v2Negative.value = settings.negative
if (v2ConceptLoraEnabled.value) {
if (settings.snofsModel != null) v2SnofsModel.value = clampImageV2Strength(settings.snofsModel, IMAGE_V2_SNOFS_MODEL) if (settings.snofsModel != null) v2SnofsModel.value = clampImageV2Strength(settings.snofsModel, IMAGE_V2_SNOFS_MODEL)
if (settings.snofsClip != null) v2SnofsClip.value = clampImageV2Strength(settings.snofsClip, IMAGE_V2_SNOFS_CLIP) if (settings.snofsClip != null) v2SnofsClip.value = clampImageV2Strength(settings.snofsClip, IMAGE_V2_SNOFS_CLIP)
} else {
v2SnofsModel.value = 0
v2SnofsClip.value = 0
}
if (mode !== 'generate') { if (mode !== 'generate') {
if (settings.consistencyModel != null) v2ConsistencyModel.value = clampImageV2Strength(settings.consistencyModel, IMAGE_V2_CONSISTENCY_MODEL) if (settings.consistencyModel != null) v2ConsistencyModel.value = clampImageV2Strength(settings.consistencyModel, IMAGE_V2_CONSISTENCY_MODEL)
if (settings.consistencyClip != null) v2ConsistencyClip.value = clampImageV2Strength(settings.consistencyClip, IMAGE_V2_CONSISTENCY_CLIP) if (settings.consistencyClip != null) v2ConsistencyClip.value = clampImageV2Strength(settings.consistencyClip, IMAGE_V2_CONSISTENCY_CLIP)
@@ -5481,8 +5492,8 @@ function setV2Engine(engine: ImageV2Engine) {
if (previous === 'krea') { if (previous === 'krea') {
v2Steps.value = IMAGE_V2_STEPS_DEFAULT v2Steps.value = IMAGE_V2_STEPS_DEFAULT
v2Cfg.value = IMAGE_V2_CFG_DEFAULT v2Cfg.value = IMAGE_V2_CFG_DEFAULT
v2SnofsModel.value = IMAGE_V2_SNOFS_MODEL v2SnofsModel.value = v2ConceptLoraEnabled.value ? IMAGE_V2_SNOFS_MODEL : 0
v2SnofsClip.value = IMAGE_V2_SNOFS_CLIP v2SnofsClip.value = v2ConceptLoraEnabled.value ? IMAGE_V2_SNOFS_CLIP : 0
} }
} }
@@ -5495,8 +5506,10 @@ function setGenerateSize(aspect: ImageV2GenerateAspect) {
function applyRefinePreset(kind: 'face' | 'hand' | 'heavy') { function applyRefinePreset(kind: 'face' | 'hand' | 'heavy') {
if (kind === 'face') { if (kind === 'face') {
v2Denoise.value = IMAGE_V2_REFINE_FACE.strength v2Denoise.value = IMAGE_V2_REFINE_FACE.strength
if (v2ConceptLoraEnabled.value) {
v2SnofsModel.value = IMAGE_V2_REFINE_FACE.snofsModel v2SnofsModel.value = IMAGE_V2_REFINE_FACE.snofsModel
v2SnofsClip.value = IMAGE_V2_REFINE_FACE.snofsClip v2SnofsClip.value = IMAGE_V2_REFINE_FACE.snofsClip
}
v2ConsistencyModel.value = IMAGE_V2_REFINE_FACE.consistencyModel v2ConsistencyModel.value = IMAGE_V2_REFINE_FACE.consistencyModel
v2ConsistencyClip.value = IMAGE_V2_REFINE_FACE.consistencyClip v2ConsistencyClip.value = IMAGE_V2_REFINE_FACE.consistencyClip
if (!prompt.value.trim()) prompt.value = IMAGE_V2_REFINE_FACE_PROMPT if (!prompt.value.trim()) prompt.value = IMAGE_V2_REFINE_FACE_PROMPT
@@ -5504,8 +5517,10 @@ function applyRefinePreset(kind: 'face' | 'hand' | 'heavy') {
} }
if (kind === 'hand') { if (kind === 'hand') {
v2Denoise.value = IMAGE_V2_REFINE_HAND.strength v2Denoise.value = IMAGE_V2_REFINE_HAND.strength
if (v2ConceptLoraEnabled.value) {
v2SnofsModel.value = IMAGE_V2_REFINE_HAND.snofsModel v2SnofsModel.value = IMAGE_V2_REFINE_HAND.snofsModel
v2SnofsClip.value = IMAGE_V2_REFINE_HAND.snofsClip v2SnofsClip.value = IMAGE_V2_REFINE_HAND.snofsClip
}
v2ConsistencyModel.value = IMAGE_V2_REFINE_HAND.consistencyModel v2ConsistencyModel.value = IMAGE_V2_REFINE_HAND.consistencyModel
v2ConsistencyClip.value = IMAGE_V2_REFINE_HAND.consistencyClip v2ConsistencyClip.value = IMAGE_V2_REFINE_HAND.consistencyClip
return return
@@ -5589,8 +5604,8 @@ async function editImageV2() {
} }
body.append('prompt', prompt.value.trim()) body.append('prompt', prompt.value.trim())
body.append('negative', v2Negative.value) body.append('negative', v2Negative.value)
body.append('snofs_model', String(clampImageV2Strength(v2SnofsModel.value, v2Engine.value === 'krea' ? 0 : IMAGE_V2_SNOFS_MODEL))) body.append('snofs_model', String(v2ConceptLoraEnabled.value ? clampImageV2Strength(v2SnofsModel.value, v2Engine.value === 'krea' ? 0 : IMAGE_V2_SNOFS_MODEL) : 0))
body.append('snofs_clip', String(clampImageV2Strength(v2SnofsClip.value, v2Engine.value === 'krea' ? 0 : IMAGE_V2_SNOFS_CLIP))) body.append('snofs_clip', String(v2ConceptLoraEnabled.value ? clampImageV2Strength(v2SnofsClip.value, v2Engine.value === 'krea' ? 0 : IMAGE_V2_SNOFS_CLIP) : 0))
body.append('consistency_model', String(v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(v2ConsistencyModel.value, IMAGE_V2_CONSISTENCY_MODEL))) body.append('consistency_model', String(v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(v2ConsistencyModel.value, IMAGE_V2_CONSISTENCY_MODEL)))
body.append('consistency_clip', String(v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(v2ConsistencyClip.value, IMAGE_V2_CONSISTENCY_CLIP))) body.append('consistency_clip', String(v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(v2ConsistencyClip.value, IMAGE_V2_CONSISTENCY_CLIP)))
body.append('steps', String(clampImageSteps(v2Steps.value, v2Engine.value === 'krea' ? IMAGE_V2_KREA_STEPS : IMAGE_V2_STEPS_DEFAULT))) body.append('steps', String(clampImageSteps(v2Steps.value, v2Engine.value === 'krea' ? IMAGE_V2_KREA_STEPS : IMAGE_V2_STEPS_DEFAULT)))
+6 -4
View File
@@ -24,6 +24,7 @@ import {
type ImageV2Mode, type ImageV2Mode,
type ImageV2Task type ImageV2Task
} from '~/utils/imageV2' } from '~/utils/imageV2'
import { isXaigenStudio } from '~/utils/videoModels'
import { getStill, rememberInputStill, stillPath } from '~/server/utils/library' import { getStill, rememberInputStill, stillPath } from '~/server/utils/library'
import { existsSync, readFileSync } from 'node:fs' import { existsSync, readFileSync } from 'node:fs'
@@ -215,8 +216,9 @@ export default defineEventHandler(async (event) => {
fields.consistency_clip, fields.consistency_clip,
mode === 'generate' || engine === 'krea' ? 0 : IMAGE_V2_CONSISTENCY_CLIP mode === 'generate' || engine === 'krea' ? 0 : IMAGE_V2_CONSISTENCY_CLIP
) )
const generateSnofsFallback = engine === 'krea' ? 0 : IMAGE_V2_SNOFS_MODEL const conceptAllowed = isXaigenStudio()
const generateSnofsClipFallback = engine === 'krea' ? 0 : IMAGE_V2_SNOFS_CLIP const generateSnofsFallback = !conceptAllowed || engine === 'krea' ? 0 : IMAGE_V2_SNOFS_MODEL
const generateSnofsClipFallback = !conceptAllowed || engine === 'krea' ? 0 : IMAGE_V2_SNOFS_CLIP
const still = imageA const still = imageA
? await rememberInputStill({ ? await rememberInputStill({
@@ -287,8 +289,8 @@ export default defineEventHandler(async (event) => {
v2Mode, v2Mode,
v2Task, v2Task,
engine, engine,
snofsModel: clampImageV2Strength(fields.snofs_model, generateSnofsFallback), snofsModel: conceptAllowed ? clampImageV2Strength(fields.snofs_model, generateSnofsFallback) : 0,
snofsClip: clampImageV2Strength(fields.snofs_clip, generateSnofsClipFallback), snofsClip: conceptAllowed ? clampImageV2Strength(fields.snofs_clip, generateSnofsClipFallback) : 0,
consistencyModel: generateConsistencyModel, consistencyModel: generateConsistencyModel,
consistencyClip: generateConsistencyClip, consistencyClip: generateConsistencyClip,
maskStillId: savedMask?.id, maskStillId: savedMask?.id,
+8 -5
View File
@@ -124,8 +124,8 @@ function sanitizeImageV2Settings(raw: unknown): ImageV2PresetSettings {
engine, engine,
task: mode === 'compose' ? parseImageV2Task(rec.task, 'scene') : mode === 'refine' ? 'refine' : mode === 'generate' ? 't2i' : 'scene', task: mode === 'compose' ? parseImageV2Task(rec.task, 'scene') : mode === 'refine' ? 'refine' : mode === 'generate' ? 't2i' : 'scene',
negative: String(rec.negative || '').slice(0, 2000), negative: String(rec.negative || '').slice(0, 2000),
snofsModel: clampImageV2Strength(rec.snofsModel ?? rec.snofs_model, IMAGE_V2_SNOFS_MODEL), snofsModel: isXaigenStudio() ? clampImageV2Strength(rec.snofsModel ?? rec.snofs_model, IMAGE_V2_SNOFS_MODEL) : 0,
snofsClip: clampImageV2Strength(rec.snofsClip ?? rec.snofs_clip, IMAGE_V2_SNOFS_CLIP), snofsClip: isXaigenStudio() ? clampImageV2Strength(rec.snofsClip ?? rec.snofs_clip, IMAGE_V2_SNOFS_CLIP) : 0,
consistencyModel: clampImageV2Strength(rec.consistencyModel ?? rec.consistency_model, mode === 'generate' ? 0 : IMAGE_V2_CONSISTENCY_MODEL), consistencyModel: clampImageV2Strength(rec.consistencyModel ?? rec.consistency_model, mode === 'generate' ? 0 : IMAGE_V2_CONSISTENCY_MODEL),
consistencyClip: clampImageV2Strength(rec.consistencyClip ?? rec.consistency_clip, mode === 'generate' ? 0 : IMAGE_V2_CONSISTENCY_CLIP), consistencyClip: clampImageV2Strength(rec.consistencyClip ?? rec.consistency_clip, mode === 'generate' ? 0 : IMAGE_V2_CONSISTENCY_CLIP),
steps: engine === 'krea' ? clampImageSteps(rec.steps, IMAGE_V2_KREA_STEPS) : turbo ? 8 : clampImageSteps(rec.steps, IMAGE_V2_STEPS_DEFAULT), steps: engine === 'krea' ? clampImageSteps(rec.steps, IMAGE_V2_KREA_STEPS) : turbo ? 8 : clampImageSteps(rec.steps, IMAGE_V2_STEPS_DEFAULT),
@@ -165,12 +165,15 @@ function normalizeStoredPreset(raw: unknown): GenerationPreset | null {
function publicPreset(preset: GenerationPreset): GenerationPreset { function publicPreset(preset: GenerationPreset): GenerationPreset {
const xaigen = isXaigenStudio() const xaigen = isXaigenStudio()
const settings = preset.kind === 'video'
? { ...preset.settings, workflow: coerceVideoWorkflow((preset.settings as VideoPresetSettings).workflow) }
: preset.kind === 'imagev2' && !xaigen
? { ...preset.settings, snofsModel: 0, snofsClip: 0 }
: preset.settings
return { return {
...preset, ...preset,
loraStack: filterLoraStackForStudio(preset.loraStack, xaigen), loraStack: filterLoraStackForStudio(preset.loraStack, xaigen),
settings: preset.kind === 'video' settings
? { ...preset.settings, workflow: coerceVideoWorkflow((preset.settings as VideoPresetSettings).workflow) }
: preset.settings
} }
} }
+23 -4
View File
@@ -8,6 +8,7 @@ import { imageComfyFetch } from '~/server/utils/imageComfy'
import { cachedComfyLoraNames } from '~/server/utils/loras' import { cachedComfyLoraNames } from '~/server/utils/loras'
import { resolveComfyLoraName, loraIdentityKey } from '~/utils/loras' import { resolveComfyLoraName, loraIdentityKey } from '~/utils/loras'
import { clampImageCfg, clampImageScaleMegapixels, clampImageSteps } from '~/utils/generationPresets' import { clampImageCfg, clampImageScaleMegapixels, clampImageSteps } from '~/utils/generationPresets'
import { isXaigenStudio } from '~/utils/videoModels'
import { import {
IMAGE_V2_CONSISTENCY_CLIP, IMAGE_V2_CONSISTENCY_CLIP,
IMAGE_V2_CONSISTENCY_LORA, IMAGE_V2_CONSISTENCY_LORA,
@@ -51,6 +52,8 @@ const NOISE = '15'
const SCHEDULER = '17' const SCHEDULER = '17'
const CFG = '18' const CFG = '18'
const SAVE = '21' const SAVE = '21'
const UNET = '4'
const CLIP = '5'
const SNOFS = '7' const SNOFS = '7'
const CONSISTENCY = '8' const CONSISTENCY = '8'
@@ -138,6 +141,16 @@ function bypassLoraNode(graph: WorkflowGraph, id: string, modelFrom: string, cli
} }
} }
function bypassLoraToBaseLoaders(graph: WorkflowGraph, id: string) {
delete graph[id]
for (const node of Object.values(graph)) {
for (const [key, value] of Object.entries(node.inputs)) {
if (!Array.isArray(value) || value[0] !== id) continue
node.inputs[key] = value[1] === 1 ? [CLIP, 0] : [UNET, 0]
}
}
}
function modelBasename(name: string) { function modelBasename(name: string) {
return String(name || '').replace(/\\/g, '/').split('/').pop() || '' return String(name || '').replace(/\\/g, '/').split('/').pop() || ''
} }
@@ -367,7 +380,7 @@ function buildKreaGenerateWorkflow(params: ImageV2BuildParams) {
const size = clampImageV2Size(params.width, params.height) const size = clampImageV2Size(params.width, params.height)
const steps = clampImageSteps(params.steps, IMAGE_V2_KREA_STEPS) const steps = clampImageSteps(params.steps, IMAGE_V2_KREA_STEPS)
const cfg = clampImageCfg(params.cfg, IMAGE_V2_KREA_CFG) const cfg = clampImageCfg(params.cfg, IMAGE_V2_KREA_CFG)
const conceptName = String(params.kreaConceptLora || '').trim() const conceptName = isXaigenStudio() ? String(params.kreaConceptLora || '').trim() : ''
const snofsModel = conceptName ? clampImageV2Strength(params.snofsModel, 0) : 0 const snofsModel = conceptName ? clampImageV2Strength(params.snofsModel, 0) : 0
const snofsClip = conceptName ? clampImageV2Strength(params.snofsClip, 0) : 0 const snofsClip = conceptName ? clampImageV2Strength(params.snofsClip, 0) : 0
const workflowFile = IMAGE_V2_KREA_GENERATE_WORKFLOW const workflowFile = IMAGE_V2_KREA_GENERATE_WORKFLOW
@@ -434,8 +447,9 @@ export function buildImageV2Workflow(params: ImageV2BuildParams) {
) as WorkflowGraph ) as WorkflowGraph
const prompt = composeImageV2Prompt(params.mode, params.task, params.prompt) const prompt = composeImageV2Prompt(params.mode, params.task, params.prompt)
const negative = String(params.negative || '') const negative = String(params.negative || '')
const snofsModel = clampImageV2Strength(params.snofsModel, IMAGE_V2_SNOFS_MODEL) const conceptAllowed = isXaigenStudio()
const snofsClip = clampImageV2Strength(params.snofsClip, IMAGE_V2_SNOFS_CLIP) const snofsModel = conceptAllowed ? clampImageV2Strength(params.snofsModel, IMAGE_V2_SNOFS_MODEL) : 0
const snofsClip = conceptAllowed ? clampImageV2Strength(params.snofsClip, IMAGE_V2_SNOFS_CLIP) : 0
const consistencyModel = clampImageV2Strength( const consistencyModel = clampImageV2Strength(
params.consistencyModel, params.consistencyModel,
generate ? 0 : IMAGE_V2_CONSISTENCY_MODEL generate ? 0 : IMAGE_V2_CONSISTENCY_MODEL
@@ -470,11 +484,16 @@ export function buildImageV2Workflow(params: ImageV2BuildParams) {
setInput(graph, SAVE, 'filename_prefix', params.filenamePrefix || (generate ? 'aigen-v2-generate' : 'aigen-v2')) setInput(graph, SAVE, 'filename_prefix', params.filenamePrefix || (generate ? 'aigen-v2-generate' : 'aigen-v2'))
if (!generate) patchScaleMegapixels(graph, params.megapixels ?? 1) if (!generate) patchScaleMegapixels(graph, params.megapixels ?? 1)
if (conceptAllowed) {
setInput(graph, SNOFS, 'lora_name', resolveRequiredLora(IMAGE_V2_SNOFS_LORA, 'SNOFS')) setInput(graph, SNOFS, 'lora_name', resolveRequiredLora(IMAGE_V2_SNOFS_LORA, 'SNOFS'))
setInput(graph, SNOFS, 'strength_model', snofsModel) setInput(graph, SNOFS, 'strength_model', snofsModel)
setInput(graph, SNOFS, 'strength_clip', snofsClip) setInput(graph, SNOFS, 'strength_clip', snofsClip)
} else {
bypassLoraToBaseLoaders(graph, SNOFS)
}
if (generate && consistencyModel <= 0 && consistencyClip <= 0) { if (generate && consistencyModel <= 0 && consistencyClip <= 0) {
bypassLoraNode(graph, CONSISTENCY, SNOFS, SNOFS) if (graph[SNOFS]) bypassLoraNode(graph, CONSISTENCY, SNOFS, SNOFS)
else bypassLoraToBaseLoaders(graph, CONSISTENCY)
} else { } else {
setInput(graph, CONSISTENCY, 'lora_name', resolveRequiredLora(IMAGE_V2_CONSISTENCY_LORA, 'Consistency')) setInput(graph, CONSISTENCY, 'lora_name', resolveRequiredLora(IMAGE_V2_CONSISTENCY_LORA, 'Consistency'))
setInput(graph, CONSISTENCY, 'strength_model', consistencyModel) setInput(graph, CONSISTENCY, 'strength_model', consistencyModel)
+3 -3
View File
@@ -4,7 +4,7 @@ import { getJob, listJobs, emitJob, type Job } from '~/server/utils/jobs'
import { listPendingJobs, patchPendingJob, readPendingJob, deletePendingJob } from '~/server/utils/pending' import { listPendingJobs, patchPendingJob, readPendingJob, deletePendingJob } from '~/server/utils/pending'
import { getShotQueue } from '~/server/utils/shotQueue' import { getShotQueue } from '~/server/utils/shotQueue'
import { fetchLiveQueue } from '~/server/utils/comfy' import { fetchLiveQueue } from '~/server/utils/comfy'
import { isLtxWorkflow, isTextToVideo, LTX_DISABLED_MESSAGE, ltxWorkflowEnabled, parseVideoWorkflow, type VideoWorkflowId } from '~/utils/videoModels' import { isLtxWorkflow, isTextToVideo, isXaigenStudio, LTX_DISABLED_MESSAGE, ltxWorkflowEnabled, parseVideoWorkflow, type VideoWorkflowId } from '~/utils/videoModels'
import { persistLoraFields } from '~/server/utils/loras' import { persistLoraFields } from '~/server/utils/loras'
import { resolveLoraStack } from '~/utils/loras' import { resolveLoraStack } from '~/utils/loras'
import { allowIdentityRefs } from '~/utils/globalLocks' import { allowIdentityRefs } from '~/utils/globalLocks'
@@ -764,8 +764,8 @@ async function startStudioEditJob(item: StudioJob) {
steps: payload.steps, steps: payload.steps,
seed: live.library?.seed || payload.seed, seed: live.library?.seed || payload.seed,
cfg: payload.cfg, cfg: payload.cfg,
snofsModel: payload.snofsModel ?? 0.65, snofsModel: payload.snofsModel ?? (isXaigenStudio() ? 0.65 : 0),
snofsClip: payload.snofsClip ?? 0.35, snofsClip: payload.snofsClip ?? (isXaigenStudio() ? 0.35 : 0),
consistencyModel: payload.consistencyModel ?? (mode === 'generate' ? 0 : 0.7), consistencyModel: payload.consistencyModel ?? (mode === 'generate' ? 0 : 0.7),
consistencyClip: payload.consistencyClip ?? (mode === 'generate' ? 0 : 0.7), consistencyClip: payload.consistencyClip ?? (mode === 'generate' ? 0 : 0.7),
megapixels: payload.scaleMegapixels ?? 1, megapixels: payload.scaleMegapixels ?? 1,
+2 -2
View File
@@ -1,6 +1,6 @@
import { loraDisplayName, normalizeLoraStack, type LoraStackItem } from '~/utils/loras' import { loraDisplayName, normalizeLoraStack, type LoraStackItem } from '~/utils/loras'
import type { ImageV2Engine, ImageV2Mode, ImageV2Task } from '~/utils/imageV2' import type { ImageV2Engine, ImageV2Mode, ImageV2Task } from '~/utils/imageV2'
import type { VideoWorkflowId } from '~/utils/videoModels' import { isXaigenStudio, type VideoWorkflowId } from '~/utils/videoModels'
export type StillGenerationKind = 'image' | 'imagev2' export type StillGenerationKind = 'image' | 'imagev2'
@@ -154,7 +154,7 @@ export function stillSettingsRows(still: { width?: number; height?: number; sett
if (settings.mode === 'generate' && settings.engine) { if (settings.mode === 'generate' && settings.engine) {
rows.push({ label: 'Engine', value: settings.engine === 'krea' ? 'Krea' : 'Flux' }) rows.push({ label: 'Engine', value: settings.engine === 'krea' ? 'Krea' : 'Flux' })
} }
if (settings.snofsModel != null || settings.snofsClip != null) { if (isXaigenStudio() && (settings.snofsModel != null || settings.snofsClip != null)) {
rows.push({ label: 'Concept LoRA', value: formatPair(settings.snofsModel, settings.snofsClip) }) rows.push({ label: 'Concept LoRA', value: formatPair(settings.snofsModel, settings.snofsClip) })
} }
if (settings.mode !== 'generate' && (settings.consistencyModel != null || settings.consistencyClip != null)) { if (settings.mode !== 'generate' && (settings.consistencyModel != null || settings.consistencyClip != null)) {
+2 -1
View File
@@ -30,7 +30,8 @@ export function loraIdentityKey(name: string) {
} }
export function isXaigenOnlyLora(name: string) { export function isXaigenOnlyLora(name: string) {
return /^xaigen/i.test(loraBasename(name)) const base = loraBasename(name)
return /^xaigen/i.test(base) || /klein_snofs/i.test(base)
} }
/** Pick the real Comfy filename: exact path, then same basename ignoring `xaigen-`, then suffix/contains. */ /** Pick the real Comfy filename: exact path, then same basename ignoring `xaigen-`, then suffix/contains. */