Merge remote-tracking branch 'origin/diffusion-auto-badges' into fold-integration

# Conflicts:
#	studio/backend/models/inference.py
#	studio/frontend/src/features/images/images-page.tsx
This commit is contained in:
Daniel Han 2026-07-07 01:08:43 +00:00
commit 2e855a018d
7 changed files with 294 additions and 4 deletions

View file

@ -4,6 +4,16 @@
import { authFetch } from "@/features/auth";
import { readFastApiError } from "@/lib/format-fastapi-error";
// One Advanced control's resolved value + provenance, for the "Auto: X" badges. `value`
// is the engaged value (a scheme/mode string, null when off, or a boolean for cpu_offload);
// `source` is "auto" (this backend decided) or "explicit" (the caller set it); `reason` is
// the short why shown as a tooltip.
export interface DiffusionResolvedControl {
value: string | boolean | null;
source: "auto" | "explicit";
reason: string;
}
export interface DiffusionStatus {
loaded: boolean;
repo_id: string | null;
@ -24,6 +34,11 @@ export interface DiffusionStatus {
// Whether the loaded model can apply a ControlNet (drives the ControlNet picker's enabled
// state). Diffusers only, for families with a ControlNet pipeline; false otherwise.
supports_controlnet?: boolean;
// Per-Advanced-control provenance, keyed by control name (speed_mode, transformer_quant,
// attention_backend, memory_mode, transformer_cache, cpu_offload). Present only when a
// model is loaded on a backend that records it; the "Auto: X" badges read it. Absent on
// older backends.
resolved?: Record<string, DiffusionResolvedControl> | null;
}
export interface DiffusionGenerateProgress {
@ -174,6 +189,28 @@ export async function getDiffusionStatus(): Promise<DiffusionStatus> {
return parseJson(await authFetch("/api/inference/images/status"));
}
// One family's bf16 component sizes + estimated resident footprint per quant scheme
// (from GET /api/inference/images/info). Hardware-independent, so it can be fetched before
// anything is loaded to size the Advanced Dtype tradeoff.
export interface DiffusionInferenceInfo {
family: string;
transformer_bf16_gb: number;
text_encoders_bf16_gb: number;
vae_bf16_gb: number;
// Estimated resident GB keyed by scheme: bf16, int8, fp8, mxfp8, nvfp4.
estimated_resident_gb: Record<string, number>;
}
export interface DiffusionInferenceInfoResponse {
families: DiffusionInferenceInfo[];
}
/** Static per-family footprint summary for the Advanced Dtype tradeoff. Hardware-independent
* (served from the pure auto-policy tables), so it is safe to fetch before a load. */
export async function getDiffusionInferenceInfo(): Promise<DiffusionInferenceInfoResponse> {
return parseJson(await authFetch("/api/inference/images/info"));
}
export async function getDiffusionLoadProgress(): Promise<DiffusionLoadProgress> {
return parseJson(await authFetch("/api/inference/images/load-progress"));
}

View file

@ -470,10 +470,44 @@ function Field({
);
}
// The engaged value of a resolved Advanced control, formatted for its "Auto: X" badge.
// Short scheme/mode tokens go uppercase (INT8, FP8, FBCACHE); the attention backend the
// backend reports as `_native_cudnn` shows as cuDNN; cpu_offload's boolean shows On/Off.
function formatResolvedValue(key: string, value: string | boolean | null): string {
if (key === "cpu_offload") return value ? "On" : "Off";
if (value === null || value === "") return "Off";
if (typeof value === "boolean") return value ? "On" : "Off";
if (value === "_native_cudnn" || value.toLowerCase() === "cudnn") return "cuDNN";
return value.toUpperCase();
}
// The "Auto: X" badge for one Advanced control: rendered only when the backend resolved
// that control itself (source === "auto"); an explicit user choice renders nothing. The
// reason is surfaced as a hover tooltip. Muted pill matching the panel's other chips.
function ResolvedBadge({
status,
controlKey,
}: {
status: DiffusionStatus | null;
controlKey: string;
}) {
const resolved = status?.resolved?.[controlKey];
if (!resolved || resolved.source !== "auto") return null;
return (
<span
title={resolved.reason || undefined}
className="shrink-0 rounded-sm bg-muted px-1 py-px text-[9px] font-medium uppercase tracking-wider text-muted-foreground"
>
Auto: {formatResolvedValue(controlKey, resolved.value)}
</span>
);
}
// A compact labeled Select row for the Advanced Options panel.
function AdvancedSelect({
label,
hint,
badge,
desc,
value,
onValueChange,
@ -481,6 +515,8 @@ function AdvancedSelect({
}: {
label: string;
hint?: ReactNode;
// An optional inline badge next to the label (e.g. the "Auto: X" resolved-value pill).
badge?: ReactNode;
// A short always-visible description under the row (the hint tooltip carries the full
// detail). Used for controls whose label alone does not convey what they do.
desc?: string;
@ -494,6 +530,7 @@ function AdvancedSelect({
<span className="flex shrink-0 items-center gap-1 whitespace-nowrap text-xs font-medium text-muted-foreground">
{label}
{hint && <InfoHint>{hint}</InfoHint>}
{badge}
</span>
<Select value={value} onValueChange={onValueChange}>
<SelectTrigger className="h-8 w-[160px] text-xs">
@ -1866,6 +1903,7 @@ export function ImagesPage({ active = true }: { active?: boolean }) {
<AdvancedSelect
label="Speed"
hint="Auto picks per model (GGUF compiles, dense stays eager). eager = fused kernels, no compile. default/max add torch.compile (max also TF32 + fused QKV)."
badge={<ResolvedBadge status={status} controlKey="speed_mode" />}
value={speedMode}
onValueChange={(v) => setSpeedMode(v as typeof speedMode)}
options={[
@ -1883,6 +1921,7 @@ export function ImagesPage({ active = true }: { active?: boolean }) {
<AdvancedSelect
label="Dtype"
hint="Transformer compute dtype. Auto picks the fastest precision the hardware supports (at least INT8 on a capable GPU; FP8 on data-center cards) by loading the FULL base model and quantising its transformer onto low-precision tensor cores, and falls back to running the GGUF as-is when the device, VRAM or disk can't take it. Off always runs the GGUF as-is."
badge={<ResolvedBadge status={status} controlKey="transformer_quant" />}
value={transformerQuant}
onValueChange={(v) => setTransformerQuant(v as typeof transformerQuant)}
options={[
@ -1905,6 +1944,7 @@ export function ImagesPage({ active = true }: { active?: boolean }) {
<AdvancedSelect
label="Attention"
hint="Attention kernel. Auto upgrades to cuDNN fused attention on NVIDIA when a speed profile is active. sage is INT8 attention (small quality cost)."
badge={<ResolvedBadge status={status} controlKey="attention_backend" />}
value={attentionBackend}
onValueChange={(v) => setAttentionBackend(v as typeof attentionBackend)}
options={[
@ -1918,6 +1958,7 @@ export function ImagesPage({ active = true }: { active?: boolean }) {
<AdvancedSelect
label="Memory"
hint="auto measures free VRAM. fast keeps everything resident. balanced streams the transformer. low_vram offloads every component (lowest VRAM, slower)."
badge={<ResolvedBadge status={status} controlKey="memory_mode" />}
value={memoryMode}
onValueChange={(v) => setMemoryMode(v as typeof memoryMode)}
options={[
@ -1930,6 +1971,7 @@ export function ImagesPage({ active = true }: { active?: boolean }) {
<AdvancedSelect
label="Step cache"
hint="First-Block-Cache reuses the transformer tail across steps for many-step models (~1.4x). Auto enables it for many-step schedules and skips it for few-step distilled models; Off disables it entirely."
badge={<ResolvedBadge status={status} controlKey="transformer_cache" />}
value={transformerCache}
onValueChange={(v) => setTransformerCache(v as typeof transformerCache)}
options={[
@ -1942,6 +1984,7 @@ export function ImagesPage({ active = true }: { active?: boolean }) {
<span className="flex items-center gap-1 text-xs font-medium text-muted-foreground">
CPU offload
<InfoHint>Offload to CPU to fit low-VRAM cards (slower). Overridden by Memory mode when that is not Auto.</InfoHint>
<ResolvedBadge status={status} controlKey="cpu_offload" />
</span>
<Switch checked={cpuOffload} onCheckedChange={setCpuOffload} />
</div>