Spaces:
Build error
Build error
| import os | |
| import uuid | |
| import requests | |
| from flask import Flask, request, jsonify, render_template_string | |
| from llama_cpp import Llama | |
| app = Flask(__name__) | |
| # === CONFIGURATION === | |
| MODEL_URL = "https://huggingface.co/CooLLaMACEO/CooLLaMA-Gemma2/resolve/main/gemma-2-2b-it.q3_k_m.gguf" | |
| MODEL_PATH = "model.gguf" | |
| # === DOWNLOAD MODEL (Run once) === | |
| if not os.path.exists(MODEL_PATH): | |
| print("Downloading GGUF model... this may take a few minutes.") | |
| with requests.get(MODEL_URL, stream=True) as r: | |
| r.raise_for_status() | |
| with open(MODEL_PATH, "wb") as f: | |
| for chunk in r.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| print("Download complete!") | |
| # === INITIALIZE LLM === | |
| # n_ctx is the context window (memory). 2048 is a good balance for free tier RAM. | |
| llm = Llama( | |
| model_path=MODEL_PATH, | |
| n_ctx=2048, | |
| n_threads=4 # Optimized for HF free tier CPU | |
| ) | |
| # === HTML TEMPLATE === | |
| HTML_TEMPLATE = """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>CooLLaMA AI</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;800&display=swap" rel="stylesheet"> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" /> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script> | |
| <style> | |
| :root { --bg:#0b0f1a; --card:#161b2c; --user-blue:#2563eb; --ai-bubble:#1e293b; --accent:#8b5cf6; --text-light:#f1f5f9; --text-dim:#64748b; } | |
| body { font-family:'Plus Jakarta Sans',sans-serif; background:var(--bg); color:var(--text-light); display:flex; height:100vh; margin:0; overflow:hidden; } | |
| #sidebar { width:260px; background:#090c14; display:flex; flex-direction:column; border-right:1px solid #2d3748; padding:20px; } | |
| .new-chat-btn { background:linear-gradient(135deg,#2563eb,#7c3aed); border:none; color:white; padding:14px; border-radius:12px; cursor:pointer; font-weight:800; margin-bottom:25px; transition:all 0.2s ease; } | |
| .new-chat-btn:hover { transform:translateY(-2px); box-shadow:0 5px 15px rgba(37,99,235,0.4); } | |
| #chat-list { flex:1; overflow-y:auto; display:flex; flex-direction:column; gap:8px; } | |
| .chat-item { padding:12px; border-radius:10px; cursor:pointer; font-size:13px; color:var(--text-dim); transition:0.3s; border:1px solid transparent; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } | |
| .chat-item:hover { background:#1e293b; color:white; border-color:#334155; } | |
| .chat-item.active { background:#1e293b; color:white; border-color:#2563eb; } | |
| #main-container { flex:1; display:flex; flex-direction:column; } | |
| header { padding:20px 30px; border-bottom:1px solid #2d3748; display:flex; justify-content:space-between; align-items:center; } | |
| header h1 { font-size:1.5rem; margin:0; font-weight:800; background:linear-gradient(90deg,#60a5fa,#a78bfa); -webkit-background-clip:text; -webkit-text-fill-color:transparent; } | |
| #chat { flex:1; padding:25px; overflow-y:auto; display:flex; flex-direction:column; gap:20px; } | |
| .message-row { display:flex; flex-direction:column; width:100%; animation:slideUp 0.3s ease-out; } | |
| .message { max-width:85%; padding:14px 20px; border-radius:18px; font-size:15px; line-height:1.6; } | |
| .user-row { align-items:flex-end; } | |
| .user { background:var(--user-blue); color:white; border-bottom-right-radius:2px; } | |
| .ai-row { align-items:flex-start; } | |
| .ai { background:var(--ai-bubble); color:var(--text-light); border:1px solid #334155; border-bottom-left-radius:2px; } | |
| .label { font-size:10px; font-weight:700; margin-bottom:6px; color:var(--text-dim); text-transform:uppercase; } | |
| #input-container { padding:25px; background:var(--card); border-top:1px solid #2d3748; } | |
| .input-box { max-width:850px; margin:0 auto; display:flex; background:#0b0f1a; border:1px solid #334155; border-radius:15px; padding:10px; } | |
| textarea { flex:1; border:none; background:transparent; padding:10px; font-size:16px; outline:none; color:white; resize:none; } | |
| button#send-btn { background:var(--user-blue); border:none; color:white; padding:0 25px; border-radius:10px; cursor:pointer; font-weight:800; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="sidebar"> | |
| <button class="new-chat-btn" onclick="startNewChat()">+ New Chat</button> | |
| <div id="chat-list"></div> | |
| </div> | |
| <div id="main-container"> | |
| <header><h1>🦙 CooLLaMA</h1><div style="font-size:10px; color:#64748b; font-weight:700;">v3.5 GGUF</div></header> | |
| <div id="chat"></div> | |
| <div id="input-container"> | |
| <div class="input-box"> | |
| <textarea id="message" placeholder="Send a message..." rows="1"></textarea> | |
| <button id="send-btn" onclick="sendMessage()">Send</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentChatId = null; | |
| let localData = JSON.parse(localStorage.getItem('coolllama_chats')) || {}; | |
| function loadSidebar(){ | |
| const list=document.getElementById("chat-list"); list.innerHTML=""; | |
| Object.keys(localData).reverse().forEach(id=>{ | |
| const item=document.createElement("div"); | |
| item.className="chat-item"+(id===currentChatId?" active":""); | |
| item.innerText=`Session ${id}`; | |
| item.onclick=()=>selectChat(id); | |
| list.appendChild(item); | |
| }); | |
| } | |
| async function startNewChat(){ | |
| const res=await fetch("/new_chat",{method:"POST"}); | |
| const data=await res.json(); | |
| currentChatId=data.chat_id; | |
| localData[currentChatId]=[]; | |
| saveToCache(); loadSidebar(); selectChat(currentChatId); | |
| } | |
| function selectChat(id){ | |
| currentChatId=id; | |
| document.getElementById("chat").innerHTML=""; | |
| (localData[id] || []).forEach(msg=>appendMessage(msg.role,msg.content)); | |
| loadSidebar(); | |
| } | |
| function appendMessage(role,text){ | |
| const chatDiv=document.getElementById("chat"); | |
| const row=document.createElement("div"); | |
| row.className=`message-row ${role==='user'?'user-row':'ai-row'}`; | |
| const content = role==='user' ? text : marked.parse(text); | |
| row.innerHTML=`<span class="label">${role==='user'?'You':'CooLLaMA'}</span><div class="message ${role==='user'?'user':'ai'}">${content}</div>`; | |
| chatDiv.appendChild(row); | |
| chatDiv.scrollTop=chatDiv.scrollHeight; | |
| } | |
| async function sendMessage(){ | |
| const input=document.getElementById("message"); | |
| const text=input.value.trim(); | |
| if(!text || !currentChatId) return; | |
| appendMessage('user', text); | |
| localData[currentChatId].push({role:'user', content:text}); | |
| input.value=""; | |
| const res=await fetch("/chat",{ | |
| method:"POST", | |
| headers:{"Content-Type":"application/json"}, | |
| body:JSON.stringify({history:localData[currentChatId]}) | |
| }); | |
| const data=await res.json(); | |
| appendMessage('assistant', data.response); | |
| localData[currentChatId].push({role:'assistant', content:data.response}); | |
| saveToCache(); | |
| } | |
| function saveToCache(){ localStorage.setItem('coolllama_chats', JSON.stringify(localData)); } | |
| loadSidebar(); | |
| if(Object.keys(localData).length > 0) selectChat(Object.keys(localData).sort().reverse()[0]); | |
| else startNewChat(); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def index(): | |
| return render_template_string(HTML_TEMPLATE) | |
| def new_chat(): | |
| return jsonify({"chat_id": str(uuid.uuid4())[:8].upper()}) | |
| def chat(): | |
| data = request.json | |
| history = data.get("history", []) | |
| # Simple Prompt Formatting for Gemma-2 | |
| user_input = history[-1]["content"] if history else "" | |
| prompt = f"<start_of_turn>user\n{user_input}<end_of_turn>\n<start_of_turn>model\n" | |
| # Generate | |
| output = llm( | |
| prompt, | |
| max_tokens=512, | |
| stop=["<end_of_turn>", "user"], | |
| echo=False | |
| ) | |
| response_text = output["choices"][0]["text"].strip() | |
| return jsonify({"response": response_text}) | |
| if __name__ == "__main__": | |
| # HF Spaces listen on port 7860 by default | |
| app.run(host="0.0.0.0", port=7860) |