返回 ppt-master
backend_ideogram.py
根目录 / skills / ppt-master / scripts / image_backends / backend_ideogram.py
1 #!/usr/bin/env python3
2 """
3 Ideogram image generation backend.
4
5 Configuration keys:
6 IDEOGRAM_API_KEY (required)
7 IDEOGRAM_BASE_URL (optional)
8 IDEOGRAM_MODEL (optional)
9 """
10
11 import sys
12 from pathlib import Path
13
14 _SCRIPTS_DIR = Path(__file__).resolve().parents[1]
15 if str(_SCRIPTS_DIR) not in sys.path:
16 sys.path.insert(0, str(_SCRIPTS_DIR))
17
18 from console_encoding import configure_utf8_stdio # noqa: E402
19
20 configure_utf8_stdio()
21
22 if __name__ == "__main__":
23 print(__doc__)
24 print("Use via: python3 skills/ppt-master/scripts/image_gen.py \"prompt\" --backend ideogram")
25 raise SystemExit(0 if any(arg in {"-h", "--help", "help"} for arg in sys.argv[1:]) else 1)
26
27 import os
28 import time
29
30 import requests
31
32 from image_backends.backend_common import (
33 MAX_RETRIES,
34 download_image,
35 http_error,
36 is_rate_limit_error,
37 require_api_key,
38 resolve_output_path,
39 retry_delay,
40 )
41
42
43 ASPECT_RATIO_MAP = {
44 "1:1": "1x1",
45 "1:4": "1x4",
46 "2:3": "2x3",
47 "3:2": "3x2",
48 "3:4": "3x4",
49 "4:1": "4x1",
50 "4:3": "4x3",
51 "4:5": "4x5",
52 "5:4": "5x4",
53 "9:16": "9x16",
54 "16:9": "16x9",
55 "21:9": "21x9",
56 }
57
58 DEFAULT_BASE_URL = "https://api.ideogram.ai"
59 DEFAULT_MODEL = "ideogram-v3"
60 MODEL_ALIASES = {"ideogram-v3", "v3"}
61
62 IMAGE_SIZE_TO_SPEED = {
63 "512px": "TURBO",
64 "1K": "DEFAULT",
65 "2K": "QUALITY",
66 "4K": "QUALITY",
67 }
68
69
70 def _resolve_url(base_url: str) -> str:
71 """Resolve the Ideogram generation endpoint."""
72 base = base_url.rstrip("/")
73 if base.endswith("/generate"):
74 return base
75 return base + "/v1/ideogram-v3/generate"
76
77
78 def _generate_image(api_key: str, prompt: str,
79 aspect_ratio: str = "1:1", image_size: str = "1K",
80 output_dir: str = None, filename: str = None,
81 model: str = DEFAULT_MODEL, base_url: str = DEFAULT_BASE_URL) -> str:
82 """Generate one image with the Ideogram backend."""
83 normalized_model = model.strip().lower()
84 if normalized_model not in MODEL_ALIASES:
85 raise ValueError(
86 f"Unsupported Ideogram model '{model}'. Supported: {sorted(MODEL_ALIASES)}"
87 )
88
89 mapped_ratio = ASPECT_RATIO_MAP.get(aspect_ratio)
90 if not mapped_ratio:
91 raise ValueError(
92 f"Unsupported aspect ratio '{aspect_ratio}' for Ideogram backend. "
93 f"Supported: {sorted(ASPECT_RATIO_MAP)}"
94 )
95
96 rendering_speed = IMAGE_SIZE_TO_SPEED.get(image_size, "DEFAULT")
97 url = _resolve_url(base_url)
98 headers = {"Api-Key": api_key}
99 files = {
100 "prompt": (None, prompt),
101 "aspect_ratio": (None, mapped_ratio),
102 "rendering_speed": (None, rendering_speed),
103 }
104
105 print("[Ideogram]")
106 print(f" Model: {normalized_model}")
107 print(f" Prompt: {prompt[:120]}{'...' if len(prompt) > 120 else ''}")
108 print(f" Aspect Ratio: {aspect_ratio} -> {mapped_ratio}")
109 print(f" Render Speed: {rendering_speed}")
110 print()
111 print(" [..] Generating...", end="", flush=True)
112 start = time.time()
113 response = requests.post(url, headers=headers, files=files, timeout=300)
114 elapsed = time.time() - start
115 print(f"\n [DONE] Response received ({elapsed:.1f}s)")
116
117 if response.status_code != 200:
118 raise http_error(response, "Ideogram generation")
119
120 payload = response.json()
121 data = payload.get("data") or []
122 image_url = data[0].get("url") if data else None
123 if not image_url:
124 raise RuntimeError(f"Ideogram response missing image URL: {payload}")
125
126 path = resolve_output_path(prompt, output_dir, filename, ".png")
127 return download_image(image_url, path)
128
129
130 def generate(prompt: str,
131 aspect_ratio: str = "1:1", image_size: str = "1K",
132 output_dir: str = None, filename: str = None,
133 model: str = None, max_retries: int = MAX_RETRIES) -> str:
134 """Generate an image with retries using the Ideogram backend."""
135 api_key = require_api_key(
136 "IDEOGRAM_API_KEY",
137 message="No API key found. Set IDEOGRAM_API_KEY in the current environment or a .env file.",
138 )
139 base_url = os.environ.get("IDEOGRAM_BASE_URL") or DEFAULT_BASE_URL
140 resolved_model = model or os.environ.get("IDEOGRAM_MODEL") or DEFAULT_MODEL
141
142 last_error = None
143 for attempt in range(max_retries + 1):
144 try:
145 return _generate_image(
146 api_key=api_key,
147 prompt=prompt,
148 aspect_ratio=aspect_ratio,
149 image_size=image_size,
150 output_dir=output_dir,
151 filename=filename,
152 model=resolved_model,
153 base_url=base_url,
154 )
155 except Exception as exc:
156 last_error = exc
157 if attempt >= max_retries:
158 break
159 limited = is_rate_limit_error(exc)
160 delay = retry_delay(attempt, rate_limited=limited)
161 label = "Rate limit hit" if limited else f"Error: {exc}"
162 print(f"\n [WARN] {label}. Retrying in {delay}s...")
163 time.sleep(delay)
164
165 raise RuntimeError(f"Failed after {max_retries + 1} attempts. Last error: {last_error}")
166
166 lines PYTHON