File size: 21,162 Bytes
f93e0d7
 
 
 
 
fd3bbca
f93e0d7
 
 
 
089cebf
f93e0d7
 
 
fd3bbca
f93e0d7
8f371b4
fd3bbca
f93e0d7
 
 
 
 
 
 
 
 
fd3bbca
 
 
 
8f371b4
fd3bbca
 
f93e0d7
fd3bbca
f93e0d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8f371b4
f93e0d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd3bbca
f93e0d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd3bbca
f93e0d7
fd3bbca
f93e0d7
fd3bbca
f93e0d7
 
 
fd3bbca
f93e0d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd3bbca
f93e0d7
 
 
 
 
 
fd3bbca
f93e0d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd3bbca
f93e0d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd3bbca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f93e0d7
fd3bbca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f93e0d7
fd3bbca
 
 
 
f93e0d7
fd3bbca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f93e0d7
fd3bbca
f93e0d7
fd3bbca
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# -*- coding: utf-8 -*-

# === Imports ===
import gradio as gr
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer, pipeline
from datetime import datetime
import os
import json
import logging
from huggingface_hub import login
import requests
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor
import re

os.environ['PYTORCH_CUDA_ALLOC_CONF'] = expandable_segments:True
# --- Configuration du logger ---
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("project.log"),
        logging.StreamHandler()
    ]
)

# --- Authentification Hugging Face ---
# Assurez-vous que la variable d'environnement HF_TOKEN est définie avec votre token Hugging Face
# Sinon, vous pouvez la définir directement ici
# os.environ["HF_TOKEN"] = "votre_token_huggingface"

login(token=os.environ["HF_TOKEN"])

# === Chargement des modèles ===
# AgentManager
manager_model_name = "meta-llama/Llama-3.1-8B-Instruct"
manager_model = AutoModelForCausalLM.from_pretrained(
    manager_model_name,
    device_map="auto",
    torch_dtype=torch.bfloat16  # Utilisation de bfloat16 comme recommandé
)
manager_tokenizer = AutoTokenizer.from_pretrained(manager_model_name)

# AgentResearcher
researcher_model_name = "hiieu/Meta-Llama-3-8B-Instruct-function-calling-json-mode"
researcher_model = AutoModelForCausalLM.from_pretrained(
    researcher_model_name,
    torch_dtype=torch.bfloat16,  # Utilisation de bfloat16 comme recommandé
    device_map="auto",
)
researcher_tokenizer = AutoTokenizer.from_pretrained(researcher_model_name)

# AgentAnalyzer
analyzer_model_name = "jpacifico/Chocolatine-3B-Instruct-DPO-Revised"
analyzer_model = AutoModelForCausalLM.from_pretrained(
    analyzer_model_name,
    device_map="auto",
    torch_dtype=torch.float16
)
analyzer_tokenizer = AutoTokenizer.from_pretrained(analyzer_model_name)

# AgentCoder
coder_model_name = "Qwen/Qwen2.5-Coder-7B-Instruct"
coder_model = AutoModelForCausalLM.from_pretrained(
    coder_model_name,
    torch_dtype="auto",
    device_map="auto"
)
coder_tokenizer = AutoTokenizer.from_pretrained(coder_model_name)

# === Variables Globales ===
project_state = {
    "AgentManager": {"structured_summary": None},
    "AgentResearcher": {"search_results": None},
    "AgentAnalyzer": {"analysis_report": None, "instruction_for_coder": None},
    "AgentCoder": {"final_code": None}
}

# --- Prompts prédéfinis ---
manager_prompt_template = """
Vous êtes l'AgentManager d'un système multi-agent.

- Votre rôle est d'interagir avec l'utilisateur pour comprendre sa demande.
- Vous devez poser des questions pertinentes pour obtenir toutes les informations nécessaires.
- Une fois que vous estimez avoir suffisamment d'informations, vous générez un résumé structuré du projet.
- Vous incluez les informations des variables du projet si elles ne sont pas vides.
- Vous demandez une validation explicite à l'utilisateur pour le résumé généré.
- Vous pouvez modifier les variables du projet si l'utilisateur en fait la demande.

Variables du projet :
{variables_context}
"""

