mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-06 17:38:35 +00:00
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:
parent
8fde956c64
commit
08fe9f7b3a
@ -808,7 +808,12 @@ class GeminiImageGenerationClient(ImageGenerationProvider):
|
|||||||
generation_config: dict[str, Any] = {"responseModalities": ["TEXT", "IMAGE"]}
|
generation_config: dict[str, Any] = {"responseModalities": ["TEXT", "IMAGE"]}
|
||||||
image_config = _gemini_flash_image_config(model, aspect_ratio, image_size)
|
image_config = _gemini_flash_image_config(model, aspect_ratio, image_size)
|
||||||
if image_config:
|
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] = {
|
body: dict[str, Any] = {
|
||||||
"contents": [{"role": "user", "parts": parts}],
|
"contents": [{"role": "user", "parts": parts}],
|
||||||
@ -864,11 +869,13 @@ def _gemini_flash_image_config(
|
|||||||
aspect_ratio: str | None,
|
aspect_ratio: str | None,
|
||||||
image_size: str | None,
|
image_size: str | None,
|
||||||
) -> dict[str, str]:
|
) -> 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
|
Values are the documented plain strings (e.g. ``16:9``, ``1K``) that the
|
||||||
additional extreme ratios, while configurable image sizes are limited to
|
live v1beta API accepts under ``imageConfig``. Capabilities are
|
||||||
the documented Gemini 3 image model families.
|
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] = {}
|
config: dict[str, str] = {}
|
||||||
if aspect_ratio and aspect_ratio in _gemini_flash_supported_aspect_ratios(model):
|
if aspect_ratio and aspect_ratio in _gemini_flash_supported_aspect_ratios(model):
|
||||||
|
|||||||
@ -464,7 +464,7 @@ async def test_gemini_flash_forwards_aspect_ratio_and_image_size() -> None:
|
|||||||
image_size="2K",
|
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"}
|
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_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"}
|
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_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"}
|
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,
|
aspect_ratio=aspect_ratio,
|
||||||
)
|
)
|
||||||
|
|
||||||
response_format = fake.calls[0]["json"]["generationConfig"].get("responseFormat")
|
image_config = fake.calls[0]["json"]["generationConfig"].get("imageConfig")
|
||||||
assert response_format == ({"image": expected} if expected else None)
|
assert image_config == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@ -553,8 +553,8 @@ async def test_gemini_flash_scopes_image_size_by_model(
|
|||||||
image_size=image_size,
|
image_size=image_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
response_format = fake.calls[0]["json"]["generationConfig"].get("responseFormat")
|
image_config = fake.calls[0]["json"]["generationConfig"].get("imageConfig")
|
||||||
assert response_format == ({"image": expected} if expected else None)
|
assert image_config == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
@ -571,7 +571,7 @@ async def test_gemini_flash_ignores_unsupported_hints() -> None:
|
|||||||
image_size="1024x1024",
|
image_size="1024x1024",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert "responseFormat" not in fake.calls[0]["json"]["generationConfig"]
|
assert "imageConfig" not in fake.calls[0]["json"]["generationConfig"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user