fix: Correct schema version from v2 to v1 and add schema generation docs

- Fix version number in ComfyWorkflow1BaseType interface (was 2, should be 1)
- Fix version literal in zComfyWorkflow1 schema (was 2, should be 1)
- Update all comments referencing v2 to correctly state v1
- Fix validateComfyWorkflow to only check for version 1, not 2
- Add comprehensive schema generation documentation in docs/SCHEMA_GENERATION.md
- Document SOP for schema updates and version bumping

The PR was incorrectly trying to create a v2 schema when only documentation
changes were made. Schema version should only increment for breaking changes.
This commit is contained in:
bymyself
2025-08-12 15:16:51 -07:00
parent 88c4b086dc
commit ae184609c8
2 changed files with 231 additions and 37 deletions

160
docs/SCHEMA_GENERATION.md Normal file
View File

@@ -0,0 +1,160 @@
# ComfyUI Workflow Schema Generation
This document describes the process for generating and maintaining JSON Schema definitions for ComfyUI workflows.
## Overview
ComfyUI uses **Zod schemas** in TypeScript to define workflow structure, which are converted to **JSON Schema** format for external consumption, documentation, and validation.
### Schema Versions
- **Version 0.4** (Legacy): Original workflow format with array-based links
- **Version 1.0** (Current): Modern format with object-based links, subgraphs, and reroutes
## Schema Generation Process
### Prerequisites
1. Ensure all dependencies are installed: `npm install`
2. Verify TypeScript compilation: `npm run typecheck`
### Command
```bash
npm run json-schema
```
This runs the generation script: `scripts/generate-json-schema.ts`
### Output
Generated schemas are written to `./schemas/` directory:
- `workflow-0_4.json` (~80KB) - Legacy workflow format
- `workflow-1_0.json` (~82KB) - Current workflow format
- `node-def-v1.json` - Node definition schema v1
- `node-def-v2.json` - Node definition schema v2
**Note**: The `./schemas/` directory is gitignored and not committed to the repository.
## When to Regenerate Schemas
### Required Regeneration
Run schema generation when:
1. **Schema Changes**: Any modifications to files in `/src/schemas/`
2. **Breaking Changes**: Changes that affect data structure or validation
3. **New Features**: Adding new workflow capabilities (subgraphs, reroutes, etc.)
4. **Documentation Updates**: When schema descriptions change
### Schema Version Bumping
Only increment schema version for **breaking changes**:
- Data structure changes (field renames, type changes)
- Required field additions
- Format changes that break backward compatibility
**Non-breaking changes** (documentation, optional fields) should NOT bump the version.
## Schema Sources
### Primary Schemas (Zod-based)
#### Workflow Schemas
- **File**: `src/schemas/comfyWorkflowSchema.ts`
- **Exports**: `zComfyWorkflow` (v0.4), `zComfyWorkflow1` (v1.0)
- **Purpose**: Defines workflow JSON structure for both legacy and modern formats
#### Node Definition Schemas
- **Files**:
- `src/schemas/nodeDefSchema.ts` (v1)
- `src/schemas/nodeDef/nodeDefSchemaV2.ts` (v2)
- **Purpose**: Defines node definition structure for different versions
### Generation Configuration
The generation script uses:
- **Library**: `zod-to-json-schema`
- **Strategy**: `$refStrategy: 'none'` (inlines all references)
- **Output**: Formatted JSON with proper naming
## Standard Operating Procedure (SOP)
### For Schema Updates
1. **Make Changes**: Modify Zod schemas in `/src/schemas/`
2. **Test Locally**: Ensure changes work with existing workflows
3. **Run Generation**: `npm run json-schema`
4. **Validate Output**: Check generated schemas for correctness
5. **Test Integration**: Verify schemas work with external tools
6. **Document Changes**: Update this document if process changes
### For Version Bumps
1. **Assess Breaking Changes**: Determine if changes are truly breaking
2. **Update Version**: Increment version number in schema definition
3. **Update Validation**: Modify `validateComfyWorkflow()` function
4. **Update Serialization**: Update LGraph serialization if needed
5. **Test Backward Compatibility**: Ensure old workflows still load
6. **Document Migration**: Provide migration guide if needed
### For External Publishing
**Current Status**: No automated publishing to docs.comfy.org exists yet.
**Recommended Process**:
1. Generate schemas locally
2. Copy to appropriate documentation repository
3. Update documentation with new schema versions
4. Coordinate with docs team for publication
## Troubleshooting
### Common Issues
#### Lodash Import Errors
**Error**: `SyntaxError: The requested module 'lodash' does not provide an export named 'clamp'`
**Solution**: Use the simplified generation script that avoids litegraph imports:
```bash
npx tsx scripts/generate-json-schema-simple.ts
```
#### Recursive Reference Warnings
**Warning**: `Recursive reference detected... Defaulting to any`
**Explanation**: This is expected for subgraph definitions and doesn't affect functionality.
### Schema Validation Issues
1. **Check Source Schema**: Verify Zod schema is valid
2. **Test TypeScript**: Run `npm run typecheck`
3. **Check Dependencies**: Ensure all imports resolve correctly
4. **Validate Output**: Test generated JSON Schema with online validators
## Integration Points
### External Documentation
- **docs.comfy.org**: Official ComfyUI documentation site
- **Workflow Spec**: https://docs.comfy.org/specs/workflow_json
### Development Tools
- **IDE Support**: Generated schemas provide autocomplete and validation
- **API Validation**: External tools can validate workflow JSON
- **Documentation Generation**: Schemas document workflow structure
## Future Improvements
1. **Automated Publishing**: Set up CI/CD to publish schemas to docs.comfy.org
2. **Version Management**: Implement semantic versioning for schema changes
3. **Migration Tools**: Create utilities to migrate between schema versions
4. **Testing**: Add automated tests for schema generation and validation
5. **Documentation**: Generate human-readable documentation from schemas
## References
- **Zod Documentation**: https://zod.dev/
- **JSON Schema Specification**: https://json-schema.org/
- **ComfyUI Workflow Specification**: https://docs.comfy.org/specs/workflow_json

