Spaces:
Sleeping
Sleeping
| import os | |
| import io | |
| import json | |
| import uuid | |
| import zipfile | |
| import traceback | |
| from pathlib import Path | |
| from typing import Optional | |
| from fastapi import FastAPI, Request, Form, HTTPException | |
| from fastapi.responses import HTMLResponse, StreamingResponse, JSONResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.templating import Jinja2Templates | |
| from app.engine.app_planner import AppPlanner | |
| from app.engine.model_recommender import ModelRecommender | |
| from app.codegen.repo_generator import RepoGenerator | |
| from app.validators.code_checker import CodeChecker | |
| app = FastAPI(title="AutoApp Builder", version="1.0.0") | |
| BASE_DIR = Path(__file__).resolve().parent | |
| GENERATED_DIR = Path("/tmp/autoapp_generated") | |
| GENERATED_DIR.mkdir(parents=True, exist_ok=True) | |
| app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static") | |
| templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) | |
| # In-memory store for generated projects (keyed by session id) | |
| projects: dict = {} | |
| planner = AppPlanner() | |
| recommender = ModelRecommender() | |
| generator = RepoGenerator() | |
| checker = CodeChecker() | |
| async def home(request: Request): | |
| examples = [ | |
| { | |
| "title": "Image Classifier", | |
| "prompt": "Build a Gradio app that classifies images using a pretrained ResNet model. Users upload an image and get top-5 predictions with confidence bars.", | |
| }, | |
| { | |
| "title": "AI Chatbot", | |
| "prompt": "Create a chatbot that uses a Hugging Face language model to have conversations. Include chat history, system prompt configuration, and a clear button.", | |
| }, | |
| { | |
| "title": "Text Summarizer", | |
| "prompt": "Build an app that summarizes long text documents. Let users paste text or upload a .txt file, choose summary length (short/medium/long), and display the result.", | |
| }, | |
| { | |
| "title": "Sentiment Dashboard", | |
| "prompt": "Create an interactive sentiment analysis tool. Users enter text and see sentiment scores (positive/negative/neutral) visualized with charts.", | |
| }, | |
| { | |
| "title": "REST API Service", | |
| "prompt": "Build a Docker-based REST API that serves a text generation model with endpoints for completion, summarization, and translation. Include API docs.", | |
| }, | |
| { | |
| "title": "Portfolio Site", | |
| "prompt": "Create a beautiful static portfolio website for a data scientist. Include sections for projects, skills, publications, and contact info with a dark theme.", | |
| }, | |
| ] | |
| return templates.TemplateResponse( | |
| "home.html", {"request": request, "examples": examples} | |
| ) | |
| async def generate( | |
| request: Request, | |
| prompt: str = Form(...), | |
| sdk_preference: str = Form("auto"), | |
| model_size: str = Form("medium"), | |
| gpu_needed: bool = Form(False), | |
| features: str = Form(""), | |
| ): | |
| try: | |
| # Step 1: Plan the app | |
| plan = planner.analyze(prompt, sdk_preference) | |
| # Step 2: Recommend models | |
| recommended_models = recommender.recommend(plan, model_size, gpu_needed) | |
| plan["recommended_models"] = recommended_models | |
| # Step 3: Parse additional features | |
| feature_list = [f.strip() for f in features.split(",") if f.strip()] | |
| plan["extra_features"] = feature_list | |
| # Step 4: Generate repository files | |
| repo_files = generator.generate(plan, prompt) | |
| # Step 5: Validate the generated code | |
| validation = checker.check(repo_files, plan["sdk"]) | |
| # Step 6: Store the project | |
| project_id = str(uuid.uuid4())[:8] | |
| projects[project_id] = { | |
| "plan": plan, | |
| "files": repo_files, | |
| "validation": validation, | |
| "prompt": prompt, | |
| } | |
| # Build file tree structure | |
| file_tree = _build_file_tree(repo_files) | |
| # Generate architecture diagram | |
| arch_diagram = _generate_arch_diagram(plan) | |
| return templates.TemplateResponse( | |
| "result.html", | |
| { | |
| "request": request, | |
| "project_id": project_id, | |
| "plan": plan, | |
| "files": repo_files, | |
| "file_tree": file_tree, | |
| "validation": validation, | |
| "arch_diagram": arch_diagram, | |
| "prompt": prompt, | |
| }, | |
| ) | |
| except Exception as e: | |
| traceback.print_exc() | |
| return templates.TemplateResponse( | |
| "home.html", | |
| { | |
| "request": request, | |
| "examples": [], | |
| "error": f"Generation failed: {str(e)}. Please try again with a different prompt.", | |
| }, | |
| ) | |
| async def edit_project( | |
| request: Request, | |
| project_id: str = Form(...), | |
| edit_prompt: str = Form(...), | |
| ): | |
| if project_id not in projects: | |
| raise HTTPException(status_code=404, detail="Project not found") | |
| project = projects[project_id] | |
| original_plan = project["plan"] | |
| original_files = project["files"] | |
| try: | |
| # Re-generate with edit instructions | |
| updated_files = generator.edit(original_plan, original_files, edit_prompt) | |
| validation = checker.check(updated_files, original_plan["sdk"]) | |
| project["files"] = updated_files | |
| project["validation"] = validation | |
| file_tree = _build_file_tree(updated_files) | |
| arch_diagram = _generate_arch_diagram(original_plan) | |
| return templates.TemplateResponse( | |
| "result.html", | |
| { | |
| "request": request, | |
| "project_id": project_id, | |
| "plan": original_plan, | |
| "files": updated_files, | |
| "file_tree": file_tree, | |
| "validation": validation, | |
| "arch_diagram": arch_diagram, | |
| "prompt": project["prompt"], | |
| "edit_prompt": edit_prompt, | |
| }, | |
| ) | |
| except Exception as e: | |
| traceback.print_exc() | |
| # Return original project with error | |
| file_tree = _build_file_tree(original_files) | |
| arch_diagram = _generate_arch_diagram(original_plan) | |
| return templates.TemplateResponse( | |
| "result.html", | |
| { | |
| "request": request, | |
| "project_id": project_id, | |
| "plan": original_plan, | |
| "files": original_files, | |
| "file_tree": file_tree, | |
| "validation": project["validation"], | |
| "arch_diagram": arch_diagram, | |
| "prompt": project["prompt"], | |
| "edit_error": f"Edit failed: {str(e)}", | |
| }, | |
| ) | |
| async def download_zip(project_id: str): | |
| if project_id not in projects: | |
| raise HTTPException(status_code=404, detail="Project not found") | |
| project = projects[project_id] | |
| files = project["files"] | |
| plan = project["plan"] | |
| app_name = plan.get("app_name", "my-hf-space") | |
| buf = io.BytesIO() | |
| with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf: | |
| for filepath, content in files.items(): | |
| zf.writestr(f"{app_name}/{filepath}", content) | |
| buf.seek(0) | |
| return StreamingResponse( | |
| buf, | |
| media_type="application/zip", | |
| headers={"Content-Disposition": f'attachment; filename="{app_name}.zip"'}, | |
| ) | |
| async def get_file(project_id: str, filepath: str): | |
| if project_id not in projects: | |
| raise HTTPException(status_code=404, detail="Project not found") | |
| files = projects[project_id]["files"] | |
| if filepath not in files: | |
| raise HTTPException(status_code=404, detail="File not found") | |
| return JSONResponse({"filename": filepath, "content": files[filepath]}) | |
| def _build_file_tree(files: dict) -> list: | |
| """Build a nested file tree structure from flat file dict.""" | |
| tree = [] | |
| dirs_seen = set() | |
| sorted_files = sorted(files.keys()) | |
| for filepath in sorted_files: | |
| parts = filepath.split("/") | |
| # Add directory entries | |
| for i in range(len(parts) - 1): | |
| dir_path = "/".join(parts[: i + 1]) | |
| if dir_path not in dirs_seen: | |
| dirs_seen.add(dir_path) | |
| tree.append( | |
| { | |
| "path": dir_path, | |
| "name": parts[i], | |
| "type": "dir", | |
| "depth": i, | |
| } | |
| ) | |
| # Add file entry | |
| tree.append( | |
| { | |
| "path": filepath, | |
| "name": parts[-1], | |
| "type": "file", | |
| "depth": len(parts) - 1, | |
| } | |
| ) | |
| return tree | |
| def _generate_arch_diagram(plan: dict) -> str: | |
| """Generate ASCII architecture diagram.""" | |
| sdk = plan.get("sdk", "gradio") | |
| app_name = plan.get("app_name", "App") | |
| components = plan.get("components", []) | |
| if sdk == "gradio": | |
| diagram = f""" | |
| +------------------------------------------------------+ | |
| | Hugging Face Spaces | | |
| | +------------------------------------------------+ | | |
| | | {app_name:^30s} | | | |
| | | +------------------------------------------+ | | | |
| | | | Gradio Interface | | | | |
| | | | +------------+ +------------------+ | | | | |
| | | | | Inputs |--->| Processing | | | | | |
| | | | +------------+ | +------------+ | | | | | |
| | | | | | HF Model | | | | | | |
| | | | +------------+ | +------------+ | | | | | |
| | | | | Outputs |<---| | | | | | |
| | | | +------------+ +------------------+ | | | | |
| | | +------------------------------------------+ | | | |
| | +------------------------------------------------+ | | |
| +------------------------------------------------------+""" | |
| elif sdk == "docker": | |
| diagram = f""" | |
| +------------------------------------------------------+ | |
| | Hugging Face Spaces | | |
| | +------------------------------------------------+ | | |
| | | Docker Container | | | |
| | | +------------------------------------------+ | | | |
| | | | {app_name:^30s} | | | | |
| | | | +----------+ +----------+ +---------+ | | | | |
| | | | | FastAPI | | Model | | Utils | | | | | |
| | | | | Routes | | Service | | | | | | | |
| | | | +-----+-----+ +----+-----+ +---------+ | | | | |
| | | | | | | | | | |
| | | | +-----v--------------v-----------------+ | | | | |
| | | | | API Endpoints | | | | | |
| | | | +---------------------------------------+ | | | | |
| | | +------------------------------------------+ | | | |
| | +------------------------------------------------+ | | |
| +------------------------------------------------------+""" | |
| else: | |
| diagram = f""" | |
| +------------------------------------------------------+ | |
| | Hugging Face Spaces | | |
| | +------------------------------------------------+ | | |
| | | Static Site | | | |
| | | +------------------------------------------+ | | | |
| | | | {app_name:^30s} | | | | |
| | | | +----------+ +----------+ +---------+ | | | | |
| | | | | HTML | | CSS | | JS | | | | | |
| | | | | Pages | | Styles | | Scripts | | | | | |
| | | | +----------+ +----------+ +---------+ | | | | |
| | | +------------------------------------------+ | | | |
| | +------------------------------------------------+ | | |
| +------------------------------------------------------+""" | |
| return diagram | |