mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
openai-image-gen: validate and normalize --output-format (#36648)
* openai-image-gen: validate and normalize output format * Skills/openai-image-gen: cover output-format edge cases * Changelog: note openai image output format validation --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -199,6 +199,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Telegram/Discord media upload caps: make outbound uploads honor channel `mediaMaxMb` config, raise Telegram's default media cap to 100MB, and remove MIME fallback limits that kept some Telegram uploads at 16MB. Thanks @vincentkoc.
|
||||
- Skills/nano-banana-pro resolution override: respect explicit `--resolution` values during image editing and only auto-detect output size from input images when the flag is omitted. (#36880) Thanks @shuofengzhang and @vincentkoc.
|
||||
- Skills/openai-image-gen CLI validation: validate `--background` and `--style` inputs early, normalize supported values, and warn when those flags are ignored for incompatible models. (#36762) Thanks @shuofengzhang and @vincentkoc.
|
||||
- Skills/openai-image-gen output formats: validate `--output-format` values early, normalize aliases like `jpg -> jpeg`, and warn when the flag is ignored for incompatible models. (#36648) Thanks @shuofengzhang and @vincentkoc.
|
||||
- WhatsApp media upload caps: make outbound media sends and auto-replies honor `channels.whatsapp.mediaMaxMb` with per-account overrides so inbound and outbound limits use the same channel config. Thanks @vincentkoc.
|
||||
|
||||
## 2026.3.2
|
||||
|
||||
@@ -9,6 +9,7 @@ import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from collections.abc import Callable
|
||||
from html import escape as html_escape
|
||||
from pathlib import Path
|
||||
|
||||
@@ -75,46 +76,82 @@ def get_model_defaults(model: str) -> tuple[str, str]:
|
||||
return ("1024x1024", "high")
|
||||
|
||||
|
||||
def normalize_background(model: str, background: str) -> str:
|
||||
"""Validate --background for GPT image models."""
|
||||
value = background.strip().lower()
|
||||
def normalize_optional_flag(
|
||||
*,
|
||||
model: str,
|
||||
raw_value: str,
|
||||
flag_name: str,
|
||||
supported: Callable[[str], bool],
|
||||
allowed: set[str],
|
||||
allowed_text: str,
|
||||
unsupported_message: str,
|
||||
aliases: dict[str, str] | None = None,
|
||||
) -> str:
|
||||
"""Normalize a string flag, warn when unsupported, and reject invalid values."""
|
||||
value = raw_value.strip().lower()
|
||||
if not value:
|
||||
return ""
|
||||
|
||||
if not model.startswith("gpt-image"):
|
||||
print(
|
||||
f"Warning: --background is only supported for gpt-image models; ignoring for '{model}'.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
if not supported(model):
|
||||
print(unsupported_message.format(model=model), file=sys.stderr)
|
||||
return ""
|
||||
|
||||
allowed = {"transparent", "opaque", "auto"}
|
||||
if aliases:
|
||||
value = aliases.get(value, value)
|
||||
|
||||
if value not in allowed:
|
||||
raise ValueError(
|
||||
f"Invalid --background '{background}'. Allowed values: transparent, opaque, auto."
|
||||
f"Invalid --{flag_name} '{raw_value}'. Allowed values: {allowed_text}."
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def normalize_background(model: str, background: str) -> str:
|
||||
"""Validate --background for GPT image models."""
|
||||
return normalize_optional_flag(
|
||||
model=model,
|
||||
raw_value=background,
|
||||
flag_name="background",
|
||||
supported=lambda candidate: candidate.startswith("gpt-image"),
|
||||
allowed={"transparent", "opaque", "auto"},
|
||||
allowed_text="transparent, opaque, auto",
|
||||
unsupported_message=(
|
||||
"Warning: --background is only supported for gpt-image models; "
|
||||
"ignoring for '{model}'."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def normalize_style(model: str, style: str) -> str:
|
||||
"""Validate --style for dall-e-3."""
|
||||
value = style.strip().lower()
|
||||
if not value:
|
||||
return ""
|
||||
return normalize_optional_flag(
|
||||
model=model,
|
||||
raw_value=style,
|
||||
flag_name="style",
|
||||
supported=lambda candidate: candidate == "dall-e-3",
|
||||
allowed={"vivid", "natural"},
|
||||
allowed_text="vivid, natural",
|
||||
unsupported_message=(
|
||||
"Warning: --style is only supported for dall-e-3; ignoring for '{model}'."
|
||||
),
|
||||
)
|
||||
|
||||
if model != "dall-e-3":
|
||||
print(
|
||||
f"Warning: --style is only supported for dall-e-3; ignoring for '{model}'.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return ""
|
||||
|
||||
allowed = {"vivid", "natural"}
|
||||
if value not in allowed:
|
||||
raise ValueError(
|
||||
f"Invalid --style '{style}'. Allowed values for dall-e-3: vivid, natural."
|
||||
)
|
||||
return value
|
||||
def normalize_output_format(model: str, output_format: str) -> str:
|
||||
"""Normalize output format for GPT image models and validate allowed values."""
|
||||
return normalize_optional_flag(
|
||||
model=model,
|
||||
raw_value=output_format,
|
||||
flag_name="output-format",
|
||||
supported=lambda candidate: candidate.startswith("gpt-image"),
|
||||
allowed={"png", "jpeg", "webp"},
|
||||
allowed_text="png, jpeg, webp",
|
||||
unsupported_message=(
|
||||
"Warning: --output-format is only supported for gpt-image models; "
|
||||
"ignoring for '{model}'."
|
||||
),
|
||||
aliases={"jpg": "jpeg"},
|
||||
)
|
||||
|
||||
|
||||
def request_images(
|
||||
@@ -239,13 +276,14 @@ def main() -> int:
|
||||
try:
|
||||
normalized_background = normalize_background(args.model, args.background)
|
||||
normalized_style = normalize_style(args.model, args.style)
|
||||
normalized_output_format = normalize_output_format(args.model, args.output_format)
|
||||
except ValueError as e:
|
||||
print(str(e), file=sys.stderr)
|
||||
return 2
|
||||
|
||||
# Determine file extension based on output format
|
||||
if args.model.startswith("gpt-image") and args.output_format:
|
||||
file_ext = args.output_format
|
||||
if args.model.startswith("gpt-image") and normalized_output_format:
|
||||
file_ext = normalized_output_format
|
||||
else:
|
||||
file_ext = "png"
|
||||
|
||||
@@ -259,7 +297,7 @@ def main() -> int:
|
||||
size,
|
||||
quality,
|
||||
normalized_background,
|
||||
args.output_format,
|
||||
normalized_output_format,
|
||||
normalized_style,
|
||||
)
|
||||
data = res.get("data", [{}])[0]
|
||||
|
||||
@@ -4,7 +4,12 @@ import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from gen import normalize_background, normalize_style, write_gallery
|
||||
from gen import (
|
||||
normalize_background,
|
||||
normalize_output_format,
|
||||
normalize_style,
|
||||
write_gallery,
|
||||
)
|
||||
|
||||
|
||||
def test_normalize_background_allows_empty_for_non_gpt_models():
|
||||
@@ -55,6 +60,43 @@ def test_normalize_style_rejects_invalid_values():
|
||||
normalize_style("dall-e-3", "cinematic")
|
||||
|
||||
|
||||
def test_normalize_output_format_allows_empty_for_non_gpt_models():
|
||||
assert normalize_output_format("dall-e-3", "jpeg") == ""
|
||||
|
||||
|
||||
def test_normalize_output_format_allows_empty_for_gpt_models():
|
||||
assert normalize_output_format("gpt-image-1", "") == ""
|
||||
assert normalize_output_format("gpt-image-1", " ") == ""
|
||||
|
||||
|
||||
def test_normalize_output_format_warns_when_model_does_not_support_flag(capsys):
|
||||
assert normalize_output_format("dall-e-3", "jpeg") == ""
|
||||
captured = capsys.readouterr()
|
||||
assert "--output-format is only supported for gpt-image models" in captured.err
|
||||
|
||||
|
||||
def test_normalize_output_format_normalizes_case_for_supported_values():
|
||||
assert normalize_output_format("gpt-image-1", "PNG") == "png"
|
||||
assert normalize_output_format("gpt-image-1", "WEBP") == "webp"
|
||||
|
||||
|
||||
def test_normalize_output_format_strips_whitespace_for_supported_values():
|
||||
assert normalize_output_format("gpt-image-1", " png ") == "png"
|
||||
def test_normalize_output_format_keeps_supported_values():
|
||||
assert normalize_output_format("gpt-image-1", "png") == "png"
|
||||
assert normalize_output_format("gpt-image-1", "jpeg") == "jpeg"
|
||||
assert normalize_output_format("gpt-image-1", "webp") == "webp"
|
||||
|
||||
|
||||
def test_normalize_output_format_normalizes_jpg_alias():
|
||||
assert normalize_output_format("gpt-image-1", "jpg") == "jpeg"
|
||||
|
||||
|
||||
def test_normalize_output_format_rejects_invalid_values():
|
||||
with pytest.raises(ValueError, match="Invalid --output-format"):
|
||||
normalize_output_format("gpt-image-1", "svg")
|
||||
|
||||
|
||||
def test_write_gallery_escapes_prompt_xss():
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
out = Path(tmpdir)
|
||||
|
||||
Reference in New Issue
Block a user