diff --git a/tools/image_generation_tool.py b/tools/image_generation_tool.py index c97d9e7b64a..68f4af9ac0c 100644 --- a/tools/image_generation_tool.py +++ b/tools/image_generation_tool.py @@ -29,7 +29,33 @@ import uuid from typing import Any, Dict, Optional, Union from urllib.parse import urlencode -import fal_client +# fal_client is imported lazily — see _load_fal_client(). Pulling it +# eagerly added ~64 ms to every CLI cold start because +# discover_builtin_tools() imports this module unconditionally during +# the registry walk, even when image generation is never used. +# +# Tests that monkeypatch this attribute (e.g. +# ``monkeypatch.setattr(image_tool, "fal_client", fake_fal_client)``) +# still work: _load_fal_client() short-circuits when the attribute is +# anything truthy, so a test-installed mock is not overwritten by a +# subsequent real import. +fal_client: Any = None + + +def _load_fal_client() -> Any: + """Lazily import fal_client and rebind the module global on first use. + + Idempotent. Returns the (now-loaded) ``fal_client`` module reference. + Skips the import if the global is already truthy — this preserves the + test pattern of monkeypatching the module global to install a mock. + """ + global fal_client + if fal_client is not None: + return fal_client + import fal_client as _fal_client # noqa: F811 — module-global rebind + fal_client = _fal_client + return fal_client + from tools.debug_helpers import DebugSession from tools.managed_tool_gateway import resolve_managed_tool_gateway @@ -338,6 +364,9 @@ class _ManagedFalSyncClient: """Small per-instance wrapper around fal_client.SyncClient for managed queue hosts.""" def __init__(self, *, key: str, queue_run_origin: str): + # Trigger the lazy import on first construction. Idempotent — the + # placeholder is overwritten with the real module on first call. + _load_fal_client() sync_client_class = getattr(fal_client, "SyncClient", None) if sync_client_class is None: raise RuntimeError("fal_client.SyncClient is required for managed FAL gateway mode") @@ -435,6 +464,8 @@ def _get_managed_fal_client(managed_gateway): def _submit_fal_request(model: str, arguments: Dict[str, Any]): """Submit a FAL request using direct credentials or the managed queue gateway.""" + # Trigger the lazy import on first call. Idempotent. + _load_fal_client() request_headers = {"x-idempotency-key": str(uuid.uuid4())} managed_gateway = _resolve_managed_fal_gateway() if managed_gateway is None: @@ -788,7 +819,11 @@ def check_image_generation_requirements() -> bool: """ try: if check_fal_api_key(): - fal_client # noqa: F401 — SDK presence check + # Trigger the lazy fal_client import here as the SDK presence + # check. Raises ImportError if the optional ``fal-client`` + # package isn't installed; the caller's except ImportError + # below catches that and continues to plugin probing. + _load_fal_client() return True except ImportError: pass