Update UI language handling and improve .gitignore for Python artifacts

This commit is contained in:
sepehr 2025-03-09 09:06:54 +01:00
parent cb43b1176f
commit 9d142c269d
7 changed files with 705 additions and 88 deletions

8
.gitignore vendored
View File

@ -1 +1,7 @@
apigit.txt
apigit.txt
*.pyc
*.pyo
*.pyd
# Dossier de cache
__pycache__/

44
app.py
View File

@ -1,22 +1,59 @@
# filepath: f:\Dev\Rag\chat_bot_rag\app.py
import gradio as gr
from config.settings import DEFAULT_MODEL, QDRANT_COLLECTION_NAME, AVAILABLE_MODELS
from services.rag_service import initialize_rag_bot
from components.chatbot import process_query, reset_conversation, change_model, change_collection
from components.ui import build_interface, update_ui_language_elements
from translations.lang_mappings import UI_TRANSLATIONS, UI_SUPPORTED_LANGUAGES, LANGUAGE_MAPPING
def update_ui_language(language):
"""Fonction pour mettre à jour la langue de l'interface utilisateur"""
if language not in UI_SUPPORTED_LANGUAGES:
language = "Français" # Langue par défaut
# Récupérer les traductions pour la langue sélectionnée
translations = UI_TRANSLATIONS[language]
# Afficher un message de débogage
print(f"Mise à jour de la langue UI : {language}")
print(f"AVAILABLE_MODELS : {AVAILABLE_MODELS}")
# Retourner les valeurs mises à jour pour tous les éléments de l'interface
return [
f"# {translations['title']}", # Titre
gr.update(placeholder=translations["placeholder"]), # Placeholder du message
gr.update(value=translations["send_btn"]), # Texte du bouton d'envoi
gr.update(value=translations["clear_btn"]), # Texte du bouton d'effacement
gr.update(label=translations["ui_language_label"], info=translations["ui_language_info"]), # Label sélecteur langue UI
# IMPORTANT : Conserver les choices=AVAILABLE_MODELS ici
gr.update(label=translations["model_selector"], info=translations["model_info"], choices=AVAILABLE_MODELS),
f"{translations['model_current']}: **{DEFAULT_MODEL}**", # Statut du modèle
gr.update(label=translations["language_selector"], info=translations["language_info"], choices=list(LANGUAGE_MAPPING.keys())), # Langue réponses
gr.update(label=translations["collection_input"], info=translations["collection_info"]), # Label du champ de collection
f"{translations['collection_current']}: **{QDRANT_COLLECTION_NAME}**", # Statut de la collection
gr.update(value=translations["apply_btn"]), # Texte du bouton d'application
gr.update(label=translations["streaming_label"], info=translations["streaming_info"]), # Label du mode streaming
gr.update(label=translations["sources_label"]), # Label de l'affichage des sources
gr.update(label=translations["max_images_label"]), # Label du nombre max d'images
f"### {translations['images_title']}", # Titre des images
f"### {translations['tables_title']}" # Titre des tableaux
]
def main():
"""Main entry point for the chatbot application"""
# Initialize the RAG chatbot
initialize_rag_bot()
# Construire l'interface
# Dans app.py, corriger l'appel à build_interface
interface = build_interface(
process_query_fn=process_query,
reset_conversation_fn=reset_conversation,
change_model_fn=change_model,
change_collection_fn=change_collection,
update_ui_language_fn=update_ui_language_elements # Ajout du paramètre manquant
update_ui_language_fn=update_ui_language # Utiliser update_ui_language, pas update_ui_language_elements
)
# Lancer l'appli Gradio
@ -28,4 +65,5 @@ def main():
)
if __name__ == "__main__":
main()
main()

View File

