Docfile commited on
Commit
886c92a
·
verified ·
1 Parent(s): d6ba638

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +163 -268
app.py CHANGED
@@ -1,311 +1,206 @@
1
- from flask import Flask, render_template, request, jsonify, Response, session
2
- import os
3
  import json
4
- import base64
5
  from google import genai
6
  from google.genai import types
 
7
  from werkzeug.utils import secure_filename
8
- import uuid
9
- from datetime import datetime
10
- import io
11
- from typing import Optional, List, Dict, Union
12
 
13
  app = Flask(__name__)
14
- app.secret_key = 'your-secret-key-here' # Changez cette clé !
15
-
16
- # Configuration de l'upload
17
- UPLOAD_FOLDER = 'uploads'
18
- ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'pdf', 'mp4', 'mov', 'txt', 'csv', 'json'}
19
- MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max file size
20
-
21
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
22
- app.config['MAX_CONTENT_LENGTH'] = MAX_CONTENT_LENGTH
23
-
24
- # Créer le dossier uploads s'il n'existe pas
25
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
26
 
27
- # Initialisation du client Gemini
28
  API_KEY = "AIzaSyAMYpF67aqFnWDJESWOx1dC-w3sEU29VcM"
29
  client = genai.Client(api_key=API_KEY)
30
 
31
- # Configuration des modèles disponibles
32
- MODELS = {
33
- "Gemini 2.5 Flash": "gemini-2.5-flash",
34
- "Gemini 2.5 Pro": "gemini-2.5-pro",
35
- "Gemini 2.5 Flash Lite": "gemini-2.5-flash-lite",
36
- "Gemini 2.0 Flash": "gemini-2.0-flash"
 
37
  }
38
 
39
- def allowed_file(filename):
40
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
 
 
 
 
41
 
42
- def configure_tools() -> List[types.Tool]:
43
- """Configure tous les outils par défaut"""
44
- tools = []
45
- tools.append(types.Tool(code_execution=types.ToolCodeExecution()))
46
- tools.append(types.Tool(google_search=types.GoogleSearch()))
47
- tools.append(types.Tool(url_context=types.UrlContext()))
48
- return tools
49
-
50
- def get_generation_config(
51
- model: str,
52
- thinking_enabled: bool = True,
53
- include_thoughts: bool = False
54
- ) -> types.GenerateContentConfig:
55
- """Configuration optimisée par défaut"""
56
- config_dict = {
57
- "temperature": 0.7,
58
- "max_output_tokens": 8192,
59
- "top_p": 0.9,
60
- "top_k": 40,
61
- "tools": configure_tools()
62
- }
63
-
64
- if thinking_enabled:
65
- config_dict["thinking_config"] = types.ThinkingConfig(
66
- thinking_budget=-1, # Dynamique par défaut
67
- include_thoughts=include_thoughts
68
- )
69
-
70
- return types.GenerateContentConfig(**config_dict)
71
-
72
- def process_uploaded_file(file) -> Optional[types.Part]:
73
- """Traite les fichiers uploadés"""
74
- if not file:
75
- return None
76
-
77
- file_bytes = file.read()
78
- mime_type = file.content_type
79
-
80
- # Réinitialiser le pointeur du fichier
81
- file.seek(0)
82
-
83
- # Gestion des différents types de fichiers
84
- if mime_type.startswith("image/"):
85
- return types.Part.from_bytes(data=file_bytes, mime_type=mime_type)
86
- elif mime_type == "application/pdf":
87
- return types.Part.from_bytes(data=file_bytes, mime_type=mime_type)
88
- elif mime_type.startswith("video/"):
89
- return types.Part.from_bytes(data=file_bytes, mime_type=mime_type)
90
- elif mime_type in ["text/plain", "text/csv", "application/json"]:
91
- return types.Part.from_bytes(data=file_bytes, mime_type=mime_type)
92
-
93
- return None
94
 
95
  @app.route('/')
96
  def index():
97
- """Page principale"""
98
- return render_template('index.html', models=MODELS)
99
 
100
- @app.route('/send_message', methods=['POST'])
101
- def send_message():
102
- """Endpoint pour envoyer un message avec streaming"""
103
- def generate():
104
- try:
105
- # Récupération des données
106
- data = request.get_json()
107
- message = data.get('message', '')
108
- model = data.get('model', 'gemini-2.5-flash')
109
- thinking_enabled = data.get('thinking_enabled', True)
110
- include_thoughts = data.get('include_thoughts', False)
111
-
112
- if not message.strip():
113
- yield f"data: {json.dumps({'error': 'Message vide'})}\n\n"
114
- return
115
-
116
- # Configuration de génération
117
- generation_config = get_generation_config(
118
- model=model,
119
- thinking_enabled=thinking_enabled,
120
- include_thoughts=include_thoughts
121
  )