View File

@@ -4,7 +4,7 @@ import { fromZodError } from 'zod-validation-error'
// GroupNode is hacking node id to be a string, so we need to allow that.
// innerNode.id = `${this.node.id}:${i}`
// Remove it after GroupNode is redesigned.
/**
/**
* Node identifier that can be either a number or string.
* Numeric IDs are standard, string IDs are used for GroupNodes.
*/
@@ -19,7 +19,7 @@ export const zNodeInputName = z
export type NodeId = z.infer<typeof zNodeId>
/**
/**
* Index of a slot on a node (input or output).
* Can be number or string that parses to a number.
*/
@@ -38,7 +38,7 @@ export const zSlotIndex = z
// TODO: Investigate usage of array and number as data type usage in custom nodes.
// Known usage:
// - https://github.com/rgthree/rgthree-comfy Context Big node is using array as type.
/**
/**
* Data type for node inputs/outputs. Can be string, array of strings, or number.
* Most common types are strings like 'IMAGE', 'LATENT', 'MODEL', etc.
*/
@@ -46,7 +46,7 @@ export const zDataType = z
.union([z.string(), z.array(z.string()), z.number()])
.describe('Data type specification for node connections')
/**
/**
* 2D position or size vector [x, y].
* Can be array tuple or object with numeric indices.
*/
@@ -71,7 +71,10 @@ const zModelFile = z
/** Download URL for the model */
url: z.string().url().describe('Download URL for the model'),
/** File hash for integrity verification */
hash: z.string().optional().describe('File hash for integrity verification'),
hash: z
.string()
.optional()
.describe('File hash for integrity verification'),
/** Hash algorithm type (e.g., 'sha256') */
hash_type: z.string().optional().describe('Hash algorithm type'),
/** Directory where model should be stored */
@@ -336,7 +339,7 @@ export const zBaseExportableGraph = z.object({
subgraphs: z.array(zSubgraphInstance).optional()
})
/**
/**
* ComfyUI Workflow JSON Schema version 0.4 (legacy).
* This is the original workflow format used by ComfyUI.
*/
@@ -353,15 +356,26 @@ export const zComfyWorkflow = zBaseExportableGraph
/** All nodes in the workflow */
nodes: z.array(zComfyNode).describe('All nodes in the workflow'),
/** Node connections (legacy tuple format) */
links: z.array(zComfyLink).describe('Node connections in legacy tuple format'),
links: z
.array(zComfyLink)
.describe('Node connections in legacy tuple format'),
/** Floating links (unconnected endpoints) */
floatingLinks: z.array(zComfyLinkObject).optional().describe('Floating links with unconnected endpoints'),
floatingLinks: z
.array(zComfyLinkObject)
.optional()
.describe('Floating links with unconnected endpoints'),
/** Visual groupings of nodes */
groups: z.array(zGroup).optional().describe('Visual groupings of nodes'),
/** Workflow configuration settings */
config: zConfig.optional().nullable().describe('Workflow configuration settings'),
config: zConfig
.optional()
.nullable()
.describe('Workflow configuration settings'),
/** Extra metadata and extensions */
extra: zExtra.optional().nullable().describe('Extra metadata and extensions'),
extra: zExtra
.optional()
.nullable()
.describe('Extra metadata and extensions'),
/** Schema version number */
version: z.number().describe('Schema version number (0.4)'),
/** Required model files */
@@ -376,7 +390,7 @@ export const zComfyWorkflow = zBaseExportableGraph
interface ComfyWorkflow1BaseType {
id?: string
revision?: number
version: 2
version: 1
models?: z.infer<typeof zModelFile>[]
state: z.infer<typeof zGraphState>
}
@@ -405,8 +419,8 @@ interface ComfyWorkflow1BaseOutput extends ComfyWorkflow1BaseType {
}
}
/**
* ComfyUI Workflow JSON Schema version 2 (current).
/**
* ComfyUI Workflow JSON Schema version 1 (current).
* This is the modern workflow format with improved structure and features.
*/
export const zComfyWorkflow1 = zBaseExportableGraph
@@ -414,11 +428,17 @@ export const zComfyWorkflow1 = zBaseExportableGraph
/** Unique workflow identifier */
id: z.string().uuid().optional().describe('Unique workflow identifier'),
/** Workflow revision number for tracking changes */
revision: z.number().optional().describe('Workflow revision number for tracking changes'),
/** Schema version (always 2 for this format) */
version: z.literal(2).describe('Schema version number (2)'),
revision: z
.number()
.optional()
.describe('Workflow revision number for tracking changes'),
/** Schema version (always 1 for this format) */
version: z.literal(1).describe('Schema version number (1)'),
/** Workflow configuration settings */
config: zConfig.optional().nullable().describe('Workflow configuration settings'),
config: zConfig
.optional()
.nullable()
.describe('Workflow configuration settings'),
/** Graph state for ID tracking and generation */
state: zGraphState.describe('Graph state for ID tracking and generation'),
/** Visual groupings of nodes */
@@ -426,35 +446,49 @@ export const zComfyWorkflow1 = zBaseExportableGraph
/** All nodes in the workflow */
nodes: z.array(zComfyNode).describe('All nodes in the workflow'),
/** Node connections (modern object format) */
links: z.array(zComfyLinkObject).optional().describe('Node connections in modern object format'),
links: z
.array(zComfyLinkObject)
.optional()
.describe('Node connections in modern object format'),
/** Floating links (unconnected endpoints) */
floatingLinks: z.array(zComfyLinkObject).optional().describe('Floating links with unconnected endpoints'),
floatingLinks: z
.array(zComfyLinkObject)
.optional()
.describe('Floating links with unconnected endpoints'),
/** Reroute nodes for organizing connections */
reroutes: z.array(zReroute).optional().describe('Reroute nodes for organizing connections'),
reroutes: z
.array(zReroute)
.optional()
.describe('Reroute nodes for organizing connections'),
/** Extra metadata and extensions */
extra: zExtra.optional().nullable().describe('Extra metadata and extensions'),
extra: zExtra
.optional()
.nullable()
.describe('Extra metadata and extensions'),
/** Required AI model files */
models: z.array(zModelFile).optional().describe('Required AI model files'),
/** Subgraph definitions */
definitions: z
.object({
/** Nested subgraph definitions */
subgraphs: z.lazy(
(): z.ZodArray<
z.ZodType<
SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>,
z.ZodTypeDef,
SubgraphDefinitionBase<ComfyWorkflow1BaseInput>
>,
'many'
> => z.array(zSubgraphDefinition)
).describe('Nested subgraph definitions')
subgraphs: z
.lazy(
(): z.ZodArray<
z.ZodType<
SubgraphDefinitionBase<ComfyWorkflow1BaseOutput>,
z.ZodTypeDef,
SubgraphDefinitionBase<ComfyWorkflow1BaseInput>
>,
'many'
> => z.array(zSubgraphDefinition)
)
.describe('Nested subgraph definitions')
})
.optional()
.describe('Subgraph definitions')
})
.passthrough()
.describe('ComfyUI Workflow JSON Schema v2')
.describe('ComfyUI Workflow JSON Schema v1')
export const zExportedSubgraphIONode = z.object({
id: zNodeId,
@@ -568,8 +602,8 @@ const zWorkflowVersion = z.object({
/**
* Validates a ComfyUI workflow JSON against the appropriate schema version.
* Supports both legacy (v0.4) and modern (v2) workflow formats.
*
* Supports both legacy (v0.4) and modern (v1) workflow formats.
*
* @param data - The workflow data to validate
* @param onError - Error callback function for validation failures
* @returns Parsed and validated workflow data or null if invalid
@@ -586,14 +620,14 @@ export async function validateComfyWorkflow(
const error = fromZodError(versionResult.error)
onError(`Workflow does not contain a valid version. Zod error:\n${error}`)
return null
} else if (versionResult.data.version === 1 || versionResult.data.version === 2) {
// Modern schema versions 1 or 2 (v2 is current)
} else if (versionResult.data.version === 1) {
// Modern schema version 1 (current)
result = await zComfyWorkflow1.safeParseAsync(data)
} else {
// Legacy or unknown version: defaults to 0.4 format
result = await zComfyWorkflow.safeParseAsync(data)
}
if (result.success) return result.data
const error = fromZodError(result.error)