Files
aigen/utils/libraryGeneration.ts
T
TowstyandCursor 5fe91e6bae Show Flux and Krea as Image v2 engine cards, and run Krea on edit, compose, and refine.
Krea image-to-image uses encode plus denoise graphs, not Klein reference latents, and still fails closed if the Krea models are missing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 07:29:31 -05:00

235 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { loraDisplayName, normalizeLoraStack, type LoraStackItem } from '~/utils/loras'
import type { ImageV2Engine, ImageV2Mode, ImageV2Task } from '~/utils/imageV2'
import { isXaigenStudio, type VideoWorkflowId } from '~/utils/videoModels'
export type StillGenerationKind = 'image' | 'imagev2'
export type StillGenerationSettings = {
kind: StillGenerationKind
mode?: ImageV2Mode
engine?: ImageV2Engine
task?: ImageV2Task
negative?: string
snofsModel?: number
snofsClip?: number
consistencyModel?: number
consistencyClip?: number
steps?: number
cfg?: number
megapixels?: number
turbo?: boolean
strength?: number
width?: number
height?: number
seed?: number
scaleToTotalPixels?: boolean
scaleMegapixels?: number
loraName?: string
loraStack?: LoraStackItem[]
sourceStillId?: string
referenceStillId?: string
}
export type SettingsRow = { label: string; value: string }
function finiteNumber(raw: unknown): number | undefined {
const value = Number(raw)
return Number.isFinite(value) ? value : undefined
}
function formatNum(raw: unknown, digits = 2) {
const value = Number(raw)
if (!Number.isFinite(value)) return ''
const rounded = Number(value.toFixed(digits))
return Number.isInteger(rounded) ? String(rounded) : String(rounded)
}
function formatPair(model?: number, clip?: number) {
if (model == null && clip == null) return ''
return `${formatNum(model ?? 0)} / ${formatNum(clip ?? 0)}`
}
function formatLoras(stack?: LoraStackItem[], name?: string) {
const items = normalizeLoraStack(stack?.length ? stack : name)
if (!items.length) return ''
return items.map((item) => {
const label = loraDisplayName(item.name)
if (item.strengthModel === item.strengthClip) return `${label} ${formatNum(item.strengthModel)}`
return `${label} ${formatNum(item.strengthModel)} / ${formatNum(item.strengthClip)}`
}).join(', ')
}
export function normalizeStillSettings(raw: unknown): StillGenerationSettings | undefined {
if (!raw || typeof raw !== 'object') return undefined
const value = raw as Record<string, unknown>
const mode = value.mode === 'compose' || value.mode === 'refine' || value.mode === 'generate' || value.mode === 'edit'
? value.mode
: undefined
const engine = value.engine === 'krea' || value.engine === 'flux' ? value.engine : undefined
const kind: StillGenerationKind | undefined = value.kind === 'imagev2' || mode
? 'imagev2'
: value.kind === 'image'
? 'image'
: undefined
if (!kind) return undefined
const task = value.task === 'identity' || value.task === 'outfit' || value.task === 'face_lock' || value.task === 'refine' || value.task === 't2i' || value.task === 'scene'
? value.task
: undefined
const stack = normalizeLoraStack(value.loraStack || value.loraName)
return {
kind,
mode,
engine,
task,
negative: typeof value.negative === 'string' ? value.negative : undefined,
snofsModel: finiteNumber(value.snofsModel),
snofsClip: finiteNumber(value.snofsClip),
consistencyModel: finiteNumber(value.consistencyModel),
consistencyClip: finiteNumber(value.consistencyClip),
steps: finiteNumber(value.steps),
cfg: finiteNumber(value.cfg),
megapixels: finiteNumber(value.megapixels),
turbo: typeof value.turbo === 'boolean' ? value.turbo : undefined,
strength: finiteNumber(value.strength),
width: finiteNumber(value.width),
height: finiteNumber(value.height),
seed: finiteNumber(value.seed),
scaleToTotalPixels: typeof value.scaleToTotalPixels === 'boolean' ? value.scaleToTotalPixels : undefined,
scaleMegapixels: finiteNumber(value.scaleMegapixels),
loraName: typeof value.loraName === 'string' ? value.loraName : undefined,
loraStack: stack.length ? stack : undefined,
sourceStillId: typeof value.sourceStillId === 'string' ? value.sourceStillId : undefined,
referenceStillId: typeof value.referenceStillId === 'string' ? value.referenceStillId : undefined
}
}
export function inferStillPipeline(still: { filename?: string; settings?: StillGenerationSettings | null }) {
if (still.settings?.kind === 'imagev2' || still.settings?.mode) return 'imagev2' as const
if (still.settings?.kind === 'image') return 'image' as const
if (/aigen_v2_/i.test(String(still.filename || ''))) return 'imagev2' as const
if (/aigen[_-]?edit/i.test(String(still.filename || ''))) return 'image' as const
return null
}
function composeTaskLabel(task?: ImageV2Task) {
if (task === 'outfit') return 'Outfit'
if (task === 'identity') return 'Identity'
if (task === 'face_lock') return 'Face lock'
if (task === 'scene') return 'Scene'
return ''
}
export function stillModeLabel(still: { filename?: string; settings?: StillGenerationSettings | null }) {
const settings = still.settings
if (settings?.kind === 'imagev2' || settings?.mode) {
const mode = settings.mode === 'generate'
? 'Generate'
: settings.mode === 'compose'
? 'Compose'
: settings.mode === 'refine'
? 'Refine'
: 'Edit'
const task = settings.mode === 'compose' ? composeTaskLabel(settings.task) : ''
const engine = settings.engine === 'krea' ? 'Krea' : settings.engine === 'flux' ? 'Flux' : ''
return [engine ? `Image v2 · ${mode} · ${engine}` : `Image v2 · ${mode}`, task].filter(Boolean).join(' · ')
}
if (settings?.kind === 'image') return 'Image edit'
if (/aigen_v2_/i.test(String(still.filename || ''))) return 'Image v2'
if (/aigen[_-]?edit/i.test(String(still.filename || ''))) return 'Image edit'
return ''
}
export function stillSettingsRows(still: { width?: number; height?: number; settings?: StillGenerationSettings | null }) {
const settings = still.settings
const rows: SettingsRow[] = []
const mode = stillModeLabel(still)
if (mode) rows.push({ label: 'Mode', value: mode })
if (!settings) return rows
if (settings.kind === 'imagev2') {
if (settings.mode === 'generate' && settings.width && settings.height) {
rows.push({ label: 'Size', value: `${settings.width}×${settings.height}` })
} else if (still.width && still.height) {
rows.push({ label: 'Output', value: `${still.width}×${still.height}` })
}
if (settings.engine) {
rows.push({ label: 'Engine', value: settings.engine === 'krea' ? 'Krea' : 'Flux' })
}
if (isXaigenStudio() && (settings.snofsModel != null || settings.snofsClip != null)) {
rows.push({ label: 'Concept LoRA', value: formatPair(settings.snofsModel, settings.snofsClip) })
}
if (settings.mode !== 'generate' && (settings.consistencyModel != null || settings.consistencyClip != null)) {
rows.push({ label: 'Consistency', value: formatPair(settings.consistencyModel, settings.consistencyClip) })
}
if (settings.mode === 'refine' && settings.strength != null) {
rows.push({ label: 'Strength', value: formatNum(settings.strength) })
}
if (settings.mode !== 'generate' && settings.megapixels != null) {
rows.push({ label: 'Scale to MP', value: formatNum(settings.megapixels) })
}
} else {
if (still.width && still.height) rows.push({ label: 'Output', value: `${still.width}×${still.height}` })
if (settings.scaleToTotalPixels) {
rows.push({ label: 'Scale to MP', value: formatNum(settings.scaleMegapixels ?? 1) })
}
const loras = formatLoras(settings.loraStack, settings.loraName)
if (loras) rows.push({ label: 'LoRA', value: loras })
}
if (settings.steps != null) rows.push({ label: 'Steps', value: String(Math.round(settings.steps)) })
if (settings.cfg != null) rows.push({ label: 'CFG', value: formatNum(settings.cfg) })
if (settings.seed != null) rows.push({ label: 'Seed', value: String(Math.round(settings.seed)) })
if (settings.turbo === true) rows.push({ label: 'Turbo', value: 'On' })
if (settings.negative?.trim()) rows.push({ label: 'Negative', value: settings.negative.trim() })
return rows
}
function videoWorkflowLabel(workflow?: string) {
if (workflow === 'v2') return 'MiniMax I2V v2'
if (workflow === 't2v') return 'MiniMax T2V'
if (workflow === 'ltx') return 'LTX Pink Cherry'
if (workflow === 'ltx-t2v') return 'LTX T2V'
if (workflow === 'v1') return 'MiniMax I2V'
return workflow || ''
}
export function clipSettingsRows(clip: {
width?: number
height?: number
duration?: number
steps?: number
turbo?: boolean
seed?: number
cfg?: number
fps?: number
samplerName?: string
scheduler?: string
aspect?: string
sound?: boolean
workflow?: VideoWorkflowId | string
loraName?: string
loraStack?: LoraStackItem[]
}) {
const rows: SettingsRow[] = []
const workflow = videoWorkflowLabel(clip.workflow)
if (workflow) rows.push({ label: 'Workflow', value: workflow })
if (clip.width && clip.height) rows.push({ label: 'Size', value: `${clip.width}×${clip.height}` })
if (clip.aspect && clip.aspect !== 'auto') rows.push({ label: 'Aspect', value: clip.aspect })
if (typeof clip.duration === 'number' && clip.duration > 0) {
const rounded = Math.round(clip.duration * 10) / 10
rows.push({ label: 'Duration', value: Number.isInteger(rounded) ? `${rounded}s` : `${rounded.toFixed(1)}s` })
}
if (clip.steps != null) {
rows.push({ label: 'Steps', value: clip.turbo ? `${clip.steps} · Turbo` : String(clip.steps) })
} else if (clip.turbo) {
rows.push({ label: 'Turbo', value: 'On' })
}
if (clip.cfg != null) rows.push({ label: 'CFG', value: formatNum(clip.cfg) })
if (clip.fps != null) rows.push({ label: 'FPS', value: String(clip.fps) })
if (clip.samplerName) rows.push({ label: 'Sampler', value: clip.samplerName })
if (clip.scheduler) rows.push({ label: 'Scheduler', value: clip.scheduler })
if (typeof clip.sound === 'boolean') rows.push({ label: 'Sound', value: clip.sound ? 'On' : 'Off' })
if (clip.seed != null) rows.push({ label: 'Seed', value: String(clip.seed) })
const loras = formatLoras(clip.loraStack, clip.loraName)
if (loras) rows.push({ label: 'LoRA', value: loras })
return rows
}