- Async node execution: on ComfyUI with native async support (detected via comfy_execution.utils, added in the same commit as async nodes), all dynamic nodes and Fal Any Endpoint run as coroutines — independent graph branches execute fal calls concurrently with no Submit/Collect required. Uploads/downloads/preflight run off-loop; older ComfyUI versions keep byte-identical sync behavior. Live-verified: two concurrent generations in 2.5s total. - Typed builder nodes (FAL/Utils/Builders): 8 chainable builders (LoRA, embedding, ControlNet, IP-Adapter, reference image/element, multi-prompt shot, key-value, JSON merge) replacing JSON-by-hand for the 467 object-typed inputs across the catalog; shapes validated against live OpenAPI schemas. - Discovery: FAL/Featured tier (data/featured_models.json, 26 flagship endpoints with display-name overrides), 434 models flagged as superseded within their family in node help, thumbnails in the endpoint picker. - Registry freshness: startup delta check against the live catalog (logs how many models are newer than the snapshot), sidebar Registry section with one-click refresh (atomic registry write; restart note). - Docs: README 1,946 → 327 lines; model tables moved to MODELS.md (generator retargeted; weekly refresh workflow now regenerates it); CONTRIBUTING.md redirects hand-written-node PRs to the registry and featured-list workflow. Review fixes: spend-guard preflight moved off the event loop in the async path; registry writes atomically via temp+rename; freshness daemon gated off in tests; non-finite numbers rejected in FalKeyValue; sidebar poll budget aligned with the server timeout.
5.8 KiB
Contributing to ComfyUI-fal-API
Thanks for helping! Before you write anything, read this — it will probably save you the PR entirely.
A new fal model does NOT need code
Historically, adding a model to this pack meant hand-writing a node class. That is no longer how it works. Every live public model on fal gets a node automatically, generated at ComfyUI startup from the committed snapshot at data/fal_registry.json. No node class, no mapping entry, no code.
The snapshot stays fresh two ways:
- A weekly GitHub Action (
.github/workflows/registry-refresh.yml) rebuilds the registry and opens a PR. - Anyone can run it locally:
python scripts/build_registry.py --out data/fal_registry.json(thenpython scripts/build_readme.pyto regenerate MODELS.md).
To check whether a model is already covered:
grep '"endpoint_id": "fal-ai/your/endpoint"' data/fal_registry.json
# or browse MODELS.md, or search the node browser in ComfyUI
If a model is live on fal.ai/models but missing from the snapshot, rerun scripts/build_registry.py — if it's still missing, open an issue with the endpoint id. And if you need a model right now, the Fal Any Endpoint (fal) node calls any endpoint by id without any registry entry at all.
So: please don't open a PR that adds a node class for a new model. It will be redundant the moment the registry refreshes.
Want a model promoted or renamed? Edit data/featured_models.json
When a model deserves curation — a spot in the FAL/Featured menu tier or a friendlier display name — add its endpoint to data/featured_models.json (featured tier + display-name override). That's the whole change: one JSON entry, not a new node class.
When a hand-written node IS justified
A curated node earns its place only when the generated node genuinely can't express the UX:
- Multi-endpoint orchestration — one node fanning out to several endpoints (e.g. Combined Video Generation).
- Special input ergonomics — first/last-frame image pairing, unified T2V/I2V dispatch, LoRA slots with per-slot scales.
If you're writing one, the rules are non-negotiable:
- Import only from the
.fal_utilsfacade (from .fal_utils import ApiHandler, FalConfig, ImageUtils, ResultProcessor, ...) — never reach intonodes/utils/internals or callfal_clientdirectly. The facade gives you the result cache, spend guard, session ledger, and error handling for free. - Raise errors — no silent fallbacks. Never return blank images or
"Error: ..."strings; letApiHandlersurface fal's actual error message. - Tooltips on every input. Users should never have to guess a parameter.
- Never change existing node keys, input names, or output signatures. Existing user workflows reference them forever.
tests/legacy_node_keys.jsonis the snapshot of keys that must never be removed or renamed, andtests/test_mappings.pyfails the suite if one disappears. New inputs must be optional with backward-compatible defaults. - Add tests alongside the existing ones in
tests/.
Dev setup
pip install -r requirements.txt
python -m pytest tests # the suite MUST pass
ruff check . # lint, same as CI
CI runs both on every PR (Python 3.10 and 3.12). The most important test to understand is the compatibility snapshot: tests/test_mappings.py asserts that every node key recorded in tests/legacy_node_keys.json still registers. If your change makes it fail, the fix is to restore the key — not to edit the snapshot.
Architecture map
scripts/build_registry.py queries fal's platform APIs → writes the snapshot
data/fal_registry.json committed model catalog (~1,391 models)
data/featured_models.json curation: featured tier + display-name overrides
scripts/build_readme.py renders MODELS.md from the snapshot
nodes/dynamic/ the auto-generated node machinery
registry_loader.py reads the snapshot, applies [dynamic_nodes] config;
never raises — failures degrade to curated-only
factory.py builds one node class per model, in memory
schema_to_inputs.py registry input specs → ComfyUI INPUT_TYPES (+ tooltips)
arguments.py widget/socket values → API arguments (uploads media)
outputs.py API result → IMAGE / VIDEO / AUDIO / result_json
any_endpoint.py the generic "call any endpoint by id" node
nodes/*.py curated hand-written nodes (image, video, llm, vlm,
trainer, upscaler, util_*)
nodes/fal_utils.py import facade — node modules import ONLY from here
nodes/utils/ the implementations behind the facade: api, config,
pricing, result_cache, ledger, billing (spend guard),
job_store, media, archive, errors, logger
nodes/platform_node.py platform nodes (Submit/Collect, costs, request ids)
nodes/inbox_node.py durable job inbox
nodes/billing_node.py account balance
nodes/server_routes.py HTTP endpoints backing the frontend extension
web/ ComfyUI frontend: cost badges, fal sidebar,
endpoint autocomplete
tests/ pytest suite, incl. the legacy_node_keys.json snapshot
PR checklist
- Not a hand-written node for a single new model (registry covers it — see above)
python -m pytest testspasses locallyruff check .is clean- No existing node keys, inputs, or outputs changed
- New curated node (if truly justified): uses
.fal_utils, raises errors, has tooltips and tests - No secrets, no
config.ini, no generated artifacts in the diff