researcher_prompt_template = """
System: Vous êtes un assistant de recherche. Vos tâches sont :
1. Basé sur le résumé structuré suivant :
{structured_summary}
2. Effectuer des recherches dans la documentation Gradio en ligne.
3. Extraire des extraits de code ou des exemples utiles.
4. Formater clairement les résultats pour validation.

Format de sortie :
- Documentation : ...
- Extraits de code : ...
"""

analyzer_prompt_template = """
Vous êtes un assistant d'analyse. Vos tâches sont :
1. Vérifier la cohérence des résultats de recherche avec le résumé structuré :
{structured_summary}
2. Analyser les résultats de recherche :
{search_results}
3. Générer un rapport indiquant si les résultats sont **valide** ou **non valide**.
4. Si **non valide**, spécifier les éléments manquants ou incohérences.
5. Votre réponse doit commencer par 'Validité: Oui' ou 'Validité: Non', suivi du rapport d'analyse.
"""

# === Fonctions Utilitaires de l'AgentManager ===
def get_variables_context():
    variables = {}
    for agent, data in project_state.items():
        variables[agent] = {}
        for key, value in data.items():
            variables[agent][key] = value if value else "N/A"
    variables_context = json.dumps(variables, indent=2, ensure_ascii=False)
    return variables_context

def update_project_state(modifications):
    for var, value in modifications.items():
        keys = var.split('.')
        target = project_state
        for key in keys[:-1]:
            target = target.get(key, {})
        target[keys[-1]] = value

def extract_modifications(user_input):
    modifications = {}
    if "modifie" in user_input.lower():
        matches = re.findall(r"modifie la variable (\w+(?:\.\w+)*) à (.+)", user_input, re.IGNORECASE)
        for match in matches:
            var_name, var_value = match
            modifications[var_name.strip()] = var_value.strip()
    return modifications

def extract_structured_summary(response):
    start_token = "Résumé Structuré :"
    end_token = "Fin du Résumé"
    start_index = response.find(start_token)
    end_index = response.find(end_token, start_index)
    if start_index != -1 and end_index != -1:
        summary = response[start_index + len(start_token):end_index].strip()
        return summary
    else:
        logging.warning("Le résumé structuré n'a pas pu être extrait.")
        return None

# === AgentManager ===
def agent_manager(chat_history, user_input):
    variables_context = get_variables_context()
    system_prompt = manager_prompt_template.format(variables_context=variables_context)

    conversation = [{"role": "system", "content": system_prompt}]

    # Ajouter l'historique
    for turn in chat_history:
        conversation.append({"role": "user", "content": turn['user']})
        conversation.append({"role": "assistant", "content": turn['assistant']})

    # Ajouter l'entrée utilisateur actuelle
    conversation.append({"role": "user", "content": user_input})

    # Vérifier si l'utilisateur souhaite modifier des variables
    modifications = extract_modifications(user_input)
    if modifications:
        update_project_state(modifications)
        response = "Les variables ont été mises à jour selon votre demande."
        chat_history.append({'user': user_input, 'assistant': response})
        return response, chat_history, False

    # Générer la réponse
    prompt = manager_tokenizer.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)

    input_ids = manager_tokenizer(prompt, return_tensors="pt").to(manager_model.device)
    output_ids = manager_model.generate(
        input_ids["input_ids"],
        max_new_tokens=256,
        eos_token_id=manager_tokenizer.eos_token_id,
        pad_token_id=manager_tokenizer.pad_token_id,
        attention_mask=input_ids["attention_mask"]
    )
    response = manager_tokenizer.decode(output_ids[0], skip_special_tokens=True)

    chat_history.append({'user': user_input, 'assistant': response})

    # Vérifier si un résumé a été généré pour validation
    if "Validez-vous ce résumé" in response:
        structured_summary = extract_structured_summary(response)
        project_state["AgentManager"]["structured_summary"] = structured_summary
        return response, chat_history, True  # Indique que le résumé est prêt pour validation
    else:
        return response, chat_history, False

