Apply and print SNOFS from the LoRA dials, not a leftover Concept LoRA row.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
|||||||
clampImageV2Denoise,
|
clampImageV2Denoise,
|
||||||
clampImageV2Size,
|
clampImageV2Size,
|
||||||
clampImageV2Strength,
|
clampImageV2Strength,
|
||||||
|
imageV2StackSpecials,
|
||||||
imageV2WorkflowFile,
|
imageV2WorkflowFile,
|
||||||
parseImageV2Engine,
|
parseImageV2Engine,
|
||||||
parseImageV2Mode,
|
parseImageV2Mode,
|
||||||
@@ -259,6 +260,15 @@ export default defineEventHandler(async (event) => {
|
|||||||
const generateSnofsClipFallback = 0
|
const generateSnofsClipFallback = 0
|
||||||
await listStudioLoras()
|
await listStudioLoras()
|
||||||
const loraStack = assertImageV2LoraStack(parsePostedLoraStack(fields.loraStack || fields.lora, 'image'), engine)
|
const loraStack = assertImageV2LoraStack(parsePostedLoraStack(fields.loraStack || fields.lora, 'image'), engine)
|
||||||
|
const stackSpecials = imageV2StackSpecials(loraStack, engine)
|
||||||
|
const snofsModel = conceptAllowed
|
||||||
|
? (stackSpecials.snofs?.strengthModel ?? clampImageV2Strength(fields.snofs_model, generateSnofsFallback))
|
||||||
|
: 0
|
||||||
|
const snofsClip = conceptAllowed
|
||||||
|
? (stackSpecials.snofs?.strengthClip ?? clampImageV2Strength(fields.snofs_clip, generateSnofsClipFallback))
|
||||||
|
: 0
|
||||||
|
const consistencyModel = stackSpecials.consistency?.strengthModel ?? generateConsistencyModel
|
||||||
|
const consistencyClip = stackSpecials.consistency?.strengthClip ?? generateConsistencyClip
|
||||||
let inputFilename = imageA?.filename || ''
|
let inputFilename = imageA?.filename || ''
|
||||||
const postedStillId = String(fields.stillId || '').trim()
|
const postedStillId = String(fields.stillId || '').trim()
|
||||||
if (postedStillId) {
|
if (postedStillId) {
|
||||||
@@ -346,10 +356,10 @@ export default defineEventHandler(async (event) => {
|
|||||||
v2Mode,
|
v2Mode,
|
||||||
v2Task,
|
v2Task,
|
||||||
engine,
|
engine,
|
||||||
snofsModel: conceptAllowed ? clampImageV2Strength(fields.snofs_model, generateSnofsFallback) : 0,
|
snofsModel,
|
||||||
snofsClip: conceptAllowed ? clampImageV2Strength(fields.snofs_clip, generateSnofsClipFallback) : 0,
|
snofsClip,
|
||||||
consistencyModel: generateConsistencyModel,
|
consistencyModel,
|
||||||
consistencyClip: generateConsistencyClip,
|
consistencyClip,
|
||||||
maskStillId: savedMask?.id,
|
maskStillId: savedMask?.id,
|
||||||
maskStillFilename: savedMask?.filename,
|
maskStillFilename: savedMask?.filename,
|
||||||
refineStrength,
|
refineStrength,
|
||||||
|
|||||||
@@ -9,7 +9,15 @@ import { emitChainJob } from '~/server/utils/watch'
|
|||||||
import { saveStill, stillChainName } from '~/server/utils/library'
|
import { saveStill, stillChainName } from '~/server/utils/library'
|
||||||
import { comfyJobPrefix } from '~/utils/outputNames'
|
import { comfyJobPrefix } from '~/utils/outputNames'
|
||||||
import { joinPromptParts } from '~/utils/promptParts'
|
import { joinPromptParts } from '~/utils/promptParts'
|
||||||
import { parseImageV2Engine, type ImageV2Engine, type ImageV2Mode, type ImageV2Task } from '~/utils/imageV2'
|
import { isXaigenStudio } from '~/utils/videoModels'
|
||||||
|
import {
|
||||||
|
imageV2StackSpecials,
|
||||||
|
mergeLegacyImageV2Loras,
|
||||||
|
parseImageV2Engine,
|
||||||
|
type ImageV2Engine,
|
||||||
|
type ImageV2Mode,
|
||||||
|
type ImageV2Task
|
||||||
|
} from '~/utils/imageV2'
|
||||||
import type { EditImageFile } from '~/server/utils/imageChain'
|
import type { EditImageFile } from '~/server/utils/imageChain'
|
||||||
|
|
||||||
export type EditV2RunParams = {
|
export type EditV2RunParams = {
|
||||||
@@ -96,6 +104,21 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
|||||||
await ensureComfyLoraNames('image')
|
await ensureComfyLoraNames('image')
|
||||||
if (!generate && params.megapixels > 0) await assertImageScaleToTotalPixelsNode()
|
if (!generate && params.megapixels > 0) await assertImageScaleToTotalPixelsNode()
|
||||||
const krea = engine === 'krea' ? await resolveKreaGenerateAssets() : null
|
const krea = engine === 'krea' ? await resolveKreaGenerateAssets() : null
|
||||||
|
const appliedStack = mergeLegacyImageV2Loras(params.loraStack?.length ? params.loraStack : library.loraStack, {
|
||||||
|
engine,
|
||||||
|
mode: params.mode,
|
||||||
|
xaigen: isXaigenStudio(),
|
||||||
|
snofsModel: params.snofsModel,
|
||||||
|
snofsClip: params.snofsClip,
|
||||||
|
consistencyModel: params.consistencyModel,
|
||||||
|
consistencyClip: params.consistencyClip,
|
||||||
|
kreaConcept: krea?.conceptLora
|
||||||
|
})
|
||||||
|
const appliedSpecials = imageV2StackSpecials(appliedStack, engine)
|
||||||
|
const snofsModel = appliedSpecials.snofs?.strengthModel ?? params.snofsModel
|
||||||
|
const snofsClip = appliedSpecials.snofs?.strengthClip ?? params.snofsClip
|
||||||
|
const consistencyModel = appliedSpecials.consistency?.strengthModel ?? params.consistencyModel
|
||||||
|
const consistencyClip = appliedSpecials.consistency?.strengthClip ?? params.consistencyClip
|
||||||
|
|
||||||
if (!generate && params.mode === 'compose' && params.reference) {
|
if (!generate && params.mode === 'compose' && params.reference) {
|
||||||
uploadedRef = await uploadImage({
|
uploadedRef = await uploadImage({
|
||||||
@@ -201,10 +224,10 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
|||||||
imageBName: uploadedRef?.name,
|
imageBName: uploadedRef?.name,
|
||||||
maskName: uploadedMask?.name,
|
maskName: uploadedMask?.name,
|
||||||
strength: params.strength,
|
strength: params.strength,
|
||||||
snofsModel: params.snofsModel,
|
snofsModel,
|
||||||
snofsClip: params.snofsClip,
|
snofsClip,
|
||||||
consistencyModel: params.consistencyModel,
|
consistencyModel,
|
||||||
consistencyClip: params.consistencyClip,
|
consistencyClip,
|
||||||
steps: params.steps,
|
steps: params.steps,
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
seed,
|
seed,
|
||||||
@@ -218,7 +241,7 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
|||||||
kreaClipName: krea?.clip,
|
kreaClipName: krea?.clip,
|
||||||
kreaVaeName: krea?.vae,
|
kreaVaeName: krea?.vae,
|
||||||
kreaConceptLora: krea?.conceptLora,
|
kreaConceptLora: krea?.conceptLora,
|
||||||
loraStack: params.loraStack?.length ? params.loraStack : (library.loraStack || undefined)
|
loraStack: appliedStack
|
||||||
})
|
})
|
||||||
const queued = await queuePrompt(built.graph, job.clientId)
|
const queued = await queuePrompt(built.graph, job.clientId)
|
||||||
job.promptId = queued.prompt_id
|
job.promptId = queued.prompt_id
|
||||||
@@ -277,10 +300,10 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
|||||||
engine,
|
engine,
|
||||||
task: params.task,
|
task: params.task,
|
||||||
negative: params.negative,
|
negative: params.negative,
|
||||||
snofsModel: params.snofsModel,
|
snofsModel,
|
||||||
snofsClip: params.snofsClip,
|
snofsClip,
|
||||||
consistencyModel: params.consistencyModel,
|
consistencyModel,
|
||||||
consistencyClip: params.consistencyClip,
|
consistencyClip,
|
||||||
steps: params.steps,
|
steps: params.steps,
|
||||||
cfg: params.cfg,
|
cfg: params.cfg,
|
||||||
megapixels: params.mode === 'generate' || !(params.megapixels > 0) ? undefined : params.megapixels,
|
megapixels: params.mode === 'generate' || !(params.megapixels > 0) ? undefined : params.megapixels,
|
||||||
@@ -293,7 +316,7 @@ export async function runEditV2(job: Job, params: EditV2RunParams) {
|
|||||||
seed,
|
seed,
|
||||||
sourceStillId: params.sourceStillId || library.stillId,
|
sourceStillId: params.sourceStillId || library.stillId,
|
||||||
referenceStillId: params.referenceStillId,
|
referenceStillId: params.referenceStillId,
|
||||||
...persistLoraFields(params.loraStack)
|
...persistLoraFields(appliedStack)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
job.stillId = still?.id
|
job.stillId = still?.id
|
||||||
|
|||||||
@@ -584,7 +584,7 @@ export const IMAGE_V2_NODE_LABELS: Record<string, string> = {
|
|||||||
'4': 'Loading diffusion model',
|
'4': 'Loading diffusion model',
|
||||||
'5': 'Loading CLIP',
|
'5': 'Loading CLIP',
|
||||||
'6': 'Loading VAE',
|
'6': 'Loading VAE',
|
||||||
'7': 'Applying Concept LoRA',
|
'7': 'Applying SNOFS',
|
||||||
'8': 'Applying Consistency',
|
'8': 'Applying Consistency',
|
||||||
'70': 'Applying LoRA',
|
'70': 'Applying LoRA',
|
||||||
'71': 'Applying LoRA',
|
'71': 'Applying LoRA',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { fetchLiveQueue } from '~/server/utils/comfy'
|
|||||||
import { isLtxWorkflow, isTextToVideo, isXaigenStudio, 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 { imageV2StackSpecials } from '~/utils/imageV2'
|
||||||
import { allowIdentityRefs, type PermanenceRef } from '~/utils/globalLocks'
|
import { allowIdentityRefs, type PermanenceRef } from '~/utils/globalLocks'
|
||||||
|
|
||||||
export type StudioJobStatus = 'waiting' | 'running' | 'held' | 'complete' | 'error' | 'cancelled'
|
export type StudioJobStatus = 'waiting' | 'running' | 'held' | 'complete' | 'error' | 'cancelled'
|
||||||
@@ -837,9 +838,11 @@ async function startStudioEditJob(item: StudioJob) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
await markStudioLive(item.ownerKey, item.id, live.id)
|
await markStudioLive(item.ownerKey, item.id, live.id)
|
||||||
|
const v2Engine = payload.engine === 'krea' ? 'krea' : 'flux'
|
||||||
|
const v2Specials = imageV2StackSpecials(payload.loraStack, v2Engine)
|
||||||
void runEditV2(live, {
|
void runEditV2(live, {
|
||||||
mode,
|
mode,
|
||||||
engine: payload.engine === 'krea' ? 'krea' : 'flux',
|
engine: v2Engine,
|
||||||
task: payload.v2Task || 'scene',
|
task: payload.v2Task || 'scene',
|
||||||
image: mode === 'generate' ? null : image,
|
image: mode === 'generate' ? null : image,
|
||||||
reference: mode === 'refine' || mode === 'generate' ? null : reference,
|
reference: mode === 'refine' || mode === 'generate' ? null : reference,
|
||||||
@@ -851,10 +854,10 @@ 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,
|
snofsModel: v2Specials.snofs?.strengthModel ?? payload.snofsModel ?? 0,
|
||||||
snofsClip: payload.snofsClip ?? 0,
|
snofsClip: v2Specials.snofs?.strengthClip ?? payload.snofsClip ?? 0,
|
||||||
consistencyModel: payload.consistencyModel ?? (mode === 'generate' || payload.engine === 'krea' ? 0 : 0.7),
|
consistencyModel: v2Specials.consistency?.strengthModel ?? payload.consistencyModel ?? (mode === 'generate' || payload.engine === 'krea' ? 0 : 0.7),
|
||||||
consistencyClip: payload.consistencyClip ?? (mode === 'generate' || payload.engine === 'krea' ? 0 : 0.7),
|
consistencyClip: v2Specials.consistency?.strengthClip ?? payload.consistencyClip ?? (mode === 'generate' || payload.engine === 'krea' ? 0 : 0.7),
|
||||||
megapixels: payload.scaleToTotalPixels === false ? 0 : (payload.scaleMegapixels ?? 1),
|
megapixels: payload.scaleToTotalPixels === false ? 0 : (payload.scaleMegapixels ?? 1),
|
||||||
strength: payload.refineStrength,
|
strength: payload.refineStrength,
|
||||||
aspect: payload.aspect || 'auto',
|
aspect: payload.aspect || 'auto',
|
||||||
|
|||||||
@@ -197,6 +197,14 @@ export function isImageV2ConsistencyLora(name: string) {
|
|||||||
return id === loraIdentityKey(IMAGE_V2_CONSISTENCY_LORA) || /flux2-klein-9b-consistency/i.test(id)
|
return id === loraIdentityKey(IMAGE_V2_CONSISTENCY_LORA) || /flux2-klein-9b-consistency/i.test(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Strengths that are actually on the stack cards. Separate snofs_* fields are legacy only. */
|
||||||
|
export function imageV2StackSpecials(stack: unknown, engine?: ImageV2Engine) {
|
||||||
|
const items = normalizeLoraStack(stack)
|
||||||
|
const snofs = items.find(item => isImageV2ConceptLora(item.name, engine))
|
||||||
|
const consistency = items.find(item => isImageV2ConsistencyLora(item.name))
|
||||||
|
return { items, snofs, consistency }
|
||||||
|
}
|
||||||
|
|
||||||
export function upsertImageV2Lora(stack: unknown, name: string, model: number, clip: number): LoraStackItem[] {
|
export function upsertImageV2Lora(stack: unknown, name: string, model: number, clip: number): LoraStackItem[] {
|
||||||
const item = makeLoraStackItem(name, model, clip)
|
const item = makeLoraStackItem(name, model, clip)
|
||||||
const current = normalizeLoraStack(stack)
|
const current = normalizeLoraStack(stack)
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
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 {
|
||||||
|
imageV2ConceptLoraName,
|
||||||
|
imageV2StackSpecials,
|
||||||
|
upsertImageV2Lora,
|
||||||
|
type ImageV2Engine,
|
||||||
|
type ImageV2Mode,
|
||||||
|
type ImageV2Task
|
||||||
|
} from '~/utils/imageV2'
|
||||||
import { isXaigenStudio, type VideoWorkflowId } from '~/utils/videoModels'
|
import { isXaigenStudio, type VideoWorkflowId } from '~/utils/videoModels'
|
||||||
import { restorePromptParts } from '~/utils/promptParts'
|
import { restorePromptParts } from '~/utils/promptParts'
|
||||||
|
|
||||||
@@ -72,6 +79,16 @@ function formatLoras(stack?: LoraStackItem[], name?: string) {
|
|||||||
}).join(', ')
|
}).join(', ')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stillDisplayLoraStack(settings: StillGenerationSettings) {
|
||||||
|
let stack = normalizeLoraStack(settings.loraStack || settings.loraName)
|
||||||
|
const { snofs } = imageV2StackSpecials(stack, settings.engine)
|
||||||
|
if (!snofs && isXaigenStudio() && (settings.snofsModel || settings.snofsClip)) {
|
||||||
|
const name = imageV2ConceptLoraName(settings.engine || 'flux')
|
||||||
|
if (name) stack = upsertImageV2Lora(stack, name, settings.snofsModel ?? 0, settings.snofsClip ?? 0)
|
||||||
|
}
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizeStillSettings(raw: unknown): StillGenerationSettings | undefined {
|
export function normalizeStillSettings(raw: unknown): StillGenerationSettings | undefined {
|
||||||
if (!raw || typeof raw !== 'object') return undefined
|
if (!raw || typeof raw !== 'object') return undefined
|
||||||
const value = raw as Record<string, unknown>
|
const value = raw as Record<string, unknown>
|
||||||
@@ -171,13 +188,18 @@ export function stillSettingsRows(still: { prompt?: string; promptPre?: string;
|
|||||||
if (settings.engine) {
|
if (settings.engine) {
|
||||||
rows.push({ label: 'Engine', value: settings.engine === 'krea' ? 'Krea' : 'Flux' })
|
rows.push({ label: 'Engine', value: settings.engine === 'krea' ? 'Krea' : 'Flux' })
|
||||||
}
|
}
|
||||||
if (isXaigenStudio() && (settings.snofsModel != null || settings.snofsClip != null)) {
|
const displayStack = stillDisplayLoraStack(settings)
|
||||||
rows.push({ label: 'Concept LoRA', value: formatPair(settings.snofsModel, settings.snofsClip) })
|
const { consistency } = imageV2StackSpecials(displayStack, settings.engine)
|
||||||
|
if (settings.mode !== 'generate' && (consistency || settings.consistencyModel != null || settings.consistencyClip != null)) {
|
||||||
|
rows.push({
|
||||||
|
label: 'Consistency',
|
||||||
|
value: formatPair(
|
||||||
|
consistency?.strengthModel ?? settings.consistencyModel,
|
||||||
|
consistency?.strengthClip ?? settings.consistencyClip
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (settings.mode !== 'generate' && (settings.consistencyModel != null || settings.consistencyClip != null)) {
|
const loras = formatLoras(displayStack)
|
||||||
rows.push({ label: 'Consistency', value: formatPair(settings.consistencyModel, settings.consistencyClip) })
|
|
||||||
}
|
|
||||||
const loras = formatLoras(settings.loraStack, settings.loraName)
|
|
||||||
if (loras) rows.push({ label: 'LoRA', value: loras })
|
if (loras) rows.push({ label: 'LoRA', value: loras })
|
||||||
if (settings.mode === 'refine' && settings.strength != null) {
|
if (settings.mode === 'refine' && settings.strength != null) {
|
||||||
rows.push({ label: 'Strength', value: formatNum(settings.strength) })
|
rows.push({ label: 'Strength', value: formatNum(settings.strength) })
|
||||||
|
|||||||
Reference in New Issue
Block a user