fix(canvas): 媒体生成 - 修复已有图片引用和图片音频视频模型匹配
This commit is contained in:
@@ -4,7 +4,7 @@ import { seedanceReferenceLabel } from "@/lib/seedance-video";
|
||||
import type { ReferenceImage } from "@/types/image";
|
||||
import type { ReferenceAudio, ReferenceVideo } from "@/types/media";
|
||||
import { CanvasNodeType, type CanvasConnection, type CanvasGenerationMode, type CanvasNodeData } from "@/types/canvas";
|
||||
import { getGenerationResourceNodes, getContextResourceNodes } from "@/lib/canvas/canvas-resource-references";
|
||||
import { getGenerationResourceNodes, getContextResourceNodes, getMentionResourceNodes } from "@/lib/canvas/canvas-resource-references";
|
||||
import { isNeutralColorGrade, resolveCanvasColorGradeReference } from "@/lib/canvas/canvas-color-grade";
|
||||
import { getNodeResourceKind } from "@/lib/canvas/node-registry";
|
||||
import { resolveCanvasDrawingReference } from "@/lib/canvas/canvas-drawing-reference";
|
||||
@@ -69,14 +69,17 @@ export function buildNodeGenerationContext(nodeId: string, nodes: CanvasNodeData
|
||||
return image ? [{ nodeId: sourceNode.id, type: "image" as const, title: sourceNode.title, image }] : [];
|
||||
})()
|
||||
: [];
|
||||
const inputs = [...connectedInputs, ...portraitTextureInput, ...buildAssetGenerationInputs(assets)];
|
||||
// 显式 @ 引用必须与提示词面板展示的资源集合一致;默认自动输入仍只取入边,
|
||||
// 避免已有图片在没有 @图片N 时被悄悄当作自身参考图。
|
||||
const mentionInputs = mergeGenerationInputs(buildNodeMentionGenerationInputs(nodeId, nodes, connections), portraitTextureInput, buildAssetGenerationInputs(assets));
|
||||
const storyboardInputs = getConnectedStoryboardRows(nodeId, nodes, connections);
|
||||
const hasExplicitResourceMention = hasResolvableGenerationMention(prompt, inputs);
|
||||
assertResolvableGenerationMentions(prompt, mentionInputs);
|
||||
const hasExplicitResourceMention = hasResolvableGenerationMention(prompt, mentionInputs);
|
||||
const isWorkflowSource = sourceNode?.type === CanvasNodeType.Config && isCanvasWorkflowProvider(sourceNode.metadata);
|
||||
if ((Boolean(sourceNode?.metadata?.composerContent?.trim()) && (sourceNode?.type === CanvasNodeType.Config || isWorkflowSource)) || hasExplicitResourceMention) {
|
||||
const autoIncludeWorkflowMedia = isWorkflowSource;
|
||||
return buildComposerGenerationContext(
|
||||
inputs,
|
||||
mentionInputs,
|
||||
prompt,
|
||||
// 工作流节点由字段映射接收全部连线媒体;视频节点的历史首尾帧字段不能再额外追加参考图。
|
||||
autoIncludeWorkflowMedia ? [] : [sourceNode?.metadata?.videoStartFrameNodeId, sourceNode?.metadata?.videoEndFrameNodeId].filter((id): id is string => Boolean(id)),
|
||||
@@ -260,14 +263,31 @@ export function normalizeGenerationNodeMentionTokens(prompt: string, inputs: Nod
|
||||
}
|
||||
|
||||
function hasResolvableGenerationMention(prompt: string, inputs: NodeGenerationInput[]) {
|
||||
return inspectGenerationMentions(prompt, inputs).hasResolved;
|
||||
}
|
||||
|
||||
function assertResolvableGenerationMentions(prompt: string, inputs: NodeGenerationInput[]) {
|
||||
const { unresolved } = inspectGenerationMentions(prompt, inputs);
|
||||
if (!unresolved.length) return;
|
||||
throw new Error(`提示词中的 ${unresolved.join("、")} 没有对应的画布资源,请重新选择引用后再生成`);
|
||||
}
|
||||
|
||||
function inspectGenerationMentions(prompt: string, inputs: NodeGenerationInput[]) {
|
||||
const normalizedPrompt = normalizeGenerationNodeMentionTokens(prompt, inputs);
|
||||
const slotInputByToken = new Map(generationSlotEntries(inputs).map(({ token, input }) => [token, input]));
|
||||
const nodeInputById = new Map(inputs.filter((input) => !input.nodeId.startsWith("asset:")).map((input) => [input.nodeId, input]));
|
||||
const assetInputById = new Map(inputs.filter((input) => input.nodeId.startsWith("asset:")).map((input) => [input.nodeId.slice("asset:".length), input]));
|
||||
const unresolved = new Set<string>();
|
||||
let hasResolved = false;
|
||||
for (const match of normalizedPrompt.matchAll(GENERATION_MENTION_PATTERN)) {
|
||||
if (resolveGenerationMention(normalizedPrompt, match, slotInputByToken, nodeInputById, assetInputById)) return true;
|
||||
if (match[3]) {
|
||||
const end = (match.index || 0) + match[0].length;
|
||||
if (!hasMentionBoundary(normalizedPrompt, end)) continue;
|
||||
}
|
||||
if (resolveGenerationMention(normalizedPrompt, match, slotInputByToken, nodeInputById, assetInputById)) hasResolved = true;
|
||||
else unresolved.add(match[0]);
|
||||
}
|
||||
return false;
|
||||
return { hasResolved, unresolved: [...unresolved] };
|
||||
}
|
||||
|
||||
function generationSlotEntries(inputs: NodeGenerationInput[]) {
|
||||
@@ -302,7 +322,14 @@ function hasMentionBoundary(value: string, index: number) {
|
||||
}
|
||||
|
||||
export function buildNodeGenerationInputs(nodeId: string, nodes: CanvasNodeData[], connections: CanvasConnection[]): NodeGenerationInput[] {
|
||||
const resourceNodes = getGenerationResourceNodes(nodeId, nodes, connections);
|
||||
return buildGenerationInputs(getGenerationResourceNodes(nodeId, nodes, connections), nodes, connections);
|
||||
}
|
||||
|
||||
function buildNodeMentionGenerationInputs(nodeId: string, nodes: CanvasNodeData[], connections: CanvasConnection[]): NodeGenerationInput[] {
|
||||
return buildGenerationInputs(getMentionResourceNodes(nodeId, nodes, connections), nodes, connections);
|
||||
}
|
||||
|
||||
function buildGenerationInputs(resourceNodes: CanvasNodeData[], nodes: CanvasNodeData[], connections: CanvasConnection[]): NodeGenerationInput[] {
|
||||
return resourceNodes.flatMap((node): NodeGenerationInput[] => {
|
||||
const character = readCharacterReference(node);
|
||||
if (character) return [{ nodeId: node.id, type: "character" as const, title: node.title, character }];
|
||||
@@ -320,6 +347,17 @@ export function buildNodeGenerationInputs(nodeId: string, nodes: CanvasNodeData[
|
||||
});
|
||||
}
|
||||
|
||||
function mergeGenerationInputs(...groups: NodeGenerationInput[][]) {
|
||||
const seen = new Set<string>();
|
||||
return groups.flatMap((group) =>
|
||||
group.filter((input) => {
|
||||
if (seen.has(input.nodeId)) return false;
|
||||
seen.add(input.nodeId);
|
||||
return true;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function buildAssetGenerationInputs(assets: Asset[]): NodeGenerationInput[] {
|
||||
return assets.flatMap((asset): NodeGenerationInput[] => {
|
||||
const nodeId = `asset:${asset.id}`;
|
||||
|
||||
Reference in New Issue
Block a user