122
-
123
- # Initialisation ou récupération du chat
124
- chat_id = session.get('chat_id')
125
- if not chat_id:
126
- chat_id = str(uuid.uuid4())
127
- session['chat_id'] = chat_id
128
- session['chat'] = client.chats.create(
129
- model=model,
130
- config=generation_config
131
- )
132
-
133
- chat = session.get('chat')
134
- if not chat:
135
- session['chat'] = client.chats.create(
136
- model=model,
137
- config=generation_config
138
- )
139
- chat = session['chat']
140
-
141
- # Préparation du contenu
142
- contents = [message]
143
-
144
- # Envoi du message avec streaming
145
- response_stream = chat.send_message_stream(
146
- contents,
147
  config=generation_config
148
  )
149
-
150
- # Streaming de la réponse
151
- full_response = ""
152
- thoughts_content = ""
153
-
154
- for chunk in response_stream:
155
- for part in chunk.candidates[0].content.parts:
156
- if part.text:
157
- if part.thought and include_thoughts:
158
- thoughts_content += part.text
159
- yield f"data: {json.dumps({'type': 'thought', 'content': part.text})}\n\n"
160
- else:
161
- full_response += part.text
162
- yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
163
-
164
- # Message de fin
165
- yield f"data: {json.dumps({'type': 'end', 'full_response': full_response})}\n\n"
166
-
167
- except Exception as e:
168
- yield f"data: {json.dumps({'error': str(e)})}\n\n"
169
-
170
- return Response(generate(), mimetype='text/event-stream')
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
- @app.route('/upload_file', methods=['POST'])
173
  def upload_file():
174
- """Endpoint pour uploader des fichiers"""
175
  try:
176
  if 'file' not in request.files:
177
- return jsonify({'error': 'Aucun fichier fourni'}), 400
178
 
179
  file = request.files['file']
180
  if file.filename == '':
181
- return jsonify({'error': 'Aucun fichier sélectionné'}), 400
182
 
183
- if file and allowed_file(file.filename):
184
- filename = secure_filename(file.filename)
185
- file_id = str(uuid.uuid4())
186
- file_path = os.path.join(app.config['UPLOAD_FOLDER'], f"{file_id}_{filename}")
187
-
188
- # Sauvegarder le fichier
189
- file.save(file_path)
190
-
191
- # Traiter le fichier pour Gemini
192
- with open(file_path, 'rb') as f:
193
- file_part = process_uploaded_file(f)
194
-
195
- if file_part:
196
- # Stocker les informations du fichier en session
197
- if 'uploaded_files' not in session:
198
- session['uploaded_files'] = {}
199
-
200
- session['uploaded_files'][file_id] = {
201
- 'filename': filename,
202
- 'path': file_path,
203
- 'mime_type': file.content_type
204
- }
205
-
206
- return jsonify({
207
- 'success': True,
208
- 'file_id': file_id,
209
- 'filename': filename,
210
- 'mime_type': file.content_type
211
- })
212
- else:
213
- # Supprimer le fichier si le traitement a échoué
214
- os.remove(file_path)
215
- return jsonify({'error': 'Type de fichier non supporté'}), 400
216
 
217
- return jsonify({'error': 'Type de fichier non autorisé'}), 400
218
-
219
  except Exception as e:
220
  return jsonify({'error': str(e)}), 500
221
 
222
- @app.route('/send_message_with_file', methods=['POST'])
223
- def send_message_with_file():
224
- """Endpoint pour envoyer un message avec fichier attaché"""
225
- def generate():
226
- try:
227
- data = request.get_json()
228
- message = data.get('message', '')
229
- model = data.get('model', 'gemini-2.5-flash')
230
- thinking_enabled = data.get('thinking_enabled', True)
231
- include_thoughts = data.get('include_thoughts', False)
232
- file_id = data.get('file_id')
233
-
234
- if not message.strip():
235
- yield f"data: {json.dumps({'error': 'Message vide'})}\n\n"
236
- return
237
-
238
- # Configuration de génération
239
- generation_config = get_generation_config(
240
- model=model,
241
- thinking_enabled=thinking_enabled,
242
- include_thoughts=include_thoughts
243
  )
