feat: adapt image tuning by engine

This commit is contained in:
inman
2026-05-29 14:32:02 +08:00
parent 4b21d2999c
commit e36f28a668
12 changed files with 138 additions and 10 deletions

View File

@@ -13,12 +13,31 @@ import type { PromptMaterial } from "@/lib/prompt/assembler";
type GenerateMode = "image" | "video";
type StudioMode = GenerateMode | ImageEditMode;
type MaterialKind = PromptMaterial["type"];
type ImageGenerateEngine = "jimeng" | "evolink";
type MentionState = {
start: number;
query: string;
};
type HealthCapability = {
id: string;
engine?: string;
};
const jimengInfluenceOptions = [
{ id: "creative", label: "创意 35", scale: 35 },
{ id: "balanced", label: "均衡 50", scale: 50 },
{ id: "precise", label: "贴合 70", scale: 70 },
{ id: "strict", label: "严格 85", scale: 85 }
];
const evolinkQualityOptions = [
{ id: "low", label: "快速", quality: "low" },
{ id: "medium", label: "标准", quality: "medium" },
{ id: "high", label: "精细", quality: "high" }
];
const imageSizePresets = [
{ label: "1:1", width: 2048, height: 2048 },
{ label: "4:3", width: 2304, height: 1728 },
@@ -41,7 +60,9 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: StudioMo
const [error, setError] = useState<string | null>(null);
const [notice, setNotice] = useState<string | null>(null);
const [imageSize, setImageSize] = useState(imageSizePresets[0]);
const [imageScale, setImageScale] = useState(50);
const [imageEngine, setImageEngine] = useState<ImageGenerateEngine>("jimeng");
const [jimengInfluence, setJimengInfluence] = useState(jimengInfluenceOptions[1].id);
const [evolinkQuality, setEvolinkQuality] = useState(evolinkQualityOptions[1].id);
const [forceSingle, setForceSingle] = useState(true);
const [videoRatio, setVideoRatio] = useState("9:16");
const [videoDuration, setVideoDuration] = useState(VIDEO_DURATION_DEFAULT);
@@ -59,6 +80,8 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: StudioMo
const isImageEditMode = mode === "inpaint" || mode === "upscale";
const generateMode: GenerateMode = mode === "video" ? "video" : "image";
const prompt = promptByMode[generateMode];
const selectedJimengInfluence = jimengInfluenceOptions.find((option) => option.id === jimengInfluence) || jimengInfluenceOptions[1];
const selectedEvolinkQuality = evolinkQualityOptions.find((option) => option.id === evolinkQuality) || evolinkQualityOptions[1];
const visibleMaterials = pageItems(materials, materialPage, MATERIAL_PAGE_SIZE);
const materialPageOffset = (clampPage(materialPage, materials.length, MATERIAL_PAGE_SIZE) - 1) * MATERIAL_PAGE_SIZE;
const mentionSuggestions = useMemo(() => {
@@ -90,6 +113,20 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: StudioMo
if (materialBoardRef.current) revealChildren(materialBoardRef.current, ".material-card");
}, [materials.length, materialPage]);
useEffect(() => {
let active = true;
void fetch("/api/health")
.then((response) => response.ok ? response.json() : null)
.then((payload: { capabilities?: HealthCapability[] } | null) => {
const engine = payload?.capabilities?.find((capability) => capability.id === "image.generate")?.engine;
if (active && (engine === "evolink" || engine === "jimeng")) setImageEngine(engine);
})
.catch(() => undefined);
return () => {
active = false;
};
}, []);
function setPrompt(value: string) {
setPromptByMode((items) => ({ ...items, [generateMode]: value }));
}
@@ -229,7 +266,9 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: StudioMo
materials: materials.filter((material) => material.type === "image"),
width: imageSize.width,
height: imageSize.height,
scale: imageScale,
...(imageEngine === "evolink"
? { quality: selectedEvolinkQuality.quality }
: { scale: selectedJimengInfluence.scale }),
force_single: forceSingle
}
: {
@@ -432,9 +471,17 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: StudioMo
{imageSizePresets.map((preset) => <option key={preset.label}>{preset.label}</option>)}
</select>
</div>
<div className="field inline-field range-field">
<label htmlFor="imageScale"> {imageScale}</label>
<input id="imageScale" type="range" min="1" max="100" value={imageScale} onChange={(event) => setImageScale(Number(event.target.value))} />
<div className="field inline-field">
<label htmlFor="imageEngineTuning">{imageEngine === "evolink" ? "生成质量" : "文本影响"}</label>
{imageEngine === "evolink" ? (
<select id="imageEngineTuning" value={evolinkQuality} onChange={(event) => setEvolinkQuality(event.target.value)}>
{evolinkQualityOptions.map((option) => <option key={option.id} value={option.id}>{option.label}</option>)}
</select>
) : (
<select id="imageEngineTuning" value={jimengInfluence} onChange={(event) => setJimengInfluence(event.target.value)}>
{jimengInfluenceOptions.map((option) => <option key={option.id} value={option.id}>{option.label}</option>)}
</select>
)}
</div>
<label className="toggle-line">
<input type="checkbox" checked={forceSingle} onChange={(event) => setForceSingle(event.target.checked)} />