Skip to content

Commit edcfae9

Browse files
committed
fix: address image CLI review feedback
1 parent 36b5c2d commit edcfae9

4 files changed

Lines changed: 58 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Release notes are generated from this file. Keep changelog entries in English.
44

55
## Unreleased
66

7+
### Fixes
8+
9+
- Teach `image_gen.py --prompt-file` to read prepared JSON slide jobs from their `prompt` field and restore image `output_compression` and `moderation` CLI options.
10+
711
## 0.5.1
812

913
### Improvements

skills/codex-ppt/prompts/slide-worker.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ Strict input images already prepared by the parent:
2626
Read the JSON job file, then follow its `prompt` field exactly. Use the selected image backend and the recorded sample generation method only.
2727
You must produce the final slide candidate by calling the selected image generation backend:
2828
- Before calling the backend, visually inspect every local image listed in `style_reference_images` and `input_images`.
29-
- Local CLI mode for normal new slides: use `scripts/image_gen.py generate --prompt-file <job prompt file> --out <deck dir>/drafts/slide_<NN>_candidate.png`.
30-
- Local CLI mode for slides with strict `input_images`: use `scripts/image_gen.py edit --image <asset> ... --prompt-file <job prompt file> --out <deck dir>/drafts/slide_<NN>_candidate.png`.
29+
- Local CLI mode for normal new slides: use `scripts/image_gen.py generate --prompt-file <job prompt file> --out <deck dir>/drafts/slide_<NN>_candidate.png`; the CLI reads the JSON job file's `prompt` field.
30+
- Local CLI mode for slides with strict `input_images`: use `scripts/image_gen.py edit --image <asset> ... --prompt-file <job prompt file> --out <deck dir>/drafts/slide_<NN>_candidate.png`; the CLI reads the JSON job file's `prompt` field.
3131
- Local CLI mode for repairing an existing generated slide: use `scripts/image_gen.py edit --image <existing slide> --prompt ... --out <deck dir>/drafts/slide_<NN>_revised.png`.
3232
- Do not pass approved sample/style reference images as `--image` inputs unless the JSON job also lists them under `input_images`.
3333
- UI built-in mode is allowed only if the parent explicitly selected it and described how to save the result to the target path.

skills/codex-ppt/scripts/image_gen.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,18 @@ def _read_prompt(prompt: Optional[str], prompt_file: Optional[str]) -> str:
269269
path = Path(prompt_file)
270270
if not path.exists():
271271
_die(f"Prompt file not found: {path}")
272-
return path.read_text(encoding="utf-8").strip()
272+
raw = path.read_text(encoding="utf-8").strip()
273+
if path.suffix.lower() == ".json" or raw.startswith("{"):
274+
try:
275+
data = json.loads(raw)
276+
except json.JSONDecodeError:
277+
return raw
278+
if isinstance(data, dict) and isinstance(data.get("prompt"), str):
279+
prompt_text = data["prompt"].strip()
280+
if prompt_text:
281+
return prompt_text
282+
_die(f"Prompt field is empty in JSON prompt file: {path}")
283+
return raw
273284
if prompt:
274285
return prompt.strip()
275286
_die("Missing prompt. Use --prompt or --prompt-file.")
@@ -407,6 +418,16 @@ def _validate_generate_payload(payload: Dict[str, Any]) -> None:
407418
_validate_model_specific_options(model=model, background=background)
408419

409420

421+
def _validate_output_compression(
422+
output_compression: Optional[int],
423+
output_format: Optional[str] = None,
424+
) -> None:
425+
if output_compression is not None and not (0 <= output_compression <= 100):
426+
_die("--output-compression must be between 0 and 100")
427+
if output_compression is not None and output_format not in (None, "jpeg", "webp"):
428+
_die("--output-compression requires --output-format jpeg or webp")
429+
430+
410431
def _build_output_paths(
411432
out: str,
412433
output_format: str,
@@ -682,6 +703,8 @@ async def _run_generate_batch(args: argparse.Namespace) -> int:
682703
"quality": args.quality,
683704
"background": args.background,
684705
"output_format": args.output_format,
706+
"output_compression": args.output_compression,
707+
"moderation": args.moderation,
685708
}
686709