244
-
245
- # Récupération du chat
246
- chat = session.get('chat')
247
- if not chat:
248
- session['chat'] = client.chats.create(
249
- model=model,
250
- config=generation_config
251
- )
252
- chat = session['chat']
253
-
254
- # Préparation du contenu
255
- contents = [message]
256
-
257
- # Ajout du fichier si présent
258
- if file_id and 'uploaded_files' in session and file_id in session['uploaded_files']:
259
- file_info = session['uploaded_files'][file_id]
260
- with open(file_info['path'], 'rb') as f:
261
- file_part = process_uploaded_file(f)
262
- if file_part:
263
- contents.append(file_part)
264
-
265
- # Envoi du message avec streaming
266
- response_stream = chat.send_message_stream(
267
- contents,
268
  config=generation_config
269
  )
270
-
271
- # Streaming de la réponse
272
- full_response = ""
273
-
274
- for chunk in response_stream:
275
- for part in chunk.candidates[0].content.parts:
276
- if part.text:
277
- if part.thought and include_thoughts:
278
- yield f"data: {json.dumps({'type': 'thought', 'content': part.text})}\n\n"
279
- else:
280
- full_response += part.text
281
- yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
282
-
283
- # Message de fin
284
- yield f"data: {json.dumps({'type': 'end', 'full_response': full_response})}\n\n"
285
-
286
- except Exception as e:
287
- yield f"data: {json.dumps({'error': str(e)})}\n\n"
288
-
289
- return Response(generate(), mimetype='text/event-stream')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
- @app.route('/reset_chat', methods=['POST'])
292
- def reset_chat():
293
- """Reset la conversation"""
294
  try:
295
- session.pop('chat_id', None)
296
- session.pop('chat', None)
297
 
298
- # Nettoyer les fichiers uploadés
299
- if 'uploaded_files' in session:
300
- for file_info in session['uploaded_files'].values():
301
- try:
302
- if os.path.exists(file_info['path']):
303
- os.remove(file_info['path'])
304
- except:
305
- pass
306
- session.pop('uploaded_files', None)
307
 
308
  return jsonify({'success': True})
 
309
  except Exception as e:
310
  return jsonify({'error': str(e)}), 500
311
 
 
1
+ from flask import Flask, request, render_template, jsonify, Response
 
2
  import json
3
+ import os
4
  from google import genai
5
  from google.genai import types
6
+ import base64
7
  from werkzeug.utils import secure_filename
8
+ import mimetypes
 
 
 
9
 
10
  app = Flask(__name__)
11
+ app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ # Configuration du client Gemini
14
  API_KEY = "AIzaSyAMYpF67aqFnWDJESWOx1dC-w3sEU29VcM"
15
  client = genai.Client(api_key=API_KEY)
16
 
17
+ # Configuration par défaut
18
+ MODEL = "gemini-2.5-flash"
19
+ DEFAULT_CONFIG = {
20
+ "temperature": 0.7,
21
+ "max_output_tokens": 8192,
22
+ "top_p": 0.9,
23
+ "top_k": 40
24
  }
25
 
26
+ # Outils activés par défaut
27
+ DEFAULT_TOOLS = [
28
+ types.Tool(code_execution=types.ToolCodeExecution()),
29
+ types.Tool(google_search=types.GoogleSearch()),
30
+ types.Tool(url_context=types.UrlContext())
31
+ ]
32
 
33
+ # Stockage des conversations (en production, utilisez une base de données)
34
+ conversations = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  @app.route('/')
37
  def index():
38
+ return render_template('index.html')
 
39
 
40
+ @app.route('/chat', methods=['POST'])
41
+ def chat():
42
+ try:
43
+ data = request.get_json()
44
+ message = data.get('message', '')
45
+ thinking_enabled = data.get('thinking_enabled', True)
46
+ conversation_id = data.get('conversation_id', 'default')
47
+
48
+ # Configuration du thinking
49
+ config_dict = DEFAULT_CONFIG.copy()
50
+ if thinking_enabled:
51
+ config_dict["thinking_config"] = types.ThinkingConfig(
52
+ thinking_budget=-1, # Dynamic thinking
53
+ include_thoughts=True
 
 
 
 
 
 
 
54
  )
55
+
56
+ config_dict["tools"] = DEFAULT_TOOLS
57
+ generation_config = types.GenerateContentConfig(**config_dict)
58
+
59
+ # Gestion de la conversation
60
+ if conversation_id not in conversations:
61
+ conversations[conversation_id] = client.chats.create(
62
+ model=MODEL,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  config=generation_config
64
  )
