darkmariam / app.py
kuro223's picture
uuz
7d3a75b
import os
import logging
import base64
import json
import uuid
import google.generativeai as genai
from datetime import datetime
from functools import wraps
from flask import Flask, render_template, request, jsonify, session, redirect
from dotenv import load_dotenv
from werkzeug.utils import secure_filename
# Configure logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
# Load environment variables
load_dotenv()
# Configure Google Gemini API
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
logger.warning("GEMINI_API_KEY not found in environment variables")
else:
logger.info("GEMINI_API_KEY found. API configured successfully.")
genai.configure(api_key=api_key)
# Initialize Flask app
app = Flask(__name__)
app.secret_key = os.environ.get("SESSION_SECRET", "default-dev-secret-key")
app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 # 10 MB max
# Middleware to ensure user has a session_id
def session_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if 'session_id' not in session:
session['session_id'] = str(uuid.uuid4())
logger.info(f"Created new session: {session['session_id']}")
return f(*args, **kwargs)
return decorated_function
# Ensure upload directory exists
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
# Configure Gemini model with specific parameters for better responses
model = genai.GenerativeModel(
model_name='gemini-2.0-flash',
generation_config={
'temperature': 0.7, # Slightly creative but still focused
'top_p': 0.9, # Diverse output but not too random
'top_k': 40, # Reasonable range of tokens to consider
'max_output_tokens': 2048 # Allow longer responses
}
)
# Configure Gemini vision model for image processing
vision_model = genai.GenerativeModel('gemini-2.0-vision-flash')
@app.route('/')
@session_required
def index():
"""Render the chat interface."""
return render_template('index.html')
@app.route('/api/chat', methods=['POST'])
@session_required
def chat():
"""Process chat messages and get responses from Gemini API."""
try:
data = request.json
user_message = data.get('message', '')
chat_history = data.get('history', [])
image_data = data.get('image', None)
if not user_message and not image_data:
return jsonify({'error': 'Veuillez entrer un message ou joindre une image.'}), 400
# Log the incoming request (but not full chat history for privacy)
session_id = session.get('session_id')
logger.info(f"Received chat request from session {session_id}. Message length: {len(user_message)}")
# Format conversation history for context
formatted_history = []
for msg in chat_history[-15:]: # Use the last 15 messages for more context
role = "user" if msg['sender'] == 'user' else "model"
formatted_history.append({"role": role, "parts": [msg['text']]})
# Handle image processing if images are included
if image_data:
try:
parts = []
# Process single image or multiple images
if isinstance(image_data, list):
# Handle multiple images
for img in image_data:
if isinstance(img, str) and ',' in img:
# Extract the base64 part after the comma
image_base64 = img.split(',')[1]
# Create image part
image = genai.types.Part.from_data(
data=base64.b64decode(image_base64),
mime_type="image/jpeg"
)
parts.append(image)
# Save each image
session_id = session.get('session_id')
session_dir = os.path.join(app.config['UPLOAD_FOLDER'], session_id)
os.makedirs(session_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = secure_filename(f"image_{timestamp}_{len(parts)}.jpg")
filepath = os.path.join(session_dir, filename)
with open(filepath, "wb") as f:
f.write(base64.b64decode(image_base64))
else:
# Handle single image
image_base64 = image_data.split(',')[1]
image = genai.types.Part.from_data(
data=base64.b64decode(image_base64),
mime_type="image/jpeg"
)
parts.append(image)
# Save image with timestamp in the session directory
session_id = session.get('session_id')
session_dir = os.path.join(app.config['UPLOAD_FOLDER'], session_id)
os.makedirs(session_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = secure_filename(f"image_{timestamp}.jpg")
filepath = os.path.join(session_dir, filename)
with open(filepath, "wb") as f:
f.write(base64.b64decode(image_base64))
# Add text message if provided
if user_message:
parts.append(user_message)
# Generate response using vision model
response = vision_model.generate_content(parts)
return jsonify({'response': response.text})
except Exception as img_error:
logger.error(f"Error processing image: {str(img_error)}")
return jsonify({
'error': 'Désolé, une erreur est survenue lors du traitement de l\'image. Veuillez réessayer.'
}), 500
else:
# Text-only processing
# Create a chat session with history
chat = model.start_chat(history=formatted_history)
# Generate response
response = chat.send_message(user_message)
# Log successful response
logger.info(f"Generated response successfully. Response length: {len(response.text)}")
# Return the response
return jsonify({'response': response.text})
except genai.types.generation_types.BlockedPromptException as be:
logger.warning(f"Content blocked: {str(be)}")
return jsonify({
'error': 'Votre message ou la conversation ne peut pas être traitée car elle contient du contenu potentiellement inapproprié.'
}), 400
except Exception as e:
logger.error(f"Error in chat endpoint: {str(e)}")
return jsonify({
'error': 'Désolé, j\'ai rencontré une erreur lors du traitement de votre demande. Veuillez réessayer.'
}), 500
@app.route('/api/save-chat', methods=['POST'])
@session_required
def save_chat():
"""Save the current chat history."""
try:
session_id = session.get('session_id')
# Create session-specific directory
session_dir = os.path.join(app.config['UPLOAD_FOLDER'], session_id)
os.makedirs(session_dir, exist_ok=True)
data = request.json
chat_history = data.get('history', [])
if not chat_history:
return jsonify({'error': 'Aucune conversation à sauvegarder.'}), 400
# Generate filename with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"chat_{timestamp}.json"
filepath = os.path.join(session_dir, filename)
# Save chat history to file
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(chat_history, f, ensure_ascii=False, indent=2)
return jsonify({'success': True, 'filename': filename})
except Exception as e:
logger.error(f"Error saving chat: {str(e)}")
return jsonify({
'error': 'Désolé, une erreur est survenue lors de la sauvegarde de la conversation.'
}), 500
@app.route('/api/load-chats', methods=['GET'])
@session_required
def load_chats():
"""Get a list of saved chat files for current session."""
try:
session_id = session.get('session_id')
# Get session-specific directory
session_dir = os.path.join(app.config['UPLOAD_FOLDER'], session_id)
# If the directory doesn't exist yet, return empty list
if not os.path.exists(session_dir):
return jsonify({'chats': []})
chat_files = []
for filename in os.listdir(session_dir):
if filename.startswith('chat_') and filename.endswith('.json'):
# Extract timestamp from filename
timestamp = filename[5:-5] # Remove 'chat_' and '.json'
# Add to list
chat_files.append({
'filename': filename,
'timestamp': timestamp
})
# Sort by timestamp (newest first)
chat_files.sort(key=lambda x: x['timestamp'], reverse=True)
logger.info(f"Loaded {len(chat_files)} chats for session {session_id}")
return jsonify({'chats': chat_files})
except Exception as e:
logger.error(f"Error loading chat list: {str(e)}")
return jsonify({
'error': 'Désolé, une erreur est survenue lors du chargement des conversations.'
}), 500
@app.route('/api/load-chat/<filename>', methods=['GET'])
@session_required
def load_chat(filename):
"""Load a specific chat history file."""
try:
session_id = session.get('session_id')
# Load from session-specific directory
session_dir = os.path.join(app.config['UPLOAD_FOLDER'], session_id)
filepath = os.path.join(session_dir, secure_filename(filename))
if not os.path.exists(filepath):
return jsonify({'error': 'Conversation introuvable.'}), 404
with open(filepath, 'r', encoding='utf-8') as f:
chat_history = json.load(f)
logger.info(f"Loaded chat {filename} for session {session_id}")
return jsonify({'history': chat_history})
except Exception as e:
logger.error(f"Error loading chat file: {str(e)}")
return jsonify({
'error': 'Désolé, une erreur est survenue lors du chargement de la conversation.'
}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)