Graph-health heuristics in workflow analysis
Status: implemented (this PR) · Implementation branch:spec/workflow-graph-health
Prior art: filliptm/ComfyUI_FL-MCPworkflow_overview(web/js/query_executor.js) reports node-type histograms, disconnected nodes, and missing required inputs — but client-side with slot-name heuristics, because the live canvas lacks schema data. We run server-side with real/object_inforequired/optional data, keeping their heuristics only as a fallback for uninstalled node types.
Relationship to graph query (#169)
get_workflow (action:"query") / panel_query_graph (comfyui-mcp #179 + panel #77) is the generic surface — arbitrary filter/traverse/aggregate over a graph, agent-composed per question. Graph health is the curated counterpart: a fixed set of opinionated findings (disconnected, missing-required, duplicate loads, orphaned branches, muted/bypassed) with severities and remediation hints, computed server-side against /object_info. Health does not duplicate query capability; anything beyond these findings should be answered with the query tools rather than by growing this list.
Motivation
create_workflow (action:"validate") (src/tools/workflow-validate.ts → src/services/workflow-validator.ts) catches hard errors: unknown node types, missing required inputs, broken/self-referencing links, invalid output indices, out-of-list combo values. get_workflow (action:"analyze") (src/tools/workflow-library.ts:409-509) explains structure. Neither answers “is this graph healthy”: dead subgraphs, duplicate model loads wasting VRAM, sampler branches whose outputs never reach a save node, or muted/bypassed nodes silently dropping connections.
Placement: create_workflow (action:"validate") (primary), get_workflow (action:"analyze") view:"health" (secondary)
create_workflow (action:"validate")takes raw workflow JSON — the shape agents hold while authoring; health findings are a validation concern (severity-tagged issues) and compose withValidationResult.issues.get_workflow (action:"analyze")takes a library filename; extending itsviewenum with"health"reusesloadWorkflowApi()(which already converts UI→API viaconvertUiToApi) for near-zero extra code.
Tool API
create_workflow (action:"validate") — unchanged params plus:
get_workflow (action:"analyze") — view enum gains "health".
New service: src/services/workflow-health.ts
objectInfo; no extra fetch, trivially unit-testable.
Checks (API-format WorkflowJSON)
- Disconnected nodes (warning): build in/out adjacency from
[nodeId, outIdx]input tuples; a node with no inbound links, no consumers, and not anoutput_nodeper object_info is isolated. - Missing required inputs (warning): primary source
objectInfo[class_type].input.required. Fallback heuristic only when the class is absent from object_info (uninstalled custom node): FL-MCP’sisInputSlotRequiredrules — known-optional slot-name list; Loader/Load families require*name*/*path*/*ckpt*; Sampler families requiremodel/positive/negative/latent_image; VAE families requiresamples/images/vae; default required. Taggedheuristic: true. - Duplicate model loads (warning): group nodes by
(class_type, model-file widget value)where class_type matches/Loader|Load/and the value matches the model-file regex already inworkflow-validator.ts:213(/\.(safetensors|gguf|ckpt|pt|pth|bin|sft)$/i). ≥2 nodes on the same file → one finding listing all node ids. - Orphaned branches / no output reachable (warning): reverse-BFS from every output node (
objectInfo[ct].output_node === trueplus the hardcoded SaveImage/PreviewImage/SaveAnimated* list atworkflow-validator.ts:137-146). Unreached non-output nodes are “computed but never saved” — reported as one finding per connected component (avoids 40 line items on big graphs). The existing zero-output validator check stays as-is. - Muted/bypassed (info): via
_meta.modewhere present. Known limitation, documented:convertUiToApidrops mode-2/4 nodes (comment atworkflow-converter.ts:731; the_meta.mode: "muted"|"bypassed"mapping lives at932-942), so forget_workflow (action:"analyze")we surface the converter’s existing warnings instead; raw API JSON handed tocreate_workflow (action:"validate")is checked directly, and silently skipped when_metais absent.
Output composition
create_workflow (action:"validate") text output gains, after Errors/Warnings:
result.issues (severity warning/info) so existing consumers see them; ValidationIssue.severity widens to "error" | "warning" | "info" and gains optional kind?: string; ValidationResult gains health: GraphHealth. valid remains errors-only — health never flips validity.
Implementation plan
src/services/workflow-health.ts—analyzeGraphHealth(~180 lines, pure). Types imported fromsrc/comfyui/types.ts.src/services/workflow-validator.ts— widenValidationIssue.severity; callanalyzeGraphHealthafter existing step 4 (objectInfo already in hand); merge findings; returnhealth.src/tools/workflow-validate.ts—healthparam; render the section incl. theinfobucket.src/tools/workflow-library.ts— add"health"toget_workflow (action:"analyze")’sviewenum; handler callsanalyzeGraphHealth(workflow, objectInfo)(both in scope at lines 443-444) and renders.
Test plan (vitest)
Newsrc/__tests__/services/workflow-health.test.ts — pure-function tests with a small mocked ObjectInfo (pattern: workflow-converter.test.ts): isolated node; duplicate checkpoint; orphaned upscale branch; heuristic fallback for an unknown Sampler-family class missing model; _meta.mode info; per-component grouping. Plus one validator test asserting health findings merge without affecting valid.
Rollout / compat
Non-breaking: defaulthealth: true adds text but never errors (flip the default to false later if token cost matters in compact mode). No config. Regenerate tool docs via npm run docs:gen.