85 lines
5.1 KiB
TypeScript
85 lines
5.1 KiB
TypeScript
import { nanoid } from "nanoid";
|
|
|
|
import { NODE_DEFAULT_SIZE } from "@/constant/canvas";
|
|
import { getGenerationCount, runBackendCanvasGenerationTask } from "@/lib/canvas/canvas-project-generation";
|
|
import { CanvasNodeType, type CanvasNodeData } from "@/types/canvas";
|
|
|
|
import type { CanvasGenerationExecution } from "./canvas-generation-executor-types";
|
|
|
|
const NODE_STATUS_LOADING = "loading" as const;
|
|
const NODE_STATUS_SUCCESS = "success" as const;
|
|
|
|
export async function executeTextGeneration({
|
|
nodeId,
|
|
sourceNode,
|
|
prompt,
|
|
effectivePrompt,
|
|
generationConfig,
|
|
generationContext,
|
|
controller,
|
|
editingTextNode,
|
|
projectId,
|
|
setNodes,
|
|
setConnections,
|
|
startGenerationRequest,
|
|
finishGenerationRequest,
|
|
bindGenerationTask,
|
|
registerPendingNodeIds,
|
|
}: CanvasGenerationExecution) {
|
|
let streamed = "";
|
|
const isConfigNode = sourceNode?.type === CanvasNodeType.Config;
|
|
const isDirectTextTarget = sourceNode?.type === CanvasNodeType.Text && !sourceNode.metadata?.content?.trim() && !editingTextNode;
|
|
const textCount = isConfigNode || (isDirectTextTarget && sourceNode?.metadata?.count) ? getGenerationCount(generationConfig.count) : 1;
|
|
const parentConfig = NODE_DEFAULT_SIZE[isConfigNode ? CanvasNodeType.Config : CanvasNodeType.Text];
|
|
const textConfig = NODE_DEFAULT_SIZE[CanvasNodeType.Text];
|
|
const parentPosition = sourceNode?.position || { x: 0, y: 0 };
|
|
const generateInPlace = !isConfigNode && !editingTextNode;
|
|
const childIds = Array.from({ length: generateInPlace ? Math.max(0, textCount - 1) : textCount }, () => nanoid());
|
|
registerPendingNodeIds(childIds);
|
|
if (childIds.length) {
|
|
const childNodes: CanvasNodeData[] = childIds.map((id, index) => ({
|
|
id,
|
|
type: CanvasNodeType.Text,
|
|
title: effectivePrompt.slice(0, 32) || "Generated Text",
|
|
position: {
|
|
x: parentPosition.x + parentConfig.width + 96,
|
|
y: parentPosition.y + parentConfig.height / 2 - textConfig.height / 2 + (index - (childIds.length - 1) / 2) * (textConfig.height + 36),
|
|
},
|
|
width: textConfig.width,
|
|
height: textConfig.height,
|
|
metadata: { prompt: effectivePrompt, status: NODE_STATUS_LOADING, fontSize: 14 },
|
|
}));
|
|
setNodes((current) => [...current.map((node) => (node.id === nodeId && isConfigNode ? { ...node, metadata: { ...node.metadata, prompt: effectivePrompt, status: NODE_STATUS_LOADING, errorDetails: undefined } } : node)), ...childNodes]);
|
|
setConnections((current) => [...current, ...childIds.map((childId) => ({ id: nanoid(), fromNodeId: nodeId, toNodeId: childId }))]);
|
|
}
|
|
|
|
const textTargetIds = generateInPlace ? [nodeId, ...childIds] : childIds;
|
|
textTargetIds.forEach((targetNodeId) => startGenerationRequest(targetNodeId, nodeId, nodeId, controller));
|
|
const answers = await Promise.all(
|
|
textTargetIds.map((targetNodeId) => {
|
|
let localStreamed = "";
|
|
return runBackendCanvasGenerationTask({ projectId, nodeId: targetNodeId, mode: "text", prompt: effectivePrompt, config: generationConfig, referenceImages: generationContext.referenceImages, referenceVideos: generationContext.referenceVideos, signal: controller.signal, metadata: { sourceNodeId: nodeId, resolvedCharacterVersions: generationContext.resolvedCharacterVersions }, onTaskCreated: (task) => bindGenerationTask(targetNodeId, task) })
|
|
.then((result) => {
|
|
localStreamed = result.text || "";
|
|
streamed = localStreamed;
|
|
if (!isConfigNode) setNodes((current) => current.map((node) => (node.id === targetNodeId ? { ...node, type: CanvasNodeType.Text, metadata: { ...node.metadata, content: localStreamed, richText: undefined, status: NODE_STATUS_LOADING } } : node)));
|
|
return { nodeId: targetNodeId, content: localStreamed };
|
|
})
|
|
.finally(() => finishGenerationRequest(targetNodeId, controller));
|
|
}),
|
|
);
|
|
if (controller.signal.aborted) return;
|
|
const answerByNodeId = new Map(answers.map((item) => [item.nodeId, item.content]));
|
|
setNodes((current) =>
|
|
current.map((node) =>
|
|
childIds.includes(node.id)
|
|
? { ...node, metadata: { ...node.metadata, content: answerByNodeId.get(node.id) || streamed, richText: undefined, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } }
|
|
: node.id === nodeId && isConfigNode
|
|
? { ...node, metadata: { ...node.metadata, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } }
|
|
: node.id === nodeId && !editingTextNode
|
|
? { ...node, type: CanvasNodeType.Text, title: effectivePrompt.slice(0, 32) || "Generated Text", metadata: { ...node.metadata, content: answerByNodeId.get(node.id) || streamed, richText: undefined, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } }
|
|
: node,
|
|
),
|
|
);
|
|
}
|