| """ |
| Gini AI - Full-stack web development assistant |
| Gradio version of the original JS-Coder-Backend (Node.js + @huggingface/transformers) |
| Created by Emalawi19. Founded by Professor Roosevelt Chinkwende. |
| """ |
|
|
| import threading |
| import time |
| from datetime import datetime |
|
|
| import gradio as gr |
| import torch |
| from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer |
|
|
| |
| MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct" |
| LOG_FILE = "generation_logs.txt" |
|
|
| |
| def log(event: str, details: dict | None = None): |
| details = details or {} |
| timestamp = datetime.utcnow().isoformat() |
| entry = f"[{timestamp}] {event.upper()} - {details}\n" |
| print(entry, end="") |
| with open(LOG_FILE, "a", encoding="utf-8") as f: |
| f.write(entry) |
|
|
|
|
| |
| log("system", {"message": "Loading model..."}) |
|
|
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) |
| model = AutoModelForCausalLM.from_pretrained( |
| MODEL_NAME, |
| torch_dtype=torch.float16 if device == "cuda" else torch.float32, |
| ).to(device) |
| model.eval() |
|
|
| log("system", {"message": "Model ready!", "device": device}) |
|
|
|
|
| |
| def classify_prompt(prompt: str) -> str: |
| lower = prompt.lower() |
|
|
| identity_words = [ |
| "who are you", "what are you", "your name", "who made you", |
| "who created you", "your company", "your founder", "about you", |
| "introduce yourself", |
| ] |
| if any(w in lower for w in identity_words): |
| return "identity" |
|
|
| general_words = [ |
| "what is", "what are", "explain", "describe", "tell me about", |
| "history of", "meaning of", "definition", "how does", "why is", |
| "who is", "who was", "when did", "where is", "function of", |
| "functions of", |
| ] |
| if any(w in lower for w in general_words): |
| return "general" |
|
|
| coding_words = [ |
| "generate", "create", "build", "make", "write", "code", "website", |
| "webpage", "page", "app", "application", "login", "register", "form", |
| "html", "css", "javascript", "js", "node", "express", "backend", |
| "frontend", "server", "api", "function", "script", "component", |
| "template", "dashboard", "portfolio", "ecommerce", "shop", "blog", |
| "landing page", "navbar", "footer", "button", "database", |
| ] |
| if any(w in lower for w in coding_words): |
| return "coding" |
|
|
| return "general" |
|
|
|
|
| |
| IDENTITY = """You are Gini AI, a full-stack web development AI assistant. |
| - Created by Emalawi19. |
| - Founded by Professor Roosevelt Chinkwende. |
| - You are an AI assistant, not a human. |
| - When asked your name: "I am Gini AI." |
| - When asked who made you: "I was created by Emalawi19." |
| - When asked about the founder: "Emalawi19 was founded by Professor Roosevelt Chinkwende.".""" |
|
|
|
|
| def build_system_prompt(prompt_type: str) -> str: |
| if prompt_type == "identity": |
| return f"""{IDENTITY} |
| Introduce yourself clearly and warmly. State your name, your purpose, your company, and your founder.""" |
|
|
| if prompt_type == "general": |
| return f"""{IDENTITY} |
| GENERAL BEHAVIOR: |
| - Answer the question directly and clearly in plain English. |
| - Be concise and helpful. |
| - Do NOT generate any code for non-coding questions. |
| - Do NOT use programming languages to answer everyday questions.""" |
|
|
| |
| return f"""{IDENTITY} |
| |
| YOU ARE A FULL-STACK WEB DEVELOPER AI. FOLLOW THESE RULES STRICTLY: |
| |
| RULE 1 β SINGLE FILE OUTPUT (MOST IMPORTANT): |
| When asked to build any website, webpage, app, or UI component: |
| - ALWAYS output ONE single complete HTML file. |
| - That single file MUST contain ALL HTML structure, ALL CSS styles, and ALL JavaScript β nothing external. |
| - ALL CSS goes inside a <style> tag inside <head>. |
| - ALL JavaScript goes inside a <script> tag at the bottom of <body>. |
| - NEVER say "create a separate CSS file" or "create a separate JS file". |
| - NEVER split code across multiple files unless the user specifically asks for backend server code. |
| |
| RULE 2 β COMPLETE CODE ONLY: |
| - The file must be 100% complete and copy-paste ready. |
| - Start ALWAYS with <!DOCTYPE html> on the very first line. |
| - End ALWAYS with </body> then </html> as the absolute last lines. |
| - NEVER use placeholders like "add your code here" or "// TODO". |
| - NEVER truncate or cut off. Always finish the complete file. |
| |
| RULE 3 β CODE QUALITY: |
| - Write clean, modern, well-commented HTML/CSS/JS. |
| - Use responsive design (flexbox or grid, mobile-friendly). |
| - Include hover effects, smooth transitions, and professional styling. |
| - Use CSS custom properties (variables) for colors and theming. |
| - JavaScript must be functional β forms should validate, buttons should work. |
| |
| RULE 4 β BACKEND REQUESTS: |
| - If the user asks for a backend (Node.js, Express, API, server), provide it as a SEPARATE clearly labeled code block AFTER the frontend file. |
| - Backend code goes in a ```javascript block labeled "server.js". |
| - Still provide the complete frontend HTML file first. |
| |
| RULE 5 β STEP BY STEP (only for complex multi-feature apps): |
| - If building a complex app, give a brief overview of what the file contains BEFORE the code. |
| - Then provide the single complete file. |
| - End with: "This is the complete file. Copy and save it as index.html and open in your browser." |
| |
| RULE 6 β FORMAT: |
| - Always wrap the HTML file in a ```html code block. |
| - Always wrap any JS server code in a ```javascript code block. |
| - Add a short explanation after the code of what was built and how to use it.""" |
|
|
|
|
| |
| def token_budget(prompt_type: str) -> int: |
| return {"identity": 200, "general": 500, "coding": 2000}.get(prompt_type, 500) |
|
|
|
|
| |
| def respond(message: str, history: list[dict]): |
| """ |
| Gradio ChatInterface streaming handler (type="messages"). |
| history is a list of {"role": ..., "content": ...} dicts of prior turns. |
| """ |
| if not message or not message.strip(): |
| yield "Please enter a prompt." |
| return |
|
|
| prompt_type = classify_prompt(message) |
| system_prompt = build_system_prompt(prompt_type) |
| max_new_tokens = token_budget(prompt_type) |
|
|
| log("job_started", { |
| "promptType": prompt_type, |
| "maxTokens": max_new_tokens, |
| "prompt": message[:80], |
| }) |
|
|
| |
| messages = [{"role": "system", "content": system_prompt}] |
| for turn in history: |
| if turn.get("role") in ("user", "assistant") and turn.get("content"): |
| messages.append({"role": turn["role"], "content": turn["content"]}) |
| messages.append({"role": "user", "content": message}) |
|
|
| input_ids = tokenizer.apply_chat_template( |
| messages, add_generation_prompt=True, return_tensors="pt" |
| ).to(device) |
|
|
| streamer = TextIteratorStreamer( |
| tokenizer, skip_prompt=True, skip_special_tokens=True |
| ) |
|
|
| generation_kwargs = dict( |
| input_ids=input_ids, |
| max_new_tokens=max_new_tokens, |
| temperature=0.2, |
| do_sample=False, |
| repetition_penalty=1.15, |
| streamer=streamer, |
| ) |
|
|
| start_time = time.time() |
| thread = threading.Thread(target=model.generate, kwargs=generation_kwargs) |
| thread.start() |
|
|
| partial_text = "" |
| for new_text in streamer: |
| partial_text += new_text |
| yield partial_text |
|
|
| thread.join() |
|
|
| |
| open_blocks = partial_text.count("```") |
| if open_blocks % 2 != 0: |
| partial_text += "\n```" |
| yield partial_text |
|
|
| duration = time.time() - start_time |
| log("job_completed", { |
| "promptType": prompt_type, |
| "durationSec": round(duration, 2), |
| "outputChars": len(partial_text), |
| }) |
|
|
|
|
| |
| demo = gr.ChatInterface( |
| fn=respond, |
| type="messages", |
| title="Gini AI β Full-Stack Web Development Assistant", |
| description=( |
| "Built by **Emalawi19** Β· Founded by **Professor Roosevelt Chinkwende**. " |
| "Ask Gini AI to build a website, explain a concept, or just say hi." |
| ), |
| examples=[ |
| "Who are you?", |
| "Build a responsive login page with a gradient background", |
| "Explain how a REST API works", |
| ], |
| theme=gr.themes.Soft(primary_hue="purple"), |
| ) |
|
|
| if __name__ == "__main__": |
| demo.queue().launch() |
|
|