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)))