refactor(canvas): 节点注册表 - 收敛素材类型、生成模式与输入计数判据
把三条按类型分支的语义判据收进注册表: - resourceKind 原 canvas-resource-references.ts,作为 @ 引用素材时的类型 - generationMode 原 canvas-connection-policy.ts,作为生成节点时的模式 - inputKind 原 canvas-connection-policy.ts,被容量校验计数时的类别 resourceKind 与 inputKind 刻意分成两个字段:它们语义不同——空图片节点不是素材 (不进 @ 引用),但照样计入 imageCount。合成一个会静默改掉连线容量校验。 两处必须保住的语义,均已由测试钉住: 1. 角色卡(workflowKind === character)是跨类型覆盖,对任何节点类型生效, 属于查注册表之前的前置判定,因此留在调用点而不进 per-type 定义。 2. 排除必须早于覆盖。原实现先 return 掉生成配置与背板,之后才判角色卡; 若把角色卡判定提前,带角色元数据的配置/背板节点会被多算成 characterCount。 新增 test/canvas-node-registry.test.ts(19 用例 / 77 断言)并加入 package.json 的 test 列表——该列表是显式枚举,不加进去文件不会跑。补测的原因是 canvas-resource-references.test.ts 只覆盖 collectUpstreamVideoNodes, 本次改动的 resourceKind 此前零覆盖,而这类判据出错不报错、只会让 @ 引用少收素材。 顺带内联 nodeGenerationMode(迁移后退化为单行转发)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
af84cf5772
commit
a4585fdc2a
8 files changed
+245
-27
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { maxModelInputCapacity, type ModelInputSummary } from "@/lib/model-selection";
|
||||
import { getNodeGenerationMode, getNodeInputKind } from "@/lib/canvas/node-registry";
|
||||
import type { AiConfig } from "@/stores/use-config-store";
|
||||
import { CanvasNodeType, type CanvasConnection, type CanvasGenerationMode, type CanvasNodeData } from "@/types/canvas";
|
||||
import { type CanvasConnection, type CanvasNodeData } from "@/types/canvas";
|
||||
|
||||
type ConnectionCandidate = Pick<CanvasConnection, "fromNodeId" | "toNodeId">;
|
||||
type CanvasConnectionPolicyOptions = {
|
||||
@@ -11,7 +12,7 @@ type CanvasConnectionPolicyOptions = {
|
||||
export function canvasConnectionError(config: AiConfig, nodes: CanvasNodeData[], connections: CanvasConnection[], candidate: ConnectionCandidate, options: CanvasConnectionPolicyOptions = {}) {
|
||||
const target = nodes.find((node) => node.id === candidate.toNodeId);
|
||||
if (!target) return "找不到连线目标节点";
|
||||
const mode = nodeGenerationMode(target);
|
||||
const mode = getNodeGenerationMode(target);
|
||||
if (!mode) return "";
|
||||
const input = connectionInputSummary(target.id, nodes, connections, candidate);
|
||||
const visualInputCount = input.imageCount + input.characterCount;
|
||||
@@ -35,25 +36,18 @@ export function connectionInputSummary(targetNodeId: string, nodes: CanvasNodeDa
|
||||
const input: ModelInputSummary = { textCount: 0, imageCount: 0, videoCount: 0, audioCount: 0, characterCount: 0 };
|
||||
sourceIds.forEach((sourceId) => {
|
||||
const source = nodes.find((node) => node.id === sourceId);
|
||||
if (!source || source.type === CanvasNodeType.Config || source.type === CanvasNodeType.Frame) return;
|
||||
if (!source) return;
|
||||
// 生成配置与背板不是参考素材,不参与容量计数——这一步必须早于角色卡判定,
|
||||
// 否则一个带角色元数据的配置/背板节点会被多算成角色。
|
||||
const inputKind = getNodeInputKind(source.type);
|
||||
if (!inputKind) return;
|
||||
// 角色卡是跨类型覆盖:落在可计数类型上时改记为角色。
|
||||
if (source.metadata?.workflowKind === "character") input.characterCount += 1;
|
||||
else if (source.type === CanvasNodeType.Image || source.type === CanvasNodeType.Drawing) input.imageCount += 1;
|
||||
else if (source.type === CanvasNodeType.Video) input.videoCount += 1;
|
||||
else if (source.type === CanvasNodeType.Audio) input.audioCount += 1;
|
||||
else input.textCount += 1;
|
||||
else input[`${inputKind}Count`] += 1;
|
||||
});
|
||||
return input;
|
||||
}
|
||||
|
||||
function nodeGenerationMode(node: CanvasNodeData): CanvasGenerationMode | null {
|
||||
if (node.type === CanvasNodeType.Config) return node.metadata?.generationMode || "image";
|
||||
if (node.type === CanvasNodeType.Image) return "image";
|
||||
if (node.type === CanvasNodeType.Video) return "video";
|
||||
if (node.type === CanvasNodeType.Audio) return "audio";
|
||||
if (node.type === CanvasNodeType.Text || node.type === CanvasNodeType.Script) return "text";
|
||||
return null;
|
||||
}
|
||||
|
||||
function capacityError(config: AiConfig, capability: "image" | "video", kind: "image" | "video" | "audio", count: number, label: string) {
|
||||
const maximum = maxModelInputCapacity(config, capability, kind);
|
||||
if (maximum === null || count <= maximum) return "";
|
||||
|
||||
Reference in new issue
Block a user