feat: workflow graph system — blocks 1-20 complete checkpoint
Full graph-based workflow execution engine with: - WorkflowGraphRuntime with two-phase dispatch (collect → fire Celery tasks) - Node registry with contract validation, socket types, execution kinds - WorkflowRuntimeServices: preflight, context resolution, shadow-mode A/B - WorkflowRouter: CRUD + dispatch/preflight/run-history API endpoints - OutputTypeContracts: workflow binding, rollout-mode resolution - Admin router: output-type workflow binding endpoints - Frontend: complete workflow editor with drag-drop canvas, node inspector, module bundles, reference bundles, preflight panel, validation banner, authoring guidance, blueprint templates, shadow/rollout gate UI - Tests: comprehensive coverage for all workflow modules - Docs: NODE_CONTRACT_AUDIT and VALIDATION_ERROR_INVENTORY All 20 blocks from NEXT_20_BLOCK_BATCH_PLAN completed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+109
-46
@@ -4,7 +4,12 @@ import type { OutputTypeArtifactKind, OutputTypeWorkflowRolloutMode } from './ou
|
||||
export type WorkflowPresetType = 'still' | 'still_graph' | 'turntable' | 'multi_angle' | 'still_with_exports' | 'custom'
|
||||
export type WorkflowExecutionMode = 'legacy' | 'graph' | 'shadow'
|
||||
export type WorkflowStarterFamily = 'cad_file' | 'order_line'
|
||||
export type WorkflowBlueprintType = 'cad_intake' | 'order_rendering' | 'still_graph_reference'
|
||||
export type WorkflowBlueprintType =
|
||||
| 'cad_intake'
|
||||
| 'order_rendering'
|
||||
| 'still_graph_reference'
|
||||
| 'still_graph_alpha_reference'
|
||||
| 'still_graph_blend_reference'
|
||||
export type WorkflowCanonicalBlueprintType = WorkflowBlueprintType | 'starter_cad_intake' | 'starter_order_rendering'
|
||||
|
||||
export interface WorkflowRolloutLatestRun {
|
||||
@@ -173,6 +178,8 @@ export interface WorkflowOrderLineContextOption {
|
||||
value: string
|
||||
label: string
|
||||
meta: string
|
||||
is_renderable: boolean
|
||||
renderability_reason: string | null
|
||||
}
|
||||
|
||||
export interface WorkflowOrderLineContextGroup {
|
||||
@@ -379,49 +386,89 @@ function extractRenderParamsFromNodes(nodes: WorkflowNode[], step: string): Work
|
||||
return normalizeRenderParams(match?.params ?? {})
|
||||
}
|
||||
|
||||
function buildOrderLineStillGraphNodes(renderParams: WorkflowParams): { nodes: WorkflowNode[]; edges: WorkflowEdge[] } {
|
||||
function buildOrderLineStillGraphNodes(
|
||||
renderParams: WorkflowParams,
|
||||
options: {
|
||||
transparentBg?: boolean
|
||||
includeBlendExport?: boolean
|
||||
} = {},
|
||||
): { nodes: WorkflowNode[]; edges: WorkflowEdge[] } {
|
||||
const resolvedRenderParams = {
|
||||
use_custom_render_settings: false,
|
||||
...renderParams,
|
||||
...(options.transparentBg !== undefined ? { transparent_bg: options.transparentBg } : {}),
|
||||
}
|
||||
|
||||
const nodes: WorkflowNode[] = [
|
||||
buildWorkflowNode('setup', 'order_line_setup', 0, 160, { label: 'Order Line Setup' }),
|
||||
buildWorkflowNode('template', 'resolve_template', 220, 160, { label: 'Resolve Template' }),
|
||||
buildWorkflowNode('populate_materials', 'auto_populate_materials', 220, 320, {
|
||||
label: 'Auto Populate Materials',
|
||||
type: 'processNode',
|
||||
}),
|
||||
buildWorkflowNode('bbox', 'glb_bbox', 220, 40, {
|
||||
label: 'Compute Bounding Box',
|
||||
type: 'processNode',
|
||||
}),
|
||||
buildWorkflowNode('resolve_materials', 'material_map_resolve', 440, 200, {
|
||||
label: 'Resolve Material Map',
|
||||
type: 'processNode',
|
||||
}),
|
||||
buildWorkflowNode('render', 'blender_still', 680, 160, {
|
||||
label: 'Still Render',
|
||||
type: 'renderNode',
|
||||
params: resolvedRenderParams,
|
||||
}),
|
||||
buildWorkflowNode('output', 'output_save', 920, 120, {
|
||||
label: 'Save Output',
|
||||
type: 'outputNode',
|
||||
}),
|
||||
buildWorkflowNode('notify', 'notify', 920, 220, {
|
||||
label: 'Notify Result',
|
||||
type: 'outputNode',
|
||||
}),
|
||||
]
|
||||
|
||||
const edges: WorkflowEdge[] = [
|
||||
{ from: 'setup', to: 'template' },
|
||||
{ from: 'setup', to: 'populate_materials' },
|
||||
{ from: 'setup', to: 'bbox' },
|
||||
{ from: 'template', to: 'resolve_materials' },
|
||||
{ from: 'populate_materials', to: 'resolve_materials' },
|
||||
{ from: 'resolve_materials', to: 'render' },
|
||||
{ from: 'bbox', to: 'render' },
|
||||
{ from: 'template', to: 'render' },
|
||||
{ from: 'render', to: 'output' },
|
||||
{ from: 'render', to: 'notify' },
|
||||
]
|
||||
|
||||
if (options.includeBlendExport) {
|
||||
nodes.push(
|
||||
buildWorkflowNode('blend_export', 'export_blend', 920, 360, {
|
||||
label: 'Export Blend',
|
||||
type: 'outputNode',
|
||||
}),
|
||||
buildWorkflowNode('save_blend', 'output_save', 1160, 320, {
|
||||
label: 'Save Blend Output',
|
||||
type: 'outputNode',
|
||||
params: { expected_artifact_role: 'blend_export' },
|
||||
}),
|
||||
buildWorkflowNode('notify_blend', 'notify', 1160, 420, {
|
||||
label: 'Notify Blend Export',
|
||||
type: 'outputNode',
|
||||
}),
|
||||
)
|
||||
edges.push(
|
||||
{ from: 'setup', to: 'blend_export' },
|
||||
{ from: 'template', to: 'blend_export' },
|
||||
{ from: 'blend_export', to: 'save_blend' },
|
||||
{ from: 'blend_export', to: 'notify_blend' },
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
nodes: [
|
||||
buildWorkflowNode('setup', 'order_line_setup', 0, 160, { label: 'Order Line Setup' }),
|
||||
buildWorkflowNode('template', 'resolve_template', 220, 160, { label: 'Resolve Template' }),
|
||||
buildWorkflowNode('populate_materials', 'auto_populate_materials', 220, 320, {
|
||||
label: 'Auto Populate Materials',
|
||||
type: 'processNode',
|
||||
}),
|
||||
buildWorkflowNode('bbox', 'glb_bbox', 220, 40, {
|
||||
label: 'Compute Bounding Box',
|
||||
type: 'processNode',
|
||||
}),
|
||||
buildWorkflowNode('resolve_materials', 'material_map_resolve', 440, 200, {
|
||||
label: 'Resolve Material Map',
|
||||
type: 'processNode',
|
||||
}),
|
||||
buildWorkflowNode('render', 'blender_still', 680, 160, {
|
||||
label: 'Still Render',
|
||||
type: 'renderNode',
|
||||
params: { use_custom_render_settings: false, ...renderParams },
|
||||
}),
|
||||
buildWorkflowNode('output', 'output_save', 920, 120, {
|
||||
label: 'Save Output',
|
||||
type: 'outputNode',
|
||||
}),
|
||||
buildWorkflowNode('notify', 'notify', 920, 220, {
|
||||
label: 'Notify Result',
|
||||
type: 'outputNode',
|
||||
}),
|
||||
],
|
||||
edges: [
|
||||
{ from: 'setup', to: 'template' },
|
||||
{ from: 'setup', to: 'populate_materials' },
|
||||
{ from: 'setup', to: 'bbox' },
|
||||
{ from: 'template', to: 'resolve_materials' },
|
||||
{ from: 'populate_materials', to: 'resolve_materials' },
|
||||
{ from: 'resolve_materials', to: 'render' },
|
||||
{ from: 'bbox', to: 'render' },
|
||||
{ from: 'template', to: 'render' },
|
||||
{ from: 'render', to: 'output' },
|
||||
{ from: 'render', to: 'notify' },
|
||||
],
|
||||
nodes,
|
||||
edges,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,12 +728,22 @@ export function buildWorkflowBlueprintConfig(blueprint: WorkflowBlueprintType):
|
||||
}
|
||||
}
|
||||
|
||||
const { nodes, edges } = buildOrderLineStillGraphNodes({
|
||||
const stillReferenceParams: WorkflowParams = {
|
||||
render_engine: 'cycles',
|
||||
samples: 256,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
})
|
||||
}
|
||||
const buildStillReference = () => {
|
||||
if (blueprint === 'still_graph_alpha_reference') {
|
||||
return buildOrderLineStillGraphNodes(stillReferenceParams, { transparentBg: true })
|
||||
}
|
||||
if (blueprint === 'still_graph_blend_reference') {
|
||||
return buildOrderLineStillGraphNodes(stillReferenceParams, { includeBlendExport: true })
|
||||
}
|
||||
return buildOrderLineStillGraphNodes(stillReferenceParams)
|
||||
}
|
||||
const { nodes, edges } = buildStillReference()
|
||||
|
||||
return {
|
||||
version: 1,
|
||||
@@ -819,7 +876,13 @@ export function normalizeWorkflowConfig(raw: Record<string, unknown>): WorkflowC
|
||||
}
|
||||
}
|
||||
|
||||
if (rawUi.blueprint === 'cad_intake' || rawUi.blueprint === 'order_rendering' || rawUi.blueprint === 'still_graph_reference') {
|
||||
if (
|
||||
rawUi.blueprint === 'cad_intake' ||
|
||||
rawUi.blueprint === 'order_rendering' ||
|
||||
rawUi.blueprint === 'still_graph_reference' ||
|
||||
rawUi.blueprint === 'still_graph_alpha_reference' ||
|
||||
rawUi.blueprint === 'still_graph_blend_reference'
|
||||
) {
|
||||
const canonical = buildWorkflowBlueprintConfig(rawUi.blueprint)
|
||||
return {
|
||||
...canonical,
|
||||
|
||||
Reference in New Issue
Block a user