Comfy-Org/ComfyUI
A prompt enters as JSON at POST /prompt, is validated into an executable output list, and becomes a heapq item on PromptQueue. The worker thread wraps it in DynamicPrompt (original + ephemeral + override layers) and builds an ExecutionList — a topological dissolve where nodes become ready when their link dependencies resolve. Each node call resolves inputs from cached upstream outputs, invokes the node's FUNCTION (mapped over input lists, async-aware), and stores outputs in signature-keyed caches; UI outputs and progress flow back over WebSocket via send_sync. Model weights flow through folder_paths resolvers into comfy.sd loaders that detect the architecture and wrap it in a ModelPatcher managed by model_management for VRAM offload. Results persist to the output dir (SaveImage) and the run record lands in the in-memory history dict (capped at 10000) plus the assets DB when enabled.
- Input boundary: POST /prompt -> trigger_on_prompt(on_prompt_handlers) -> number assignment -> node_replace_manager.apply_replacements -> execution.validate_prompt -> PromptQueue.put((number, prompt_id, prompt, extra_data, outputs_to_execute, sensitive)).
- validate_prompt collects OUTPUT_NODE classes (optionally filtered by partial_execution_targets), recursively validates inputs (type matching via validate_node_input, min/max, combo membership, custom VALIDATE_INPUTS/validate_inputs hooks), detects dependency cycles, and validates loop boundaries via validate_loops.
- Execution loop: PromptExecutor.execute_async wraps everything in torch.inference_mode, builds DynamicPrompt + ExecutionList, seeds it with output node ids, then repeatedly calls stage_node_execution -> module-level execute() -> complete/unstage until the list empties.
- Per-node execution order inside execute(): check output cache -> get_input_data resolves each input link through execution_list.get_cache (missing -> mark_missing and lazy evaluation via check_lazy_status returns PENDING) -> instantiate/cache the node object in caches.objects -> get_output_data -> _async_map_node_over_list -> node FUNCTION.
- Intermediate results are cached by CacheSet: outputs cache uses CacheKeySetInputSignature (class_type + IS_CHANGED value + ancestor input signatures, folded via to_hashable) while objects cache uses CacheKeySetID (node_id, class_type); modes are CLASSIC/LRU/NONE/RAM_PRESSURE chosen by --cache-* flags.
- Node functions can return plain tuples, dicts with 'result'/'ui'/'expand', or V3 NodeOutput objects; 'expand' produces an ephemeral subgraph injected into DynamicPrompt.ephemeral_prompt and re-scheduled, with the node's final outputs resolved later from pending_subgraph_results.
- Streaming output: nodes emit UI data via 'ui' dicts -> register_executed_outputs enriches them into asset references -> send_sync('executed', ...) to the client; sampler progress goes through comfy.utils.ProgressBar global hook -> send_sync('progress') + binary preview frames.
- Model loading path: folder_paths.get_full_path_or_raise resolves a filename inside a category's search dirs -> comfy.utils.load_torch_file reads safetensors/ckpt -> model_detection.model_config_from_unet fingerprints the state dict -> comfy.supported_models builds the model -> ModelPatcher wraps it -> load_models_gpu places it per VRAM policy.
- Sampling path inside a node: KSampler.sample -> common_ksampler -> comfy.sample.prepare_noise (seeded) -> latent_preview.prepare_callback -> comfy.sample.sample -> comfy.samplers.KSampler.sample drives the denoise loop calling model.apply_model per sigma step.
- History persistence: task_done() pops currently_running, strips the sensitive tuple element via process_item=remove_sensitive, and stores {prompt, outputs, status} in self.history keyed by prompt_id, evicting FIFO beyond MAXIMUM_HISTORY_SIZE=10000.
- Auth-sensitive extra_data keys (auth_token_comfy_org, api_key_comfy_org) are popped into a separate `sensitive` dict at enqueue time, reattached only inside the worker, and injected into node inputs via hidden input plumbing (Hidden.auth_token_comfy_org / hidden AUTH_TOKEN_COMFY_ORG).
- File output boundary: SaveImage writes PNGs (with embedded prompt/workflow metadata unless --disable-metadata) into folder_paths.get_output_directory() with counter-suffixed filenames; LoadImage reads from the input directory through annotated_filepath/get_annotated_filepath resolvers.
- Asset enrichment: when enabled, AssetManager pauses its background scan during execution, registers uploaded/executed files into the DB catalog, and /view resolves blake3:<hash> filenames to asset paths via resolve_hash_to_path.