File size: 9,118 Bytes
5237fdf
c494a1f
 
 
 
 
 
 
 
 
 
 
 
 
f498ba1
c494a1f
 
 
f498ba1
c494a1f
f498ba1
2b78dcf
228a20c
f498ba1
2b78dcf
f498ba1
c494a1f
 
f498ba1
c494a1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f498ba1
c494a1f
 
 
 
 
f498ba1
c494a1f
 
f498ba1
c494a1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f498ba1
 
 
 
e39c339
f498ba1
e39c339
f498ba1
 
 
 
 
 
 
 
c494a1f
 
 
e39c339
 
 
c494a1f
e39c339
f498ba1
 
 
c494a1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f498ba1
c494a1f
 
 
 
 
f498ba1
c494a1f
 
 
 
 
 
 
f498ba1
c494a1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f498ba1
c494a1f
f498ba1
c494a1f
f498ba1
c494a1f
 
 
f498ba1
 
c494a1f
 
 
f498ba1
c494a1f
 
 
 
f498ba1
 
 
 
 
 
 
c494a1f
 
f498ba1
c494a1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
import streamlit as st
import requests
import re
import tempfile
import os
from dotenv import load_dotenv

# Nouveaux imports pour Groq et LangSmith
from groq import Groq
from langsmith import traceable

# Charger les variables d'environnement
load_dotenv()

# --- INTERFACE STREAMLIT ---
st.set_page_config(page_title="nlp", layout="wide")
st.title("SN NATURAL LANGUAGE PROCESSING")

st.subheader('Étudiant MASTER II: TATSA TCHINDA Colince')

# Sidebar logo
try:
    image_path = 'src/Keyce.jpg'
    st.sidebar.image(image_path, caption="Keyce informatique et intelligence artificielle", use_container_width=True)
except FileNotFoundError:
    st.sidebar.warning("Image 'keyce.jpg' non trouvée. Assurez-vous qu'elle est dans le même répertoire que le script.")


# --- GESTION DES CLÉS API (depuis .env) ---
st.session_state["API_TOKEN_HF"] = os.getenv("HUGGINGFACE_API_KEY")
st.session_state["API_TOKEN_GROQ"] = os.getenv("GROQ_API_KEY")
api_token_langsmith = os.getenv("LANGCHAIN_API_KEY")

# Configuration de LangSmith si la clé est fournie
if api_token_langsmith:
    st.session_state["LANGSMITH_CONFIGURED"] = True
    os.environ["LANGCHAIN_TRACING_V2"] = "true"
    os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
    os.environ["LANGCHAIN_API_KEY"] = api_token_langsmith
    os.environ["LANGCHAIN_PROJECT"] = "Mon App Streamlit NLP" # Nom du projet dans LangSmith
    st.sidebar.info("Tracing LangSmith activé.")
else:
    st.session_state["LANGSMITH_CONFIGURED"] = False
    # Désactiver le tracing si aucune clé n'est fournie
    if "LANGCHAIN_TRACING_V2" in os.environ:
        del os.environ["LANGCHAIN_TRACING_V2"]

# --- INITIALISATION DES CLIENTS ---
headers = {}
if st.session_state.get("API_TOKEN_HF"):
    headers = {"Authorization": f"Bearer {st.session_state['API_TOKEN_HF']}"}


# --- FONCTIONS UTILES ---

def nettoyer_reponse(text):
    # La fonction de nettoyage n'est plus nécessaire pour Groq, mais on la garde au cas où.
    cleaned = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL)
    return cleaned.strip()

@traceable(name="Groq Llama3 Generator") # Le décorateur @traceable active le suivi LangSmith
def generate_with_groq_llama(prompt, api_key):
    """
    Génère du texte en utilisant l'API Groq avec un modèle Llama3 et trace l'appel avec LangSmith.
    """
    try:
        client_groq = Groq(api_key=api_key)
        chat_completion = client_groq.chat.completions.create(
            messages=[
                {
                    "role": "user",
                    "content": prompt,
                }
            ],
            model="llama3-8b-8192",
            temperature=0.7,
            max_tokens=1024,
        )
        return chat_completion.choices[0].message.content
    except Groq.APIConnectionError as e:
        st.error(f"Erreur de connexion avec Groq: {e.__cause__}")
        return "Impossible de se connecter à l'API Groq. Veuillez vérifier votre connexion réseau."
    except Exception as e:
        st.error(f"Une erreur inattendue est survenue avec Groq: {e}")
        return "Une erreur est survenue lors de la génération de texte."

