Woziii commited on
Commit
f93e0d7
·
verified ·
1 Parent(s): d39ef67

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +451 -0
app.py ADDED
@@ -0,0 +1,451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # === Imports ===
4
+ import gradio as gr
5
+ import torch
6
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
7
+ from datetime import datetime
8
+ import os
9
+ import json
10
+ import logging
11
+
12
+ # --- Imports spécifiques pour l'AgentResearcher ---
13
+ import requests
14
+ from bs4 import BeautifulSoup
15
+ import logging
16
+ from concurrent.futures import ThreadPoolExecutor
17
+
18
+ # === Configuration du logger ===
19
+ logging.basicConfig(
20
+ level=logging.INFO,
21
+ format="%(asctime)s - %(levelname)s - %(message)s",
22
+ handlers=[
23
+ logging.FileHandler("project.log"),
24
+ logging.StreamHandler()
25
+ ]
26
+ )
27
+
28
+ # === Chargement des modèles ===
29
+ # Chargement du modèle pour l'AgentManager
30
+ manager_model_name = "meta-llama/Llama-3.1-8B-Instruct"
31
+ manager_model = AutoModelForCausalLM.from_pretrained(
32
+ manager_model_name,
33
+ device_map="auto",
34
+ torch_dtype=torch.bfloat16 # Utilisation de bfloat16 comme recommandé
35
+ )
36
+ manager_tokenizer = AutoTokenizer.from_pretrained(manager_model_name)
37
+
38
+ # AgentResearcher
39
+ researcher_model_name = "hiieu/Meta-Llama-3-8B-Instruct-function-calling-json-mode"
40
+ researcher_model = AutoModelForCausalLM.from_pretrained(
41
+ researcher_model_name,
42
+ torch_dtype=torch.bfloat16, # Utilisation de bfloat16 comme recommandé
43
+ device_map="auto",
44
+ )
45
+ researcher_tokenizer = AutoTokenizer.from_pretrained(researcher_model_name)
46
+
47
+ # AgentAnalyzer
48
+ analyzer_model_name = "jpacifico/Chocolatine-3B-Instruct-DPO-Revised"
49
+ analyzer_model = AutoModelForCausalLM.from_pretrained(
50
+ analyzer_model_name,
51
+ device_map="auto",
52
+ torch_dtype=torch.float16
53
+ )
54
+ analyzer_tokenizer = AutoTokenizer.from_pretrained(analyzer_model_name)
55
+
56
+ # AgentCoder
57
+ # AgentCoder
58
+ coder_model_name = "Qwen/Qwen2.5-Coder-14B-Instruct"
59
+ coder_model = AutoModelForCausalLM.from_pretrained(
60
+ coder_model_name,
61
+ torch_dtype="auto",
62
+ device_map="auto"
63
+ )
64
+ coder_tokenizer = AutoTokenizer.from_pretrained(coder_model_name)
65
+
66
+ # === Variables Globales ===
67
+ project_state = {
68
+ "AgentManager": {"structured_summary": None},
69
+ "AgentResearcher": {"search_results": None},
70
+ "AgentAnalyzer": {"analysis_report": None, "instruction_for_coder": None},
71
+ "AgentCoder": {"final_code": None}
72
+ }
73
+
74
+ # --- Prompts prédéfinis ---
75
+ manager_prompt_template = """
76
+ Vous êtes l'AgentManager d'un système multi-agent.
77
+
78
+ - Votre rôle est d'interagir avec l'utilisateur pour comprendre sa demande.
79
+ - Vous devez poser des questions pertinentes pour obtenir toutes les informations nécessaires.
80
+ - Une fois que vous estimez avoir suffisamment d'informations, vous générez un résumé structuré du projet.
81
+ - Vous incluez les informations des variables du projet si elles ne sont pas vides.
82
+ - Vous demandez une validation explicite à l'utilisateur pour le résumé généré.
83
+ - Vous pouvez modifier les variables du projet si l'utilisateur en fait la demande.
84
+
85
+ Variables du projet :
86
+ {variables_context}
87
+ """
88
+
89
+ researcher_prompt_template = """
90
+ System: Vous êtes un assistant de recherche. Vos tâches sont :
91
+ 1. Basé sur le résumé structuré suivant :
92
+ {structured_summary}
93
+ 2. Effectuer des recherches dans la documentation Gradio en ligne.
94
+ 3. Extraire des extraits de code ou des exemples utiles.
95
+ 4. Formater clairement les résultats pour validation.
96
+
97
+ Format de sortie :
98
+ - Documentation : ...
99
+ - Extraits de code : ...
100
+ """
101
+
102
+ analyzer_prompt_template = """
103
+ Vous êtes un assistant d'analyse. Vos tâches sont :
104
+ 1. Vérifier la cohérence des résultats de recherche avec le résumé structuré :
105
+ {structured_summary}
106
+ 2. Analyser les résultats de recherche :
107
+ {search_results}
108
+ 3. Générer un rapport indiquant si les résultats sont **valide** ou **non valide**.
109
+ 4. Si **non valide**, spécifier les éléments manquants ou incohérences.
110
+ 5. Votre réponse doit commencer par 'Validité: Oui' ou 'Validité: Non', suivi du rapport d'analyse.
111
+ """
112
+
113
+ coder_prompt_template = """
114
+ System: Vous êtes un assistant de codage. Votre tâche est de :
115
+ 1. Générer du code basé sur le résumé structuré validé suivant :
116
+ {structured_summary}
117
+ 2. Incorporer les résultats de recherche suivants :
118
+ {search_results}
119
+ """
120
+
121
+ # === Définition des fonctions pour chaque agent ===
122
+
123
+
124
+ # === Fonctions Utilitaires de l'agentManager ===
125
+ def get_variables_context():
126
+ variables = {}
127
+ for agent, data in project_state.items():
128
+ variables[agent] = {}
129
+ for key, value in data.items():
130
+ variables[agent][key] = value if value else "N/A"
131
+ variables_context = json.dumps(variables, indent=2, ensure_ascii=False)
132
+ return variables_context
133
+
134
+ def update_project_state(modifications):
135
+ for var, value in modifications.items():
136
+ keys = var.split('.')
137
+ target = project_state
138
+ for key in keys[:-1]:
139
+ target = target.get(key, {})
140
+ target[keys[-1]] = value
141
+
142
+ def extract_modifications(user_input):
143
+ # Extraction simplifiée pour l'exemple
144
+ modifications = {}
145
+ if "modifie" in user_input.lower():
146
+ import re
147
+ matches = re.findall(r"modifie la variable (\w+(?:\.\w+)*) à (.+)", user_input, re.IGNORECASE)
148
+ for match in matches:
149
+ var_name, var_value = match
150
+ modifications[var_name.strip()] = var_value.strip()
151
+ return modifications
152
+
153
+ def extract_structured_summary(response):
154
+ start_token = "Résumé Structuré :"
155
+ end_token = "Fin du Résumé"
156
+ start_index = response.find(start_token)
157
+ end_index = response.find(end_token, start_index)
158
+ if start_index != -1 and end_index != -1:
159
+ summary = response[start_index + len(start_token):end_index].strip()
160
+ return summary
161
+ else:
162
+ logging.warning("Le résumé structuré n'a pas pu être extrait.")
163
+ return None
164
+
165
+ # === AgentManager ===
166
+ def agent_manager(chat_history, user_input):
167
+ variables_context = get_variables_context()
168
+ system_prompt = manager_prompt_template.format(variables_context=variables_context)
169
+
170
+ conversation = [{"role": "system", "content": system_prompt}]
171
+
172
+ # Ajouter l'historique
173
+ for turn in chat_history:
174
+ conversation.append({"role": "user", "content": turn['user']})
175
+ conversation.append({"role": "assistant", "content": turn['assistant']})
176
+
177
+ # Ajouter l'entrée utilisateur actuelle
178
+ conversation.append({"role": "user", "content": user_input})
179
+
180
+ # Vérifier si l'utilisateur souhaite modifier des variables
181
+ modifications = extract_modifications(user_input)
182
+ if modifications:
183
+ update_project_state(modifications)
184
+ response = "Les variables ont été mises à jour selon votre demande."
185
+ chat_history.append({'user': user_input, 'assistant': response})
186
+ return response, chat_history, False
187
+
188
+ # Générer la réponse
189
+ prompt = ""
190
+ for msg in conversation:
191
+ prompt += f"{msg['role']}: {msg['content']}\n"
192
+
193
+ input_ids = manager_tokenizer.encode(prompt, return_tensors="pt").to(manager_model.device)
194
+ output_ids = manager_model.generate(
195
+ input_ids,
196
+ max_new_tokens=256,
197
+ eos_token_id=manager_tokenizer.eos_token_id,
198
+ pad_token_id=manager_tokenizer.pad_token_id,
199
+ attention_mask=input_ids.new_ones(input_ids.shape)
200
+ )
201
+ response = manager_tokenizer.decode(output_ids[0], skip_special_tokens=True)
202
+
203
+ chat_history.append({'user': user_input, 'assistant': response})
204
+
205
+ # Vérifier si un résumé a été généré pour validation
206
+ if "Validez-vous ce résumé" in response:
207
+ structured_summary = extract_structured_summary(response)
208
+ project_state["AgentManager"]["structured_summary"] = structured_summary
209
+ return response, chat_history, True # Indique que le résumé est prêt pour validation
210
+ else:
211
+ return response, chat_history, False
212
+
213
+ # --- AgentResearcher ---
214
+ # Fonctions spécifiques pour les recherches dynamiques
215
+
216
+ def fetch_webpage(url: str) -> str:
217
+ """
218
+ Télécharge le contenu HTML d'une URL donnée.
219
+ """
220
+ try:
221
+ response = requests.get(url, timeout=10)
222
+ response.raise_for_status()
223
+ logging.info(f"Page téléchargée avec succès : {url}")
224
+ return response.text
225
+ except requests.RequestException as e:
226
+ logging.error(f"Erreur lors de la récupération de la page {url}: {e}")
227
+ return ""
228
+
229
+ def extract_information_from_html(html: str, keyword: str) -> list:
230
+ """
231
+ Extrait des informations pertinentes depuis le HTML en fonction d'un mot-clé.
232
+ """
233
+ try:
234
+ soup = BeautifulSoup(html, "html.parser")
235
+ results = []
236
+ for code_block in soup.find_all("code"):
237
+ if keyword.lower() in code_block.get_text().lower():
238
+ results.append(code_block.get_text())
239
+ logging.info(f"Nombre de sections extraites pour '{keyword}' : {len(results)}")
240
+ return results
241
+ except Exception as e:
242
+ logging.error(f"Erreur lors de l'extraction des informations : {e}")
243
+ return []
244
+
245
+ def search_gradio_docs(query: str) -> dict:
246
+ """
247
+ Recherche dans la documentation Gradio les sections pertinentes pour une requête donnée.
248
+ """
249
+ url = "https://gradio.app/docs/"
250
+ logging.info(f"Lancement de la recherche pour la requête : {query}")
251
+ html_content = fetch_webpage(url)
252
+ if not html_content:
253
+ return {"error": "Impossible de télécharger la documentation Gradio."}
254
+ results = extract_information_from_html(html_content, query)
255
+ if not results:
256
+ return {"error": f"Aucun résultat trouvé pour '{query}'."}
257
+ return {"query": query, "results": results}
258
+
259
+ def agent_researcher():
260
+ structured_summary = project_state["AgentManager"]["structured_summary"]
261
+ if not structured_summary:
262
+ return "Le résumé structuré n'est pas disponible."
263
+
264
+ # Création du prompt en utilisant apply_chat_template
265
+ messages = [
266
+ {"role": "system", "content": "Vous êtes un assistant de recherche. Vous devez répondre en JSON avec les clés 'documentation' et 'extraits_code'."},
267
+ {"role": "user", "content": researcher_prompt_template.format(structured_summary=structured_summary)}
268
+ ]
269
+
270
+ input_ids = researcher_tokenizer.apply_chat_template(
271
+ messages,
272
+ add_generation_prompt=True,
273
+ return_tensors="pt"
274
+ ).to(researcher_model.device)
275
+
276
+ terminators = [
277
+ researcher_tokenizer.eos_token_id,
278
+ researcher_tokenizer.convert_tokens_to_ids("<|eot_id|>")
279
+ ]
280
+
281
+ output_ids = researcher_model.generate(
282
+ input_ids,
283
+ max_new_tokens=512,
284
+ eos_token_id=terminators,
285
+ do_sample=True,
286
+ temperature=0.6,
287
+ top_p=0.9,
288
+ )
289
+ response_ids = output_ids[0][input_ids.shape[-1]:]
290
+ response = researcher_tokenizer.decode(response_ids, skip_special_tokens=True)
291
+
292
+ # Parser la réponse JSON
293
+ try:
294
+ response_json = json.loads(response)
295
+ except json.JSONDecodeError:
296
+ logging.error("La réponse du modèle n'est pas un JSON valide.")
297
+ response_json = {"documentation": "", "extraits_code": ""}
298
+
299
+ # Recherches dynamiques
300
+ search_results = search_gradio_docs(structured_summary)
301
+ if "error" in search_results:
302
+ logging.error(search_results["error"])
303
+ return search_results["error"]
304
+
305
+ # Mise à jour de l'état global
306
+ project_state["AgentResearcher"]["search_results"] = {
307
+ "model_response": response_json,
308
+ "dynamic_results": search_results["results"]
309
+ }
310
+
311
+ return f"Résultats de l'AgentResearcher :\n{response_json}\n\nRésultats dynamiques :\n{search_results['results']}"
312
+
313
+ # --- AgentAnalyzer ---
314
+ def agent_analyzer():
315
+ structured_summary = project_state["AgentManager"]["structured_summary"]
316
+ search_results = project_state["AgentResearcher"]["search_results"]
317
+ if not structured_summary or not search_results:
318
+ return "Les informations nécessaires ne sont pas disponibles pour l'analyse."
319
+
320
+ # Création du prompt avec apply_chat_template
321
+ messages = [
322
+ {"role": "system", "content": "Vous êtes un assistant d'analyse. Votre tâche est d'analyser les résultats de recherche et de vérifier leur cohérence avec le résumé structuré."},
323
+ {"role": "user", "content": analyzer_prompt_template.format(
324
+ structured_summary=structured_summary,
325
+ search_results=json.dumps(search_results, ensure_ascii=False)
326
+ )}
327
+ ]
328
+ prompt = analyzer_tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
329
+
330
+ # Création du pipeline
331
+ analyzer_pipeline = transformers.pipeline(
332
+ "text-generation",
333
+ model=analyzer_model,
334
+ tokenizer=analyzer_tokenizer,
335
+ device_map="auto"
336
+ )
337
+
338
+ # Génération du rapport d'analyse
339
+ sequences = analyzer_pipeline(
340
+ prompt,
341
+ do_sample=True,
342
+ temperature=0.7,
343
+ top_p=0.9,
344
+ num_return_sequences=1,
345
+ max_new_tokens=256,
346
+ )
347
+ analysis_report = sequences[0]['generated_text']
348
+
349
+ # Mise à jour de l'état global
350
+ project_state["AgentAnalyzer"]["analysis_report"] = analysis_report
351
+
352
+ # Détermination de la validité
353
+ if "Validité: Oui" in analysis_report:
354
+ instruction_for_coder = f"Générer du code basé sur :\n{structured_summary}\n\nRésultats de recherche :\n{search_results}"
355
+ project_state["AgentAnalyzer"]["instruction_for_coder"] = instruction_for_coder
356
+ return f"Rapport valide.\nInstructions pour l'AgentCoder prêtes."
357
+ elif "Validité: Non" in analysis_report:
358
+ project_state["AgentAnalyzer"]["instruction_for_coder"] = None
359
+ # Retourner le rapport à l'AgentManager pour clarification
360
+ return f"Rapport non valide. Besoin de clarification.\n{analysis_report}"
361
+ else:
362
+ project_state["AgentAnalyzer"]["instruction_for_coder"] = None
363
+ return f"Le rapport d'analyse ne contient pas d'information claire sur la validité. Besoin de clarification.\n{analysis_report}"
364
+
365
+ # --- AgentCoder ---
366
+ def agent_coder():
367
+ instruction_for_coder = project_state["AgentAnalyzer"]["instruction_for_coder"]
368
+ if not instruction_for_coder:
369
+ return "Les instructions pour le code ne sont pas disponibles."
370
+
371
+ # Création des messages avec apply_chat_template
372
+ messages = [
373
+ {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
374
+ {"role": "user", "content": instruction_for_coder}
375
+ ]
376
+ prompt = coder_tokenizer.apply_chat_template(
377
+ messages,
378
+ tokenize=False,
379
+ add_generation_prompt=True
380
+ )
381
+
382
+ # Préparation des entrées du modèle
383
+ model_inputs = coder_tokenizer(prompt, return_tensors="pt").to(coder_model.device)
384
+
385
+ # Génération du code
386
+ generated_ids = coder_model.generate(
387
+ **model_inputs,
388
+ max_new_tokens=1024,
389
+ temperature=0.7,
390
+ top_p=0.9,
391
+ )
392
+ # Exclure les tokens du prompt des sorties
393
+ generated_ids = generated_ids[:, model_inputs.input_ids.shape[-1]:]
394
+ final_code = coder_tokenizer.decode(generated_ids[0], skip_special_tokens=True)
395
+
396
+ # Mise à jour de l'état global
397
+ project_state["AgentCoder"]["final_code"] = final_code
398
+
399
+ return f"Code généré par l'AgentCoder :\n{final_code}"
400
+
401
+ # === Fonction d'interaction avec l'utilisateur ===
402
+ def user_interaction(message, chat_history):
403
+ if chat_history is None:
404
+ chat_history = []
405
+
406
+ # Vérifier si nous attendons une validation
407
+ if chat_history and isinstance(chat_history[-1], dict) and chat_history[-1].get('status') == 'awaiting_validation':
408
+ # Traiter la validation de l'utilisateur
409
+ user_validation = message
410
+ if user_validation.lower() in ["oui", "yes"]:
411
+ # Procéder avec les agents
412
+ researcher_response = agent_researcher()
413
+ analyzer_response = agent_analyzer()
414
+ if "valide" in analyzer_response.lower():
415
+ coder_response = agent_coder()
416
+ response = coder_response
417
+ else:
418
+ response = analyzer_response
419
+ else:
420
+ response = "Le résumé structuré n'a pas été validé. Veuillez fournir plus de détails."
421
+ # Retirer le statut de chat_history
422
+ chat_history.pop()
423
+ chat_history.append({'user': message, 'assistant': response})
424
+ return chat_history, chat_history
425
+ else:
426
+ # Interaction régulière avec l'AgentManager
427
+ response, chat_history, is_summary_ready = agent_manager(chat_history, message)
428
+ if is_summary_ready:
429
+ # Indiquer que nous attendons une validation
430
+ chat_history.append({'status': 'awaiting_validation'})
431
+ return chat_history, chat_history
432
+
433
+ # === Interface Gradio ===
434
+ with gr.Blocks() as interface:
435
+ chatbot = gr.Chatbot()
436
+ state = gr.State([])
437
+ msg = gr.Textbox(placeholder="Entrez votre message ici...")
438
+ send_btn = gr.Button("Envoyer")
439
+
440
+ def respond(message, chat_history):
441
+ updated_chat_history, _ = user_interaction(message, chat_history)
442
+ bot_message = updated_chat_history[-1]['assistant']
443
+ chatbot.append((message, bot_message))
444
+ return chatbot, updated_chat_history
445
+
446
+ send_btn.click(respond, inputs=[msg, state], outputs=[chatbot, state])
447
+ msg.submit(respond, inputs=[msg, state], outputs=[chatbot, state])
448
+
449
+ if __name__ == "__main__":
450
+ interface.launch()
451
+