Files
aigen/utils/generationPresets.ts
T
TowstyandCursor 21fe2412f9 Add Image v2 as a sibling of Image edit, with separate Klein graphs.
Keep the v1 Image tab on its dual-branch template. The host agent follows Desktop's live port so Coolify stays on :8198.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 21:22:37 -05:00

91 lines
2.8 KiB
TypeScript

import type { LoraStackItem } from '~/utils/loras'
import type { VideoWorkflowId } from '~/utils/videoModels'
import type { ImageV2PresetSettings } from '~/utils/imageV2'
export type GenerationPresetKind = 'video' | 'image' | 'imagev2'
export type VideoPresetSettings = {
workflow?: VideoWorkflowId
turbo?: boolean
cfg?: number
fps?: number
samplerName?: string
scheduler?: string
duration?: number
aspect?: string
sound?: boolean
}
export type ImagePresetSettings = {
steps?: number
cfg?: number
negative?: string
scaleToTotalPixels?: boolean
scaleMegapixels?: number
}
export const IMAGE_STEPS_MIN = 1
export const IMAGE_STEPS_MAX = 50
export const IMAGE_CFG_MIN = 0
export const IMAGE_CFG_MAX = 20
export const IMAGE_CFG_STEP = 0.1
export const IMAGE_SCALE_MP_MIN = 0.01
export const IMAGE_SCALE_MP_MAX = 16
export const IMAGE_SCALE_MP_STEP = 0.01
export const IMAGE_SCALE_MP_DEFAULT = 1
export const VIDEO_CFG_MIN = 0
export const VIDEO_CFG_MAX = 20
export const VIDEO_CFG_STEP = 0.1
export function clampImageSteps(raw: unknown, fallback = 20) {
const value = Number(raw)
if (!Number.isFinite(value)) return fallback
return Math.min(IMAGE_STEPS_MAX, Math.max(IMAGE_STEPS_MIN, Math.round(value)))
}
export function clampImageCfg(raw: unknown, fallback = 1) {
const value = Number(raw)
if (!Number.isFinite(value)) return fallback
const snapped = Math.round(value / IMAGE_CFG_STEP) * IMAGE_CFG_STEP
return Math.min(IMAGE_CFG_MAX, Math.max(IMAGE_CFG_MIN, Math.round(snapped * 10) / 10))
}
export function clampImageScaleMegapixels(raw: unknown, fallback = IMAGE_SCALE_MP_DEFAULT) {
const value = Number(raw)
if (!Number.isFinite(value)) return fallback
const snapped = Math.round(value / IMAGE_SCALE_MP_STEP) * IMAGE_SCALE_MP_STEP
return Math.min(IMAGE_SCALE_MP_MAX, Math.max(IMAGE_SCALE_MP_MIN, Math.round(snapped * 100) / 100))
}
export function parseImageScaleToTotalPixels(raw: unknown) {
return raw === true || raw === 'true' || raw === '1' || raw === 1
}
export function clampVideoCfg(raw: unknown, fallback = 1.5) {
const value = Number(raw)
if (!Number.isFinite(value)) return fallback
const snapped = Math.round(value / VIDEO_CFG_STEP) * VIDEO_CFG_STEP
return Math.min(VIDEO_CFG_MAX, Math.max(VIDEO_CFG_MIN, Math.round(snapped * 10) / 10))
}
export type GenerationPreset = {
id: string
name: string
kind: GenerationPresetKind
createdAt: number
updatedAt: number
loraStack: LoraStackItem[]
settings: VideoPresetSettings | ImagePresetSettings | ImageV2PresetSettings
}
export function parseGenerationPresetKind(raw: unknown): GenerationPresetKind {
if (raw === 'image') return 'image'
if (raw === 'imagev2') return 'imagev2'
return 'video'
}
export function normalizePresetName(raw: unknown) {
return String(raw || '').trim().replace(/\s+/g, ' ').slice(0, 80)
}