# --- AgentResearcher ---
def fetch_webpage(url: str) -> str:
    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        logging.info(f"Page téléchargée avec succès : {url}")
        return response.text
    except requests.RequestException as e:
        logging.error(f"Erreur lors de la récupération de la page {url}: {e}")
        return ""

def extract_information_from_html(html: str, keyword: str) -> list:
    try:
        soup = BeautifulSoup(html, "html.parser")
        results = []
        for code_block in soup.find_all("code"):
            if keyword.lower() in code_block.get_text().lower():
                results.append(code_block.get_text())
        logging.info(f"Nombre de sections extraites pour '{keyword}' : {len(results)}")
        return results
    except Exception as e:
        logging.error(f"Erreur lors de l'extraction des informations : {e}")
        return []

def search_gradio_docs(query: str) -> dict:
    url = "https://gradio.app/docs/"
    logging.info(f"Lancement de la recherche pour la requête : {query}")
    html_content = fetch_webpage(url)
    if not html_content:
        return {"error": "Impossible de télécharger la documentation Gradio."}
    results = extract_information_from_html(html_content, query)
    if not results:
        return {"error": f"Aucun résultat trouvé pour '{query}'."}
    return {"query": query, "results": results}

def agent_researcher():
    structured_summary = project_state["AgentManager"]["structured_summary"]
    if not structured_summary:
        return "Le résumé structuré n'est pas disponible."

    # Création du prompt en utilisant apply_chat_template
    messages = [
        {"role": "system", "content": "Vous êtes un assistant de recherche. Vous devez répondre en JSON avec les clés 'documentation' et 'extraits_code'."},
        {"role": "user", "content": researcher_prompt_template.format(structured_summary=structured_summary)}
    ]

    input_ids = researcher_tokenizer.apply_chat_template(
        messages,
        add_generation_prompt=True,
        return_tensors="pt"
    ).to(researcher_model.device)

    terminators = [
        researcher_tokenizer.eos_token_id,
        researcher_tokenizer.convert_tokens_to_ids("<|eot_id|>")
    ]

    output_ids = researcher_model.generate(
        input_ids["input_ids"],
        max_new_tokens=512,
        eos_token_id=terminators,
        do_sample=True,
        temperature=0.6,
        top_p=0.9,
    )
    response_ids = output_ids[0][input_ids["input_ids"].shape[-1]:]
    response = researcher_tokenizer.decode(response_ids, skip_special_tokens=True)

    # Parser la réponse JSON
    try:
        response_json = json.loads(response)
    except json.JSONDecodeError:
        logging.error("La réponse du modèle n'est pas un JSON valide.")
        response_json = {"documentation": "", "extraits_code": ""}

    # Recherches dynamiques
    search_results = search_gradio_docs(structured_summary)
    if "error" in search_results:
        logging.error(search_results["error"])
        return search_results["error"]

    # Mise à jour de l'état global
    project_state["AgentResearcher"]["search_results"] = {
        "model_response": response_json,
        "dynamic_results": search_results["results"]
    }

    return f"Résultats de l'AgentResearcher :\n{response_json}\n\nRésultats dynamiques :\n{search_results['results']}"