65
+
66
+ chat = conversations[conversation_id]
67
+
68
+ # Génération de la réponse avec streaming
69
+ def generate():
70
+ try:
71
+ response_stream = chat.send_message_stream(
72
+ message,
73
+ config=generation_config
74
+ )
75
+
76
+ full_response = ""
77
+ thoughts = ""
78
+
79
+ for chunk in response_stream:
80
+ for part in chunk.candidates[0].content.parts:
81
+ if part.text:
82
+ if part.thought and thinking_enabled:
83
+ thoughts += part.text
84
+ yield f"data: {json.dumps({'type': 'thought', 'content': part.text})}\n\n"
85
+ else:
86
+ full_response += part.text
87
+ yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
88
+
89
+ # Signal de fin
90
+ yield f"data: {json.dumps({'type': 'end'})}\n\n"
91
+
92
+ except Exception as e:
93
+ yield f"data: {json.dumps({'type': 'error', 'content': str(e)})}\n\n"
94
+
95
+ return Response(generate(), mimetype='text/plain')
96
+
97
+ except Exception as e:
98
+ return jsonify({'error': str(e)}), 500
99
 
100
+ @app.route('/upload', methods=['POST'])
101
  def upload_file():
 
102
  try:
103
  if 'file' not in request.files:
104
+ return jsonify({'error': 'No file uploaded'}), 400
105
 
106
  file = request.files['file']
107
  if file.filename == '':
108
+ return jsonify({'error': 'No file selected'}), 400
109
 
110
+ # Lire le fichier
111
+ file_bytes = file.read()
112
+ mime_type = file.content_type or mimetypes.guess_type(file.filename)[0]
113
+
114
+ # Encoder en base64 pour le stockage temporaire
115
+ file_b64 = base64.b64encode(file_bytes).decode()
116
+
117
+ return jsonify({
118
+ 'success': True,
119
+ 'filename': file.filename,
120
+ 'mime_type': mime_type,
121
+ 'data': file_b64
122
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
 
 
124
  except Exception as e:
125
  return jsonify({'error': str(e)}), 500
126
 
127
+ @app.route('/chat_with_file', methods=['POST'])
128
+ def chat_with_file():
129
+ try:
130
+ data = request.get_json()
131
+ message = data.get('message', '')
132
+ file_data = data.get('file_data')
133
+ thinking_enabled = data.get('thinking_enabled', True)
134
+ conversation_id = data.get('conversation_id', 'default')
135
+
136
+ # Configuration du thinking
137
+ config_dict = DEFAULT_CONFIG.copy()
138
+ if thinking_enabled:
139
+ config_dict["thinking_config"] = types.ThinkingConfig(
140
+ thinking_budget=-1,
141
+ include_thoughts=True
 
 
 
 
 
 
142
  )
143
+
144
+ config_dict["tools"] = DEFAULT_TOOLS
145
+ generation_config = types.GenerateContentConfig(**config_dict)
146
+
147
+ # Gestion de la conversation
148
+ if conversation_id not in conversations:
149
+ conversations[conversation_id] = client.chats.create(
150
+ model=MODEL,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  config=generation_config
152
  )
153
+
154
+ chat = conversations[conversation_id]
155
+
156
+ # Préparation du contenu avec fichier
157
+ contents = [message]
158
+
159
+ if file_data:
160
+ file_bytes = base64.b64decode(file_data['data'])
161
+ file_part = types.Part.from_bytes(
162
+ data=file_bytes,
163
+ mime_type=file_data['mime_type']
164
+ )
165
+ contents.append(file_part)
166
+
167
+ # Génération de la réponse avec streaming
168
+ def generate():
169
+ try:
170
+ response_stream = chat.send_message_stream(
171
+ contents,
172
+ config=generation_config
173
+ )
174
+
175
+ for chunk in response_stream:
176
+ for part in chunk.candidates[0].content.parts:
177
+ if part.text:
178
+ if part.thought and thinking_enabled:
179
+ yield f"data: {json.dumps({'type': 'thought', 'content': part.text})}\n\n"
180
+ else:
181
+ yield f"data: {json.dumps({'type': 'text', 'content': part.text})}\n\n"
182
+
183
+ yield f"data: {json.dumps({'type': 'end'})}\n\n"
184
+
185
+ except Exception as e:
186
+ yield f"data: {json.dumps({'type': 'error', 'content': str(e)})}\n\n"
187
+
188
+ return Response(generate(), mimetype='text/plain')
189
+
190
+ except Exception as e:
191
+ return jsonify({'error': str(e)}), 500
192
 
193
+ @app.route('/reset_conversation', methods=['POST'])
194
+ def reset_conversation():
 
195
  try:
196
+ data = request.get_json()
197
+ conversation_id = data.get('conversation_id', 'default')
198
 
199
+ if conversation_id in conversations:
200
+ del conversations[conversation_id]
 
 
 
 
 
 
 
201
 
202
  return jsonify({'success': True})
203
+
204
  except Exception as e:
205
  return jsonify({'error': str(e)}), 500
206