返回 ppt-master
backend_stability.py
根目录 / skills / ppt-master / scripts / image_backends / backend_stability.py
1 #!/usr/bin/env python3
2 """
3 Stability AI image generation backend.
4
5 Configuration keys:
6 STABILITY_API_KEY (required)
7 STABILITY_BASE_URL (optional)
8 STABILITY_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 stability")
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 http_error,
35 is_rate_limit_error,
36 report_resolution,
37 require_api_key,
38 resolve_output_path,
39 retry_delay,
40 )
41
42
43 VALID_ASPECT_RATIOS = [
44 "1:1", "2:3", "3:2", "3:4", "4:3",
45 "4:5", "5:4", "9:16", "16:9", "21:9",
46 ]
47
48 DEFAULT_BASE_URL = "https://api.stability.ai"
49 DEFAULT_MODEL = "stable-image-core"
50
51 MODEL_ENDPOINTS = {
52 "core": "/v2beta/stable-image/generate/core",
53 "stable-image-core": "/v2beta/stable-image/generate/core",
54 "ultra": "/v2beta/stable-image/generate/ultra",
55 "stable-image-ultra": "/v2beta/stable-image/generate/ultra",
56 }
57
58
59 def _resolve_endpoint(model: str, image_size: str, base_url: str) -> tuple[str, str]:
60 """Resolve the Stability model alias and endpoint URL."""
61 resolved_model = model or DEFAULT_MODEL
62 if not model and image_size.upper() in ("2K", "4K"):
63 resolved_model = "stable-image-ultra"
64
65 normalized_model = resolved_model.lower()
66 endpoint = MODEL_ENDPOINTS.get(normalized_model)
67 if not endpoint:
68 supported = sorted(MODEL_ENDPOINTS)
69 raise ValueError(
70 f"Unsupported Stability model '{resolved_model}'. Supported aliases: {supported}"
71 )
72 return normalized_model, base_url.rstrip("/") + endpoint
73
74
75 def _generate_image(api_key: str, prompt: str,
76 aspect_ratio: str = "1:1", image_size: str = "1K",
77 output_dir: str = None, filename: str = None,
78 model: str = DEFAULT_MODEL, base_url: str = DEFAULT_BASE_URL) -> str:
79 """Generate one image with the Stability backend."""
80 if aspect_ratio not in VALID_ASPECT_RATIOS:
81 raise ValueError(
82 f"Unsupported aspect ratio '{aspect_ratio}' for Stability backend. "
83 f"Supported: {VALID_ASPECT_RATIOS}"
84 )
85
86 resolved_model, url = _resolve_endpoint(model, image_size, base_url)
87 headers = {
88 "Authorization": f"Bearer {api_key}",
89 "Accept": "image/*",
90 }
91 data = {
92 "prompt": prompt,
93 "aspect_ratio": aspect_ratio,
94 "output_format": "png",
95 }
96
97 print("[Stability AI]")
98 print(f" Model: {resolved_model}")
99 print(f" Prompt: {prompt[:120]}{'...' if len(prompt) > 120 else ''}")
100 print(f" Aspect Ratio: {aspect_ratio}")
101 print(f" Preset Size: {image_size}")
102 print()
103 print(" [..] Generating...", end="", flush=True)
104 start = time.time()
105
106 response = requests.post(url, headers=headers, data=data, timeout=300)
107 elapsed = time.time() - start
108 print(f"\n [DONE] Response received ({elapsed:.1f}s)")
109
110 if response.status_code != 200:
111 raise http_error(response, "Stability generation")
112
113 path = resolve_output_path(prompt, output_dir, filename, ".png")
114 with open(path, "wb") as f:
115 f.write(response.content)
116 print(f" File saved to: {path}")
117 report_resolution(path)
118 return path
119
120
121 def generate(prompt: str,
122 aspect_ratio: str = "1:1", image_size: str = "1K",
123 output_dir: str = None, filename: str = None,
124 model: str = None, max_retries: int = MAX_RETRIES) -> str:
125 """Generate an image with retries using the Stability backend."""
126 api_key = require_api_key(
127 "STABILITY_API_KEY",
128 message="No API key found. Set STABILITY_API_KEY in the current environment or a .env file.",
129 )
130 base_url = os.environ.get("STABILITY_BASE_URL") or DEFAULT_BASE_URL
131 resolved_model = model or os.environ.get("STABILITY_MODEL") or DEFAULT_MODEL
132
133 last_error = None
134 for attempt in range(max_retries + 1):
135 try:
136 return _generate_image(
137 api_key=api_key,
138 prompt=prompt,
139 aspect_ratio=aspect_ratio,
140 image_size=image_size,
141 output_dir=output_dir,
142 filename=filename,
143 model=resolved_model,
144 base_url=base_url,
145 )
146 except Exception as exc:
147 last_error = exc
148 if attempt >= max_retries:
149 break
150 limited = is_rate_limit_error(exc)
151 delay = retry_delay(attempt, rate_limited=limited)
152 label = "Rate limit hit" if limited else f"Error: {exc}"
153 print(f"\n [WARN] {label}. Retrying in {delay}s...")
154 time.sleep(delay)
155
156 raise RuntimeError(f"Failed after {max_retries + 1} attempts. Last error: {last_error}")
157
157 lines PYTHON