- 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.
63 lines
1.7 KiB
Python
63 lines
1.7 KiB
Python
import importlib
|
|
import importlib.util
|
|
|
|
node_list = [
|
|
"image_node",
|
|
"video_node",
|
|
"llm_node",
|
|
"vlm_node",
|
|
"trainer_node",
|
|
"upscaler_node",
|
|
"platform_node",
|
|
"billing_node",
|
|
"inbox_node",
|
|
"util_dataset_node",
|
|
"util_media_in_node",
|
|
"util_video_node",
|
|
"util_image_node",
|
|
"util_data_node",
|
|
"builder_node",
|
|
]
|
|
|
|
NODE_CLASS_MAPPINGS = {}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
|
|
|
for module_name in node_list:
|
|
imported_module = importlib.import_module(f".nodes.{module_name}", __name__)
|
|
|
|
NODE_CLASS_MAPPINGS = {**NODE_CLASS_MAPPINGS, **imported_module.NODE_CLASS_MAPPINGS}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
**NODE_DISPLAY_NAME_MAPPINGS,
|
|
**imported_module.NODE_DISPLAY_NAME_MAPPINGS,
|
|
}
|
|
|
|
try:
|
|
from .nodes.dynamic import get_dynamic_mappings
|
|
|
|
dyn_classes, dyn_display = get_dynamic_mappings()
|
|
# static nodes win on any key collision
|
|
for k, v in dyn_classes.items():
|
|
NODE_CLASS_MAPPINGS.setdefault(k, v)
|
|
for k, v in dyn_display.items():
|
|
NODE_DISPLAY_NAME_MAPPINGS.setdefault(k, v)
|
|
except Exception as _dynamic_error: # never break static nodes
|
|
import logging
|
|
|
|
logging.getLogger(__name__).error(
|
|
"Failed to load dynamic fal nodes: %s", _dynamic_error
|
|
)
|
|
|
|
|
|
WEB_DIRECTORY = "./web"
|
|
|
|
try:
|
|
from .nodes import server_routes as _server_routes # noqa: F401 registers /fal_api routes
|
|
except Exception as _routes_error: # never break node loading over HTTP extras
|
|
import logging
|
|
|
|
logging.getLogger(__name__).warning(
|
|
"fal API server routes not registered: %s", _routes_error
|
|
)
|
|
|
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
|