Files
wehub-resource-sync cddb07a176
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:06 +08:00

278 lines
10 KiB
Python

import logging
import pytest
from PIL import Image
from invokeai.app.services.config.config_default import InvokeAIAppConfig
from invokeai.app.services.external_generation.errors import (
ExternalProviderCapabilityError,
ExternalProviderNotConfiguredError,
ExternalProviderNotFoundError,
)
from invokeai.app.services.external_generation.external_generation_base import ExternalProvider
from invokeai.app.services.external_generation.external_generation_common import (
ExternalGeneratedImage,
ExternalGenerationRequest,
ExternalGenerationResult,
ExternalReferenceImage,
)
from invokeai.app.services.external_generation.external_generation_default import ExternalGenerationService
from invokeai.backend.model_manager.configs.external_api import (
ExternalApiModelConfig,
ExternalImageSize,
ExternalModelCapabilities,
)
class DummyProvider(ExternalProvider):
def __init__(self, provider_id: str, configured: bool, result: ExternalGenerationResult | None = None) -> None:
super().__init__(InvokeAIAppConfig(), logging.getLogger("test"))
self.provider_id = provider_id
self._configured = configured
self._result = result
self.last_request: ExternalGenerationRequest | None = None
def is_configured(self) -> bool:
return self._configured
def generate(self, request: ExternalGenerationRequest) -> ExternalGenerationResult:
self.last_request = request
assert self._result is not None
return self._result
def _build_model(capabilities: ExternalModelCapabilities) -> ExternalApiModelConfig:
return ExternalApiModelConfig(
key="external_test",
name="External Test",
provider_id="openai",
provider_model_id="gpt-image-1",
capabilities=capabilities,
)
def _build_request(
*,
model: ExternalApiModelConfig,
mode: str = "txt2img",
seed: int | None = None,
num_images: int = 1,
width: int = 64,
height: int = 64,
init_image: Image.Image | None = None,
mask_image: Image.Image | None = None,
reference_images: list[ExternalReferenceImage] | None = None,
) -> ExternalGenerationRequest:
return ExternalGenerationRequest(
model=model,
mode=mode, # type: ignore[arg-type]
prompt="A test prompt",
seed=seed,
num_images=num_images,
width=width,
height=height,
image_size=None,
init_image=init_image,
mask_image=mask_image,
reference_images=reference_images or [],
metadata=None,
)
def _make_image() -> Image.Image:
return Image.new("RGB", (64, 64), color="black")
def test_generate_requires_registered_provider() -> None:
model = _build_model(ExternalModelCapabilities(modes=["txt2img"]))
request = _build_request(model=model)
service = ExternalGenerationService({}, logging.getLogger("test"))
with pytest.raises(ExternalProviderNotFoundError):
service.generate(request)
def test_generate_requires_configured_provider() -> None:
model = _build_model(ExternalModelCapabilities(modes=["txt2img"]))
request = _build_request(model=model)
provider = DummyProvider("openai", configured=False)
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
with pytest.raises(ExternalProviderNotConfiguredError):
service.generate(request)
def test_generate_validates_mode_support() -> None:
model = _build_model(ExternalModelCapabilities(modes=["txt2img"]))
request = _build_request(model=model, mode="img2img", init_image=_make_image())
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
with pytest.raises(ExternalProviderCapabilityError, match="Mode 'img2img'"):
service.generate(request)
def test_generate_requires_init_image_for_img2img() -> None:
model = _build_model(ExternalModelCapabilities(modes=["img2img"]))
request = _build_request(model=model, mode="img2img")
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
with pytest.raises(ExternalProviderCapabilityError, match="requires an init image"):
service.generate(request)
def test_generate_requires_mask_for_inpaint() -> None:
model = _build_model(ExternalModelCapabilities(modes=["inpaint"]))
request = _build_request(model=model, mode="inpaint", init_image=_make_image())
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
with pytest.raises(ExternalProviderCapabilityError, match="requires a mask"):
service.generate(request)
def test_generate_validates_reference_images() -> None:
model = _build_model(ExternalModelCapabilities(modes=["txt2img"], supports_reference_images=False))
request = _build_request(
model=model,
reference_images=[ExternalReferenceImage(image=_make_image())],
)
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
with pytest.raises(ExternalProviderCapabilityError, match="Reference images"):
service.generate(request)
def test_generate_validates_limits() -> None:
model = _build_model(
ExternalModelCapabilities(
modes=["txt2img"],
supports_reference_images=True,
max_reference_images=1,
max_images_per_request=1,
)
)
request = _build_request(
model=model,
num_images=2,
reference_images=[
ExternalReferenceImage(image=_make_image()),
ExternalReferenceImage(image=_make_image()),
],
)
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
with pytest.raises(ExternalProviderCapabilityError, match="supports at most"):
service.generate(request)
def test_generate_validates_allowed_aspect_ratios() -> None:
model = _build_model(
ExternalModelCapabilities(
modes=["txt2img"],
allowed_aspect_ratios=["1:1", "16:9"],
aspect_ratio_sizes={
"1:1": ExternalImageSize(width=1024, height=1024),
"16:9": ExternalImageSize(width=1344, height=768),
},
)
)
request = _build_request(model=model)
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
response = service.generate(request)
assert response.images == []
assert provider.last_request is not None
assert provider.last_request.width == 1024
assert provider.last_request.height == 1024
def test_generate_validates_allowed_aspect_ratios_with_bucket_sizes() -> None:
model = _build_model(
ExternalModelCapabilities(
modes=["txt2img"],
allowed_aspect_ratios=["1:1", "16:9"],
aspect_ratio_sizes={
"1:1": ExternalImageSize(width=1024, height=1024),
"16:9": ExternalImageSize(width=1344, height=768),
},
)
)
request = _build_request(model=model, width=160, height=90)
provider = DummyProvider("openai", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
response = service.generate(request)
assert response.images == []
assert provider.last_request is not None
assert provider.last_request.width == 1344
assert provider.last_request.height == 768
def test_generate_happy_path() -> None:
model = _build_model(ExternalModelCapabilities(modes=["txt2img"], supports_seed=True))
request = _build_request(model=model, seed=42)
result = ExternalGenerationResult(images=[ExternalGeneratedImage(image=_make_image(), seed=42)])
provider = DummyProvider("openai", configured=True, result=result)
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
response = service.generate(request)
assert response is result
assert provider.last_request == request
def test_generate_resizes_inpaint_result_to_original_init_size() -> None:
model = _build_model(ExternalModelCapabilities(modes=["inpaint"]))
request = _build_request(
model=model,
mode="inpaint",
width=128,
height=128,
init_image=_make_image(),
mask_image=_make_image(),
)
generated_large = Image.new("RGB", (128, 128), color="black")
result = ExternalGenerationResult(images=[ExternalGeneratedImage(image=generated_large, seed=1)])
provider = DummyProvider("openai", configured=True, result=result)
service = ExternalGenerationService({"openai": provider}, logging.getLogger("test"))
response = service.generate(request)
assert request.init_image is not None
assert response.images[0].image.width == request.init_image.width
assert response.images[0].image.height == request.init_image.height
assert response.images[0].seed == 1
def test_qwen_image_edit_max_enforces_three_reference_images() -> None:
from invokeai.backend.model_manager.starter_models import alibabacloud_qwen_image_edit_max
capabilities = alibabacloud_qwen_image_edit_max.capabilities
assert capabilities is not None
assert capabilities.max_reference_images == 3
model = ExternalApiModelConfig(
key="qwen_image_edit_max",
name="Qwen Image Edit Max",
provider_id="alibabacloud",
provider_model_id="qwen-image-edit-max",
capabilities=capabilities,
)
request = _build_request(
model=model,
reference_images=[ExternalReferenceImage(image=_make_image()) for _ in range(4)],
)
provider = DummyProvider("alibabacloud", configured=True, result=ExternalGenerationResult(images=[]))
service = ExternalGenerationService({"alibabacloud": provider}, logging.getLogger("test"))
with pytest.raises(ExternalProviderCapabilityError, match="supports at most 3 reference images"):
service.generate(request)
assert provider.last_request is None