mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
feat(generation): add a 1K/2K base resolution tier to Krea 2
Krea 2 generated at a fixed ~1MP across every aspect ratio. The comfy builds now take a resolution tier where 2K doubles each bucket to ~4MP, which is the low end of what was asked for (CU-868m15v7g) and what the orchestrator is now ready to serve. Defaults to 1K, so per-generation Buzz cost only moves for someone who opts in. The FAL medium/large tiers get no selector: that API takes size + aspectRatio with no width/height, so a tier there would promise dimensions the orchestrator never reads. Edit and community checkpoints keep it, since both always run a comfy build. Mirrored across both graph lanes for the dual-graph window, with three new differential shapes covering comfy-2K, FAL-rejects-tier and edit-keeps-tier. The Resolution control also moves above Aspect Ratio in both forms, and the docs row claiming it lives in the Advanced accordion under Wan/Sora was wrong in both halves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,29 +1,30 @@
|
||||
# Advanced Section Controllers (GenerationForm)
|
||||
|
||||
All controllers/nodes rendered inside the `<AccordionLayout label="Advanced">` section in [GenerationForm.tsx](src/components/generation_v2/GenerationForm.tsx#L757-L1087).
|
||||
All controllers/nodes rendered inside the `<AccordionLayout label="Advanced">` section in [GenerationForm.tsx](src/components/generation_v2/GenerationForm.tsx#L1974-L2629).
|
||||
|
||||
| # | Node Name | Label | Input Component | Description | Conditional |
|
||||
|---|-----------|-------|-----------------|-------------|-------------|
|
||||
| 1 | `resolution` | Resolution | `SegmentedControlWrapper` | Video quality/resolution selector | Wan/Sora video ecosystems |
|
||||
| 2 | `cfgScale` | CFG Scale | `SliderInput` | Controls how closely generation follows the text prompt | — |
|
||||
| 3 | `sampler` | Sampler | `SelectInput` | Sampling method — each produces different results | — |
|
||||
| 4 | `scheduler` | Scheduler | `SelectInput` | Controls the noise schedule during generation | SdCpp ecosystems |
|
||||
| 5 | `steps` | Steps | `SliderInput` | Number of iterations spent generating | — |
|
||||
| 6 | `movementAmplitude` | Movement Amplitude | `SegmentedControlWrapper` | Camera movement and subject action scale | Vidu |
|
||||
| 7 | `seed` | Seed | `SeedInput` | Random seed for reproducibility | — |
|
||||
| 8 | `clipSkip` | CLIP Skip | `SliderInput` | Skip CLIP layers | SD only |
|
||||
| 9 | `denoise` | Denoise Strength | `SliderInput` | Denoising strength for img2img | img2img only (renders `null` when no meta) |
|
||||
| 10 | `vae` | VAE | `ResourceSelectInput` | Additional color and detail improvements | SD only |
|
||||
| 11 | `enhancedCompatibility` | Enhanced Compatibility | `Checkbox` | Toggle enhanced compatibility mode | — |
|
||||
| 12 | `usePro` | Pro Mode | `Checkbox` | Higher quality generation (more credits) | Sora |
|
||||
| 13 | `fluxUltraRaw` | Raw Mode | `Checkbox` | More natural, less processed look | Flux Ultra |
|
||||
| 14 | `transparent` | Transparent Background | `Checkbox` | Generate image with transparent background | OpenAI |
|
||||
| 15 | `quality` | Quality | `SelectInput` | Quality level selector | OpenAI |
|
||||
| 16 | `enablePromptEnhancer` | Enhance prompt | `Checkbox` | Automatically improve prompt for better results | Video ecosystems |
|
||||
| 17 | `draft` | Draft Mode | `Checkbox` | Generate faster at lower quality | Wan v2.2-5b |
|
||||
| 18 | `shift` | Shift | `SliderInput` | Shift parameter | Wan v2.2, v2.2-5b |
|
||||
| 19 | `interpolatorModel` | Interpolator | `SelectInput` | Interpolator model selector | Wan v2.2 |
|
||||
| 1 | `cfgScale` | CFG Scale | `SliderInput` | Controls how closely generation follows the text prompt | — |
|
||||
| 2 | `sampler` | Sampler | `SelectInput` | Sampling method — each produces different results | — |
|
||||
| 3 | `scheduler` | Scheduler | `SelectInput` | Controls the noise schedule during generation | SdCpp ecosystems |
|
||||
| 4 | `steps` | Steps | `SliderInput` | Number of iterations spent generating | — |
|
||||
| 5 | `movementAmplitude` | Movement Amplitude | `SegmentedControlWrapper` | Camera movement and subject action scale | Vidu |
|
||||
| 6 | `seed` | Seed | `SeedInput` | Random seed for reproducibility | — |
|
||||
| 7 | `clipSkip` | CLIP Skip | `SliderInput` | Skip CLIP layers | SD only |
|
||||
| 8 | `denoise` | Denoise Strength | `SliderInput` | Denoising strength for img2img | img2img only (renders `null` when no meta) |
|
||||
| 9 | `vae` | VAE | `ResourceSelectInput` | Additional color and detail improvements | SD only |
|
||||
| 10 | `enhancedCompatibility` | Enhanced Compatibility | `Checkbox` | Toggle enhanced compatibility mode | — |
|
||||
| 11 | `usePro` | Pro Mode | `Checkbox` | Higher quality generation (more credits) | Sora |
|
||||
| 12 | `fluxUltraRaw` | Raw Mode | `Checkbox` | More natural, less processed look | Flux Ultra |
|
||||
| 13 | `transparent` | Transparent Background | `Checkbox` | Generate image with transparent background | OpenAI |
|
||||
| 14 | `quality` | Quality | `SelectInput` | Quality level selector | OpenAI |
|
||||
| 15 | `enablePromptEnhancer` | Enhance prompt | `Checkbox` | Automatically improve prompt for better results | Video ecosystems |
|
||||
| 16 | `draft` | Draft Mode | `Checkbox` | Generate faster at lower quality | Wan v2.2-5b |
|
||||
| 17 | `shift` | Shift | `SliderInput` | Shift parameter | Wan v2.2, v2.2-5b |
|
||||
| 18 | `interpolatorModel` | Interpolator | `SelectInput` | Interpolator model selector | Wan v2.2 |
|
||||
|
||||
**Note:** There is also a commented-out duplicate `draft` controller (lines 1075–1086) labeled "Turbo Mode" for Wan v2.2 — currently inactive.
|
||||
**Not in this section:** `resolution` renders in the main form body, directly above `aspectRatio` — not in Advanced. It is a quality tier on the video ecosystems and a base-resolution tier on the image ecosystems that expose it (Seedream, Nano Banana, Lens, HiDream-O1, and Krea 2's comfy builds, where 1K/2K scales the aspect-ratio dimensions).
|
||||
|
||||
**Note:** There is also a commented-out duplicate `draft` controller (lines 2587–2598) labeled "Turbo Mode" for Wan v2.2 — currently inactive.
|
||||
|
||||
All controllers use the `<Controller graph={graph} name="..." />` pattern and only render when the current workflow/ecosystem graph exposes the corresponding node.
|
||||
|
||||
@@ -442,7 +442,7 @@ home); full suite + typecheck + lint green; Briant reviews the final diff.
|
||||
| image: flux2 (dev/flex/pro/max) | DONE | DONE | mode by model.id; only dev carries resources |
|
||||
| image: flux2-klein (9B/9B-base/4B/4B-base) | DONE | DONE | FOUR ecosystems share the graph — mode from ecosystem, not model. negativePrompt is NOT a snippet target (v1's own comment claims it self-registers; the differential says no). Handler pins distilled steps/cfg even though the graph exposes a steps slider — v1 quirk, mirrored |
|
||||
| image: boogu (base/turbo/edit/editTurbo) | DONE | DONE | workflowVersions: version options are WORKFLOW-scoped and the MODEL WINS the workflow (probed — an edit checkpoint on txt2img parses as img2img:edit, model kept; v1's index-remap transform is dead code). The cross-workflow rewrite lives in `reconcile.ts` (`deriveWorkflowFromModel`, registry `workflowScopedVersions`) since a family cannot change the root workflow. negativePrompt only in base/edit modes |
|
||||
| image: krea2 (fal/raw/turbo/editRaw/editTurbo) | DONE | DONE | locked checkpoint; version selector splits across FAL (creativity/styleReferences) and comfy (LoRA/cfg/steps) engines; img2img:edit narrows the picker to comfy builds and the lock substitutes FAL tiers to the edit default. NO cross-workflow pull (unlike boogu) |
|
||||
| image: krea2 (fal/raw/turbo/editRaw/editTurbo) | DONE | DONE | locked checkpoint; version selector splits across FAL (creativity/styleReferences) and comfy (LoRA/cfg/steps) engines; img2img:edit narrows the picker to comfy builds and the lock substitutes FAL tiers to the edit default. 1K/2K resolution tier on the comfy builds only (`defFamily` AR, 2K doubles each side); FAL takes size + aspectRatio, so no tier. NO cross-workflow pull (unlike boogu) |
|
||||
| image: imagen4 | DONE | DONE | locked single version; negative prompt registers at top level |
|
||||
| image: pony-v7 | DONE | DONE | LoRAs, no negative prompt; ecosystem-defaults lock applies |
|
||||
| image: reve | DONE | DONE | locked; AR on txt2img only, edit takes 1-4 reference frames |
|
||||
|
||||
@@ -336,6 +336,23 @@ export function ImageGenerationForm({ store }: { store: GenerationStore }) {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
graph={imageHub}
|
||||
name="resolution"
|
||||
render={({ value, meta, onChange }) => (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Input.Label>Resolution</Input.Label>
|
||||
<SegmentedControlWrapper
|
||||
value={value}
|
||||
onChange={(v) => onChange(v as typeof value)}
|
||||
data={(meta as { options: { label: string; value: string }[] }).options.map((o) => ({
|
||||
label: o.label,
|
||||
value: o.value,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
graph={imageHub}
|
||||
name="aspectRatio"
|
||||
@@ -356,23 +373,6 @@ export function ImageGenerationForm({ store }: { store: GenerationStore }) {
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
graph={imageHub}
|
||||
name="resolution"
|
||||
render={({ value, meta, onChange }) => (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Input.Label>Resolution</Input.Label>
|
||||
<SegmentedControlWrapper
|
||||
value={value}
|
||||
onChange={(v) => onChange(v as typeof value)}
|
||||
data={(meta as { options: { label: string; value: string }[] }).options.map((o) => ({
|
||||
label: o.label,
|
||||
value: o.value,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
graph={imageHub}
|
||||
name="creativity"
|
||||
|
||||
@@ -1772,6 +1772,25 @@ export function GenerationForm() {
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Resolution */}
|
||||
<Controller
|
||||
graph={graph}
|
||||
name="resolution"
|
||||
render={({ value, meta, onChange }) => (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Input.Label>Resolution</Input.Label>
|
||||
<SegmentedControlWrapper
|
||||
value={value}
|
||||
onChange={(v) => onChange(v as typeof value)}
|
||||
data={meta.options.map((o: { label: string; value: string }) => ({
|
||||
label: o.label,
|
||||
value: o.value,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Aspect ratio */}
|
||||
<Controller
|
||||
graph={graph}
|
||||
@@ -1858,25 +1877,6 @@ export function GenerationForm() {
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Resolution (Wan/Sora video quality) */}
|
||||
<Controller
|
||||
graph={graph}
|
||||
name="resolution"
|
||||
render={({ value, meta, onChange }) => (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Input.Label>Resolution</Input.Label>
|
||||
<SegmentedControlWrapper
|
||||
value={value}
|
||||
onChange={(v) => onChange(v as typeof value)}
|
||||
data={meta.options.map((o: { label: string; value: string }) => ({
|
||||
label: o.label,
|
||||
value: o.value,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Prime (Wan 3.0) */}
|
||||
<Controller
|
||||
graph={graph}
|
||||
|
||||
@@ -75,3 +75,38 @@ describe('krea2 img2img:edit bases', () => {
|
||||
expect(stepsMax(g)).toBe(60);
|
||||
});
|
||||
});
|
||||
|
||||
describe('krea2 base resolution tier', () => {
|
||||
const dimsOf = (g: any) => {
|
||||
const { width, height } = g.getSnapshot().aspectRatio ?? {};
|
||||
return { width, height };
|
||||
};
|
||||
|
||||
it('is offered on the comfy builds and defaults to 1K', () => {
|
||||
const g = init('txt2img', krea2VersionIds.raw);
|
||||
expect(g.hasNode('resolution')).toBe(true);
|
||||
expect(g.getSnapshot().resolution).toBe('1K');
|
||||
expect(dimsOf(g)).toEqual({ width: 1024, height: 1024 });
|
||||
});
|
||||
|
||||
it('doubles every side at 2K, keeping each divisible by 32', () => {
|
||||
const g = init('txt2img', krea2VersionIds.turbo);
|
||||
g.set({ resolution: '2K', aspectRatio: '16:9' });
|
||||
expect(dimsOf(g)).toEqual({ width: 2752, height: 1536 });
|
||||
const { width, height } = dimsOf(g);
|
||||
expect(width % 32).toBe(0);
|
||||
expect(height % 32).toBe(0);
|
||||
});
|
||||
|
||||
// FAL takes size + aspectRatio only, so a tier there would promise dimensions
|
||||
// the orchestrator never reads.
|
||||
it('is hidden on the FAL size tiers', () => {
|
||||
expect(init('txt2img', krea2VersionIds.medium).hasNode('resolution')).toBe(false);
|
||||
expect(init('txt2img', krea2VersionIds.large).hasNode('resolution')).toBe(false);
|
||||
});
|
||||
|
||||
it('survives on edit, where a FAL version still runs a comfy build', () => {
|
||||
expect(init('img2img:edit', krea2VersionIds.medium).hasNode('resolution')).toBe(true);
|
||||
expect(init('txt2img', communityVersionId).hasNode('resolution')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -136,12 +136,31 @@ const krea2AspectRatioDimensions = {
|
||||
'9:16': { width: 768, height: 1376 },
|
||||
} satisfies Partial<Record<GenerationAspectRatio, AspectRatioDimensions>>;
|
||||
|
||||
const krea2AspectRatioOptions = (
|
||||
Object.keys(krea2AspectRatioDimensions) as (keyof typeof krea2AspectRatioDimensions)[]
|
||||
).map((ratio) => {
|
||||
const { width, height } = krea2AspectRatioDimensions[ratio];
|
||||
return { label: ratio, value: ratio, width, height };
|
||||
});
|
||||
/** 2K doubles each ~1MP bucket to ~4MP. The FAL tiers take `size` + `aspectRatio` only, so they get no tier. */
|
||||
const krea2ResolutionOptions = [
|
||||
{ label: '1K', value: '1K' },
|
||||
{ label: '2K', value: '2K' },
|
||||
] as const;
|
||||
|
||||
const krea2AspectRatioOptionsFor = (scale: number) =>
|
||||
(Object.keys(krea2AspectRatioDimensions) as (keyof typeof krea2AspectRatioDimensions)[]).map(
|
||||
(ratio) => {
|
||||
const { width, height } = krea2AspectRatioDimensions[ratio];
|
||||
return { label: ratio, value: ratio, width: width * scale, height: height * scale };
|
||||
}
|
||||
);
|
||||
|
||||
const krea2AspectRatioOptionsByResolution: Record<
|
||||
string,
|
||||
ReturnType<typeof krea2AspectRatioOptionsFor>
|
||||
> = {
|
||||
'1K': krea2AspectRatioOptionsFor(1),
|
||||
'2K': krea2AspectRatioOptionsFor(2),
|
||||
};
|
||||
|
||||
/** Edit runs a comfy build whichever version is picked, so it keeps the tier; unknown ids are community checkpoints, comfy-only. */
|
||||
const krea2UsesComfyEngine = (modelId?: number, workflow?: string) =>
|
||||
workflow === 'img2img:edit' || modelId === undefined || !krea2VersionIdToSize.has(modelId);
|
||||
|
||||
/** Standard preferred ratios — substitute 4:5 for 3:4 since Krea lacks 3:4. */
|
||||
const krea2PriorityRatios = ['16:9', '4:3', '1:1', '4:5', '9:16'];
|
||||
@@ -326,13 +345,23 @@ export const krea2Graph = new DataGraph<
|
||||
},
|
||||
['workflow']
|
||||
)
|
||||
.node(
|
||||
'resolution',
|
||||
(ctx) => ({
|
||||
...enumNode({ options: krea2ResolutionOptions, defaultValue: '1K' }),
|
||||
when: krea2UsesComfyEngine(ctx.model?.id, ctx.workflow),
|
||||
}),
|
||||
['model', 'workflow']
|
||||
)
|
||||
.node(
|
||||
'aspectRatio',
|
||||
aspectRatioNode({
|
||||
options: krea2AspectRatioOptions,
|
||||
defaultValue: '1:1',
|
||||
priorityOptions: krea2PriorityRatios,
|
||||
})
|
||||
(ctx) =>
|
||||
aspectRatioNode({
|
||||
options: krea2AspectRatioOptionsByResolution[ctx.resolution ?? '1K'],
|
||||
defaultValue: '1:1',
|
||||
priorityOptions: krea2PriorityRatios,
|
||||
}),
|
||||
['resolution']
|
||||
)
|
||||
// Unknown ids are community checkpoints. Only the comfy builds can load one via
|
||||
// `diffusionModel`, so they fall back off the FAL tiers — and to the full-step
|
||||
|
||||
@@ -203,6 +203,9 @@ const KREA2_ONLY_SHAPES: AnyRecord[] = [
|
||||
styleReferences: [{ image: 'https://example.com/s.png', strength: 0.7 }, {}],
|
||||
},
|
||||
{ prompt: 'a cat', model: 3072332 }, // comfy turbo
|
||||
{ prompt: 'a cat', model: 3072332, resolution: '2K' }, // comfy 2K tier
|
||||
{ prompt: 'a cat', model: 2983023, resolution: '2K' }, // FAL: no tier to accept
|
||||
{ prompt: 'a cat', model: 2983023, images: [IMG], resolution: '2K' }, // edit keeps the tier
|
||||
{ prompt: 'a cat', model: 2983023, images: [IMG] }, // fal model where edit rows substitute
|
||||
// community checkpoints (unknown ids): comfy-only, so they fall to the RAW
|
||||
// full-step build — and to editRaw on edit (kaydaxter's custom-checkpoint fix)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { branch, defineGraph } from 'form-graph';
|
||||
import { branch, defFamily, defineGraph } from 'form-graph';
|
||||
import { checkpointDef } from '../checkpoint';
|
||||
import { SEED, aspectRatioDef, enumDef, imagesDef, workflowScoped } from '../defs';
|
||||
import {
|
||||
@@ -70,10 +70,29 @@ const krea2AspectRatioDimensions: Record<string, { width: number; height: number
|
||||
'9:16': { width: 768, height: 1376 },
|
||||
};
|
||||
|
||||
const krea2AspectRatioOptions = Object.keys(krea2AspectRatioDimensions).map((ratio) => {
|
||||
const { width, height } = krea2AspectRatioDimensions[ratio]!;
|
||||
return { label: ratio, value: ratio, width, height };
|
||||
});
|
||||
/** 2K doubles each ~1MP bucket to ~4MP. */
|
||||
const krea2ResolutionOptions = [
|
||||
{ label: '1K', value: '1K' },
|
||||
{ label: '2K', value: '2K' },
|
||||
] as const;
|
||||
|
||||
const krea2AspectRatioOptionsFor = (scale: number) =>
|
||||
Object.keys(krea2AspectRatioDimensions).map((ratio) => {
|
||||
const { width, height } = krea2AspectRatioDimensions[ratio]!;
|
||||
return { label: ratio, value: ratio, width: width * scale, height: height * scale };
|
||||
});
|
||||
|
||||
const krea2AspectRatioOptionsByResolution: Record<
|
||||
string,
|
||||
ReturnType<typeof krea2AspectRatioOptionsFor>
|
||||
> = {
|
||||
'1K': krea2AspectRatioOptionsFor(1),
|
||||
'2K': krea2AspectRatioOptionsFor(2),
|
||||
};
|
||||
|
||||
/** Edit and community checkpoints are comfy-only, so they keep the tier too. */
|
||||
const krea2UsesComfyEngine = (modelId?: number, workflow?: string) =>
|
||||
workflow === 'img2img:edit' || modelId == null || !krea2VersionIdToSize.has(modelId);
|
||||
|
||||
const krea2PriorityRatios = ['16:9', '4:3', '1:1', '4:5', '9:16'];
|
||||
|
||||
@@ -207,6 +226,17 @@ const editRaw = defineGraph<Krea2VariantExt>()
|
||||
/** Tagged: v1's `krea2Variant` computed becomes the branch key. */
|
||||
const variants = branch('krea2Variant', variantOf, { fal, raw, turbo, editRaw, editTurbo });
|
||||
|
||||
const RESOLUTION = enumDef({ options: krea2ResolutionOptions, default: '1K' });
|
||||
|
||||
const AR = defFamily((resolution: string) =>
|
||||
aspectRatioDef({
|
||||
options:
|
||||
krea2AspectRatioOptionsByResolution[resolution] ?? krea2AspectRatioOptionsByResolution['1K']!,
|
||||
default: '1:1',
|
||||
priorityOptions: krea2PriorityRatios,
|
||||
})
|
||||
);
|
||||
|
||||
export const krea2 = defineGraph<FamilyExt>({ scope: familyScope })
|
||||
.field('model', ({ _ext }) => {
|
||||
const isEdit = _ext.workflow === 'img2img:edit';
|
||||
@@ -218,14 +248,10 @@ export const krea2 = defineGraph<FamilyExt>({ scope: familyScope })
|
||||
defaultModelId: isEdit ? KREA2_EDIT_DEFAULT_VERSION_ID : krea2VersionIds.raw,
|
||||
});
|
||||
})
|
||||
.field(
|
||||
'aspectRatio',
|
||||
aspectRatioDef({
|
||||
options: krea2AspectRatioOptions,
|
||||
default: '1:1',
|
||||
priorityOptions: krea2PriorityRatios,
|
||||
})
|
||||
.field('resolution', ({ model, _ext }) =>
|
||||
krea2UsesComfyEngine(modelIdOf(model) ?? undefined, _ext.workflow) ? RESOLUTION : null
|
||||
)
|
||||
.field('aspectRatio', ({ resolution }) => AR(resolution ?? '1K'))
|
||||
.use(variants)
|
||||
// negativePrompt exists only in the comfy variants; its in-branch snippet
|
||||
// registration never fires
|
||||
|
||||
Reference in New Issue
Block a user