diff --git a/gradio_chatbot.py b/gradio_chatbot.py index 0038bfb..e588e22 100644 --- a/gradio_chatbot.py +++ b/gradio_chatbot.py @@ -44,6 +44,19 @@ 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", + "فارسی": "Persian" # Added Persian language +} + # Initialiser le chatbot RAG avec le modèle par défaut rag_bot = MultimodalRAGChatbot( qdrant_url=qdrant_url, @@ -77,8 +90,30 @@ def change_model(model_name): 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): +def process_query(message, history, streaming, show_sources, max_images, language): global current_images, current_tables if not message.strip(): @@ -104,6 +139,8 @@ def process_query(message, history, streaming, show_sources, max_images): 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 @@ -120,10 +157,12 @@ def process_query(message, history, streaming, show_sources, max_images): - 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 "Cette information n'est pas disponible dans les documents fournis." + 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} @@ -133,13 +172,15 @@ def process_query(message, history, streaming, show_sources, max_images): 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 + question=message, + language=LANGUAGE_MAPPING.get(language, "français") # Use the mapped language value ) # 5. Créer un handler de streaming personnalisé @@ -295,15 +336,65 @@ def display_tables(): return None html = "" - for table in current_tables: + for idx, table in enumerate(current_tables): + # Convert raw table data to a proper HTML table + table_data = table['data'] + table_html = "" + + # Try to convert the table data to a formatted HTML table + try: + # If it's a string representation, convert to DataFrame and then to HTML + if isinstance(table_data, str): + # Try to parse as markdown table or CSV + if '|' in table_data: + # Clean up the table data - remove extra pipes and spaces + rows = table_data.strip().split('\n') + table_html = '
' + + for i, row in enumerate(rows): + # Skip separator rows (---|---) in markdown tables + if i == 1 and all(c in ':-|' for c in row): + continue + + # Process each cell + cells = row.split('|') + + # Remove empty cells from start/end (caused by leading/trailing |) + if cells and cells[0].strip() == '': + cells = cells[1:] + if cells and cells[-1].strip() == '': + cells = cells[:-1] + + # Create table row + if cells: + is_header = (i == 0) + table_html += '' + for cell in cells: + cell_content = cell.strip() + if is_header: + table_html += f'' + else: + table_html += f'' + table_html += '' + + table_html += '
{cell_content}{cell_content}
' + else: + # If not pipe-separated, wrap in pre for code formatting + table_html = f'
{table_data}
' + else: + # For any other format, just use a pre tag + table_html = f'
{table_data}
' + except Exception as e: + # Fallback if conversion fails + print(f"Error formatting table {idx}: {e}") + table_html = f'
{table_data}
' + + # Create the table container with metadata - REMOVED description html += f"""

{table['caption']}

Source: {table['source']}, Page: {table['page']}

-

Description: {table['description']}

-
-
{table['data']}
-
+ {table_html}
""" @@ -355,6 +446,25 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo: ) model_status = gr.Markdown(f"Modèle actuel: **{default_model}**") + # Sélecteur de langue + language_selector = gr.Dropdown( + choices=["Français", "English", "Español", "Deutsch", "Italiano", "中文", "日本語", "العربية", "فارسی"], + value="Français", + label="Langue des réponses", + info="Choisir la langue dans laquelle l'assistant répondra" + ) + + # 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" + ) + collection_status = gr.Markdown(f"Collection actuelle: **{qdrant_collection_name}**") + + # Apply collection button + apply_collection_btn = gr.Button("Appliquer la collection") + streaming = gr.Checkbox( label="Mode streaming", value=True, @@ -390,16 +500,23 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo: outputs=model_status ) + # Connecter le changement de collection + apply_collection_btn.click( + fn=change_collection, + inputs=collection_name_input, + outputs=collection_status + ) + # Configuration des actions msg.submit( process_query, - inputs=[msg, chat_interface, streaming, show_sources, max_images], + inputs=[msg, chat_interface, streaming, show_sources, max_images, language_selector], outputs=[chat_interface, source_info, image_gallery, tables_display] ).then(lambda: "", outputs=msg) submit_btn.click( process_query, - inputs=[msg, chat_interface, streaming, show_sources, max_images], + inputs=[msg, chat_interface, streaming, show_sources, max_images, language_selector], outputs=[chat_interface, source_info, image_gallery, tables_display] ).then(lambda: "", outputs=msg) @@ -415,53 +532,135 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as demo: #chatbot {height: 600px; overflow-y: auto;} #sources_info {margin-top: 10px; color: #666;} - /* Style pour les équations */ + /* Improved styles for equations */ .katex { font-size: 1.1em !important; } .math-inline { background: #f8f9fa; padding: 2px 5px; border-radius: 4px; } .math-display { background: #f8f9fa; margin: 10px 0; padding: 10px; border-radius: 5px; overflow-x: auto; text-align: center; } + + /* Table styles */ + table { + border-collapse: collapse; + width: 100%; + margin: 15px 0; + font-size: 0.9em; + } + table, th, td { + border: 1px solid #ddd; + } + th, td { + padding: 8px 12px; + text-align: left; + } + th { + background-color: #f2f2f2; + } + tr:nth-child(even) { + background-color: #f9f9f9; + } + .table-container { + overflow-x: auto; + margin-top: 10px; + } - + """)