Spaces:
Sleeping
Sleeping
| """Plan an application from a prompt using a real Hugging Face LLM. | |
| Returns a structured plan including the actual source files for a deployable | |
| Hugging Face Space. Falls back to a deterministic heuristic plan if the LLM is | |
| unavailable (so the builder never hard-fails). | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import re | |
| from app.inference import InferenceError, chat | |
| SYSTEM_PROMPT = ( | |
| "You are a senior engineer that scaffolds complete, deployable Hugging Face " | |
| "Spaces from a product description. You output STRICT JSON only — no prose, " | |
| "no markdown fences." | |
| ) | |
| USER_TEMPLATE = """Design a deployable Hugging Face Space for this idea: | |
| \"\"\"{prompt}\"\"\" | |
| Preferred SDK: {preferred_sdk} (if "auto", pick the best of: gradio, docker, static) | |
| Return STRICT JSON with EXACTLY this shape: | |
| {{ | |
| "app_name": "kebab-case-name", | |
| "selected_sdk": "gradio|docker|static", | |
| "summary": "2-3 sentence description of what the app does", | |
| "features": ["feature 1", "feature 2", "feature 3"], | |
| "files": [ | |
| {{"path": "README.md", "content": "...full file content..."}}, | |
| {{"path": "app.py", "content": "..."}}, | |
| {{"path": "requirements.txt", "content": "..."}} | |
| ] | |
| }} | |
| Requirements for the generated files: | |
| - Include a README.md whose top is a YAML metadata block (---) with title, emoji, | |
| colorFrom, colorTo, sdk, and (app_file for gradio/static, or app_port: 7860 for docker). | |
| - For gradio: provide app.py (a runnable gradio app) and requirements.txt. | |
| - For docker: provide Dockerfile exposing 7860, app.py, requirements.txt. | |
| - For static: provide index.html. | |
| - Code must be COMPLETE and runnable, not pseudo-code. Keep it focused and self-contained. | |
| - Prefer free Hugging Face inference (huggingface_hub InferenceClient) when the app needs AI. | |
| - Output JSON only. | |
| """ | |
| def _slugify(text: str) -> str: | |
| text = re.sub(r"[^a-zA-Z0-9]+", "-", text.strip().lower()).strip("-") | |
| return text[:50] or "generated-space" | |
| def _extract_json(text: str) -> dict: | |
| text = text.strip() | |
| text = re.sub(r"^```(?:json)?", "", text).strip() | |
| text = re.sub(r"```$", "", text).strip() | |
| start = text.find("{") | |
| end = text.rfind("}") | |
| if start == -1 or end == -1: | |
| raise ValueError("No JSON object in model output.") | |
| return json.loads(text[start : end + 1]) | |
| def _heuristic_plan(prompt: str, preferred_sdk: str) -> dict: | |
| prompt_lower = prompt.lower() | |
| selected_sdk = preferred_sdk | |
| if preferred_sdk == "auto": | |
| if any(k in prompt_lower for k in ["stream", "agent", "api", "sandbox", "workflow"]): | |
| selected_sdk = "docker" | |
| elif any(k in prompt_lower for k in ["landing page", "portfolio", "static"]): | |
| selected_sdk = "static" | |
| else: | |
| selected_sdk = "gradio" | |
| return { | |
| "prompt": prompt, | |
| "app_name": _slugify(prompt.split(".")[0][:60]), | |
| "selected_sdk": selected_sdk, | |
| "summary": f"Starter scaffold for: {prompt}", | |
| "features": [ | |
| "configurable UI scaffold", | |
| "placeholder business logic", | |
| "Hugging Face Space metadata", | |
| "local development instructions", | |
| ], | |
| "files": [], # signals repo_generator to use built-in templates | |
| "source": "fallback", | |
| } | |
| _VALID_SDKS = {"gradio", "docker", "static"} | |
| def plan_application(prompt: str, preferred_sdk: str = "auto") -> dict: | |
| if not prompt or not prompt.strip(): | |
| return _heuristic_plan("Untitled app", preferred_sdk) | |
| messages = [ | |
| {"role": "system", "content": SYSTEM_PROMPT}, | |
| {"role": "user", "content": USER_TEMPLATE.format(prompt=prompt, preferred_sdk=preferred_sdk)}, | |
| ] | |
| try: | |
| raw = chat(messages, max_tokens=2600, temperature=0.4) | |
| data = _extract_json(raw) | |
| sdk = str(data.get("selected_sdk", "gradio")).lower().strip() | |
| if preferred_sdk in _VALID_SDKS: | |
| sdk = preferred_sdk | |
| if sdk not in _VALID_SDKS: | |
| sdk = "gradio" | |
| files = [] | |
| for f in data.get("files", []) or []: | |
| path = str(f.get("path", "")).strip() | |
| content = f.get("content", "") | |
| if path and isinstance(content, str) and content.strip(): | |
| files.append({"path": path, "content": content}) | |
| return { | |
| "prompt": prompt, | |
| "app_name": _slugify(str(data.get("app_name") or prompt.split(".")[0])), | |
| "selected_sdk": sdk, | |
| "summary": str(data.get("summary", "")).strip() or f"Generated app for: {prompt}", | |
| "features": [str(x) for x in (data.get("features") or [])][:8], | |
| "files": files, | |
| "source": "llm", | |
| } | |
| except InferenceError: | |
| raise | |
| except Exception: | |
| return _heuristic_plan(prompt, preferred_sdk) | |