fix(image): send Gemini Flash hints via generationConfig.imageConfig

The live v1beta API rejects the legacy responseFormat.image block
(enum-based aspectRatio/imageSize fields) for gemini-3.1-flash-lite-image
with INVALID_ARGUMENT, even for documented plain-string values. Gemini
Flash image models accept plain-string hints under
generationConfig.imageConfig instead (e.g. aspectRatio 16:9, imageSize
1K), which the API accepts. Switch the flash path to imageConfig and
update the provider tests accordingly. Other providers (aihubmix,
ollama, imagen) are untouched.
This commit is contained in:
arcdrake22 2026-08-02 22:25:59 +02:00 committed by chengyongru
parent 8fde956c64
commit 08fe9f7b3a
2 changed files with 20 additions and 13 deletions

View File

@ -808,7 +808,12 @@ class GeminiImageGenerationClient(ImageGenerationProvider):
generation_config: dict[str, Any] = {"responseModalities": ["TEXT", "IMAGE"]}
image_config = _gemini_flash_image_config(model, aspect_ratio, image_size)
if image_config:
generation_config["responseFormat"] = {"image": image_config}
# Gemini Flash image models accept plain-string values under
# ``generationConfig.imageConfig``. The legacy
# ``responseFormat.image`` block is rejected with INVALID_ARGUMENT
# by gemini-3.1-flash-lite-image (enum-based fields), so it is not
# used here.
generation_config["imageConfig"] = image_config
body: dict[str, Any] = {
"contents": [{"role": "user", "parts": parts}],
@ -864,11 +869,13 @@ def _gemini_flash_image_config(
aspect_ratio: str | None,
image_size: str | None,
) -> dict[str, str]:
"""Build the ``responseFormat.image`` config for Gemini Flash image models.
"""Build the ``generationConfig.imageConfig`` config for Gemini Flash image models.
Capabilities are model-specific: Gemini 3.1 Flash variants support four
additional extreme ratios, while configurable image sizes are limited to
the documented Gemini 3 image model families.
Values are the documented plain strings (e.g. ``16:9``, ``1K``) that the
live v1beta API accepts under ``imageConfig``. Capabilities are
model-specific: Gemini 3.1 Flash variants support four additional extreme
ratios, while configurable image sizes are limited to the documented
Gemini 3 image model families.
"""
config: dict[str, str] = {}
if aspect_ratio and aspect_ratio in _gemini_flash_supported_aspect_ratios(model):

View File

@ -464,7 +464,7 @@ async def test_gemini_flash_forwards_aspect_ratio_and_image_size() -> None:
image_size="2K",
)
image_config = fake.calls[0]["json"]["generationConfig"]["responseFormat"]["image"]
image_config = fake.calls[0]["json"]["generationConfig"]["imageConfig"]
assert image_config == {"aspectRatio": "16:9", "imageSize": "2K"}
@ -480,7 +480,7 @@ async def test_gemini_flash_2_5_drops_image_size() -> None:
image_size="1K",
)
image_config = fake.calls[0]["json"]["generationConfig"]["responseFormat"]["image"]
image_config = fake.calls[0]["json"]["generationConfig"]["imageConfig"]
assert image_config == {"aspectRatio": "4:3"}
@ -496,7 +496,7 @@ async def test_gemini_flash_2_0_drops_image_size() -> None:
image_size="1K",
)
image_config = fake.calls[0]["json"]["generationConfig"]["responseFormat"]["image"]
image_config = fake.calls[0]["json"]["generationConfig"]["imageConfig"]
assert image_config == {"aspectRatio": "16:9"}
@ -524,8 +524,8 @@ async def test_gemini_flash_scopes_extreme_aspect_ratios_by_model(
aspect_ratio=aspect_ratio,
)
response_format = fake.calls[0]["json"]["generationConfig"].get("responseFormat")
assert response_format == ({"image": expected} if expected else None)
image_config = fake.calls[0]["json"]["generationConfig"].get("imageConfig")
assert image_config == expected
@pytest.mark.parametrize(
@ -553,8 +553,8 @@ async def test_gemini_flash_scopes_image_size_by_model(
image_size=image_size,
)
response_format = fake.calls[0]["json"]["generationConfig"].get("responseFormat")
assert response_format == ({"image": expected} if expected else None)
image_config = fake.calls[0]["json"]["generationConfig"].get("imageConfig")
assert image_config == expected
@pytest.mark.asyncio
@ -571,7 +571,7 @@ async def test_gemini_flash_ignores_unsupported_hints() -> None:
image_size="1024x1024",
)
assert "responseFormat" not in fake.calls[0]["json"]["generationConfig"]
assert "imageConfig" not in fake.calls[0]["json"]["generationConfig"]
@pytest.mark.asyncio