comfyanonymous/ComfyUI
comfy_execution/
The execution engine. Small (2,037 lines, 8 files) but central — every prompt the server queues runs through these modules. comfy_execution/ is invoked by the top-level execution.py (PromptExecutor) and consumed by all nodes via the comfy_api.
Layout
comfy_execution/
├── graph.py # DynamicPrompt, ExecutionList, ExecutionBlocker, get_input_info
├── graph_utils.py # GraphBuilder, is_link
├── caching.py # CacheKeySet, BasicCache, HierarchicalCache, LRUCache, RAMPressureCache, NullCache
├── cache_provider.py # External cache provider hooks (custom nodes can plug in)
├── jobs.py # Job objects and lifecycle
├── progress.py # Progress state, WebUIProgressHandler, PreviewImageTuple
├── utils.py # CurrentNodeContext, get_executing_context
└── validation.py # validate_node_inputKey abstractions
| Type / function | File | Description |
|---|---|---|
DynamicPrompt |
comfy_execution/graph.py |
Mutable view over a prompt; supports node expansion via add_ephemeral_node |
ExecutionList |
comfy_execution/graph.py |
DAG traversal with topology + caching |
ExecutionBlocker |
comfy_execution/graph_utils.py |
Sentinel that short-circuits a branch |
GraphBuilder |
comfy_execution/graph_utils.py |
Helper for nodes that build subgraphs at runtime |
CacheKeySetID, CacheKeySetInputSignature |
comfy_execution/caching.py |
Two key derivation strategies — by node ID and by input-signature hash |
HierarchicalCache |
comfy_execution/caching.py |
The default cache; nests by subcache key for subgraph isolation |
LRUCache |
comfy_execution/caching.py |
Bounded by entry count |
RAMPressureCache |
comfy_execution/caching.py |
Drops large entries when free RAM falls below the configured headroom |
NullCache |
comfy_execution/caching.py |
Disables caching (--cache-none) |
register_cache_provider, _get_cache_providers |
comfy_execution/cache_provider.py |
External cache plugin API (used by comfy_api.latest.Caching) |
Job, JobStatus, get_job, get_all_jobs |
comfy_execution/jobs.py |
Job records the executor reads/writes during a prompt |
get_progress_state, WebUIProgressHandler, PreviewImageTuple |
comfy_execution/progress.py |
Progress fan-out to the WebSocket |
CurrentNodeContext, get_executing_context |
comfy_execution/utils.py |
Async-context-friendly "what node am I in right now" |
validate_node_input |
comfy_execution/validation.py |
Pre-execution input validation |
How it fits together
graph LR
Prompt[/prompt JSON/] --> DP[DynamicPrompt]
DP --> EL[ExecutionList]
EL -->|next ready node| Exec[execution.PromptExecutor]
Exec -->|key| Caches{CacheSet}
Caches -->|hit| Reuse[reuse outputs]
Caches -->|miss| Run[run node FUNCTION]
Run --> Outputs[node outputs]
Outputs --> Caches
Run -->|node expansion| GB[GraphBuilder] -->|new ephemeral nodes| DP
Run --> Progress[get_progress_state]
Progress --> WS[WebUIProgressHandler]DynamicPrompt is what makes node expansion work. A node can return {"expand": GraphBuilder().build(), "result": (...)}; the executor splices the new nodes into the graph as ephemeral nodes and continues. This is how features like recursive samplers and "Custom Sampler" composition work.
HierarchicalCache indexes by both subcache key (which subgraph context you're in) and data key (the actual input signature). When an ephemeral subgraph runs, its cache lives in a sub-namespace so it doesn't pollute the parent.
Cache modes (CLI mapping)
| Flag | Cache type | Behavior |
|---|---|---|
--cache-classic |
HierarchicalCache (CLASSIC) |
Aggressive — drop output ASAP. The historical default |
--cache-lru N |
LRUCache |
Keep up to N node results, evict by LRU |
--cache-ram [G] |
RAMPressureCache |
Keep results until free RAM drops below G GB; auto-tune by default |
--cache-none |
NullCache |
No caching |
Selection happens in prompt_worker in main.py; the chosen CacheType flows into execution.PromptExecutor.
External cache providers
Custom nodes can register a cache provider via comfy_api.latest.Caching.register_provider(...). The implementation routes through comfy_execution/cache_provider.py's register_cache_provider. Providers are called in registration order to look up and store cache entries — this is what would let you persist node outputs across processes or share them across machines.
Progress and previews
get_progress_state() is a global progress accumulator. Inside a node, set_progress(value, max_value, preview_image=...) (the V3 API surface) reports back. WebUIProgressHandler formats and pushes events to the WebSocket. Sampling preview images come through here as PreviewImageTuples of (format, PIL.Image, max_size).
Integration points
- Imported by the top-level
execution.py. - Imported by
server.pyforJobStatus,get_job,get_all_jobs. - Exposed to custom nodes via
comfy_api.latest.Cachingandcomfy_api.latest.Execution.
Where to start a change
- Adding a new cache mode: subclass
BasicCacheincaching.py, expose aCacheTypeenum entry inexecution.py, wire the flag incomfy/cli_args.py, and select it inprompt_workerinmain.py. - Tweaking node expansion: changes go in
graph.py'sDynamicPromptandExecutionList. Addtests-unit/execution_test/cases. - New progress event types: extend
PreviewImageTupleandWebUIProgressHandlerinprogress.py. Don't forget the protocol bytes inprotocol.py.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.