def transcribe_audio(path, content_type):
    request_headers = headers.copy()
    if not request_headers.get("Authorization"):
        st.error("Veuillez entrer une clé API Hugging Face pour utiliser cette fonctionnalité.")
        return "Erreur : clé API Hugging Face manquante."

    request_headers["Content-Type"] = content_type
    API_URL = "https://api-inference.huggingface.co/models/openai/whisper-large-v2"

    # Débogage
    st.info(f"Tentative de transcription audio...")
    st.info(f"Chemin du fichier audio : {path}")
    st.info(f"Type de contenu (Content-Type) : {content_type}")
    st.info(f"En-têtes de la requête (partie Authorization masquée) : {{'Authorization': 'Bearer [MASQUÉE]', 'Content-Type': '{content_type}'}}")
    if not st.session_state.get("API_TOKEN_HF"):
        st.error("DEBUG: La clé API Hugging Face n'est PAS présente dans st.session_state.")
    else:
        st.success("DEBUG: La clé API Hugging Face est présente dans st.session_state.")

    try:
        with open(path, "rb") as f:
            response = requests.post(API_URL, headers=request_headers, data=f)
        response.raise_for_status()  # Lève une exception pour les codes d'erreur HTTP
        return response.json().get("text", "Erreur: 'text' non trouvé dans la réponse.")
    except requests.exceptions.HTTPError as err:
        st.error(f"Erreur Whisper Large: {err.response.status_code} - {err.response.text}")
        return f"Erreur lors de la transcription (Code: {err.response.status_code}). Vérifiez votre clé API HF et les limites d'utilisation."
    except Exception as e:
        st.error(f"Une erreur inattendue est survenue lors de la transcription : {str(e)}")
        return "Erreur lors de la transcription."

def summarize_text(text):
    """
    Génère un résumé de texte en utilisant le modèle Groq Llama3.
    """
    if not st.session_state.get("API_TOKEN_GROQ"):
        st.error("Veuillez configurer votre clé API Groq dans le fichier .env pour le résumé.")
        return "Erreur : clé API Groq manquante."

    prompt = f"Résume le texte suivant de manière concise en français :\n\nTexte : '''{text}'''\n\nRésumé :"

    summary = generate_with_groq_llama(prompt, st.session_state["API_TOKEN_GROQ"])
    return summary


# --- MENU ---
option = st.sidebar.radio(
    "Choisissez une fonctionnalité :",
    ["📝 Générateur de texte", "🎙 Audio vers texte", "🧠 Résumeur de texte"]
)

# --- GÉNÉRATEUR DE TEXTE ---
if option == "📝 Générateur de texte":
    st.subheader("📝 Génération de texte (Groq Llama3)")
    st.markdown("Utilise le modèle `llama3-8b-8192` via l'API ultra-rapide de Groq.")
    if st.session_state.get("LANGSMITH_CONFIGURED"):
        st.info("Le suivi avec LangSmith est activé. [Voir le projet](https://smith.langchain.com/)", icon="🔗")

    if not st.session_state.get("API_TOKEN_GROQ"):
        st.warning("Veuillez entrer une clé API Groq dans votre fichier `.env` pour utiliser le générateur de texte.")
    else:
        prompt = st.text_area("Entrez votre prompt :", key="prompt", height=150)
        if st.button("Générer", key="gen"):
            if prompt.strip():
                with st.spinner("Génération en cours avec Groq..."):
                    try:
                        output = generate_with_groq_llama(prompt, st.session_state["API_TOKEN_GROQ"])
                        st.success("Texte généré :")
                        st.write(output)
                    except Exception as e:
                        st.error(f"Erreur lors de la génération avec Groq : {e}")
            else:
                st.warning("Veuillez entrer un prompt.")


# --- AUDIO VERS TEXTE ---
elif option == "🎙 Audio vers texte":
    st.subheader("🎧 Transcription automatisée d’un fichier audio (30 sec max)")
    if not st.session_state.get("API_TOKEN_HF"):
        st.warning("Veuillez entrer une clé API Hugging Face dans votre fichier `.env` pour utiliser cette fonctionnalité.")
    else:
        audio_file = st.file_uploader("🎵 Chargez un fichier audio", type=["wav", "mp3", "m4a"])
        if audio_file is not None:
            # Créer un fichier temporaire pour sauvegarder l'audio
            with tempfile.NamedTemporaryFile(delete=False, suffix=f'.{audio_file.name.split(".")[-1]}') as tmp_file:
                tmp_file.write(audio_file.getvalue())
                audio_path = tmp_file.name
            st.audio(audio_path)
            
            if st.button("✍️ Transcrire"):
                with st.spinner("Transcription en cours..."):
                    # On passe le type MIME du fichier original à la fonction
                    transcript = transcribe_audio(audio_path, audio_file.type)
                
                if "Erreur" not in transcript: # Vérifiez si la transcription n'est pas une erreur
                    st.markdown(f"**Transcription :**")
                    st.text_area("Résultat", transcript, height=150)
                
                # Nettoyer le fichier temporaire
                os.unlink(audio_path)


# --- RÉSUMEUR DE TEXTE ---
elif option == "🧠 Résumeur de texte":
    st.subheader("🧠 Résumé de texte (Groq Llama3)")
    st.markdown("Utilise le modèle `llama3-8b-8192` pour générer un résumé.")
    if not st.session_state.get("API_TOKEN_GROQ"):
        st.warning("Veuillez vous assurer qu'une clé API Groq est configurée dans votre fichier .env pour utiliser cette fonctionnalité.")
    else:
        input_text = st.text_area("Texte à résumer :", height=300)
        if st.button("Résumer"):
            if input_text.strip():
                with st.spinner("Résumé en cours avec Groq..."):
                    summary = summarize_text(input_text)
                    st.success("Résumé généré :")
                    st.write(summary)
            else:
                st.warning("Veuillez entrer un texte à résumer.")