fix(image): narrow Gemini Flash aspect-ratio and image-size scoping

Address review feedback that the capability checks were broader than the
documented per-model matrix:

- Drop the extreme aspect ratios (1:4, 4:1, 1:8, 8:1) from the Flash
  allow-list. They are only documented for 3.1 Flash / Flash Lite, so the
  global set could send an unsupported ratio to 2.5 Flash Image or 3.1 Pro
  Image. Keep the ratios common to every Flash image model.
- Identify imageSize support positively via "gemini-3" instead of excluding
  "2.5". The old predicate also matched gemini-2.0-flash-preview-image-
  generation, which (with the default 1K size) altered that model's request
  shape even though only Gemini 3+ image models accept a configurable size.

Add tests for the gemini-2.0 image-size drop and the extreme-ratio drop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
stupidloud
2026-07-27 03:07:41 +08:00
committed by Xubin Ren
co-authored by Claude Opus 4.8
parent 4986590bd7
commit ef445cc246
2 changed files with 34 additions and 8 deletions
+14 -6
View File
@@ -33,12 +33,15 @@ _AIHUBMIX_ASPECT_RATIO_SIZES = {
}
_GEMINI_DEFAULT_TIMEOUT_S = 120.0
_GEMINI_IMAGEN_ASPECT_RATIOS = {"1:1", "9:16", "16:9", "3:4", "4:3"}
# Aspect ratios accepted by the Gemini Flash image (generateContent) models.
# Aspect ratios documented for every Gemini Flash image (generateContent) model.
# The extreme ratios (1:4, 4:1, 1:8, 8:1) are only listed for the 3.1 Flash /
# Flash Lite tables, so they are left out to avoid sending an unsupported value
# to 2.5 Flash Image or 3.1 Pro Image.
_GEMINI_FLASH_ASPECT_RATIOS = {
"1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4",
"9:16", "16:9", "21:9", "1:4", "4:1", "1:8", "8:1",
"1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9",
}
# Image-size tokens accepted by Gemini 3+ image models (2.5 Flash Image ignores it).
# Image-size tokens accepted by Gemini 3+ image models (earlier Flash image
# models expose only a single fixed resolution).
_GEMINI_FLASH_IMAGE_SIZES = {"512", "1K", "2K", "4K"}
_OLLAMA_DEFAULT_SIDE = 1024
_OLLAMA_SIZE_PRESETS = {
@@ -787,8 +790,13 @@ def _gemini_flash_image_config(
def _gemini_flash_supports_image_size(model: str) -> bool:
"""Return whether the model honors ``imageSize`` (Gemini 3+ image models)."""
return "2.5" not in model.lower()
"""Return whether the model honors ``imageSize``.
Only Gemini 3+ image models expose a configurable image size; earlier Flash
image models (2.0, 2.5) generate at a single fixed resolution, so ``imageSize``
is identified positively rather than by excluding a single version.
"""
return "gemini-3" in model.lower()
async def _aihubmix_images_from_payload(
+20 -2
View File
@@ -465,14 +465,32 @@ async def test_gemini_flash_2_5_drops_image_size() -> None:
@pytest.mark.asyncio
async def test_gemini_flash_ignores_unsupported_hints() -> None:
async def test_gemini_flash_2_0_drops_image_size() -> None:
fake = FakeClient(_gemini_flash_image_response())
client = GeminiImageGenerationClient(api_key="AIza-test", client=fake) # type: ignore[arg-type]
await client.generate(
prompt="draw a cat",
model="gemini-2.0-flash-preview-image-generation",
aspect_ratio="16:9",
image_size="1K",
)
image_config = fake.calls[0]["json"]["generationConfig"]["responseFormat"]["image"]
assert image_config == {"aspectRatio": "16:9"}
@pytest.mark.asyncio
async def test_gemini_flash_ignores_unsupported_hints() -> None:
fake = FakeClient(_gemini_flash_image_response())
client = GeminiImageGenerationClient(api_key="AIza-test", client=fake) # type: ignore[arg-type]
# 7:5 is not a documented ratio; 1:8 is only valid for 3.1 Flash, not Pro;
# 1024x1024 is not a valid Gemini image-size token. All are dropped.
await client.generate(
prompt="draw a cat",
model="gemini-3-pro-image",
aspect_ratio="7:5",
aspect_ratio="1:8",
image_size="1024x1024",
)