Files
gokayfem-ComfyUI-fal-API/__init__.py
T
Gökay Aydoğan 8a47f0598b feat: v2.5.0 — async execution, typed builders, featured tier, registry freshness (#80)
- 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.
2026-07-02 20:37:15 +03:00

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"]