BYOK OpenAI gpt-image generation and editing nodes for ComfyUI — calls go directly to the OpenAI API with your own key (no proxy, no credit markup). - Two nodes: "Imagent: OpenAI Image" (text-to-image) and "Imagent: OpenAI Image Edit" (edit / inpaint / multi-reference compose). - Models gpt-image-2 / gpt-image-1.5 / gpt-image-1 with a per-model dynamic UI (comfy_api DynamicCombo): size, background, custom WxH, and input_fidelity are revealed only for the models that support them. - Edit node accepts an auto-growing set of reference images (up to 16) plus an optional inpaint mask; generate supports batches up to 8. - API key via OPENAI_API_KEY or a local config.json. Requires ComfyUI >= 0.23.0.
73 lines
2.7 KiB
Python
73 lines
2.7 KiB
Python
import json
|
|
import imagent.client as client
|
|
|
|
|
|
def test_env_var_takes_precedence(monkeypatch, tmp_path):
|
|
monkeypatch.setenv("OPENAI_API_KEY", "sk-from-env")
|
|
monkeypatch.setattr(client, "_CONFIG_PATH", tmp_path / "config.json")
|
|
(tmp_path / "config.json").write_text(json.dumps({"OPENAI_API_KEY": "sk-from-file"}))
|
|
assert client.resolve_api_key() == "sk-from-env"
|
|
|
|
|
|
def test_config_file_fallback(monkeypatch, tmp_path):
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
cfg = tmp_path / "config.json"
|
|
cfg.write_text(json.dumps({"OPENAI_API_KEY": "sk-from-file"}))
|
|
monkeypatch.setattr(client, "_CONFIG_PATH", cfg)
|
|
assert client.resolve_api_key() == "sk-from-file"
|
|
|
|
|
|
def test_missing_key_returns_none(monkeypatch, tmp_path):
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
monkeypatch.setattr(client, "_CONFIG_PATH", tmp_path / "nope.json")
|
|
assert client.resolve_api_key() is None
|
|
|
|
|
|
def test_placeholder_key_is_ignored(monkeypatch, tmp_path):
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
cfg = tmp_path / "config.json"
|
|
cfg.write_text(json.dumps({"OPENAI_API_KEY": "sk-..."}))
|
|
monkeypatch.setattr(client, "_CONFIG_PATH", cfg)
|
|
assert client.resolve_api_key() is None
|
|
|
|
|
|
def test_model_catalog_defaults():
|
|
assert client.DEFAULT_MODEL == "gpt-image-2"
|
|
assert set(client.MODELS) == {"gpt-image-1", "gpt-image-1.5", "gpt-image-2"}
|
|
assert "gpt-image-1-mini" not in client.MODELS
|
|
assert "gpt-image-2-2026-04-21" not in client.MODELS
|
|
assert all("dall-e" not in m for m in client.MODELS)
|
|
|
|
|
|
def test_friendly_error_maps_403_and_429():
|
|
assert "verif" in client.friendly_error(Exception("Error code: 403 - org not verified")).lower()
|
|
assert "rate" in client.friendly_error(Exception("Error code: 429 - too many requests")).lower()
|
|
|
|
|
|
def test_malformed_config_returns_none(monkeypatch, tmp_path, caplog):
|
|
import logging
|
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
cfg = tmp_path / "config.json"
|
|
cfg.write_text("{bad json")
|
|
monkeypatch.setattr(client, "_CONFIG_PATH", cfg)
|
|
with caplog.at_level(logging.WARNING, logger="imagent"):
|
|
assert client.resolve_api_key() is None
|
|
assert "could not read" in caplog.text
|
|
|
|
|
|
def test_env_placeholder_is_ignored(monkeypatch, tmp_path):
|
|
monkeypatch.setenv("OPENAI_API_KEY", "your_openai_api_key_here")
|
|
monkeypatch.setattr(client, "_CONFIG_PATH", tmp_path / "nope.json")
|
|
assert client.resolve_api_key() is None
|
|
|
|
|
|
def test_friendly_error_uses_status_code():
|
|
class FakeAPIError(Exception):
|
|
status_code = 403
|
|
|
|
class FakeRateError(Exception):
|
|
status_code = 429
|
|
|
|
assert "verif" in client.friendly_error(FakeAPIError("denied")).lower()
|
|
assert "rate" in client.friendly_error(FakeRateError("slow down")).lower()
|