687710
provider = _create_provider(args)
@@ -704,6 +727,10 @@ async def _run_generate_batch(args: argparse.Namespace) -> int:
704727
_validate_generate_payload(job_payload)
705728
effective_output_format = _normalize_output_format(job_payload.get("output_format"))
706729
_validate_transparency(job_payload.get("background"), effective_output_format)
730+
_validate_output_compression(
731+
job_payload.get("output_compression"),
732+
effective_output_format,
733+
)
707734
job_payload["output_format"] = effective_output_format
708735

709736
n = int(job_payload.get("n", 1))
@@ -758,6 +785,10 @@ async def run_job(i: int, job: Dict[str, Any]) -> Tuple[int, Optional[str]]:
758785
_validate_generate_payload(payload)
759786
effective_output_format = _normalize_output_format(payload.get("output_format"))
760787
_validate_transparency(payload.get("background"), effective_output_format)
788+
_validate_output_compression(
789+
payload.get("output_compression"),
790+
effective_output_format,
791+
)
761792
payload["output_format"] = effective_output_format
762793
outputs = _job_output_paths(
763794
out_dir=out_dir,
@@ -825,11 +856,14 @@ def _generate(args: argparse.Namespace) -> None:
825856
"quality": args.quality,
826857
"background": args.background,
827858
"output_format": args.output_format,
859+
"output_compression": args.output_compression,
860+
"moderation": args.moderation,
828861
}
829862
payload = {k: v for k, v in payload.items() if v is not None}
830863

831864
output_format = _normalize_output_format(args.output_format)
832865
_validate_transparency(args.background, output_format)
866+
_validate_output_compression(args.output_compression, output_format)
833867
payload["output_format"] = output_format
834868
output_paths = _build_output_paths(args.out, output_format, args.n, args.out_dir)
835869
downscaled = None
@@ -889,12 +923,15 @@ def _edit(args: argparse.Namespace) -> None:
889923
"quality": args.quality,
890924
"background": args.background,
891925
"output_format": args.output_format,
926+
"output_compression": args.output_compression,
892927
"input_fidelity": args.input_fidelity,
928+
"moderation": args.moderation,
893929
}
894930
payload = {k: v for k, v in payload.items() if v is not None}
895931

896932
output_format = _normalize_output_format(args.output_format)
897933
_validate_transparency(args.background, output_format)
934+
_validate_output_compression(args.output_compression, output_format)
898935
payload["output_format"] = output_format
899936
_validate_input_fidelity(args.input_fidelity)
900937
output_paths = _build_output_paths(args.out, output_format, args.n, args.out_dir)
@@ -953,6 +990,8 @@ def _add_shared_args(parser: argparse.ArgumentParser) -> None:
953990
parser.add_argument("--quality", default=DEFAULT_QUALITY)
954991
parser.add_argument("--background")
955992
parser.add_argument("--output-format")
993+
parser.add_argument("--output-compression", type=int)
994+
parser.add_argument("--moderation")
956995
parser.add_argument("--out", default=DEFAULT_OUTPUT_PATH)
957996
parser.add_argument("--out-dir")
958997
parser.add_argument("--force", action="store_true")
@@ -1023,6 +1062,7 @@ def main() -> int:
10231062
_validate_size(args.size, args.model)
10241063
_validate_quality(args.quality)
10251064
_validate_background(args.background)
1065+
_validate_output_compression(args.output_compression)
10261066
_validate_model_specific_options(
10271067
model=args.model,
10281068
background=args.background,

skills/codex-ppt/scripts/image_providers/codex_oauth.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,22 @@ def _run_images_endpoint(self, payload: Dict[str, Any], image_paths: List[Path])
6969

7070
def _images_body(self, payload: Dict[str, Any], image_paths: List[Path]) -> Dict[str, Any]:
7171
output_format = payload.get("output_format")
72-
if isinstance(output_format, str) and output_format.lower() != "png":
73-
raise RuntimeError("Codex OAuth images endpoint currently supports png output only.")
72+
if isinstance(output_format, str) and output_format.lower() not in {"png", "jpeg", "webp"}:
73+
raise RuntimeError("Codex OAuth images endpoint supports png, jpeg, or webp output.")
7474

7575
body: Dict[str, Any] = {
7676
"prompt": str(payload["prompt"]),
7777
"model": str(payload["model"]),
7878
}
79-
for key in ("n", "size", "quality", "background"):
79+
for key in (
80+
"n",
81+
"size",
82+
"quality",
83+
"background",
84+
"output_format",
85+
"output_compression",
86+
"moderation",
87+
):
8088
value = payload.get(key)
8189
if value is not None:
8290
body[key] = value

0 commit comments

Comments
 (0)