Spaces:
Runtime error
Runtime error
File size: 4,292 Bytes
6cc3d86 982bad4 6cc3d86 38b7ac0 6cc3d86 a70128c 38b7ac0 6cc3d86 982bad4 6cc3d86 06e36cd 6cc3d86 06e36cd 6cc3d86 6249f41 6cc3d86 df31fd7 6cc3d86 a70128c 38b7ac0 6cc3d86 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | import {
createContext,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
import { apiFetch } from "../api";
export type LabeledModel = { value: string; label: string };
export type ThinkingLevelOption = { value: string; label: string };
/** Video model entry; `supports_reference_images` / `supports_4k` / `supports_end_frame` default to true if omitted. */
export type VideoModelOption = {
value: string;
label: string;
supports_reference_images?: boolean;
supports_4k?: boolean;
/** Start/end mode: if false, only start frame (e.g. Veo 3 family). */
supports_end_frame?: boolean;
};
export type GenerationOptions = {
image: {
models: LabeledModel[];
aspect_ratios: string[];
resolutions: string[];
/** @deprecated No longer used by the UI; image model + thinking are combined in ImageGen. */
thinking_levels?: ThinkingLevelOption[];
};
video: {
models: VideoModelOption[];
aspect_ratios: string[];
resolutions: string[];
durations_seconds: number[];
};
video_frames: {
models: VideoModelOption[];
aspect_ratios: string[];
resolutions: string[];
durations_seconds: number[];
};
};
const GenerationOptionsContext = createContext<GenerationOptions | null>(null);
export function GenerationOptionsProvider({ children }: { children: ReactNode }) {
const [data, setData] = useState<GenerationOptions | null>(null);
const [err, setErr] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
apiFetch("/api/config/generation-options")
.then(async (r) => {
if (r.status === 401) {
window.location.assign("/login");
return null;
}
const j = await r.json().catch(() => ({}));
if (!r.ok) {
const detail = (j as { detail?: unknown }).detail;
const msg =
typeof detail === "string"
? detail
: Array.isArray(detail)
? JSON.stringify(detail)
: r.statusText;
throw new Error(msg);
}
return j as GenerationOptions;
})
.then((opts) => {
if (cancelled || opts === null) return;
setData(opts);
})
.catch((e: unknown) => {
if (!cancelled)
setErr(e instanceof Error ? e.message : "Failed to load options");
});
return () => {
cancelled = true;
};
}, []);
if (err) {
return (
<div className="p-8 text-sm text-red-800">
页面暂时打不开,请稍后再试。
{err ? (
<span className="block mt-2 text-xs text-mist">({err})</span>
) : null}
</div>
);
}
if (!data) {
return (
<div className="p-8 text-sm text-mist">加载中…</div>
);
}
return (
<GenerationOptionsContext.Provider value={data}>
{children}
</GenerationOptionsContext.Provider>
);
}
const FAST_MODEL_IDS = new Set([
"gemini-3.1-flash-image-preview",
"veo-3.0-fast-generate-001",
"veo-3.1-fast-generate-preview",
]);
/** Prefer Flash/Fast id, else label 快速, else first or `fallback`. */
export function defaultFastModelValue(
models: { value: string; label?: string }[],
fallback: string,
): string {
const byId = models.find((m) => FAST_MODEL_IDS.has(m.value));
if (byId) return byId.value;
const byLabel = models.find((m) => m.label?.includes("快速"));
return byLabel?.value ?? models[0]?.value ?? fallback;
}
export function modelSupportsReferenceImages(
models: VideoModelOption[],
value: string,
): boolean {
const m = models.find((x) => x.value === value);
return m?.supports_reference_images !== false;
}
export function modelSupports4k(models: VideoModelOption[], value: string): boolean {
const m = models.find((x) => x.value === value);
return m?.supports_4k !== false;
}
export function modelSupportsEndFrame(models: VideoModelOption[], value: string): boolean {
const m = models.find((x) => x.value === value);
return m?.supports_end_frame !== false;
}
export function useGenerationOptions(): GenerationOptions {
const ctx = useContext(GenerationOptionsContext);
if (!ctx) {
throw new Error("useGenerationOptions must be used inside GenerationOptionsProvider");
}
return ctx;
}
|