91 lines
4.4 KiB
TypeScript
91 lines
4.4 KiB
TypeScript
import { nanoid } from "nanoid";
|
||
|
||
import { NODE_DEFAULT_SIZE } from "@/constant/canvas";
|
||
import { getGenerationCount, runCanvasGenerationTaskToConsumer } 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,
|
||
applyGenerationTaskResult,
|
||
registerPendingNodeIds,
|
||
taskContext,
|
||
retryContext,
|
||
}: CanvasGenerationExecution) {
|
||
const isConfigNode = sourceNode?.type === CanvasNodeType.Config;
|
||
const isDirectTextTarget = sourceNode?.type === CanvasNodeType.Text && !sourceNode.metadata?.content?.trim() && !editingTextNode;
|
||
// 独立文本份数(textCount),默认 1,不再复用餐图片数量 count(对齐上游 v0.16 语义)
|
||
const textCount = getGenerationCount(String(sourceNode?.metadata?.textCount ?? 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) =>
|
||
runCanvasGenerationTaskToConsumer(
|
||
{
|
||
projectId,
|
||
nodeId: targetNodeId,
|
||
...retryContext,
|
||
mode: "text",
|
||
prompt: effectivePrompt,
|
||
config: generationConfig,
|
||
referenceImages: generationContext.referenceImages,
|
||
referenceVideos: generationContext.referenceVideos,
|
||
signal: controller.signal,
|
||
metadata: { sourceNodeId: nodeId, ...taskContext, resolvedCharacterVersions: generationContext.resolvedCharacterVersions },
|
||
},
|
||
{
|
||
bindTask: (task) => bindGenerationTask(targetNodeId, task),
|
||
consumeTask: (task) => applyGenerationTaskResult(targetNodeId, task),
|
||
},
|
||
)
|
||
.then(() => ({ nodeId: targetNodeId }))
|
||
.finally(() => finishGenerationRequest(targetNodeId, controller)),
|
||
),
|
||
);
|
||
if (controller.signal.aborted) return;
|
||
void answers;
|
||
if (isConfigNode) {
|
||
setNodes((current) => current.map((node) => (node.id === nodeId ? { ...node, metadata: { ...node.metadata, status: NODE_STATUS_SUCCESS, errorDetails: undefined, generationErrorCode: undefined, failedPromptFingerprint: undefined } } : node)));
|
||
}
|
||
}
|