diff --git a/CHANGELOG.md b/CHANGELOG.md index 21b5de5..877aecb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,15 @@ Release notes are generated from this file. Keep changelog entries in English. ### Features +- Default to GPT Image 2.5 Flare and support Sunburst selection, xhigh/max quality, and transparent PNG/WebP output with model-specific validation. (#5) + - Add the initial Codex GPT Image skill and Codex OAuth image-generation CLI. - Add Codex device-code login fallback for machines without an existing Codex auth file. ### Improvements +- Distinguish the requested image model from the backend-reported model in CLI output. (#5) + - Increase the default Codex Images request timeout to 600 seconds. (#3) - Route Codex OAuth image requests through the Codex Images endpoints instead of the Responses image-generation tool. (#2) - Add CLI options for moderation, output compression, edit masks, and end-user identifiers. (#2) @@ -26,6 +30,8 @@ Release notes are generated from this file. Keep changelog entries in English. ### Documentation +- Document OAuth probe results and unverified backend model, quality, and size enforcement. (#5) + - Add Chinese and English installation and usage documentation. - Add an OpenAI Images API parameter reference for agent workflows. (#2) - Consolidate Images API parameters and Codex request-shape guidance into one reference. (#2) diff --git a/README.md b/README.md index 0a014e7..2a7988c 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ [![English](https://img.shields.io/badge/docs-English-blue)](README_en.md) [![Skill](https://img.shields.io/badge/skill-codex--gpt--image-cd3b35)](skills/codex-gpt-image) -一个面向 **OpenClaw / Claude Code / Codex / Hermes Agent** 的 `SKILL.md` 生图 skill:通过 **Codex OAuth / ChatGPT 登录态** 调用 `gpt-image-2`,不需要 `OPENAI_API_KEY`。 +一个面向 **OpenClaw / Claude Code / Codex / Hermes Agent** 的 `SKILL.md` 生图 skill:通过 **Codex OAuth / ChatGPT 登录态** 调用 `gpt-image-2.5-flare`,不需要 `OPENAI_API_KEY`。 它读取本机 `~/.codex/auth.json`,请求 Codex Images 后端 `https://chatgpt.com/backend-api/codex/images/generations` 或 `https://chatgpt.com/backend-api/codex/images/edits`,让 agent 复用已有 Codex / ChatGPT 订阅权限生成图片。 ## 适合谁用 -- 想在 OpenClaw / Claude Code / Codex / Hermes Agent 里直接用 `gpt-image-2` 生图 +- 想在 OpenClaw / Claude Code / Codex / Hermes Agent 里直接用 `gpt-image-2.5-flare` 生图 - 已经有 Codex / ChatGPT OAuth 登录态,不想再配置 OpenAI API key - 想把同一套 GPT Image skill 复用到多个支持 `SKILL.md` 的 agent - 需要文本生图、参考图编辑,或在用户明确要求时指定合法输出尺寸 @@ -17,9 +17,9 @@ - OpenClaw skill / Claude Code skill / Codex skill / Hermes Agent skill - Codex OAuth:读取 `~/.codex/auth.json`,不要求 OpenAI API key -- 默认使用 `gpt-image-2`,支持 `low`、`medium`、`high`、`auto` 质量参数 +- 默认使用 `gpt-image-2.5-flare`,支持 `low`、`medium`、`high`、`xhigh`、`max`、`auto` 质量参数 - 支持文本生图和多参考图编辑 -- 支持 `gpt-image-2` 合法尺寸校验 +- 支持 `gpt-image-2.5-flare` 合法尺寸校验 - 支持官方 Images API 的常用参数:`background`、`moderation`、`output_format`、`output_compression`、`mask` - 纯 Python 标准库脚本,便于在任意 agent 环境里调用 @@ -148,10 +148,16 @@ python3 skills/codex-gpt-image/scripts/codex_gpt_image.py generate \ - 这不是 OpenAI API key 方案,不使用 `OPENAI_API_KEY` 计费。 - 这不是 OpenAI 官方推荐的 API 集成方式;Codex Images 后端接口可能随时变更或失效,也可能受到账号、产品权限或用量规则影响。 - 请求会发到 Codex Images 后端:`https://chatgpt.com/backend-api/codex/images/generations` 或 `https://chatgpt.com/backend-api/codex/images/edits`。 -- `gpt-image-2` 不支持透明背景;保持默认 `background=auto`,或显式使用 `opaque`。 +- GPT Image 2.5 支持透明背景:使用 `--background transparent` 并选择 PNG 或 WebP;旧模型 `gpt-image-2` 仍不支持。 - Codex OAuth token 可能过期;遇到 401/403 时先重新登录 Codex。 - 不要把 `~/.codex/auth.json` 提交到任何仓库。 +### GPT Image 2.5 模型选择 + +默认使用 `gpt-image-2.5-flare`;精确编辑可用 `--model gpt-image-2.5-sunburst`。两款模型及其日期快照支持 `xhigh`、`max` 质量档位。可通过 `CODEX_GPT_IMAGE_MODEL` 覆盖默认模型,或显式选择旧模型 `--model gpt-image-2`。OAuth 可用性取决于账号和后端开放情况。 + +实测边界:2026-09-11 的 Codex OAuth 测试中,两款 2.5 模型名均返回图片,编辑与透明 PNG 也成功;但不存在的模型名同样成功,响应未提供实际模型 ID,且实际尺寸与请求尺寸不一致。因此这些结果只证明链路可用,不能证明后端遵循模型或质量选择。CLI 会区分请求模型和后端报告模型。 + ## 许可证 MIT diff --git a/README_en.md b/README_en.md index 007ea4b..9607873 100644 --- a/README_en.md +++ b/README_en.md @@ -2,13 +2,13 @@ [![中文](https://img.shields.io/badge/docs-中文-blue)](README.md) [![Skill](https://img.shields.io/badge/skill-codex--gpt--image-cd3b35)](skills/codex-gpt-image) -A `SKILL.md` image-generation skill for **OpenClaw, Claude Code, Codex, Hermes Agent**, and other skill-capable agents. It generates images with **`gpt-image-2` via Codex OAuth / ChatGPT login**, without requiring `OPENAI_API_KEY`. +A `SKILL.md` image-generation skill for **OpenClaw, Claude Code, Codex, Hermes Agent**, and other skill-capable agents. It generates images with **`gpt-image-2.5-flare` via Codex OAuth / ChatGPT login**, without requiring `OPENAI_API_KEY`. The skill reads the local `~/.codex/auth.json` and calls the Codex Images backend at `https://chatgpt.com/backend-api/codex/images/generations` or `https://chatgpt.com/backend-api/codex/images/edits` so agents can reuse an existing Codex / ChatGPT subscription session. ## Who this is for -- You want `gpt-image-2` image generation inside OpenClaw, Claude Code, Codex, or Hermes Agent. +- You want `gpt-image-2.5-flare` image generation inside OpenClaw, Claude Code, Codex, or Hermes Agent. - You already have Codex / ChatGPT OAuth login and do not want to configure an OpenAI API key. - You want one GPT Image skill that works across multiple `SKILL.md`-capable agents. - You need text-to-image, reference-image editing, or explicit legal output dimensions when the user asks for them. @@ -18,9 +18,9 @@ The skill reads the local `~/.codex/auth.json` and calls the Codex Images backen - OpenClaw skill / Claude Code skill / Codex skill / Hermes Agent skill - Codex OAuth auth from `~/.codex/auth.json` - No OpenAI API key required -- Defaults to `gpt-image-2` +- Defaults to `gpt-image-2.5-flare` - Supports text-to-image and reference-image editing -- Validates legal `gpt-image-2` output dimensions +- Validates legal `gpt-image-2.5-flare` output dimensions - Supports common official Images API parameters: `background`, `moderation`, `output_format`, `output_compression`, and `mask` - Pure Python standard-library CLI @@ -124,10 +124,16 @@ python3 skills/codex-gpt-image/scripts/codex_gpt_image.py generate \ - This skill does not use `OPENAI_API_KEY` billing. - This is not OpenAI's recommended API integration path; the Codex Images backend interface may change or stop working at any time and can be affected by account, product access, or usage rules. - Requests go to `https://chatgpt.com/backend-api/codex/images/generations` or `https://chatgpt.com/backend-api/codex/images/edits`. -- `gpt-image-2` does not support transparent backgrounds; keep the default `background=auto`, or use `opaque` explicitly. +- GPT Image 2.5 supports `--background transparent` with PNG or WebP output. The older `gpt-image-2` model still does not support transparency. - If you get 401/403, refresh Codex auth with `codex login`. - Never commit `~/.codex/auth.json`. +### GPT Image 2.5 model selection + +The default is `gpt-image-2.5-flare`. Select `--model gpt-image-2.5-sunburst` for precise editing. Both models and their dated snapshots support `xhigh` and `max` quality. Override the default with `CODEX_GPT_IMAGE_MODEL`, or select the older `--model gpt-image-2` explicitly. OAuth availability depends on the account and backend rollout. + +Verification boundary: In the 2026-09-11 Codex OAuth probes, both 2.5 model names returned images, including an edit and a transparent PNG. However, an invalid model name also succeeded, responses omitted the actual model ID, and output dimensions differed from the requested size. These results verify the image pipeline, not backend model or quality selection. The CLI distinguishes the requested model from the backend-reported model. + ## License MIT diff --git a/skills/codex-gpt-image/SKILL.md b/skills/codex-gpt-image/SKILL.md index c57145e..e1dbddd 100644 --- a/skills/codex-gpt-image/SKILL.md +++ b/skills/codex-gpt-image/SKILL.md @@ -1,6 +1,6 @@ --- name: codex-gpt-image -description: Generate or edit images with gpt-image-2 through Codex/ChatGPT subscription authentication instead of OPENAI_API_KEY. Use for text-to-image, reference-image editing, or visual assets when the user wants local Codex auth, especially when no native image tool is available. Do not use for official OpenAI API-key billing or OpenAI-compatible gateways. +description: Generate or edit images with GPT Image 2.5 through Codex/ChatGPT subscription authentication instead of OPENAI_API_KEY. Use for text-to-image, reference-image editing, or visual assets when the user wants local Codex auth, especially when no native image tool is available. Do not use for official OpenAI API-key billing or OpenAI-compatible gateways. --- # Codex GPT Image @@ -9,7 +9,7 @@ Use this skill to generate or edit images through Codex OAuth instead of the Ope ## When To Use -- The user asks to use `gpt-image-2` or GPT Image through Codex auth/subscription. +- The user asks to use `gpt-image-2.5-flare` or GPT Image through Codex auth/subscription. - The current agent supports `SKILL.md` but does not have a native image tool. - The user explicitly does not want to use `OPENAI_API_KEY`. - The user wants the same local image workflow across Codex, Claude Code, OpenClaw, Hermes Agent, or similar agents. @@ -38,7 +38,7 @@ All CLI commands below assume the working directory is this skill folder. Otherw 4. Edit or use reference images by passing one or more `--image` inputs. For edits, build the prompt from the user's requested changes and the invariants that must stay unchanged. -5. Report the saved path(s), model, size, and whether Codex OAuth was used. +5. Report the saved path(s), requested model, actual output size when inspected, and whether Codex OAuth was used. Do not claim the requested model was used unless the backend identifies it. ## Defaults @@ -47,7 +47,7 @@ All CLI commands below assume the working directory is this skill folder. Otherw - Login fallback: `login` uses OpenAI Codex device-code auth and writes the same auth file - Login client id: `--client-id`, `CODEX_APP_SERVER_LOGIN_CLIENT_ID`, then the public Codex default - Images base URL: `https://chatgpt.com/backend-api/codex` -- Image model: `gpt-image-2` +- Image model: `gpt-image-2.5-flare` - Size: `auto` - Quality: `auto` - Background: `auto` @@ -85,6 +85,12 @@ The CLI sends Codex Images requests with: - generation endpoint: `POST https://chatgpt.com/backend-api/codex/images/generations` - edit endpoint: `POST https://chatgpt.com/backend-api/codex/images/edits` - auth: `Authorization: Bearer ` -- model: `gpt-image-2` +- model: `gpt-image-2.5-flare` It parses the JSON Images response and writes returned base64 image payloads to local files. + +## GPT Image 2.5 Compatibility + +Use the default Flare model for everyday generation, or select `--model gpt-image-2.5-sunburst` for precise editing. Both models and their dated snapshots support `xhigh` and `max` quality and transparent backgrounds with PNG/WebP. Older models cannot use the new quality levels; this CLI keeps transparency disabled for older models. Codex OAuth access depends on the account and backend rollout. + +Codex OAuth probes on 2026-09-11 also accepted an invalid model name, omitted the actual model ID, and returned dimensions different from the requested size. Successful generation does not verify model or quality selection. Treat these flags as requested settings; do not present public API capabilities as guaranteed OAuth backend behavior. diff --git a/skills/codex-gpt-image/references/openai-images-api-parameters.md b/skills/codex-gpt-image/references/openai-images-api-parameters.md index 131a1cb..5b4ec0a 100644 --- a/skills/codex-gpt-image/references/openai-images-api-parameters.md +++ b/skills/codex-gpt-image/references/openai-images-api-parameters.md @@ -6,7 +6,8 @@ Treat the linked OpenAI documentation as the source of truth when behavior chang - Create image: https://developers.openai.com/api/reference/resources/images/methods/generate - Create image edit: https://developers.openai.com/api/reference/resources/images/methods/edit - Image generation guide: https://developers.openai.com/api/docs/guides/image-generation -- GPT Image 2 model page: https://developers.openai.com/api/docs/models/gpt-image-2 +- GPT Image 2.5 Flare: https://developers.openai.com/api/docs/models/gpt-image-2.5-flare +- GPT Image 2.5 Sunburst: https://developers.openai.com/api/docs/models/gpt-image-2.5-sunburst ## Codex Request Shape @@ -40,6 +41,10 @@ Request headers: Generation requests contain the user's prompt plus the shared image parameters below. Edit requests also contain one or more local reference images encoded as base64 data URLs, and may include a mask when the user requests a masked edit. +## Observed OAuth Backend Limits + +On 2026-09-11, requests naming Flare and Sunburst returned images; an xhigh transparent PNG and a max-quality edit request also returned usable images. An invalid model name succeeded too, without a model ID in the response. All four valid-name probes requested 1024x1024 but returned other dimensions. These probes do not establish actual model routing or quality enforcement. Public API constraints below describe request validation, not a guarantee that the OAuth backend applies every field. + ## Parameter Selection Prefer official API defaults unless the user request requires a specific option. @@ -51,12 +56,12 @@ For `size`, use an explicit value only when the user asks for a specific dimensi | API field | CLI flag | Default | Values / constraints | Notes | | --- | --- | --- | --- | --- | -| `model` | `--model` | `gpt-image-2` | GPT Image model string | This skill defaults to `gpt-image-2` by design. | +| `model` | `--model` | `gpt-image-2.5-flare` | GPT Image model string | Use `gpt-image-2.5-sunburst` for precise editing; dated snapshots are also accepted. `CODEX_GPT_IMAGE_MODEL` overrides the default. | | `prompt` | `--prompt`, `--prompt-file` | Required | Text, up to 32000 chars for GPT image models | Required for generation and edits. Use the user's actual prompt; do not reuse wording from this reference. | | `n` | `--count` | `1` | Integer `1` through `10` | Number of generated or edited images. | -| `size` | `--size` | `auto` | `auto` or `WIDTHxHEIGHT` | For `gpt-image-2`, both edges must be multiples of 16, max edge <= 3840, aspect ratio <= 3:1, total pixels between 655360 and 8294400. | -| `quality` | `--quality` | `auto` | `low`, `medium`, `high`, `auto` | GPT image models support these values. | -| `background` | `--background` | `auto` | CLI exposes `auto`, `opaque` | The public API also documents `transparent`, but `gpt-image-2` does not support it, so this CLI does not expose that value. | +| `size` | `--size` | `auto` | `auto` or `WIDTHxHEIGHT` | For both GPT Image 2.5 models and `gpt-image-2`, both edges must be multiples of 16, max edge <= 3840, aspect ratio <= 3:1, total pixels between 655360 and 8294400. | +| `quality` | `--quality` | `auto` | `low`, `medium`, `high`, `xhigh`, `max`, `auto` | `xhigh` and `max` require GPT Image 2.5 (including dated snapshots). | +| `background` | `--background` | `auto` | `auto`, `opaque`, `transparent` | This CLI allows transparency only for GPT Image 2.5 with PNG or WebP output; `gpt-image-2` does not support it. | | `moderation` | `--moderation` | `auto` | `low`, `auto` | GPT image model moderation strictness. | | `output_format` | `--output-format` | `png` | `png`, `jpeg`, `webp` | GPT image models return base64 image data. | | `output_compression` | `--output-compression` | `100` for `jpeg` and `webp` | Integer `0` through `100` | Only valid when `output_format` is `jpeg` or `webp`. | diff --git a/skills/codex-gpt-image/scripts/codex_gpt_image.py b/skills/codex-gpt-image/scripts/codex_gpt_image.py index aa8257b..bf5d2e6 100644 --- a/skills/codex-gpt-image/scripts/codex_gpt_image.py +++ b/skills/codex-gpt-image/scripts/codex_gpt_image.py @@ -33,7 +33,7 @@ DEFAULT_OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann" CODEX_CLIENT_ID_ENV_VAR = "CODEX_APP_SERVER_LOGIN_CLIENT_ID" OPENAI_CODEX_DEVICE_CALLBACK_URL = f"{OPENAI_AUTH_BASE_URL}/deviceauth/callback" -DEFAULT_IMAGE_MODEL = "gpt-image-2" +DEFAULT_IMAGE_MODEL = "gpt-image-2.5-flare" DEFAULT_SIZE = "auto" DEFAULT_QUALITY = "auto" DEFAULT_OUTPUT_FORMAT = "png" @@ -50,9 +50,9 @@ MAX_RESPONSE_BYTES = 64 * 1024 * 1024 MAX_BASE64_CHARS = 64 * 1024 * 1024 MAX_IMAGE_DATA_URL_CHARS = 20_971_520 -SUPPORTED_QUALITIES = {"low", "medium", "high", "auto"} +SUPPORTED_QUALITIES = {"low", "medium", "high", "xhigh", "max", "auto"} SUPPORTED_OUTPUT_FORMATS = {"png", "jpeg", "jpg", "webp"} -SUPPORTED_BACKGROUNDS = {"opaque", "auto"} +SUPPORTED_BACKGROUNDS = {"opaque", "transparent", "auto"} SUPPORTED_MODERATIONS = {"low", "auto"} CHATGPT_AUTH_CLAIM = "https://api.openai.com/auth" CHATGPT_ACCOUNT_ID_CLAIM = "chatgpt_account_id" @@ -405,14 +405,25 @@ def normalize_output_format(value: str | None) -> str: return "jpeg" if fmt == "jpg" else fmt -def validate_quality(value: str) -> None: +def is_gpt_image_2_5(model: str) -> bool: + return bool(re.fullmatch(r"gpt-image-2\.5-(?:flare|sunburst)(?:-\d{4}-\d{2}-\d{2})?", model)) + + +def validate_quality(value: str, model: str) -> None: if value not in SUPPORTED_QUALITIES: - raise CliError("quality must be low, medium, high, or auto.") + raise CliError("quality must be low, medium, high, xhigh, max, or auto.") + if value in {"xhigh", "max"} and not is_gpt_image_2_5(model): + raise CliError("xhigh and max quality require gpt-image-2.5-flare or gpt-image-2.5-sunburst.") -def validate_background(value: str | None) -> None: +def validate_background(value: str | None, model: str, output_format: str) -> None: if value is not None and value not in SUPPORTED_BACKGROUNDS: - raise CliError("background must be auto or opaque.") + raise CliError("background must be auto, opaque, or transparent.") + if value == "transparent": + if not is_gpt_image_2_5(model): + raise CliError("transparent backgrounds require gpt-image-2.5-flare or gpt-image-2.5-sunburst in this CLI.") + if output_format not in {"png", "webp"}: + raise CliError("transparent backgrounds require png or webp output.") def validate_moderation(value: str | None) -> None: @@ -443,7 +454,7 @@ def parse_size(value: str) -> tuple[int, int] | None: def is_gpt_image_2(model: str) -> bool: - return "gpt-image-2" in model + return bool(re.fullmatch(r"gpt-image-2(?:-\d{4}-\d{2}-\d{2})?", model)) or is_gpt_image_2_5(model) def validate_size(size: str, model: str) -> None: @@ -461,13 +472,13 @@ def validate_size(size: str, model: str) -> None: min_edge = min(width, height) pixels = width * height if max_edge > GPT_IMAGE_2_MAX_EDGE: - raise CliError("gpt-image-2 max edge must be <= 3840.") + raise CliError("GPT Image 2/2.5 max edge must be <= 3840.") if width % 16 != 0 or height % 16 != 0: - raise CliError("gpt-image-2 width and height must be multiples of 16.") + raise CliError("GPT Image 2/2.5 width and height must be multiples of 16.") if max_edge / min_edge > GPT_IMAGE_2_MAX_RATIO: - raise CliError("gpt-image-2 long-to-short ratio must be <= 3:1.") + raise CliError("GPT Image 2/2.5 long-to-short ratio must be <= 3:1.") if pixels < GPT_IMAGE_2_MIN_PIXELS or pixels > GPT_IMAGE_2_MAX_PIXELS: - raise CliError("gpt-image-2 total pixels must be between 655,360 and 8,294,400.") + raise CliError("GPT Image 2/2.5 total pixels must be between 655,360 and 8,294,400.") def guess_mime(path: Path, data: bytes) -> str: @@ -512,8 +523,8 @@ def build_image_body(args: argparse.Namespace, prompt: str, image_paths: list[st else default_output_compression(output_format) ) - validate_quality(args.quality) - validate_background(args.background) + validate_quality(args.quality, image_model) + validate_background(args.background, image_model, output_format) validate_moderation(args.moderation) validate_output_compression(output_compression, output_format) validate_size(args.size, image_model) @@ -688,6 +699,7 @@ def cmd_generate(args: argparse.Namespace) -> int: for path in written: print(path) eprint(f"Generated {len(written)} image(s) via Codex OAuth in {time.time() - start:.1f}s.") + eprint(f"Requested model: {image_model}; backend-reported model: {response.get('model') or 'not reported (selection unverified)'}.") return 0 diff --git a/tests/test_image_parameters.py b/tests/test_image_parameters.py new file mode 100644 index 0000000..e9ba453 --- /dev/null +++ b/tests/test_image_parameters.py @@ -0,0 +1,60 @@ +import importlib.util +from pathlib import Path +import sys +import unittest + +SCRIPT = Path(__file__).resolve().parents[1] / 'skills/codex-gpt-image/scripts/codex_gpt_image.py' +spec = importlib.util.spec_from_file_location('codex_gpt_image', SCRIPT) +cli = importlib.util.module_from_spec(spec) +sys.modules[spec.name] = cli +spec.loader.exec_module(cli) + + +class ImageParametersTest(unittest.TestCase): + def body(self, *flags): + args = cli.build_parser().parse_args(['generate', '--prompt', 'test', *flags]) + return cli.build_image_body(args, 'test', [])[2] + + def test_default_model_and_legacy_override(self): + self.assertEqual(self.body()['model'], 'gpt-image-2.5-flare') + self.assertEqual(self.body('--model', 'gpt-image-2')['model'], 'gpt-image-2') + + def test_new_models_and_snapshots_accept_new_options(self): + for model in ('gpt-image-2.5-flare', 'gpt-image-2.5-sunburst'): + for suffix in ('', '-2026-09-08'): + for quality in ('xhigh', 'max'): + for fmt in ('png', 'webp'): + with self.subTest(model=model + suffix, quality=quality, fmt=fmt): + body = self.body('--model', model + suffix, '--quality', quality, + '--background', 'transparent', '--output-format', fmt) + self.assertEqual(body['quality'], quality) + self.assertEqual(body['background'], 'transparent') + + def test_legacy_rejects_new_options(self): + for model in ('gpt-image-2', 'gpt-image-2-2026-04-21'): + for option in (('--quality', 'max'), ('--quality', 'xhigh'), ('--background', 'transparent')): + with self.subTest(model=model, option=option), self.assertRaises(cli.CliError): + self.body('--model', model, *option) + + def test_transparent_jpeg_rejected(self): + for fmt in ('jpeg', 'jpg'): + with self.subTest(fmt=fmt), self.assertRaises(cli.CliError): + self.body('--background', 'transparent', '--output-format', fmt) + + def test_size_boundaries(self): + for model in ('gpt-image-2', 'gpt-image-2.5-flare', 'gpt-image-2.5-sunburst'): + for size in ('auto', '1024x640', '3840x2160'): + with self.subTest(model=model, size=size): + self.body('--model', model, '--size', size) + for size in ('1000x1000', '4096x2048', '1024x512', '3840x1024', '3840x3840'): + with self.subTest(model=model, size=size), self.assertRaises(cli.CliError): + self.body('--model', model, '--size', size) + + def test_model_matching_is_not_substring_based(self): + for model in ('not-gpt-image-2', 'gpt-image-20', 'gpt-image-2.5-flare-invalid'): + self.assertFalse(cli.is_gpt_image_2(model)) + self.assertFalse(cli.is_gpt_image_2_5(model)) + + +if __name__ == '__main__': + unittest.main()