Hacerbeni61's picture
Update app.py
b223f3d verified
raw
history blame
9.51 kB
#================imports==============
import uuid
import requests
import os
os.environ["USER_AGENT"] = "RAG-App/1.0"
from typing import Dict, List, Any, Generator
from dotenv import load_dotenv
from bs4 import BeautifulSoup
from langchain_core.globals import set_llm_cache
from langchain_core.caches import InMemoryCache
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import Weaviate
from langchain_community.vectorstores import FAISS
from langchain_groq import ChatGroq
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_classic.chains.combine_documents import create_stuff_documents_chain
from langchain_classic.chains import create_retrieval_chain
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
import gradio as gr
#================== CONFIG==================
load_dotenv()
set_llm_cache(InMemoryCache())
api_key = os.environ["GROQ_API_KEY"]
print("api chargée:" if api_key else "y'a probleme!!")
#========== charger et decouper documents=================
urls = [
"https://fr.wikipedia.org/wiki/%C3%89levage",
"https://fr.wikipedia.org/wiki/La_P%C3%AAche"
]
loader = WebBaseLoader(urls,
requests_kwargs={
"headers": {
"User-Agent": "RAG-App/1.0"
}
}
)
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(docs)
#============embeding et indexation vers faiss_db================
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
faiss_db = FAISS.from_documents(
documents=chunks,
embedding=embeddings
)
retriever = faiss_db.as_retriever(search_type="similarity", search_kwargs={"k": 3})
#=============== LLM et Prompt=================
llm = ChatGroq(
model="llama-3.3-70b-versatile",
temperature=0.0,
max_tokens=1200,
streaming=True # Activer le streaming
)
prompt = ChatPromptTemplate.from_messages([
("system", """Tu es un assistant expert en dans le domaine de l'elevage et la pêche. Réponds clairement.
Si tu ne connais pas, n'invente pas. Garde un ton amical.
Contexte :
{context}"""),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}"),
])
#============= CHAINE DE RECUPERATION=======
stuff_chain = create_stuff_documents_chain(llm, prompt)
rag_chain = create_retrieval_chain(retriever, stuff_chain)
# ====== GESTION DE L'HISTORIQUE ======
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# ====== CHAIN AVEC MÉMOIRE ======
convers_chain = RunnableWithMessageHistory(
rag_chain,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history",
output_messages_key="answer"
)
# =============FONCTION CHAT AVEC STREAMING ================
SESSION_ID = str(uuid.uuid4()) # session globale pour l'historique
def chat_fn_stream(message: str, history: list) -> Generator[str, None, None]:
"""
Fonction de chat avec streaming en temps réel.
Yield chaque token de la réponse au fur et à mesure.
"""
# Récupérer l'historique de la session
session_history = get_session_history(SESSION_ID)
# Construire le contexte à partir de l'historique
result = convers_chain.invoke(
{"input": message},
config={"configurable": {"session_id": SESSION_ID}}
)
# Récupérer la réponse complète
full_response = result.get("answer", str(result))
# Simuler le streaming en yieldant caractère par caractère
partial_response = ""
for char in full_response:
partial_response += char
yield partial_response
def get_history_list() -> list:
"""
Récupère l'historique de la conversation sous forme de liste
pour l'affichage dans la sidebar.
"""
session_history = get_session_history(SESSION_ID)
messages = session_history.messages
history_list = []
for i in range(0, len(messages), 2):
if i + 1 < len(messages):
question = messages[i].content
answer = messages[i + 1].content
history_list.append({
"question": question[:100] + "..." if len(question) > 100 else question,
"full_question": question,
"full_answer": answer
})
return history_list
def load_conversation(question: str, history: list) -> list:
"""
Charge une conversation précédente et affiche la réponse.
"""
session_history = get_session_history(SESSION_ID)
messages = session_history.messages
# Trouver la question et sa réponse correspondante
for i in range(0, len(messages), 2):
if i + 1 < len(messages) and messages[i].content == question:
history.append({"role": "user", "content": question})
history.append({"role": "assistant", "content": messages[i + 1].content})
return history
return history
# ================= INTERFACE GRADIO AVEC HISTORIQUE ====================
with gr.Blocks(title="🤖 RAG: Specialist en Science Animale") as demo:
gr.Markdown("# 🤖 RAG: Specialist en Science Animale")
gr.Markdown("Posez vos questions sur l'élévage et la pêche")
with gr.Row():
# Sidebar pour l'historique
with gr.Column(scale=1, min_width=300):
gr.Markdown("### 📚 Historique des conversations")
# Bouton pour rafraîchir l'historique
refresh_btn = gr.Button("🔄 Rafraîchir l'historique")
# Liste des questions précédentes
history_list = gr.Dataframe(
headers=["Question", "Action"],
label="Questions précédentes",
interactive=True,
wrap=True
)
def update_history_list():
"""Met à jour la liste des questions dans le dataframe."""
session_history = get_session_history(SESSION_ID)
messages = session_history.messages
data = []
for i in range(0, len(messages), 2):
if i + 1 < len(messages):
question = messages[i].content
data.append([question[:100] + "..." if len(question) > 100 else question, "📋 Voir"])
return data
refresh_btn.click(
fn=update_history_list,
outputs=[history_list]
)
# Zone principale de chat
with gr.Column(scale=3):
chatbot = gr.Chatbot(
label="Assistant RAG",
height=500
)
# Barre de saisie et bouton d'envoi
with gr.Row():
msg = gr.Textbox(
label="Votre question",
placeholder="Posez votre question sur l'élevage ou la pêche...",
scale=4
)
send_btn = gr.Button("Envoyer", variant="primary", scale=1)
# Bouton pour effacer la conversation
clear_btn = gr.Button("🗑️ Effacer la conversation")
# Exemples de questions
gr.Examples(
examples=[
"C'est quoi la pêche ?",
"Explique l'élévage",
"Quelle est la différence entre l'élévage et pêche ?"
],
inputs=[msg]
)
# Fonction pour gérer l'envoi de message
def respond(message: str, history: list) -> tuple:
"""Gère l'envoi du message et met à jour le chatbot."""
# Ajouter le message utilisateur à l'historique
history.append({"role": "user", "content": message})
# Obtenir la réponse
result = convers_chain.invoke(
{"input": message},
config={"configurable": {"session_id": SESSION_ID}}
)
response = result.get("answer", str(result))
# Ajouter la réponse à l'historique
history.append({"role": "assistant", "content": response})
return "", history
def clear_conversation():
"""Efface la conversation actuelle."""
store[SESSION_ID] = ChatMessageHistory()
return [], []
# Gestionnaires d'événements
msg.submit(
respond,
inputs=[msg, chatbot],
outputs=[msg, chatbot]
)
send_btn.click(
respond,
inputs=[msg, chatbot],
outputs=[msg, chatbot]
)
clear_btn.click(
clear_conversation,
outputs=[chatbot, history_list]
)
# Charger l'historique initial
demo.load(
fn=update_history_list,
outputs=[history_list]
)
# ===================LANCEMENT ================
demo.launch()