@ -155,9 +155,36 @@ def change_collection(collection_name, language="Français"):
return f"❌ Erreur: {str(e)}"
# Fonction de traitement de requête
def convert_to_messages_format(history):
"""Convertit différents formats d'historique au format messages."""
messages = []
# Vérifier si nous avons déjà le format messages
if history and isinstance(history[0], dict) and "role" in history[0]:
return history
# Format tuples [(user_msg, assistant_msg), ...]
try:
for item in history:
if isinstance(item, tuple) and len(item) == 2:
user_msg, assistant_msg = item
messages.append({"role": "user", "content": user_msg})
if assistant_msg: # Éviter les messages vides
messages.append({"role": "assistant", "content": assistant_msg})
except ValueError:
# Journaliser l'erreur pour le débogage
print(f"Format d'historique non reconnu: {history}")
# Retourner un historique vide en cas d'erreur
return []
return messages
def process_query(message, history, streaming, show_sources, max_images, language):
global current_images, current_tables
# Debug plus clair
print(f"Langue sélectionnée pour la réponse: {language} -> {LANGUAGE_MAPPING.get(language, 'français')}")
if not message.strip():
return history, "", None, None
@ -168,8 +195,10 @@ def process_query(message, history, streaming, show_sources, max_images, languag
try:
if streaming:
# Version avec streaming dans Gradio
history = history + [(message, "")]
# Convertir history en format messages pour l'affichage
messages_history = convert_to_messages_format(history)
messages_history.append({"role": "user", "content": message})
messages_history.append({"role": "assistant", "content": ""})
# 1. Récupérer les documents pertinents
docs = rag_bot._retrieve_relevant_documents(message)
@ -180,50 +209,39 @@ def process_query(message, history, streaming, show_sources, max_images, languag
# 3. Préparer le prompt
prompt_template = ChatPromptTemplate.from_template("""
Tu es un assistant documentaire spécialisé qui utilise toutes les informations disponibles dans le contexte fourni.
Tu es un assistant documentaire spécialisé qui utilise le contexte fourni.
TRÈS IMPORTANT: Tu dois répondre EXCLUSIVEMENT en {language}. Ne réponds JAMAIS dans une autre langue.
===== INSTRUCTION CRUCIALE SUR LA LANGUE =====
RÉPONDS UNIQUEMENT EN {language}. C'est une exigence ABSOLUE.
NE RÉPONDS JAMAIS dans une autre langue que {language}, quelle que soit la langue de la question.
==============================================
Instructions spécifiques:
1. Pour chaque image mentionnée dans le contexte, inclue TOUJOURS dans ta réponse:
- La légende/caption exacte de l'image
- La source et le numéro de page
- Une description brève de ce qu'elle montre
2. Pour chaque tableau mentionné dans le contexte, inclue TOUJOURS:
- Le titre/caption exact du tableau
- La source et le numéro de page
- Ce que contient et signifie le tableau
3. Lorsque tu cites des équations mathématiques:
- Utilise la syntaxe LaTeX exacte comme dans le document ($...$ ou $$...$$)
- Reproduis-les fidèlement sans modification
4. IMPORTANT: Ne pas inventer d'informations - si une donnée n'est pas explicitement fournie dans le contexte,
indique clairement que cette information n'est pas disponible dans les documents fournis.
5. Cite précisément les sources pour chaque élément d'information (format: [Source, Page]).
6. CRUCIAL: Ta réponse doit être UNIQUEMENT et INTÉGRALEMENT en {language}, quelle que soit la langue de la question.
1. Pour chaque image mentionnée: inclure la légende, source, page et description
2. Pour chaque tableau: inclure titre, source, page et signification
3. Pour les équations: utiliser la syntaxe LaTeX exacte
4. Ne pas inventer d'informations hors du contexte fourni
5. Citer précisément les sources
Historique de conversation:
{chat_history}
Contexte (à utiliser pour répondre):
Contexte:
{context}
Question: {question}
Réponds de façon structurée et précise en intégrant activement les images, tableaux et équations disponibles dans le contexte.
Ta réponse doit être exclusivement en {language}.
Réponds de façon structurée en intégrant les images, tableaux et équations disponibles.
TA RÉPONSE DOIT ÊTRE UNIQUEMENT ET ENTIÈREMENT EN {language}. CETTE RÈGLE EST ABSOLUE.
""")
# 4. Formater les messages pour le LLM
# Assurer que la langue est bien passée dans le format du prompt
selected_language = LANGUAGE_MAPPING.get(language, "français")
messages = prompt_template.format_messages(
chat_history=history_text,
context=context,
question=message,
language=LANGUAGE_MAPPING.get(language, "français")
language=selected_language
)
# 5. Créer un handler de streaming personnalisé
@ -255,8 +273,9 @@ def process_query(message, history, streaming, show_sources, max_images, languag
# Nettoyer la réponse uniquement pour l'affichage (pas pour l'historique interne)
clean_response = clean_llm_response(partial_response)
history[-1] = (message, clean_response)
yield history, "", None, None
# Mettre à jour le dernier message (assistant)
messages_history[-1]["content"] = clean_response
yield messages_history, "", None, None
except queue.Empty:
continue
@ -310,17 +329,25 @@ def process_query(message, history, streaming, show_sources, max_images, languag
# 13. Retourner les résultats finaux
images_display = display_images()
tables_display = display_tables()
yield history, source_info, images_display, tables_display
yield messages_history, source_info, images_display, tables_display
else:
# Version sans streaming
print("Mode non-streaming activé")
source_info = ""
result = rag_bot.chat(message, stream=False)
result = rag_bot.chat(
message,
stream=False,
language=LANGUAGE_MAPPING.get(language, "français") # Vérifiez que cette ligne existe
)
# Nettoyer la réponse des balises <think>
result["response"] = clean_llm_response(result["response"])
history = history + [(message, result["response"])]
# Convertir l'historique au format messages
messages_history = convert_to_messages_format(history)
messages_history.append({"role": "user", "content": message})
messages_history.append({"role": "assistant", "content": result["response"]})
# Mise à jour de l'historique interne
rag_bot.chat_history.append({"role": "user", "content": message})
@ -364,7 +391,7 @@ def process_query(message, history, streaming, show_sources, max_images, languag
"description": table.get("description", "")
})
yield history, source_info, display_images(), display_tables()
yield messages_history, source_info, display_images(), display_tables()
except Exception as e:
error_msg = f"Une erreur est survenue: {str(e)}"
@ -382,4 +409,5 @@ def reset_conversation():
rag_bot.clear_history()
# Retourner une liste vide au format messages
return [], "", None, None

View File

@ -1,11 +1,58 @@
import gradio as gr
from config.settings import DEFAULT_MODEL, QDRANT_COLLECTION_NAME, AVAILABLE_MODELS
from translations.lang_mappings import UI_TRANSLATIONS, UI_SUPPORTED_LANGUAGES
from translations.lang_mappings import UI_TRANSLATIONS, UI_SUPPORTED_LANGUAGES, LANGUAGE_MAPPING
from utils.katex_script import KATEX_CSS_JS
def update_ui_language_elements(language):
"""Met à jour les éléments de l'interface utilisateur en fonction de la langue sélectionnée"""
pass # Implémentez selon vos besoins
"""Met à jour tous les éléments de l'interface avec la langue sélectionnée"""
# Vérifier si la langue est supportée par l'interface
if language not in UI_SUPPORTED_LANGUAGES:
language = "Français" # Langue par défaut
# Récupérer les traductions pour la langue sélectionnée
translations = UI_TRANSLATIONS[language]
# Créer un dictionnaire pour stocker tous les éléments modifiés
ui_elements = {}
# Mettre à jour le titre
ui_elements["title"] = translations["title"]
# Mettre à jour le placeholder et les boutons
ui_elements["placeholder"] = translations["placeholder"]
ui_elements["send_btn"] = translations["send_btn"]
ui_elements["clear_btn"] = translations["clear_btn"]
# Ajouter les traductions pour la langue de l'interface
ui_elements["ui_language_label"] = translations["ui_language_label"]
ui_elements["ui_language_info"] = translations["ui_language_info"]
# Mettre à jour les libellés des options
ui_elements["options_label"] = "Options" # Ce texte pourrait aussi être traduit
ui_elements["model_label"] = translations["model_selector"]
ui_elements["model_info"] = translations["model_info"]
ui_elements["model_current_prefix"] = translations["model_current"]
ui_elements["language_label"] = translations["language_selector"]
ui_elements["language_info"] = translations["language_info"]
ui_elements["collection_label"] = translations["collection_input"]
ui_elements["collection_info"] = translations["collection_info"]
ui_elements["collection_current_prefix"] = translations["collection_current"]
ui_elements["apply_btn"] = translations["apply_btn"]
ui_elements["streaming_label"] = translations["streaming_label"]
ui_elements["streaming_info"] = translations["streaming_info"]
ui_elements["sources_label"] = translations["sources_label"]
ui_elements["max_images_label"] = translations["max_images_label"]
ui_elements["images_title"] = translations["images_title"]
ui_elements["tables_title"] = translations["tables_title"]
return ui_elements
def build_interface(
process_query_fn,
@ -14,102 +61,139 @@ def build_interface(
change_collection_fn,
update_ui_language_fn
):
"""Construit l'interface utilisateur avec Gradio."""
"""Construit l'interface utilisateur avec Gradio"""
print("Initialisation de l'interface")
print("AVAILABLE_MODELS chargé dans ui.py:", AVAILABLE_MODELS)
# Initialiser avec la langue par défaut (Français)
ui_elements = update_ui_language_elements("Français")
with gr.Blocks(css=KATEX_CSS_JS, theme=gr.themes.Soft(primary_hue="blue")) as interface:
gr.Markdown("# 📚 Assistant documentaire intelligent")
title_md = gr.Markdown(f"# {ui_elements['title']}")
with gr.Row():
with gr.Column(scale=2):
# Chatbot principal
chat_interface = gr.Chatbot(
height=600,
show_label=False,
layout="bubble",
elem_id="chatbot"
elem_id="chatbot",
type="messages" # Ajoutez cette ligne
)
with gr.Row():
msg = gr.Textbox(
show_label=False,
placeholder="Posez votre question...",
placeholder=ui_elements['placeholder'],
container=False,
scale=4
)
submit_btn = gr.Button("Envoyer", variant="primary", scale=1)
submit_btn = gr.Button(ui_elements['send_btn'], variant="primary", scale=1)
clear_btn = gr.Button("Effacer la conversation")
clear_btn = gr.Button(ui_elements['clear_btn'])
source_info = gr.Markdown("", elem_id="sources_info")
with gr.Column(scale=1):
with gr.Accordion("Options", open=True):
# Sélecteur de modèle
# Sélecteur de langue pour l'interface
language_ui_selector = gr.Dropdown(
choices=UI_SUPPORTED_LANGUAGES,
value="Français",
label=ui_elements['ui_language_label'], # Utiliser une clé différente
info=ui_elements['ui_language_info']
)
# Sélecteur de modèle - assurez-vous que cette section est présente
model_selector = gr.Dropdown(
choices=AVAILABLE_MODELS,
value=DEFAULT_MODEL,
label="Modèle Ollama",
info="Choisir le modèle de language à utiliser"
label=ui_elements['model_label'],
info=ui_elements['model_info']
)
model_status = gr.Markdown(f"Modèle actuel: **{DEFAULT_MODEL}**")
model_status = gr.Markdown(f"{ui_elements['model_current_prefix']}: **{DEFAULT_MODEL}**")
# Sélecteur de langue
# Sélecteur de langue pour les réponses
language_selector = gr.Dropdown(
choices=UI_SUPPORTED_LANGUAGES,
value=UI_SUPPORTED_LANGUAGES[0],
label="Langue des réponses",
info="Choisir la langue dans laquelle l'assistant répondra"
choices=list(LANGUAGE_MAPPING.keys()),
value="Français",
label=ui_elements['language_label'],
info=ui_elements['language_info']
)
# Sélecteur de collection Qdrant
collection_name_input = gr.Textbox(
value=QDRANT_COLLECTION_NAME,
label="Collection Qdrant",
info="Nom de la collection de documents à utiliser"
label=ui_elements['collection_label'],
info=ui_elements['collection_info']
)
collection_status = gr.Markdown(f"Collection actuelle: **{QDRANT_COLLECTION_NAME}**")
collection_status = gr.Markdown(f"{ui_elements['collection_current_prefix']}: **{QDRANT_COLLECTION_NAME}**")
# Bouton d'application de la collection
apply_collection_btn = gr.Button("Appliquer la collection")
# Bouton pour appliquer la collection
apply_collection_btn = gr.Button(ui_elements['apply_btn'])
# Options de streaming et sources
streaming = gr.Checkbox(
label="Mode streaming",
label=ui_elements['streaming_label'],
value=True,
info="Voir les réponses s'afficher progressivement"
info=ui_elements['streaming_info']
)
show_sources = gr.Checkbox(label="Afficher les sources", value=True)
show_sources = gr.Checkbox(label=ui_elements['sources_label'], value=True)
max_images = gr.Slider(
minimum=1,
maximum=10,
value=3,
step=1,
label="Nombre max d'images"
label=ui_elements['max_images_label']
)
gr.Markdown("---")
gr.Markdown("### 🖼️ Images pertinentes")
images_title = gr.Markdown(f"### {ui_elements['images_title']}")
image_gallery = gr.Gallery(
label="Images pertinentes",
label=ui_elements['images_title'],
show_label=False,
columns=2,
height=300,
object_fit="contain"
)
gr.Markdown("### 📊 Tableaux")
tables_title = gr.Markdown(f"### {ui_elements['tables_title']}")
tables_display = gr.HTML()
# Connecter le changement de modèle
model_selector.change(
fn=change_model_fn,
inputs=model_selector,
outputs=model_status
)
# Connecter le changement de collection
apply_collection_btn.click(
fn=change_collection_fn,
inputs=collection_name_input,
outputs=collection_status
# Ajouter cette fonction juste avant de connecter le changement de langue
def preserve_models_wrapper(language):
"""Préserve la liste des modèles lors du changement de langue"""
# Obtenir les mises à jour depuis la fonction d'origine
updates = update_ui_language_fn(language)
# Force la liste complète des modèles disponibles (position 5 dans les sorties)
# Cela garantit que quelles que soient les mises à jour, la liste des modèles reste intacte
if isinstance(updates[5], dict) and "choices" in updates[5]:
print("Préservation de la liste des modèles:", AVAILABLE_MODELS)
updates[5]["choices"] = AVAILABLE_MODELS
return updates
# Puis modifier la connexion du language_ui_selector.change comme suit :
language_ui_selector.change(
fn=preserve_models_wrapper, # Utiliser notre wrapper au lieu de la fonction directe
inputs=language_ui_selector,
outputs=[
title_md,
msg,
submit_btn,
clear_btn,
language_ui_selector,
model_selector,
model_status,
language_selector,
collection_name_input,
collection_status,
apply_collection_btn,
streaming,
show_sources,
max_images,
images_title,
tables_title
]
)
# Fonction pour effacer l'entrée
@ -134,6 +218,20 @@ def build_interface(
outputs=[chat_interface, source_info, image_gallery, tables_display]
)
# Connecter le changement de modèle
model_selector.change(
fn=change_model_fn,
inputs=model_selector,
outputs=model_status
)
# Connecter le changement de collection
apply_collection_btn.click(
fn=change_collection_fn,
inputs=collection_name_input,
outputs=collection_status
)
# Style KaTeX et amélioration du design
gr.Markdown("""
<style>

File diff suppressed because one or more lines are too long

328
services/rag_service.py Normal file
View File

@ -0,0 +1,328 @@
import base64
from io import BytesIO
from PIL import Image
import traceback
import threading
import queue
import time
from rag_chatbot import MultimodalRAGChatbot
from langchain.prompts import ChatPromptTemplate
from langchain_ollama import ChatOllama
from langchain.callbacks.base import BaseCallbackHandler
# Handler personnalisé pour capturer les tokens en streaming
class GradioStreamingHandler(BaseCallbackHandler):
def __init__(self):
self.tokens_queue = queue.Queue()
self.full_text = ""
def on_llm_new_token(self, token, **kwargs):
self.tokens_queue.put(token)
self.full_text += token
# Fonction pour créer un objet Image à partir des données base64
def base64_to_image(base64_data):
"""Convertit une image base64 en objet Image pour l'affichage direct"""
try:
if not base64_data:
return None
image_bytes = base64.b64decode(base64_data)
image = Image.open(BytesIO(image_bytes))
return image
except Exception as e:
print(f"Erreur lors de la conversion d'image: {e}")
return None
# Configuration pour initialiser le chatbot
QDRANT_URL = "http://localhost:6333"
QDRANT_COLLECTION_NAME = "my_custom_collection"
EMBEDDING_MODEL = "mxbai-embed-large"
OLLAMA_URL = "http://127.0.0.1:11434"
DEFAULT_MODEL = "llama3.2"
# Liste des modèles disponibles
AVAILABLE_MODELS = ["llama3.1", "llama3.2", "deepseek-r1:7b", "deepseek-r1:14b"]
# Mapping des langues pour une meilleure compréhension par le LLM
LANGUAGE_MAPPING = {
"Français": "français",
"English": "English",
"Español": "español",
"Deutsch": "Deutsch",
"Italiano": "italiano",
"中文": "Chinese",
"日本語": "Japanese",
"العربية": "Arabic"
}
# Variables globales pour stocker les images et tableaux de la dernière requête
current_images = []
current_tables = []
# Initialiser le chatbot RAG avec le modèle par défaut
def initialize_rag_bot():
global rag_bot
rag_bot = MultimodalRAGChatbot(
qdrant_url=QDRANT_URL,
qdrant_collection_name=QDRANT_COLLECTION_NAME,
ollama_model=DEFAULT_MODEL,
embedding_model=EMBEDDING_MODEL,
ollama_url=OLLAMA_URL
)
print(f"Chatbot initialisé avec modèle: {DEFAULT_MODEL}")
# Fonction pour changer de modèle
def change_model(model_name):
global rag_bot
try:
# Réinitialiser le chatbot avec le nouveau modèle
rag_bot = MultimodalRAGChatbot(
qdrant_url=QDRANT_URL,
qdrant_collection_name=QDRANT_COLLECTION_NAME,
ollama_model=model_name,
embedding_model=EMBEDDING_MODEL,
ollama_url=OLLAMA_URL
)
print(f"Modèle changé pour: {model_name}")
return f"✅ Modèle changé pour: {model_name}"
except Exception as e:
print(f"Erreur lors du changement de modèle: {e}")
return f"❌ Erreur: {str(e)}"
# Fonction pour changer de collection
def change_collection(collection_name):
global rag_bot, QDRANT_COLLECTION_NAME
try:
# Mise à jour de la variable globale
QDRANT_COLLECTION_NAME = collection_name
# Réinitialiser le chatbot avec la nouvelle collection
rag_bot = MultimodalRAGChatbot(
qdrant_url=QDRANT_URL,
qdrant_collection_name=collection_name,
ollama_model=rag_bot.llm.model, # Conserver le modèle actuel
embedding_model=EMBEDDING_MODEL,
ollama_url=OLLAMA_URL
)
print(f"Collection changée pour: {collection_name}")
return f"✅ Collection changée pour: {collection_name}"
except Exception as e:
print(f"Erreur lors du changement de collection: {e}")
return f"❌ Erreur: {str(e)}"
# Fonction de traitement des requêtes avec support du streaming dans Gradio
def process_query(message, history, streaming, show_sources, max_images, language):
global current_images, current_tables
if not message.strip():
return history, "", None, None
current_images = []
current_tables = []
try:
if streaming:
# Version avec streaming dans Gradio
history = history + [(message, "")]
# 1. Récupérer les documents pertinents
docs = rag_bot._retrieve_relevant_documents(message)
# 2. Préparer le contexte et l'historique
context = rag_bot._format_documents(docs)
history_text = rag_bot._format_chat_history()
# 3. Préparer le prompt
prompt_template = ChatPromptTemplate.from_template("""
Tu es un assistant documentaire spécialisé qui utilise toutes les informations disponibles dans le contexte fourni.
TRÈS IMPORTANT: Tu dois répondre EXCLUSIVEMENT en {language}. Ne réponds JAMAIS dans une autre langue.
Instructions spécifiques:
1. Pour chaque image mentionnée dans le contexte, inclue TOUJOURS dans ta réponse:
- La légende/caption exacte de l'image
- La source et le numéro de page
- Une description brève de ce qu'elle montre
2. Pour chaque tableau mentionné dans le contexte, inclue TOUJOURS:
- Le titre/caption exact du tableau
- La source et le numéro de page
- Ce que contient et signifie le tableau
3. Lorsque tu cites des équations mathématiques:
- Utilise la syntaxe LaTeX exacte comme dans le document ($...$ ou $$...$$)
- Reproduis-les fidèlement sans modification
4. IMPORTANT: Ne pas inventer d'informations - si une donnée n'est pas explicitement fournie dans le contexte,
indique clairement que cette information n'est pas disponible dans les documents fournis.
5. Cite précisément les sources pour chaque élément d'information (format: [Source, Page]).
6. CRUCIAL: Ta réponse doit être UNIQUEMENT et INTÉGRALEMENT en {language}, quelle que soit la langue de la question.
Historique de conversation:
{chat_history}
Contexte (à utiliser pour répondre):
{context}
Question: {question}
Réponds de façon structurée et précise en intégrant activement les images, tableaux et équations disponibles dans le contexte.
Ta réponse doit être exclusivement en {language}.
""")
# 4. Formater les messages pour le LLM
messages = prompt_template.format_messages(
chat_history=history_text,
context=context,
question=message,
language=LANGUAGE_MAPPING.get(language, "français") # Use the mapped language value
)
# 5. Créer un handler de streaming personnalisé
handler = GradioStreamingHandler()
# 6. Créer un modèle LLM avec notre handler
streaming_llm = ChatOllama(
model=rag_bot.llm.model,
base_url=rag_bot.llm.base_url,
streaming=True,
callbacks=[handler]
)
# 7. Lancer la génération dans un thread pour ne pas bloquer l'UI
def generate_response():
streaming_llm.invoke(messages)
thread = threading.Thread(target=generate_response)
thread.start()
# 8. Récupérer les tokens et mettre à jour l'interface
partial_response = ""
# Attendre les tokens avec un timeout
while thread.is_alive() or not handler.tokens_queue.empty():
try:
token = handler.tokens_queue.get(timeout=0.05)
partial_response += token
history[-1] = (message, partial_response)
yield history, "", None, None
except queue.Empty:
continue
# 9. Thread terminé, mettre à jour l'historique de conversation du chatbot
rag_bot.chat_history.append({"role": "user", "content": message})
rag_bot.chat_history.append({"role": "assistant", "content": partial_response})
# 10. Récupérer les sources, images, tableaux
texts, images, tables = rag_bot._process_documents(docs)
# Préparer les informations sur les sources
source_info = ""
if texts:
source_info += f"📚 {len(texts)} textes • "
if images:
source_info += f"🖼️ {len(images)} images • "
if tables:
source_info += f"📊 {len(tables)} tableaux"
if source_info:
source_info = "Sources trouvées: " + source_info
# 11. Traiter les images
if show_sources and images:
images = images[:max_images]
for img in images:
img_data = img.get("image_data")
if img_data:
image = base64_to_image(img_data)
if image:
current_images.append({
"image": image,
"caption": img.get("caption", ""),
"source": img.get("source", ""),
"page": img.get("page", ""),
"description": img.get("description", "")
})
# 12. Traiter les tableaux
if show_sources and tables:
for table in tables:
current_tables.append({
"data": rag_bot.format_table(table.get("table_data", "")),
"caption": table.get("caption", ""),
"source": table.get("source", ""),
"page": table.get("page", ""),
"description": table.get("description", "")
})
# 13. Retourner les résultats finaux
yield history, source_info, display_images(current_images), display_tables(current_tables, language)
else:
# Version sans streaming (code existant)
result = rag_bot.chat(message, stream=False)
history = history + [(message, result["response"])]
# Préparer les informations sur les sources
source_info = ""
if "texts" in result:
source_info += f"📚 {len(result['texts'])} textes • "
if "images" in result:
source_info += f"🖼️ {len(result['images'])} images • "
if "tables" in result:
source_info += f"📊 {len(result['tables'])} tableaux"
if source_info:
source_info = "Sources trouvées: " + source_info
# Traiter les images et tableaux
if show_sources and "images" in result and result["images"]:
images = result["images"][:max_images]
for img in images:
img_data = img.get("image_data")
if img_data:
image = base64_to_image(img_data)
if image:
current_images.append({
"image": image,
"caption": img.get("caption", ""),
"source": img.get("source", ""),
"page": img.get("page", ""),
"description": img.get("description", "")
})
if show_sources and "tables" in result and result["tables"]:
tables = result["tables"]
for table in tables:
current_tables.append({
"data": rag_bot.format_table(table.get("table_data", "")),
"caption": table.get("caption", ""),
"source": table.get("source", ""),
"page": table.get("page", ""),
"description": table.get("description", "")
})
return history, source_info, display_images(current_images), display_tables(current_tables, language)
except Exception as e:
error_msg = f"Une erreur est survenue: {str(e)}"
traceback_text = traceback.format_exc()
print(error_msg)
print(traceback_text)
history = history + [(message, error_msg)]
return history, "Erreur lors du traitement de la requête", None, None
# Fonction pour réinitialiser la conversation
def reset_conversation():
global current_images, current_tables
current_images = []
current_tables = []
rag_bot.clear_history()
return [], "", None, None

View File

@ -7,8 +7,7 @@ LANGUAGE_MAPPING = {
"Italiano": "italiano",
"中文": "Chinese",
"日本語": "Japanese",
"العربية": "Arabic"
}
}
# Dictionnaire de traductions pour l'interface
UI_TRANSLATIONS = {
@ -39,7 +38,9 @@ UI_TRANSLATIONS = {
"error_msg": "Une erreur est survenue",
"processing_error": "Erreur lors du traitement de la requête",
"table_translation": "Traduction",
"table_description": "Ce tableau présente des données sur"
"table_description": "Ce tableau présente des données sur",
"ui_language_label": "Langue de l'interface",
"ui_language_info": "Changer la langue de l'interface uniquement"
},
"English": {
"title": "📚 Intelligent Document Assistant",
@ -68,7 +69,9 @@ UI_TRANSLATIONS = {
"error_msg": "An error occurred",
"processing_error": "Error processing request",
"table_translation": "Translation",
"table_description": "This table presents data on"
"table_description": "This table presents data on",
"ui_language_label": "UI Language",
"ui_language_info": "Change only the interface language"
},
"Español": {
"title": "📚 Asistente documental inteligente",
@ -97,7 +100,9 @@ UI_TRANSLATIONS = {
"error_msg": "Se ha producido un error",
"processing_error": "Error al procesar la solicitud",
"table_translation": "Traducción",
"table_description": "Esta tabla presenta datos sobre"
"table_description": "Esta tabla presenta datos sobre",
"ui_language_label": "Idioma de la interfaz",
"ui_language_info": "Cambiar solo el idioma de la interfaz"
}
}