# --- AgentAnalyzer ---
def agent_analyzer():
    structured_summary = project_state["AgentManager"]["structured_summary"]
    search_results = project_state["AgentResearcher"]["search_results"]
    if not structured_summary or not search_results:
        return "Les informations nécessaires ne sont pas disponibles pour l'analyse."

    # Création du prompt avec apply_chat_template
    messages = [
        {"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é."},
        {"role": "user", "content": analyzer_prompt_template.format(
            structured_summary=structured_summary,
            search_results=json.dumps(search_results, ensure_ascii=False)
        )}
    ]
    prompt = analyzer_tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)

    # Création du pipeline
    analyzer_pipeline = pipeline(
        "text-generation",
        model=analyzer_model,
        tokenizer=analyzer_tokenizer,
        device_map="auto"
    )

    # Génération du rapport d'analyse
    sequences = analyzer_pipeline(
        prompt,
        do_sample=True,
        temperature=0.7,
        top_p=0.9,
        num_return_sequences=1,
        max_new_tokens=256,
    )
    analysis_report = sequences[0]['generated_text']

    # Mise à jour de l'état global
    project_state["AgentAnalyzer"]["analysis_report"] = analysis_report

    # Détermination de la validité
    if "Validité: Oui" in analysis_report:
        instruction_for_coder = f"Générer du code basé sur :\n{structured_summary}\n\nRésultats de recherche :\n{search_results}"
        project_state["AgentAnalyzer"]["instruction_for_coder"] = instruction_for_coder
        return f"Rapport valide.\nInstructions pour l'AgentCoder prêtes."
    elif "Validité: Non" in analysis_report:
        project_state["AgentAnalyzer"]["instruction_for_coder"] = None
        # Retourner le rapport à l'AgentManager pour clarification
        return f"Rapport non valide. Besoin de clarification.\n{analysis_report}"
    else:
        project_state["AgentAnalyzer"]["instruction_for_coder"] = None
        return f"Le rapport d'analyse ne contient pas d'information claire sur la validité. Besoin de clarification.\n{analysis_report}"

# --- AgentCoder ---
def agent_coder():
    instruction_for_coder = project_state["AgentAnalyzer"]["instruction_for_coder"]
    if not instruction_for_coder:
        return "Les instructions pour le code ne sont pas disponibles."

    # Création des messages avec apply_chat_template
    messages = [
        {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
        {"role": "user", "content": instruction_for_coder}
    ]
    prompt = coder_tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True
    )

    # Préparation des entrées du modèle
    model_inputs = coder_tokenizer(prompt, return_tensors="pt").to(coder_model.device)

    # Génération du code
    generated_ids = coder_model.generate(
        **model_inputs,
        max_new_tokens=1024,
        temperature=0.7,
        top_p=0.9,
    )
    # Exclure les tokens du prompt des sorties
    generated_ids = generated_ids[:, model_inputs.input_ids.shape[-1]:]
    final_code = coder_tokenizer.decode(generated_ids[0], skip_special_tokens=True)

    # Mise à jour de l'état global
    project_state["AgentCoder"]["final_code"] = final_code

    return f"Code généré par l'AgentCoder :\n{final_code}"

# === Fonction d'interaction avec l'utilisateur ===
def user_interaction(message, chat_history):
    if chat_history is None:
        chat_history = []

    # Vérifier si nous attendons une validation
    if chat_history and isinstance(chat_history[-1], dict) and chat_history[-1].get('status') == 'awaiting_validation':
        # Traiter la validation de l'utilisateur
        user_validation = message
        if user_validation.lower() in ["oui", "yes"]:
            # Procéder avec les agents
            researcher_response = agent_researcher()
            analyzer_response = agent_analyzer()
            if "valide" in analyzer_response.lower():
                coder_response = agent_coder()
                response = coder_response
            else:
                response = analyzer_response
        else:
            response = "Le résumé structuré n'a pas été validé. Veuillez fournir plus de détails."
        # Retirer le statut de chat_history
        chat_history.pop()
        chat_history.append({'user': message, 'assistant': response})
        return chat_history, chat_history
    else:
        # Interaction régulière avec l'AgentManager
        response, chat_history, is_summary_ready = agent_manager(chat_history, message)
        if is_summary_ready:
            # Indiquer que nous attendons une validation
            chat_history.append({'status': 'awaiting_validation'})
        return chat_history, chat_history

