import type { AgentThreadState } from "@/lib/ai/runtime/agent-thread-state"; import type { AgentWorkflowStatus } from "@/lib/ai/runtime/agent-workflow-state"; import type { PromptMemoryEntry } from "@/lib/ai/prompt-memory"; import type { StoryboardPreferenceMemory } from "@/lib/video-agent/storyboard-preference-memory"; type StoryboardTaskDetail = { task: { id: string; status: string | null; confirmationStatus?: string | null; hdImageStageStatus?: string | null; gridType?: string | null; gridRows?: number | null; gridCols?: number | null; currentShot: number | null; totalShots: number | null; sourceImageUrl?: string | null; providerType?: string | null; normalizedScript?: unknown; }; shots: Array<{ shotIndex: number | null; prompt?: string | null; status?: string | null; tileImageUrl?: string | null; videoUrl?: string | null; }>; }; function normalizeText(value: unknown, max = 240): string | undefined { const normalized = String(value || "").trim(); if (!normalized) return undefined; return normalized.slice(0, max); } function mapStoryboardTaskStatusToWorkflowStatus( task: StoryboardTaskDetail["task"], ): AgentWorkflowStatus { const rawConfirmationStatus = (task as Record).confirmationStatus; const confirmationStatus = normalizeText(rawConfirmationStatus, 40); if (confirmationStatus === "awaiting_user_confirmation") { return "awaiting_user_confirmation"; } if (confirmationStatus === "confirmed") { return "confirmed"; } if (confirmationStatus === "rejected") { return "rejected"; } const status = normalizeText(task.status, 40); switch (status) { case "pending": return "draft"; case "running": return "running"; case "succeeded": case "partial_success": return "completed"; case "failed": return "failed"; case "cancelled": return "cancelled"; default: return "draft"; } } export function buildAgentThreadStateFromStoryboardTask( detail: StoryboardTaskDetail | null | undefined, options?: { storyboardPreferenceMemory?: StoryboardPreferenceMemory | null; promptMemory?: PromptMemoryEntry | null; }, ): AgentThreadState | null { if (!detail?.task?.id) { if (!options?.storyboardPreferenceMemory && !options?.promptMemory) return null; return { workflowState: null, memoryHints: [ options?.storyboardPreferenceMemory ? "当前会话里存在 storyboard 偏好记忆,可在分镜/宫格任务里优先参考用户常用版式和确认习惯。" : "", options?.promptMemory?.optimizedPrompt ? "当前会话里存在上一轮可复用提示词;如果用户明确要求沿用上一个提示词继续做图,应优先把它作为本轮生图提示词。" : "", ].filter(Boolean), metadata: { workflowSource: "storyboard_preference_memory", storyboardPreferenceMemory: options.storyboardPreferenceMemory, promptMemory: options?.promptMemory || undefined, }, }; } const totalShots = Math.max(0, Number(detail.task.totalShots || detail.shots.length || 0)); const currentShot = Math.max(0, Number(detail.task.currentShot || 0)); const gridRows = Math.max(0, Number(detail.task.gridRows || 0)); const gridCols = Math.max(0, Number(detail.task.gridCols || 0)); const gridLabel = gridRows > 0 && gridCols > 0 ? `${gridCols}x${gridRows}` : `${totalShots} 段`; return { workflowState: { workflowId: detail.task.id, type: "storyboard_video", status: mapStoryboardTaskStatusToWorkflowStatus(detail.task), title: `分镜视频任务(${gridLabel})`, summary: totalShots > 0 ? `当前分镜任务进度:第 ${Math.min(currentShot, totalShots)}/${totalShots} 段。` : "当前存在一个分镜视频任务。", confirmPhrases: ["确认开始生成视频", "开始生成", "按这个分镜生成"], revisionHints: ["修改第3镜头", "调整第5镜头动作", "重生第2张高清图"], pendingItems: detail.shots.slice(0, 9).map((shot) => ({ id: Number.isFinite(Number(shot.shotIndex)) && Number(shot.shotIndex) > 0 ? `shot-${Math.round(Number(shot.shotIndex))}` : undefined, index: Number.isFinite(Number(shot.shotIndex)) && Number(shot.shotIndex) > 0 ? Math.round(Number(shot.shotIndex)) : undefined, title: Number.isFinite(Number(shot.shotIndex)) && Number(shot.shotIndex) > 0 ? `镜头 ${Math.round(Number(shot.shotIndex))}` : "镜头", summary: normalizeText(shot.prompt, 120), status: normalizeText(shot.status, 32), })), metadata: { providerType: normalizeText(detail.task.providerType, 40), gridType: normalizeText(detail.task.gridType, 32), gridRows: gridRows || undefined, gridCols: gridCols || undefined, sourceImageUrl: normalizeText(detail.task.sourceImageUrl, 500), normalizedScript: detail.task.normalizedScript && typeof detail.task.normalizedScript === "object" && !Array.isArray(detail.task.normalizedScript) ? detail.task.normalizedScript : undefined, }, }, memoryHints: [ "如果当前对话是在继续一个分镜视频任务,优先理解为对当前分镜流程的确认、修改或取消。", "如果用户提到第N镜头、重生某张图、调整某个镜头,优先解释为对当前 storyboard workflow 的修订。", ], contextSummary: totalShots > 0 ? `当前 chat 存在一个分镜视频任务,共 ${totalShots} 段,当前进度第 ${Math.min(currentShot, totalShots)}/${totalShots} 段。` : "当前 chat 存在一个分镜视频任务。", metadata: { workflowSource: "video_storyboard_task", taskId: detail.task.id, shotCount: totalShots, storyboardPreferenceMemory: options?.storyboardPreferenceMemory || undefined, promptMemory: options?.promptMemory || undefined, }, }; }