增加2.5模型调用

This commit is contained in:
andy committed 2026-09-23 15:10:13 +08:00
1 parent 3f29ba716a
commit be655b6569
32 files changed
+1075 -56

No files matched your search

+86 -13
View File
@@ -6,6 +6,7 @@ import clsx from "clsx";
import { clampPage, pageItems, Pagination } from "@/components/pagination";
import { SeedreamWorkspace } from "@/components/seedream-workspace";
import { imageSizePresets, imageDimensionsForEngine, imageSizePresetFromDimensions, type ImageSizePreset } from "@/lib/image-size-presets";
import { DEFAULT_IMAGE_MODEL, IMAGE_MODEL_OPTIONS, configuredImageModel, evolinkModelCapability, imageModelAfterHealth, imageModelFromJob, imageModelLabel, templateImageModel } from "@/lib/image-models";
import { SeedreamLayerResultViewer, type SeedreamLayerOutput } from "@/components/seedream-layer-result-viewer";
import { crossfadeIn, pulseFeedback, revealChildren, runScopedMotion } from "@/lib/ui/motion";
import {
@@ -74,6 +75,8 @@ type HealthCapability = {
id: string;
engine?: string;
reqKey?: string;
models?: Array<{ id: string; label: string }>;
evolink?: { reqKey?: string; models?: Array<{ id: string; label: string }> };
};
type ImageTemplate = {
@@ -83,6 +86,7 @@ type ImageTemplate = {
previewImageUrl?: string;
settings: {
engine?: ImageGenerateEngine;
model?: string;
width?: number;
height?: number;
forceSingle?: boolean;
@@ -101,6 +105,7 @@ type TemplateForm = {
prompt: string;
previewImageUrl: string;
engine: ImageGenerateEngine;
evolinkModel: string;
jimengInfluence: string;
evolinkQuality: string;
seedreamSize: string;
@@ -123,6 +128,8 @@ const evolinkQualityOptions = [
{ id: "high", label: "精细", quality: "high" }
];
const evolinkModelOptions = IMAGE_MODEL_OPTIONS;
const seedreamSizeOptions = ["1K", "1.5K", "2K"] as const;
const seedreamLayerSizeOptions = ["auto", "1K", "1.5K", "2K"] as const;
const seedreamOutputFormatOptions = [
@@ -138,6 +145,8 @@ type TemplateConsoleSnapshot = {
promptByMode: Record<GenerateMode, string>;
imageSize: ImageSizePreset;
imageEngine: ImageGenerateEngine;
evolinkModel: string;
evolinkModelChosen: boolean;
jimengInfluence: string;
evolinkQuality: string;
seedreamSize: string;
@@ -155,6 +164,7 @@ const defaultTemplateForm: TemplateForm = {
prompt: "",
previewImageUrl: "",
engine: "jimeng",
evolinkModel: DEFAULT_IMAGE_MODEL,
jimengInfluence: jimengInfluenceOptions[1].id,
evolinkQuality: evolinkQualityOptions[1].id,
seedreamSize: "1.5K",
@@ -197,6 +207,9 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
const [notice, setNotice] = useState<string | null>(null);
const [imageSize, setImageSize] = useState(imageSizePresets[0]);
const [imageEngine, setImageEngine] = useState<ImageGenerateEngine>("jimeng");
const [evolinkModel, setEvolinkModel] = useState<string>(DEFAULT_IMAGE_MODEL);
const [evolinkDefaultModel, setEvolinkDefaultModel] = useState<string>(DEFAULT_IMAGE_MODEL);
const [evolinkModels, setEvolinkModels] = useState<Array<{ id: string; label: string }>>([...evolinkModelOptions]);
const [jimengInfluence, setJimengInfluence] = useState(jimengInfluenceOptions[1].id);
const [evolinkQuality, setEvolinkQuality] = useState(evolinkQualityOptions[1].id);
const [seedreamSize, setSeedreamSize] = useState("1.5K");
@@ -226,6 +239,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
const promptRef = useRef<HTMLTextAreaElement | null>(null);
const templatePromptRef = useRef<HTMLTextAreaElement | null>(null);
const materialDraftInputRef = useRef<HTMLInputElement | null>(null);
const evolinkModelChosenRef = useRef(false);
const generateMode: GenerateMode = mode === "video" ? "video" : "image";
const prompt = promptByMode[generateMode];
@@ -400,6 +414,13 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
if (active && (imageCapability?.engine === "evolink" || imageCapability?.engine === "jimeng" || imageCapability?.engine === "bailian" || imageCapability?.engine === "seedream")) {
setImageEngine(imageCapability.engine);
}
const evolinkCapability = evolinkModelCapability(imageCapability);
if (active && evolinkCapability) {
const models = normalizedImageModelOptions(evolinkCapability.models);
setEvolinkModels(models);
setEvolinkDefaultModel(configuredImageModel(models, evolinkCapability.reqKey));
setEvolinkModel((current) => imageModelAfterHealth(models, evolinkCapability.reqKey, current, evolinkModelChosenRef.current));
}
if (active && (videoCapability?.engine === "seedance" || videoCapability?.engine === "bailian" || videoCapability?.engine === "minimax")) {
setVideoEngine(videoCapability.engine);
if (videoCapability.engine === "seedance") {
@@ -498,7 +519,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
active = false;
window.clearTimeout(timer);
};
}, [generateMode, imageCreationMode, imageEngine, imageSize.height, imageSize.width, seedreamInteractiveSourceKeys, seedreamRequestAnnotations, seedreamLayerSize, seedreamOptimizeMode, seedreamOutputFormat, seedreamSize, seedreamSourceKey, selectedEvolinkQuality.quality, selectedJimengInfluence.scale, videoDuration, videoEngine, videoModel, videoRatio, videoResolution, materials, generationInputReady, prompt]);
}, [generateMode, imageCreationMode, imageEngine, evolinkModel, imageSize.height, imageSize.width, seedreamInteractiveSourceKeys, seedreamRequestAnnotations, seedreamLayerSize, seedreamOptimizeMode, seedreamOutputFormat, seedreamSize, seedreamSourceKey, selectedEvolinkQuality.quality, selectedJimengInfluence.scale, videoDuration, videoEngine, videoModel, videoRatio, videoResolution, materials, generationInputReady, prompt]);
async function loadImageTemplates(isActive: () => boolean = () => true) {
setTemplatesLoading(true);
@@ -775,6 +796,8 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
promptByMode: { ...promptByMode },
imageSize,
imageEngine,
evolinkModel,
evolinkModelChosen: evolinkModelChosenRef.current,
jimengInfluence,
evolinkQuality,
seedreamSize,
@@ -797,6 +820,8 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
setPromptByMode(snapshot.promptByMode);
setImageSize(snapshot.imageSize);
setImageEngine(snapshot.imageEngine);
evolinkModelChosenRef.current = snapshot.evolinkModelChosen;
setEvolinkModel(snapshot.evolinkModel);
setJimengInfluence(snapshot.jimengInfluence);
setEvolinkQuality(snapshot.evolinkQuality);
setSeedreamSize(snapshot.seedreamSize);
@@ -806,6 +831,8 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
setPromptByMode((items) => ({ ...items, [generateMode]: "" }));
setImageSize(imageSizePresets[0]);
setImageEngine(imageCreationMode === "basic" ? "jimeng" : "seedream");
evolinkModelChosenRef.current = false;
setEvolinkModel(evolinkDefaultModel);
setJimengInfluence(jimengInfluenceOptions[1].id);
setEvolinkQuality(evolinkQualityOptions[1].id);
setSeedreamSize("1.5K");
@@ -822,6 +849,10 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
if (generateMode === "image") {
const templateEngine = normalizeImageEngine(template.settings.engine);
if (templateEngine) setImageEngine(imageCreationMode === "basic" ? templateEngine : "seedream");
if (templateEngine === "evolink") {
evolinkModelChosenRef.current = Boolean(template.settings.model);
setEvolinkModel(templateImageModel(template.settings.model, evolinkDefaultModel));
}
const preset = imageSizePresetFromDimensions(template.settings.width, template.settings.height);
if (preset) setImageSize(preset);
const templateInfluence = jimengInfluenceOptions.find((option) => option.scale === template.settings.scale);
@@ -839,7 +870,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
}
function resetTemplateForm() {
setTemplateForm(defaultTemplateFormForEngine(imageEngine));
setTemplateForm(defaultTemplateFormForEngine(imageEngine, evolinkModel));
setEditingTemplateId(null);
setTemplateError(null);
setMaterialDraft(null);
@@ -853,7 +884,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
function openTemplateEditor(template: ImageTemplate) {
setTemplateForm({
...formFromTemplate(template),
...formFromTemplate(template, evolinkDefaultModel),
previewImageUrl: storedAssetPreviewUrl(template.previewImageUrl, taskAssets)
});
setEditingTemplateId(template.id);
@@ -889,7 +920,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
const rest = items.filter((item) => item.id !== saved.id);
return [...rest, saved].sort(sortTemplates);
});
setTemplateForm(formFromTemplate(saved));
setTemplateForm(formFromTemplate(saved, evolinkDefaultModel));
setEditingTemplateId(null);
setTemplateEditorOpen(false);
applyTemplateToConsole(saved, isEditing ? "模板已更新并应用。" : "模板已保存并应用。");
@@ -1069,7 +1100,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
materials: materials.filter((material) => material.type === "image"),
...imageDimensionsForEngine(imageSize, imageEngine),
...(imageEngine === "evolink"
? { quality: selectedEvolinkQuality.quality }
? { model: evolinkModel, quality: selectedEvolinkQuality.quality }
: { scale: selectedJimengInfluence.scale }),
force_single: true
};
@@ -1241,6 +1272,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
<div className="image-template-card-footer">
<div className="image-template-card-copy">
<strong>{template.name}</strong>
{template.settings.engine === "evolink" ? <span>{imageModelLabel(templateImageModel(template.settings.model, evolinkDefaultModel))}</span> : null}
</div>
<button
className="image-template-edit-button"
@@ -1481,10 +1513,21 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
<select id="imageEngine" value={specializedImageMode ? "seedream" : imageEngine} disabled={specializedImageMode} onChange={(event) => setImageEngine(normalizeImageEngine(event.target.value) || "jimeng")}>
<option value="jimeng">即梦</option>
<option value="seedream">Seedream 5.0 Pro</option>
<option value="evolink">Image2</option>
<option value="evolink">EvoLink</option>
<option value="bailian">阿里云百炼</option>
</select>
</div>
{!specializedImageMode && imageEngine === "evolink" ? (
<div className="field inline-field">
<label htmlFor="evolinkModel">生图模型</label>
<select id="evolinkModel" value={evolinkModel} onChange={(event) => {
evolinkModelChosenRef.current = true;
setEvolinkModel(event.target.value);
}}>
{modelOptionsWithSelection(evolinkModels, evolinkModel).map((option) => <option key={option.id} value={option.id}>{option.label}</option>)}
</select>
</div>
) : null}
<div className="field inline-field">
<label htmlFor="imageSize">{imageEngine === "seedream" ? "分辨率" : "画幅"}</label>
{imageEngine === "seedream" ? (
@@ -1660,7 +1703,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
/>
</label>
<div className="template-preview-meta" aria-label="模板参数预览">
<span>{imageEngineLabel(templateForm.engine)}</span>
<span>{templateForm.engine === "evolink" ? imageModelLabel(templateForm.evolinkModel) : imageEngineLabel(templateForm.engine)}</span>
<span>{templateForm.engine === "evolink" ? selectedTemplateEvolinkQuality.label : templateForm.engine === "bailian" ? "智能推理" : templateForm.engine === "seedream" ? selectedTemplateSeedreamOptimizeMode.label : selectedTemplateJimengInfluence.label}</span>
<span>{templateForm.engine === "seedream" ? templateForm.seedreamSize : templateForm.size}</span>
</div>
@@ -1687,7 +1730,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
aria-pressed={templateForm.engine === "evolink"}
onClick={() => setTemplateForm((form) => ({ ...form, engine: "evolink" }))}
>
Image2
EvoLink
</button>
<button
type="button"
@@ -1707,6 +1750,14 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
</button>
</div>
</div>
{templateForm.engine === "evolink" ? (
<label className="field">
<span>生图模型</span>
<select value={templateForm.evolinkModel} onChange={(event) => setTemplateForm((form) => ({ ...form, evolinkModel: event.target.value }))}>
{modelOptionsWithSelection(evolinkModels, templateForm.evolinkModel).map((option) => <option key={option.id} value={option.id}>{option.label}</option>)}
</select>
</label>
) : null}
<label className="field">
<span>{templateForm.engine === "evolink" ? "生成质量" : templateForm.engine === "bailian" ? "生成模式" : templateForm.engine === "seedream" ? "提示词优化" : "文本影响"}</span>
{templateForm.engine === "evolink" ? (
@@ -1896,6 +1947,7 @@ export function CreateStudio({ initialMode = "image" }: { initialMode?: Generate
<h3 title={taskName(job)}>{taskName(job)}</h3>
<div className="create-task-meta">
<span className={`status ${taskStatusClass(job, taskAssetById)}`}>{statusLabel(job.status, Boolean(firstOutputAsset(job, taskAssetById)))}</span>
{imageModelFromJob(job) ? <span>{imageModelLabel(imageModelFromJob(job)!)}</span> : null}
<span>{durationLabel(job, durationNow)}</span>
<span className="billing-task-cost">{billingLabel(job)}</span>
</div>
@@ -2290,6 +2342,8 @@ function taskParameterEntries(job: GenerationJob) {
const engine = stringValue(values.engine);
if (engine) entries.push({ label: "引擎", value: taskEngineLabel(engine) });
if (job.capability === "image.generate") {
const model = imageModelFromJob(job);
if (model) entries.push({ label: "生图模型", value: imageModelLabel(model) });
const creationMode = taskImageCreationMode(job);
if (creationMode) entries.push({ label: "创作方式", value: seedreamCreationModeLabel(creationMode) });
const width = scalarValue(values.width);
@@ -2328,7 +2382,7 @@ function taskImageCreationMode(job: GenerationJob): SeedreamCreationMode | undef
function taskEngineLabel(value: string) {
if (value === "jimeng") return "即梦";
if (value === "seedream") return "Seedream 5.0 Pro";
if (value === "evolink") return "Image2";
if (value === "evolink") return "EvoLink";
if (value === "bailian") return "阿里云百炼";
if (value === "seedance") return "Seedance";
if (value === "minimax") return "MiniMax H3";
@@ -2458,7 +2512,7 @@ function renderMaterialPreview(material: PromptMaterial, size: "normal" | "tiny"
);
}
function formFromTemplate(template: ImageTemplate): TemplateForm {
function formFromTemplate(template: ImageTemplate, defaultModel: string): TemplateForm {
const engine = normalizeImageEngine(template.settings.engine) || "jimeng";
const jimengInfluence = jimengInfluenceOptions.find((option) => option.scale === template.settings.scale)?.id || jimengInfluenceOptions[1].id;
const evolinkQuality = evolinkQualityOptions.find((option) => option.quality === template.settings.quality)?.id || evolinkQualityOptions[1].id;
@@ -2467,6 +2521,7 @@ function formFromTemplate(template: ImageTemplate): TemplateForm {
prompt: template.prompt,
previewImageUrl: template.previewImageUrl || "",
engine,
evolinkModel: templateImageModel(template.settings.model, defaultModel),
jimengInfluence,
evolinkQuality,
seedreamSize: seedreamSizeOptions.includes(template.settings.size as (typeof seedreamSizeOptions)[number]) ? template.settings.size! : "1.5K",
@@ -2492,7 +2547,7 @@ function templatePayloadFromForm(form: TemplateForm) {
? { size: form.seedreamSize, outputFormat: form.seedreamOutputFormat, optimizeMode: form.seedreamOptimizeMode }
: imageDimensionsForEngine(size, form.engine)),
...(form.engine === "evolink"
? { quality: selectedQuality.quality }
? { model: form.evolinkModel, quality: selectedQuality.quality }
: form.engine === "jimeng"
? { scale: selectedInfluence.scale }
: {})
@@ -2501,10 +2556,11 @@ function templatePayloadFromForm(form: TemplateForm) {
};
}
function defaultTemplateFormForEngine(engine: ImageGenerateEngine): TemplateForm {
function defaultTemplateFormForEngine(engine: ImageGenerateEngine, defaultModel: string): TemplateForm {
return {
...defaultTemplateForm,
engine,
evolinkModel: defaultModel,
jimengInfluence: jimengInfluenceOptions[1].id,
evolinkQuality: evolinkQualityOptions[1].id,
seedreamSize: "1.5K",
@@ -2522,8 +2578,25 @@ function normalizeImageEngine(value?: string): ImageGenerateEngine | undefined {
return undefined;
}
function normalizedImageModelOptions(models: HealthCapability["models"]): Array<{ id: string; label: string }> {
if (!Array.isArray(models)) return [...IMAGE_MODEL_OPTIONS];
const seen = new Set<string>();
const options = models.filter((model): model is { id: string; label: string } => {
if (!model || typeof model.id !== "string" || !/^[a-z0-9][a-z0-9._-]{0,99}$/.test(model.id) || seen.has(model.id)) return false;
seen.add(model.id);
return true;
}).map((model) => ({ id: model.id, label: typeof model.label === "string" && model.label.trim() ? model.label : imageModelLabel(model.id) }));
return options.length ? options : [...IMAGE_MODEL_OPTIONS];
}
function modelOptionsWithSelection(options: ReadonlyArray<{ id: string; label: string }>, selected: string) {
return options.some((option) => option.id === selected)
? options
: [...options, { id: selected, label: imageModelLabel(selected) }];
}
function imageEngineLabel(engine: ImageGenerateEngine) {
return engine === "evolink" ? "Image2" : engine === "bailian" ? "百炼" : engine === "seedream" ? "Seedream 5.0" : "即梦";
return engine === "evolink" ? "EvoLink" : engine === "bailian" ? "百炼" : engine === "seedream" ? "Seedream 5.0" : "即梦";
}
function sortTemplates(a: ImageTemplate, b: ImageTemplate) {