import os import json import bcrypt import pandas as pd from typing import List from pathlib import Path from langchain_huggingface import HuggingFaceEmbeddings from langchain_huggingface import HuggingFaceEndpoint from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.schema import StrOutputParser from operator import itemgetter from pinecone import Pinecone from langchain_pinecone import PineconeVectorStore from langchain_community.chat_message_histories import ChatMessageHistory from langchain.memory import ConversationBufferMemory from langchain.schema.runnable import Runnable, RunnablePassthrough, RunnableConfig, RunnableLambda from langchain.callbacks.base import BaseCallbackHandler from langchain.chains import ( StuffDocumentsChain, ConversationalRetrievalChain, create_extraction_chain ) import chainlit as cl from chainlit.input_widget import TextInput, Select, Switch, Slider from deep_translator import GoogleTranslator from datetime import timedelta @cl.step(type="tool") async def LLMistral(): os.environ['HUGGINGFACEHUB_API_TOKEN'] = os.environ['HUGGINGFACEHUB_API_TOKEN'] #repo_id = "mistralai/Ministral-8B-Instruct-2410" #repo_id = "mistralai/Mixtral-8x7B-Instruct-v0.1" #repo_id = "nvidia/Mistral-NeMo-Minitron-8B-Base" repo_id = "mistralai/Mistral-7B-Instruct-v0.2" #repo_id = "mistralai/Mistral-7B-Instruct-v0.3" #repo_id = "allenai/OLMoE-1B-7B-0924-Instruct" llm = HuggingFaceEndpoint( repo_id=repo_id, max_new_tokens=5300, temperature=0.5, task="text2text-generation", streaming=True ) return llm @cl.step(type="tool") async def VectorDatabase(categorie): if categorie == "year" or categorie == "videosTC": index_name = "all-jdlp" embeddings = HuggingFaceEmbeddings() vectorstore = PineconeVectorStore( index_name=index_name, embedding=embeddings, pinecone_api_key=os.getenv('PINECONE_API_KEYJDLP') ) return vectorstore @cl.step(type="retrieval") async def Retriever(categorie): vectorstore = await VectorDatabase(categorie) if categorie == "videosTC": retriever = vectorstore.as_retriever(search_type="similarity_score_threshold", search_kwargs={"score_threshold": .7, "k": 250,"filter": {"title": {"$eq": "videos-table-rondeia"}, "time": {"$gte": 1320}}}) return retriever @cl.step(type="embedding") async def Search(input, categorie): vectorstore = await VectorDatabase(categorie) results = [] test = [] sources_text = "" sources_offres = "" verbatim_text = "" count = 0 countOffres = 0 if categorie == "videosTC": search = vectorstore.similarity_search(input,k=50, filter={"title": {"$eq": "videos-table-rondeia"}, "time": {"$gte": 1320}}) for i in range(0,len(search)): if count <= 23: count = count + 1 timeSeq = search[i].metadata["time"] timeSeqRound = round(timeSeq) time = timedelta(seconds=timeSeqRound) sources_text = sources_text + '
' + search[i].metadata['titre'] + ' : ...' + search[i].page_content + '

🕓 ' + str(time) + ' : ...' + search[i].page_content + ' : ' + search[i].metadata['titre'] + '

' sources_audio = sources_text + '
' + search[i].metadata['titre'] + ' : ...' + search[i].page_content + '

🕓 ' + str(time) + ' : ...' + search[i].page_content + ' : ' + search[i].metadata['titre'] + '

' verbatim_text = verbatim_text + "

" + str(count) + ". " + search[i].metadata['titre'] + "

🕓 "+ str(time) + " : " + search[i].page_content + "

