ai-chatbot/lib/video-agent/agent-thread-state.ts
Your Name e7812060b8
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
Update conversation memory and canvas persistence
2026-03-13 12:31:13 +08:00

162 lines
6.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<string, unknown>).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,
},
};
}