Spaces:
Running on Zero
Running on Zero
Show Hugging Face model errors
Browse files
app.py
CHANGED
|
@@ -11,6 +11,7 @@ import time
|
|
| 11 |
from dataclasses import dataclass
|
| 12 |
from html import escape
|
| 13 |
from urllib.parse import quote
|
|
|
|
| 14 |
from urllib.request import Request, urlopen
|
| 15 |
|
| 16 |
import gradio as gr
|
|
@@ -248,9 +249,18 @@ def extract_json_object(text: str) -> dict | None:
|
|
| 248 |
return value if isinstance(value, dict) else None
|
| 249 |
|
| 250 |
|
| 251 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
if not HF_TOKEN:
|
| 253 |
-
return None
|
| 254 |
|
| 255 |
role_block = "\n".join(f"- {role}: {prompt}" for role, prompt in role_lines)
|
| 256 |
instruction = (
|
|
@@ -299,23 +309,23 @@ def hf_prompt_json(html_code: str, role_lines: list[tuple[str, str]], style_hint
|
|
| 299 |
text = str(parsed)
|
| 300 |
obj = extract_json_object(text)
|
| 301 |
if not obj:
|
| 302 |
-
return None
|
| 303 |
return {
|
| 304 |
role: str(obj.get(role, "")).strip()
|
| 305 |
for role, _ in role_lines
|
| 306 |
if str(obj.get(role, "")).strip()
|
| 307 |
-
}
|
| 308 |
-
except Exception:
|
| 309 |
-
return None
|
| 310 |
|
| 311 |
|
| 312 |
-
def build_prompt_map(html_code: str, raw_roles: str, style_hint: str) -> tuple[list[tuple[str, str]], dict[str, str], str]:
|
| 313 |
role_lines = parse_role_lines(raw_roles)
|
| 314 |
local_map = local_prompt_map(role_lines, style_hint)
|
| 315 |
-
ai_map = hf_prompt_json(html_code, role_lines, style_hint)
|
| 316 |
if ai_map and all(role in ai_map for role, _ in role_lines):
|
| 317 |
-
return role_lines, ai_map, HF_PROMPT_MODEL
|
| 318 |
-
return role_lines, local_map, "local prompt interpreter"
|
| 319 |
|
| 320 |
|
| 321 |
def parse_assets(raw_roles: str, style_hint: str, prompt_map: dict[str, str] | None = None) -> list[AssetSpec]:
|
|
@@ -961,9 +971,9 @@ def placeholder_png_bytes(role: str, width: int, height: int) -> bytes:
|
|
| 961 |
return out.getvalue()
|
| 962 |
|
| 963 |
|
| 964 |
-
def hf_image_png(spec: AssetSpec, index: int, run_id: int) -> bytes | None:
|
| 965 |
if not HF_TOKEN:
|
| 966 |
-
return None
|
| 967 |
|
| 968 |
payload = {
|
| 969 |
"inputs": spec.prompt,
|
|
@@ -991,14 +1001,15 @@ def hf_image_png(spec: AssetSpec, index: int, run_id: int) -> bytes | None:
|
|
| 991 |
content_type = response.headers.get("content-type", "")
|
| 992 |
content = response.read()
|
| 993 |
if "image" not in content_type:
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
|
|
|
| 998 |
|
| 999 |
|
| 1000 |
def generate_asset(spec: AssetSpec, index: int, run_id: int) -> tuple[str, str, str | None, str]:
|
| 1001 |
-
png_content = hf_image_png(spec, index, run_id)
|
| 1002 |
source = HF_IMAGE_MODEL
|
| 1003 |
if png_content is None:
|
| 1004 |
png_content = local_asset_png(spec, index, run_id)
|
|
@@ -1006,7 +1017,7 @@ def generate_asset(spec: AssetSpec, index: int, run_id: int) -> tuple[str, str,
|
|
| 1006 |
return (
|
| 1007 |
png_bytes_to_data_uri(png_content),
|
| 1008 |
write_gallery_image(png_content, spec.role),
|
| 1009 |
-
|
| 1010 |
source,
|
| 1011 |
)
|
| 1012 |
|
|
@@ -1167,7 +1178,7 @@ def generate_images_and_game(html_code: str, roles: str, style_hint: str):
|
|
| 1167 |
if not html_code.strip():
|
| 1168 |
return "", "Paste HTML game code first.", [], "", "", ""
|
| 1169 |
|
| 1170 |
-
role_lines, prompt_map, prompt_model = build_prompt_map(html_code, roles, style_hint or "pixel art style")
|
| 1171 |
specs = parse_assets(roles, style_hint or "pixel art style", prompt_map)
|
| 1172 |
if not specs:
|
| 1173 |
return html_code, "Add at least one asset role, like `player: brave knight`.", [], "", "", build_preview(html_code)
|
|
@@ -1177,6 +1188,8 @@ def generate_images_and_game(html_code: str, roles: str, style_hint: str):
|
|
| 1177 |
errors = []
|
| 1178 |
model_rows = []
|
| 1179 |
run_id = time.time_ns()
|
|
|
|
|
|
|
| 1180 |
|
| 1181 |
for index, spec in enumerate(specs):
|
| 1182 |
data_uri, gallery_path, error, image_model = generate_asset(spec, index, run_id)
|
|
@@ -1184,7 +1197,7 @@ def generate_images_and_game(html_code: str, roles: str, style_hint: str):
|
|
| 1184 |
gallery.append((gallery_path, f"{spec.role} -> {spec.filename}"))
|
| 1185 |
model_rows.append((spec.role, prompt_model, image_model))
|
| 1186 |
if error:
|
| 1187 |
-
errors.append(f"{spec.role}:
|
| 1188 |
|
| 1189 |
rewritten = embed_assets(html_code, assets, specs)
|
| 1190 |
status = (
|
|
@@ -1192,7 +1205,7 @@ def generate_images_and_game(html_code: str, roles: str, style_hint: str):
|
|
| 1192 |
f"{summarize_model_sources(model_rows)}. Run {str(run_id)[-6:]}."
|
| 1193 |
)
|
| 1194 |
if errors:
|
| 1195 |
-
status += "\n\n" + "\n".join(
|
| 1196 |
return rewritten, status, gallery, build_prompt_preview(specs), build_model_report(model_rows), build_preview(rewritten)
|
| 1197 |
|
| 1198 |
|
|
|
|
| 11 |
from dataclasses import dataclass
|
| 12 |
from html import escape
|
| 13 |
from urllib.parse import quote
|
| 14 |
+
from urllib.error import HTTPError, URLError
|
| 15 |
from urllib.request import Request, urlopen
|
| 16 |
|
| 17 |
import gradio as gr
|
|
|
|
| 249 |
return value if isinstance(value, dict) else None
|
| 250 |
|
| 251 |
|
| 252 |
+
def short_error(exc: Exception) -> str:
|
| 253 |
+
if isinstance(exc, HTTPError):
|
| 254 |
+
body = exc.read().decode("utf-8", errors="replace")[:500]
|
| 255 |
+
return f"HTTP {exc.code}: {body}"
|
| 256 |
+
if isinstance(exc, URLError):
|
| 257 |
+
return f"URL error: {exc.reason}"
|
| 258 |
+
return f"{type(exc).__name__}: {str(exc)[:500]}"
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def hf_prompt_json(html_code: str, role_lines: list[tuple[str, str]], style_hint: str) -> tuple[dict[str, str] | None, str | None]:
|
| 262 |
if not HF_TOKEN:
|
| 263 |
+
return None, "HF_TOKEN is not visible to the Space runtime"
|
| 264 |
|
| 265 |
role_block = "\n".join(f"- {role}: {prompt}" for role, prompt in role_lines)
|
| 266 |
instruction = (
|
|
|
|
| 309 |
text = str(parsed)
|
| 310 |
obj = extract_json_object(text)
|
| 311 |
if not obj:
|
| 312 |
+
return None, f"{HF_PROMPT_MODEL} returned non-JSON text: {text[:300]}"
|
| 313 |
return {
|
| 314 |
role: str(obj.get(role, "")).strip()
|
| 315 |
for role, _ in role_lines
|
| 316 |
if str(obj.get(role, "")).strip()
|
| 317 |
+
}, None
|
| 318 |
+
except Exception as exc:
|
| 319 |
+
return None, short_error(exc)
|
| 320 |
|
| 321 |
|
| 322 |
+
def build_prompt_map(html_code: str, raw_roles: str, style_hint: str) -> tuple[list[tuple[str, str]], dict[str, str], str, str | None]:
|
| 323 |
role_lines = parse_role_lines(raw_roles)
|
| 324 |
local_map = local_prompt_map(role_lines, style_hint)
|
| 325 |
+
ai_map, prompt_error = hf_prompt_json(html_code, role_lines, style_hint)
|
| 326 |
if ai_map and all(role in ai_map for role, _ in role_lines):
|
| 327 |
+
return role_lines, ai_map, HF_PROMPT_MODEL, None
|
| 328 |
+
return role_lines, local_map, "local prompt interpreter", prompt_error
|
| 329 |
|
| 330 |
|
| 331 |
def parse_assets(raw_roles: str, style_hint: str, prompt_map: dict[str, str] | None = None) -> list[AssetSpec]:
|
|
|
|
| 971 |
return out.getvalue()
|
| 972 |
|
| 973 |
|
| 974 |
+
def hf_image_png(spec: AssetSpec, index: int, run_id: int) -> tuple[bytes | None, str | None]:
|
| 975 |
if not HF_TOKEN:
|
| 976 |
+
return None, "HF_TOKEN is not visible to the Space runtime"
|
| 977 |
|
| 978 |
payload = {
|
| 979 |
"inputs": spec.prompt,
|
|
|
|
| 1001 |
content_type = response.headers.get("content-type", "")
|
| 1002 |
content = response.read()
|
| 1003 |
if "image" not in content_type:
|
| 1004 |
+
preview = content.decode("utf-8", errors="replace")[:500]
|
| 1005 |
+
return None, f"{HF_IMAGE_MODEL} returned {content_type or 'non-image response'}: {preview}"
|
| 1006 |
+
return image_to_png_bytes(content, spec.width, spec.height), None
|
| 1007 |
+
except Exception as exc:
|
| 1008 |
+
return None, short_error(exc)
|
| 1009 |
|
| 1010 |
|
| 1011 |
def generate_asset(spec: AssetSpec, index: int, run_id: int) -> tuple[str, str, str | None, str]:
|
| 1012 |
+
png_content, hf_error = hf_image_png(spec, index, run_id)
|
| 1013 |
source = HF_IMAGE_MODEL
|
| 1014 |
if png_content is None:
|
| 1015 |
png_content = local_asset_png(spec, index, run_id)
|
|
|
|
| 1017 |
return (
|
| 1018 |
png_bytes_to_data_uri(png_content),
|
| 1019 |
write_gallery_image(png_content, spec.role),
|
| 1020 |
+
hf_error if source == "local style fallback" else None,
|
| 1021 |
source,
|
| 1022 |
)
|
| 1023 |
|
|
|
|
| 1178 |
if not html_code.strip():
|
| 1179 |
return "", "Paste HTML game code first.", [], "", "", ""
|
| 1180 |
|
| 1181 |
+
role_lines, prompt_map, prompt_model, prompt_error = build_prompt_map(html_code, roles, style_hint or "pixel art style")
|
| 1182 |
specs = parse_assets(roles, style_hint or "pixel art style", prompt_map)
|
| 1183 |
if not specs:
|
| 1184 |
return html_code, "Add at least one asset role, like `player: brave knight`.", [], "", "", build_preview(html_code)
|
|
|
|
| 1188 |
errors = []
|
| 1189 |
model_rows = []
|
| 1190 |
run_id = time.time_ns()
|
| 1191 |
+
if prompt_error:
|
| 1192 |
+
errors.append(f"prompt model: {prompt_error}")
|
| 1193 |
|
| 1194 |
for index, spec in enumerate(specs):
|
| 1195 |
data_uri, gallery_path, error, image_model = generate_asset(spec, index, run_id)
|
|
|
|
| 1197 |
gallery.append((gallery_path, f"{spec.role} -> {spec.filename}"))
|
| 1198 |
model_rows.append((spec.role, prompt_model, image_model))
|
| 1199 |
if error:
|
| 1200 |
+
errors.append(f"{spec.role}: image model failed ({error}); used local style fallback")
|
| 1201 |
|
| 1202 |
rewritten = embed_assets(html_code, assets, specs)
|
| 1203 |
status = (
|
|
|
|
| 1205 |
f"{summarize_model_sources(model_rows)}. Run {str(run_id)[-6:]}."
|
| 1206 |
)
|
| 1207 |
if errors:
|
| 1208 |
+
status += "\n\n" + "\n".join(errors)
|
| 1209 |
return rewritten, status, gallery, build_prompt_preview(specs), build_model_report(model_rows), build_preview(rewritten)
|
| 1210 |
|
| 1211 |
|