" results = [sources_text, verbatim_text, sources_audio] return results @cl.set_chat_profiles async def chat_profile(): return [ cl.ChatProfile(name="Table ronde autour de l'IA : «IA et gestes professionnels de l’enseignant»",markdown_description="VidĂ©o exploratoire autour de l'Ă©vĂ©nement",icon="/public/logo-ofipe.png",), ] #@cl.on_chat_start #async def on_chat_start(): # await cl.Message(f"Bonjour").send() @cl.set_starters async def set_starters(): return [ cl.Starter( label="L'IA comme outil pour les enseignants et les Ă©tudiants", message="Comment les enseignants et les Ă©tudiants peuvent-ils utiliser l'IA comme un outil pour amĂ©liorer l'apprentissage et l'enseignement ?", icon="/public/videocam-theme1.svg", ), cl.Starter( label="Les limites de l'IA et la nĂ©cessitĂ© de l'intervention humaine", message="Quelles sont les limites de l'IA et pourquoi est-il important que les enseignants interviennent dans le processus d'apprentissage avec l'IA ?", icon="/public/videocam-theme2.svg", ), cl.Starter( label="L'importance de l'esprit critique face Ă  l'IA", message="Comment encourager les Ă©tudiants Ă  dĂ©velopper un esprit critique face Ă  l'IA et Ă  ses rĂ©sultats ?", icon="/public/videocam-theme3.svg", ), cl.Starter( label="L'Ă©thique et l'IA dans les enseignements du supĂ©rieur", message="Quels sont les enjeux Ă©thiques liĂ©s Ă  l'utilisation de l'IA dans les enseignements du supĂ©rieur et comment y faire face ?", icon="/public/videocam-theme4.svg", ), cl.Starter( label="Les impacts de l'IA sur les mĂ©tiers et les formations", message="Comment l'IA va transformer les mĂ©tiers et les formations du supĂ©rieur et quels sont les dĂ©fis Ă  relever ?", icon="/public/videocam-theme5.svg", ), cl.Starter( label="Les limites de l'IA et les dĂ©fis technologiques", message="Quelles sont les limites actuelles de l'IA et quels sont les dĂ©fis technologiques Ă  relever pour amĂ©liorer son utilisation dans les enseignements du supĂ©rieur ?", icon="/public/videocam-theme6.svg", ) ] @cl.on_message async def on_message(message: cl.Message): if cl.context.session.client_type == "copilot": await cl.Message(f"Type : {message.type}").send() if message.type == "system_message": # do something with the message await cl.Message(f"Lien : ").send() return model = await LLMistral() retriever = await Retriever("videosTC") ########## Chain with streaming ########## message_history = ChatMessageHistory() memory = ConversationBufferMemory(memory_key="chat_history",output_key="answer",chat_memory=message_history,return_messages=True) qa = ConversationalRetrievalChain.from_llm( model, memory=memory, chain_type="stuff", return_source_documents=True, verbose=False, retriever=retriever ) msg = cl.Message(content="") class PostMessageHandler(BaseCallbackHandler): """ Callback handler for handling the retriever and LLM processes. Used to post the sources of the retrieved documents as a Chainlit element. """ def __init__(self, msg: cl.Message): BaseCallbackHandler.__init__(self) self.msg = msg self.sources = set() # To store unique pairs def on_retriever_end(self, documents, *, run_id, parent_run_id, **kwargs): for d in documents: source_page_pair = (d.metadata['source'], d.metadata['page']) self.sources.add(source_page_pair) # Add unique pairs to the set def on_llm_end(self, response, *, run_id, parent_run_id, **kwargs): sources_text = "\n".join([f"{source}#page={page}" for source, page in self.sources]) self.msg.elements.append( cl.Text(name="Sources", content=sources_text, display="inline") ) cb = cl.AsyncLangchainCallbackHandler() results = await qa.acall("Contexte : Vous ĂȘtes un chercheur de l'enseignement supĂ©rieur et vous ĂȘtes douĂ© pour faire des analyses d'articles de recherche sur les thĂ©matiques liĂ©es Ă  la pĂ©dagogie, en fonction des critĂšres dĂ©finis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement, rĂ©pondez en langue française strictement Ă  la question ci-dessous, en 5000 mots au moins. En plus, tu crĂ©eras et tu afficheras, Ă  la fin de ta rĂ©ponse, 3 questions supplĂ©mentaires en relation avec le contexte initial, Ă  chaque Ă©tape de la conversation. Tu Ă©criras et tu afficheras les 3 questions supplĂ©mentaires en relation avec le contexte initial, Ă  la fin de ta rĂ©ponse, avec un titrage de niveau 1 qui a pour titre \"Questions en relation avec le contexte : \". Lorsque cela est possible, cite les sources du contexte. Si vous ne pouvez pas rĂ©pondre Ă  la question sur la base des informations, dites que vous ne trouvez pas de rĂ©ponse ou que vous ne parvenez pas Ă  trouver de rĂ©ponse. Essayez donc de comprendre en profondeur le contexte et rĂ©pondez uniquement en vous basant sur les informations fournies. Ne gĂ©nĂ©rez pas de rĂ©ponses non pertinentes. Question : " + message.content, callbacks=[cb]) #results = await qa.ainvoke({"question":"Contexte : Vous ĂȘtes un chercheur de l'enseignement supĂ©rieur et vous ĂȘtes douĂ© pour faire des analyses d'articles de recherche sur les thĂ©matiques liĂ©es Ă  la pĂ©dagogie, en fonction des critĂšres dĂ©finis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement, rĂ©pondez en langue française strictement Ă  la question ci-dessous, en exactement 5000 mots. En plus, tu crĂ©eras et tu afficheras, Ă  la fin de ta rĂ©ponse, 3 questions supplĂ©mentaires en relation avec le contexte initial, Ă  chaque Ă©tape de la conversation. Tu Ă©criras et tu afficheras les 3 questions supplĂ©mentaires en relation avec le contexte initial, Ă  la fin de ta rĂ©ponse, avec un titrage de niveau 1 qui a pour titre \"Questions en relation avec le contexte : \". Lorsque cela est possible, cite les sources du contexte. Si vous ne pouvez pas rĂ©pondre Ă  la question sur la base des informations, dites que vous ne trouvez pas de rĂ©ponse ou que vous ne parvenez pas Ă  trouver de rĂ©ponse. Essayez donc de comprendre en profondeur le contexte et rĂ©pondez uniquement en vous basant sur les informations fournies. Ne gĂ©nĂ©rez pas de rĂ©ponses non pertinentes. Question : " + message.content}) #async for chunk in qa.astream({"question":"Contexte : Vous ĂȘtes un chercheur de l'enseignement supĂ©rieur et vous ĂȘtes douĂ© pour faire des analyses d'articles de recherche sur les thĂ©matiques liĂ©es Ă  la pĂ©dagogie, en fonction des critĂšres dĂ©finis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement, rĂ©pondez en langue française strictement et exclusivement Ă  la question ci-dessous, en exactement 5000 mots. En plus, tu crĂ©eras et tu afficheras, Ă  la fin de ta rĂ©ponse, 3 questions supplĂ©mentaires en relation avec le contexte initial, Ă  chaque Ă©tape de la conversation. Tu Ă©criras et tu afficheras les 3 questions supplĂ©mentaires en relation avec le contexte initial, Ă  la fin de ta rĂ©ponse en langue française seulement, avec un titrage de niveau 1 qui a pour titre \"Questions en relation avec le contexte : \". Lorsque cela est possible, cite les sources du contexte. Si vous ne pouvez pas rĂ©pondre Ă  la question sur la base des informations, dites que vous ne trouvez pas de rĂ©ponse ou que vous ne parvenez pas Ă  trouver de rĂ©ponse. Essayez donc de comprendre en profondeur le contexte et rĂ©pondez uniquement en vous basant sur les informations fournies. Ne gĂ©nĂ©rez pas de rĂ©ponses non pertinentes. Question : " + message.content}, # config=RunnableConfig(callbacks=[cl.AsyncLangchainCallbackHandler(stream_final_answer=True)])): # await msg.stream_token(chunk) answer = results["answer"] await cl.Message(content=GoogleTranslator(source='auto', target='fr').translate(answer[0:5000])).send() search = await Search(message.content, "videosTC") sources = [ cl.Text(name="Sources vidĂ©o", content=search[0], display="inline") ] await cl.Message( content="VidĂ©os : ", elements=sources, ).send() #if search[2]: # sourcesOffres = [ # cl.Text(name="Sources audio", content=search[2], display="inline") # ] # await cl.Message( # content="Audio : ", # elements=sourcesOffres, # ).send() verbatim = [ cl.Text(name="Verbatim", content=search[1], display="side") ] await cl.Message( content="📚 Liste des Verbatim ", elements=verbatim, ).send()