{"repo": "blossomz37/blood-assassin-image-suite", "files": {"server.py": {"06ca04c35f2db9476706284537b113de75be96f8": {"t": 1785674356.394766, "n": 5757, "h": "02ba59288b0c5629", "c": "#!/usr/bin/env python3\n\"\"\"\nTiny Flask server to expose image generation over HTTP and serve a static UI.\n\nEndpoints:\n- GET / -> serve web UI\n- POST /api/generate -> generate images from prompt text or uploaded .txt file\n- GET /generated-images/ -> serve generated images\n\"\"\"\n\nfrom __future__ import annotations\n\nimport os\nfrom pathlib import Path\nfrom typing import List, Optional\n\nfrom flask import Flask, jsonify, request, send_from_directory\nfrom flask_cors import CORS\n\n# Local import\nfrom generate_images import generate_image, OUTPUT_DIR, PROMPTS_DIR, OPENROUTER_API_KEY\n\nROOT = Path(__file__).parent\nWEB_DIR = ROOT / \"web\"\nEXPERIMENTS_DIRS = {\n \"phase-4-html-gallery\": ROOT / \"phase-4-html-gallery\",\n \"theme-factory\": ROOT / \"01-theme-factory\",\n \"artifacts-builder\": ROOT / \"02-artifacts-builder\",\n \"visual-assets-package\": ROOT / \"visual-assets-package\",\n \"visual-assets-package-alt\": ROOT / \"04-visual-assets-package\",\n}\n\napp = Flask(__name__, static_folder=str(WEB_DIR), static_url_path=\"\")\nCORS(app)\n\n\n@app.route(\"/\")\ndef index():\n # Serve the static index.html\n index_path = WEB_DIR / \"index.html\"\n if not index_path.exists():\n return \"UI not built. Missing web/index.html\", 404\n return app.send_static_file(\"index.html\")\n\n\n@app.route(\"/experiments\")\n@app.route(\"/experiments/\")\ndef experiments_home():\n # Serve the experiments index page\n exp_index = WEB_DIR / \"experiments\" / \"index.html\"\n if not exp_index.exists():\n return \"Experiments page not found.\", 404\n return send_from_directory(str((WEB_DIR / \"experiments\").resolve()), \"index.html\")\n\n\n@app.route(\"/api/generate\", methods=[\"POST\"])\ndef api_generate():\n \"\"\"\n Accepts either JSON body { prompt: string, name?: string }\n or multipart/form-data with fields:\n - prompt_file: uploaded .txt file (optional)\n - prompt: text (optional)\n - name: base output filename without extension (optional)\n Returns: { images: [\"filename.png\", ...] }\n \"\"\"\n prompt_text: Optional[str] = None\n base_name: str = \"image\"\n\n api_key_override: Optional[str] = None\n if request.content_type and request.content_type.startswith(\"application/json\"):\n data = request.get_json(silent=True) or {}\n prompt_text = (data.get(\"prompt\") or \"\").strip() or None\n base_name = (data.get(\"name\") or base_name).strip() or base_name\n # Optional temporary API key (do not store server-side)\n api_key_override = (data.get(\"apiKey\") or \"\").strip() or None\n else:\n # multipart form\n if \"prompt_file\" in request.files:\n f = request.files[\"prompt_file\"]\n try:\n text = f.read().decode(\"utf-8\", errors=\"ignore\").strip()\n if text:\n prompt_text = text\n # Use the uploaded filename (stem) if provided and name not set\n if f.filename:\n stem = Path(f.filename).stem\n if stem:\n base_name = stem\n except UnicodeDecodeError:\n # Keep going; user may also provide text in textarea\n pass\n form_prompt = (request.form.get(\"prompt\") or \"\").strip()\n if form_prompt:\n prompt_text = form_prompt\n form_name = (request.form.get(\"name\") or \"\").strip()\n if form_name:\n base_name = form_name\n api_key_override = (request.form.get(\"apiKey\") or \"\").strip() or None\n\n # Auth check: allow either env var or a provided temporary key from the request\n has_env_key = bool(OPENROUTER_API_KEY and str(OPENROUTER_API_KEY).startswith(\"sk-or-v1-\"))\n has_temp_key = bool(api_key_override and str(api_key_override).startswith(\"sk-or-v1-\"))\n if not (has_env_key or has_temp_key):\n return jsonify({\n \"error\": \"Missing or invalid API key. Provide OPENROUTER_API_KEY server env var or include a temporary key in the request.\"\n }), 400\n\n if not prompt_text:\n return jsonify({\"error\": \"No prompt provided. Provide JSON {prompt} or upload a .txt file.\"}), 400\n\n images: List[str] = generate_image(prompt_text, base_name, api_key=api_key_override) or []\n\n if not images:\n return jsonify({\"error\": \"No images returned from model.\"}), 502\n\n # Return URLs for the generated files\n urls = [f\"/generated-images/{name}\" for name in images]\n return jsonify({\"images\": images, \"urls\": urls})\n\n\n@app.route('/generated-images/')\ndef serve_generated(filename: str):\n directory = str(OUTPUT_DIR.resolve())\n return send_from_directory(directory, filename, as_attachment=False)\n\n\n@app.route('/image-prompts/')\ndef serve_prompts(filename: str):\n # Optional: expose prompt files for convenience in UI if needed\n directory = str(PROMPTS_DIR.resolve())\n return send_from_directory(directory, filename, as_attachment=False)\n\n\n@app.route('/experiments-src//')\ndef serve_experiment_source(group: str, filename: str):\n \"\"\"\n Serve static experiment HTML files that live outside the web/ directory.\n This allows lightweight wrapper pages under web/experiments to embed them via