2026-06-02 14:18:26 +08:00
|
|
|
import { isServerRequestError } from "../../api/serverConnection";
|
|
|
|
|
import { ENTERPRISE_VIDEO_MODEL_OPTIONS } from "../../utils/enterpriseVideoPolicy";
|
|
|
|
|
import type { WebGenerationPreviewTask } from "../../types";
|
2026-06-05 01:00:33 +08:00
|
|
|
import type { GenerationLifecycleStatus, TaskRefundStatus, TextTokenUsage } from "../../utils/taskLifecycle";
|
2026-06-02 14:18:26 +08:00
|
|
|
import type { ReactNode } from "react";
|
|
|
|
|
|
|
|
|
|
export type WorkbenchMode = "chat" | "image" | "video";
|
|
|
|
|
export type ToolbarMenuId =
|
|
|
|
|
| "studio-mode"
|
2026-06-05 18:01:55 +08:00
|
|
|
| "chat-model"
|
|
|
|
|
| "chat-speed"
|
|
|
|
|
| "chat-depth"
|
2026-06-02 14:18:26 +08:00
|
|
|
| "image-model"
|
|
|
|
|
| "image-settings"
|
|
|
|
|
| "image-grid-mode"
|
|
|
|
|
| "video-model"
|
|
|
|
|
| "video-mode"
|
|
|
|
|
| "video-ratio"
|
|
|
|
|
| "video-duration"
|
|
|
|
|
| "video-quality"
|
|
|
|
|
| null;
|
|
|
|
|
export type ReferenceKind = "image" | "video" | "audio" | "file";
|
|
|
|
|
|
|
|
|
|
export interface WorkbenchOption {
|
|
|
|
|
value: string;
|
|
|
|
|
label: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
badge?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WorkbenchFieldGroup {
|
|
|
|
|
label: string;
|
|
|
|
|
value: string;
|
|
|
|
|
options: WorkbenchOption[];
|
|
|
|
|
onChange: (value: string) => void;
|
|
|
|
|
kind?: "ratio" | "pill";
|
|
|
|
|
columns?: 2 | 3 | 4;
|
|
|
|
|
icon?: ReactNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ReferenceItem {
|
|
|
|
|
id: string;
|
|
|
|
|
kind: ReferenceKind;
|
|
|
|
|
name: string;
|
|
|
|
|
previewUrl?: string;
|
|
|
|
|
file?: File;
|
|
|
|
|
remoteUrl?: string;
|
|
|
|
|
token: string;
|
|
|
|
|
fingerprint?: string;
|
|
|
|
|
originalSize?: number;
|
|
|
|
|
compressed?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type PromptMentionItem = Pick<ReferenceItem, "token" | "id" | "name" | "kind" | "previewUrl" | "remoteUrl">;
|
|
|
|
|
|
|
|
|
|
export interface PromptMentionTokenRange {
|
|
|
|
|
start: number;
|
|
|
|
|
end: number;
|
|
|
|
|
item: PromptMentionItem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ChatAttachment {
|
|
|
|
|
kind: ReferenceKind;
|
|
|
|
|
name: string;
|
|
|
|
|
token: string;
|
|
|
|
|
previewUrl?: string;
|
|
|
|
|
remoteUrl?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ChatMessage {
|
|
|
|
|
id: string;
|
|
|
|
|
role: "user" | "assistant";
|
|
|
|
|
author: string;
|
|
|
|
|
mode: WorkbenchMode;
|
|
|
|
|
body: string;
|
|
|
|
|
prompt?: string;
|
|
|
|
|
createdAt: string;
|
2026-06-05 01:00:33 +08:00
|
|
|
status?: "thinking" | "queued" | "completed" | "failed" | "stopping" | "local_timeout";
|
|
|
|
|
taskLifecycleStatus?: GenerationLifecycleStatus;
|
|
|
|
|
taskRefundStatus?: TaskRefundStatus;
|
|
|
|
|
taskUsage?: TextTokenUsage;
|
2026-06-02 14:18:26 +08:00
|
|
|
taskId?: string;
|
|
|
|
|
conversationId?: number;
|
|
|
|
|
taskProgress?: number;
|
|
|
|
|
taskStatusLabel?: string;
|
|
|
|
|
attachments?: ChatAttachment[];
|
|
|
|
|
resultUrl?: string;
|
|
|
|
|
resultType?: "image" | "video";
|
|
|
|
|
resultOriginalUrl?: string;
|
|
|
|
|
resultOssKey?: string;
|
|
|
|
|
resultMimeType?: string;
|
|
|
|
|
result?: {
|
|
|
|
|
title: string;
|
|
|
|
|
summary: string;
|
|
|
|
|
specs: string[];
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface DeleteDialogState {
|
|
|
|
|
projectId: string;
|
|
|
|
|
title: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WorkbenchKeepaliveTask {
|
|
|
|
|
taskId: string;
|
|
|
|
|
conversationId: number;
|
|
|
|
|
assistantMessageId: string;
|
|
|
|
|
concurrencySlotId?: string;
|
|
|
|
|
operation?: "generation" | "video-super-resolution";
|
|
|
|
|
mode: "image" | "video";
|
|
|
|
|
modelLabel: string;
|
|
|
|
|
specs: string[];
|
|
|
|
|
referenceCount: number;
|
|
|
|
|
progress: number;
|
|
|
|
|
statusLabel: string;
|
|
|
|
|
startedAt: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface WorkbenchResultActionPayload {
|
|
|
|
|
title: string;
|
|
|
|
|
prompt: string;
|
|
|
|
|
resultUrl: string;
|
|
|
|
|
resultType: "image" | "video";
|
|
|
|
|
taskId?: string;
|
|
|
|
|
resultOriginalUrl?: string;
|
|
|
|
|
resultOssKey?: string;
|
|
|
|
|
resultMimeType?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ─── Constants ───────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export const MESSAGE_STORAGE_KEY = "omniai-web-workbench-messages";
|
|
|
|
|
export const ACTIVE_CONVERSATION_STORAGE_KEY = "omniai-web-workbench-active-conversation-id";
|
|
|
|
|
export const PROMPT_HISTORY_STORAGE_KEY = "omniai-web-workbench-prompt-history";
|
|
|
|
|
export const TASK_KEEPALIVE_STORAGE_KEY = "omniai-web-workbench-active-tasks";
|
|
|
|
|
export const WORKBENCH_TASK_STALE_MS = 6 * 60 * 60 * 1000;
|
|
|
|
|
export const WORKBENCH_TASK_MAX_POLL_FAILURES = 10;
|
|
|
|
|
export const REFERENCE_IMAGE_COMPRESS_THRESHOLD = 10 * 1024 * 1024;
|
|
|
|
|
export const REFERENCE_IMAGE_MAX_DIMENSION = 1920;
|
|
|
|
|
export const REFERENCE_IMAGE_INITIAL_QUALITY = 0.84;
|
|
|
|
|
export const REFERENCE_IMAGE_MIN_QUALITY = 0.62;
|
|
|
|
|
export const CHAT_MODEL = "gemini-3.1-pro";
|
|
|
|
|
|
2026-06-05 18:01:55 +08:00
|
|
|
export const CHAT_MODEL_OPTIONS: WorkbenchOption[] = [
|
|
|
|
|
{ value: "gemini", label: "Gemini" },
|
|
|
|
|
{ value: "wanxian", label: "万相" },
|
|
|
|
|
{ value: "deepseek", label: "DeepSeek" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const THINKING_SPEED_OPTIONS: WorkbenchOption[] = [
|
|
|
|
|
{ value: "default", label: "默认" },
|
2026-06-08 21:30:48 +08:00
|
|
|
{ value: "high", label: "思考速度:高" },
|
|
|
|
|
{ value: "ultra", label: "思考速度:急速" },
|
2026-06-05 18:01:55 +08:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const THINKING_DEPTH_OPTIONS: WorkbenchOption[] = [
|
|
|
|
|
{ value: "default", label: "默认" },
|
2026-06-08 21:30:48 +08:00
|
|
|
{ value: "strong", label: "推理深度:强" },
|
|
|
|
|
{ value: "extreme", label: "推理深度:极限" },
|
2026-06-05 18:01:55 +08:00
|
|
|
];
|
|
|
|
|
|
2026-06-02 14:18:26 +08:00
|
|
|
export const CHAT_NATURAL_SYSTEM_PROMPT = [
|
|
|
|
|
"你是 OmniAI 的创作协作助手,像一个正在一起工作的同伴一样说话。",
|
|
|
|
|
`默认使用自然、简洁的中文,不要官腔,不要机械套话,不要频繁使用“首先、其次、最后”这种模板。`,
|
|
|
|
|
"先直接回应用户当前关心的点;需要拆解时,用短段落或少量要点,把下一步说清楚。",
|
|
|
|
|
`不说“作为一个 AI”,不做空泛总结,不编造不确定的信息。`,
|
|
|
|
|
"当用户在排查问题或调整页面时,优先给判断、原因和可执行的下一步。",
|
|
|
|
|
].join("\n");
|
|
|
|
|
|
|
|
|
|
export const CHAT_TURN_STYLE_REMINDER = [
|
|
|
|
|
"本轮回答继续保持像正常人协作的口吻:",
|
|
|
|
|
`不要以"好的,以下是""当然可以""根据你的需求"这类模板开头。`,
|
|
|
|
|
"能一句话说清就先一句话说清;需要展开时再分点。",
|
|
|
|
|
"少用宏大标题,多用具体判断和下一步动作。",
|
|
|
|
|
].join("\n");
|
|
|
|
|
|
|
|
|
|
export const NON_CONVERSATIONAL_ASSISTANT_TEXT = new Set([
|
|
|
|
|
"我先看一下上下文,马上接上。",
|
|
|
|
|
"我在整理,马上说清楚。",
|
|
|
|
|
"正在读取当前模式、模型、规格和参考素材,准备创建生成任务。",
|
|
|
|
|
"Task submitted, generating...",
|
|
|
|
|
"任务已提交,正在生成中...",
|
|
|
|
|
"AI 正在整理回答...",
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
export const MODE_META: Record<
|
|
|
|
|
WorkbenchMode,
|
|
|
|
|
{
|
|
|
|
|
label: string;
|
|
|
|
|
menuLabel: string;
|
|
|
|
|
accent: string;
|
|
|
|
|
placeholder: string;
|
|
|
|
|
description: string;
|
|
|
|
|
subline: string;
|
|
|
|
|
taskType: WebGenerationPreviewTask["type"];
|
|
|
|
|
}
|
|
|
|
|
> = {
|
|
|
|
|
chat: {
|
|
|
|
|
label: "OmniChat",
|
|
|
|
|
menuLabel: "对话模式",
|
|
|
|
|
accent: "#6be7ff",
|
|
|
|
|
placeholder: "把创意、脚本、素材要求或工作流目标发给我",
|
|
|
|
|
description: "直接对话、拆解需求、整理上下文,并把想法推进到可执行结果。",
|
|
|
|
|
subline: "适合连续协作、问答推演、脚本整理和工作流规划。",
|
|
|
|
|
taskType: "agent",
|
|
|
|
|
},
|
|
|
|
|
image: {
|
|
|
|
|
label: "图像生成",
|
|
|
|
|
menuLabel: "图像生成",
|
|
|
|
|
accent: "#00b1cc",
|
|
|
|
|
placeholder: "描述角色、场景、商品图、首帧或尾帧画面",
|
|
|
|
|
description: "在同一界面完成文生图、图生图、参考图管理和候选筛选。",
|
|
|
|
|
subline: "模型、比例、清晰度和多宫格保持在同一条工作链里。",
|
|
|
|
|
taskType: "image",
|
|
|
|
|
},
|
|
|
|
|
video: {
|
|
|
|
|
label: "视频生成",
|
|
|
|
|
menuLabel: "视频生成",
|
|
|
|
|
accent: "#2197ff",
|
|
|
|
|
placeholder: "描述成片目标、人物、场景、镜头运动、节奏、比例和时长",
|
|
|
|
|
description: "用统一工作台管理起始帧、动作描述、镜头节奏和视频输出。",
|
|
|
|
|
subline: "支持首尾帧、参考素材、比例、时长和画质等关键设置。",
|
|
|
|
|
taskType: "video",
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const MODE_OPTIONS: WorkbenchOption[] = (Object.keys(MODE_META) as WorkbenchMode[]).map((mode) => ({
|
|
|
|
|
value: mode,
|
|
|
|
|
label: MODE_META[mode].menuLabel,
|
|
|
|
|
description: MODE_META[mode].subline,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
export const IMAGE_MODEL_OPTIONS: WorkbenchOption[] = [
|
2026-06-08 18:26:44 +08:00
|
|
|
{ value: "wan2.7-image-pro", label: "wan 2.7 Pro" },
|
2026-06-08 11:39:28 +08:00
|
|
|
{ value: "wan2.7-image", label: "wan 2.7" },
|
2026-06-08 14:40:47 +08:00
|
|
|
{ value: "gpt-image-2", label: "omni-GPT" },
|
|
|
|
|
{ value: "gpt-image-2-vip", label: "omni-GPT VIP" },
|
|
|
|
|
{ value: "nano-banana-pro", label: "omni-水果 Pro" },
|
|
|
|
|
{ value: "nano-banana-2", label: "omni-水果 2" },
|
|
|
|
|
{ value: "nano-banana-fast", label: "omni-水果" },
|
2026-06-02 14:18:26 +08:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const VIDEO_MODEL_OPTIONS: WorkbenchOption[] = ENTERPRISE_VIDEO_MODEL_OPTIONS.map((option) => ({ ...option }));
|
|
|
|
|
export const RATIO_OPTIONS: WorkbenchOption[] = [
|
|
|
|
|
{ value: "21:9", label: "21:9" },
|
|
|
|
|
{ value: "16:9", label: "16:9" },
|
|
|
|
|
{ value: "4:3", label: "4:3" },
|
|
|
|
|
{ value: "1:1", label: "1:1" },
|
|
|
|
|
{ value: "3:4", label: "3:4" },
|
|
|
|
|
{ value: "9:16", label: "9:16" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const GRID_MODE_OPTIONS: WorkbenchOption[] = [
|
|
|
|
|
{ value: "single", label: "单图" },
|
|
|
|
|
{ value: "grid-4", label: "4 宫格" },
|
|
|
|
|
{ value: "grid-9", label: "9 宫格" },
|
|
|
|
|
{ value: "grid-25", label: "25 宫格" },
|
|
|
|
|
];
|
|
|
|
|
|
2026-06-05 01:48:13 +08:00
|
|
|
export const GRID_SUPPORTED_MODELS = new Set([
|
|
|
|
|
"wan2.7-image-pro",
|
|
|
|
|
"wan2.7-image",
|
|
|
|
|
"gpt-image-2",
|
|
|
|
|
"gpt-image-2-vip",
|
|
|
|
|
]);
|
|
|
|
|
|
2026-06-02 14:18:26 +08:00
|
|
|
export const VIDEO_FRAME_OPTIONS: WorkbenchOption[] = [
|
|
|
|
|
{ value: "omni", label: "全能参考" },
|
|
|
|
|
{ value: "start-end", label: "首尾帧" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export const VIDEO_DURATION_OPTIONS: WorkbenchOption[] = [
|
|
|
|
|
{ value: "5", label: "5s" },
|
|
|
|
|
{ value: "6", label: "6s" },
|
|
|
|
|
{ value: "7", label: "7s" },
|
|
|
|
|
{ value: "8", label: "8s" },
|
|
|
|
|
{ value: "9", label: "9s" },
|
|
|
|
|
{ value: "10", label: "10s" },
|
|
|
|
|
{ value: "11", label: "11s" },
|
|
|
|
|
{ value: "12", label: "12s" },
|
|
|
|
|
{ value: "13", label: "13s" },
|
|
|
|
|
{ value: "14", label: "14s" },
|
|
|
|
|
{ value: "15", label: "15s" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// ─── Shared helpers ──────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
export function getCachedRole(): string {
|
|
|
|
|
try {
|
|
|
|
|
const raw = window.localStorage.getItem("omniai-web-session");
|
|
|
|
|
if (!raw) return "";
|
|
|
|
|
return String(JSON.parse(raw)?.user?.role || "").trim().toLowerCase();
|
|
|
|
|
} catch { return ""; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getSessionUserId(): string {
|
|
|
|
|
try {
|
|
|
|
|
const raw = window.localStorage.getItem("omniai-web-session");
|
|
|
|
|
if (!raw) return "anon";
|
|
|
|
|
const id = JSON.parse(raw)?.user?.id;
|
|
|
|
|
return id ? String(id) : "anon";
|
|
|
|
|
} catch { return "anon"; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function userKey(base: string): string {
|
|
|
|
|
return `${base}:${getSessionUserId()}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createId(prefix: string) {
|
|
|
|
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function formatWorkbenchTimestamp(date = new Date()): string {
|
|
|
|
|
const year = date.getFullYear();
|
|
|
|
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
|
|
|
const day = String(date.getDate()).padStart(2, "0");
|
|
|
|
|
const hours = String(date.getHours()).padStart(2, "0");
|
|
|
|
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function parseWorkbenchTimestampValue(value: string): number {
|
|
|
|
|
const matched = value.match(/^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2})/);
|
|
|
|
|
if (matched) {
|
|
|
|
|
const [, year, month, day, hours, minutes] = matched;
|
|
|
|
|
return new Date(Number(year), Number(month) - 1, Number(day), Number(hours), Number(minutes)).getTime();
|
|
|
|
|
}
|
|
|
|
|
const parsed = Date.parse(value);
|
|
|
|
|
return Number.isFinite(parsed) ? parsed : NaN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function buildChatAttachments(items: ReferenceItem[]): ChatAttachment[] {
|
|
|
|
|
return items.map((item) => ({
|
|
|
|
|
kind: item.kind,
|
|
|
|
|
name: item.name,
|
|
|
|
|
token: item.token,
|
|
|
|
|
previewUrl: item.remoteUrl || item.previewUrl,
|
|
|
|
|
remoteUrl: item.remoteUrl,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function buildNaturalChatHistoryMessages(messages: ChatMessage[]): Array<{ role: "user" | "assistant"; content: string }> {
|
|
|
|
|
return messages
|
|
|
|
|
.filter((message) => {
|
|
|
|
|
const body = message.body.trim();
|
|
|
|
|
if (!body) return false;
|
|
|
|
|
if (message.role === "user") return true;
|
|
|
|
|
if (message.mode !== "chat") return false;
|
|
|
|
|
if (message.status === "thinking" || message.status === "queued") return false;
|
|
|
|
|
if (NON_CONVERSATIONAL_ASSISTANT_TEXT.has(body)) return false;
|
|
|
|
|
return true;
|
|
|
|
|
})
|
|
|
|
|
.slice(-10)
|
|
|
|
|
.map((message) => ({
|
|
|
|
|
role: message.role,
|
|
|
|
|
content: message.body.trim(),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getErrorText(error: unknown): string {
|
|
|
|
|
return error instanceof Error ? error.message : String(error || "Unknown error");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isAuthFailure(error: unknown): boolean {
|
|
|
|
|
return isServerRequestError(error) && (error.status === 401 || error.status === 403);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isInsufficientBalance(error: unknown): boolean {
|
|
|
|
|
if (isServerRequestError(error) && error.status === 402) return true;
|
|
|
|
|
const msg = error instanceof Error ? error.message : String(error || "");
|
|
|
|
|
return /余额不足|积分不足|insufficient.?balance/i.test(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isInsufficientBalanceMessage(msg: string | undefined | null): boolean {
|
|
|
|
|
if (!msg) return false;
|
|
|
|
|
return /余额不足|积分不足|insufficient.?balance/i.test(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function isTransientMessage(message: ChatMessage): boolean {
|
|
|
|
|
return (message.status === "thinking" || message.status === "queued") && !message.taskId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getPersistableMessages(messages: ChatMessage[]): ChatMessage[] {
|
|
|
|
|
return messages.filter((message, index) => {
|
|
|
|
|
if (isTransientMessage(message)) return false;
|
|
|
|
|
if (message.role === "assistant") return true;
|
|
|
|
|
const nextMessage = messages[index + 1];
|
|
|
|
|
return (
|
|
|
|
|
nextMessage?.role === "assistant" &&
|
|
|
|
|
nextMessage.conversationId === message.conversationId &&
|
|
|
|
|
!isTransientMessage(nextMessage)
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function shouldPersistPatch(patch: Partial<ChatMessage>): boolean {
|
|
|
|
|
return (
|
|
|
|
|
patch.status === "completed" ||
|
|
|
|
|
patch.status === "failed" ||
|
2026-06-05 01:00:33 +08:00
|
|
|
patch.status === "local_timeout" ||
|
|
|
|
|
patch.status === "stopping" ||
|
2026-06-02 14:18:26 +08:00
|
|
|
typeof patch.taskId === "string" ||
|
|
|
|
|
typeof patch.resultUrl === "string" ||
|
|
|
|
|
typeof patch.resultOssKey === "string" ||
|
|
|
|
|
typeof patch.resultOriginalUrl === "string" ||
|
2026-06-05 01:00:33 +08:00
|
|
|
typeof patch.resultMimeType === "string" ||
|
|
|
|
|
typeof patch.taskRefundStatus === "string" ||
|
|
|
|
|
typeof patch.taskLifecycleStatus === "string" ||
|
|
|
|
|
typeof patch.taskUsage === "object"
|
2026-06-02 14:18:26 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function buildAssistantResult(
|
|
|
|
|
mode: WorkbenchMode,
|
|
|
|
|
model: string,
|
|
|
|
|
specs: string[],
|
|
|
|
|
referenceCount: number,
|
|
|
|
|
): ChatMessage["result"] {
|
|
|
|
|
if (mode === "image") {
|
|
|
|
|
return {
|
|
|
|
|
title: "图像任务已创建",
|
|
|
|
|
summary: referenceCount > 0 ? "已携带参考图,后续结果会进入资产库和画布。" : "已按当前模型和规格进入图像生成流程。",
|
|
|
|
|
specs,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode === "video") {
|
|
|
|
|
return {
|
|
|
|
|
title: "视频任务已创建",
|
|
|
|
|
summary: referenceCount > 0 ? "已携带参考素材,生成后可继续拆分镜头并发布案例。" : "已按当前镜头设置进入视频生成流程。",
|
|
|
|
|
specs,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
title: "Agent 已接管",
|
|
|
|
|
summary: "我会把当前输入整理成脚本、分镜、素材需求和可复制的工作流节点。",
|
|
|
|
|
specs: [model, ...specs],
|
|
|
|
|
};
|
2026-06-05 01:00:33 +08:00
|
|
|
}
|