mirror of
https://github.com/Ladebeze66/ragflow_preprocess.git
synced 2026-02-04 06:00:27 +01:00
431 lines
18 KiB
Python
431 lines
18 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Panneau de configuration des agents LLM
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QPushButton, QComboBox, QTextEdit, QGroupBox,
|
|
QListWidget, QSplitter, QTabWidget, QSpinBox,
|
|
QDoubleSpinBox, QFormLayout, QMessageBox, QCheckBox)
|
|
from PyQt6.QtCore import Qt, QSize
|
|
|
|
class LLMConfigPanel(QWidget):
|
|
"""Panneau de configuration des agents LLM et de gestion des sélections"""
|
|
|
|
def __init__(self, parent):
|
|
super().__init__(parent)
|
|
self.parent = parent
|
|
self.current_selection = None
|
|
self.analysis_results = {}
|
|
|
|
self.init_ui()
|
|
self.load_llm_profiles()
|
|
|
|
def init_ui(self):
|
|
"""Initialise l'interface utilisateur du panneau"""
|
|
main_layout = QVBoxLayout(self)
|
|
|
|
# Tabs pour organiser les fonctionnalités
|
|
tabs = QTabWidget()
|
|
main_layout.addWidget(tabs)
|
|
|
|
# 1. Onglet Sélections
|
|
selections_tab = QWidget()
|
|
tab_layout = QVBoxLayout(selections_tab)
|
|
|
|
# Liste des sélections
|
|
selection_group = QGroupBox("Régions sélectionnées")
|
|
selection_layout = QVBoxLayout(selection_group)
|
|
|
|
self.selection_list = QListWidget()
|
|
self.selection_list.setMinimumHeight(100)
|
|
self.selection_list.currentRowChanged.connect(self.selection_changed)
|
|
selection_layout.addWidget(self.selection_list)
|
|
|
|
# Boutons pour la gestion des sélections
|
|
selection_btns = QHBoxLayout()
|
|
|
|
self.remove_btn = QPushButton("Supprimer")
|
|
self.remove_btn.clicked.connect(self.remove_selection)
|
|
selection_btns.addWidget(self.remove_btn)
|
|
|
|
selection_layout.addLayout(selection_btns)
|
|
tab_layout.addWidget(selection_group)
|
|
|
|
# Type de la sélection
|
|
type_group = QGroupBox("Type de contenu")
|
|
type_layout = QVBoxLayout(type_group)
|
|
|
|
self.type_combo = QComboBox()
|
|
self.type_combo.addItems(["schéma", "tableau", "formule", "texte", "autre"])
|
|
self.type_combo.currentTextChanged.connect(self.update_selection_type)
|
|
type_layout.addWidget(self.type_combo)
|
|
|
|
tab_layout.addWidget(type_group)
|
|
|
|
# Contexte textuel
|
|
context_group = QGroupBox("Contexte textuel")
|
|
context_layout = QVBoxLayout(context_group)
|
|
|
|
self.context_edit = QTextEdit()
|
|
self.context_edit.setPlaceholderText("Ajoutez ici le contexte pour cette sélection...")
|
|
self.context_edit.textChanged.connect(self.update_selection_context)
|
|
context_layout.addWidget(self.context_edit)
|
|
|
|
tab_layout.addWidget(context_group)
|
|
|
|
# 2. Onglet Agents LLM
|
|
agent_tab = QWidget()
|
|
agent_layout = QVBoxLayout(agent_tab)
|
|
|
|
# Mode d'analyse
|
|
mode_group = QGroupBox("Mode d'analyse")
|
|
mode_layout = QHBoxLayout(mode_group)
|
|
|
|
mode_label = QLabel("Niveau :")
|
|
mode_layout.addWidget(mode_label)
|
|
|
|
self.mode_combo = QComboBox()
|
|
self.mode_combo.addItems(["🔹 Léger", "⚪ Moyen", "🔸 Avancé"])
|
|
self.mode_combo.currentTextChanged.connect(self.update_mode)
|
|
mode_layout.addWidget(self.mode_combo)
|
|
|
|
agent_layout.addWidget(mode_group)
|
|
|
|
# Sélection des agents
|
|
agent_config_group = QGroupBox("Configuration des agents")
|
|
agent_config_layout = QVBoxLayout(agent_config_group)
|
|
|
|
# Sélection du type d'agent
|
|
agent_type_layout = QFormLayout()
|
|
|
|
# Agent Vision
|
|
agent_type_layout.addRow(QLabel("<b>Agent Vision</b>"))
|
|
|
|
self.vision_model_combo = QComboBox()
|
|
self.vision_model_combo.addItems(["llava:34b", "llava", "llama3.2-vision"])
|
|
agent_type_layout.addRow("Modèle:", self.vision_model_combo)
|
|
|
|
self.vision_lang_combo = QComboBox()
|
|
self.vision_lang_combo.addItems(["fr", "en"])
|
|
agent_type_layout.addRow("Langue:", self.vision_lang_combo)
|
|
|
|
# Agent de résumé/reformulation
|
|
agent_type_layout.addRow(QLabel("<b>Agent Résumé</b>"))
|
|
|
|
self.summary_model_combo = QComboBox()
|
|
self.summary_model_combo.addItems(["mistral", "deepseek-r1"])
|
|
agent_type_layout.addRow("Modèle:", self.summary_model_combo)
|
|
|
|
# Agent de traduction
|
|
agent_type_layout.addRow(QLabel("<b>Agent Traduction</b>"))
|
|
|
|
self.translation_model_combo = QComboBox()
|
|
self.translation_model_combo.addItems(["mistral", "qwen2.5", "deepseek"])
|
|
agent_type_layout.addRow("Modèle:", self.translation_model_combo)
|
|
|
|
agent_config_layout.addLayout(agent_type_layout)
|
|
|
|
# Paramètres communs
|
|
params_layout = QFormLayout()
|
|
params_layout.addRow(QLabel("<b>Paramètres de génération</b>"))
|
|
|
|
self.temp_spin = QDoubleSpinBox()
|
|
self.temp_spin.setRange(0.1, 1.0)
|
|
self.temp_spin.setSingleStep(0.1)
|
|
self.temp_spin.setValue(0.2)
|
|
params_layout.addRow("Température:", self.temp_spin)
|
|
|
|
self.top_p_spin = QDoubleSpinBox()
|
|
self.top_p_spin.setRange(0.1, 1.0)
|
|
self.top_p_spin.setSingleStep(0.05)
|
|
self.top_p_spin.setValue(0.95)
|
|
params_layout.addRow("Top-p:", self.top_p_spin)
|
|
|
|
self.token_spin = QSpinBox()
|
|
self.token_spin.setRange(100, 4000)
|
|
self.token_spin.setSingleStep(100)
|
|
self.token_spin.setValue(1024)
|
|
params_layout.addRow("Max tokens:", self.token_spin)
|
|
|
|
agent_config_layout.addLayout(params_layout)
|
|
|
|
agent_layout.addWidget(agent_config_group)
|
|
|
|
# Boutons d'action
|
|
action_layout = QHBoxLayout()
|
|
|
|
self.run_btn = QPushButton("▶ Appliquer l'agent")
|
|
self.run_btn.setMinimumHeight(40)
|
|
self.run_btn.clicked.connect(self.run_agent)
|
|
action_layout.addWidget(self.run_btn)
|
|
|
|
agent_layout.addLayout(action_layout)
|
|
|
|
# Aperçu des résultats
|
|
result_group = QGroupBox("Résultat")
|
|
result_layout = QVBoxLayout(result_group)
|
|
|
|
self.result_text = QTextEdit()
|
|
self.result_text.setReadOnly(True)
|
|
self.result_text.setMinimumHeight(100)
|
|
result_layout.addWidget(self.result_text)
|
|
|
|
agent_layout.addWidget(result_group)
|
|
|
|
# 3. Onglet Export
|
|
export_tab = QWidget()
|
|
export_layout = QVBoxLayout(export_tab)
|
|
|
|
export_group = QGroupBox("Options d'export")
|
|
export_group_layout = QVBoxLayout(export_group)
|
|
|
|
export_format_layout = QHBoxLayout()
|
|
export_format_layout.addWidget(QLabel("Format:"))
|
|
self.export_format_combo = QComboBox()
|
|
self.export_format_combo.addItems(["Markdown (.md)"])
|
|
export_format_layout.addWidget(self.export_format_combo)
|
|
|
|
export_group_layout.addLayout(export_format_layout)
|
|
|
|
self.include_images_check = QCheckBox("Inclure les images")
|
|
self.include_images_check.setChecked(True)
|
|
export_group_layout.addWidget(self.include_images_check)
|
|
|
|
export_layout.addWidget(export_group)
|
|
|
|
export_btn = QPushButton("Exporter")
|
|
export_btn.setMinimumHeight(40)
|
|
export_btn.clicked.connect(self.export_results)
|
|
export_layout.addWidget(export_btn)
|
|
|
|
export_layout.addStretch()
|
|
|
|
# Ajout des onglets
|
|
tabs.addTab(selections_tab, "Sélections")
|
|
tabs.addTab(agent_tab, "Agents LLM")
|
|
tabs.addTab(export_tab, "Export")
|
|
|
|
def load_llm_profiles(self):
|
|
"""Charge les profils LLM depuis le fichier de configuration"""
|
|
try:
|
|
config_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "config")
|
|
profile_path = os.path.join(config_dir, "llm_profiles.json")
|
|
|
|
if os.path.exists(profile_path):
|
|
with open(profile_path, "r", encoding="utf-8") as f:
|
|
self.profiles = json.load(f)
|
|
else:
|
|
# Profil par défaut si le fichier n'existe pas
|
|
self.profiles = {
|
|
"léger": {
|
|
"vision": {"model": "llava:34b", "language": "en", "temperature": 0.2},
|
|
"translation": {"model": "mistral", "language": "fr", "temperature": 0.1},
|
|
"summary": {"model": "mistral", "language": "fr", "temperature": 0.2}
|
|
},
|
|
"moyen": {
|
|
"vision": {"model": "llava", "language": "en", "temperature": 0.2},
|
|
"translation": {"model": "qwen2.5", "language": "fr", "temperature": 0.1},
|
|
"summary": {"model": "deepseek-r1", "language": "fr", "temperature": 0.2}
|
|
},
|
|
"avancé": {
|
|
"vision": {"model": "llama3.2-vision", "language": "en", "temperature": 0.2},
|
|
"translation": {"model": "deepseek", "language": "fr", "temperature": 0.1},
|
|
"summary": {"model": "deepseek-r1", "language": "fr", "temperature": 0.2}
|
|
}
|
|
}
|
|
|
|
# Créer le répertoire de configuration s'il n'existe pas
|
|
os.makedirs(config_dir, exist_ok=True)
|
|
|
|
# Sauvegarder le profil par défaut
|
|
with open(profile_path, "w", encoding="utf-8") as f:
|
|
json.dump(self.profiles, f, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
print(f"Erreur lors du chargement des profils: {str(e)}")
|
|
|
|
def update_selection_list(self):
|
|
"""Met à jour la liste des sélections"""
|
|
self.selection_list.clear()
|
|
|
|
for i, selection in enumerate(self.parent.selected_regions):
|
|
page = selection["page"] + 1
|
|
typ = selection["type"]
|
|
self.selection_list.addItem(f"Page {page} - {typ}")
|
|
|
|
def selection_changed(self, index):
|
|
"""Appelé lorsque la sélection change dans la liste"""
|
|
if index >= 0 and index < len(self.parent.selected_regions):
|
|
selection = self.parent.selected_regions[index]
|
|
self.current_selection = selection
|
|
|
|
# Mettre à jour l'interface
|
|
self.type_combo.setCurrentText(selection["type"])
|
|
self.context_edit.setText(selection["context"])
|
|
|
|
# Afficher les résultats si disponibles
|
|
if id(selection) in self.analysis_results:
|
|
self.result_text.setText(self.analysis_results[id(selection)])
|
|
else:
|
|
self.result_text.clear()
|
|
|
|
def update_selection_type(self, new_type):
|
|
"""Met à jour le type de la sélection actuelle"""
|
|
if self.current_selection:
|
|
self.current_selection["type"] = new_type
|
|
self.update_selection_list()
|
|
|
|
def update_selection_context(self):
|
|
"""Met à jour le contexte de la sélection actuelle"""
|
|
if self.current_selection:
|
|
self.current_selection["context"] = self.context_edit.toPlainText()
|
|
|
|
def remove_selection(self):
|
|
"""Supprime la sélection actuelle"""
|
|
if self.current_selection:
|
|
idx = self.parent.selected_regions.index(self.current_selection)
|
|
self.parent.selected_regions.remove(self.current_selection)
|
|
|
|
# Supprimer les résultats associés
|
|
if id(self.current_selection) in self.analysis_results:
|
|
del self.analysis_results[id(self.current_selection)]
|
|
|
|
self.current_selection = None
|
|
self.update_selection_list()
|
|
|
|
# Sélectionner le prochain élément si disponible
|
|
if self.selection_list.count() > 0:
|
|
new_idx = min(idx, self.selection_list.count() - 1)
|
|
self.selection_list.setCurrentRow(new_idx)
|
|
else:
|
|
self.context_edit.clear()
|
|
self.result_text.clear()
|
|
|
|
def update_mode(self, mode_text):
|
|
"""Met à jour le mode d'analyse en fonction du niveau sélectionné"""
|
|
if "léger" in mode_text.lower():
|
|
profile = self.profiles.get("léger", {})
|
|
elif "moyen" in mode_text.lower():
|
|
profile = self.profiles.get("moyen", {})
|
|
elif "avancé" in mode_text.lower():
|
|
profile = self.profiles.get("avancé", {})
|
|
else:
|
|
return
|
|
|
|
# Mise à jour des combobox avec les paramètres du profil
|
|
if "vision" in profile:
|
|
vision = profile["vision"]
|
|
if "model" in vision and vision["model"] in [self.vision_model_combo.itemText(i) for i in range(self.vision_model_combo.count())]:
|
|
self.vision_model_combo.setCurrentText(vision["model"])
|
|
if "language" in vision:
|
|
self.vision_lang_combo.setCurrentText(vision["language"])
|
|
if "temperature" in vision:
|
|
self.temp_spin.setValue(vision["temperature"])
|
|
|
|
if "summary" in profile:
|
|
summary = profile["summary"]
|
|
if "model" in summary and summary["model"] in [self.summary_model_combo.itemText(i) for i in range(self.summary_model_combo.count())]:
|
|
self.summary_model_combo.setCurrentText(summary["model"])
|
|
|
|
if "translation" in profile:
|
|
translation = profile["translation"]
|
|
if "model" in translation and translation["model"] in [self.translation_model_combo.itemText(i) for i in range(self.translation_model_combo.count())]:
|
|
self.translation_model_combo.setCurrentText(translation["model"])
|
|
|
|
def run_agent(self):
|
|
"""Execute l'agent LLM sur la sélection actuelle"""
|
|
if not self.current_selection:
|
|
QMessageBox.warning(self, "Aucune sélection",
|
|
"Veuillez sélectionner une région à analyser.")
|
|
return
|
|
|
|
# Pour cette démonstration, nous simulons le résultat
|
|
# Dans une implémentation réelle, nous utiliserions les agents LLM
|
|
|
|
# Récupérer les paramètres
|
|
vision_model = self.vision_model_combo.currentText()
|
|
summary_model = self.summary_model_combo.currentText()
|
|
translation_model = self.translation_model_combo.currentText()
|
|
lang = self.vision_lang_combo.currentText()
|
|
temp = self.temp_spin.value()
|
|
|
|
# Exemple de résultat simulé
|
|
result = f"**Analyse de l'agent Vision ({vision_model})**\n\n"
|
|
|
|
if self.current_selection["type"] == "schéma":
|
|
result += "Le schéma illustre un processus en plusieurs étapes avec des connections entre les différents éléments.\n\n"
|
|
elif self.current_selection["type"] == "tableau":
|
|
result += "Le tableau contient des données structurées avec plusieurs colonnes et rangées.\n\n"
|
|
elif self.current_selection["type"] == "formule":
|
|
result += "La formule mathématique représente une équation complexe.\n\n"
|
|
else:
|
|
result += "Le contenu sélectionné a été analysé.\n\n"
|
|
|
|
result += f"**Résumé ({summary_model})**\n\n"
|
|
result += "Ce contenu montre l'importance des éléments sélectionnés dans le contexte du document.\n\n"
|
|
|
|
if lang == "en":
|
|
result += f"**Traduction ({translation_model})**\n\n"
|
|
result += "The selected content has been analyzed and shows the importance of the selected elements in the context of the document.\n\n"
|
|
|
|
result += "**Paramètres utilisés**\n"
|
|
result += f"modèle vision={vision_model}, modèle résumé={summary_model}, "
|
|
result += f"temperature={temp}, langue={lang}"
|
|
|
|
# Stocker et afficher le résultat
|
|
self.analysis_results[id(self.current_selection)] = result
|
|
self.result_text.setText(result)
|
|
|
|
# Mise à jour du statut
|
|
self.parent.status_bar.showMessage("Analyse terminée.")
|
|
|
|
def export_results(self):
|
|
"""Exporte les résultats au format Markdown"""
|
|
if not self.parent.selected_regions:
|
|
QMessageBox.warning(self, "Aucune sélection",
|
|
"Il n'y a aucune sélection à exporter.")
|
|
return
|
|
|
|
try:
|
|
# Créer le répertoire de sortie s'il n'existe pas
|
|
output_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "outputs")
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
# Générer le contenu Markdown
|
|
md_content = "# Analyse de document\n\n"
|
|
|
|
# Trier les sélections par numéro de page
|
|
sorted_selections = sorted(self.parent.selected_regions, key=lambda x: x["page"])
|
|
|
|
for selection in sorted_selections:
|
|
page = selection["page"] + 1
|
|
typ = selection["type"]
|
|
context = selection["context"]
|
|
|
|
md_content += f"## {typ.capitalize()} - Page {page}\n\n"
|
|
|
|
if context:
|
|
md_content += f"**Contexte** :\n{context}\n\n"
|
|
|
|
# Ajouter les résultats d'analyse si disponibles
|
|
if id(selection) in self.analysis_results:
|
|
md_content += f"**Analyse IA** :\n{self.analysis_results[id(selection)]}\n\n"
|
|
|
|
md_content += "---\n\n"
|
|
|
|
# Écrire le fichier Markdown
|
|
file_path = os.path.join(output_dir, "analyse_document.md")
|
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
f.write(md_content)
|
|
|
|
QMessageBox.information(self, "Export réussi",
|
|
f"Le fichier a été exporté avec succès :\n{file_path}")
|
|
|
|
except Exception as e:
|
|
QMessageBox.critical(self, "Erreur d'export",
|
|
f"Une erreur est survenue lors de l'export :\n{str(e)}") |