import "server-only"; import type { AgentThreadState } from "@/lib/ai/runtime/agent-thread-state"; import type { AgentExecutionPlan, AgentPlannedToolCall } from "@/lib/ai/auto-agent-graph"; import type { AgentSpecialistRoute } from "@/lib/ai/specialists"; import type { AgentProjectToolDefinition } from "@/lib/ai/tools-registry"; import { getImageWorkflowPlanningHints, } from "@/lib/ai/image-workflow-memory"; import { getPromptMemoryPlanningHints, isPromptCarryForwardRequest, } from "@/lib/ai/prompt-memory"; function normalizeCompactText(text: string) { return String(text || "").toLowerCase().replace(/\s+/g, "").trim(); } export function buildImageGenerationSpecialistRoute(params: { text: string; threadState?: AgentThreadState | null; hasImage: boolean; }): AgentSpecialistRoute | null { const normalized = normalizeCompactText(params.text); const promptHints = getPromptMemoryPlanningHints(params.threadState); const workflowHints = getImageWorkflowPlanningHints(params.threadState); const hasPromptMemory = promptHints.length > 0; const hasImageWorkflowMemory = workflowHints.length > 0; const carryForward = isPromptCarryForwardRequest({ text: params.text, hasImage: params.hasImage, }); const mentionsPromptExecution = /提示词|prompt|关键词|咒语/.test(normalized) && /生成|生图|出图|继续做|继续生成|执行/.test(normalized); if (!hasPromptMemory && !hasImageWorkflowMemory && !carryForward && !mentionsPromptExecution) { return null; } const planningHints = [ carryForward ? "用户当前要求沿用上一轮优化提示词继续执行,应优先理解为连续生图执行。" : "", hasPromptMemory ? "当前会话里存在上一轮可复用提示词,如果用户在说“上一个提示词/刚才那个 prompt”,优先复用这段提示词。" : "", hasImageWorkflowMemory ? "当前会话里存在图片工作流记忆,优先参考上一轮参考图、结果图、数量和画幅偏好。" : "", params.hasImage ? "如果当前回合上传了新图片,并要求沿用上一轮提示词,应理解为“上一轮提示词 + 当前图片条件”的联合执行。" : "", ...promptHints, ...workflowHints, ].filter(Boolean); return { domain: "image_generation_memory", specialistId: "image-generation-subagent", reason: carryForward ? "当前回合命中提示词接力生图" : "当前回合命中提示词相关的连续生图任务", planningHints, toolAllowlist: [ "respondDirect", "generateImage", "useSelectedCanvasImage", "useLatestResultImage", "useOriginalUploadImage", "publishToCanvas", ], }; } function hasTool( availableTools: AgentProjectToolDefinition[], name: AgentPlannedToolCall["name"], ) { return availableTools.some((tool) => tool.name === name); } function upsertTool( toolCalls: AgentPlannedToolCall[], name: AgentPlannedToolCall["name"], reason: string, options?: { prepend?: boolean }, ) { const existing = toolCalls.find((tool) => tool.name === name); if (existing) return toolCalls; const nextTool = { name, reason } satisfies AgentPlannedToolCall; return options?.prepend ? [nextTool, ...toolCalls] : [...toolCalls, nextTool]; } export function refinePlanWithImageGenerationSubagent(params: { text: string; plan: AgentExecutionPlan; availableTools: AgentProjectToolDefinition[]; threadState?: AgentThreadState | null; hasImage: boolean; }): AgentExecutionPlan { const carryForward = isPromptCarryForwardRequest({ text: params.text, hasImage: params.hasImage, }); const hasPromptMemory = getPromptMemoryPlanningHints(params.threadState).length > 0; const hasImageWorkflowMemory = getImageWorkflowPlanningHints(params.threadState).length > 0; if (!carryForward && !hasPromptMemory && !hasImageWorkflowMemory) { return params.plan; } let toolCalls = [...params.plan.toolCalls]; if (hasTool(params.availableTools, "generateImage")) { toolCalls = [{ name: "generateImage", reason: "image-generation 子代理要求优先走连续生图执行链路" }]; } if (hasTool(params.availableTools, "publishToCanvas")) { toolCalls = upsertTool( toolCalls, "publishToCanvas", "image-generation 子代理建议把连续生图结果同步到画布" ); } return { ...params.plan, nextAction: "generate_image", intent: "image_gen", responseMode: "tool_then_execute", preferredCapability: "generate_image", shouldUseStructuredReply: true, toolCalls, }; }