Move Concept and Consistency into the Image v2 LoRA chain.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-29 19:45:34 -05:00
co-authored by Cursor
parent e296bb5b96
commit 253e47ed81
3 changed files with 191 additions and 119 deletions
+68 -101
View File
@@ -833,41 +833,6 @@
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<template v-if="v2ConceptLoraEnabled">
<label class="block text-sm">
<span class="mb-1 block text-zinc-400">Concept LoRA — Model</span>
<input
v-model.number="v2SnofsModel"
type="number"
min="0"
max="2"
step="0.05"
:disabled="v2Engine === 'krea' && !kreaConceptConfigured"
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 disabled:opacity-50"
>
</label>
<label class="block text-sm">
<span class="mb-1 block text-zinc-400">Concept LoRA — CLIP</span>
<input
v-model.number="v2SnofsClip"
type="number"
min="0"
max="2"
step="0.05"
:disabled="v2Engine === 'krea' && !kreaConceptConfigured"
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 disabled:opacity-50"
>
</label>
<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">
<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">
</label>
<label v-if="v2Mode !== 'generate' && v2Engine !== 'krea'" class="block text-sm">
<span class="mb-1 block text-zinc-400">Consistency — CLIP</span>
<input v-model.number="v2ConsistencyClip" 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">
</label>
<label class="block text-sm">
<span class="mb-1 block text-zinc-400">Steps</span>
<input v-model.number="v2Steps" type="number" :min="IMAGE_STEPS_MIN" :max="IMAGE_STEPS_MAX" step="1" :disabled="v2Turbo" 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 disabled:opacity-50">
@@ -2032,8 +1997,7 @@ import {
} from '~/utils/libraryGeneration'
import {
IMAGE_V2_CFG_DEFAULT,
IMAGE_V2_CONSISTENCY_CLIP,
IMAGE_V2_CONSISTENCY_MODEL,
IMAGE_V2_CONSISTENCY_LORA,
IMAGE_V2_DENOISE_DEFAULT,
IMAGE_V2_DENOISE_MAX,
IMAGE_V2_DENOISE_MIN,
@@ -2050,13 +2014,16 @@ import {
IMAGE_V2_REFINE_FACE_PROMPT,
IMAGE_V2_REFINE_HAND,
IMAGE_V2_REFINE_HEAVY,
IMAGE_V2_SNOFS_CLIP,
IMAGE_V2_SNOFS_MODEL,
IMAGE_V2_SNOFS_LORA,
IMAGE_V2_STEPS_DEFAULT,
clampImageV2Denoise,
clampImageV2Size,
clampImageV2Strength,
defaultImageV2LoraStack,
isImageV2ConceptLora,
isImageV2ConsistencyLora,
mergeLegacyImageV2Loras,
parseImageV2Engine,
upsertImageV2Lora,
type ImageV2Engine,
type ImageV2GenerateAspect,
type ImageV2Mode,
@@ -2340,12 +2307,7 @@ const editRefPreview = ref('')
const v2Mode = ref<ImageV2Mode>('edit')
const v2Engine = ref<ImageV2Engine>('flux')
const v2Task = ref<ImageV2Task>('scene')
const kreaConceptConfigured = computed(() => Boolean(String(runtimeConfig.public.kreaConceptLora || IMAGE_V2_KREA_CONCEPT_LORA || '').trim()))
const v2Negative = ref('')
const v2SnofsModel = ref(isXaigenStudio(runtimeConfig.public) ? IMAGE_V2_SNOFS_MODEL : 0)
const v2SnofsClip = ref(isXaigenStudio(runtimeConfig.public) ? IMAGE_V2_SNOFS_CLIP : 0)
const v2ConsistencyModel = ref(IMAGE_V2_CONSISTENCY_MODEL)
const v2ConsistencyClip = ref(IMAGE_V2_CONSISTENCY_CLIP)
const v2Steps = ref(IMAGE_V2_STEPS_DEFAULT)
const v2Cfg = ref(IMAGE_V2_CFG_DEFAULT)
const v2Megapixels = ref(1)
@@ -2401,6 +2363,23 @@ const v2ConceptLoraEnabled = computed(() => isXaigenStudio({
studio: runtimeConfig.public.studio,
instanceName: instanceName.value
}))
function imageV2EngineLoras(stack?: unknown) {
return filterLoraStackForImageEngine(
filterLoraStackForStudio(stack ?? imageV2LoraStack.value, ltxEnabled.value),
v2Engine.value
)
}
function seedImageV2LorasIfEmpty() {
if (imageV2LoraStack.value.length) return
imageV2LoraStack.value = defaultImageV2LoraStack({
engine: v2Engine.value,
mode: v2Mode.value,
xaigen: v2ConceptLoraEnabled.value,
kreaConcept: String(runtimeConfig.public.kreaConceptLora || IMAGE_V2_KREA_CONCEPT_LORA || '').trim()
})
}
const folders = ref<LibraryFolder[]>([])
const clips = ref<LibraryClip[]>([])
const stills = ref<LibraryStill[]>([])
@@ -3345,6 +3324,7 @@ onMounted(async () => {
videoLoraStack.value = readStoredLoraStack('aigen-video-lora')
imageLoraStack.value = readStoredLoraStack('aigen-image-lora')
imageV2LoraStack.value = readStoredLoraStack('aigen-image-v2-lora')
seedImageV2LorasIfEmpty()
globalLocks.value = localStorage.getItem(LOCKS_STORE) || ''
try {
const savedRefs = JSON.parse(localStorage.getItem(REFS_STORE) || '')
@@ -3707,10 +3687,10 @@ function currentPresetSnapshot() {
engine: v2Engine.value,
task: v2Mode.value === 'compose' ? v2Task.value : v2Mode.value === 'refine' ? 'refine' : v2Mode.value === 'generate' ? 't2i' : 'scene',
negative: v2Negative.value,
snofsModel: v2ConceptLoraEnabled.value ? clampImageV2Strength(v2SnofsModel.value, IMAGE_V2_SNOFS_MODEL) : 0,
snofsClip: v2ConceptLoraEnabled.value ? clampImageV2Strength(v2SnofsClip.value, IMAGE_V2_SNOFS_CLIP) : 0,
consistencyModel: v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(v2ConsistencyModel.value, IMAGE_V2_CONSISTENCY_MODEL),
consistencyClip: v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(v2ConsistencyClip.value, IMAGE_V2_CONSISTENCY_CLIP),
snofsModel: imageV2EngineLoras().find(item => isImageV2ConceptLora(item.name))?.strengthModel ?? 0,
snofsClip: imageV2EngineLoras().find(item => isImageV2ConceptLora(item.name))?.strengthClip ?? 0,
consistencyModel: imageV2EngineLoras().find(item => isImageV2ConsistencyLora(item.name))?.strengthModel ?? 0,
consistencyClip: imageV2EngineLoras().find(item => isImageV2ConsistencyLora(item.name))?.strengthClip ?? 0,
steps: clampImageSteps(v2Steps.value, v2Engine.value === 'krea' ? IMAGE_V2_KREA_STEPS : IMAGE_V2_STEPS_DEFAULT),
cfg: clampImageCfg(v2Cfg.value, v2Engine.value === 'krea' ? IMAGE_V2_KREA_CFG : IMAGE_V2_CFG_DEFAULT),
megapixels: v2Mode.value === 'generate' ? undefined : clampImageScaleMegapixels(v2Megapixels.value, 1),
@@ -3760,10 +3740,16 @@ function applyGenerationPreset(preset: GenerationPreset) {
v2Engine.value = parseImageV2Engine(settings.engine, 'flux')
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
v2SnofsModel.value = v2ConceptLoraEnabled.value ? clampImageV2Strength(settings.snofsModel, IMAGE_V2_SNOFS_MODEL) : 0
v2SnofsClip.value = v2ConceptLoraEnabled.value ? clampImageV2Strength(settings.snofsClip, IMAGE_V2_SNOFS_CLIP) : 0
v2ConsistencyModel.value = v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(settings.consistencyModel, IMAGE_V2_CONSISTENCY_MODEL)
v2ConsistencyClip.value = v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(settings.consistencyClip, IMAGE_V2_CONSISTENCY_CLIP)
imageV2LoraStack.value = mergeLegacyImageV2Loras(filterLoraStackForImageEngine(stack, v2Engine.value), {
engine: v2Engine.value,
mode: v2Mode.value,
xaigen: v2ConceptLoraEnabled.value,
snofsModel: settings.snofsModel,
snofsClip: settings.snofsClip,
consistencyModel: settings.consistencyModel,
consistencyClip: settings.consistencyClip,
kreaConcept: String(runtimeConfig.public.kreaConceptLora || IMAGE_V2_KREA_CONCEPT_LORA || '').trim()
})
v2Steps.value = clampImageSteps(settings.steps, v2Engine.value === 'krea' ? IMAGE_V2_KREA_STEPS : IMAGE_V2_STEPS_DEFAULT)
v2Cfg.value = clampImageCfg(settings.cfg, v2Engine.value === 'krea' ? IMAGE_V2_KREA_CFG : IMAGE_V2_CFG_DEFAULT)
v2Megapixels.value = clampImageScaleMegapixels(settings.megapixels, 1)
@@ -3774,7 +3760,6 @@ function applyGenerationPreset(preset: GenerationPreset) {
v2Width.value = size.width
v2Height.value = size.height
}
imageV2LoraStack.value = filterLoraStackForImageEngine(stack, v2Engine.value)
void ensureRefineCanvas()
imageV2PresetId.value = preset.id
imageV2PresetName.value = preset.name
@@ -5044,17 +5029,6 @@ function applyStillSettings(still: LibraryStill) {
v2Engine.value = parseImageV2Engine(settings?.engine, 'flux')
if (!settings) return
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.snofsClip != null) v2SnofsClip.value = clampImageV2Strength(settings.snofsClip, IMAGE_V2_SNOFS_CLIP)
} else {
v2SnofsModel.value = 0
v2SnofsClip.value = 0
}
if (mode !== 'generate' && v2Engine.value !== 'krea') {
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.steps != null) v2Steps.value = clampImageSteps(settings.steps, IMAGE_V2_STEPS_DEFAULT)
if (settings.cfg != null) v2Cfg.value = clampImageCfg(settings.cfg, IMAGE_V2_CFG_DEFAULT)
if (settings.megapixels != null) v2Megapixels.value = clampImageScaleMegapixels(settings.megapixels, 1)
@@ -5067,12 +5041,19 @@ function applyStillSettings(still: LibraryStill) {
}
if (mode === 'compose' && settings.task) v2Task.value = settings.task
if (settings.seed != null) seedInput.value = String(Math.round(settings.seed))
if (settings.loraStack?.length || settings.loraName) {
imageV2LoraStack.value = filterLoraStackForImageEngine(
filterLoraStackForStudio(readLoraStack(settings), ltxEnabled.value),
v2Engine.value
)
}
imageV2LoraStack.value = mergeLegacyImageV2Loras(
settings.loraStack?.length || settings.loraName ? readLoraStack(settings) : imageV2LoraStack.value,
{
engine: v2Engine.value,
mode,
xaigen: v2ConceptLoraEnabled.value,
snofsModel: settings.snofsModel,
snofsClip: settings.snofsClip,
consistencyModel: settings.consistencyModel,
consistencyClip: settings.consistencyClip,
kreaConcept: String(runtimeConfig.public.kreaConceptLora || IMAGE_V2_KREA_CONCEPT_LORA || '').trim()
}
)
return
}
studioMode.value = 'edit'
@@ -5663,7 +5644,6 @@ async function editImage() {
}
function setV2Mode(mode: ImageV2Mode) {
const previous = v2Mode.value
v2Mode.value = mode
if (mode === 'edit') v2Task.value = 'scene'
if (mode === 'refine') {
@@ -5672,11 +5652,6 @@ function setV2Mode(mode: ImageV2Mode) {
}
if (mode === 'generate') {
v2Task.value = 't2i'
v2ConsistencyModel.value = 0
v2ConsistencyClip.value = 0
} else if (previous === 'generate' && v2Engine.value === 'flux' && v2ConsistencyModel.value === 0 && v2ConsistencyClip.value === 0) {
v2ConsistencyModel.value = IMAGE_V2_CONSISTENCY_MODEL
v2ConsistencyClip.value = IMAGE_V2_CONSISTENCY_CLIP
}
if (v2Engine.value === 'krea' && mode === 'edit') v2Denoise.value = IMAGE_V2_KREA_EDIT_DENOISE
if (v2Engine.value === 'krea' && mode === 'compose') v2Denoise.value = IMAGE_V2_KREA_COMPOSE_DENOISE
@@ -5688,14 +5663,11 @@ function setV2Engine(engine: ImageV2Engine) {
if (engine === 'krea') {
v2Steps.value = IMAGE_V2_KREA_STEPS
v2Cfg.value = IMAGE_V2_KREA_CFG
v2SnofsModel.value = 0
v2SnofsClip.value = 0
v2ConsistencyModel.value = 0
v2ConsistencyClip.value = 0
v2Turbo.value = false
const dropped = imageV2LoraStack.value.filter(item => isKleinImageLora(item.name))
if (dropped.length) {
imageV2LoraStack.value = filterLoraStackForImageEngine(imageV2LoraStack.value, 'krea')
seedImageV2LorasIfEmpty()
toast(dropped.length === 1
? kleinLoraOnKreaMessage(dropped[0].name)
: 'Flux LoRAs were removed. They cannot be used with Krea.')
@@ -5707,12 +5679,7 @@ function setV2Engine(engine: ImageV2Engine) {
if (previous === 'krea') {
v2Steps.value = IMAGE_V2_STEPS_DEFAULT
v2Cfg.value = IMAGE_V2_CFG_DEFAULT
v2SnofsModel.value = v2ConceptLoraEnabled.value ? IMAGE_V2_SNOFS_MODEL : 0
v2SnofsClip.value = v2ConceptLoraEnabled.value ? IMAGE_V2_SNOFS_CLIP : 0
if (v2Mode.value !== 'generate') {
v2ConsistencyModel.value = IMAGE_V2_CONSISTENCY_MODEL
v2ConsistencyClip.value = IMAGE_V2_CONSISTENCY_CLIP
}
seedImageV2LorasIfEmpty()
}
}
@@ -5725,23 +5692,23 @@ function setGenerateSize(aspect: ImageV2GenerateAspect) {
function applyRefinePreset(kind: 'face' | 'hand' | 'heavy') {
if (kind === 'face') {
v2Denoise.value = IMAGE_V2_REFINE_FACE.strength
if (v2ConceptLoraEnabled.value) {
v2SnofsModel.value = IMAGE_V2_REFINE_FACE.snofsModel
v2SnofsClip.value = IMAGE_V2_REFINE_FACE.snofsClip
if (v2Engine.value === 'flux') {
if (v2ConceptLoraEnabled.value) {
imageV2LoraStack.value = upsertImageV2Lora(imageV2LoraStack.value, IMAGE_V2_SNOFS_LORA, IMAGE_V2_REFINE_FACE.snofsModel, IMAGE_V2_REFINE_FACE.snofsClip)
}
imageV2LoraStack.value = upsertImageV2Lora(imageV2LoraStack.value, IMAGE_V2_CONSISTENCY_LORA, IMAGE_V2_REFINE_FACE.consistencyModel, IMAGE_V2_REFINE_FACE.consistencyClip)
}
v2ConsistencyModel.value = IMAGE_V2_REFINE_FACE.consistencyModel
v2ConsistencyClip.value = IMAGE_V2_REFINE_FACE.consistencyClip
if (!prompt.value.trim()) prompt.value = IMAGE_V2_REFINE_FACE_PROMPT
return
}
if (kind === 'hand') {
v2Denoise.value = IMAGE_V2_REFINE_HAND.strength
if (v2ConceptLoraEnabled.value) {
v2SnofsModel.value = IMAGE_V2_REFINE_HAND.snofsModel
v2SnofsClip.value = IMAGE_V2_REFINE_HAND.snofsClip
if (v2Engine.value === 'flux') {
if (v2ConceptLoraEnabled.value) {
imageV2LoraStack.value = upsertImageV2Lora(imageV2LoraStack.value, IMAGE_V2_SNOFS_LORA, IMAGE_V2_REFINE_HAND.snofsModel, IMAGE_V2_REFINE_HAND.snofsClip)
}
imageV2LoraStack.value = upsertImageV2Lora(imageV2LoraStack.value, IMAGE_V2_CONSISTENCY_LORA, IMAGE_V2_REFINE_HAND.consistencyModel, IMAGE_V2_REFINE_HAND.consistencyClip)
}
v2ConsistencyModel.value = IMAGE_V2_REFINE_HAND.consistencyModel
v2ConsistencyClip.value = IMAGE_V2_REFINE_HAND.consistencyClip
return
}
v2Denoise.value = IMAGE_V2_REFINE_HEAVY.strength
@@ -5825,10 +5792,10 @@ async function editImageV2() {
body.append('engine', v2Engine.value)
body.append('prompt', prompt.value.trim())
body.append('negative', v2Negative.value)
body.append('snofs_model', String(v2ConceptLoraEnabled.value ? clampImageV2Strength(v2SnofsModel.value, v2Engine.value === 'krea' ? 0 : IMAGE_V2_SNOFS_MODEL) : 0))
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_clip', String(v2Mode.value === 'generate' || v2Engine.value === 'krea' ? 0 : clampImageV2Strength(v2ConsistencyClip.value, IMAGE_V2_CONSISTENCY_CLIP)))
body.append('snofs_model', String(imageV2EngineLoras().find(item => isImageV2ConceptLora(item.name))?.strengthModel ?? 0))
body.append('snofs_clip', String(imageV2EngineLoras().find(item => isImageV2ConceptLora(item.name))?.strengthClip ?? 0))
body.append('consistency_model', String(imageV2EngineLoras().find(item => isImageV2ConsistencyLora(item.name))?.strengthModel ?? 0))
body.append('consistency_clip', String(imageV2EngineLoras().find(item => isImageV2ConsistencyLora(item.name))?.strengthClip ?? 0))
body.append('steps', String(clampImageSteps(v2Steps.value, v2Engine.value === 'krea' ? IMAGE_V2_KREA_STEPS : IMAGE_V2_STEPS_DEFAULT)))
body.append('cfg', String(clampImageCfg(v2Cfg.value, v2Engine.value === 'krea' ? IMAGE_V2_KREA_CFG : IMAGE_V2_CFG_DEFAULT)))
if (v2Mode.value !== 'generate') body.append('megapixels', String(clampImageScaleMegapixels(v2Megapixels.value, 1)))
+30 -18
View File
@@ -36,6 +36,7 @@ import {
clampImageV2Strength,
composeImageV2Prompt,
imageV2WorkflowFile,
mergeLegacyImageV2Loras,
parseImageV2Engine,
type ImageV2Engine,
type ImageV2Mode,
@@ -401,7 +402,18 @@ function applyKreaConceptLora(graph: WorkflowGraph, loraName: string, model: num
}
function applyKreaConceptIfNeeded(graph: WorkflowGraph, params: ImageV2BuildParams) {
const merged = mergeLegacyImageV2Loras(params.loraStack, {
engine: 'krea',
mode: params.mode,
xaigen: isXaigenStudio(),
snofsModel: params.snofsModel,
snofsClip: params.snofsClip,
kreaConcept: params.kreaConceptLora
})
const conceptName = isXaigenStudio() ? String(params.kreaConceptLora || '').trim() : ''
if (conceptName && merged.some(item => loraIdentityKey(item.name) === loraIdentityKey(conceptName))) {
return { snofsModel: 0, snofsClip: 0 }
}
const snofsModel = conceptName ? clampImageV2Strength(params.snofsModel, 0) : 0
const snofsClip = conceptName ? clampImageV2Strength(params.snofsClip, 0) : 0
if (!conceptName || (snofsModel <= 0 && snofsClip <= 0)) return { snofsModel: 0, snofsClip: 0 }
@@ -413,6 +425,19 @@ function applyKreaConceptIfNeeded(graph: WorkflowGraph, params: ImageV2BuildPara
return { snofsModel, snofsClip, name: resolved }
}
function imageV2UserStack(params: ImageV2BuildParams, engine: ImageV2Engine) {
return mergeLegacyImageV2Loras(params.loraStack, {
engine,
mode: params.mode,
xaigen: isXaigenStudio(),
snofsModel: params.snofsModel,
snofsClip: params.snofsClip,
consistencyModel: params.consistencyModel,
consistencyClip: params.consistencyClip,
kreaConcept: params.kreaConceptLora
})
}
function requireKreaModels(params: ImageV2BuildParams) {
if (!params.kreaUnetName || !params.kreaClipName || !params.kreaVaeName) {
throw createError({
@@ -463,7 +488,7 @@ function buildKreaItiWorkflow(params: ImageV2BuildParams) {
setInput(graph, SAVE, 'filename_prefix', params.filenamePrefix || `aigen-v2-krea-${params.mode}`)
patchScaleMegapixels(graph, params.megapixels ?? 1)
const concept = applyKreaConceptIfNeeded(graph, params)
const userLoras = applyImageV2UserLoras(graph, params.loraStack, 'krea')
const userLoras = applyImageV2UserLoras(graph, imageV2UserStack(params, 'krea'), 'krea')
assertImageV2Graph(graph, params.mode, compose ? params.imageBName : undefined, 'krea')
const loaders = loadImageNames(graph)
@@ -514,7 +539,7 @@ function buildKreaGenerateWorkflow(params: ImageV2BuildParams) {
setInput(graph, '15', 'scheduler', 'simple')
setInput(graph, SAVE, 'filename_prefix', params.filenamePrefix || 'aigen-v2-krea')
const concept = applyKreaConceptIfNeeded(graph, params)
const userLoras = applyImageV2UserLoras(graph, params.loraStack, 'krea')
const userLoras = applyImageV2UserLoras(graph, imageV2UserStack(params, 'krea'), 'krea')
assertImageV2Graph(graph, 'generate', undefined, 'krea')
const loaders = loadImageNames(graph)
@@ -596,22 +621,9 @@ export function buildImageV2Workflow(params: ImageV2BuildParams) {
setInput(graph, SAVE, 'filename_prefix', params.filenamePrefix || (generate ? 'aigen-v2-generate' : 'aigen-v2'))
if (!generate) patchScaleMegapixels(graph, params.megapixels ?? 1)
if (conceptAllowed) {
setInput(graph, SNOFS, 'lora_name', resolveRequiredLora(IMAGE_V2_SNOFS_LORA, 'SNOFS'))
setInput(graph, SNOFS, 'strength_model', snofsModel)
setInput(graph, SNOFS, 'strength_clip', snofsClip)
} else {
bypassLoraToBaseLoaders(graph, SNOFS)
}
if (generate && consistencyModel <= 0 && consistencyClip <= 0) {
if (graph[SNOFS]) bypassLoraNode(graph, CONSISTENCY, SNOFS, SNOFS)
else bypassLoraToBaseLoaders(graph, CONSISTENCY)
} else {
setInput(graph, CONSISTENCY, 'lora_name', resolveRequiredLora(IMAGE_V2_CONSISTENCY_LORA, 'Consistency'))
setInput(graph, CONSISTENCY, 'strength_model', consistencyModel)
setInput(graph, CONSISTENCY, 'strength_clip', consistencyClip)
}
const userLoras = applyImageV2UserLoras(graph, params.loraStack, 'flux')
if (graph[CONSISTENCY]) bypassLoraToBaseLoaders(graph, CONSISTENCY)
if (graph[SNOFS]) bypassLoraToBaseLoaders(graph, SNOFS)
const userLoras = applyImageV2UserLoras(graph, imageV2UserStack(params, 'flux'), 'flux')
assertImageV2Graph(graph, params.mode, generate || refine ? undefined : params.imageBName, engine)
+93
View File
@@ -1,3 +1,11 @@
import {
filterLoraStackForImageEngine,
loraIdentityKey,
makeLoraStackItem,
normalizeLoraStack,
type LoraStackItem
} from '~/utils/loras'
export const IMAGE_V2_MODES = ['edit', 'compose', 'refine', 'generate'] as const
export const IMAGE_V2_TASKS = ['scene', 'identity', 'outfit', 'face_lock', 'refine', 't2i'] as const
export const IMAGE_V2_ENGINES = ['flux', 'krea'] as const
@@ -166,3 +174,88 @@ export type ImageV2PresetSettings = {
width?: number
height?: number
}
export function isImageV2ConceptLora(name: string) {
const id = loraIdentityKey(name)
return id === loraIdentityKey(IMAGE_V2_SNOFS_LORA) || /klein_snofs/i.test(id)
}
export function isImageV2ConsistencyLora(name: string) {
const id = loraIdentityKey(name)
return id === loraIdentityKey(IMAGE_V2_CONSISTENCY_LORA) || /flux2-klein-9b-consistency/i.test(id)
}
export function upsertImageV2Lora(stack: unknown, name: string, model: number, clip: number): LoraStackItem[] {
const item = makeLoraStackItem(name, model, clip)
const current = normalizeLoraStack(stack)
if (!item) return current
const key = loraIdentityKey(name)
const index = current.findIndex(entry => loraIdentityKey(entry.name) === key)
if (index >= 0) {
const next = [...current]
next[index] = { ...next[index], strengthModel: item.strengthModel, strengthClip: item.strengthClip }
return next
}
if (isImageV2ConsistencyLora(name)) {
const conceptAt = current.findIndex(entry => isImageV2ConceptLora(entry.name))
if (conceptAt >= 0) return [...current.slice(0, conceptAt + 1), item, ...current.slice(conceptAt + 1)]
}
return [item, ...current]
}
export function defaultImageV2LoraStack(opts: {
engine: ImageV2Engine
mode: ImageV2Mode
xaigen: boolean
kreaConcept?: string
}): LoraStackItem[] {
if (opts.engine === 'krea') {
const name = String(opts.kreaConcept || '').trim()
if (!opts.xaigen || !name) return []
const item = makeLoraStackItem(name, IMAGE_V2_SNOFS_MODEL, IMAGE_V2_SNOFS_CLIP)
return item ? [item] : []
}
const out: LoraStackItem[] = []
if (opts.xaigen) {
const concept = makeLoraStackItem(IMAGE_V2_SNOFS_LORA, IMAGE_V2_SNOFS_MODEL, IMAGE_V2_SNOFS_CLIP)
if (concept) out.push(concept)
}
if (opts.mode !== 'generate') {
const consistency = makeLoraStackItem(IMAGE_V2_CONSISTENCY_LORA, IMAGE_V2_CONSISTENCY_MODEL, IMAGE_V2_CONSISTENCY_CLIP)
if (consistency) out.push(consistency)
}
return out
}
export function mergeLegacyImageV2Loras(stack: unknown, opts: {
engine: ImageV2Engine
mode: ImageV2Mode
xaigen: boolean
snofsModel?: number
snofsClip?: number
consistencyModel?: number
consistencyClip?: number
kreaConcept?: string
}): LoraStackItem[] {
let items = filterLoraStackForImageEngine(stack, opts.engine)
if (opts.engine === 'krea') {
const name = String(opts.kreaConcept || '').trim()
const model = Number(opts.snofsModel) || 0
const clip = Number(opts.snofsClip) || 0
if (name && (model > 0 || clip > 0) && !items.some(item => loraIdentityKey(item.name) === loraIdentityKey(name))) {
return upsertImageV2Lora(items, name, model, clip)
}
return items
}
const conceptModel = opts.xaigen ? Number(opts.snofsModel) || 0 : 0
const conceptClip = opts.xaigen ? Number(opts.snofsClip) || 0 : 0
if ((conceptModel > 0 || conceptClip > 0) && !items.some(item => isImageV2ConceptLora(item.name))) {
items = upsertImageV2Lora(items, IMAGE_V2_SNOFS_LORA, conceptModel, conceptClip)
}
const consistencyModel = opts.mode === 'generate' ? 0 : Number(opts.consistencyModel) || 0
const consistencyClip = opts.mode === 'generate' ? 0 : Number(opts.consistencyClip) || 0
if ((consistencyModel > 0 || consistencyClip > 0) && !items.some(item => isImageV2ConsistencyLora(item.name))) {
items = upsertImageV2Lora(items, IMAGE_V2_CONSISTENCY_LORA, consistencyModel, consistencyClip)
}
return items
}