diff --git a/src/schemas/comfyWorkflowSchema.ts b/src/schemas/comfyWorkflowSchema.ts index 71ac093fd..5006b7539 100644 --- a/src/schemas/comfyWorkflowSchema.ts +++ b/src/schemas/comfyWorkflowSchema.ts @@ -214,6 +214,23 @@ const zComfyNode = z }) .passthrough() +const zSubgraphInstance = z + .object({ + id: zNodeId, + type: z.string().uuid(), + pos: zVector2, + size: zVector2, + flags: zFlags, + order: z.number(), + mode: z.number(), + inputs: z.array(zNodeInput).optional(), + outputs: z.array(zNodeOutput).optional(), + widgets_values: zWidgetValues.optional(), + color: z.string().optional(), + bgcolor: z.string().optional() + }) + .passthrough() + const zGroup = z .object({ id: z.number().optional(), @@ -248,9 +265,22 @@ const zExtra = z }) .passthrough() +export const zGraphDefinitions = z.object({ + subgraphs: z.lazy(() => z.array(zSubgraphDefinition)) +}) + +export const zBaseExportableGraph = z.object({ + /** Unique graph ID. Automatically generated if not provided. */ + id: z.string().uuid().optional(), + revision: z.number().optional(), + config: zConfig.optional().nullable(), + /** Details of the appearance and location of subgraphs shown in this graph. Similar to */ + subgraphs: z.array(zSubgraphInstance).optional() +}) + /** Schema version 0.4 */ -export const zComfyWorkflow = z - .object({ +export const zComfyWorkflow = zBaseExportableGraph + .extend({ id: z.string().uuid().optional(), revision: z.number().optional(), last_node_id: zNodeId, @@ -262,13 +292,54 @@ export const zComfyWorkflow = z config: zConfig.optional().nullable(), extra: zExtra.optional().nullable(), version: z.number(), - models: z.array(zModelFile).optional() + models: z.array(zModelFile).optional(), + definitions: zGraphDefinitions.optional() }) .passthrough() +/** Required for recursive definition of subgraphs. */ +interface ComfyWorkflow1BaseType { + id?: string + revision?: number + version: 1 + models?: z.infer[] + definitions?: { + subgraphs: SubgraphInferredBaseType[] + } +} + +/** Required for recursive definition of subgraphs. */ +interface ComfyWorkflow1BaseInput extends ComfyWorkflow1BaseType { + state: z.input + groups?: z.input[] + nodes: z.input[] + links?: z.input[] + floatingLinks?: z.input[] + reroutes?: z.input[] +} + +/** Required for recursive definition of subgraphs. */ +interface ComfyWorkflow1BaseOutput extends ComfyWorkflow1BaseType { + state: z.output + groups?: z.output[] + nodes: z.output[] + links?: z.output[] + floatingLinks?: z.output[] + reroutes?: z.output[] +} + +/** Required for recursive definition of subgraphs. */ +type ComfyWorkflow1DefinitionInput = z.input & + ComfyWorkflow1BaseInput & + SubgraphInferredBaseType +/** Required for recursive definition of subgraphs. */ +type ComfyWorkflow1DefinitionOutput = z.output & + ComfyWorkflow1BaseOutput & + SubgraphInferredBaseType + /** Schema version 1 */ -export const zComfyWorkflow1 = z - .object({ +export const zComfyWorkflow1 = zBaseExportableGraph + .extend({ id: z.string().uuid().optional(), revision: z.number().optional(), version: z.literal(1), @@ -280,7 +351,94 @@ export const zComfyWorkflow1 = z floatingLinks: z.array(zComfyLinkObject).optional(), reroutes: z.array(zReroute).optional(), extra: zExtra.optional().nullable(), - models: z.array(zModelFile).optional() + models: z.array(zModelFile).optional(), + definitions: z.object({ + subgraphs: z.lazy( + (): z.ZodArray< + z.ZodType< + ComfyWorkflow1DefinitionOutput, + z.ZodTypeDef, + ComfyWorkflow1DefinitionInput + >, + 'many' + > => z.array(zSubgraphDefinition) + ) + }) + }) + .passthrough() + +export const zSubgraphIO = z.object({ + /** Slot ID (internal; never changes once instantiated). */ + id: z.string().uuid(), + /** The data type this slot uses. Unlike nodes, this does not support legacy numeric types. */ + type: z.string(), + /** Links connected to this slot, or `undefined` if not connected. An ouptut slot should only ever have one link. */ + linkIds: z.array(z.number()).nullable().optional() +}) + +export const zExportedSubgraphIONode = z.object({ + id: zNodeId, + bounding: z.tuple([z.number(), z.number(), z.number(), z.number()]), + pinned: z.boolean().optional() +}) + +export const zExposedWidget = z.object({ + id: z.string(), + name: z.string() +}) + +interface SubgraphInferredBaseType { + /** Unique graph ID. Automatically generated if not provided. */ + id: string + revision: number + inputNode: z.infer + outputNode: z.infer + /** Ordered list of inputs to the subgraph itself. Similar to a reroute, with the input side in the graph, and the output side in the subgraph. */ + inputs?: z.infer[] + /** Ordered list of outputs from the subgraph itself. Similar to a reroute, with the input side in the subgraph, and the output side in the graph. */ + outputs?: z.infer[] + /** A list of node widgets displayed in the parent graph, on the subgraph object. */ + widgets?: z.infer[] + definitions?: { + subgraphs: SubgraphInferredBaseType[] + } +} + +/** Required for recursive definition of subgraphs. */ +type SubgraphDefinitionInput = z.input & + SubgraphInferredBaseType +/** Required for recursive definition of subgraphs. */ +type SubgraphDefinitionOutput = z.output & + SubgraphInferredBaseType + +/** A subgraph definition `worfklow.definitions.subgraphs` */ +export const zSubgraphDefinition = zComfyWorkflow1 + .extend({ + /** Unique graph ID. Automatically generated if not provided. */ + id: z.string().uuid(), + revision: z.number(), + + inputNode: zExportedSubgraphIONode, + outputNode: zExportedSubgraphIONode, + + /** Ordered list of inputs to the subgraph itself. Similar to a reroute, with the input side in the graph, and the output side in the subgraph. */ + inputs: z.array(zSubgraphIO).optional(), + /** Ordered list of outputs from the subgraph itself. Similar to a reroute, with the input side in the subgraph, and the output side in the graph. */ + outputs: z.array(zSubgraphIO).optional(), + /** A list of node widgets displayed in the parent graph, on the subgraph object. */ + widgets: z.array(zExposedWidget), + definitions: z.object({ + subgraphs: z.lazy( + (): z.ZodArray< + z.ZodType< + SubgraphDefinitionOutput, + z.ZodTypeDef, + SubgraphDefinitionInput + >, + 'many' + > => zSubgraphDefinition.array() + ) + }) }) .passthrough()