Spaces:
Sleeping
Sleeping
| import streamlit as st # type: ignore | |
| import os | |
| from datetime import datetime | |
| from extra_streamlit_components import tab_bar, TabBarItemData | |
| import io | |
| from gtts import gTTS | |
| import soundfile as sf | |
| import wavio | |
| from audio_recorder_streamlit import audio_recorder | |
| import speech_recognition as sr | |
| import whisper | |
| import numpy as np | |
| from translate_app import tr | |
| import getpass | |
| from langchain_mistralai import ChatMistralAI | |
| from langchain_openai import ChatOpenAI | |
| from langgraph.checkpoint.memory import MemorySaver | |
| from langgraph.graph import START, END, MessagesState, StateGraph | |
| from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder | |
| from typing import Sequence | |
| from langchain_core.messages import BaseMessage, SystemMessage, HumanMessage, AIMessage, trim_messages | |
| from langgraph.graph.message import add_messages | |
| from typing_extensions import Annotated, TypedDict | |
| from dotenv import load_dotenv | |
| import time | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| title = "Sales coaching" | |
| sidebar_name = "Sales coaching" | |
| dataPath = st.session_state.DataPath | |
| os.environ["LANGCHAIN_TRACING_V2"] = "true" | |
| os.environ["LANGCHAIN_ENDPOINT"]="https://api.smith.langchain.com" | |
| os.environ["LANGCHAIN_HUB_API_URL"]="https://api.smith.langchain.com" | |
| os.environ["LANGCHAIN_PROJECT"] = "Sales Coaching Chatbot" | |
| if st.session_state.Cloud != 0: | |
| load_dotenv() | |
| os.getenv("LANGCHAIN_API_KEY") | |
| os.getenv("MISTRAL_API_KEY") | |
| os.getenv("OPENAI_API_KEY") | |
| prompt = ChatPromptTemplate.from_messages( | |
| [ | |
| ( | |
| "system", | |
| "Répond à toutes les questions du mieux possible dans la langue {language}, même si la question est posée dans une autre langue", | |
| ), | |
| MessagesPlaceholder(variable_name="messages"), | |
| ] | |
| ) | |
| class State(TypedDict): | |
| messages: Annotated[Sequence[BaseMessage], add_messages] | |
| language: str | |
| def call_model(state: State): | |
| chain = prompt | model | |
| response = chain.invoke(state) | |
| return {"messages": [response]} | |
| # Define a new graph | |
| workflow = StateGraph(state_schema=State) | |
| # Define the (single) node in the graph | |
| workflow.add_edge(START, "model") | |
| workflow.add_node("model", call_model) | |
| workflow.add_edge("model", END) | |
| # Add memory | |
| memory = MemorySaver() | |
| app = workflow.compile(checkpointer=memory) | |
| selected_index1 = 0 | |
| selected_index2 = 0 | |
| selected_index3 = 0 | |
| selected_options4 = [] | |
| selected_options5 = [] | |
| selected_options6 = [] | |
| selected_options7 = [] | |
| selected_index8 = 0 | |
| virulence = 1 | |
| # @st.cache_data | |
| def init(): | |
| global config,thread_id, context,human_message1,ai_message1,language, app, model_speech,prompt,model | |
| global selected_index1, selected_index2, selected_index3, selected_options4,selected_options5,selected_options6,selected_options7, selected_index8, virulence | |
| model_speech = whisper.load_model("base") | |
| if st.button(label=tr("Nouvelle conversation"), type="primary"): | |
| selected_index1 = 0 | |
| selected_index2 = 0 | |
| selected_index3 = 0 | |
| selected_options4 = [] | |
| selected_options5 = [] | |
| selected_options6 = [] | |
| selected_options7 = [] | |
| selected_index8 = 0 | |
| virulence = 1 | |
| options1 = ["Directeur Commercial", "Directeur Général", "Directeur Marketing"] | |
| translated_options1 = [tr(o) for o in options1] | |
| selected_option1 = st.selectbox(tr("Interlocuteur"),translated_options1, index = selected_index1) # index=int(var1_init)) | |
| selected_index1 = translated_options1.index(selected_option1) | |
| options2 = ["Entreprise qui commercialise des solutions (produits et ou services) B2B innovantes avec une équipe commerciale de plus de 15 personnes", | |
| "Entreprise qui commercialise des solutions (produits et ou services) B2B innovantes avec une équipe commerciale de plus de 100 personnes"] | |
| translated_options2 = [tr(o) for o in options2] | |
| selected_option2 = st.selectbox(tr("Activité"),translated_options2, index = selected_index2) # index=int(var2_init)) | |
| selected_index2 = translated_options2.index(selected_option2) | |
| options3 = ["Logiciels informatiques et d’application SaaS", | |
| "Équipements et solutions industrielles", | |
| "Services et conseil spécialisés"] | |
| translated_options3 = [tr(o) for o in options3] | |
| selected_option3 = st.selectbox(tr("Domaine d'activité"),translated_options3, index=selected_index3) #index=int(var3_init)) | |
| selected_index3 = translated_options3.index(selected_option3) | |
| context = tr(f"""Tu es un {options1[selected_index1]}, mal organisé, d'une {options2[selected_index2]}. | |
| Cette entreprise propose des {options3[selected_index3]}. | |
| """) | |
| context = st.text_area(label=tr("Résumé du Contexte (modifiable):"), value=context) | |
| st.markdown(''' | |
| ------------------------------------------------------------------------------------ | |
| ''') | |
| options4 = ["Il est difficile pour les forces de vente d'articuler clairement les messages de la proposition de valeur", | |
| "Il est difficile d’affiner une proposition de valeur unique et pertinente de l'offre face à la concurrence qui évolue rapidement", | |
| "Il est chronophage de former les forces de ventes sur la proposition de valeur et ses évolutions" | |
| ] | |
| selected_options4 = st.multiselect(tr("Problématiques"),[tr(o) for o in options4], default=[tr(o) for o in selected_options4]) | |
| problematique = selected_options4 | |
| markdown_text4 = tr(""" | |
| Les problématiques rencontrés par notre prospect (problèmes à résoudre) sont:""") | |
| markdown_text4 = markdown_text4+"".join(f"\n- {o}" for o in problematique) | |
| st.write(markdown_text4) | |
| options5 = ["Former la force de ventes sur l'articulation de la proposition de valeur", | |
| "Aligner les messages marketing et commerciaux", | |
| "Affiner et modéliser la proposition de valeur", | |
| "Mettre en oeuvre des meilleures pratiques commerciales" | |
| ] | |
| selected_options5 = st.multiselect(tr("Processus"),[tr(o) for o in options5],default=[tr(o) for o in selected_options5]) | |
| processus = selected_options5 | |
| markdown_text5 = tr(""" | |
| \nLes processus adressés par le prospect (cas d’usages) sont:""") | |
| markdown_text5 = markdown_text5+"".join(f"\n- {o}" for o in processus) | |
| st.write(markdown_text5) | |
| options6 = ["Augmenter les performances commerciales", | |
| "Croissance du chiffre d’affaires", | |
| "Réduire les cycles de vente", | |
| "Augmenter taux de conversion d’affaires gagnées", | |
| "Améliorer l’efficience et la confiance des forces de ventes", | |
| "Réduire temps de monté en compétence des nouvelles embauches", | |
| "Fidéliser les clients" | |
| ] | |
| selected_options6 = st.multiselect(tr("Objectifs d'amélioration"),[tr(o) for o in options6],default=[tr(o) for o in selected_options6]) | |
| objectifs = selected_options6 | |
| markdown_text6 = tr(""" | |
| \nLes objectifs d’amélioration opérationnelle du prospect (Valeur ajoutée) sont:""") | |
| markdown_text6 = markdown_text6+"".join(f"\n- {o}" for o in objectifs) | |
| st.write(markdown_text6) | |
| options7 = ["Gestion de contenu commercial avec logiciel Microsoft sharePoint ou GoogleDrive", | |
| "Playbook développé en interne sur outils génériques tels que logiciel Notion, Powerpoint, Excel, Word, Docs", | |
| "Outils de sales enablement tels que application Seismic", | |
| "Outils de gestion des présentations clients tels que Logiciel Powerpoint ou Google slide", | |
| "Conseil externe en positionnement marché & produit", | |
| "Services externes de formation des équipes commerciales" | |
| ] | |
| selected_options7 = st.multiselect(tr("Solutions utilisées"),[tr(o) for o in options7],default=[tr(o) for o in selected_options7]) | |
| solutions_utilisees = selected_options7 | |
| markdown_text7 = tr(""" | |
| \nLes principales Solutions utilisées par le prospect pour traiter les cas d’usages (Catégories de solutions du marché) sont:""") | |
| markdown_text7 = markdown_text7+"".join(f"\n- {o}" for o in solutions_utilisees) | |
| st.write(markdown_text7) | |
| st.write("") | |
| options8 = ["Connaitre les freins du prospect à l'adoption de nouvelles solutions pour résoudre ses problèmes", | |
| "Obtenir du prospect qu'il teste la solution que je propose ou la fasse tester par un membre de son équipe", | |
| "Obtenir du prospect qu'il achète ou s'engage à acheter la solution que je propose"] | |
| translated_options8 = [tr(o) for o in options8] | |
| selected_option8 = st.selectbox(tr("Objectif du vendeur lors de sa conversation avec le prospect:"),translated_options8, index = selected_index8) | |
| selected_index8 = translated_options8.index(selected_option8) | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| virulence = st.slider(tr("Virulence (choisissez une valeur entre 1 et 5)"), min_value=1, max_value=5, step=1,value=virulence) | |
| markdown_text8 = tr(f"""\nLe prospect est très occupé et n'aime être dérangé inutilement. | |
| Tu vas utiliser une échelle de 1 à 5 de virulence du prospect à l'égard du vendeur. | |
| Pour cette simulation utilise le niveaux {virulence}""") | |
| human_message1 = tr("""Je souhaites que nous ayons une conversation verbale entre un commercial de mon entreprise, et toi que je prospecte. | |
| Mon entreprise propose une solution logicielle pour gérer la proposition de valeur d’entreprises B2B qui commercialises des solutions technologiques. | |
| """)+markdown_text4+markdown_text5+markdown_text6+markdown_text7+tr(""" | |
| Je suis le vendeur. | |
| Répond à mes questions en tant que Directeur commercial désorganisé, connaissant mal le concept de proposition de valeur, | |
| et mon équipe de vente n'est pas performante. | |
| Attention: Ce n'est pas toi qui m'aide, c'est moi qui t'aide avec ma solution. | |
| """)+markdown_text8 | |
| human_message1 = st.text_area(label=tr("Consigne"), value=tr(human_message1),height=300) | |
| st.markdown(''' | |
| ------------------------------------------------------------------------------------ | |
| ''') | |
| ai_message1 = tr(f"J'ai bien compris, je suis un {options1[selected_index1]} prospecté et je réponds seulement à tes questions. Je réponds à une seule question à la fois, sans commencer mes réponses par 'En tant que {options1[selected_index1]}'") | |
| # ai_message1 = st.text_area(label=tr("Réponse du prospect"), value=ai_message1) | |
| messages = [ | |
| SystemMessage(content=context), | |
| HumanMessage(content=human_message1), | |
| AIMessage(content=ai_message1), | |
| HumanMessage(content="") | |
| ] | |
| st.write("") | |
| if ("context" in st.session_state) and ("human_message1" in st.session_state): | |
| if (st.session_state.context != context) or (st.session_state.human_message1 != human_message1 ): | |
| to_init = True | |
| else: | |
| to_init = False | |
| else: | |
| to_init = True | |
| if to_init: | |
| thread_id = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| config = {"configurable": {"thread_id": thread_id}} | |
| app.invoke( | |
| {"messages": messages, "language": language}, | |
| config, | |
| ) | |
| st.session_state.thread_id = thread_id | |
| st.session_state.config = config | |
| st.session_state.messages_init = messages | |
| st.session_state.context = context | |
| st.session_state.human_message1 = human_message1 | |
| st.session_state.messages = [] | |
| return config, thread_id | |
| # Fonction pour générer et jouer le texte en speech | |
| def play_audio(custom_sentence, Lang_target, speed=1.0): | |
| # Générer le speech avec gTTS | |
| audio_stream_bytesio_src = io.BytesIO() | |
| tts = gTTS(custom_sentence, lang=Lang_target) | |
| # Revenir au début du flux audio | |
| audio_stream_bytesio_src.seek(0) | |
| audio_stream_bytesio_src.truncate(0) | |
| tts.write_to_fp(audio_stream_bytesio_src) | |
| audio_stream_bytesio_src.seek(0) | |
| # Charger l'audio dans un tableau numpy | |
| data, samplerate = sf.read(audio_stream_bytesio_src) | |
| # Modifier la vitesse de lecture en ajustant le taux d'échantillonnage | |
| new_samplerate = int(samplerate * speed) | |
| new_audio_stream_bytesio = io.BytesIO() | |
| # Enregistrer l'audio avec la nouvelle fréquence d'échantillonnage | |
| sf.write(new_audio_stream_bytesio, data, new_samplerate, format='wav') | |
| new_audio_stream_bytesio.seek(0) | |
| # Lire l'audio dans Streamlit | |
| # time.sleep(2) | |
| st.audio(new_audio_stream_bytesio, start_time=0, autoplay=True) | |
| def run(): | |
| global thread_id, config, model_speech, language,prompt,model | |
| st.write("") | |
| st.write("") | |
| st.title(tr(title)) | |
| if 'language_label' in st.session_state: | |
| language = st.session_state['language_label'] | |
| else: language = "French" | |
| chosen_id = tab_bar(data=[ | |
| TabBarItemData(id="tab1", title=tr("Initialisation"), description=tr("d'une nouvelle conversation")), | |
| TabBarItemData(id="tab2", title=tr("Conversation"), description=tr("avec le prospect")), | |
| TabBarItemData(id="tab3", title=tr("Evaluation"), description=tr("de l'acte de vente"))], | |
| default="tab1") | |
| if (chosen_id == "tab1"): | |
| if 'model' in st.session_state and (st.session_state.model[:3]=="gpt") and ("OPENAI_API_KEY" in st.session_state): | |
| model = ChatOpenAI(model=st.session_state.model, | |
| temperature=0.8, # Adjust creativity level | |
| max_tokens=150 # Define max output token limit | |
| ) | |
| else: | |
| st.session_state.model = "mistral-large-latest" | |
| model = ChatMistralAI(model=st.session_state.model) | |
| config,thread_id = init() | |
| query = "" | |
| st.button(label=tr("Validez"), type="primary") | |
| st.write("**thread_id:** "+thread_id) | |
| elif (chosen_id == "tab2"): | |
| try: | |
| config | |
| # On ne fait rien | |
| except NameError: | |
| config,thread_id = init() | |
| with st.container(): | |
| # Diviser l'écran en deux colonnes | |
| col1, col2 = st.columns(2) | |
| # with col1: | |
| # st.markdown( | |
| # """ | |
| # <div style="height: 400px;"> | |
| # </div> | |
| # """, | |
| # unsafe_allow_html=True, | |
| # ) | |
| with col1: | |
| st.write("**thread_id:** "+thread_id) | |
| query = "" | |
| audio_bytes = audio_recorder (pause_threshold=2.0, sample_rate=16000, auto_start=False, text=tr("Cliquez pour parler, puis attendre 2sec."), \ | |
| recording_color="#e8b62c", neutral_color="#1ec3bc", icon_size="6x",) | |
| if audio_bytes: | |
| # st.write("**"+tr("Vendeur")+" :**\n") | |
| # Fonction pour générer et jouer le texte en speech | |
| st.audio(audio_bytes, format="audio/wav", autoplay=False) | |
| try: | |
| detection = False | |
| if detection: | |
| # Create a BytesIO object from the audio stream | |
| audio_stream_bytesio = io.BytesIO(audio_bytes) | |
| # Read the WAV stream using wavio | |
| wav = wavio.read(audio_stream_bytesio) | |
| # Extract the audio data from the wavio.Wav object | |
| audio_data = wav.data | |
| # Convert the audio data to a NumPy array | |
| audio_input = np.array(audio_data, dtype=np.float32) | |
| audio_input = np.mean(audio_input, axis=1)/32768 | |
| result = model_speech.transcribe(audio_input) | |
| Lang_detected = result["language"] | |
| query = result["text"] | |
| else: | |
| # Avec l'aide de la bibliothèque speech_recognition de Google | |
| Lang_detected = st.session_state['Language'] | |
| # Transcription google | |
| audio_stream = sr.AudioData(audio_bytes, 32000, 2) | |
| r = sr.Recognizer() | |
| query = r.recognize_google(audio_stream, language = Lang_detected) | |
| # Transcription | |
| # st.write("**"+tr("Vendeur :")+"** "+query) | |
| with st.chat_message("user"): | |
| st.markdown(query) | |
| st.write("") | |
| if query != "": | |
| input_messages = [HumanMessage(query)] | |
| output = app.invoke( | |
| {"messages": input_messages, "language": language}, | |
| config, | |
| ) | |
| #with st.chat_message("user"): | |
| # Add user message to chat history | |
| st.session_state.messages.append({"role": "user", "content": query}) | |
| # Récupération de la réponse | |
| custom_sentence = output["messages"][-1].content | |
| # Joue l'audio | |
| play_audio(custom_sentence,Lang_detected , 1) | |
| # st.write("**"+tr("Prospect :")+"** "+custom_sentence) | |
| with st.chat_message("assistant"): | |
| st.markdown(custom_sentence) | |
| # Add user message to chat history | |
| st.session_state.messages.append({"role": "assistant", "content": custom_sentence}) | |
| except KeyboardInterrupt: | |
| st.write(tr("Arrêt de la reconnaissance vocale.")) | |
| except: | |
| st.write(tr("Problème, essayer de nouveau..")) | |
| st.write("") | |
| # Ajouter un espace pour séparer les zones | |
| # st.divider() | |
| with col2: | |
| if ("messages" in st.session_state) : | |
| if (st.session_state.messages != []): | |
| # Display chat messages from history on app rerun | |
| for message in st.session_state.messages: | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| else: | |
| st.write("") | |
| st.write("") | |
| st.write("**thread_id:** "+thread_id) | |
| st.write("") | |
| q1 = st.text_input(label="", value=tr("Combien le vendeur a-t-il posé de questions ouvertes ?"),label_visibility="collapsed") | |
| output = app.invoke( | |
| {"messages": q1,"language": language}, | |
| config, | |
| ) | |
| custom_sentence = output["messages"][-1].content | |
| st.write(custom_sentence) | |
| st.write("") | |
| st.divider() | |
| st.write("") | |
| q2 = st.text_input(label="", value=tr(f"Quel est le % de temps de parole du prospect ?"),label_visibility="collapsed") | |
| output = app.invoke( | |
| {"messages": q2,"language": language}, | |
| config, | |
| ) | |
| custom_sentence = output["messages"][-1].content | |
| st.write(custom_sentence) | |
| st.write("") | |
| st.divider() | |
| st.write("") | |
| q3 = st.text_input(label="", value=tr("Peux tu me donner une analyse succinte de la tonalité du vendeur ?"),label_visibility="collapsed") | |
| output = app.invoke( | |
| {"messages": q3,"language": language}, | |
| config, | |
| ) | |
| custom_sentence = output["messages"][-1].content | |
| st.write(custom_sentence) | |
| st.write("") | |
| st.divider() | |
| st.write("") | |
| q3 = st.text_input(label="", value=tr("Le vendeur a-t-il atteint son objectif ? Si ce n'est pas cas, est il loin de l'avoir atteint ? Dans tous les cas, explique ta réponse."),label_visibility="collapsed") | |
| output = app.invoke( | |
| {"messages": q3,"language": language}, | |
| config, | |
| ) | |
| custom_sentence = output["messages"][-1].content | |
| st.write(custom_sentence) | |
| st.write("") |