Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions comfy/comfy_api_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Runtime config the frontend reads from /features to follow --comfy-api-base.

For a non-prod comfy.org backend (staging or an ephemeral preview env), "/features" exposes the api and
platform base so the frontend talks to it without a rebuild; the frontend picks the Firebase project from the api base.
Prod bases are left alone and keep their build-time defaults.
"""

from typing import Any
from urllib.parse import urlparse

from comfy.cli_args import args

# Staging and the ephemeral preview envs ("testenvs") are one tier: same dev Firebase project and platform.
_STAGING_API_HOST = "stagingapi.comfy.org"
_TESTENV_HOST_SUFFIX = ".testenvs.comfy.org"
_STAGING_PLATFORM_BASE_URL = "https://stagingplatform.comfy.org"


def _is_staging_tier(host: str) -> bool:
return host == _STAGING_API_HOST or host.endswith(_TESTENV_HOST_SUFFIX)


def normalize_comfy_api_base(url: str) -> str:
"""Rewrite a testenv's friendly main host to its comfy-api '-registry' sibling."""
parsed = urlparse(url)
host = parsed.hostname or ""
if not host.endswith(_TESTENV_HOST_SUFFIX):
return url
label = host[: -len(_TESTENV_HOST_SUFFIX)]
if label.endswith("-registry"):
return url
return f"{parsed.scheme or 'https'}://{label}-registry{_TESTENV_HOST_SUFFIX}"
Comment thread
coderabbitai[bot] marked this conversation as resolved.


def frontend_config_for_base(base_url: str) -> dict[str, Any] | None:
"""The /features overrides for a staging-tier base, or None for prod."""
if not _is_staging_tier(urlparse(base_url).hostname or ""):
return None
return {
"comfy_api_base_url": normalize_comfy_api_base(base_url).rstrip("/"),
"comfy_platform_base_url": _STAGING_PLATFORM_BASE_URL,
}


def get_frontend_config() -> dict[str, Any] | None:
return frontend_config_for_base(getattr(args, "comfy_api_base", "") or "")
10 changes: 9 additions & 1 deletion comfy_api/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import Any, TypedDict

from comfy.cli_args import args
from comfy.comfy_api_env import get_frontend_config


class FeatureFlagInfo(TypedDict):
Expand Down Expand Up @@ -162,4 +163,11 @@ def get_server_features() -> dict[str, Any]:
Returns:
Dictionary of server feature flags
"""
return SERVER_FEATURE_FLAGS.copy()
features = SERVER_FEATURE_FLAGS.copy()
# When --comfy-api-base targets a staging-tier comfy.org backend (the staging api host or an ephemeral testenv),
# surface the api + platform base so the frontend can reach it without a rebuild
# (it derives the Firebase project from the api base). Prod / self-hosted bases keep build-time defaults.
overrides = get_frontend_config()
if overrides:
features.update(overrides)
return features
3 changes: 2 additions & 1 deletion comfy_api_nodes/util/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from yarl import URL

from comfy.cli_args import args
from comfy.comfy_api_env import normalize_comfy_api_base
from comfy.deploy_environment import get_deploy_environment
from comfy.model_management import processing_interrupted
from comfy_api.latest import IO
Expand Down Expand Up @@ -63,7 +64,7 @@ def get_comfy_api_headers(node_cls: type[IO.ComfyNode]) -> dict[str, str]:


def default_base_url() -> str:
return getattr(args, "comfy_api_base", "https://api.comfy.org")
return normalize_comfy_api_base(getattr(args, "comfy_api_base", "https://api.comfy.org"))


async def sleep_with_interrupt(
Expand Down
51 changes: 51 additions & 0 deletions tests-unit/feature_flags_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
_coerce_flag_value,
_parse_cli_feature_flags,
)
from comfy.comfy_api_env import (
frontend_config_for_base,
normalize_comfy_api_base,
)


class TestFeatureFlags:
Expand Down Expand Up @@ -181,3 +185,50 @@ def test_registry_entries_have_required_fields(self):
assert "type" in info, f"{key} missing 'type'"
assert "default" in info, f"{key} missing 'default'"
assert "description" in info, f"{key} missing 'description'"


class TestComfyApiEnv:
"""--comfy-api-base staging-tier detection + testenv main-host -> -registry rewrite."""

@pytest.mark.parametrize(
"url, expected",
[
# testenv friendly main host -> comfy-api -registry sibling (slash trimmed)
("https://pr-4398.testenvs.comfy.org", "https://pr-4398-registry.testenvs.comfy.org"),
("https://pr-4398.testenvs.comfy.org/", "https://pr-4398-registry.testenvs.comfy.org"),
("https://pr-4398-registry.testenvs.comfy.org", "https://pr-4398-registry.testenvs.comfy.org"),
# staging + everything else -> unchanged (no -registry split)
("https://stagingapi.comfy.org", "https://stagingapi.comfy.org"),
("https://api.comfy.org", "https://api.comfy.org"),
("https://pr-1.testenvs.comfy.org.evil.com", "https://pr-1.testenvs.comfy.org.evil.com"),
("", ""),
],
)
def test_normalize_comfy_api_base(self, url, expected):
assert normalize_comfy_api_base(url) == expected

def test_config_for_staging_tier_else_none(self):
# ephemeral testenv: friendly main host -> -registry, staging platform
eph = frontend_config_for_base("https://pr-1234.testenvs.comfy.org/")
assert eph["comfy_api_base_url"] == "https://pr-1234-registry.testenvs.comfy.org"
assert eph["comfy_platform_base_url"] == "https://stagingplatform.comfy.org"
# staging api host: emitted as-is
stg = frontend_config_for_base("https://stagingapi.comfy.org")
assert stg["comfy_api_base_url"] == "https://stagingapi.comfy.org"
assert stg["comfy_platform_base_url"] == "https://stagingplatform.comfy.org"
# prod / unknown: nothing
assert frontend_config_for_base("https://api.comfy.org") is None

def test_server_features_merge_only_for_staging_tier(self, monkeypatch):
def set_base(url):
monkeypatch.setattr(
"comfy.comfy_api_env.args",
type("Args", (), {"comfy_api_base": url})(),
)

set_base("https://stagingapi.comfy.org")
assert "comfy_api_base_url" in get_server_features()
set_base("https://pr-7.testenvs.comfy.org")
assert "comfy_api_base_url" in get_server_features()
set_base("https://api.comfy.org")
assert "comfy_api_base_url" not in get_server_features()
Loading