jo
Browse files- __pycache__/app.cpython-311.pyc +0 -0
- app.py +42 -22
__pycache__/app.cpython-311.pyc
CHANGED
|
Binary files a/__pycache__/app.cpython-311.pyc and b/__pycache__/app.cpython-311.pyc differ
|
|
|
app.py
CHANGED
|
@@ -8,21 +8,37 @@ from werkzeug.utils import secure_filename
|
|
| 8 |
import mimetypes
|
| 9 |
from dotenv import load_dotenv
|
| 10 |
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
app = Flask(__name__)
|
| 13 |
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
|
| 14 |
load_dotenv()
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
def load_system_instruction():
|
| 17 |
"""Charge les instructions système depuis le fichier Markdown"""
|
| 18 |
try:
|
| 19 |
with open('instructions/system_instruction.md', 'r', encoding='utf-8') as f:
|
| 20 |
return f.read().strip()
|
| 21 |
except FileNotFoundError:
|
| 22 |
-
|
| 23 |
return "Tu es un assistant intelligent et amical nommé Mariam. Tu assistes les utilisateurs au mieux de tes capacités. Tu as été créé par Aenir."
|
| 24 |
except Exception as e:
|
| 25 |
-
|
| 26 |
return "Tu es un assistant intelligent et amical nommé Mariam. Tu assistes les utilisateurs au mieux de tes capacités. Tu as été créé par Aenir."
|
| 27 |
|
| 28 |
# Configuration du client Gemini
|
|
@@ -30,15 +46,15 @@ API_KEY = os.getenv("GOOGLE_API_KEY")
|
|
| 30 |
SYSTEM_INSTRUCTION = load_system_instruction()
|
| 31 |
|
| 32 |
if not API_KEY:
|
| 33 |
-
|
| 34 |
-
|
| 35 |
client = None
|
| 36 |
else:
|
| 37 |
try:
|
| 38 |
client = genai.Client(api_key=API_KEY)
|
| 39 |
-
|
| 40 |
except Exception as e:
|
| 41 |
-
|
| 42 |
client = None
|
| 43 |
|
| 44 |
# Configuration par défaut
|
|
@@ -139,7 +155,7 @@ def chat():
|
|
| 139 |
thinking_enabled = data.get('thinking_enabled', True)
|
| 140 |
conversation_id = data.get('conversation_id', 'default')
|
| 141 |
|
| 142 |
-
|
| 143 |
|
| 144 |
# Ajouter le message de l'utilisateur à l'historique
|
| 145 |
add_message_to_history(conversation_id, 'user', message)
|
|
@@ -173,7 +189,7 @@ def chat():
|
|
| 173 |
yield f"data: {json.dumps({'type': 'error', 'content': 'API Gemini non configurée. Définissez GOOGLE_API_KEY.'})}\n\n"
|
| 174 |
return
|
| 175 |
|
| 176 |
-
|
| 177 |
response_stream = chat.send_message_stream(
|
| 178 |
message,
|
| 179 |
config=generation_config
|
|
@@ -185,7 +201,7 @@ def chat():
|
|
| 185 |
|
| 186 |
for chunk in response_stream:
|
| 187 |
chunk_count += 1
|
| 188 |
-
|
| 189 |
for part in chunk.candidates[0].content.parts:
|
| 190 |
if part.text:
|
| 191 |
if part.thought and thinking_enabled:
|
|
@@ -194,7 +210,7 @@ def chat():
|
|
| 194 |
full_response += part.text
|
| 195 |
yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
|
| 196 |
|
| 197 |
-
|
| 198 |
|
| 199 |
# Ajouter la réponse de l'assistant à l'historique
|
| 200 |
if full_response:
|
|
@@ -204,7 +220,7 @@ def chat():
|
|
| 204 |
yield f"data: {json.dumps({'type': 'end'})}\n\n"
|
| 205 |
|
| 206 |
except Exception as e:
|
| 207 |
-
|
| 208 |
yield f"data: {json.dumps({'type': 'error', 'content': f'Erreur API: {str(e)}'})}\n\n"
|
| 209 |
|
| 210 |
return Response(generate(), mimetype='text/event-stream', headers={
|
|
@@ -230,6 +246,7 @@ def upload_file():
|
|
| 230 |
# Lire le fichier
|
| 231 |
file_bytes = file.read()
|
| 232 |
mime_type = file.content_type or mimetypes.guess_type(file.filename)[0]
|
|
|
|
| 233 |
|
| 234 |
# Encoder en base64 pour le stockage temporaire
|
| 235 |
file_b64 = base64.b64encode(file_bytes).decode()
|
|
@@ -255,7 +272,7 @@ def chat_with_file():
|
|
| 255 |
thinking_enabled = data.get('thinking_enabled', True)
|
| 256 |
conversation_id = data.get('conversation_id', 'default')
|
| 257 |
|
| 258 |
-
|
| 259 |
|
| 260 |
# Ensure file_data_list is a list
|
| 261 |
if not isinstance(file_data_list, list):
|
|
@@ -308,7 +325,7 @@ def chat_with_file():
|
|
| 308 |
yield f"data: {json.dumps({'type': 'error', 'content': 'API Gemini non configurée. Définissez GOOGLE_API_KEY.'})}\n\n"
|
| 309 |
return
|
| 310 |
|
| 311 |
-
|
| 312 |
response_stream = chat.send_message_stream(
|
| 313 |
contents,
|
| 314 |
config=generation_config
|
|
@@ -320,7 +337,7 @@ def chat_with_file():
|
|
| 320 |
|
| 321 |
for chunk in response_stream:
|
| 322 |
chunk_count += 1
|
| 323 |
-
|
| 324 |
for part in chunk.candidates[0].content.parts:
|
| 325 |
if part.text:
|
| 326 |
if part.thought and thinking_enabled:
|
|
@@ -329,7 +346,7 @@ def chat_with_file():
|
|
| 329 |
full_response += part.text
|
| 330 |
yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
|
| 331 |
|
| 332 |
-
|
| 333 |
|
| 334 |
# Ajouter la réponse de l'assistant à l'historique
|
| 335 |
if full_response:
|
|
@@ -339,7 +356,7 @@ def chat_with_file():
|
|
| 339 |
yield f"data: {json.dumps({'type': 'end'})}\n\n"
|
| 340 |
|
| 341 |
except Exception as e:
|
| 342 |
-
|
| 343 |
yield f"data: {json.dumps({'type': 'error', 'content': f'Erreur API avec fichiers: {str(e)}'})}\n\n"
|
| 344 |
|
| 345 |
return Response(generate(), mimetype='text/event-stream', headers={
|
|
@@ -360,12 +377,13 @@ def reset_conversation():
|
|
| 360 |
|
| 361 |
if conversation_id in conversations:
|
| 362 |
del conversations[conversation_id]
|
| 363 |
-
|
| 364 |
# Marquer la conversation comme terminée dans les métadonnées
|
| 365 |
if conversation_id in conversation_metadata:
|
| 366 |
conversation_metadata[conversation_id]['status'] = 'reset'
|
| 367 |
conversation_metadata[conversation_id]['last_activity'] = datetime.now().isoformat()
|
| 368 |
-
|
|
|
|
| 369 |
return jsonify({'success': True})
|
| 370 |
|
| 371 |
except Exception as e:
|
|
@@ -377,10 +395,11 @@ def delete_conversation(conversation_id):
|
|
| 377 |
try:
|
| 378 |
if conversation_id in conversations:
|
| 379 |
del conversations[conversation_id]
|
| 380 |
-
|
| 381 |
if conversation_id in conversation_metadata:
|
| 382 |
del conversation_metadata[conversation_id]
|
| 383 |
-
|
|
|
|
| 384 |
return jsonify({'success': True})
|
| 385 |
except Exception as e:
|
| 386 |
return jsonify({'error': str(e)}), 500
|
|
@@ -393,7 +412,8 @@ def export_conversation(conversation_id):
|
|
| 393 |
return jsonify({'error': 'Conversation non trouvée'}), 404
|
| 394 |
|
| 395 |
conversation_data = conversation_metadata[conversation_id]
|
| 396 |
-
|
|
|
|
| 397 |
return jsonify({
|
| 398 |
'conversation_id': conversation_id,
|
| 399 |
'export_date': datetime.now().isoformat(),
|
|
@@ -480,4 +500,4 @@ def debug_api_test():
|
|
| 480 |
})
|
| 481 |
|
| 482 |
if __name__ == '__main__':
|
| 483 |
-
app.run(debug=True, host='0.0.0.0', port=
|
|
|
|
| 8 |
import mimetypes
|
| 9 |
from dotenv import load_dotenv
|
| 10 |
from datetime import datetime
|
| 11 |
+
import logging
|
| 12 |
+
|
| 13 |
+
# Configure logging
|
| 14 |
+
logging.basicConfig(
|
| 15 |
+
level=logging.INFO,
|
| 16 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
| 17 |
+
handlers=[
|
| 18 |
+
logging.StreamHandler(),
|
| 19 |
+
logging.FileHandler('app.log', mode='a', encoding='utf-8')
|
| 20 |
+
]
|
| 21 |
+
)
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
|
| 24 |
app = Flask(__name__)
|
| 25 |
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
|
| 26 |
load_dotenv()
|
| 27 |
|
| 28 |
+
@app.before_request
|
| 29 |
+
def log_request_info():
|
| 30 |
+
logger.info(f'Request: {request.method} {request.url} from {request.remote_addr}')
|
| 31 |
+
|
| 32 |
def load_system_instruction():
|
| 33 |
"""Charge les instructions système depuis le fichier Markdown"""
|
| 34 |
try:
|
| 35 |
with open('instructions/system_instruction.md', 'r', encoding='utf-8') as f:
|
| 36 |
return f.read().strip()
|
| 37 |
except FileNotFoundError:
|
| 38 |
+
logger.error("Fichier d'instructions système non trouvé.")
|
| 39 |
return "Tu es un assistant intelligent et amical nommé Mariam. Tu assistes les utilisateurs au mieux de tes capacités. Tu as été créé par Aenir."
|
| 40 |
except Exception as e:
|
| 41 |
+
logger.exception("Erreur lors du chargement des instructions système")
|
| 42 |
return "Tu es un assistant intelligent et amical nommé Mariam. Tu assistes les utilisateurs au mieux de tes capacités. Tu as été créé par Aenir."
|
| 43 |
|
| 44 |
# Configuration du client Gemini
|
|
|
|
| 46 |
SYSTEM_INSTRUCTION = load_system_instruction()
|
| 47 |
|
| 48 |
if not API_KEY:
|
| 49 |
+
logger.warning("GOOGLE_API_KEY non définie dans les variables d'environnement")
|
| 50 |
+
logger.warning("L'application démarrera mais les fonctionnalités de chat seront limitées")
|
| 51 |
client = None
|
| 52 |
else:
|
| 53 |
try:
|
| 54 |
client = genai.Client(api_key=API_KEY)
|
| 55 |
+
logger.info("Client Gemini initialisé avec succès")
|
| 56 |
except Exception as e:
|
| 57 |
+
logger.exception("Erreur lors de l'initialisation du client Gemini")
|
| 58 |
client = None
|
| 59 |
|
| 60 |
# Configuration par défaut
|
|
|
|
| 155 |
thinking_enabled = data.get('thinking_enabled', True)
|
| 156 |
conversation_id = data.get('conversation_id', 'default')
|
| 157 |
|
| 158 |
+
logger.info(f"Requête chat reçue: message='{message[:50]}...', conversation_id={conversation_id}")
|
| 159 |
|
| 160 |
# Ajouter le message de l'utilisateur à l'historique
|
| 161 |
add_message_to_history(conversation_id, 'user', message)
|
|
|
|
| 189 |
yield f"data: {json.dumps({'type': 'error', 'content': 'API Gemini non configurée. Définissez GOOGLE_API_KEY.'})}\n\n"
|
| 190 |
return
|
| 191 |
|
| 192 |
+
logger.info(f"Démarrage du streaming pour conversation {conversation_id}")
|
| 193 |
response_stream = chat.send_message_stream(
|
| 194 |
message,
|
| 195 |
config=generation_config
|
|
|
|
| 201 |
|
| 202 |
for chunk in response_stream:
|
| 203 |
chunk_count += 1
|
| 204 |
+
logger.debug(f"Chunk {chunk_count} reçu")
|
| 205 |
for part in chunk.candidates[0].content.parts:
|
| 206 |
if part.text:
|
| 207 |
if part.thought and thinking_enabled:
|
|
|
|
| 210 |
full_response += part.text
|
| 211 |
yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
|
| 212 |
|
| 213 |
+
logger.info(f"Streaming terminé, réponse complète: {len(full_response)} caractères")
|
| 214 |
|
| 215 |
# Ajouter la réponse de l'assistant à l'historique
|
| 216 |
if full_response:
|
|
|
|
| 220 |
yield f"data: {json.dumps({'type': 'end'})}\n\n"
|
| 221 |
|
| 222 |
except Exception as e:
|
| 223 |
+
logger.exception("Erreur lors du streaming")
|
| 224 |
yield f"data: {json.dumps({'type': 'error', 'content': f'Erreur API: {str(e)}'})}\n\n"
|
| 225 |
|
| 226 |
return Response(generate(), mimetype='text/event-stream', headers={
|
|
|
|
| 246 |
# Lire le fichier
|
| 247 |
file_bytes = file.read()
|
| 248 |
mime_type = file.content_type or mimetypes.guess_type(file.filename)[0]
|
| 249 |
+
logger.info(f"Fichier uploadé: {file.filename}, taille: {len(file_bytes)} bytes, type: {mime_type}")
|
| 250 |
|
| 251 |
# Encoder en base64 pour le stockage temporaire
|
| 252 |
file_b64 = base64.b64encode(file_bytes).decode()
|
|
|
|
| 272 |
thinking_enabled = data.get('thinking_enabled', True)
|
| 273 |
conversation_id = data.get('conversation_id', 'default')
|
| 274 |
|
| 275 |
+
logger.info(f"Requête chat_with_file reçue: message='{message[:50]}...', fichiers={len(file_data_list)}, conversation_id={conversation_id}")
|
| 276 |
|
| 277 |
# Ensure file_data_list is a list
|
| 278 |
if not isinstance(file_data_list, list):
|
|
|
|
| 325 |
yield f"data: {json.dumps({'type': 'error', 'content': 'API Gemini non configurée. Définissez GOOGLE_API_KEY.'})}\n\n"
|
| 326 |
return
|
| 327 |
|
| 328 |
+
logger.info(f"Démarrage du streaming avec fichiers pour conversation {conversation_id}")
|
| 329 |
response_stream = chat.send_message_stream(
|
| 330 |
contents,
|
| 331 |
config=generation_config
|
|
|
|
| 337 |
|
| 338 |
for chunk in response_stream:
|
| 339 |
chunk_count += 1
|
| 340 |
+
logger.debug(f"Chunk {chunk_count} reçu (avec fichiers)")
|
| 341 |
for part in chunk.candidates[0].content.parts:
|
| 342 |
if part.text:
|
| 343 |
if part.thought and thinking_enabled:
|
|
|
|
| 346 |
full_response += part.text
|
| 347 |
yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
|
| 348 |
|
| 349 |
+
logger.info(f"Streaming avec fichiers terminé, réponse complète: {len(full_response)} caractères")
|
| 350 |
|
| 351 |
# Ajouter la réponse de l'assistant à l'historique
|
| 352 |
if full_response:
|
|
|
|
| 356 |
yield f"data: {json.dumps({'type': 'end'})}\n\n"
|
| 357 |
|
| 358 |
except Exception as e:
|
| 359 |
+
logger.exception("Erreur lors du streaming avec fichiers")
|
| 360 |
yield f"data: {json.dumps({'type': 'error', 'content': f'Erreur API avec fichiers: {str(e)}'})}\n\n"
|
| 361 |
|
| 362 |
return Response(generate(), mimetype='text/event-stream', headers={
|
|
|
|
| 377 |
|
| 378 |
if conversation_id in conversations:
|
| 379 |
del conversations[conversation_id]
|
| 380 |
+
|
| 381 |
# Marquer la conversation comme terminée dans les métadonnées
|
| 382 |
if conversation_id in conversation_metadata:
|
| 383 |
conversation_metadata[conversation_id]['status'] = 'reset'
|
| 384 |
conversation_metadata[conversation_id]['last_activity'] = datetime.now().isoformat()
|
| 385 |
+
|
| 386 |
+
logger.info(f"Conversation {conversation_id} réinitialisée")
|
| 387 |
return jsonify({'success': True})
|
| 388 |
|
| 389 |
except Exception as e:
|
|
|
|
| 395 |
try:
|
| 396 |
if conversation_id in conversations:
|
| 397 |
del conversations[conversation_id]
|
| 398 |
+
|
| 399 |
if conversation_id in conversation_metadata:
|
| 400 |
del conversation_metadata[conversation_id]
|
| 401 |
+
|
| 402 |
+
logger.info(f"Conversation {conversation_id} supprimée")
|
| 403 |
return jsonify({'success': True})
|
| 404 |
except Exception as e:
|
| 405 |
return jsonify({'error': str(e)}), 500
|
|
|
|
| 412 |
return jsonify({'error': 'Conversation non trouvée'}), 404
|
| 413 |
|
| 414 |
conversation_data = conversation_metadata[conversation_id]
|
| 415 |
+
|
| 416 |
+
logger.info(f"Conversation {conversation_id} exportée")
|
| 417 |
return jsonify({
|
| 418 |
'conversation_id': conversation_id,
|
| 419 |
'export_date': datetime.now().isoformat(),
|
|
|
|
| 500 |
})
|
| 501 |
|
| 502 |
if __name__ == '__main__':
|
| 503 |
+
app.run(debug=True, host='0.0.0.0', port=7860)
|