import gradio as gr from huggingface_hub import InferenceClient import os import re # For post-processing and parsing # --- Configuration --- API_TOKEN = os.getenv("HF_TOKEN", None) # Consider using a model known for stronger coding capabilities if backend generation is complex MODEL = "Qwen/Qwen2-7B-Instruct" # Example: Switched to a smaller, faster model for potentially better backend handling # MODEL = "Qwen/Qwen2.5-Coder-32B-Instruct" # Or keep your original model # --- Initialize Inference Client --- try: print(f"Attempting to initialize Inference Client for model: {MODEL}") if API_TOKEN: print("Using HF Token found in environment.") client = InferenceClient(model=MODEL, token=API_TOKEN) else: print("HF Token not found. Running without token (may lead to rate limits).") client = InferenceClient(model=MODEL) print("Inference Client initialized successfully.") except Exception as e: print(f"Error initializing Inference Client: {e}") raise gr.Error(f"Failed to initialize the AI model client for '{MODEL}'. Check model name, network, and HF_TOKEN secret if applicable. Error: {e}") # --- Helper Function for Parsing --- def parse_code_blocks(text, file_structure, backend_choice): """Parses the generated text into code blocks based on markers.""" if file_structure == "Single File": # Everything goes into HTML for single file mode return { "html": text.strip(), "css": "/* CSS is embedded in HTML */", "js": "// JavaScript is embedded in HTML", "backend": f"// No backend file generated for 'Single File' mode." } # Default markers markers = { "html": r"", "css": r"/\*\s*style\.css\s*\*/", "js": r"//\s*script\.js\s*//", # Added trailing // to potentially help delimit } # Add backend markers based on choice if backend_choice == "Flask": markers["backend"] = r"#\s*app\.py\s*#" # Using # marker # elif backend_choice == "Node.js": markers["backend"] = r"//\s*(server|app)\.js\s*//" # Using // marker // # Find all marker positions marker_positions = {} for key, pattern in markers.items(): match = re.search(pattern, text, re.IGNORECASE) if match: marker_positions[key] = match.start() # If no markers found, assume it's all HTML (fallback) if not marker_positions: print("Warning: No file markers found in the output. Assuming all content is HTML.") # Check if it looks like CSS or JS first before defaulting to HTML cleaned_text = text.strip() if cleaned_text.startswith(("{", ".", "#", "/*")) and "{" in cleaned_text and "}" in cleaned_text: print("Heuristic: Output looks like CSS.") return {"html": "", "css": cleaned_text, "js": "", "backend": ""} elif cleaned_text.startswith(("function", "const", "let", "var", "//", "import")) and ("(" in cleaned_text or "{" in cleaned_text): print("Heuristic: Output looks like JS.") return {"html": "", "css": "", "js": cleaned_text, "backend": ""} else: # Default to HTML return {"html": cleaned_text, "css": "", "js": "", "backend": ""} # Sort markers by their position sorted_markers = sorted(marker_positions.items(), key=lambda item: item[1]) # Extract code blocks code_blocks = {key: "" for key in markers} # Initialize all keys for i, (key, start_pos) in enumerate(sorted_markers): # Find the start of the code block (after the marker) marker_match = re.search(markers[key], text, re.IGNORECASE) # Find the specific marker text code_start = marker_match.end() # Find the end of the code block (start of the next marker or end of text) if i + 1 < len(sorted_markers): next_marker_key, next_marker_pos = sorted_markers[i+1] code_end = next_marker_pos else: code_end = len(text) # Extract and clean the code code = text[code_start:code_end].strip() code_blocks[key] = code # Fill potential missing keys if they existed in original markers dict final_blocks = { "html": code_blocks.get("html", ""), "css": code_blocks.get("css", ""), "js": code_blocks.get("js", ""), "backend": code_blocks.get("backend", "") } # If backend is static but backend code was somehow generated, clear it if backend_choice == "Static": final_blocks["backend"] = "// No backend file needed for 'Static' mode." # Fallback if HTML is empty but others aren't (marker parsing failed maybe?) if not final_blocks["html"] and (final_blocks["css"] or final_blocks["js"] or final_blocks["backend"]): # Check if the original text looks like HTML if text.strip().startswith("", "/* style.css */", "// script.js //"] # Base markers if backend_choice == "Static": backend_instructions = ( f"- **Backend is '{backend_choice}':** Generate ONLY frontend code (HTML, CSS, JS). Do NOT generate any server-side files or logic.\n" ) file_structure_detail = ( "Generate code for `index.html`, `style.css`, and `script.js` (if JS is needed). " "Use these EXACT markers to separate the files:\n" " ``\n" " `/* style.css */`\n" " `// script.js //` (only include if JS is generated)\n" "- Place the corresponding code directly after each marker.\n" "- Inside `index.html`, link `style.css` in the `
` and include `script.js` before `` if generated." ) elif backend_choice == "Flask": backend_instructions = ( f"- **Backend is '{backend_choice}':** Generate a basic Python Flask application (`app.py`).\n" " - Include necessary imports (`Flask`, `render_template`).\n" " - Create a simple Flask app instance.\n" " - Define a root route (`@app.route('/')`) that renders `index.html`.\n" " - Include the standard `if __name__ == '__main__': app.run(debug=True)` block.\n" "- **HTML Templates:** Modify the generated `index.html` to be a Flask template.\n" " - Use Jinja2 syntax (e.g., `{{ variable }}`) *if* the prompt implies dynamic data, otherwise generate static HTML structure within the template.\n" " - Link CSS using `url_for('static', filename='style.css')`.\n" " - Include JS using `url_for('static', filename='script.js')`.\n" "- Assume CSS and JS are served from a `static` folder (but generate the code for `style.css` and `script.js` directly).\n" ) file_markers.append("# app.py #") # Add Flask marker file_structure_detail = ( "Generate code for `index.html` (as a Flask template), `style.css`, `script.js` (if JS is needed), and `app.py`.\n" "Use these EXACT markers to separate the files:\n" " ``\n" " `/* style.css */`\n" " `// script.js //` (only include if JS is generated)\n" " `# app.py #`\n" "- Place the corresponding code directly after each marker." ) elif backend_choice == "Node.js": backend_instructions = ( f"- **Backend is '{backend_choice}':** Generate a basic Node.js Express application (`server.js` or `app.js`).\n" " - Include necessary requires (`express`, `path`).\n" " - Create an Express app instance.\n" " - Configure middleware to serve static files from a `public` directory (e.g., `app.use(express.static('public'))`).\n" " - Define a root route (`app.get('/')`) that sends `index.html` (located in `public`).\n" " - Start the server (`app.listen(...)`).\n" "- **HTML:** Generate a standard `index.html` file. Link CSS (`/style.css`) and JS (`/script.js`) assuming they are in the `public` folder.\n" ) file_markers.append("// server.js //") # Add Node marker file_structure_detail = ( "Generate code for `index.html`, `style.css`, `script.js` (if JS is needed), and `server.js` (or `app.js`).\n" "Use these EXACT markers to separate the files:\n" " ``\n" " `/* style.css */`\n" " `// script.js //` (only include if JS is generated)\n" " `// server.js //`\n" "- Place the corresponding code directly after each marker." ) # File Structure Instructions if file_structure == "Single File": file_structure_instruction = ( "- **File Structure is 'Single File':** Generate ONLY a single, complete `index.html` file. " "Embed ALL CSS directly within `