Graidoai / app.py
Emalawi19's picture
Rename app (1).py to app.py
e6886f4 verified
Raw
History Blame Contribute Delete
9.83 kB
"""
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
# ── CONFIG ───────────────────────────────────────────────────────────────────
MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
LOG_FILE = "generation_logs.txt"
# ── LOGGING ──────────────────────────────────────────────────────────────────
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)
# ── MODEL LOADING ────────────────────────────────────────────────────────────
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})
# ── PROMPT CLASSIFIER ────────────────────────────────────────────────────────
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"
# ── SYSTEM PROMPT BUILDER ────────────────────────────────────────────────────
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."""
# prompt_type == "coding"
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."""
# ── TOKEN BUDGET PER TYPE ────────────────────────────────────────────────────
def token_budget(prompt_type: str) -> int:
return {"identity": 200, "general": 500, "coding": 2000}.get(prompt_type, 500)
# ── STREAMING GENERATION ─────────────────────────────────────────────────────
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],
})
# Build the chat messages: system + prior turns + new user message
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()
# Repair unclosed code blocks, same as the original Node.js chunk repair
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),
})
# ── GRADIO UI ─────────────────────────────────────────────────────────────────
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()