Files
ttulttul-ComfyUI-Modal/comfyui_modal_sync_cloud.py
T

2767 lines
106 KiB
Python

"""Stable Modal cloud entrypoint for ComfyUI Modal-Sync."""
import asyncio
import copy
import gc
import hashlib
import importlib
import importlib.metadata
import importlib.util
from io import BytesIO
import inspect
import json
import logging
import os
import queue
import sys
import tempfile
import threading
import time
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import Any, Callable, Iterable, Iterator, Mapping, Sequence
_REPO_ROOT = Path(__file__).resolve().parent
_REMOTE_REPO_ROOT = Path("/root/comfyui_modal_sync_repo")
_LOCAL_COMFYUI_ROOT = (Path.home() / "git" / "ComfyUI").resolve()
_REMOTE_COMFYUI_ROOT = Path("/root/comfyui_src")
_REMOTE_LLM_COMPILE_CACHE_ROOT = Path("/root/.cache/comfy-modal-llm")
for candidate in (
_REPO_ROOT,
_REMOTE_REPO_ROOT,
_LOCAL_COMFYUI_ROOT,
_REMOTE_COMFYUI_ROOT,
):
candidate_str = str(candidate)
try:
candidate_exists = candidate.exists()
except PermissionError:
candidate_exists = False
if candidate_exists and candidate_str not in sys.path:
sys.path.insert(0, candidate_str)
from runtime_environment import ( # noqa: E402 - paths are bootstrapped above.
COMFYUI_RUNTIME_SOURCE_DIRECTORIES as _COMFYUI_IMAGE_RUNTIME_DIRECTORIES,
COMFYUI_RUNTIME_SOURCE_FILES as _COMFYUI_IMAGE_RUNTIME_FILES,
REMOTE_APP_PROTOCOL_VERSION as _REMOTE_APP_PROTOCOL_VERSION,
REMOTE_PYTHON_VERSION,
RemoteTorchBuild as _RemoteTorchBuild,
build_remote_runtime_identity,
custom_node_runtime_packages as _custom_node_runtime_packages,
remote_accelerator_packages as _remote_accelerator_packages,
remote_accelerator_validation_command as _remote_accelerator_validation_command,
remote_apt_packages as _comfyui_apt_packages,
remote_huggingface_packages as _remote_huggingface_packages,
remote_huggingface_validation_command as _remote_huggingface_validation_command,
remote_runtime_packages as _comfyui_runtime_packages,
select_remote_torch_build as _select_remote_torch_build,
)
from llm_recovery import ( # noqa: E402
LLM_FORCE_VLLM_THROUGHPUT_PAYLOAD_KEY,
is_llm_memory_recovery_exhausted,
)
from llm_staging import resolve_and_stage_model_references # noqa: E402
from durable_state import ( # noqa: E402 - paths are bootstrapped above.
DurableObjectCommitBatch,
DurableObjectRef,
DurableStateError,
FileDurableObjectStore,
InMemoryRemoteInvocationStore,
RemoteInvocationRecord,
new_running_invocation_record,
read_modal_volume_file,
)
from output_artifacts import ( # noqa: E402 - paths are bootstrapped above.
RemoteOutputSnapshot,
capture_execution_result,
snapshot_output_directory,
)
from remote_protocol import ( # noqa: E402 - paths are bootstrapped above.
BOUNDARY_INPUT_SIGNATURES_KEY as _BOUNDARY_INPUT_SIGNATURES_KEY,
PRIMITIVE_WIDGET_INPUT_TYPES as _PRIMITIVE_WIDGET_INPUT_TYPES,
)
from serialization import ( # noqa: E402 - paths are bootstrapped above.
coerce_serialized_node_outputs,
deserialize_node_inputs,
deserialize_node_outputs,
deserialize_value,
serialize_mapping,
serialize_node_inputs,
serialize_node_outputs,
serialize_value,
)
from session_state import ( # noqa: E402 - paths are bootstrapped above.
InMemoryRemoteSessionBridgeStore,
InMemoryRemoteSessionStore,
RemoteSessionBridgeRecord,
RemoteSessionBridgeRecoveryKind,
RemoteSessionBridgeRef,
RemoteSessionHandle,
RemoteSessionStateError,
RemoteSessionValueRef,
is_remote_session_bridge_ref_payload,
is_remote_session_handle_payload,
is_remote_session_value_ref_payload,
stable_session_bridge_key,
)
from settings import ( # noqa: E402 - paths are bootstrapped above.
DEFAULT_MODAL_SECRET_NAME,
get_settings,
modal_deployment_app_name,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_comfy_bootstrap import (
CloudComfyBootstrapHooks,
_active_comfyui_root,
_alias_flux_rms_norm_weight_keys,
_build_checkpoint_loader_cache_key,
_build_clip_loader_cache_key,
_build_dual_clip_loader_cache_key,
_build_unet_loader_cache_key,
_build_vae_loader_cache_key,
_clone_loader_cache_outputs,
_clone_loader_cache_value,
_custom_node_package_for_candidate_file,
_ensure_comfy_runtime_initialized,
_ensure_comfyui_support_packages,
_ensure_default_custom_nodes_dir,
_ensure_headless_prompt_server_instance,
_ensure_prompt_node_classes_registered,
_extract_custom_nodes_bundle,
_force_import_package_from_root,
_install_loader_cache_wrappers,
_install_model_state_dict_compatibility_wrappers,
_iter_custom_nodes_manifest_assets,
_iter_missing_class_candidate_files,
_load_custom_nodes_manifest,
_load_execution_module,
_load_nodes_module,
_loader_cache_metric_snapshot,
_materialize_custom_nodes_manifest_assets,
_materialize_remote_asset_path,
_missing_node_class_diagnostics,
_patched_folder_paths_absolute_lookup,
_prompt_missing_node_class_types,
_readthrough_cache_path,
_record_loader_cache_metric,
_register_custom_nodes_root,
_register_modal_sync_runtime_nodes,
_reload_external_custom_nodes_for_missing_classes,
_resolve_custom_nodes_archives,
_resolve_custom_nodes_bundle_path,
_resolve_runtime_asset_path,
_rewrite_modal_asset_references,
_serialize_loader_cache_key,
_validated_custom_node_asset_relative_path,
_wrap_loader_method_with_cache,
clear_warm_caches as clear_comfy_bootstrap_warm_caches,
configure_cloud_comfy_bootstrap_hooks,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_comfy_bootstrap import (
CloudComfyBootstrapHooks,
_active_comfyui_root,
_alias_flux_rms_norm_weight_keys,
_build_checkpoint_loader_cache_key,
_build_clip_loader_cache_key,
_build_dual_clip_loader_cache_key,
_build_unet_loader_cache_key,
_build_vae_loader_cache_key,
_clone_loader_cache_outputs,
_clone_loader_cache_value,
_custom_node_package_for_candidate_file,
_ensure_comfy_runtime_initialized,
_ensure_comfyui_support_packages,
_ensure_default_custom_nodes_dir,
_ensure_headless_prompt_server_instance,
_ensure_prompt_node_classes_registered,
_extract_custom_nodes_bundle,
_force_import_package_from_root,
_install_loader_cache_wrappers,
_install_model_state_dict_compatibility_wrappers,
_iter_custom_nodes_manifest_assets,
_iter_missing_class_candidate_files,
_load_custom_nodes_manifest,
_load_execution_module,
_load_nodes_module,
_loader_cache_metric_snapshot,
_materialize_custom_nodes_manifest_assets,
_materialize_remote_asset_path,
_missing_node_class_diagnostics,
_patched_folder_paths_absolute_lookup,
_prompt_missing_node_class_types,
_readthrough_cache_path,
_record_loader_cache_metric,
_register_custom_nodes_root,
_register_modal_sync_runtime_nodes,
_reload_external_custom_nodes_for_missing_classes,
_resolve_custom_nodes_archives,
_resolve_custom_nodes_bundle_path,
_resolve_runtime_asset_path,
_rewrite_modal_asset_references,
_serialize_loader_cache_key,
_validated_custom_node_asset_relative_path,
_wrap_loader_method_with_cache,
clear_warm_caches as clear_comfy_bootstrap_warm_caches,
configure_cloud_comfy_bootstrap_hooks,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_node_output_cache import (
CloudNodeOutputCacheHooks,
_NodeOutputCacheLookupResult,
_PersistedNodeCacheRestoreState,
_await_maybe,
_boundary_output_node_ids,
_build_node_output_cache_immediate_signature,
_build_node_output_cache_signature_from_key_set_async,
_build_node_output_cache_signature_from_key_set_sync,
_cache_signature_link_output_index,
_canonicalize_node_output_cache_key_part,
_deserialize_node_output_cache_entry,
_emit_restored_node_cache_events,
_estimate_node_output_cache_value_size_bytes,
_include_unique_id_in_input_signature,
_install_prompt_executor_persisted_cache_restore,
_is_input_signature_cache_key_set,
_node_output_cache_ancestor_ids,
_node_output_cache_key,
_node_output_cache_key_from_key_set_async,
_node_output_cache_key_from_key_set_sync,
_node_output_cache_key_preview,
_node_output_cache_store,
_node_output_cache_store_get,
_node_output_cache_store_put,
_node_output_cache_value_preview,
_persist_node_output_cache_entries,
_prompt_executor_cache_get_sync,
_restore_persisted_node_output_cache_entries,
_restore_persisted_node_output_cache_entries_into_prepared_cache,
_serialize_node_output_cache_entry,
_tensor_cache_key_digest,
configure_cloud_node_output_cache_hooks,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_node_output_cache import (
CloudNodeOutputCacheHooks,
_NodeOutputCacheLookupResult,
_PersistedNodeCacheRestoreState,
_await_maybe,
_boundary_output_node_ids,
_build_node_output_cache_immediate_signature,
_build_node_output_cache_signature_from_key_set_async,
_build_node_output_cache_signature_from_key_set_sync,
_cache_signature_link_output_index,
_canonicalize_node_output_cache_key_part,
_deserialize_node_output_cache_entry,
_emit_restored_node_cache_events,
_estimate_node_output_cache_value_size_bytes,
_include_unique_id_in_input_signature,
_install_prompt_executor_persisted_cache_restore,
_is_input_signature_cache_key_set,
_node_output_cache_ancestor_ids,
_node_output_cache_key,
_node_output_cache_key_from_key_set_async,
_node_output_cache_key_from_key_set_sync,
_node_output_cache_key_preview,
_node_output_cache_store,
_node_output_cache_store_get,
_node_output_cache_store_put,
_node_output_cache_value_preview,
_persist_node_output_cache_entries,
_prompt_executor_cache_get_sync,
_restore_persisted_node_output_cache_entries,
_restore_persisted_node_output_cache_entries_into_prepared_cache,
_serialize_node_output_cache_entry,
_tensor_cache_key_digest,
configure_cloud_node_output_cache_hooks,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_prompt_execution import (
CloudPromptExecutionHooks,
_ReusablePromptExecutorState,
_aggregate_mapped_phase_outputs,
_apply_boundary_inputs,
_boundary_input_cache_signature,
_build_phase_subgraph_payload,
_coerce_primitive_prompt_input_value,
_coerce_prompt_primitive_input_values,
_collapse_cache_slot,
_copy_json_safe_prompt_metadata,
_execute_mapped_subgraph_payload,
_execute_node_locally_raw,
_execute_prompt_executor_compat,
_execute_subgraph_prompt,
_extract_prompt_executor_error,
_extract_prompt_executor_error_payload,
_format_prompt_executor_error_payload,
_get_or_create_prompt_executor_state,
_install_metadata_safe_dynamic_prompt_wrapper,
_invoke_original_node,
_is_link,
_log_prompt_executor_failure_details,
_mapped_phase_definition,
_merge_static_and_mapped_outputs,
_merge_static_or_mapped_values,
_node_input_type_map,
_node_input_types,
_node_required_input_names,
_normalize_link_output_index,
_normalize_prompt_input_value,
_normalize_subgraph_payload,
_prompt_executor_cache_config,
_prompt_executor_ram_thresholds,
_remote_session_ref_cache_signature,
_reset_prompt_executor_request_state,
_resolve_required_subgraph_nodes,
_serialize_prompt_executor_cache_scope,
_shared_subgraph_payload_fields,
_short_circuit_restored_session_output_subgraph,
_split_phase_outputs,
_summarize_suspicious_prompt_inputs,
_temporary_node_mapping,
_temporary_progress_hook,
_temporary_prompt_metadata,
_temporary_remote_interrupt_monitor,
_trim_subgraph_payload_to_required_nodes,
_unwrap_wrapped_prompt_link,
_validate_prompt_input_shapes,
_validate_required_prompt_inputs,
clear_warm_caches as clear_cloud_prompt_execution_warm_caches,
configure_cloud_prompt_execution_hooks,
execute_node_locally,
execute_subgraph_locally,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_prompt_execution import (
CloudPromptExecutionHooks,
_ReusablePromptExecutorState,
_aggregate_mapped_phase_outputs,
_apply_boundary_inputs,
_boundary_input_cache_signature,
_build_phase_subgraph_payload,
_coerce_primitive_prompt_input_value,
_coerce_prompt_primitive_input_values,
_collapse_cache_slot,
_copy_json_safe_prompt_metadata,
_execute_mapped_subgraph_payload,
_execute_node_locally_raw,
_execute_prompt_executor_compat,
_execute_subgraph_prompt,
_extract_prompt_executor_error,
_extract_prompt_executor_error_payload,
_format_prompt_executor_error_payload,
_get_or_create_prompt_executor_state,
_install_metadata_safe_dynamic_prompt_wrapper,
_invoke_original_node,
_is_link,
_log_prompt_executor_failure_details,
_mapped_phase_definition,
_merge_static_and_mapped_outputs,
_merge_static_or_mapped_values,
_node_input_type_map,
_node_input_types,
_node_required_input_names,
_normalize_link_output_index,
_normalize_prompt_input_value,
_normalize_subgraph_payload,
_prompt_executor_cache_config,
_prompt_executor_ram_thresholds,
_remote_session_ref_cache_signature,
_reset_prompt_executor_request_state,
_resolve_required_subgraph_nodes,
_serialize_prompt_executor_cache_scope,
_shared_subgraph_payload_fields,
_short_circuit_restored_session_output_subgraph,
_split_phase_outputs,
_summarize_suspicious_prompt_inputs,
_temporary_node_mapping,
_temporary_progress_hook,
_temporary_prompt_metadata,
_temporary_remote_interrupt_monitor,
_trim_subgraph_payload_to_required_nodes,
_unwrap_wrapped_prompt_link,
_validate_prompt_input_shapes,
_validate_required_prompt_inputs,
clear_warm_caches as clear_cloud_prompt_execution_warm_caches,
configure_cloud_prompt_execution_hooks,
execute_node_locally,
execute_subgraph_locally,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_prompt_server_shims import (
CloudPromptServerHooks,
_HeadlessPromptQueue,
_HeadlessPromptServerInstance,
_NullPromptServer,
_TracingPromptServer,
configure_cloud_prompt_server_hooks,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_prompt_server_shims import (
CloudPromptServerHooks,
_HeadlessPromptQueue,
_HeadlessPromptServerInstance,
_NullPromptServer,
_TracingPromptServer,
configure_cloud_prompt_server_hooks,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_session_bridge import (
CloudSessionBridgeHooks,
_RemoteSessionBridgeResolutionStats,
_bridge_record_replays_sampling_node,
_build_durable_bridge_rehydration_plan,
_build_remote_session_bridge_record,
_deserialize_remote_session_bridge_producer_inputs,
_get_remote_session_bridge_value,
_json_payload_size_bytes,
_load_loader_snapshot_profile,
_load_remote_session_bridge_record,
_log_remote_session_resolution_summary,
_offload_large_bridge_payloads,
_payload_remote_session_handle,
_record_remote_session_resolution_event,
_rehydrate_remote_session_bridge_value,
_remote_session_bridge_recovery_input_names,
_remote_session_bridge_replay_stack,
_resolve_remote_session_inputs,
_restore_planned_remote_session_bridge_value,
_restore_serialized_remote_session_bridge_value,
_sanitize_payload_for_session_bridge_record,
_select_remote_session_bridge_recovery_kind,
_serialize_durable_bridge_output,
_session_bridge_store,
_snapshot_profile_store,
_store_remote_session_bridge_record,
_store_remote_session_bridge_value,
_subgraph_contains_sampling_node,
configure_cloud_session_bridge_hooks,
remote_session_store,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_session_bridge import (
CloudSessionBridgeHooks,
_RemoteSessionBridgeResolutionStats,
_bridge_record_replays_sampling_node,
_build_durable_bridge_rehydration_plan,
_build_remote_session_bridge_record,
_deserialize_remote_session_bridge_producer_inputs,
_get_remote_session_bridge_value,
_json_payload_size_bytes,
_load_loader_snapshot_profile,
_load_remote_session_bridge_record,
_log_remote_session_resolution_summary,
_offload_large_bridge_payloads,
_payload_remote_session_handle,
_record_remote_session_resolution_event,
_rehydrate_remote_session_bridge_value,
_remote_session_bridge_recovery_input_names,
_remote_session_bridge_replay_stack,
_resolve_remote_session_inputs,
_restore_planned_remote_session_bridge_value,
_restore_serialized_remote_session_bridge_value,
_sanitize_payload_for_session_bridge_record,
_select_remote_session_bridge_recovery_kind,
_serialize_durable_bridge_output,
_session_bridge_store,
_snapshot_profile_store,
_store_remote_session_bridge_record,
_store_remote_session_bridge_value,
_subgraph_contains_sampling_node,
configure_cloud_session_bridge_hooks,
remote_session_store,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_durable_invocation import (
DurableInvocationErrors,
_begin_remote_invocation,
_canary_barrier_marker_exists,
_canary_barrier_marker_key,
_canary_interrupt_requested,
_complete_remote_invocation,
_durable_object_store,
_execute_canary_payload,
_execute_payload_with_output_capture,
_execute_with_durable_invocation,
_fail_remote_invocation,
_invocation_record_store,
_load_completed_remote_invocation_result,
_load_remote_invocation_record,
_put_canary_barrier_marker,
_raise_if_canary_interrupted,
_remote_comfy_output_directory,
_remote_output_snapshot,
_store_remote_invocation_record,
_wait_for_canary_barrier,
_wait_for_running_remote_invocation,
configure_durable_invocation_errors,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_durable_invocation import (
DurableInvocationErrors,
_begin_remote_invocation,
_canary_barrier_marker_exists,
_canary_barrier_marker_key,
_canary_interrupt_requested,
_complete_remote_invocation,
_durable_object_store,
_execute_canary_payload,
_execute_payload_with_output_capture,
_execute_with_durable_invocation,
_fail_remote_invocation,
_invocation_record_store,
_load_completed_remote_invocation_result,
_load_remote_invocation_record,
_put_canary_barrier_marker,
_raise_if_canary_interrupted,
_remote_comfy_output_directory,
_remote_output_snapshot,
_store_remote_invocation_record,
_wait_for_canary_barrier,
_wait_for_running_remote_invocation,
configure_durable_invocation_errors,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_image_env import (
_REMOTE_LLM_COMPILE_CACHE_ROOT,
_install_custom_node_packages,
_install_remote_accelerator_packages,
_install_remote_torch_build,
_modal_image_environment,
_modal_secret_from_settings,
_model_stager_image_environment,
_remote_engine_cls_options,
_should_ignore_comfyui_path,
_should_ignore_repo_path,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_image_env import (
_REMOTE_LLM_COMPILE_CACHE_ROOT,
_install_custom_node_packages,
_install_remote_accelerator_packages,
_install_remote_torch_build,
_modal_image_environment,
_modal_secret_from_settings,
_model_stager_image_environment,
_remote_engine_cls_options,
_should_ignore_comfyui_path,
_should_ignore_repo_path,
)
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_app_guard import guard_against_existing_modal_app
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_app_guard import guard_against_existing_modal_app
try: # noqa: E402 - support package and flat Modal-container imports.
from .cloud_runtime_context import (
interrupt_flag_store,
invocation_record_store,
node_output_cache_store,
register_cloud_runtime_stores,
session_bridge_store,
snapshot_profile_store,
volume_store,
)
except ImportError: # pragma: no cover - exercised by flat cloud imports.
from cloud_runtime_context import (
interrupt_flag_store,
invocation_record_store,
node_output_cache_store,
register_cloud_runtime_stores,
session_bridge_store,
snapshot_profile_store,
volume_store,
)
logger = logging.getLogger(__name__)
# Cloud app and ComfyUI bootstrap state.
_CLOUD_HANDLER_NAME = "comfyui-modal-sync-cloud-timestamped"
# Loader and prewarm state.
_LOADER_PREWARM_PLAN_KEYS_LOCK = threading.Lock()
_LOADER_PREWARM_PLAN_KEYS: set[str] = set()
_LLM_PREWARM_PLAN_KEYS_LOCK = threading.Lock()
_LLM_PREWARM_PLAN_KEYS: set[str] = set()
# Modal volume reload and poisoned-container retirement state.
_MODAL_VOLUME_RELOAD_MARKERS_LOCK = threading.Lock()
_MODAL_VOLUME_RELOAD_OPEN_FILE_RETRY_DELAYS_SECONDS = (
0.0,
0.25,
0.5,
1.0,
2.0,
4.0,
8.0,
)
_MODAL_VOLUME_RELOAD_MARKER_CACHE_LIMIT = 256
_MODAL_VOLUME_RELOAD_MARKERS: queue.SimpleQueue[str] | None = None
_MODAL_VOLUME_RELOAD_MARKER_SET: set[str] = set()
_CONTAINER_TERMINATION_LOCK = threading.Lock()
_REMOTE_ERROR_CONTAINER_EXIT_DELAY_SECONDS = 1.0
_CONTAINER_TERMINATION_SCHEDULED = False
# Durable invocation and session-bridge state.
_REMOTE_INVOCATION_ABANDON_JOIN_SECONDS = 0.5
try:
import modal # type: ignore
except ModuleNotFoundError: # pragma: no cover - remote entrypoint only.
modal = None
class RemoteSubgraphExecutionError(RuntimeError):
"""Raised when remote subgraph execution fails."""
class RemoteInvocationInProgressError(RuntimeError):
"""Raised when an idempotent invocation is already running remotely."""
class RemoteInvocationAbandonedError(RuntimeError):
"""Raised when a streamed invocation loses its consumer before completion."""
class RemoteCanaryInterruptedError(RuntimeError):
"""Raised when a live remote canary observes its shared interrupt flag."""
class RemoteCanaryBarrierTimeoutError(TimeoutError):
"""Raised when live canary calls fail to overlap before their deadline."""
class ExistingModalAppError(RuntimeError):
"""Raised when deploying would overwrite an existing Modal app."""
configure_durable_invocation_errors(
DurableInvocationErrors(
invocation_in_progress=RemoteInvocationInProgressError,
canary_interrupted=RemoteCanaryInterruptedError,
canary_barrier_timeout=RemoteCanaryBarrierTimeoutError,
)
)
class RemoteFailureDisposition(str, Enum):
"""Describe whether one remote failure implies poisoned worker state."""
EXPECTED = "expected"
DETERMINISTIC = "deterministic"
POISONED_WORKER = "poisoned-worker"
def _guard_against_existing_modal_app(settings: Any, modal_module: Any) -> None:
"""Fail local Modal app construction when the configured app already exists."""
guard_against_existing_modal_app(
settings,
modal_module,
error_type=ExistingModalAppError,
)
@dataclass
class _RemoteExecutionControl:
"""Track interruption state for one active remote payload execution."""
cancellation_event: threading.Event
interrupt_flag_key: str
def _meaningful_progress_values(
node_state: dict[str, Any]
) -> tuple[float, float] | None:
"""Return numeric progress values only for node states that represent real progress."""
try:
progress_value = float(node_state.get("value", 0.0))
max_value = float(node_state.get("max", 1.0))
except (TypeError, ValueError):
return None
if max_value <= 1.0:
return None
return progress_value, max_value
def _schedule_process_exit(delay_seconds: float, exit_code: int) -> None:
"""Exit the current process after a short delay to retire a bad Modal container."""
def exit_later() -> None:
"""Sleep briefly so Modal can ship the error response before exiting the worker."""
if delay_seconds > 0:
time.sleep(delay_seconds)
logger.error(
"Exiting Modal container process with code=%s after remote failure.",
exit_code,
)
sys.stdout.flush()
sys.stderr.flush()
os._exit(exit_code)
threading.Thread(
target=exit_later,
name="modal-container-exit",
daemon=True,
).start()
def _schedule_process_exit_unless_cancelled(
*,
delay_seconds: float,
exit_code: int,
cancel_event: threading.Event,
reason: str,
) -> None:
"""Exit the current process after a delay unless the caller cancels first."""
def exit_later() -> None:
"""Wait for cancellation or exit the worker if the delay expires."""
if delay_seconds > 0 and cancel_event.wait(timeout=delay_seconds):
logger.debug("Cancelled delayed Modal container restart for %s.", reason)
return
logger.error(
"Exiting Modal container process with code=%s after %s.", exit_code, reason
)
sys.stdout.flush()
sys.stderr.flush()
os._exit(exit_code)
threading.Thread(
target=exit_later,
name="modal-container-cancel-restart",
daemon=True,
).start()
def _schedule_remote_cancel_restart(
*,
component_id: str,
completion_event: threading.Event,
) -> bool:
"""Restart the Modal worker if a cancelled remote prompt keeps executing."""
if not _is_modal_container_runtime():
return False
delay_seconds = max(0.0, get_settings().remote_cancel_restart_seconds)
logger.warning(
"Remote cancellation requested for component=%s; scheduling container restart in %.3fs unless execution stops first.",
component_id,
delay_seconds,
)
_schedule_process_exit_unless_cancelled(
delay_seconds=delay_seconds,
exit_code=0,
cancel_event=completion_event,
reason=f"remote cancellation timeout for component={component_id}",
)
return True
def _is_interrupt_like_failure(exc: Exception) -> bool:
"""Return whether one remote failure represents an expected interruption rather than a crash."""
return "interrupt" in str(exc).lower()
def _is_session_state_like_failure(exc: Exception) -> bool:
"""Return whether one remote failure came from prompt-scoped session routing/state issues."""
if isinstance(exc, RemoteSessionStateError):
return True
return "remote session" in str(exc).lower()
def _remote_failure_disposition(exc: Exception) -> RemoteFailureDisposition:
"""Classify one execution failure for worker-retirement decisions."""
if _is_interrupt_like_failure(exc) or _is_session_state_like_failure(exc):
return RemoteFailureDisposition.EXPECTED
if isinstance(exc, MemoryError) or is_llm_memory_recovery_exhausted(exc):
return RemoteFailureDisposition.POISONED_WORKER
message = str(exc).lower()
poisoned_runtime_markers = (
"cuda out of memory",
"cuda error",
"device-side assert",
"illegal memory access",
"cublas_status",
"cudnn_status",
"hip error",
)
if any(marker in message for marker in poisoned_runtime_markers):
return RemoteFailureDisposition.POISONED_WORKER
return RemoteFailureDisposition.DETERMINISTIC
def _maybe_schedule_container_termination_on_error(
payload: dict[str, Any],
exc: Exception,
) -> bool:
"""Retire the current Modal container after a remote execution crash when configured."""
if not _is_modal_container_runtime():
return False
if not bool(payload.get("terminate_container_on_error", True)):
return False
disposition = _remote_failure_disposition(exc)
if disposition is not RemoteFailureDisposition.POISONED_WORKER:
logger.warning(
"Skipping Modal container termination for component=%s failure_disposition=%s because the worker is safe to reuse.",
payload.get("component_id"),
disposition.value,
exc_info=(type(exc), exc, exc.__traceback__),
)
return False
global _CONTAINER_TERMINATION_SCHEDULED
with _CONTAINER_TERMINATION_LOCK:
if _CONTAINER_TERMINATION_SCHEDULED:
return False
_CONTAINER_TERMINATION_SCHEDULED = True
logger.error(
"Scheduling Modal container termination after remote execution failure for component=%s.",
payload.get("component_id"),
exc_info=(type(exc), exc, exc.__traceback__),
)
_schedule_process_exit(_REMOTE_ERROR_CONTAINER_EXIT_DELAY_SECONDS, 1)
return True
def _build_cloud_log_formatter() -> logging.Formatter:
"""Return the default formatter for remote Modal-Sync logs with timestamps."""
return logging.Formatter(
fmt="%(asctime)s.%(msecs)03d +%(relativeCreated)07.0fms %(levelname)s [%(name)s] %(message)s",
datefmt="%H:%M:%S",
)
def _configure_cloud_logging() -> logging.Logger:
"""Install a dedicated timestamped handler for the cloud runtime logger."""
logger.setLevel(logging.INFO)
for existing_handler in logger.handlers:
if getattr(existing_handler, "name", "") == _CLOUD_HANDLER_NAME:
return logger
handler = logging.StreamHandler(sys.stdout)
handler.set_name(_CLOUD_HANDLER_NAME)
handler.setLevel(logging.INFO)
handler.setFormatter(_build_cloud_log_formatter())
logger.addHandler(handler)
logger.propagate = False
return logger
def _is_modal_container_runtime() -> bool:
"""Return whether the current process is executing inside a Modal container."""
return os.getenv("MODAL_IS_REMOTE") == "1" or bool(os.getenv("MODAL_TASK_ID"))
def _cloud_formatter() -> logging.Formatter:
"""Return the configured formatter used for cloud phase trace lines."""
for existing_handler in logger.handlers:
if getattr(existing_handler, "name", "") == _CLOUD_HANDLER_NAME:
formatter = existing_handler.formatter
if formatter is not None:
return formatter
return _build_cloud_log_formatter()
def _emit_cloud_info(message: str, *args: Any) -> None:
"""Emit an info line through logging and mirror it to stdout inside Modal containers."""
if not _is_modal_container_runtime():
logger.info(message, *args)
return
record = logger.makeRecord(
logger.name,
logging.INFO,
__file__,
0,
message,
args,
exc_info=None,
)
print(_cloud_formatter().format(record), file=sys.stdout, flush=True)
def _remote_execution_key(payload: dict[str, Any]) -> tuple[str, str]:
"""Return the registry key for one active remote execution."""
prompt_id = str(
payload.get("prompt_id") or payload.get("component_id") or "modal-subgraph"
)
component_id = str(payload.get("component_id") or "single-node")
return prompt_id, component_id
def _observe_remote_workflow_for_llm_mode(payload: dict[str, Any]) -> None:
"""Record real workflow arrivals for container-local vLLM auto promotion."""
if payload.get("payload_kind") == "canary":
return
from modal_llm_runtime import (
force_modal_vllm_throughput_after_memory_recovery,
observe_modal_workflow_execution,
)
prompt_id = payload.get("prompt_id")
normalized_prompt_id = str(prompt_id).strip() if prompt_id is not None else None
if bool(payload.get(LLM_FORCE_VLLM_THROUGHPUT_PAYLOAD_KEY)):
force_modal_vllm_throughput_after_memory_recovery(normalized_prompt_id)
return
observe_modal_workflow_execution(normalized_prompt_id)
def _remote_interrupt_flag_key(prompt_id: str, component_id: str) -> str:
"""Return the shared Modal interrupt-store key for one payload execution."""
return f"{prompt_id}:{component_id}"
@contextmanager
def _registered_remote_execution(
payload: dict[str, Any],
) -> Iterator[_RemoteExecutionControl]:
"""Prepare interruption state for one active remote execution."""
prompt_id, component_id = _remote_execution_key(payload)
control = _RemoteExecutionControl(
cancellation_event=threading.Event(),
interrupt_flag_key=_remote_interrupt_flag_key(prompt_id, component_id),
)
try:
yield control
finally:
interrupt_store = interrupt_flag_store()
if modal is not None and interrupt_store is not None:
interrupt_store.pop(control.interrupt_flag_key, None)
@contextmanager
def _timed_phase(phase: str, **fields: Any) -> Iterator[None]:
"""Log a start/finish pair with elapsed time for a named execution phase."""
field_suffix = ""
if fields:
rendered_fields = " ".join(f"{key}={value}" for key, value in fields.items())
field_suffix = f" {rendered_fields}"
phase_started_at = time.perf_counter()
_emit_cloud_info("Starting %s%s", phase, field_suffix)
try:
yield
finally:
_emit_cloud_info(
"Finished %s in %.3fs%s",
phase,
time.perf_counter() - phase_started_at,
field_suffix,
)
_configure_cloud_logging()
class _BoundedStreamEventBuffer:
"""Bound progress memory while preserving terminal stream events."""
def __init__(self, maxsize: int) -> None:
"""Initialize a bounded event queue and close signal."""
self._queue: queue.Queue[tuple[str, Any]] = queue.Queue(maxsize=max(4, maxsize))
self._closed = threading.Event()
self._dropped_progress_events = 0
self._dropped_lock = threading.Lock()
@property
def dropped_progress_events(self) -> int:
"""Return how many stale progress events were coalesced away."""
with self._dropped_lock:
return self._dropped_progress_events
@property
def queue_size(self) -> int:
"""Return the approximate number of currently buffered events."""
return self._queue.qsize()
def publish_progress(self, payload: Any) -> None:
"""Publish the newest progress event without exceeding the queue bound."""
event = ("progress", payload)
while not self._closed.is_set():
try:
self._queue.put_nowait(event)
return
except queue.Full:
try:
discarded_event = self._queue.get_nowait()
except queue.Empty:
continue
if discarded_event[0] != "progress":
self._queue.put_nowait(discarded_event)
return
with self._dropped_lock:
self._dropped_progress_events += 1
def publish_terminal(self, event_kind: str, payload: Any) -> bool:
"""Publish a result, error, or completion unless the consumer closed."""
while not self._closed.is_set():
try:
self._queue.put((event_kind, payload), timeout=0.1)
return True
except queue.Full:
continue
return False
def get(self) -> tuple[str, Any]:
"""Wait for and return the next buffered stream event."""
return self._queue.get()
def close(self) -> None:
"""Release any producer waiting after the consumer stops."""
self._closed.set()
def _abandon_streamed_remote_invocation(
*,
payload: Mapping[str, Any],
running_record: RemoteInvocationRecord | None,
cancellation_event: threading.Event | None,
event_buffer: _BoundedStreamEventBuffer,
worker_thread: threading.Thread,
) -> None:
"""Cancel unfinished compute and make its invocation record retryable."""
component_id = str(payload.get("component_id") or "payload")
if cancellation_event is not None:
cancellation_event.set()
event_buffer.close()
worker_thread.join(timeout=max(0.0, _REMOTE_INVOCATION_ABANDON_JOIN_SECONDS))
abandoned_error = RemoteInvocationAbandonedError(
f"Remote invocation stream for component {component_id!r} closed before completion."
)
if running_record is not None:
_fail_remote_invocation(running_record, abandoned_error)
logger.warning(
"Abandoned remote invocation stream component=%s invocation_id=%s worker_stopped=%s; the invocation is retryable.",
component_id,
running_record.invocation_id if running_record is not None else "none",
not worker_thread.is_alive(),
)
def _stream_remote_payload_events(
payload: dict[str, Any],
kwargs_payload: bytes | bytearray | str | dict[str, Any],
cancellation_event: threading.Event | None = None,
interrupt_store: Any | None = None,
interrupt_flag_key: str | None = None,
) -> Iterator[dict[str, Any]]:
"""Yield progress and result events for one remote payload execution."""
event_buffer = _BoundedStreamEventBuffer(get_settings().stream_event_queue_maxsize)
task_id = os.getenv("MODAL_TASK_ID")
component_id = str(payload.get("component_id") or "payload")
invocation_id = str(payload.get("invocation_id") or "none")
def publish_status(progress_state: dict[str, Any]) -> None:
"""Queue a progress envelope for the remote caller."""
event_buffer.publish_progress(serialize_mapping(progress_state))
def execute_once() -> bytes:
"""Run the underlying payload once and return serialized outputs."""
if payload.get("payload_kind") == "canary":
return _execute_canary_payload(
payload,
kwargs_payload,
cancellation_event=cancellation_event,
interrupt_store=interrupt_store,
interrupt_flag_key=interrupt_flag_key,
)
if payload.get("payload_kind") == "mapped_subgraph":
custom_nodes_root = _extract_custom_nodes_bundle(
payload.get("custom_nodes_bundle")
)
_ensure_comfy_runtime_initialized(custom_nodes_root)
hydrated_inputs = deserialize_node_inputs(kwargs_payload)
return serialize_node_outputs(
_execute_mapped_subgraph_payload(
payload,
hydrated_inputs,
custom_nodes_root,
status_callback=publish_status,
cancellation_event=cancellation_event,
interrupt_store=interrupt_store,
interrupt_flag_key=interrupt_flag_key,
)
)
if payload.get("payload_kind") == "subgraph":
execute_subgraph_kwargs: dict[str, Any] = {
"status_callback": publish_status
}
if (
"cancellation_event"
in inspect.signature(execute_subgraph_locally).parameters
):
execute_subgraph_kwargs["cancellation_event"] = cancellation_event
if (
"interrupt_store"
in inspect.signature(execute_subgraph_locally).parameters
):
execute_subgraph_kwargs["interrupt_store"] = interrupt_store
if (
"interrupt_flag_key"
in inspect.signature(execute_subgraph_locally).parameters
):
execute_subgraph_kwargs["interrupt_flag_key"] = interrupt_flag_key
return execute_subgraph_locally(
payload,
kwargs_payload,
**execute_subgraph_kwargs,
)
execute_node_kwargs: dict[str, Any] = {}
if "cancellation_event" in inspect.signature(execute_node_locally).parameters:
execute_node_kwargs["cancellation_event"] = cancellation_event
if "interrupt_store" in inspect.signature(execute_node_locally).parameters:
execute_node_kwargs["interrupt_store"] = interrupt_store
if "interrupt_flag_key" in inspect.signature(execute_node_locally).parameters:
execute_node_kwargs["interrupt_flag_key"] = interrupt_flag_key
return execute_node_locally(payload, kwargs_payload, **execute_node_kwargs)
object_store = _durable_object_store()
def execute_payload() -> None:
"""Run compute in a worker thread while deferring Modal volume commits."""
pending_batch: DurableObjectCommitBatch | None = None
try:
with object_store.batch_commits(commit_on_exit=False) as pending_batch:
outputs = _execute_payload_with_output_capture(payload, execute_once)
except (
Exception
) as exc: # pragma: no cover - exercised through generator consumer tests.
event_buffer.publish_terminal("error", (exc, pending_batch))
else:
result_bytes = len(outputs)
pending_object_write = bool(pending_batch and pending_batch.wrote_object)
logger.info(
"Remote stream worker produced result component=%s invocation_id=%s task_id=%s "
"result_bytes=%d pending_object_write=%s buffer_queue_size=%d.",
component_id,
invocation_id,
task_id or "none",
result_bytes,
pending_object_write,
event_buffer.queue_size,
)
publish_started_at = time.monotonic()
published = event_buffer.publish_terminal("result", (outputs, pending_batch))
logger.info(
"Finished publishing remote stream result to event buffer in %.3fs component=%s "
"invocation_id=%s task_id=%s published=%s result_bytes=%d buffer_queue_size=%d.",
time.monotonic() - publish_started_at,
component_id,
invocation_id,
task_id or "none",
published,
result_bytes,
event_buffer.queue_size,
)
finally:
event_buffer.publish_terminal("done", None)
worker_thread = threading.Thread(
target=execute_payload,
name=f"modal-stream-{payload.get('component_id', 'payload')}",
daemon=True,
)
if task_id:
yield {"kind": "remote_logs", "task_id": task_id}
running_record, completed_result = _begin_remote_invocation(payload)
if completed_result is not None:
yield {"kind": "result", "outputs": completed_result}
return
worker_thread.start()
invocation_finalized = False
try:
while True:
event_kind, event_payload = event_buffer.get()
if event_kind == "progress":
yield {"kind": "progress", **event_payload}
continue
if event_kind == "result":
outputs, pending_batch = event_payload
serialized_outputs = coerce_serialized_node_outputs(outputs)
logger.info(
"Remote stream consumer received buffered result component=%s invocation_id=%s "
"task_id=%s result_bytes=%d pending_object_write=%s buffer_queue_size=%d.",
component_id,
invocation_id,
task_id or "none",
len(serialized_outputs),
bool(pending_batch and pending_batch.wrote_object),
event_buffer.queue_size,
)
if running_record is not None:
_complete_remote_invocation(
running_record,
serialized_outputs,
pending_batch=pending_batch,
)
elif pending_batch is not None:
object_store.commit_batch(pending_batch)
invocation_finalized = True
yield_started_at = time.monotonic()
logger.info(
"Yielding remote stream result to Modal transport component=%s invocation_id=%s "
"task_id=%s result_bytes=%d buffer_queue_size=%d.",
component_id,
invocation_id,
task_id or "none",
len(serialized_outputs),
event_buffer.queue_size,
)
try:
yield {"kind": "result", "outputs": serialized_outputs}
finally:
logger.info(
"Remote stream result yield released after %.3fs component=%s invocation_id=%s "
"task_id=%s result_bytes=%d buffer_queue_size=%d.",
time.monotonic() - yield_started_at,
component_id,
invocation_id,
task_id or "none",
len(serialized_outputs),
event_buffer.queue_size,
)
continue
if event_kind == "error":
error, pending_batch = event_payload
if pending_batch is not None:
object_store.commit_batch(pending_batch)
if running_record is not None:
_fail_remote_invocation(running_record, error)
invocation_finalized = True
raise error
if event_kind == "done":
return
finally:
if invocation_finalized:
event_buffer.close()
worker_thread.join(timeout=1.0)
else:
_abandon_streamed_remote_invocation(
payload=payload,
running_record=running_record,
cancellation_event=cancellation_event,
event_buffer=event_buffer,
worker_thread=worker_thread,
)
if event_buffer.dropped_progress_events:
logger.info(
"Coalesced %d stale remote progress event(s) for component=%s to keep the stream buffer bounded.",
event_buffer.dropped_progress_events,
payload.get("component_id"),
)
def _prewarm_snapshot_state(
*,
gpu_snapshot_enabled: bool,
snapshot_profile_key: str = "",
) -> None:
"""Run snapshot-safe initialization before Modal captures a memory snapshot."""
with _timed_phase(
"prewarm_snapshot_state",
gpu_snapshot=gpu_snapshot_enabled,
snapshot_profile=snapshot_profile_key or None,
):
_ensure_comfyui_support_packages()
normalized_snapshot_profile_key = snapshot_profile_key.strip()
if gpu_snapshot_enabled and normalized_snapshot_profile_key:
_ensure_comfy_runtime_initialized(None)
_load_execution_module()
loader_prewarm_plans = _load_loader_snapshot_profile(
normalized_snapshot_profile_key
)
if loader_prewarm_plans:
_execute_loader_prewarm_plans(
component_id=f"snapshot-profile:{normalized_snapshot_profile_key}",
loader_prewarm_plans=loader_prewarm_plans,
custom_nodes_root=None,
)
_emit_cloud_info(
"Completed GPU-snapshot ComfyUI prewarm before snapshot capture."
)
return
if gpu_snapshot_enabled:
_emit_cloud_info(
"Skipping GPU-snapshot ComfyUI prewarm before snapshot capture because no snapshot profile was provided."
)
else:
_emit_cloud_info(
"Skipping full ComfyUI runtime prewarm during CPU-only snapshot to avoid accidental CUDA initialization."
)
def _reload_compile_cache_volume(volume: Any | None) -> bool:
"""Refresh persistent compiler artifacts before a runtime opens its caches."""
if volume is None:
return False
reload_method = getattr(volume, "reload", None)
if not callable(reload_method):
logger.warning("Modal compile-cache Volume does not expose reload().")
return False
with _timed_phase("llm_compile_cache_reload"):
try:
reload_method()
except RuntimeError as exc:
if _is_modal_volume_open_files_error(exc):
_log_compile_cache_memory_maps()
raise
return True
def _mapped_process_files_under(
volume_root: Path,
*,
proc_root: Path = Path("/proc"),
) -> tuple[tuple[int, str], ...]:
"""Return process ids and files memory-mapped beneath one filesystem root."""
try:
resolved_root = volume_root.resolve(strict=True)
process_directories = tuple(proc_root.iterdir())
except OSError:
return ()
mapped_files: set[tuple[int, str]] = set()
for process_directory in process_directories:
if not process_directory.name.isdecimal():
continue
try:
maps_text = (process_directory / "maps").read_text(
encoding="utf-8",
errors="replace",
)
except OSError:
continue
for line in maps_text.splitlines():
fields = line.split(maxsplit=5)
if len(fields) != 6 or not fields[5].startswith("/"):
continue
mapped_path = fields[5].removesuffix(" (deleted)")
try:
Path(mapped_path).relative_to(resolved_root)
except ValueError:
continue
mapped_files.add((int(process_directory.name), mapped_path))
return tuple(sorted(mapped_files))
def _log_compile_cache_memory_maps() -> None:
"""Log native mappings that explain a busy compile-cache Volume reload."""
mapped_files = _mapped_process_files_under(_REMOTE_LLM_COMPILE_CACHE_ROOT)
if not mapped_files:
logger.warning(
"Modal compile-cache Volume reload reported open files, but no "
"memory-mapped cache files were visible in /proc."
)
return
logger.error(
"Modal compile-cache Volume reload is blocked by %d memory-mapped "
"native cache file(s): %s",
len(mapped_files),
[
{"pid": process_id, "path": mapped_path}
for process_id, mapped_path in mapped_files[:8]
],
)
def _prewarm_restored_runtime(compile_cache_volume: Any | None = None) -> None:
"""Run post-restore initialization that should be ready before serving requests."""
with _timed_phase("prewarm_restored_runtime"):
_reload_compile_cache_volume(compile_cache_volume)
_ensure_comfy_runtime_initialized(None)
_load_execution_module()
def _should_reload_modal_volume(payload: dict[str, Any]) -> bool:
"""Return whether this request needs the mounted Modal volume reloaded."""
if _payload_volume_paths(payload) and not _payload_volume_paths_visible(payload):
return True
if not bool(payload.get("requires_volume_reload", True)):
return False
if _payload_uploaded_volume_paths_visible(payload):
reload_marker = _modal_volume_reload_marker(payload)
if reload_marker is not None:
_record_modal_volume_reload_marker(reload_marker)
return False
reload_marker = _modal_volume_reload_marker(payload)
if reload_marker is None:
return True
return not _has_seen_modal_volume_reload_marker(reload_marker)
def _modal_volume_reload_marker(payload: dict[str, Any]) -> str | None:
"""Return the per-request Modal volume reload marker attached to this payload."""
marker = payload.get("volume_reload_marker")
if marker is None:
return None
marker_text = str(marker).strip()
return marker_text or None
def _has_seen_modal_volume_reload_marker(reload_marker: str) -> bool:
"""Return whether this container already reloaded the volume for this marker."""
with _MODAL_VOLUME_RELOAD_MARKERS_LOCK:
return reload_marker in _MODAL_VOLUME_RELOAD_MARKER_SET
def _record_modal_volume_reload_marker(reload_marker: str) -> None:
"""Remember that this container has already reloaded the volume for one marker."""
global _MODAL_VOLUME_RELOAD_MARKERS
with _MODAL_VOLUME_RELOAD_MARKERS_LOCK:
if reload_marker in _MODAL_VOLUME_RELOAD_MARKER_SET:
return
if _MODAL_VOLUME_RELOAD_MARKERS is None:
_MODAL_VOLUME_RELOAD_MARKERS = queue.SimpleQueue()
_MODAL_VOLUME_RELOAD_MARKER_SET.add(reload_marker)
_MODAL_VOLUME_RELOAD_MARKERS.put(reload_marker)
while (
len(_MODAL_VOLUME_RELOAD_MARKER_SET)
> _MODAL_VOLUME_RELOAD_MARKER_CACHE_LIMIT
):
expired_marker = _MODAL_VOLUME_RELOAD_MARKERS.get()
_MODAL_VOLUME_RELOAD_MARKER_SET.discard(expired_marker)
def _clear_warm_remote_caches() -> None:
"""Drop warm-container caches that may retain references to mounted volume files."""
clear_cloud_prompt_execution_warm_caches()
clear_comfy_bootstrap_warm_caches()
def _prepare_for_modal_volume_reload() -> None:
"""Release warm runtime state so a Modal volume reload can proceed safely."""
_clear_warm_remote_caches()
try:
import comfy.model_management as model_management
except ModuleNotFoundError:
gc.collect()
return
model_management.unload_all_models()
model_management.cleanup_models()
model_management.soft_empty_cache(True)
gc.collect()
def _is_modal_volume_open_files_error(exc: RuntimeError) -> bool:
"""Return whether a Modal volume reload failed because mounted files are still open."""
return "open files" in str(exc)
def _sleep_before_modal_volume_reload_retry(delay_seconds: float) -> None:
"""Pause briefly so recently cancelled work can release mounted-volume file handles."""
if delay_seconds <= 0:
return
time.sleep(delay_seconds)
def _iter_payload_input_strings(value: Any) -> Iterator[str]:
"""Yield string literals nested inside one serialized prompt input value."""
if isinstance(value, str):
yield value
return
if isinstance(value, list):
if len(value) == 2 and isinstance(value[0], str):
return
for item in value:
yield from _iter_payload_input_strings(item)
return
if isinstance(value, dict):
for nested_value in value.values():
yield from _iter_payload_input_strings(nested_value)
def _payload_volume_paths(payload: dict[str, Any]) -> set[Path]:
"""Return mounted-volume paths referenced by this remote payload."""
remote_storage_root = Path(get_settings().remote_storage_root).resolve()
referenced_paths: set[Path] = set()
custom_nodes_bundle = payload.get("custom_nodes_bundle")
if isinstance(custom_nodes_bundle, str):
bundle_path = Path(_materialize_remote_asset_path(custom_nodes_bundle))
if bundle_path.is_absolute() and bundle_path.resolve().is_relative_to(
remote_storage_root
):
referenced_paths.add(bundle_path)
prompt = payload.get("subgraph_prompt", {})
if not isinstance(prompt, dict):
return referenced_paths
for prompt_node in prompt.values():
if not isinstance(prompt_node, dict):
continue
inputs = prompt_node.get("inputs", {})
if not isinstance(inputs, dict):
continue
for input_value in inputs.values():
for candidate_path in _iter_payload_input_strings(input_value):
materialized_path = _materialize_remote_asset_path(candidate_path)
materialized_path_obj = Path(materialized_path)
if (
materialized_path_obj.is_absolute()
and materialized_path_obj.resolve().is_relative_to(
remote_storage_root
)
):
referenced_paths.add(materialized_path_obj)
return referenced_paths
def _payload_uploaded_volume_paths(payload: dict[str, Any]) -> set[Path]:
"""Return newly uploaded mounted-volume paths relevant to this payload."""
remote_storage_root = Path(get_settings().remote_storage_root).resolve()
uploaded_paths: set[Path] = set()
for candidate_path in payload.get("uploaded_volume_paths", []):
if isinstance(candidate_path, str) and candidate_path.strip():
materialized_path = Path(_materialize_remote_asset_path(candidate_path))
if (
materialized_path.is_absolute()
and materialized_path.resolve().is_relative_to(remote_storage_root)
):
uploaded_paths.add(materialized_path)
return uploaded_paths
def _payload_uploaded_volume_paths_visible(payload: dict[str, Any]) -> bool:
"""Return whether every newly uploaded mounted-volume path is already visible."""
uploaded_paths = _payload_uploaded_volume_paths(payload)
if not uploaded_paths:
return False
return all(_runtime_volume_path_visible(path) for path in uploaded_paths)
def _runtime_volume_path_visible(volume_path: Path) -> bool:
"""Return whether a mounted path is available directly or through read-through storage."""
if volume_path.exists():
return True
cache_path = _readthrough_cache_path(volume_path)
return cache_path is not None and cache_path.exists()
def _payload_volume_paths_visible(payload: dict[str, Any]) -> bool:
"""Return whether every mounted-volume path referenced by this payload is already visible."""
referenced_paths = _payload_volume_paths(payload)
if not referenced_paths:
return False
return all(_runtime_volume_path_visible(path) for path in referenced_paths)
def _download_committed_volume_path(
volume: Any, volume_path: Path, cache_path: Path
) -> None:
"""Stream one committed Modal Volume file into the worker's ephemeral cache."""
remote_storage_root = Path(get_settings().remote_storage_root).resolve()
relative_path = volume_path.resolve().relative_to(remote_storage_root).as_posix()
read_file_into_fileobj = getattr(volume, "read_file_into_fileobj", None)
read_file = getattr(volume, "read_file", None)
if not callable(read_file_into_fileobj) and not callable(read_file):
raise AttributeError(
"The configured Modal Volume does not support committed file reads."
)
cache_path.parent.mkdir(parents=True, exist_ok=True)
descriptor, temporary_name = tempfile.mkstemp(
prefix=f".{cache_path.name}.",
suffix=".tmp",
dir=cache_path.parent,
)
os.close(descriptor)
temporary_path = Path(temporary_name)
try:
with temporary_path.open("wb") as cache_file:
if callable(read_file_into_fileobj):
read_file_into_fileobj(relative_path, cache_file)
else:
assert callable(read_file)
for chunk in read_file(relative_path):
cache_file.write(chunk)
os.replace(temporary_path, cache_path)
finally:
temporary_path.unlink(missing_ok=True)
def _custom_nodes_manifest_dependency_paths(
volume_path: Path,
runtime_path: Path,
) -> set[Path]:
"""Return mounted-volume dependencies declared by one custom-node manifest."""
remote_storage_root = Path(get_settings().remote_storage_root).resolve()
custom_nodes_root = remote_storage_root / "custom_nodes"
if (
volume_path.suffix.lower() != ".json"
or not volume_path.resolve().is_relative_to(custom_nodes_root)
):
return set()
try:
manifest_payload = _load_custom_nodes_manifest(runtime_path)
except RuntimeError:
return set()
dependency_paths: set[Path] = set()
entry_payloads = manifest_payload.get("entries", [])
if not isinstance(entry_payloads, list):
return dependency_paths
for entry_payload in entry_payloads:
if not isinstance(entry_payload, dict):
continue
candidate_payloads = [entry_payload]
asset_payloads = entry_payload.get("assets", [])
if isinstance(asset_payloads, list):
candidate_payloads.extend(
asset_payload
for asset_payload in asset_payloads
if isinstance(asset_payload, dict)
)
for candidate_payload in candidate_payloads:
remote_path = candidate_payload.get("remote_path")
if not isinstance(remote_path, str) or not remote_path.strip():
continue
materialized_path = Path(_materialize_remote_asset_path(remote_path))
if _readthrough_cache_path(materialized_path) is not None:
dependency_paths.add(materialized_path)
return dependency_paths
def _hydrate_missing_payload_volume_paths(
volume: Any, payload: dict[str, Any]
) -> list[Path]:
"""Cache committed payload files that are absent from this worker's mounted snapshot."""
candidate_paths = _payload_volume_paths(payload) | _payload_uploaded_volume_paths(
payload
)
if not candidate_paths:
return []
if not callable(getattr(volume, "read_file_into_fileobj", None)) and not callable(
getattr(volume, "read_file", None)
):
return []
hydrated_paths: list[Path] = []
pending_paths = sorted(candidate_paths)
visited_paths: set[Path] = set()
component_id = str(payload.get("component_id") or "modal-subgraph")
while pending_paths:
volume_path = pending_paths.pop(0)
if volume_path in visited_paths:
continue
visited_paths.add(volume_path)
runtime_path = Path(_resolve_runtime_asset_path(str(volume_path)))
if not _runtime_volume_path_visible(volume_path):
cache_path = _readthrough_cache_path(volume_path)
if cache_path is None:
continue
try:
with _timed_phase(
"committed_volume_readthrough",
component=component_id,
path=volume_path.name,
):
_download_committed_volume_path(volume, volume_path, cache_path)
except FileNotFoundError:
logger.warning(
"Committed Modal Volume path %s was unavailable for component=%s; falling back to mounted-volume reload.",
volume_path,
component_id,
)
continue
hydrated_paths.append(cache_path)
runtime_path = cache_path
pending_paths.extend(
sorted(
_custom_nodes_manifest_dependency_paths(volume_path, runtime_path)
- visited_paths
)
)
if hydrated_paths:
_emit_cloud_info(
"Hydrated %d missing committed volume file(s) through read-through storage for component=%s.",
len(hydrated_paths),
component_id,
)
return hydrated_paths
def _log_payload_volume_reload_diagnostics(
component_id: str,
payload: dict[str, Any] | None,
*,
context: str,
) -> None:
"""Log the mounted-volume paths relevant to one reload decision or failure."""
if payload is None:
return
uploaded_paths = sorted(
str(path) for path in _payload_uploaded_volume_paths(payload)
)
referenced_paths = sorted(str(path) for path in _payload_volume_paths(payload))
logger.info(
"Modal volume reload diagnostics for component=%s context=%s uploaded_paths=%s referenced_paths=%s visible_uploaded=%s visible_referenced=%s.",
component_id,
context,
uploaded_paths,
referenced_paths,
_payload_uploaded_volume_paths_visible(payload),
_payload_volume_paths_visible(payload),
)
def _reload_modal_volume_for_request(
volume: Any,
component_id: str,
reload_marker: str | None = None,
payload: dict[str, Any] | None = None,
) -> None:
"""Reload the Modal volume, retrying briefly while warm state releases open files."""
with _timed_phase("modal_volume_reload", component=component_id):
diagnostics_logged = False
for attempt_index, retry_delay_seconds in enumerate(
_MODAL_VOLUME_RELOAD_OPEN_FILE_RETRY_DELAYS_SECONDS,
start=1,
):
if attempt_index > 1:
_sleep_before_modal_volume_reload_retry(retry_delay_seconds)
try:
volume.reload()
if reload_marker is not None:
_record_modal_volume_reload_marker(reload_marker)
if attempt_index > 1:
_emit_cloud_info(
"Modal volume reload succeeded for component=%s after %d attempt(s).",
component_id,
attempt_index,
)
return
except RuntimeError as exc:
if not _is_modal_volume_open_files_error(exc):
raise
if payload is not None and not diagnostics_logged:
_log_payload_volume_reload_diagnostics(
component_id,
payload,
context="open_files_retry",
)
diagnostics_logged = True
if attempt_index == len(
_MODAL_VOLUME_RELOAD_OPEN_FILE_RETRY_DELAYS_SECONDS
):
if payload is not None and _payload_volume_paths_visible(payload):
_emit_cloud_info(
"Modal volume reload still reported open files for component=%s after %d attempt(s), "
"but all referenced mounted-volume paths are already visible. Proceeding without reload.",
component_id,
attempt_index,
)
if reload_marker is not None:
_record_modal_volume_reload_marker(reload_marker)
return
raise
_emit_cloud_info(
"Modal volume reload hit open files for component=%s on attempt %d/%d; clearing warm caches and retrying after %.2fs.",
component_id,
attempt_index,
len(_MODAL_VOLUME_RELOAD_OPEN_FILE_RETRY_DELAYS_SECONDS),
_MODAL_VOLUME_RELOAD_OPEN_FILE_RETRY_DELAYS_SECONDS[attempt_index],
)
_prepare_for_modal_volume_reload()
def _emit_modal_volume_reload_skip(component_id: Any, payload: dict[str, Any]) -> None:
"""Log why a request did not need a Modal volume reload."""
if _payload_uploaded_volume_paths_visible(payload):
_emit_cloud_info(
"Skipping modal_volume_reload for component=%s because all uploaded mounted-volume paths are already visible in this container.",
component_id,
)
_log_payload_volume_reload_diagnostics(
str(component_id),
payload,
context="skip_visible_uploaded_paths",
)
return
reload_marker = _modal_volume_reload_marker(payload)
if reload_marker is not None and _has_seen_modal_volume_reload_marker(
reload_marker
):
_emit_cloud_info(
"Skipping modal_volume_reload for component=%s because this container already reloaded marker=%s.",
component_id,
reload_marker,
)
_log_payload_volume_reload_diagnostics(
str(component_id),
payload,
context="skip_reload_marker_seen",
)
return
_emit_cloud_info(
"Skipping modal_volume_reload for component=%s because no new assets were uploaded for this request.",
component_id,
)
_log_payload_volume_reload_diagnostics(
str(component_id),
payload,
context="skip_no_new_assets",
)
def _prepare_warm_container_for_request(
volume: Any,
payload: dict[str, Any],
compile_cache_volume: Any | None = None,
) -> dict[str, Any]:
"""Prime one RemoteEngine container for a request before the first real execution payload arrives."""
component_id = str(payload.get("component_id") or "modal-warmup")
reload_marker = _modal_volume_reload_marker(payload)
_hydrate_missing_payload_volume_paths(volume, payload)
needs_volume_reload = _should_reload_modal_volume(payload)
with _timed_phase("remote_engine_warmup", component=component_id):
if needs_volume_reload:
_reload_modal_volume_for_request(
volume,
component_id,
reload_marker=reload_marker,
payload=payload,
)
else:
_emit_modal_volume_reload_skip(component_id, payload)
custom_nodes_bundle = payload.get("custom_nodes_bundle")
custom_nodes_root: Path | None = None
if isinstance(custom_nodes_bundle, str) and custom_nodes_bundle.strip():
custom_nodes_root = _extract_custom_nodes_bundle(custom_nodes_bundle)
if custom_nodes_root is not None:
_register_custom_nodes_root(custom_nodes_root)
loader_prewarm_plans = payload.get("loader_prewarm_plans")
if isinstance(loader_prewarm_plans, list) and loader_prewarm_plans:
_execute_loader_prewarm_plans(
component_id=component_id,
loader_prewarm_plans=loader_prewarm_plans,
custom_nodes_root=custom_nodes_root,
)
llm_prewarm_plans = payload.get("llm_prewarm_plans")
llm_prewarm_results: list[dict[str, Any]] = []
if isinstance(llm_prewarm_plans, list) and llm_prewarm_plans:
llm_prewarm_results = _execute_llm_prewarm_plans(
component_id=component_id,
prompt_id=(
str(payload["prompt_id"])
if payload.get("prompt_id") is not None
else None
),
llm_prewarm_plans=llm_prewarm_plans,
compile_cache_volume=compile_cache_volume,
)
return {
"component_id": component_id,
"task_id": os.getenv("MODAL_TASK_ID"),
"warmup_slot_index": (
int(payload["warmup_slot_index"])
if payload.get("warmup_slot_index") is not None
else None
),
"reloaded_volume": needs_volume_reload,
"llm_prewarm_results": llm_prewarm_results,
}
def _loader_prewarm_plan_key(plan: Mapping[str, Any]) -> str | None:
"""Return the stable worker-local dedupe key for one loader prewarm plan."""
signature = plan.get("signature")
if signature is None:
return None
normalized_signature = str(signature).strip()
return normalized_signature or None
def _build_loader_prewarm_payload(
*,
component_id: str,
plan_index: int,
plan: Mapping[str, Any],
) -> dict[str, Any]:
"""Build one synthetic single-node subgraph payload for loader warmup."""
plan_node_id = str(plan.get("node_id") or f"loader-{plan_index}")
prompt_id = plan.get("prompt_id")
return {
"payload_kind": "subgraph",
"component_id": f"{component_id}::loader-prewarm:{plan_node_id}",
"prompt_id": (str(prompt_id) if prompt_id is not None else None),
"component_node_ids": [plan_node_id],
"subgraph_prompt": copy.deepcopy(dict(plan["subgraph_prompt"])),
"boundary_inputs": [],
"boundary_outputs": [],
"execute_node_ids": list(plan.get("execute_node_ids") or [plan_node_id]),
"extra_data": {},
}
def _execute_loader_prewarm_plans(
*,
component_id: str,
loader_prewarm_plans: list[dict[str, Any]],
custom_nodes_root: Path | None,
) -> None:
"""Execute synthetic one-node loader workflows so fresh workers preload heavyweight models."""
if not get_settings().enable_loader_prewarm:
return
_ensure_comfy_runtime_initialized(custom_nodes_root)
executable_plans: list[tuple[int, Mapping[str, Any], str | None]] = []
skipped_plan_count = 0
for plan_index, plan in enumerate(loader_prewarm_plans):
if not isinstance(plan, Mapping):
continue
plan_key = _loader_prewarm_plan_key(plan)
if plan_key is not None:
with _LOADER_PREWARM_PLAN_KEYS_LOCK:
if plan_key in _LOADER_PREWARM_PLAN_KEYS:
skipped_plan_count += 1
continue
_LOADER_PREWARM_PLAN_KEYS.add(plan_key)
executable_plans.append((plan_index, plan, plan_key))
def execute_plan(
plan_entry: tuple[int, Mapping[str, Any], str | None]
) -> None:
"""Execute one reserved loader plan and make failures retryable."""
plan_index, plan, plan_key = plan_entry
started_at = time.perf_counter()
try:
_execute_subgraph_prompt(
_build_loader_prewarm_payload(
component_id=component_id,
plan_index=plan_index,
plan=plan,
),
hydrated_inputs={},
custom_nodes_root=custom_nodes_root,
)
except Exception:
if plan_key is not None:
with _LOADER_PREWARM_PLAN_KEYS_LOCK:
_LOADER_PREWARM_PLAN_KEYS.discard(plan_key)
raise
logger.info(
"Completed loader prewarm component=%s class_type=%s plan_index=%d elapsed_seconds=%.3f.",
component_id,
plan.get("class_type"),
plan_index,
time.perf_counter() - started_at,
)
worker_count = min(
len(executable_plans),
max(1, int(get_settings().loader_prewarm_workers)),
)
if worker_count > 1:
logger.info(
"Running %d independent loader prewarms with bounded concurrency=%d component=%s.",
len(executable_plans),
worker_count,
component_id,
)
with ThreadPoolExecutor(
max_workers=worker_count,
thread_name_prefix="modal-loader-prewarm",
) as executor:
futures = [
executor.submit(execute_plan, plan_entry)
for plan_entry in executable_plans
]
for future in futures:
future.result()
else:
for plan_entry in executable_plans:
execute_plan(plan_entry)
executed_plan_count = len(executable_plans)
if executed_plan_count or skipped_plan_count:
logger.info(
"Warm container loader prewarm finished for component=%s executed=%d skipped=%d.",
component_id,
executed_plan_count,
skipped_plan_count,
)
def _llm_prewarm_model_profile(plan: Mapping[str, Any]) -> str:
"""Return the staged model profile from one rewritten LLM warmup plan."""
prompt_node = plan.get("prompt_node")
if isinstance(prompt_node, Mapping):
inputs = prompt_node.get("inputs")
if isinstance(inputs, Mapping):
model_profile = inputs.get("model_profile")
if isinstance(model_profile, str) and model_profile.strip():
return model_profile.strip()
model_profile = plan.get("model_profile")
if not isinstance(model_profile, str) or not model_profile.strip():
raise ValueError("LLM prewarm plan requires a fixed model_profile.")
return model_profile.strip()
def _llm_compile_manifest_path(signature: str) -> Path:
"""Return the content-addressed completion marker for one JIT warmup plan."""
cache_root = Path(
os.getenv("TRITON_CACHE_DIR", str(_REMOTE_LLM_COMPILE_CACHE_ROOT))
).parent
return cache_root / "manifests" / f"{signature}.json"
def _write_llm_compile_manifest(
*,
signature: str,
model_profile: str,
result: Mapping[str, Any],
) -> Path:
"""Atomically publish successful representative-warmup metadata."""
manifest_path = _llm_compile_manifest_path(signature)
manifest_path.parent.mkdir(parents=True, exist_ok=True)
temporary_path = manifest_path.with_suffix(f".{os.getpid()}.tmp")
temporary_path.write_text(
json.dumps(
{
"signature": signature,
"model_profile": model_profile,
"runtime_fingerprint": os.getenv(
"COMFY_MODAL_RUNTIME_FINGERPRINT", ""
),
"completed_at": time.time(),
"result": dict(result),
},
sort_keys=True,
),
encoding="utf-8",
)
temporary_path.replace(manifest_path)
return manifest_path
def _execute_llm_prewarm_plans(
*,
component_id: str,
prompt_id: str | None,
llm_prewarm_plans: list[dict[str, Any]],
compile_cache_volume: Any | None,
) -> list[dict[str, Any]]:
"""Load resident LLMs, exercise representative shapes, and commit JIT caches."""
from modal_llm_runtime import prewarm_modal_llm_profile
results: list[dict[str, Any]] = []
for plan in llm_prewarm_plans:
if not isinstance(plan, Mapping):
continue
plan_signature = str(plan.get("signature") or "").strip()
if not plan_signature:
raise ValueError("LLM prewarm plan requires a stable signature.")
model_profile = _llm_prewarm_model_profile(plan)
signature = hashlib.sha256(
json.dumps(
{
"model_profile": model_profile,
"plan_signature": plan_signature,
"runtime_fingerprint": os.getenv(
"COMFY_MODAL_RUNTIME_FINGERPRINT", ""
),
},
sort_keys=True,
).encode("utf-8")
).hexdigest()
manifest_path = _llm_compile_manifest_path(signature)
representative_request_count = (
1
if manifest_path.exists()
else max(1, int(plan.get("representative_request_count") or 3))
)
with _LLM_PREWARM_PLAN_KEYS_LOCK:
already_resident = signature in _LLM_PREWARM_PLAN_KEYS
_LLM_PREWARM_PLAN_KEYS.add(signature)
if already_resident:
logger.info(
"Skipping duplicate resident LLM prewarm profile=%s component=%s.",
model_profile,
component_id,
)
continue
try:
compile_checkpoint = _LLMCompileMissCheckpoint(
profiles=(model_profile,),
signal_size=_triton_compile_miss_signal_size(),
listener_engine_pids=_triton_compile_listener_engine_pids(),
)
with _timed_phase(
"llm_representative_prewarm",
component=component_id,
profile=model_profile,
requests=representative_request_count,
):
result = prewarm_modal_llm_profile(
model_profile=model_profile,
representative_request_count=representative_request_count,
workflow_execution_id=prompt_id,
)
manifest_path = _write_llm_compile_manifest(
signature=signature,
model_profile=model_profile,
result=result,
)
compile_cache_committed = _commit_actual_llm_compile_cache(
compile_checkpoint,
compile_cache_volume,
)
results.append(
{
**result,
"manifest_path": str(manifest_path),
"manifest_cache_hit": representative_request_count == 1,
"compile_cache_committed": compile_cache_committed,
}
)
except Exception:
with _LLM_PREWARM_PLAN_KEYS_LOCK:
_LLM_PREWARM_PLAN_KEYS.discard(signature)
raise
return results
def _llm_profiles_in_payload(payload: Mapping[str, Any]) -> tuple[str, ...]:
"""Collect ModalLLM profiles in the executable subgraph dependency closure."""
if payload.get("payload_kind") not in {"subgraph", "mapped_subgraph"}:
return ()
subgraph_prompt = payload.get("subgraph_prompt")
execute_node_ids = payload.get("execute_node_ids")
if not isinstance(subgraph_prompt, Mapping) or not isinstance(
execute_node_ids, (list, tuple)
):
return ()
prompt = {str(node_id): node for node_id, node in subgraph_prompt.items()}
profiles: set[str] = set()
visited: set[str] = set()
pending = [str(node_id) for node_id in execute_node_ids]
while pending:
node_id = pending.pop()
if node_id in visited:
continue
visited.add(node_id)
prompt_node = prompt.get(node_id)
if not isinstance(prompt_node, Mapping):
continue
inputs = prompt_node.get("inputs")
if prompt_node.get("class_type") == "ModalLLM" and isinstance(
inputs, Mapping
):
profile = inputs.get("model_profile")
if isinstance(profile, str) and profile.strip():
profiles.add(profile.strip())
if not isinstance(inputs, Mapping):
continue
for input_value in inputs.values():
if _is_link(input_value):
pending.append(str(input_value[0]))
return tuple(sorted(profiles))
@dataclass(frozen=True)
class _LLMCompileMissCheckpoint:
"""Capture the genuine Triton miss signal before one LLM subgraph executes."""
profiles: tuple[str, ...]
signal_size: int
listener_engine_pids: tuple[int, ...]
def _triton_compile_miss_signal_size() -> int:
"""Read the EngineCore compile-miss signal shared through container storage."""
runtime_module = importlib.import_module("modal_llm_runtime")
signal_reader = getattr(runtime_module, "triton_compile_miss_signal_size", None)
if not callable(signal_reader):
raise RuntimeError(
"Modal LLM runtime does not expose triton_compile_miss_signal_size()."
)
return int(signal_reader())
def _triton_compile_listener_engine_pids() -> tuple[int, ...]:
"""Return live EngineCore processes with cache-aware Triton telemetry."""
runtime_module = importlib.import_module("modal_llm_runtime")
listener_reader = getattr(
runtime_module,
"triton_compile_listener_engine_pids",
None,
)
if not callable(listener_reader):
raise RuntimeError(
"Modal LLM runtime does not expose "
"triton_compile_listener_engine_pids()."
)
return tuple(int(pid) for pid in listener_reader())
def _llm_compile_miss_checkpoint(
payload: Mapping[str, Any],
) -> _LLMCompileMissCheckpoint | None:
"""Capture the current miss signal for an executable ModalLLM subgraph."""
profiles = _llm_profiles_in_payload(payload)
if not profiles:
return None
return _LLMCompileMissCheckpoint(
profiles=profiles,
signal_size=_triton_compile_miss_signal_size(),
listener_engine_pids=_triton_compile_listener_engine_pids(),
)
def _commit_actual_llm_compile_cache(
checkpoint: _LLMCompileMissCheckpoint | None,
compile_cache_volume: Any | None,
) -> bool:
"""Commit the compile-cache Volume after a genuine Triton disk-cache miss."""
if checkpoint is None or compile_cache_volume is None:
return False
listener_engine_pids = _triton_compile_listener_engine_pids()
if not listener_engine_pids:
logger.warning(
"Skipping LLM compile-cache commit because no live vLLM EngineCore "
"reported the cache-aware Triton listener profiles=%s "
"listener_pids_before=%s.",
checkpoint.profiles,
checkpoint.listener_engine_pids,
)
return False
signal_size = _triton_compile_miss_signal_size()
if signal_size < checkpoint.signal_size:
raise RuntimeError(
"Triton compile-miss signal shrank during LLM execution: "
f"before={checkpoint.signal_size} after={signal_size}."
)
if signal_size == checkpoint.signal_size:
logger.info(
"Skipping LLM compile-cache commit because every Triton lookup hit "
"the persistent cache profiles=%s signal_size=%d listener_pids=%s.",
checkpoint.profiles,
signal_size,
listener_engine_pids,
)
return False
commit_method = getattr(compile_cache_volume, "commit", None)
if not callable(commit_method):
raise RuntimeError("Modal compile-cache Volume does not expose commit().")
with _timed_phase(
"llm_actual_compile_cache_commit",
profiles=checkpoint.profiles,
miss_signal_bytes=signal_size - checkpoint.signal_size,
):
commit_method()
return True
configure_cloud_session_bridge_hooks(
CloudSessionBridgeHooks(
clone_loader_cache_value=_clone_loader_cache_value,
emit_cloud_info=_emit_cloud_info,
execute_node_locally_raw=_execute_node_locally_raw,
execute_subgraph_prompt=_execute_subgraph_prompt,
is_link=_is_link,
normalize_prompt_input_value=_normalize_prompt_input_value,
resolve_required_subgraph_nodes=_resolve_required_subgraph_nodes,
)
)
configure_cloud_comfy_bootstrap_hooks(
CloudComfyBootstrapHooks(
emit_cloud_info=_emit_cloud_info,
timed_phase=_timed_phase,
remote_subgraph_error=RemoteSubgraphExecutionError,
)
)
configure_cloud_node_output_cache_hooks(
CloudNodeOutputCacheHooks(
emit_cloud_info=_emit_cloud_info,
timed_phase=_timed_phase,
)
)
configure_cloud_prompt_execution_hooks(
CloudPromptExecutionHooks(
emit_cloud_info=_emit_cloud_info,
timed_phase=_timed_phase,
schedule_remote_cancel_restart=_schedule_remote_cancel_restart,
remote_subgraph_error=RemoteSubgraphExecutionError,
)
)
configure_cloud_prompt_server_hooks(
CloudPromptServerHooks(
collapse_cache_slot=_collapse_cache_slot,
emit_cloud_info=_emit_cloud_info,
meaningful_progress_values=_meaningful_progress_values,
)
)
if modal is not None: # pragma: no branch - remote entrypoint configuration.
settings = globals().get("__comfy_modal_settings_override__") or get_settings()
__comfy_modal_gpu__ = settings.modal_gpu
__comfy_modal_app_name__ = modal_deployment_app_name(settings)
__comfy_modal_secret_name__ = str(
getattr(settings, "modal_secret_name", DEFAULT_MODAL_SECRET_NAME)
).strip()
_guard_against_existing_modal_app(settings, modal)
app = modal.App(__comfy_modal_app_name__)
modal_secret = _modal_secret_from_settings(settings, modal)
vol = modal.Volume.from_name(settings.volume_name, create_if_missing=True)
llm_compile_cache_vol = modal.Volume.from_name(
getattr(
settings,
"llm_compile_cache_volume_name",
f"{settings.volume_name}-llm-compile-cache",
),
create_if_missing=True,
)
interrupt_flags = modal.Dict.from_name(
settings.interrupt_dict_name,
create_if_missing=True,
)
node_output_cache = modal.Dict.from_name(
settings.node_output_cache_dict_name,
create_if_missing=True,
)
session_bridge_cache = modal.Dict.from_name(
settings.session_bridge_dict_name,
create_if_missing=True,
)
invocation_records = modal.Dict.from_name(
settings.invocation_dict_name,
create_if_missing=True,
)
snapshot_profiles = modal.Dict.from_name(
settings.snapshot_profile_dict_name,
create_if_missing=True,
)
register_cloud_runtime_stores(
session_bridge_cache=session_bridge_cache,
invocation_records=invocation_records,
volume=vol,
snapshot_profiles=snapshot_profiles,
node_output_cache=node_output_cache,
interrupt_flags=interrupt_flags,
)
custom_node_packages = _custom_node_runtime_packages(settings.custom_nodes_dir)
torch_build = _select_remote_torch_build(settings.modal_gpu)
runtime_identity = build_remote_runtime_identity(
repo_root=_REPO_ROOT,
comfyui_root=settings.comfyui_root,
custom_nodes_dir=settings.custom_nodes_dir,
settings=settings,
)
logger.info(
"Building Modal runtime fingerprint=%s protocol=%d python=%s.",
runtime_identity.fingerprint,
_REMOTE_APP_PROTOCOL_VERSION,
REMOTE_PYTHON_VERSION,
)
logger.info(
"Selected Modal PyTorch build gpu=%s cuda=%s install_layers=%s.",
settings.modal_gpu,
torch_build.cuda_version,
torch_build.install_layers,
)
image = (
modal.Image.debian_slim(python_version=REMOTE_PYTHON_VERSION)
.apt_install(*_comfyui_apt_packages())
.pip_install(*_comfyui_runtime_packages())
)
image = _install_custom_node_packages(image, custom_node_packages)
image = _install_remote_torch_build(image, torch_build)
image = _install_remote_accelerator_packages(image, settings.modal_gpu)
image = image.env(
_modal_image_environment(settings, runtime_identity.fingerprint)
)
image = image.add_local_dir(
_REPO_ROOT,
remote_path="/root/comfyui_modal_sync_repo",
ignore=_should_ignore_repo_path,
)
if settings.comfyui_root is not None and settings.comfyui_root.exists():
image = image.add_local_dir(
settings.comfyui_root,
remote_path=str(_REMOTE_COMFYUI_ROOT),
ignore=_should_ignore_comfyui_path,
)
logger.info(
"Including local ComfyUI checkout %s in Modal image at %s.",
settings.comfyui_root,
_REMOTE_COMFYUI_ROOT,
)
else:
logger.warning(
"No local ComfyUI checkout was discovered; remote Modal execution may fail to import ComfyUI core modules."
)
stager_image = (
modal.Image.debian_slim(python_version=REMOTE_PYTHON_VERSION)
.env(
_model_stager_image_environment(
settings,
runtime_identity.fingerprint,
)
)
.pip_install(*_remote_huggingface_packages())
.run_commands(_remote_huggingface_validation_command())
.add_local_dir(
_REPO_ROOT,
remote_path="/root/comfyui_modal_sync_repo",
ignore=_should_ignore_repo_path,
)
)
@app.cls(
image=stager_image,
volumes={settings.remote_storage_root: vol},
secrets=[modal_secret],
cpu=4.0,
memory=16384,
max_containers=1,
scaledown_window=300,
timeout=7200,
)
@modal.concurrent(max_inputs=1)
class ModelStager:
"""Resolve and stage pinned Hugging Face snapshots without consuming GPU time."""
def _stage_profiles(
self,
model_references: list[str],
resolved_profiles: Mapping[str, Any] | None = None,
progress_callback: Callable[[dict[str, Any]], None] | None = None,
) -> list[dict[str, Any]]:
"""Resolve and stage profiles while optionally publishing progress."""
staged_profiles = resolve_and_stage_model_references(
model_references,
settings.remote_storage_root,
resolved_profiles=resolved_profiles,
owner_id=f"modal:{os.getpid()}:{time.time_ns()}",
progress_callback=(
lambda progress: progress_callback(
{
"stage": progress.stage,
"message": progress.message,
"value": progress.value,
"max": progress.maximum,
"unit": progress.unit,
"indeterminate": progress.indeterminate,
"model_reference": progress.model_reference,
}
)
if progress_callback is not None
else None
),
)
results = [profile.to_dict() for profile in staged_profiles]
if any(
result["downloaded"]
or result["manifest_created"]
for result in results
):
vol.commit()
else:
logger.info(
"Skipping Modal Volume commit because all requested LLM "
"profiles and weights were already durable."
)
logger.info(
"Modal LLM CPU resolution and staging completed for models=%s.",
model_references,
)
return results
@modal.method()
def stage_profiles(
self,
model_references: list[str],
resolved_profiles: Mapping[str, Any] | None = None,
) -> list[dict[str, Any]]:
"""Resolve model references, stage snapshots, and return metadata."""
return self._stage_profiles(model_references, resolved_profiles)
@modal.method()
def stage_profiles_stream(
self,
model_references: list[str],
resolved_profiles: Mapping[str, Any] | None = None,
) -> Iterator[dict[str, Any]]:
"""Stream CPU staging progress and finish with resolved profile data."""
progress_events: queue.Queue[dict[str, Any]] = queue.Queue()
results: list[list[dict[str, Any]]] = []
errors: list[Exception] = []
def run_staging() -> None:
"""Run blocking Hugging Face work while the generator yields events."""
try:
results.append(
self._stage_profiles(
model_references,
resolved_profiles,
progress_events.put,
)
)
except Exception as error:
errors.append(error)
staging_thread = threading.Thread(
target=run_staging,
name="modal-llm-model-stager",
daemon=True,
)
staging_thread.start()
while staging_thread.is_alive() or not progress_events.empty():
try:
progress = progress_events.get(timeout=0.25)
except queue.Empty:
continue
yield {"kind": "progress", **progress}
staging_thread.join()
if errors:
raise errors[0]
yield {"kind": "result", "results": results[0] if results else []}
@modal.method()
def runtime_version(self) -> dict[str, Any]:
"""Return deployment identity without allocating a GPU container."""
return {
"protocol_version": _REMOTE_APP_PROTOCOL_VERSION,
"app_name": __comfy_modal_app_name__,
"runtime_fingerprint": os.environ.get(
"COMFY_MODAL_RUNTIME_FINGERPRINT",
"",
),
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}",
}
@app.cls(
**_remote_engine_cls_options(
settings,
vol,
image,
modal_secret,
llm_compile_cache_vol,
)
)
@modal.concurrent(max_inputs=1)
class RemoteEngine:
"""Modal runtime class that executes proxied ComfyUI payloads."""
snapshot_profile_key: str = modal.parameter(default="")
gpu_snapshot_enabled: bool = modal.parameter(default=False)
worker_affinity_key: str = modal.parameter(default="worker-pool:slot:0")
@modal.enter(snap=True)
def setup_snapshot_state(self) -> None:
"""Prepare snapshot-friendly runtime state before Modal captures memory."""
with _timed_phase("remote_engine_setup_snapshot"):
_prewarm_snapshot_state(
gpu_snapshot_enabled=bool(self.gpu_snapshot_enabled),
snapshot_profile_key=self.snapshot_profile_key,
)
logger.info(
"RemoteEngine snapshot setup complete for snapshot_profile_key=%s gpu_snapshot_enabled=%s worker_affinity=%s.",
self.snapshot_profile_key or None,
bool(self.gpu_snapshot_enabled),
self.worker_affinity_key,
)
@modal.enter(snap=False)
def setup_restored_runtime(self) -> None:
"""Prepare request-serving runtime state after a fresh boot or snapshot restore."""
with _timed_phase("remote_engine_setup_restored"):
_prewarm_restored_runtime(llm_compile_cache_vol)
logger.info(
"RemoteEngine restored-runtime setup complete for snapshot_profile_key=%s.",
self.snapshot_profile_key or None,
)
@modal.method()
def execute_payload(
self, payload: dict[str, Any], kwargs_payload: bytes
) -> bytes:
"""Execute a proxied node or subgraph inside the Modal container."""
_observe_remote_workflow_for_llm_mode(payload)
component_id = payload.get("component_id", "single-node")
reload_marker = _modal_volume_reload_marker(payload)
try:
with _registered_remote_execution(payload) as execution_control:
with _timed_phase(
"remote_engine_execute_payload",
component=component_id,
payload_kind=payload.get("payload_kind"),
):
_hydrate_missing_payload_volume_paths(vol, payload)
if _should_reload_modal_volume(payload):
_reload_modal_volume_for_request(
vol,
str(component_id),
reload_marker=reload_marker,
payload=payload,
)
else:
_emit_modal_volume_reload_skip(component_id, payload)
def execute_once() -> bytes:
"""Execute the underlying payload once inside this request context."""
if payload.get("payload_kind") == "canary":
return _execute_canary_payload(
payload,
kwargs_payload,
cancellation_event=execution_control.cancellation_event,
interrupt_store=interrupt_flags,
interrupt_flag_key=execution_control.interrupt_flag_key,
)
if payload.get("payload_kind") == "mapped_subgraph":
custom_nodes_root = _extract_custom_nodes_bundle(
payload.get("custom_nodes_bundle")
)
_ensure_comfy_runtime_initialized(custom_nodes_root)
hydrated_inputs = deserialize_node_inputs(
kwargs_payload
)
return serialize_node_outputs(
_execute_mapped_subgraph_payload(
payload,
hydrated_inputs,
custom_nodes_root,
cancellation_event=execution_control.cancellation_event,
interrupt_store=interrupt_flags,
interrupt_flag_key=execution_control.interrupt_flag_key,
)
)
if payload.get("payload_kind") == "subgraph":
return execute_subgraph_locally(
payload,
kwargs_payload,
cancellation_event=execution_control.cancellation_event,
interrupt_store=interrupt_flags,
interrupt_flag_key=execution_control.interrupt_flag_key,
)
return execute_node_locally(
payload,
kwargs_payload,
cancellation_event=execution_control.cancellation_event,
interrupt_store=interrupt_flags,
interrupt_flag_key=execution_control.interrupt_flag_key,
)
compile_miss_checkpoint = _llm_compile_miss_checkpoint(payload)
result = _execute_with_durable_invocation(
payload, execute_once
)
_commit_actual_llm_compile_cache(
compile_miss_checkpoint,
llm_compile_cache_vol,
)
return result
except Exception as exc:
_maybe_schedule_container_termination_on_error(payload, exc)
raise
@modal.method()
def warmup_for_request(self, payload: dict[str, Any]) -> dict[str, Any]:
"""Prime the current or a newly started Modal container for one prompt."""
return _prepare_warm_container_for_request(
vol,
payload,
llm_compile_cache_vol,
)
@modal.method()
def keepalive_for_local_gap(self, payload: dict[str, Any]) -> dict[str, Any]:
"""Keep this affinity slot active while the workflow executes locally."""
logger.info(
"Remote local-gap keepalive prompt=%s component=%s worker_affinity=%s.",
payload.get("prompt_id"),
payload.get("component_id"),
self.worker_affinity_key,
)
return {
"component_id": str(payload.get("component_id") or "modal-keepalive"),
"task_id": os.getenv("MODAL_TASK_ID"),
"worker_affinity_key": self.worker_affinity_key,
}
@modal.method()
def runtime_version(self) -> dict[str, Any]:
"""Return the deployed runtime identity expected by the local client."""
return {
"protocol_version": _REMOTE_APP_PROTOCOL_VERSION,
"app_name": __comfy_modal_app_name__,
"runtime_fingerprint": os.environ.get(
"COMFY_MODAL_RUNTIME_FINGERPRINT",
"",
),
"python_version": f"{sys.version_info.major}.{sys.version_info.minor}",
"vllm_version": importlib.metadata.version("vllm"),
}
@modal.method()
def execute_payload_stream(
self,
payload: dict[str, Any],
kwargs_payload: bytes,
) -> Iterator[dict[str, Any]]:
"""Stream progress envelopes and a final serialized result for one payload."""
_observe_remote_workflow_for_llm_mode(payload)
component_id = payload.get("component_id", "single-node")
reload_marker = _modal_volume_reload_marker(payload)
try:
with _registered_remote_execution(payload) as execution_control:
with _timed_phase(
"remote_engine_execute_payload",
component=component_id,
payload_kind=payload.get("payload_kind"),
):
_hydrate_missing_payload_volume_paths(vol, payload)
if _should_reload_modal_volume(payload):
_reload_modal_volume_for_request(
vol,
str(component_id),
reload_marker=reload_marker,
payload=payload,
)
else:
_emit_modal_volume_reload_skip(component_id, payload)
compile_miss_checkpoint = _llm_compile_miss_checkpoint(payload)
yield from _stream_remote_payload_events(
payload,
kwargs_payload,
cancellation_event=execution_control.cancellation_event,
interrupt_store=interrupt_flags,
interrupt_flag_key=execution_control.interrupt_flag_key,
)
_commit_actual_llm_compile_cache(
compile_miss_checkpoint,
llm_compile_cache_vol,
)
except Exception as exc:
_maybe_schedule_container_termination_on_error(payload, exc)
raise
else:
app = None
RemoteEngine = None