mayank23raj's picture
Update app.py
3eaad37 verified
import os
import json
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, StreamingResponse
from huggingface_hub import hf_hub_download
from llama_cpp import Llama
# --- ENGINE SETUP ---
# Using the 1B model for maximum speed on Free Tier
repo_id = "unsloth/Llama-3.2-1B-Instruct-GGUF"
filename = "Llama-3.2-1B-Instruct-Q4_K_M.gguf"
print("🚀 Mahoba Logic: Downloading/Loading Model...")
model_path = hf_hub_download(repo_id=repo_id, filename=filename)
llm = Llama(model_path=model_path, n_ctx=2048, n_threads=4, verbose=False)
print("✅ Engine Ready!")
# --- THE FRONTEND ---
HTML_CONTENT = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mayank's AI Career Guider | The 1% Filter</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
:root { --bg-gradient: linear-gradient(135deg, #6366F1, #7C3AED); --chat-bg: #F9FAFB; --primary: #6D28D9; --secondary: #4F46E5; }
body { margin: 0; font-family: 'Inter', sans-serif; background: var(--bg-gradient); height: 100vh; width: 100vw; display: flex; justify-content: center; align-items: center; overflow: hidden; }
.welcome-screen { position: fixed; inset: 0; background: var(--bg-gradient); display: flex; justify-content: center; align-items: center; z-index: 9999; }
.welcome-box { text-align: center; color: white; max-width: 420px; padding: 30px; }
.welcome-box h1 { font-weight: 800; font-size: 2.5rem; margin-bottom: 10px; }
.welcome-box button, .input-box button { background: white; color: var(--primary); border: none; padding: 14px 28px; border-radius: 12px; font-weight: 600; cursor: pointer; transition: 0.3s; }
.welcome-box button:hover { transform: scale(1.05); box-shadow: 0 10px 20px rgba(0,0,0,0.2); }
.chat-container { width: 90%; max-width: 900px; height: 85vh; background: white; border-radius: 24px; display: flex; flex-direction: column; box-shadow: 0 25px 60px rgba(0,0,0,0.3); overflow: hidden; position: relative; }
.header { padding: 20px; text-align: center; font-weight: 600; color: white; background: linear-gradient(135deg, #4F46E5, #7C3AED); }
#progress-container { padding: 12px 24px; background: #fdfdfd; border-bottom: 1px solid #f0f0f0; }
.chat { flex: 1; background: var(--chat-bg); overflow-y: auto; padding: 25px; display: flex; flex-direction: column; gap: 16px; scroll-behavior: smooth; }
.msg { max-width: 75%; padding: 14px 20px; border-radius: 18px; font-size: 15px; line-height: 1.6; }
.ai { background: white; align-self: flex-start; border: 1px solid #eef0f2; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
.user { background: var(--primary); color: white; align-self: flex-end; }
.input-box { display: flex; padding: 20px; gap: 12px; background: white; border-top: 1px solid #f0f0f0; }
.input-box textarea { flex: 1; padding: 14px; border-radius: 12px; border: 1px solid #e5e7eb; outline: none; resize: none; font-family: inherit; }
</style>
</head>
<body>
<div class="welcome-screen" id="welcomeScreen">
<div class="welcome-box">
<h1>🎯 The 1% Filter</h1>
<p>Stop following the crowd. Build your roadmap for career domination.</p>
<button onclick="startApp()">Enter Mission</button>
</div>
</div>
<div class="chat-container">
<div class="header">🚀 STRATEGIC GUIDANCE ENGINE</div>
<div id="progress-container">
<p id="progress-text" style="font-size: 12px; color: #6b7280;">Mission Progress: 0%</p>
<div style="width: 100%; background: #e5e7eb; border-radius: 10px; height: 8px; overflow: hidden;">
<div id="progress-bar" style="width: 0%; height: 100%; background: var(--secondary); transition: width 1s;"></div>
</div>
</div>
<div class="chat" id="chat"></div>
<div class="input-box">
<textarea id="input" placeholder="Type and press Enter..." rows="1"></textarea>
<button onclick="send()">Send</button>
</div>
</div>
<script>
const chat = document.getElementById("chat");
const input = document.getElementById("input");
let step = 0;
let userData = { name: "", interest: "", progress: 0 };
input.addEventListener("keypress", (e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
send();
}
});
function addMessage(text, type) {
const div = document.createElement("div");
div.className = `msg ${type}`;
div.innerHTML = type === "ai" ? marked.parse(text) : text;
chat.appendChild(div);
chat.scrollTop = chat.scrollHeight;
return div;
}
function startApp() {
document.getElementById("welcomeScreen").style.display = "none";
addMessage("Hey. I'm here to build your roadmap to the 1%. First, what's your name?", "ai");
step = 1;
}
async function send() {
const text = input.value.trim();
if (!text) return;
addMessage(text, "user");
input.value = "";
if (step === 1) {
userData.name = text;
addMessage(`Localized. **${text}**, what career do you want to dominate?`, "ai");
step = 2;
return;
}
if (step === 2) {
userData.interest = text;
addMessage(`Analyzing **${text}**. Ask me your first strategic question.`, "ai");
step = 3;
return;
}
const aiMsgDiv = addMessage("<i>Thinking...</i>", "ai");
try {
const response = await fetch("/guide", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ name: userData.name, interest: userData.interest, followup: text })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = "";
let buffer = "";
aiMsgDiv.innerHTML = ""; // Clear the "Thinking..."
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\\n");
buffer = lines.pop();
for (const line of lines) {
if (!line.trim()) continue;
try {
const data = JSON.parse(line);
fullText += data.token;
aiMsgDiv.innerHTML = marked.parse(fullText);
chat.scrollTop = chat.scrollHeight;
} catch(err) { console.error("JSON split error"); }
}
}
userData.progress = Math.min(userData.progress + 15, 100);
document.getElementById("progress-bar").style.width = userData.progress + "%";
document.getElementById("progress-text").textContent = `Mission Progress: ${userData.progress}%`;
} catch (e) {
aiMsgDiv.innerHTML = "Error connecting to engine. Check logs.";
}
}
</script>
</body>
</html>
"""
# --- BACKEND LOGIC ---
app = FastAPI()
@app.get("/")
async def get_ui():
return HTMLResponse(content=HTML_CONTENT)
@app.post("/guide")
async def guide(request: Request):
data = await request.json()
user_name = data.get("name", "Student")
interest = data.get("interest", "Career")
query = data.get("followup", "")
# Optimized prompt for Llama 3.2 1B
prompt = f"<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\nYou are the 1% Filter AI by Mayank. Helping {user_name} with {interest}. Be short, brutal, and strategic. Focus on real skills, not degrees.<|eot_id|><|start_header_id|>user<|end_header_id|>\n\n{query}<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n"
def stream_generator():
stream = llm(
prompt,
max_tokens=150,
stream=True,
stop=["<|eot_id|>", "User:"]
)
for output in stream:
if "choices" in output:
token = output["choices"][0].get("text", "")
yield json.dumps({"token": token}) + "\n"
return StreamingResponse(
stream_generator(),
media_type="application/x-ndjson",
headers={"Cache-Control": "no-cache", "X-Content-Type-Options": "nosniff"}
)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=7860)