Bbxbzbdb / app.py
Kfjjdjdjdhdhd's picture
Update app.py
e627161 verified
import os
import logging
from flask import Flask, render_template_string, send_file, abort, request, jsonify
from huggingface_hub import hf_hub_download, login as hf_login
from dotenv import load_dotenv
load_dotenv()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
hf_token = os.getenv("HF_TOKEN")
try:
hf_login(token=hf_token)
logger.info("Hugging Face Login Successful")
except Exception as e:
logger.error(f"Hugging Face Login Error: {e}")
app = Flask(__name__)
MODEL_FILENAME = 'gemma3-1b-it-int4.task'
HUGGINGFACE_REPO = 'litert-community/Gemma3-1B-IT'
MODEL_LOCAL_PATH = os.path.join(os.getcwd(), MODEL_FILENAME)
loaded_model = None
def download_model_file():
if not os.path.exists(MODEL_LOCAL_PATH):
logger.info("Model file not found locally. Downloading from Hugging Face...")
try:
hf_hub_download(repo_id=HUGGINGFACE_REPO, filename=MODEL_FILENAME, local_dir=".", local_dir_use_symlinks=False)
logger.info(f"Download Completed: {MODEL_LOCAL_PATH}")
except Exception as e:
logger.error(f"Error downloading model file: {e}")
raise
else:
logger.info("Model file already exists locally.")
return MODEL_LOCAL_PATH
download_model_file()
@app.route('/download')
def download_model():
if os.path.exists(MODEL_LOCAL_PATH):
return send_file(MODEL_LOCAL_PATH, as_attachment=True, download_name=MODEL_FILENAME)
else:
abort(404)
def perform_inference(input_text: str) -> str:
return "Backend inference is not used with MediaPipe. Inference happens in the browser."
HTML_CONTENT = """<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LLM Chatbot Demo with MediaPipe</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<style>
:root {
--primary-color: #2E4053;
--secondary-color: #3498db;
--background-color: #f8f9fa;
--text-color: #343a40;
--light-gray: #e0e0e0;
--message-user-bg: #e2f0cb;
--message-bot-bg: #fff;
}
body {
font-family: 'Roboto', sans-serif;
margin: 0;
padding: 0;
background-color: var(--background-color);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
width: 100vw;
overflow: hidden;
}
#app-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.chatbot-container {
background-color: #fff;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
width: 95%;
height: 95%;
max-width: 800px;
max-height: 700px;
display: flex;
flex-direction: column;
overflow: hidden;
animation: fadeIn 0.5s ease-out;
border: 1px solid var(--light-gray);
}
.chat-header {
background-color: var(--primary-color);
color: #fff;
padding: 20px;
text-align: left;
border-bottom: 1px solid var(--light-gray);
display: flex;
align-items: center;
justify-content: space-between;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
}
.header-info {
display: flex;
align-items: center;
}
.header-icon {
width: 45px;
height: 45px;
background-color: #fff;
color: var(--primary-color);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.4rem;
margin-right: 15px;
}
.header-text h1 {
font-size: 1.5rem;
margin-bottom: 5px;
color: #fff;
font-weight: 500;
}
.header-text p {
font-size: 0.95rem;
color: rgba(255,255,255,0.8);
margin: 0;
}
.status-indicator {
font-size: 0.9rem;
color: #5cb85c;
}
.header-actions button {
background: none;
border: none;
color: rgba(255,255,255,0.7);
cursor: pointer;
font-size: 1rem;
transition: color 0.2s;
margin-left: 15px;
}
.header-actions button:hover {
color: #fff;
}
.chat-history {
padding: 20px;
overflow-y: auto;
flex-grow: 1;
display: flex;
flex-direction: column;
scrollbar-width: thin;
scrollbar-color: var(--light-gray) var(--background-color);
}
.chat-history::-webkit-scrollbar {
width: 6px;
}
.chat-history::-webkit-scrollbar-track {
background: var(--background-color);
border-radius: 5px;
}
.chat-history::-webkit-scrollbar-thumb {
background: var(--light-gray);
border-radius: 5px;
}
.message {
background-color: var(--message-bot-bg);
color: var(--text-color);
border-radius: 18px;
padding: 14px 18px;
margin-bottom: 12px;
width: fit-content;
max-width: 75%;
word-wrap: break-word;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
position: relative;
border: 1px solid var(--light-gray);
}
.message.user-message {
background-color: var(--message-user-bg);
color: var(--text-color);
align-self: flex-end;
border-bottom-right-radius: 5px;
border-top-right-radius: 18px;
border-top-left-radius: 18px;
border-bottom-left-radius: 18px;
border: none;
box-shadow: 0 2px 5px rgba(0,0,0,0.08);
}
.message.bot-message {
align-self: flex-start;
border-bottom-left-radius: 5px;
border-top-left-radius: 18px;
border-top-right-radius: 18px;
border-bottom-right-radius: 18px;
border: 1px solid var(--light-gray);
}
.message .timestamp {
position: absolute;
bottom: -15px;
right: 8px;
font-size: 0.7rem;
color: #777;
}
.chat-input-area {
padding: 20px;
background-color: #fff;
border-top: 1px solid var(--light-gray);
display: flex;
align-items: center;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
box-shadow: inset 0 2px 5px rgba(0,0,0,0.03);
}
.chat-input {
flex-grow: 1;
border: 1px solid var(--light-gray);
border-radius: 8px;
padding: 12px 15px;
font-size: 1rem;
font-family: 'Roboto Mono', monospace;
margin-right: 10px;
box-shadow: none;
transition: border-color 0.2s ease;
color: var(--text-color);
}
.chat-input:focus {
outline: none;
border-color: var(--secondary-color);
box-shadow: 0 0 0 2px rgba(var(--secondary-color-rgb), 0.2);
}
.send-button {
background-color: var(--secondary-color);
color: white;
border: none;
border-radius: 8px;
padding: 12px 18px;
cursor: pointer;
transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
font-size: 1rem;
}
.send-button:hover {
background-color: #257ab5;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.12);
}
.send-button:focus {
outline: none;
}
.send-button:disabled {
background-color: var(--light-gray);
color: #777;
cursor: not-allowed;
box-shadow: none;
transform: none;
}
#loading-indicator {
margin-left: 10px;
font-style: italic;
color: #777;
opacity: 0.8;
animation: pulse 1.5s infinite;
display: none;
font-size: 0.9rem;
}
#model-status {
margin-left: 10px;
font-size: 0.9rem;
color: #777;
}
#model-status.loaded {
color: #5cb85c;
}
#model-status.loading {
font-style: italic;
}
#output {
white-space: pre-wrap;
word-break: break-word;
display: none;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/@mediapipe/tasks-genai/dist/tasks-genai.js" crossorigin="anonymous"></script>
</head>
<body>
<div id="app-container">
<div class="chatbot-container">
<div class="chat-header">
<div class="header-info">
<div class="header-icon"><i class="fas fa-robot"></i></div>
<div class="header-text">
<h1>AI Chat Assistant</h1>
<p>Powered by MediaPipe GenAI</p>
<span id="model-status" class="loading">Loading Model...</span>
</div>
</div>
<div class="header-actions">
<button id="clear-chat-button" title="Clear Chat"><i class="fas fa-trash"></i></button>
</div>
</div>
<div class="chat-history" id="chat-history">
</div>
<div class="chat-input-area">
<input type="text" id="input" class="chat-input" placeholder="Type your message..." disabled>
<button id="submit" class="send-button" disabled><i class="fas fa-paper-plane"></i> Send</button>
<span id="loading-indicator">Thinking...</span>
<div id="output" style="display: none;"></div>
</div>
</div>
</div>
<script type="module" src="/index.js"></script>
</body>
</html>
"""
JS_CONTENT = """
import { FilesetResolver, LlmInference } from 'https://cdn.jsdelivr.net/npm/@mediapipe/tasks-genai/dist/tasks-genai.js';
const chatInput = document.getElementById('input');
const sendButton = document.getElementById('submit');
const chatHistory = document.getElementById('chat-history');
const loadingIndicator = document.getElementById('loading-indicator');
const clearChatButton = document.getElementById('clear-chat-button');
const modelStatus = document.getElementById('model-status');
const outputElement = document.getElementById('output');
let isModelLoaded = false;
let messageHistory = [];
let llmInference;
function createMessageElement(text, isUserMessage) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('message');
text = text.replace(/\\n/g, '<br>');
messageDiv.innerHTML = text;
if (isUserMessage) {
messageDiv.classList.add('user-message');
} else {
messageDiv.classList.add('bot-message');
}
const timestampDiv = document.createElement('div');
timestampDiv.classList.add('timestamp');
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
timestampDiv.textContent = `${hours}:${minutes}`;
messageDiv.appendChild(timestampDiv);
return messageDiv;
}
function displayBotMessage(text) {
const botMessageElement = createMessageElement(text, false);
chatHistory.appendChild(botMessageElement);
chatHistory.scrollTop = chatHistory.scrollHeight;
messageHistory.push({ text: text, isUserMessage: false });
sendButton.disabled = false;
loadingIndicator.style.display = 'none';
}
function renderMessageHistory() {
chatHistory.innerHTML = '';
messageHistory.forEach(message => {
const messageElement = createMessageElement(message.text, message.isUserMessage);
chatHistory.appendChild(messageElement);
});
chatHistory.scrollTop = chatHistory.scrollHeight;
}
function clearChatHistory() {
messageHistory = [];
renderMessageHistory();
}
function displayPartialResults(partialResults, complete) {
outputElement.textContent += partialResults;
if (complete) {
if (!outputElement.textContent) {
outputElement.textContent = 'Result is empty';
}
sendButton.disabled = false;
loadingIndicator.style.display = 'none';
outputElement.style.display = 'none';
displayBotMessage(outputElement.textContent);
}
}
async function initializeChatbot() {
try {
const filesetResolver = await FilesetResolver.forGenAiTasks('https://cdn.jsdelivr.net/npm/@mediapipe/tasks-genai/dist/wasm');
llmInference = await LlmInference.createFromFileset(filesetResolver, '/download');
isModelLoaded = true;
sendButton.disabled = false;
chatInput.disabled = false;
modelStatus.textContent = "Model Loaded";
modelStatus.classList.remove('loading');
modelStatus.classList.add('loaded');
console.log('MediaPipe GenAI Model Initialized.');
} catch (error) {
console.error("Error initializing MediaPipe GenAI:", error);
modelStatus.textContent = "Model Load Error";
modelStatus.classList.remove('loading');
modelStatus.classList.add('error');
} finally {
loadingIndicator.style.display = 'none';
renderMessageHistory();
}
}
sendButton.onclick = async () => {
if (!isModelLoaded) {
alert('Chatbot is not initialized yet.');
return;
}
const userMessageText = chatInput.value.trim();
if (!userMessageText) return;
const userMessageElement = createMessageElement(userMessageText, true);
chatHistory.appendChild(userMessageElement);
chatHistory.scrollTop = chatHistory.scrollHeight;
messageHistory.push({ text: userMessageText, isUserMessage: true });
chatInput.value = '';
sendButton.disabled = true;
loadingIndicator.style.display = 'inline-block';
outputElement.style.display = 'block';
outputElement.textContent = '';
try {
await llmInference.generateResponse(userMessageText, displayPartialResults);
} catch (error) {
console.error("Inference error:", error);
displayBotMessage('Error generating response. Please try again.');
sendButton.disabled = false;
loadingIndicator.style.display = 'none';
outputElement.style.display = 'none';
}
};
chatInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendButton.click();
}
});
clearChatButton.onclick = clearChatHistory;
document.addEventListener('DOMContentLoaded', initializeChatbot);
"""
@app.route('/')
def index():
return render_template_string(HTML_CONTENT)
@app.route('/index.js')
def serve_js():
return JS_CONTENT, 200, {'Content-Type': 'application/javascript'}
@app.route('/api/infer', methods=['POST'])
def api_infer():
return jsonify({'error': 'Backend inference is not used with MediaPipe. Inference happens in the browser.'}), 501
if __name__ == '__main__':
logger.info("Starting Flask application on port 7860")
app.run(debug=True, host="0.0.0.0", port=7860)