import os import time from operator import itemgetter from collections import Counter from langchain_community.document_loaders import PyPDFLoader, TextLoader from chainlit.types import AskFileResponse from langchain.text_splitter import CharacterTextSplitter, RecursiveCharacterTextSplitter from langchain.schema.runnable import Runnable, RunnablePassthrough, RunnableLambda from langchain.schema.runnable.config import RunnableConfig from langchain_community.embeddings import HuggingFaceEmbeddings from langchain.chains import ConversationalRetrievalChain, create_extraction_chain from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from langchain_community.llms import HuggingFaceEndpoint from langchain.chains import LLMChain from langchain_core.prompts import PromptTemplate from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder from langchain.schema import StrOutputParser from langchain.chains.conversational_retrieval.prompts import CONDENSE_QUESTION_PROMPT from langchain.chains.question_answering import load_qa_chain from langchain.chains.qa_with_sources import load_qa_with_sources_chain from langchain_pinecone import PineconeVectorStore from pinecone import Pinecone from langchain.memory import ChatMessageHistory, ConversationBufferMemory import pandas as pd import numpy as np import chainlit as cl from chainlit.input_widget import Select, TextInput from chainlit import user_session from offres_emploi import Api from offres_emploi.utils import dt_to_str_iso import datetime import plotly.express as px import bcrypt import json import requests import http.client from bs4 import BeautifulSoup from literalai import LiteralClient literal_client = LiteralClient(api_key=os.getenv("LITERAL_API_KEY")) literal_client.instrument_openai() @cl.password_auth_callback def auth_callback(username: str, password: str): auth = json.loads(os.environ['CHAINLIT_AUTH_LOGIN']) ident = next(d['ident'] for d in auth if d['ident'] == username) pwd = next(d['pwd'] for d in auth if d['ident'] == username) resultLogAdmin = bcrypt.checkpw(username.encode('utf-8'), bcrypt.hashpw(ident.encode('utf-8'), bcrypt.gensalt())) resultPwdAdmin = bcrypt.checkpw(password.encode('utf-8'), bcrypt.hashpw(pwd.encode('utf-8'), bcrypt.gensalt())) resultRole = next(d['role'] for d in auth if d['ident'] == username) if resultLogAdmin and resultPwdAdmin and resultRole == "admindatapcc": return cl.User( identifier=ident + " : đŸ§‘â€đŸ’Œ Admin Datapcc", metadata={"role": "admin", "provider": "credentials"} ) elif resultLogAdmin and resultPwdAdmin and resultRole == "userdatapcc": return cl.User( identifier=ident + " : 🧑‍🎓 User Datapcc", metadata={"role": "user", "provider": "credentials"} ) def process_file(file: AskFileResponse): if file.type == "text/plain": Loader = TextLoader elif file.type == "application/pdf": Loader = PyPDFLoader text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100) loader = Loader(file.path) documents = loader.load() docs = text_splitter.split_documents(documents) return docs def removeTags(all): for data in all(['style', 'script']): data.decompose() return ' '.join(all.stripped_strings) def localisation(): ListCentroids = [ { "ID": "01", "Longitude": 5.3245259, "Latitude":46.0666003 }, { "ID": "02", "Longitude": 3.5960246, "Latitude": 49.5519632 }, { "ID": "03", "Longitude": 3.065278, "Latitude": 46.4002783 }, { "ID": "04", "Longitude": 6.2237688, "Latitude": 44.1105837 }, { "ID": "05", "Longitude": 6.2018836, "Latitude": 44.6630487 }, { "ID": "06", "Longitude": 7.0755745, "Latitude":43.9463082 }, { "ID": "07", "Longitude": 4.3497308, "Latitude": 44.7626044 }, { "ID": "08", "Longitude": 4.6234893, "Latitude": 49.6473884 }, { "ID": "09", "Longitude": 1.6037147, "Latitude": 42.9696091 }, { "ID": "10", "Longitude": 4.1394954, "Latitude": 48.2963286 }, { "ID": "11", "Longitude": 2.3140163, "Latitude": 43.1111427 }, { "ID": "12", "Longitude": 2.7365234, "Latitude": 44.2786323 }, { "ID": "13", "Longitude": 5.0515492, "Latitude": 43.5539098 }, { "ID": "14", "Longitude": -0.3930779, "Latitude": 49.1024215 }, { "ID": "15", "Longitude": 2.6367657, "Latitude": 44.9643217 }, { "ID": "16", "Longitude": 0.180475, "Latitude": 45.706264 }, { "ID": "17", "Longitude": -0.7082589, "Latitude": 45.7629699 }, { "ID": "18", "Longitude": 2.5292424, "Latitude": 47.0926687 }, { "ID": "19", "Longitude": 1.8841811, "Latitude": 45.3622055 }, { "ID": "2A", "Longitude": 8.9906834, "Latitude": 41.8619761 }, { "ID": "2B", "Longitude": 9.275489, "Latitude": 42.372014 }, { "ID": "21", "Longitude": 4.7870471, "Latitude": 47.4736746 }, { "ID": "22", "Longitude": -2.9227591, "Latitude": 48.408402 }, { "ID": "23", "Longitude": 2.0265508, "Latitude": 46.0837382 }, { "ID": "24", "Longitude": 0.7140145, "Latitude": 45.1489678 }, { "ID": "25", "Longitude": 6.3991355, "Latitude": 47.1879451 }, { "ID": "26", "Longitude": 5.1717552, "Latitude": 44.8055408 }, { "ID": "27", "Longitude": 0.9488116, "Latitude": 49.1460288 }, { "ID": "28", "Longitude": 1.2793491, "Latitude": 48.3330017 }, { "ID": "29", "Longitude": -4.1577074, "Latitude": 48.2869945 }, { "ID": "30", "Longitude": 4.2650329, "Latitude": 43.9636468 }, { "ID": "31", "Longitude": 1.2728958, "Latitude": 43.3671081 }, { "ID": "32", "Longitude": 0.4220039, "Latitude": 43.657141 }, { "ID": "33", "Longitude": -0.5760716, "Latitude": 44.8406068 }, { "ID": "34", "Longitude": 3.4197556, "Latitude": 43.62585 }, { "ID": "35", "Longitude": -1.6443812, "Latitude": 48.1801254 }, { "ID": "36", "Longitude": 1.6509938, "Latitude": 46.7964222 }, { "ID": "37", "Longitude": 0.7085619, "Latitude": 47.2802601 }, { "ID": "38", "Longitude": 5.6230772, "Latitude": 45.259805 }, { "ID": "39", "Longitude": 5.612871, "Latitude": 46.7398138 }, { "ID": "40", "Longitude": -0.8771738, "Latitude": 44.0161251 }, { "ID": "41", "Longitude": 1.3989178, "Latitude": 47.5866519 }, { "ID": "42", "Longitude": 4.2262355, "Latitude": 45.7451186 }, { "ID": "43", "Longitude": 3.8118151, "Latitude": 45.1473029 }, { "ID": "44", "Longitude": -1.7642949, "Latitude": 47.4616509 }, { "ID": "45", "Longitude": 2.2372695, "Latitude": 47.8631395 }, { "ID": "46", "Longitude": 1.5732157, "Latitude": 44.6529284 }, { "ID": "47", "Longitude": 0.4788052, "Latitude": 44.4027215 }, { "ID": "48", "Longitude": 3.4991239, "Latitude": 44.5191573 }, { "ID": "49", "Longitude": -0.5136056, "Latitude": 47.3945201 }, { "ID": "50", "Longitude": -1.3203134, "Latitude": 49.0162072 }, { "ID": "51", "Longitude": 4.2966555, "Latitude": 48.9479636 }, { "ID": "52", "Longitude": 5.1325796, "Latitude": 48.1077196 }, { "ID": "53", "Longitude": -0.7073921, "Latitude": 48.1225795 }, { "ID": "54", "Longitude": 6.144792, "Latitude": 48.7995163 }, { "ID": "55", "Longitude": 5.2888292, "Latitude": 49.0074545 }, { "ID": "56", "Longitude": -2.8746938, "Latitude": 47.9239486 }, { "ID": "57", "Longitude": 6.5610683, "Latitude": 49.0399233 }, { "ID": "58", "Longitude": 3.5544332, "Latitude": 47.1122301 }, { "ID": "59", "Longitude": 3.2466616, "Latitude": 50.4765414 }, { "ID": "60", "Longitude": 2.4161734, "Latitude": 49.3852913 }, { "ID": "61", "Longitude": 0.2248368, "Latitude": 48.5558919 }, { "ID": "62", "Longitude": 2.2555152, "Latitude": 50.4646795 }, { "ID": "63", "Longitude": 3.1322144, "Latitude": 45.7471805 }, { "ID": "64", "Longitude": -0.793633, "Latitude": 43.3390984 }, { "ID": "65", "Longitude": 0.1478724, "Latitude": 43.0526238 }, { "ID": "66", "Longitude": 2.5239855, "Latitude": 42.5825094 }, { "ID": "67", "Longitude": 7.5962225, "Latitude": 48.662515 }, { "ID": "68", "Longitude": 7.2656284, "Latitude": 47.8586205 }, { "ID": "69", "Longitude": 4.6859896, "Latitude": 45.8714754 }, { "ID": "70", "Longitude": 6.1388571, "Latitude": 47.5904191 }, { "ID": "71", "Longitude": 4.6394021, "Latitude": 46.5951234 }, { "ID": "72", "Longitude": 0.1947322, "Latitude": 48.0041421 }, { "ID": "73", "Longitude": 6.4662232, "Latitude": 45.4956055 }, { "ID": "74", "Longitude": 6.3609606, "Latitude": 46.1045902 }, { "ID": "75", "Longitude": 2.3416082, "Latitude": 48.8626759 }, { "ID": "76", "Longitude": 1.025579, "Latitude": 49.6862911 }, { "ID": "77", "Longitude": 2.8977309, "Latitude": 48.5957831 }, { "ID": "78", "Longitude": 1.8080138, "Latitude": 48.7831982 }, { "ID": "79", "Longitude": -0.3159014, "Latitude": 46.5490257 }, { "ID": "80", "Longitude": 2.3380595, "Latitude": 49.9783317 }, { "ID": "81", "Longitude": 2.2072751, "Latitude": 43.8524305 }, { "ID": "82", "Longitude": 1.2649374, "Latitude": 44.1254902 }, { "ID": "83", "Longitude": 6.1486127, "Latitude": 43.5007903 }, { "ID": "84", "Longitude": 5.065418, "Latitude": 44.0001599 }, { "ID": "85", "Longitude": -1.3956692, "Latitude": 46.5929102 }, { "ID": "86", "Longitude": 0.4953679, "Latitude": 46.5719095 }, { "ID": "87", "Longitude": 1.2500647, "Latitude": 45.9018644 }, { "ID": "88", "Longitude": 6.349702, "Latitude": 48.1770451 }, { "ID": "89", "Longitude": 3.5634078, "Latitude": 47.8474664 }, { "ID": "90", "Longitude": 6.9498114, "Latitude": 47.6184394 }, { "ID": "91", "Longitude": 2.2714555, "Latitude": 48.5203114 }, { "ID": "92", "Longitude": 2.2407148, "Latitude": 48.835321 }, { "ID": "93", "Longitude": 2.4811577, "Latitude": 48.9008719 }, { "ID": "94", "Longitude": 2.4549766, "Latitude": 48.7832368 }, { "ID": "95", "Longitude": 2.1802056, "Latitude": 49.076488 }, { "ID": "974", "Longitude": 55.536384, "Latitude": -21.115141 }, { "ID": "973", "Longitude": -53.125782, "Latitude": 3.933889 }, { "ID": "972", "Longitude": -61.024174, "Latitude": 14.641528 }, { "ID": "971", "Longitude": -61.551, "Latitude": 16.265 } ] longLat = pd.DataFrame(ListCentroids) return longLat def modele(document): match document: case "Note de composante sectorielle": note = """2. Analyse du systĂšme travail 2.1 Secteurs en lien avec la discipline 2.1.1 Indiquer la nature des secteurs, la rĂ©partition des entreprises. DĂ©crire les enjeux pour ce secteur (axes de dĂ©veloppement, de transformations). Indiquer les OPĂ©rateurs de COmpĂ©tences de la branche professionnelle correspondante en France. 2.2 Analyses des offres d’emploi 2.2.1 Indiquer les statistiques de l’emploi sur une pĂ©riode. Identifier les 5 principales appellations mĂ©tiers seulement en fonction du contexte, en crĂ©er une liste contextualisĂ©e, avec les pourcentages du nombre d'offres pour chaque emploi par rapport au nombre total d'offres. 2.2.2 missions, activitĂ©s et compĂ©tences demandĂ©es (Ă©crites avec un verbe d'action). DĂ©crire le/les profils types des recrutĂ©s par les employeurs du systĂšme de travail. Lister, au format liste, les Ă©volutions professionnelles ou les exemples de spĂ©cialisation, lister, au format liste, les dĂ©bouchĂ©s, lister, au format liste, les avantages du mĂ©tier, lister, au format liste, les inconvĂ©nients du mĂ©tier, lister, au format liste, les conseils pour rĂ©ussir dans ce mĂ©tier. 2.2.3 Indiquer si les emplois sont en tension """ case "Fiche Potentiel Profil de Sortie": note = """1. Nom de la fiche 2. Niveau du diplĂŽme et son IntitulĂ© (nom long plus sigle). Le niveau de qualification 3. Le rĂ©sumĂ© du profil et du potentiel de sortie. Il est composĂ© de plusieurs parties : L'identitĂ©/ les spĂ©cificitĂ©s de la composante. Cette introduction de 5 Ă  10 lignes est utile pour caractĂ©riser le diplĂŽme. Il s'agit d’avoir une description sur les thĂ©matiques de recherche de la composante. Elles sont indiquĂ©es afin d’établir le lien entre la recherche et des enjeux possibles dans le systĂšme travail. Elle facilite la comprĂ©hension des domaines de compĂ©tences dans lequel s’inscrit le futur diplĂŽmĂ©. La culture disciplinaire est Ă  indiquer car elle contribue Ă  caractĂ©riser le diplĂŽme. L'identitĂ© professionnelle du diplĂŽmĂ©. Les informations professionnelles sont organisĂ©es par mailles (du plus large au plus prĂ©cis) secteur, famille de mĂ©tiers, activitĂ©s, compĂ©tences, compĂ©tences transversales. Il est nĂ©cessaire d’ĂȘtre attentif au niveau de qualification de sortie. Nous avons des emplois accessibles dĂšs l’obtention du diplĂŽme, d'autres ne le seront qu’avec un une qualification supĂ©rieure et/ou avec de l’expĂ©rience. Il souhaitable de faire une description globale du profil en apportant des informations sur le niveau d’autonomie et de responsabilitĂ© et les caractĂ©ristiques d’exercice des emplois (spĂ©cialisĂ© ou gĂ©nĂ©raliste, polyvalent ou expert etc).Cette seconde partie de texte de 10 Ă  15 lignes introduit les domaines et enjeux sectoriels et/ou terrain de mise en Ɠuvre (3 lignes), les principales appellations d’emploi (1 Ă  2 lignes), les activitĂ©s professionnelles (employabilitĂ© ) et le processus mĂ©tier (3 Ă  4 lignes), les principaux interlocuteurs (1 Ă  2 lignes), les diffĂ©rents contextes de mise en Ɠuvre (dĂ©placements, langues Ă©trangĂšres). Cette description peut ĂȘtre suivi la liste d’emplois (avec une prĂ©sentation courte 5 lignes) accessibles en indiquant le cas Ă©chĂ©ant les spĂ©cificitĂ©s 4. La rĂ©glementation le cas Ă©chĂ©ant 5. Secteurs d'activitĂ© ou types d'emplois accessibles par le dĂ©tenteur de ce diplĂŽme 6. Le type de structure et d’organisations professionnelles 7. Listes des suites de parcours acadĂ©miques ou passerelles de formation 8. Codes Rome 9. RĂ©fĂ©rence de la fiche RNCP """ return note def definition(document): if document == "activite": meanings = """ DĂ©finition d'une activitĂ© : une activitĂ© est un ensemble cohĂ©rent d'actions combinĂ©es : pour la rĂ©aliser, plusieurs compĂ©tences et opĂ©rations sont nĂ©cessaires, soit successivement, soit conjointement. Elles s'inscrivent Ă  des moments clĂ©s du processus de rĂ©alisation et ne peuvent en aucun cas ĂȘtre occultĂ©es, car elles conditionnent le rĂ©sultat. Plusieurs activitĂ©s en vue d'une finalitĂ© avec une valeur ajoutĂ©e Ă  un produit ou un service sont nĂ©cessaires pour mettre en Ɠuvre un processus mĂ©tier. De ce fait, il est essentiel de dĂ©terminer pour chaque activitĂ© sa propre finalitĂ© et de s'assurer que l'ensemble des activitĂ©s participent bien d'un mĂȘme processus. """ elif document == "competence": meanings = """ DĂ©finition d'une compĂ©tence : la compĂ©tence est une combinaison de savoirs en action, mobilisĂ©s en vue de rĂ©aliser une activitĂ© professionnelle. Elle s'apprĂ©cie, en tant qu'acquis de l'apprentissage selon des modalitĂ©s adaptĂ©es permettant d'en certifier la possession et au regard de l'atteinte d'un rĂ©sultat pour un niveau d'exigence prĂ©dĂ©terminĂ©. Les compĂ©tences peuvent ĂȘtre regroupĂ©es par domaines selon la nature et leur liaison subordonnĂ©e aux activitĂ©s. Elles s'Ă©crivent Ă  l'aide de verbe d'action Ă  l'infinitif comme le stipule la taxonomie de Bloom pour marquer une progression dans l'exercice de la compĂ©tence. """ elif document == "promptLibraryNCS": meanings = """ Exemple de requĂȘtes sur la note sectorielle : traitement statistique et gĂ©nĂ©ration des codes des objets de datavisualisation\nQuestion1 : donne le dataframe des appellations mĂ©tiers et de leur pourcentage.\nQuestion2 : donne le plotly.js du dataframe avec les labels des appellations mĂ©tiers et les labels des pourcentages.\nQuestion3 : convertis en plotly.js au format javascript\nQuestion4 : donne les salaires moyens.\nQuestion5 : donne le rĂ©sultat des salaires moyens par appellations mĂ©tiers dans un tableau.\nQuestion6 : donne le plotly du tableau des salaires moyens par appellation mĂ©tier.\nQuestion7 : convertis en plotly.js au format javascript avec les labels des salaires moyens et les labels des appellations mĂ©tiers\nQuestion8 : donne le pourcentage des contrats en CDI.\nQuestion9 : donne le rĂ©sultat dans un tableau\nQuestion10 : donne le plotly du tableau\nQuestion11 : convertis le plotly en plotly.js au format javascript, avec affichage de tous les labels.\nQuestion12 : donne les 10 compĂ©tences professionnelles principales avec leur pourcentage.\nQuestion13 : donne le rĂ©sultat dans un tableau.\nQuestion14 : donne le plotly du tableau\nQuestion15 : convertis le plotly en plotly.js au format javascript, avec affichage de tous les labels.\nQuestion16 : quelles sont les appellations mĂ©tiers accessibles selon une expĂ©rience dĂ©butant, en donnant un pourcentage?\nQuestion17 : donne le rĂ©sultat dans un tableau.\nQuestion18 : donne le plotly du tableau.\nQuestion19 : convertis le plotly en plotly.js au format javascript, avec affichage de tous les labels.\nQuestion20 : quelles sont les appellations mĂ©tiers accessibles selon un niveau de qualification jusqu'Ă  Bac+2 ou assimilĂ©s, en donnant un pourcentage?\nQuestion21 : donne le rĂ©sultat dans un tableau.\nQuestion22 : donne le plotly du tableau\nQuestion23 : convertis le plotly en plotly.js au format javascript, avec affichage de tous les labels.\nQuestion24 : donne le pourcentage des appellations mĂ©tiers en fonction des types d'entreprise.\nQuestion25 : construis le tableau en faisant une estimation.\nQuestion26 : donne le plotly du tableau estimĂ© avec les pourcentage Ă©valuĂ©s par toi-mĂȘme\nQuestion27 : convertis le plotly en plotly.js au format javascript, avec affichage de tous les labels, issus de votre estimation. """ elif document == "promptLibraryFCS": meanings = """ Exemple de requĂȘtes sur la fiche synoptique : construction d'un programme de formation complet\nQuestion1 : crĂ©e un programme de formation, en 4000 mots, sur 3 ans dĂ©coupĂ©s en 6 semestres, comportant 3 blocs de compĂ©tences pĂ©dagogiques, dont les intitulĂ©s commencent par un verbe d'action, par semestre correspondant Ă  3 unitĂ©s d'enseignement par semestre et 3 cours par unitĂ© d'enseignement, en corrĂ©lation avec les activitĂ©s professionnelles et les compĂ©tences professionnelles de la fiche synoptique, marquant une progression dans les apprentissages.\nQuestion2 : donne le synopsis du cours1 de l'UE1\nQuestion3 : plus?\nQuestion4 : et les supports pĂ©dagogiques? """ return meanings def listToString(list): return str(list) def arrayToString(array): arrayList = [] for i in range(0,len(array)): if listToString(array[i]).find("libelle")!=-1: arrayList.append(array[i]['libelle']) else: arrayList.append("; ") string = ', '.join(arrayList) return string + '; ' def searchByRome(rome,index): libelle = '' if rome.find(',') != -1: romeArray = rome.split(',') for i in range(0,len(romeArray)): codeRome = romeArray[i].strip() if i <= 5 and len(codeRome) == 5: all_docs = index.query( top_k=1, vector= [0] * 768, # embedding dimension namespace='', filter={"categorie": {"$eq": "rome"}, "rome":{"$eq": codeRome}}, include_metadata=True ) libelle = libelle + " " + all_docs['matches'][0]['metadata']['libelle_rome'] else: all_docs = index.query( top_k=1, vector= [0] * 768, # embedding dimension namespace='', filter={"categorie": {"$eq": "rome"}, "rome":{"$eq": rome}}, include_metadata=True ) libelle = libelle + " " + all_docs['matches'][0]['metadata']['libelle_rome'] return libelle @cl.author_rename def rename(orig_author: str): rename_dict = {"ConversationalRetrievalChain": "💬 Assistant conversationnel", "Retriever": "Agent conversationnel", "StuffDocumentsChain": "ChaĂźne de documents", "LLMChain": "Agent", "HuggingFaceEndpoint": "Mistral AI đŸ€–"} return rename_dict.get(orig_author, orig_author) @cl.action_callback("download") async def on_action(action): content = [] content.append(action.value) arrayContent = np.array(content) df = pd.DataFrame(arrayContent) with open('./' + action.description + '.txt', 'wb') as csv_file: df.to_csv(path_or_buf=csv_file, index=False,header=False, encoding='utf-8') elements = [ cl.File( name= action.description + ".txt", path="./" + action.description + ".txt", display="inline", ), ] await cl.Message( author="Datapcc : 🌐🌐🌐", content="[Lien] 🔗", elements=elements ).send() await action.remove() @cl.action_callback("datavizEmploi") async def on_action(action): client = Api(client_id=os.environ['POLE_EMPLOI_CLIENT_ID'], client_secret=os.environ['POLE_EMPLOI_CLIENT_SECRET']) todayDate = datetime.datetime.today() month, year = (todayDate.month-1, todayDate.year) if todayDate.month != 1 else (12, todayDate.year-1) start_dt = todayDate.replace(day=1, month=month, year=year) end_dt = datetime.datetime.today() arraydataframe = [] arrayfirstdataframe = [] arraylocalisationdataframe = [] results = [] count = 0 listrome = action.value arrayrome = listrome.split(',') for k in arrayrome: params = {"motsCles": k,'minCreationDate': dt_to_str_iso(start_dt),'maxCreationDate': dt_to_str_iso(end_dt),'range':'0-149'} search_on_big_data = client.search(params=params) results += search_on_big_data["resultats"] results_df = pd.DataFrame(results) if results_df.empty == False: count = count + 1 finals = results_df[['intitule','typeContratLibelle','experienceLibelle','competences','qualitesProfessionnelles','salaire','lieuTravail','formations']] finals["lieuTravail"] = finals["lieuTravail"].apply(lambda x: np.array(x)).apply(lambda x: x['libelle']).apply(lambda x: x[0:3]).apply(lambda x: x.strip()) finals_df = finals finals_df.dropna(subset=['qualitesProfessionnelles','formations','competences'], inplace=True) finals_df["competences"] = finals_df["competences"].apply(lambda x:[str(e['libelle']) for e in x]).apply(lambda x:'; '.join(map(str, x))) finals_df["qualitesProfessionnelles"] = finals_df["qualitesProfessionnelles"].apply(lambda x:[str(e['libelle']) + ": " + str(e['description']) for e in x]).apply(lambda x:'; '.join(map(str, x))) finals_df["formations"] = finals_df["formations"].apply(lambda x:[str(e['niveauLibelle']) for e in x]).apply(lambda x:'; '.join(map(str, x))) finals_df = finals_df.sort_values(by=['lieuTravail']) finals_localisation = results_df[['lieuTravail']] finals_localisation["lieuTravail"] = finals_localisation["lieuTravail"].apply(lambda x: np.array(x)).apply(lambda x: x['libelle']).apply(lambda x: x[0:3]).apply(lambda x: x.strip()) finals_localisation.drop(finals_localisation[finals_localisation['lieuTravail'] == 'Fra'].index, inplace = True) finals_localisation.drop(finals_localisation[finals_localisation['lieuTravail'] == 'FRA'].index, inplace = True) finals_localisation.drop(finals_localisation[finals_localisation['lieuTravail'] == 'Ile'].index, inplace = True) finals_localisation.drop(finals_localisation[finals_localisation['lieuTravail'] == 'Mar'].index, inplace = True) finals_localisation.drop(finals_localisation[finals_localisation['lieuTravail'] == 'Bou'].index, inplace = True) finals_localisation.drop(finals_localisation[finals_localisation['lieuTravail'] == '976'].index, inplace = True) arraylocalisationdataframe.append(finals_localisation) arrayfirstdataframe.append(results_df) if len(finals_df) != 0: arraydataframe.append(finals_df) first_df = pd.concat(arrayfirstdataframe) finals_df = pd.concat(arraydataframe) localisation_df = pd.concat(arraylocalisationdataframe) ######## Emplois ######## df_intitule = first_df.groupby('intitule').size().reset_index(name='obs') df_intitule = df_intitule.sort_values(by=['obs']) df_intitule = df_intitule.iloc[-25:] fig_intitule = px.bar(df_intitule, x='obs', y='intitule', width=800, height=600, orientation='h', color='obs', title="Les principaux emplois", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10,color="RebeccaPurple"),autosize=True).update_traces(hovertemplate=df_intitule["intitule"] + '
Nombre : %{x}', y=[y[:100] + "..." for y in df_intitule["intitule"]], showlegend=False) ######## Types de contrat ######## df_contrat = first_df.groupby('typeContratLibelle').size().reset_index(name='obs') fig_contrat = px.pie(df_contrat, names='typeContratLibelle', width=800, height=800, values='obs', color='obs', title="Les types de contrat", labels={'obs':'nombre'}, color_discrete_sequence=px.colors.qualitative.Safe).update_traces(textposition='inside', textinfo='percent+label').update_layout(font=dict(size=10,color="RebeccaPurple")) df_secteur = first_df.groupby('secteurActiviteLibelle').size().reset_index(name='obs') df_secteur = df_secteur.sort_values(by=['obs']) df_secteur = df_secteur.iloc[-25:] fig_secteur = px.bar(df_secteur, x='obs', y='secteurActiviteLibelle', width=800, height=600, orientation='h', color='obs', title="Les principaux secteurs d'activités", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10,color="RebeccaPurple"),autosize=True).update_traces(hovertemplate=df_secteur["secteurActiviteLibelle"] + '
Nombre : %{x}', y=[y[:100] + "..." for y in df_secteur["secteurActiviteLibelle"]], showlegend=False) ######## Compétences professionnelles ######## df1 = finals_df df1['competences'] = finals_df['competences'].str.split(';') df2 = df1.explode('competences') df2 = df2.groupby('competences').size().reset_index(name='obs') df2 = df2.sort_values(by=['obs']) df2 = df2.iloc[-20:] fig_competences = px.bar(df2, x='obs', y='competences', width=800, height=550, orientation='h', color='obs', title="Les principales compétences professionnelles", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10,color="RebeccaPurple"),autosize=True).update_traces(hovertemplate=df2["competences"] + '
Nombre : %{x}', y=[y[:100] + "..." for y in df2['competences']], showlegend=False) ######## Compétences transversales ######## df_transversales = finals_df df_transversales['qualitesProfessionnelles'] = finals_df['qualitesProfessionnelles'].str.split(';') df_comptransversales = df_transversales.explode('qualitesProfessionnelles') df_comptransversales = df_comptransversales.groupby('qualitesProfessionnelles').size().reset_index(name='obs') df_comptransversales = df_comptransversales.sort_values(by=['obs']) df_comptransversales = df_comptransversales.iloc[-20:] fig_transversales = px.bar(df_comptransversales, x='obs', y='qualitesProfessionnelles', width=800, height=550, orientation='h', color='obs', title="Les principales compétences transversales", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10,color="RebeccaPurple"),autosize=True).update_traces(hovertemplate=df_comptransversales["qualitesProfessionnelles"] + '
Nombre : %{x}', y=[y[:100] + "..." for y in df_comptransversales["qualitesProfessionnelles"]], showlegend=False) ######## Niveaux de qualification ######## df_formations = finals_df.groupby('formations').size().reset_index(name='obs') fig_formations = px.pie(df_formations, names='formations', width=800, height=800, values='obs', color='obs', title="Les niveaux de qualification", labels={'obs':'nombre'}, color_discrete_sequence=px.colors.qualitative.Safe).update_traces(textposition='inside', textinfo='percent+label').update_layout(font=dict(size=10,color="RebeccaPurple")) ######## ExpĂ©riences professionnelles ######## df_experience = finals_df.groupby('experienceLibelle').size().reset_index(name='obs') fig_experience = px.pie(df_experience, names='experienceLibelle', width=800, height=800, values='obs', color='obs', title="Les expĂ©riences professionnelles", labels={'obs':'nombre'}, color_discrete_sequence=px.colors.qualitative.Safe).update_traces(textposition='inside', textinfo='percent+label').update_layout(font=dict(size=10,color="RebeccaPurple")) res = requests.get( "https://raw.githubusercontent.com/codeforgermany/click_that_hood/main/public/data/spain-provinces.geojson" ) ######## localisation ######## longLat = localisation() df_localisation = localisation_df.groupby('lieuTravail').size().reset_index(name='obs') df_localisation = df_localisation.sort_values(by=['lieuTravail']) df_localisation['longitude'] = df_localisation['lieuTravail'] df_localisation['latitude'] = df_localisation['lieuTravail'] df_localisation["longitude"] = df_localisation['longitude'].apply(lambda x:longLat.loc[longLat['ID'] == x, 'Longitude'].iloc[0]) df_localisation["latitude"] = df_localisation['latitude'].apply(lambda x:longLat.loc[longLat['ID'] == x, 'Latitude'].iloc[0]) fig_localisation = px.scatter_mapbox(df_localisation, lat="latitude", lon="longitude", height=600,hover_name="lieuTravail", size="obs").update_layout( mapbox={ "style": "carto-positron", "center": {"lon": 2, "lat" : 47}, "zoom": 4.5, "layers": [ { "source": res.json(), "type": "line", "color": "green", "line": {"width": 0}, } ], } ) elements.append(cl.Plotly(name="chart_intitule", figure=fig_intitule, display="inline", size="large")) elements.append(cl.Plotly(name="chart_contrat", figure=fig_contrat, display="inline", size="large")) elements.append(cl.Plotly(name="chart_competences", figure=fig_competences, display="inline", size="large")) elements.append(cl.Plotly(name="chart_transversales", figure=fig_transversales, display="inline", size="large")) elements.append(cl.Plotly(name="chart_formations", figure=fig_formations, display="inline", size="large")) elements.append(cl.Plotly(name="chart_experience", figure=fig_experience, display="inline", size="large")) elements.append(cl.Plotly(name="chart_secteur", figure=fig_secteur, display="inline", size="large")) elements.append(cl.Plotly(name="chart_localisation", figure=fig_localisation, display="inline", size="large")) await cl.Message(content="Datavisualisation du marchĂ© de l'emploi", elements=elements).send() await action.remove() @cl.action_callback("saveMemory") async def on_action(action): buffer = cl.user_session.get("saveMemory") cl.user_session.set("saveMemory", buffer + action.value) await cl.Message( author="Datapcc : 🌐🌐🌐", content="đŸ—ƒïž Document sauvegardĂ© dans le buffer Memory!" ).send() await action.remove() @cl.cache def to_cache(file): #time.sleep(5) # Simulate a time-consuming process return "https://cipen.univ-gustave-eiffel.fr/fileadmin/CIPEN/datas/assets/docs/" + file + ".csv" @cl.set_chat_profiles async def chat_profile(): return [ cl.ChatProfile(name="Note composante sectorielle - NCS",markdown_description="Note composante sectorielle",icon="./public/favicon.png",), ] @cl.on_chat_start async def start(): await cl.Avatar( name="You", path="./public/logo-ofipe.jpg", ).send() chat_profile = cl.user_session.get("chat_profile") chatProfile = chat_profile.split(' - ') if chatProfile[1] == 'NCS': app_user = cl.user_session.get("user") welcomeUser = app_user.identifier welcomeUserArray = welcomeUser.split('@') welcomeUserStr = welcomeUserArray[0].replace('.',' ') await cl.Message(f"> Bonjour {welcomeUserStr}").send() df_allcompetences = pd.read_csv('./public/referentiel_competence.csv') df_competences = df_allcompetences[['libelle_competence']].copy() df_competences = df_competences.sort_values(by=['libelle_competence']) competences_list = df_competences['libelle_competence'].tolist() competences_list.sort() competences_list.insert(0, "") cl.user_session.set("arraySettingsComp", competences_list) settings = await cl.ChatSettings( [ Select( id="competence", label="CompĂ©tences", values=competences_list, initial_index=0, ), TextInput(id="competenceInput", label="ou saisir une compĂ©tence voire des objectifs pĂ©dagogiques", placeholder="ou saisir une compĂ©tence voire des objectifs pĂ©dagogiques", tooltip="saisir une compĂ©tence voire des objectifs pĂ©dagogiques"), ] ).send() value = settings["competence"] if len(value) < 2: warning = [ cl.Image(name="Warning", size="small", display="inline", path="./public/warning.png") ] await cl.Message(author="Datapcc : 🌐🌐🌐",content="1ïžâƒŁ Cliquez sur le bouton dont l'image suit, dans le prompt, pour commencer Ă  Ă©laborer une note sectorielle de la chaĂźne documentaire APCC!").send() await cl.Message(author="Datapcc : 🌐🌐🌐",content="", elements=warning).send() await cl.Message(author="Datapcc : 🌐🌐🌐",content="2ïžâƒŁ Puis sĂ©lectionnez ou saisissez une compĂ©tence ou des objectifs pĂ©dagogiques. Et vous ĂȘtes prĂȘt!\n\n🔗 Plateforme de feedback et de fil d'activitĂ© : https://cloud.getliteral.ai/").send() contextChat = cl.user_session.get("contextChatBot") if not contextChat: contextChat = df_competences.to_string(index = False) os.environ['HUGGINGFACEHUB_API_TOKEN'] = os.environ['HUGGINGFACEHUB_API_TOKEN'] repo_id = "mistralai/Mistral-7B-Instruct-v0.3" model = HuggingFaceEndpoint( repo_id=repo_id, max_new_tokens=6000, temperature=1.0, streaming=True ) cl.user_session.set("memory", ConversationBufferMemory(return_messages=True)) memory = cl.user_session.get("memory") prompt = ChatPromptTemplate.from_messages( [ ( "system", f"Contexte : Vous ĂȘtes un spĂ©cialiste du marchĂ© de l'emploi en fonction du niveau de qualification, des compĂ©tences professionnelles, des compĂ©tences transversales, du salaire et de l'expĂ©rience. Vous ĂȘtes douĂ© pour faire des analyses du systĂšme travail sur les mĂ©tiers les plus demandĂ©s grĂące Ă  votre aptitude Ă  synthĂ©tiser les informations en fonction des critĂšres dĂ©finis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement. Contexte : {contextChat[0:26500]}. RĂ©ponds Ă  la question suivante de la maniĂšre la plus pertinente, la plus exhaustive et la plus dĂ©taillĂ©e possible, avec au minimum 3000 tokens jusqu'Ă  4000 tokens, seulement et strictement dans le contexte et les informations fournies. Essayez donc de comprendre en profondeur le contexte et rĂ©pondez uniquement en vous basant sur les informations fournies.", ), MessagesPlaceholder(variable_name="history"), ("human", "{question}, dans le contexte fourni."), ] ) runnable = ( RunnablePassthrough.assign( history=RunnableLambda(memory.load_memory_variables) | itemgetter("history") ) | prompt | model ) cl.user_session.set("runnable", runnable) @literal_client.step(type="run") async def construction_NCS(competenceList): context = await contexte(competenceList) emploisST = cl.user_session.get("EmploiST") memory = ConversationBufferMemory(return_messages=True) ### Mistral Completion ### client_llm = await IA() structure = str(modele('Note de composante sectorielle')) definitions = definition('activite') + ' ' + definition('competence') template = """[INST] Vous ĂȘtes un spĂ©cialiste du marchĂ© de l'emploi en fonction du niveau de qualification, des compĂ©tences professionnelles, des compĂ©tences transversales, du salaire et de l'expĂ©rience. Vous ĂȘtes douĂ© pour faire des analyses du systĂšme travail sur les mĂ©tiers les plus demandĂ©s grĂące Ă  votre aptitude Ă  synthĂ©tiser les informations en fonction des critĂšres dĂ©finis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement, rĂ©pondez Ă  la question ci-dessous Ă  partir du contexte ci-dessous : {context} {question} [/INST] """ question_p =""" Peux-tu crĂ©er une note sectorielle d'aprĂšs le modĂšle de note sectorielle prĂ©cĂ©dent en respectant ses parties : 2., 2.1, 2.1.1, 2.2, 2.2.1, 2.2.2, 2.2.3 et d'aprĂšs le contexte en vous rĂ©ferrant strictement aux donnĂ©es du contexte fixĂ©? RĂ©ponse sous forme d'un texte gĂ©nĂ©rĂ© d'aprĂšs le modĂšle et le contexte en 5000 mots et en langue française absolument. """ context_p = f"Contexte : {context}. {definitions} ModĂšle de note sectorielle : {structure}. RĂ©ponds en langue française strictement Ă  la question suivante en respectant strictement les donnĂ©es 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. Si les informations du contexte sont insuffisantes, procĂ©dez Ă  une projection sur le secteur, les entreprises et le marchĂ© de l'emploi, pour construire la note de composante sectorielle." prompt = PromptTemplate(template=template, input_variables=["question","context"]) #llm_chain = LLMChain(prompt=prompt, llm=client_llm) #completion_NCS = llm_chain.run({"question":question_p,"context":context_p}, callbacks=[StreamingStdOutCallbackHandler()]) chain = ( RunnablePassthrough.assign( history=RunnableLambda(memory.load_memory_variables) | itemgetter("history") ) | prompt | client_llm ) msg = cl.Message(author="Datapcc : 🌐🌐🌐",content="") async for chunk in chain.astream({"question":question_p,"context":context_p}, config=RunnableConfig(callbacks=[cl.AsyncLangchainCallbackHandler(stream_final_answer=True)])): await msg.stream_token(chunk) cl.user_session.set("NCS" + romeListArray[0], msg.content) cl.user_session.set("contextChatBot", context + "\n" + msg.content) await cl.sleep(2) listEmplois_name = f"Liste des emplois" text_elements = [] text_elements.append( cl.Text(content="Question : " + romeListArray[0] + "\n\nRĂ©ponse :\n" + emploisST.replace('Emploi : ','\n✔ Emploi : ').replace('Contrat : ','\nContrat : ').replace('CompĂ©tences professionnelles : ','\nCompĂ©tences professionnelles : ').replace('Salaire : ','\nSalaire : ').replace('Qualification : ','\nQualification : '), name=listEmplois_name) ) await cl.Message(author="Datapcc : 🌐🌐🌐",content="đŸ‘šâ€đŸ’Œ Source PĂŽle Emploi : " + listEmplois_name, elements=text_elements).send() await cl.sleep(2) datavizEmploi = [ cl.Action(name="datavizEmploi", value=romeListArray[0], description="Afficher la datavisualisation du marchĂ© de l'emploi") ] await cl.Message(author="Datapcc : 🌐🌐🌐",content="📊 Afficher la datavisualisation du marchĂ© de l'emploi", actions=datavizEmploi).send() await cl.sleep(2) if romeListArray[0].find(',') != -1: codeArray = romeListArray[0].split(',') ficheMetiers = [] for i in range(0,len(codeArray)): ficheMetiers = [ cl.File(name= "Fiche mĂ©tier " + codeArray[i],url="https://www.soi-tc.fr/assets/fiches_pe/FEM_" + codeArray[i] + ".pdf",display="inline",) ] await cl.Message( author="Datapcc : 🌐🌐🌐", content="[Fiches mĂ©tiers] 🔗", elements=ficheMetiers ).send() else: ficheMetiers = [ cl.File(name= "Fiche mĂ©tier " + romeListArray[0],url="https://www.soi-tc.fr/assets/fiches_pe/FEM_" + romeListArray[0] + ".pdf",display="inline",) ] await cl.Message( author="Datapcc : 🌐🌐🌐", content="[Fiches mĂ©tiers] 🔗", elements=ficheMetiers ).send() await cl.sleep(2) listPrompts_name = f"Liste des requĂȘtes sur la note sectorielle" prompt_elements = [] prompt_elements.append( cl.Text(content=definition('promptLibraryNCS'), name=listPrompts_name) ) await cl.Message(author="Datapcc : 🌐🌐🌐",content="📚 BibliothĂšque de prompts : " + listPrompts_name, elements=prompt_elements).send() await cl.sleep(2) actions = [ cl.Action(name="download", value=msg.content, description="download_note_sectorielle") ] await cl.Message(author="Datapcc : 🌐🌐🌐",content="TĂ©lĂ©charger la note", actions=actions).send() await cl.sleep(2) saves = [ cl.Action(name="saveToMemory", value=msg.content, description="Mettre en mĂ©moire la note") ] await cl.Message(author="Datapcc : 🌐🌐🌐",content="Mettre en mĂ©moire la note", actions=saves).send() await cl.sleep(2) memory.chat_memory.add_user_message(question_p) memory.chat_memory.add_ai_message(msg.content) return "Construction de la Note Sectorielle" @cl.step(type="run") async def recuperation_contexte(getNote): getContext = cl.user_session.get(getNote) return getNote + " :\n" + getContext @cl.step(type="retrieval") async def contexte(romeListArray): #results = await API_FranceTravail(romeListArray) results = await creation_liste_code_Rome_et_emplois(romeListArray) results_df = results finals = results_df[['intitule','typeContratLibelle','experienceLibelle','competences','qualitesProfessionnelles','salaire','lieuTravail','formations','description']].copy() finals["lieuTravail"] = finals["lieuTravail"].apply(lambda x: x['libelle']).apply(lambda x: x[0:3]).apply(lambda x: x.strip()) emplois = results_df.values.tolist() listEmplois = [] for i in range(0,len(emplois)): listEmplois.append("\nEmploi : " + emplois[i][0] + "; Contrat : " + emplois[i][1] + "; CompĂ©tences professionnelles : " + arrayToString(emplois[i][3]) + "; " + "Salaire : " + listToString(emplois[i][5]) + "; Qualification : " + emplois[i][4] + "; Localisation : " + emplois[i][6] + "; ExpĂ©rience : " + emplois[i][2] + "; Niveau de qualification : " + listToString(emplois[i][7]) + "; Description de l'emploi : " + listToString(emplois[i][8])) #emplois_list = results[0] #context = emplois_list.replace('[','').replace(']','').replace('{','').replace('}','') context = listEmplois ficheMetier = await FicheMetier("https://candidat.francetravail.fr/metierscope/fiche-metier/", romeListArray[0]) ficheClesMetier = await ChiffresClesMetier("https://dataemploi.francetravail.fr/metier/chiffres-cles/NAT/FR/", romeListArray[0]) #ficheMetiersCompetencesSavoirs = await Fiche_metier_competences_savoirs(romeListArray[0]) #metierSecteurContexteTravail = await Metier_secteur_contexte_travail(romeListArray[0]) cl.user_session.set("EmploiST", context) return "Fiches MĂ©tiers :\n" + ficheMetier + "\nChiifres clĂ©s mĂ©tiers :\n" + ficheClesMetier + "\nListe des emplois issus de France Travail :\n" + context #return "Fiche mĂ©tier CompĂ©tences Savoirs :\n" + ficheMetiersCompetencesSavoirs + "\nMetier secteur contexte au travail :\n" + metierSecteurContexteTravail + "\nListe des emplois issus de France Travail :\n" + context #return "Liste des emplois issus de France Travail :\n" + context #return "\nMetier secteur contexte au travail :\n" + metierSecteurContexteTravail + "\nListe des emplois issus de France Travail :\n" + context @cl.step(type="tool") async def FicheMetier(url, codes): if codes.find(',') != -1: all = "" codeArray = codes.split(',') for i in range(0,len(codeArray)): response = requests.get(url + codeArray[i]) soup = BeautifulSoup(response.text, "html.parser") allmissions = soup.select('div.fm-presentation-text') allcompetences = soup.select('div#part2') allcontextes = soup.select('div#part3') all = all + "Fiche MĂ©tier " + codeArray[i] + ":\nLes missions principales : " + removeTags(allmissions[0]) + ". Les compĂ©tences recherchĂ©es : " + removeTags(allcompetences[0]) + ". Les contextes au travail : " + removeTags(allcontextes[0]) + "." else: response = requests.get(url + codes) soup = BeautifulSoup(response.text, "html.parser") allmissions = soup.select('div.fm-presentation-text') allcompetences = soup.select('div#part2') allcontextes = soup.select('div#part3') all = "Fiche MĂ©tier " + codes + ":\nLes missions principales : " + removeTags(allmissions[0]) + ". Les compĂ©tences recherchĂ©es : " + removeTags(allcompetences[0]) + ". Les contextes au travail : " + removeTags(allcontextes[0]) + "." return all @cl.step(type="tool") async def ChiffresClesMetier(url, codes): if codes.find(',') != -1: all = "" codeArray = codes.split(',') for i in range(0,len(codeArray)): response = requests.get(url + codeArray[i]) soup = BeautifulSoup(response.text, "html.parser") if soup.select('h1#titreMetier'): alltitre = soup.select('h1#titreMetier') allTitre = removeTags(alltitre[0]) else: allTitre = "" if soup.select('div.jobs_item-container-flex'): allembauches = soup.select('div.jobs_item-container-flex') allEmbauches = removeTags(allembauches[0]) else: allEmbauches = "" if soup.select('div.key-number_block.shadow.inset'): allsalaires = soup.select('div.key-number_block.shadow.inset') allSalaires = removeTags(allsalaires[0]) else: allSalaires = "" if soup.select('tbody.sectorTable__body'): allsalairesMedian = soup.select('tbody.sectorTable__body') allSalairesMedian = removeTags(allsalairesMedian[0]) else: allSalairesMedian = "" if soup.select('div.dynamism_canvas-wrapper > p.sr-only'): allDiff = soup.select('div.dynamism_canvas-wrapper > p.sr-only') alldiff = removeTags(allDiff[0]) else: alldiff = "" if soup.select('div.tabs-main-data_persp-col2'): allDiffOrigin = soup.select('div.tabs-main-data_persp-col2') alldiffOrigin = removeTags(allDiffOrigin[0]) else: alldiffOrigin = "" allTypeContrat = "" if soup.find_all("div", class_="hiring-contract_legende_item ng-star-inserted"): allContrat = soup.find_all("div", class_="hiring-contract_legende_item ng-star-inserted") for j in range(0,len(allContrat)): allTypeContrat = allTypeContrat + removeTags(allContrat[j]) + ", " if soup.find_all("div", class_="horizontal-graph_patterns"): allEntreprise = soup.find_all("div", class_="horizontal-graph_patterns") allentreprise = removeTags(allEntreprise[0]) else: allentreprise = "" all = all + "\n\nChiffres-clĂ©s MĂ©tier " + allTitre + ":\nDemandeurs d'emploi et Offres d'emploi : " + allEmbauches + ". Salaires proposĂ©s dans les offres : " + allSalaires + ". Salaires mĂ©dians constatĂ©s : " + allSalairesMedian + ". DifficultĂ©s de recrutement pour les entreprises : " + alldiff + ". Origine des difficultĂ©s : " + alldiffOrigin + ". RĂ©partition des embauches par type de contrat : " + allTypeContrat + ". RĂ©partition des embauches par taille d'entreprise : " + allentreprise + "." else: response = requests.get(url + codes) soup = BeautifulSoup(response.text, "html.parser") if soup.select('h1#titreMetier'): alltitre = soup.select('h1#titreMetier') allTitre = removeTags(alltitre[0]) else: allTitre = "" if soup.select('div.jobs_item-container-flex'): allembauches = soup.select('div.jobs_item-container-flex') allEmbauches = removeTags(allembauches[0]) else: allEmbauches = "" if soup.select('div.key-number_block.shadow.inset'): allsalaires = soup.select('div.key-number_block.shadow.inset') allSalaires = removeTags(allsalaires[0]) else: allSalaires = "" if soup.select('tbody.sectorTable__body'): allsalairesMedian = soup.select('tbody.sectorTable__body') allSalairesMedian = removeTags(allsalairesMedian[0]) else: allSalairesMedian = "" if soup.select('div.dynamism_canvas-wrapper > p.sr-only'): allDiff = soup.select('div.dynamism_canvas-wrapper > p.sr-only') alldiff = removeTags(allDiff[0]) else: alldiff = "" if soup.select('div.tabs-main-data_persp-col2'): allDiffOrigin = soup.select('div.tabs-main-data_persp-col2') alldiffOrigin = removeTags(allDiffOrigin[0]) else: alldiffOrigin = "" allTypeContrat = "" if soup.find_all("div", class_="hiring-contract_legende_item ng-star-inserted"): allContrat = soup.find_all("div", class_="hiring-contract_legende_item ng-star-inserted") for j in range(0,len(allContrat)): allTypeContrat = allTypeContrat + removeTags(allContrat[j]) + ", " if soup.find_all("div", class_="horizontal-graph_patterns"): allEntreprise = soup.find_all("div", class_="horizontal-graph_patterns") allentreprise = removeTags(allEntreprise[0]) else: allentreprise = "" all = "\n\nChiffres-clĂ©s MĂ©tier " + allTitre + ":\nDemandeurs d'emploi et Offres d'emploi : " + allEmbauches + ". Salaires proposĂ©s dans les offres : " + allSalaires + ". Salaires mĂ©dians constatĂ©s : " + allSalairesMedian + ". DifficultĂ©s de recrutement pour les entreprises : " + alldiff + ". Origine des difficultĂ©s : " + alldiffOrigin + ". RĂ©partition des embauches par type de contrat : " + allTypeContrat + ". RĂ©partition des embauches par taille d'entreprise : " + allentreprise + "." return all @cl.step(type="tool") async def creation_liste_code_Rome_et_emplois(competence): os.environ['PINECONE_API_KEYROME'] = os.environ['PINECONE_API_KEYROME'] docsearch = await connexion_catalogue_Rome() retrieve_comp = docsearch.similarity_search(competence, k=30, filter={"categorie": {"$eq": os.environ['PINECONE_API_KEYROME']}}) retrieve = pd.DataFrame(retrieve_comp) codeRome = [] competence = [] metier = [] for i in range(0,len(retrieve_comp)): codeRome.append(retrieve_comp[i].metadata['code_rome']) competence.append(retrieve_comp[i].metadata['libelle_competence']) metier.append(retrieve_comp[i].metadata['libelle_appellation_long']) results_df = pd.DataFrame({'codeRome': codeRome,'competence': competence, 'metier': metier}) arrayresults = results_df.values.tolist() displayresults = '| Code Rome | CompĂ©tence | MĂ©tier |\n| -------- | ------- | ------- |' for j in range(0, len(arrayresults)): displayresults += '\n| ' + arrayresults[j][0] + ' | ' + arrayresults[j][1] + ' | ' + arrayresults[j][2] + ' |' print(arrayresults[0][0] + arrayresults[0][1] + arrayresults[0][2]) await cl.Message(author="Datapcc : 🌐🌐🌐",content="Voici le rĂ©sultat de la recherche sĂ©mantique sur le catalogue Rome :\n" + displayresults).send() results_df = results_df.drop_duplicates(subset=["codeRome"]) results_df = results_df.head(5) codeRomeString = results_df["codeRome"].to_string(index = False) codeRome_list = results_df["codeRome"].tolist() actionRome = await cl.AskActionMessage( content="Etes-vous d'accord avec la sĂ©lection des 5 codes Rome automatiques issus de la recherche sĂ©mantique ? :" + codeRomeString, actions=[ cl.Action(name="continue", value="continue", label="✅ Oui, je veux continuer vers l'extraction en temps rĂ©el des offres d'emploi"), cl.Action(name="cancel", value="cancel", label="❌ Non, je veux saisir ma liste de codes Rome, sĂ©parĂ©s par des virgules"), ], ).send() if actionRome and actionRome.get("value") == "continue": await cl.Message( content="Connexion Ă  France Travail, et rĂ©cupĂ©ration des offres d'emploi", ).send() df_emplois = await API_France_Travail(codeRome_list) cl.user_session.set("codeRomeArray", codeRome_list) else: actionsaisierome = await cl.AskUserMessage(content="Saisissez vos codes Rome dans le prompt? ⚠ Attention, indiquez seulement des codes Rome sĂ©parĂ©s par des virgules", timeout=3600).send() if actionsaisierome: await cl.Message( content=f"Votre saisie est : {actionsaisierome['output']}", ).send() stringCodeRome = actionsaisierome['output'].replace(' ','') stopWords = [';','.',':','!','|'] teststringCodeRome = [ele for ele in stopWords if(ele in stringCodeRome)] teststringCodeRome = bool(teststringCodeRome) if teststringCodeRome == False: arrayCodeRome = stringCodeRome.spit(',') else: arrayCodeRome = codeRome_list await cl.Message(author="Datapcc : 🌐🌐🌐",content="Votre ssaisie est erronĂ©e. Nous continuons l'action avec les codes Rome sĂ©lectionnĂ©s automatiquement pour vous : " + codeRome_list).send() df_emplois = await API_France_Travail(arrayCodeRome) cl.user_session.set("codeRomeArray", arrayCodeRome) return df_emplois @cl.step(type="tool") async def connexion_catalogue_Rome(): os.environ['PINECONE_API_KEY'] = os.environ['PINECONE_API_KEY'] os.environ['PINECONE_INDEX_NAME'] = os.environ['PINECONE_INDEX_NAME'] embeddings = HuggingFaceEmbeddings() docsearch = PineconeVectorStore.from_existing_index(os.environ['PINECONE_INDEX_NAME'], embeddings) return docsearch @cl.step(type="tool") async def API_France_Travail(romeListArray): client = Api(client_id=os.environ['POLE_EMPLOI_CLIENT_ID'], client_secret=os.environ['POLE_EMPLOI_CLIENT_SECRET']) todayDate = datetime.datetime.today() month, year = (todayDate.month-1, todayDate.year) if todayDate.month != 1 else (12, todayDate.year-1) start_dt = todayDate.replace(day=1, month=month, year=year) end_dt = datetime.datetime.today() results = [] for k in romeListArray: params = {"motsCles": k,'minCreationDate': dt_to_str_iso(start_dt),'maxCreationDate': dt_to_str_iso(end_dt),'range':'0-149'} search_on_big_data = client.search(params=params) results += search_on_big_data["resultats"] results_df = pd.DataFrame(results) return results_df @cl.step(type="llm") async def IA(): os.environ['HUGGINGFACEHUB_API_TOKEN'] = os.environ['HUGGINGFACEHUB_API_TOKEN'] #repo_id = "mistralai/Mixtral-8x7B-Instruct-v0.1" repo_id = "mistralai/Mistral-7B-Instruct-v0.3" llm = HuggingFaceEndpoint( repo_id=repo_id, max_new_tokens=5000, temperature=1.0, task="text2text-generation", streaming=True ) return llm @cl.on_settings_update async def setup_agent(settings): if not settings['competence'] and not settings['competenceInput']: await cl.Message( author="Datapcc : 🌐🌐🌐",content=f"⚠ Pas de contexte : {settings['competence']}\n⛔ Vous ne pouvez pas Ă©laborer de note sectorielle!" ).send() elif settings['competence'] and not settings['competenceInput']: await cl.Message( author="Datapcc : 🌐🌐🌐",content=f"👍 Changement de contexte : {settings['competence']}" ).send() competenceList = settings['competence'] cl.user_session.set("competenceFree", competenceList) else: await cl.Message( author="Datapcc : 🌐🌐🌐",content=f"👍 Changement de contexte : {settings['competenceInput']}" ).send() competenceList = settings['competenceInput'] cl.user_session.set("competenceFree", competenceList) if not cl.user_session.get("saveMemory"): cl.user_session.set("saveMemory", "") await construction_NCS(competenceList) contextChat = cl.user_session.get("contextChatBot") if not contextChat: contextChat = "Il n'y a pas de contexte." os.environ['HUGGINGFACEHUB_API_TOKEN'] = os.environ['HUGGINGFACEHUB_API_TOKEN'] repo_id = "mistralai/Mistral-7B-Instruct-v0.3" model = HuggingFaceEndpoint( repo_id=repo_id, max_new_tokens=6000, temperature=0.5, streaming=True ) memory = cl.user_session.get("memory") prompt = ChatPromptTemplate.from_messages( [ ( "system", f"Contexte : Vous ĂȘtes un spĂ©cialiste du marchĂ© de l'emploi en fonction du niveau de qualification, des compĂ©tences professionnelles, des compĂ©tences transversales, du salaire et de l'expĂ©rience. Vous ĂȘtes douĂ© pour faire des analyses du systĂšme travail sur les mĂ©tiers les plus demandĂ©s grĂące Ă  votre aptitude Ă  synthĂ©tiser les informations en fonction des critĂšres dĂ©finis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement. Contexte : {contextChat[0:28875]}. RĂ©ponds Ă  la question suivante de la maniĂšre la plus pertinente, la plus exhaustive et la plus dĂ©taillĂ©e possible, avec au minimum 3000 tokens jusqu'Ă  3600 tokens, seulement et strictement dans le contexte et les informations fournies. Essayez donc de comprendre en profondeur le contexte et rĂ©pondez uniquement en vous basant sur les informations fournies.", ), MessagesPlaceholder(variable_name="history"), ("human", "{question}, dans le contexte fourni."), ] ) runnable = ( RunnablePassthrough.assign( history=RunnableLambda(memory.load_memory_variables) | itemgetter("history") ) | prompt | model ) cl.user_session.set("runnable", runnable) @cl.on_message async def main(message: cl.Message): async with cl.Step(root=True, name="RĂ©ponse de Mistral", type="llm") as parent_step: parent_step.input = message.content chat_profile = cl.user_session.get("chat_profile") chatProfile = chat_profile.split(' - ') memory = cl.user_session.get("memory") runnable = cl.user_session.get("runnable") # type: Runnable msg = cl.Message(author="Datapcc : 🌐🌐🌐",content="") text_elements = [] answer = [] async for chunk in runnable.astream({"question": message.content}, config=RunnableConfig(callbacks=[cl.AsyncLangchainCallbackHandler(stream_final_answer=True)])): await parent_step.stream_token(chunk) await msg.stream_token(chunk) QA_context_name = f"Question-rĂ©ponse sur le contexte" text_elements.append( cl.Text(content="Question : " + message.content + "\n\nRĂ©ponse :\n" + msg.content, name=QA_context_name) ) actions = [ cl.Action(name="download", value="Question : " + message.content + "\n\nRĂ©ponse : " + msg.content, description="download_QA_emplois") ] await cl.Message(author="Datapcc : 🌐🌐🌐",content="Download", actions=actions).send() await cl.sleep(2) saves = [ cl.Action(name="saveToMemory", value="Question : " + message.content + "\n\nRĂ©ponse : " + msg.content, description="Mettre en mĂ©moire la rĂ©ponse Ă  votre requĂȘte") ] await cl.Message(author="Datapcc : 🌐🌐🌐",content="Mettre en mĂ©moire la rĂ©ponse Ă  votre requĂȘte", actions=saves).send() await cl.sleep(2) memories = [ cl.Action(name="download", value=cl.user_session.get('saveMemory'), description="download_referentiel") ] await cl.Message(author="Datapcc : 🌐🌐🌐",content="TĂ©lĂ©charger la mise en mĂ©moire de vos fiches", actions=memories).send() await cl.sleep(1.5) await cl.Message(author="Datapcc : 🌐🌐🌐",content="Contexte : " + QA_context_name, elements=text_elements).send() memory.chat_memory.add_user_message(message.content) memory.chat_memory.add_ai_message(msg.content)