mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-05-06 07:12:30 +08:00
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
Python Linting / Run Pylint (push) Has been cancelled
Build package / Build Test (3.10) (push) Has been cancelled
Build package / Build Test (3.11) (push) Has been cancelled
Build package / Build Test (3.12) (push) Has been cancelled
Build package / Build Test (3.13) (push) Has been cancelled
Build package / Build Test (3.14) (push) Has been cancelled
Address code review feedback: - _coerce_flag_value: wrap coercion in try/except (ValueError, TypeError) and log a warning instead of crashing startup on malformed values. - _parse_cli_feature_flags: bare --feature-flag KEY (no '=') now defaults to 'true' so registered bool flags work as toggles. - Remove the get_cli_feature_flag_registry() wrapper; export and use CLI_FEATURE_FLAG_REGISTRY directly in main.py and tests. Add tests for coercion-failure fallback and bare-flag default behavior. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019deba2-bfe2-7118-913c-562beee48972
143 lines
3.9 KiB
Python
143 lines
3.9 KiB
Python
"""
|
|
Feature flags module for ComfyUI WebSocket protocol negotiation.
|
|
|
|
This module handles capability negotiation between frontend and backend,
|
|
allowing graceful protocol evolution while maintaining backward compatibility.
|
|
"""
|
|
|
|
import logging
|
|
from typing import Any, TypedDict
|
|
|
|
from comfy.cli_args import args
|
|
|
|
|
|
class FeatureFlagInfo(TypedDict):
|
|
type: str
|
|
default: Any
|
|
description: str
|
|
|
|
|
|
# Registry of known CLI-settable feature flags.
|
|
# Launchers can query this via --list-feature-flags to discover valid flags.
|
|
CLI_FEATURE_FLAG_REGISTRY: dict[str, FeatureFlagInfo] = {
|
|
"show_signin_button": {
|
|
"type": "bool",
|
|
"default": False,
|
|
"description": "Show the sign-in button in the frontend even when not signed in",
|
|
},
|
|
}
|
|
|
|
|
|
_COERCE_FNS: dict[str, Any] = {
|
|
"bool": lambda v: v.lower() == "true",
|
|
"int": lambda v: int(v),
|
|
"float": lambda v: float(v),
|
|
}
|
|
|
|
|
|
def _coerce_flag_value(key: str, raw_value: str) -> Any:
|
|
"""Coerce a raw string value using the registry type, or keep as string.
|
|
|
|
Returns the raw string if the key is unregistered, the type is unknown,
|
|
or coercion fails (with a warning logged in the failure case).
|
|
"""
|
|
info = CLI_FEATURE_FLAG_REGISTRY.get(key)
|
|
if info is None:
|
|
return raw_value
|
|
coerce = _COERCE_FNS.get(info["type"])
|
|
if coerce is None:
|
|
return raw_value
|
|
try:
|
|
return coerce(raw_value)
|
|
except (ValueError, TypeError):
|
|
logging.warning(
|
|
"Could not coerce --feature-flag %s=%r to %s; using raw string.",
|
|
key, raw_value, info["type"],
|
|
)
|
|
return raw_value
|
|
|
|
|
|
def _parse_cli_feature_flags() -> dict[str, Any]:
|
|
"""Parse --feature-flag key=value pairs from CLI args into a dict.
|
|
|
|
Items without '=' default to the value 'true' (bare flag form).
|
|
"""
|
|
result: dict[str, Any] = {}
|
|
for item in getattr(args, "feature_flag", []):
|
|
key, sep, raw_value = item.partition("=")
|
|
key = key.strip()
|
|
if not key:
|
|
continue
|
|
if not sep:
|
|
raw_value = "true"
|
|
result[key] = _coerce_flag_value(key, raw_value.strip())
|
|
return result
|
|
|
|
|
|
# Default server capabilities
|
|
_CORE_FEATURE_FLAGS: dict[str, Any] = {
|
|
"supports_preview_metadata": True,
|
|
"max_upload_size": args.max_upload_size * 1024 * 1024, # Convert MB to bytes
|
|
"extension": {"manager": {"supports_v4": True}},
|
|
"node_replacements": True,
|
|
"assets": args.enable_assets,
|
|
}
|
|
|
|
# CLI-provided flags cannot overwrite core flags
|
|
_cli_flags = {k: v for k, v in _parse_cli_feature_flags().items() if k not in _CORE_FEATURE_FLAGS}
|
|
|
|
SERVER_FEATURE_FLAGS: dict[str, Any] = {**_CORE_FEATURE_FLAGS, **_cli_flags}
|
|
|
|
|
|
def get_connection_feature(
|
|
sockets_metadata: dict[str, dict[str, Any]],
|
|
sid: str,
|
|
feature_name: str,
|
|
default: Any = False
|
|
) -> Any:
|
|
"""
|
|
Get a feature flag value for a specific connection.
|
|
|
|
Args:
|
|
sockets_metadata: Dictionary of socket metadata
|
|
sid: Session ID of the connection
|
|
feature_name: Name of the feature to check
|
|
default: Default value if feature not found
|
|
|
|
Returns:
|
|
Feature value or default if not found
|
|
"""
|
|
if sid not in sockets_metadata:
|
|
return default
|
|
|
|
return sockets_metadata[sid].get("feature_flags", {}).get(feature_name, default)
|
|
|
|
|
|
def supports_feature(
|
|
sockets_metadata: dict[str, dict[str, Any]],
|
|
sid: str,
|
|
feature_name: str
|
|
) -> bool:
|
|
"""
|
|
Check if a connection supports a specific feature.
|
|
|
|
Args:
|
|
sockets_metadata: Dictionary of socket metadata
|
|
sid: Session ID of the connection
|
|
feature_name: Name of the feature to check
|
|
|
|
Returns:
|
|
Boolean indicating if feature is supported
|
|
"""
|
|
return get_connection_feature(sockets_metadata, sid, feature_name, False) is True
|
|
|
|
|
|
def get_server_features() -> dict[str, Any]:
|
|
"""
|
|
Get the server's feature flags.
|
|
|
|
Returns:
|
|
Dictionary of server feature flags
|
|
"""
|
|
return SERVER_FEATURE_FLAGS.copy()
|