Initial commit: add all skills files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
133
frontend-dev/scripts/minimax_image.py
Normal file
133
frontend-dev/scripts/minimax_image.py
Normal file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
MiniMax Text-to-Image — synchronous generation.
|
||||
|
||||
Usage:
|
||||
python minimax_image.py "A cat in space" -o cat.png
|
||||
python minimax_image.py "Mountain landscape" -o bg.png --ratio 16:9
|
||||
python minimax_image.py "Product icons" -o icons.png -n 4 --ratio 1:1
|
||||
|
||||
Env: MINIMAX_API_KEY (required)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
import requests
|
||||
|
||||
API_KEY = os.getenv("MINIMAX_API_KEY")
|
||||
API_BASE = "https://api.minimax.io/v1"
|
||||
|
||||
ASPECT_RATIOS = ["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]
|
||||
|
||||
|
||||
def _headers():
|
||||
if not API_KEY:
|
||||
raise SystemExit("ERROR: MINIMAX_API_KEY is not set.\n export MINIMAX_API_KEY='your-key'")
|
||||
return {
|
||||
"Authorization": f"Bearer {API_KEY}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
|
||||
def generate_image(
|
||||
prompt: str,
|
||||
model: str = "image-01",
|
||||
aspect_ratio: str = "1:1",
|
||||
n: int = 1,
|
||||
response_format: str = "url",
|
||||
prompt_optimizer: bool = False,
|
||||
seed: int = None,
|
||||
) -> dict:
|
||||
"""Generate image(s). Returns API response dict."""
|
||||
payload = {
|
||||
"model": model,
|
||||
"prompt": prompt,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
"n": n,
|
||||
"response_format": response_format,
|
||||
"prompt_optimizer": prompt_optimizer,
|
||||
}
|
||||
if seed is not None:
|
||||
payload["seed"] = seed
|
||||
|
||||
resp = requests.post(
|
||||
f"{API_BASE}/image_generation",
|
||||
headers=_headers(),
|
||||
json=payload,
|
||||
timeout=120,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
base_resp = data.get("base_resp", {})
|
||||
if base_resp.get("status_code", 0) != 0:
|
||||
raise SystemExit(f"API Error [{base_resp.get('status_code')}]: {base_resp.get('status_msg')}")
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def download_and_save(url: str, output_path: str):
|
||||
"""Download image from URL and save."""
|
||||
resp = requests.get(url, timeout=60)
|
||||
resp.raise_for_status()
|
||||
with open(output_path, "wb") as f:
|
||||
f.write(resp.content)
|
||||
return len(resp.content)
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="MiniMax Text-to-Image")
|
||||
p.add_argument("prompt", help="Image description (max 1500 chars)")
|
||||
p.add_argument("-o", "--output", required=True, help="Output file path (.png/.jpg)")
|
||||
p.add_argument("--model", default="image-01", help="Model (default: image-01)")
|
||||
p.add_argument("--ratio", default="1:1", choices=ASPECT_RATIOS, help="Aspect ratio (default: 1:1)")
|
||||
p.add_argument("-n", "--count", type=int, default=1, choices=range(1, 10), help="Number of images (1-9, default: 1)")
|
||||
p.add_argument("--seed", type=int, default=None, help="Random seed for reproducibility")
|
||||
p.add_argument("--optimize", action="store_true", help="Enable prompt auto-optimization")
|
||||
p.add_argument("--base64", action="store_true", help="Use base64 response instead of URL")
|
||||
args = p.parse_args()
|
||||
|
||||
os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
|
||||
|
||||
fmt = "base64" if args.base64 else "url"
|
||||
result = generate_image(
|
||||
prompt=args.prompt,
|
||||
model=args.model,
|
||||
aspect_ratio=args.ratio,
|
||||
n=args.count,
|
||||
response_format=fmt,
|
||||
prompt_optimizer=args.optimize,
|
||||
seed=args.seed,
|
||||
)
|
||||
|
||||
meta = result.get("metadata", {})
|
||||
print(f"Generated: {meta.get('success_count', '?')} success, {meta.get('failed_count', '?')} failed")
|
||||
|
||||
if args.base64:
|
||||
images = result.get("data", {}).get("image_base64", [])
|
||||
import base64
|
||||
for i, b64 in enumerate(images):
|
||||
path = args.output if len(images) == 1 else _numbered_path(args.output, i)
|
||||
raw = base64.b64decode(b64)
|
||||
with open(path, "wb") as f:
|
||||
f.write(raw)
|
||||
print(f"OK: {len(raw)} bytes -> {path}")
|
||||
else:
|
||||
urls = result.get("data", {}).get("image_urls", [])
|
||||
for i, url in enumerate(urls):
|
||||
path = args.output if len(urls) == 1 else _numbered_path(args.output, i)
|
||||
size = download_and_save(url, path)
|
||||
print(f"OK: {size} bytes -> {path}")
|
||||
|
||||
|
||||
def _numbered_path(path: str, index: int) -> str:
|
||||
"""Insert index before extension: out.png -> out-0.png"""
|
||||
base, ext = os.path.splitext(path)
|
||||
return f"{base}-{index}{ext}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user