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 <cursoragent@cursor.com>
This commit is contained in:
+110
-61
@@ -66,7 +66,7 @@
|
||||
<h2 class="font-display text-2xl font-bold">Input</h2>
|
||||
<p class="text-sm text-zinc-400">{{ 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 @@
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="studioMode === 'editv2'" class="space-y-3">
|
||||
<p class="text-xs text-zinc-500">Klein v2 · 9B Base · euler. Edit, Compose, Refine, and Generate are separate graphs. Shares the desktop GPU with video.</p>
|
||||
<div v-if="v2Mode === 'generate'" class="space-y-2 rounded-2xl border border-white/10 bg-zinc-950/40 p-3">
|
||||
<p class="text-xs text-zinc-400">Size · near 1 MP</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="(pair, aspect) in IMAGE_V2_GENERATE_ASPECTS"
|
||||
:key="aspect"
|
||||
type="button"
|
||||
class="rounded-full border px-2.5 py-1 text-xs"
|
||||
:class="v2Width === pair[0] && v2Height === pair[1] ? 'border-amber-300/70 text-amber-100' : 'border-white/10 text-zinc-400'"
|
||||
@click="setGenerateSize(aspect)"
|
||||
>
|
||||
{{ aspect }} · {{ pair[0] }}×{{ pair[1] }}
|
||||
</button>
|
||||
<p class="text-xs text-zinc-500">Edit, Compose, Refine, and Generate are separate graphs. Shares the desktop GPU with video.</p>
|
||||
<div v-if="v2Mode === 'generate'" class="space-y-3 rounded-2xl border border-white/10 bg-zinc-950/40 p-3">
|
||||
<div>
|
||||
<p class="text-xs text-zinc-400">Engine</p>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full border px-2.5 py-1 text-xs"
|
||||
:class="v2Engine === 'flux' ? 'border-amber-300/70 text-amber-100' : 'border-white/10 text-zinc-400'"
|
||||
@click="setV2Engine('flux')"
|
||||
>
|
||||
Flux
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full border px-2.5 py-1 text-xs"
|
||||
:class="v2Engine === 'krea' ? 'border-amber-300/70 text-amber-100' : 'border-white/10 text-zinc-400'"
|
||||
@click="setV2Engine('krea')"
|
||||
>
|
||||
Krea
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-zinc-500">Flux is the current edit and compose engine. Krea is a separate photograph engine.</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full border px-2.5 py-1 text-xs"
|
||||
:class="v2SnofsModel === 0 && v2SnofsClip === 0 ? 'border-amber-300/70 text-amber-100' : 'border-white/10 text-zinc-400'"
|
||||
@click="applyGenerateLook('sfw')"
|
||||
>
|
||||
SFW scene
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-full border px-2.5 py-1 text-xs"
|
||||
:class="v2SnofsModel === IMAGE_V2_GENERATE_SNOFS.snofsModel && v2SnofsClip === IMAGE_V2_GENERATE_SNOFS.snofsClip ? 'border-amber-300/70 text-amber-100' : 'border-white/10 text-zinc-400'"
|
||||
@click="applyGenerateLook('snofs')"
|
||||
>
|
||||
SNOF T2I
|
||||
</button>
|
||||
<div>
|
||||
<p class="text-xs text-zinc-400">Size · near 1 MP</p>
|
||||
<div class="mt-2 flex flex-wrap gap-2">
|
||||
<button
|
||||
v-for="(pair, aspect) in IMAGE_V2_GENERATE_ASPECTS"
|
||||
:key="aspect"
|
||||
type="button"
|
||||
class="rounded-full border px-2.5 py-1 text-xs"
|
||||
:class="v2Width === pair[0] && v2Height === pair[1] ? 'border-amber-300/70 text-amber-100' : 'border-white/10 text-zinc-400'"
|
||||
@click="setGenerateSize(aspect)"
|
||||
>
|
||||
{{ aspect }} · {{ pair[0] }}×{{ pair[1] }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="v2Mode === 'refine'" class="space-y-2 rounded-2xl border border-white/10 bg-zinc-950/40 p-3">
|
||||
@@ -788,19 +794,36 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<label class="block text-sm">
|
||||
<span class="mb-1 block text-zinc-400">SNOFS Model</span>
|
||||
<input v-model.number="v2SnofsModel" 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">
|
||||
<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">SNOFS CLIP</span>
|
||||
<input v-model.number="v2SnofsClip" 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">
|
||||
<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>
|
||||
<label v-if="v2Mode !== 'generate'" class="block text-sm">
|
||||
<span class="mb-1 block text-zinc-400">Consistency Model</span>
|
||||
<p v-if="v2Engine === 'krea' && !kreaConceptConfigured" class="col-span-2 text-xs text-zinc-500">No Krea concept LoRA is configured.</p>
|
||||
<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'" class="block text-sm">
|
||||
<span class="mb-1 block text-zinc-400">Consistency CLIP</span>
|
||||
<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">
|
||||
@@ -820,7 +843,7 @@
|
||||
<input v-model.number="v2Megapixels" type="number" :min="IMAGE_SCALE_MP_MIN" :max="IMAGE_SCALE_MP_MAX" :step="IMAGE_SCALE_MP_STEP" 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>
|
||||
</div>
|
||||
<label class="flex cursor-pointer items-center gap-2 text-xs text-zinc-400">
|
||||
<label v-if="v2Engine !== 'krea'" class="flex cursor-pointer items-center gap-2 text-xs text-zinc-400">
|
||||
<span class="relative inline-flex h-5 w-9 shrink-0 items-center">
|
||||
<input v-model="v2Turbo" type="checkbox" class="peer sr-only" role="switch">
|
||||
<span class="absolute inset-0 rounded-full bg-zinc-700 transition peer-checked:bg-amber-400" />
|
||||
@@ -1959,9 +1982,10 @@ import {
|
||||
IMAGE_V2_DENOISE_STEP,
|
||||
IMAGE_V2_GENERATE_ASPECTS,
|
||||
IMAGE_V2_GENERATE_HEIGHT,
|
||||
IMAGE_V2_GENERATE_SNOFS,
|
||||
IMAGE_V2_GENERATE_SFW,
|
||||
IMAGE_V2_GENERATE_WIDTH,
|
||||
IMAGE_V2_KREA_CFG,
|
||||
IMAGE_V2_KREA_CONCEPT_LORA,
|
||||
IMAGE_V2_KREA_STEPS,
|
||||
IMAGE_V2_REFINE_FACE,
|
||||
IMAGE_V2_REFINE_FACE_PROMPT,
|
||||
IMAGE_V2_REFINE_HAND,
|
||||
@@ -1972,6 +1996,8 @@ import {
|
||||
clampImageV2Denoise,
|
||||
clampImageV2Size,
|
||||
clampImageV2Strength,
|
||||
parseImageV2Engine,
|
||||
type ImageV2Engine,
|
||||
type ImageV2GenerateAspect,
|
||||
type ImageV2Mode,
|
||||
type ImageV2PresetSettings,
|
||||
@@ -2245,7 +2271,9 @@ const editScaleMegapixels = ref(1)
|
||||
const editRefFile = ref<File | null>(null)
|
||||
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(IMAGE_V2_SNOFS_MODEL)
|
||||
const v2SnofsClip = ref(IMAGE_V2_SNOFS_CLIP)
|
||||
@@ -3469,6 +3497,7 @@ function currentPresetSnapshot() {
|
||||
loraStack: [],
|
||||
settings: {
|
||||
mode: v2Mode.value,
|
||||
engine: v2Mode.value === 'generate' ? v2Engine.value : 'flux',
|
||||
task: v2Mode.value === 'compose' ? v2Task.value : v2Mode.value === 'refine' ? 'refine' : v2Mode.value === 'generate' ? 't2i' : 'scene',
|
||||
negative: v2Negative.value,
|
||||
snofsModel: clampImageV2Strength(v2SnofsModel.value, IMAGE_V2_SNOFS_MODEL),
|
||||
@@ -3521,6 +3550,7 @@ function applyGenerationPreset(preset: GenerationPreset) {
|
||||
if (preset.kind === 'imagev2') {
|
||||
const settings = preset.settings as ImageV2PresetSettings
|
||||
v2Mode.value = settings.mode === 'compose' ? 'compose' : settings.mode === 'refine' ? 'refine' : settings.mode === 'generate' ? 'generate' : 'edit'
|
||||
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'
|
||||
if (typeof settings.negative === 'string') v2Negative.value = settings.negative
|
||||
v2SnofsModel.value = clampImageV2Strength(settings.snofsModel, IMAGE_V2_SNOFS_MODEL)
|
||||
@@ -4803,6 +4833,7 @@ function applyStillSettings(still: LibraryStill) {
|
||||
? settings.mode
|
||||
: 'edit'
|
||||
setV2Mode(mode)
|
||||
v2Engine.value = mode === 'generate' ? parseImageV2Engine(settings.engine, 'flux') : 'flux'
|
||||
if (!settings) return
|
||||
if (typeof settings.negative === 'string') v2Negative.value = settings.negative
|
||||
if (settings.snofsModel != null) v2SnofsModel.value = clampImageV2Strength(settings.snofsModel, IMAGE_V2_SNOFS_MODEL)
|
||||
@@ -5424,9 +5455,34 @@ function setV2Mode(mode: ImageV2Mode) {
|
||||
v2Task.value = 't2i'
|
||||
v2ConsistencyModel.value = 0
|
||||
v2ConsistencyClip.value = 0
|
||||
} else if (previous === 'generate' && v2ConsistencyModel.value === 0 && v2ConsistencyClip.value === 0) {
|
||||
v2ConsistencyModel.value = IMAGE_V2_CONSISTENCY_MODEL
|
||||
v2ConsistencyClip.value = IMAGE_V2_CONSISTENCY_CLIP
|
||||
} else {
|
||||
v2Engine.value = 'flux'
|
||||
if (previous === 'generate' && v2ConsistencyModel.value === 0 && v2ConsistencyClip.value === 0) {
|
||||
v2ConsistencyModel.value = IMAGE_V2_CONSISTENCY_MODEL
|
||||
v2ConsistencyClip.value = IMAGE_V2_CONSISTENCY_CLIP
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setV2Engine(engine: ImageV2Engine) {
|
||||
if (engine === 'krea' && v2Mode.value !== 'generate') return
|
||||
const previous = v2Engine.value
|
||||
v2Engine.value = engine
|
||||
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
|
||||
return
|
||||
}
|
||||
if (previous === 'krea') {
|
||||
v2Steps.value = IMAGE_V2_STEPS_DEFAULT
|
||||
v2Cfg.value = IMAGE_V2_CFG_DEFAULT
|
||||
v2SnofsModel.value = IMAGE_V2_SNOFS_MODEL
|
||||
v2SnofsClip.value = IMAGE_V2_SNOFS_CLIP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5436,14 +5492,6 @@ function setGenerateSize(aspect: ImageV2GenerateAspect) {
|
||||
v2Height.value = height
|
||||
}
|
||||
|
||||
function applyGenerateLook(kind: 'sfw' | 'snofs') {
|
||||
const look = kind === 'sfw' ? IMAGE_V2_GENERATE_SFW : IMAGE_V2_GENERATE_SNOFS
|
||||
v2SnofsModel.value = look.snofsModel
|
||||
v2SnofsClip.value = look.snofsClip
|
||||
v2ConsistencyModel.value = 0
|
||||
v2ConsistencyClip.value = 0
|
||||
}
|
||||
|
||||
function applyRefinePreset(kind: 'face' | 'hand' | 'heavy') {
|
||||
if (kind === 'face') {
|
||||
v2Denoise.value = IMAGE_V2_REFINE_FACE.strength
|
||||
@@ -5537,15 +5585,16 @@ async function editImageV2() {
|
||||
const size = clampImageV2Size(v2Width.value, v2Height.value)
|
||||
body.append('width', String(size.width))
|
||||
body.append('height', String(size.height))
|
||||
body.append('engine', v2Engine.value)
|
||||
}
|
||||
body.append('prompt', prompt.value.trim())
|
||||
body.append('negative', v2Negative.value)
|
||||
body.append('snofs_model', String(clampImageV2Strength(v2SnofsModel.value, IMAGE_V2_SNOFS_MODEL)))
|
||||
body.append('snofs_clip', String(clampImageV2Strength(v2SnofsClip.value, IMAGE_V2_SNOFS_CLIP)))
|
||||
body.append('consistency_model', String(v2Mode.value === 'generate' ? 0 : clampImageV2Strength(v2ConsistencyModel.value, IMAGE_V2_CONSISTENCY_MODEL)))
|
||||
body.append('consistency_clip', String(v2Mode.value === 'generate' ? 0 : clampImageV2Strength(v2ConsistencyClip.value, IMAGE_V2_CONSISTENCY_CLIP)))
|
||||
body.append('steps', String(clampImageSteps(v2Steps.value, IMAGE_V2_STEPS_DEFAULT)))
|
||||
body.append('cfg', String(clampImageCfg(v2Cfg.value, IMAGE_V2_CFG_DEFAULT)))
|
||||
body.append('snofs_model', String(clampImageV2Strength(v2SnofsModel.value, v2Engine.value === 'krea' ? 0 : IMAGE_V2_SNOFS_MODEL)))
|
||||
body.append('snofs_clip', String(clampImageV2Strength(v2SnofsClip.value, v2Engine.value === 'krea' ? 0 : IMAGE_V2_SNOFS_CLIP)))
|
||||
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('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)))
|
||||
body.append('turbo', String(v2Turbo.value === true))
|
||||
body.append('seed', seedInput.value || 'random')
|
||||
@@ -5566,7 +5615,7 @@ async function editImageV2() {
|
||||
body
|
||||
})
|
||||
if (started.queued) {
|
||||
toast('Queued Klein v2 behind the current job.')
|
||||
toast(v2Engine.value === 'krea' ? 'Queued Krea generate behind the current job.' : 'Queued Image v2 behind the current job.')
|
||||
await refreshStudioQueue()
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user