# === Interface Gradio ===
with gr.Blocks() as interface:
    with gr.Tabs():
        # Onglet "Chat"
        with gr.Tab("Chat"):
            with gr.Row():
                # Colonne gauche : Chat principal
                with gr.Column(scale=3):
                    chatbot = gr.Chatbot(label="Chat Principal")
                    state = gr.State([])  # Historique des messages
                    msg = gr.Textbox(placeholder="Entrez votre message ici...")
                    send_btn = gr.Button("Envoyer")

                # Colonne droite : Statut des agents et logs
                with gr.Column(scale=2):
                    agent_status_chat = gr.Chatbot(label="Suivi des Agents")
                    logs_box = gr.Textbox(
                        value="",
                        lines=10,
                        interactive=False,
                        placeholder="Logs d'exécution",
                        label="Logs",
                    )

        # Onglet "Output"
        with gr.Tab("Output"):
            output_code = gr.Code(
                value="# Le code généré sera affiché ici.\n",
                language="python",
                label="Code Final",
            )

    # === Fonctions de mise à jour des statuts et logs ===
    def update_agent_status_and_logs(chat_history):
        """
        Met à jour les messages des agents et les logs d'exécution.
        """
        # Initialisation des messages
        agent_status_messages = []

        # AgentManager
        structured_summary = project_state["AgentManager"]["structured_summary"]
        if structured_summary:
            manager_message = f"AgentManager : Résumé structuré disponible.\n{structured_summary}"
        else:
            manager_message = "AgentManager : En attente d'informations de l'utilisateur."
        agent_status_messages.append(("AgentManager", manager_message))

        # AgentResearcher
        researcher_result = project_state["AgentResearcher"]["search_results"]
        if researcher_result:
            researcher_message = (
                f"AgentResearcher : Résultats obtenus\n"
                f"Documentation : {researcher_result.get('documentation', 'N/A')}\n"
                f"Extraits de code : {researcher_result.get('extraits_code', 'N/A')}"
            )
        else:
            researcher_message = "AgentResearcher : Recherche en cours..."
        agent_status_messages.append(("AgentResearcher", researcher_message))

        # AgentAnalyzer
        analysis_report = project_state["AgentAnalyzer"]["analysis_report"]
        if analysis_report:
            analyzer_message = (
                f"AgentAnalyzer : Analyse terminée\n"
                f"{analysis_report}"
            )
        else:
            analyzer_message = "AgentAnalyzer : Analyse en cours..."
        agent_status_messages.append(("AgentAnalyzer", analyzer_message))

        # AgentCoder
        final_code = project_state["AgentCoder"]["final_code"]
        if final_code:
            coder_message = "AgentCoder : Code généré avec succès ✔️"
        else:
            coder_message = "AgentCoder : En attente des instructions."
        agent_status_messages.append(("AgentCoder", coder_message))

        # Logs
        logs = ""
        with open("project.log", "r") as log_file:
            logs = log_file.read()

        return agent_status_messages, logs

    # === Fonction principale de réponse ===
    def respond(message, chat_history, agent_chat):
        """
        Gestion des interactions principales et mise à jour des statuts/logs.
        """
        # Mettre à jour le chat principal
        updated_chat_history, _ = user_interaction(message, chat_history)
        bot_message = updated_chat_history[-1]["assistant"]

        # Mettre à jour le statut des agents et les logs
        agent_status, logs = update_agent_status_and_logs(updated_chat_history)

        # Mettre à jour le chatbot des agents
        agent_chat.clear()
        for agent_name, msg_content in agent_status:
            agent_chat.append((agent_name, msg_content))

        # Générer le code final si disponible
        generated_code = project_state["AgentCoder"].get("final_code", "")
        if not generated_code:
            generated_code = "# Aucun code n'a encore été généré."
        else:
            generated_code = f"{generated_code}"

        return chatbot.update([(message, bot_message)]), updated_chat_history, agent_chat.update(), logs, generated_code

    # === Actions des boutons et soumission ===
    send_btn.click(
        respond,
        inputs=[msg, state, agent_status_chat],
        outputs=[chatbot, state, agent_status_chat, logs_box, output_code],
    )
    msg.submit(
        respond,
        inputs=[msg, state, agent_status_chat],
        outputs=[chatbot, state, agent_status_chat, logs_box, output_code],
    )

# Lancer l'interface
if __name__ == "__main__":
    interface.launch()