NLP_BABONG / app.py
LewisBabong's picture
Update app.py
59fd815 verified
import streamlit as st
import requests
import tempfile
import uuid
import os
from dotenv import load_dotenv
from groq import Groq
from langsmith import Client as LangSmithClient
import time
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
load_dotenv()
HF_TOKEN = f"Bearer {os.getenv('HF_TOKEN_SECRET')}"
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
LANGSMITH_API_KEY = os.getenv("LANGSMITH_API_KEY")
groq_client = Groq(api_key=GROQ_API_KEY)
langsmith_client = LangSmithClient(api_key=LANGSMITH_API_KEY)
st.set_page_config(
page_title="🧠 NLP Magique",
page_icon="✨",
layout="wide",
initial_sidebar_state="collapsed"
)
st.markdown("""
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css');
.stApp {
background: linear-gradient(135deg, #f5f7ff, #e4e9ff);
font-family: 'Inter', sans-serif;
color: #1f2937;
}
.main-title {
text-align: center;
font-size: 3.5rem;
font-weight: 700;
color: #4f46e5;
margin: 2rem 0;
animation: fadeInDown 1s ease-out;
}
.navbar {
background: linear-gradient(90deg, #ffffff, #f9fafb);
padding: 1rem 2rem;
border-bottom: 1px solid #e5e7eb;
display: flex;
align-items: center;
gap: 2rem;
margin-bottom: 2rem;
overflow-x: auto;
white-space: nowrap;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
border-radius: 8px;
}
.navbar-brand {
font-size: 1.25rem;
font-weight: 700;
color: #4f46e5;
display: flex;
align-items: center;
gap: 0.5rem;
}
.navbar-brand i {
font-size: 1.5rem;
}
.navbar button {
background: none;
border: none;
font-size: 1rem;
font-weight: 500;
color: #4b5563;
padding: 0.75rem 1.25rem;
cursor: pointer;
transition: all 0.3s ease;
border-radius: 6px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.navbar button:hover {
color: #4f46e5;
background: #e0e7ff;
transform: scale(1.05);
}
.navbar button.active {
color: #4f46e5;
background: #e0e7ff;
font-weight: 600;
border-bottom: 3px solid #4f46e5;
}
.navbar button i {
font-size: 1rem;
}
.card {
background: linear-gradient(145deg, #ffffff, #f9fafb);
border-radius: 16px;
padding: 2rem;
margin: 1.5rem 0;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
transition: all 0.3s ease;
border: 1px solid #e5e7eb;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 6px 24px rgba(0,0,0,0.12);
}
.stButton>button {
background: linear-gradient(90deg, #4f46e5, #7c3aed);
color: white;
font-weight: 600;
border-radius: 8px;
padding: 0.75rem 1.5rem;
border: none;
transition: all 0.3s ease;
width: 100%;
}
.stButton>button:hover {
background: linear-gradient(90deg, #4338ca, #6d28d9);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(79,70,229,0.3);
}
.stTextArea textarea, .stSlider>div, .stTextInput input {
background: #f9fafb;
border-radius: 8px;
padding: 1rem;
border: 1px solid #d1d5db;
transition: all 0.3s ease;
}
.stTextArea textarea:focus, .stTextInput input:focus {
border-color: #4f46e5;
box-shadow: 0 0 0 3px rgba(79,70,229,0.1);
}
.stFileUploader {
background: #f9fafb;
border-radius: 8px;
padding: 1rem;
border: 1px solid #d1d5db;
}
.output-card {
background: #f9fafb;
border-radius: 8px;
padding: 1.5rem;
margin-top: 1rem;
border: 1px solid #e5e7eb;
position: relative;
font-size: 1rem;
line-height: 1.6;
}
.copy-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: #4f46e5;
color: white;
border: none;
border-radius: 6px;
padding: 0.5rem 1rem;
font-size: 0.875rem;
cursor: pointer;
transition: all 0.3s ease;
}
.copy-button:hover {
background: #4338ca;
}
.chat-window {
width: 100%;
max-width: 600px;
height: 500px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 6px 20px rgba(0,0,0,0.15);
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0 auto;
border: 1px solid #e5e7eb;
}
.chat-header {
background: #f9fafb;
color: #1f2937;
padding: 0.75rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 500;
font-size: 0.875rem;
border-bottom: 1px solid #e5e7eb;
}
.chat-body {
flex: 1;
overflow-y: auto;
padding: 0.75rem;
background: #f9fafb;
}
.chat-message {
margin: 0.5rem 0;
padding: 0.5rem 0.75rem;
border-radius: 8px;
max-width: 85%;
font-size: 0.875rem;
line-height: 1.4;
}
.user-message {
background: #e0e7ff;
margin-left: auto;
border: 1px solid #c7d2fe;
}
.bot-message {
background: #f3e8ff;
margin-right: auto;
border: 1px solid #e9d5ff;
}
.chat-input {
display: flex;
align-items: center;
padding: 0.5rem;
background: #ffffff;
border-top: 1px solid #e5e7eb;
gap: 0.5rem;
}
.chat-input button {
background: #4f46e5;
color: white;
border: none;
border-radius: 6px;
padding: 0.5rem 1rem;
font-size: 0.875rem;
cursor: pointer;
transition: background 0.2s ease;
}
.chat-input button:hover {
background: #4338ca;
}
.langsmith-table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
.langsmith-table th, .langsmith-table td {
border: 1px solid #e5e7eb;
padding: 0.75rem;
text-align: left;
font-size: 0.875rem;
}
.langsmith-table th {
background: #f9fafb;
font-weight: 600;
}
.langsmith-table tr:nth-child(even) {
background: #f9fafb;
}
.footer {
text-align: center;
padding: 2rem;
margin-top: 3rem;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
font-size: 1rem;
color: #4b5563;
}
.footer a {
color: #4f46e5;
font-weight: 600;
text-decoration: none;
}
.footer a:hover {
color: #4338ca;
text-decoration: underline;
}
.social-icons img {
width: 24px;
margin: 0 0.5rem;
transition: transform 0.3s ease;
}
.social-icons img:hover {
transform: scale(1.2);
}
@keyframes fadeInDown {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.main-title { font-size: 2.5rem; }
.card { padding: 1.5rem; }
.chat-window { width: 90vw; }
.navbar { gap: 1rem; padding: 0.75rem 1rem; }
.navbar-brand { font-size: 1rem; }
.navbar button { padding: 0.5rem 0.75rem; font-size: 0.875rem; }
}
</style>
""", unsafe_allow_html=True)
st.markdown("""
<script>
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
alert('Texte copié !');
});
}
</script>
""", unsafe_allow_html=True)
def generate_text(prompt):
url = "https://api-inference.huggingface.co/models/gpt2"
headers = {"Authorization": HF_TOKEN}
payload = {"inputs": f"{prompt}"}
response = requests.post(url, headers=headers, json=payload)
if response.ok:
return response.json()[0]["generated_text"]
else:
st.error(f"Erreur: {response.status_code} - {response.text}")
return "Erreur lors de la génération."
def summarize_text(text):
url = "https://api-inference.huggingface.co/models/facebook/bart-large-cnn"
headers = {"Authorization": HF_TOKEN}
payload = {"inputs": text}
response = requests.post(url, headers=headers, json=payload)
if response.ok:
return response.json()[0]["summary_text"]
else:
st.error(f"Erreur: {response.status_code} - {response.text}")
return "Erreur lors du résumé."
def transcribe_audio(path):
url = "https://api-inference.huggingface.co/models/openai/whisper-large-v2"
headers = {
"Authorization": HF_TOKEN,
"Content-Type": "application/octet-stream"
}
with open(path, "rb") as f:
response = requests.post(url, headers=headers, data=f)
if response.ok:
return response.json()["text"]
else:
st.error(f"Erreur: {response.status_code} - {response.text}")
return "Erreur lors de la transcription."
def chat_with_grok(user_input, conversation_id):
try:
run = langsmith_client.create_run(
name="Grok_Chat",
run_type="chain",
inputs={"user_input": user_input, "conversation_id": conversation_id}
)
response = groq_client.chat.completions.create(
messages=[
{"role": "system",
"content": "Vous êtes Grok, un assistant IA créé par xAI. Répondez en français avec précision et clarté."},
{"role": "user", "content": user_input}
],
model="llama-3.3-70b-versatile",
temperature=0.7,
max_tokens=1000
)
response_text = response.choices[0].message.content
if run:
run.update({"outputs": response_text, "end_time": time.time()})
return response_text
except Exception as e:
if 'run' in locals() and run:
run.update({"outputs": {"error": str(e)}, "end_time": time.time(), "status": "error"})
st.error(f"Erreur: {str(e)}")
return "Une erreur s'est produite. Réessayez."
# Navbar
st.markdown('<div class="navbar">', unsafe_allow_html=True)
st.markdown('<span class="navbar-brand"><i class="fas fa-brain"></i> NLP Magique</span>', unsafe_allow_html=True)
tabs = [
{"name": "Résumé", "icon": "fas fa-compress"},
{"name": "Génération", "icon": "fas fa-pen-fancy"},
{"name": "Transcription", "icon": "fas fa-microphone"},
{"name": "Chatbot", "icon": "fas fa-comment-dots"},
{"name": "LangSmith Monitoring", "icon": "fas fa-chart-line"}
]
selected_tab = st.session_state.get('selected_tab', tabs[0]["name"])
for tab in tabs:
active = 'active' if selected_tab == tab["name"] else ''
if st.button(f'<i class="{tab["icon"]}"></i> {tab["name"]}', key=f"nav_{tab["name"]}", help=tab["name"],
unsafe_allow_html=True):
st.session_state.selected_tab = tab["name"]
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
# Initialize session state
if 'chat_history' not in st.session_state:
st.session_state.chat_history = []
if 'conversation_id' not in st.session_state:
st.session_state.conversation_id = str(uuid.uuid4())
if 'langsmith_runs' not in st.session_state:
st.session_state.langsmith_runs = []
st.markdown('<h1 class="main-title">✨ NLP Magique</h1>', unsafe_allow_html=True)
if selected_tab == "Génération":
with st.container():
st.markdown("<div class='card'>", unsafe_allow_html=True)
st.subheader("Génération de texte")
prompt = st.text_area("Entrez votre idée :", "La médecine moderne", height=150)
temp = st.slider("Créativité", 0.1, 1.0, 0.7, step=0.1)
if st.button("Générer"):
with st.spinner("Génération..."):
output = generate_text(prompt)
st.markdown(
f'<div class="output-card">{output}<button class="copy-button" onclick="copyToClipboard(\'{output}\')">Copier</button></div>',
unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)
elif selected_tab == "Résumé":
with st.container():
st.markdown("<div class='card'>", unsafe_allow_html=True)
st.subheader("Résumé de texte")
texte = st.text_area("Collez votre texte :", height=200)
if st.button("Résumer"):
with st.spinner("Résumé..."):
summary = summarize_text(texte)
st.markdown(
f'<div class="output-card">{summary}<button class="copy-button" onclick="copyToClipboard(\'{summary}\')">Copier</button></div>',
unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)
elif selected_tab == "Transcription":
with st.container():
st.markdown("<div class='card'>", unsafe_allow_html=True)
st.subheader("Transcription audio")
audio_file = st.file_uploader("Chargez un audio (max 30s)", type=["wav", "mp3", "m4a"])
if audio_file:
with tempfile.NamedTemporaryFile(delete=False, suffix='.wav') as tmp_file:
tmp_file.write(audio_file.read())
audio_path = tmp_file.name
st.audio(audio_path)
if st.button("Transcrire"):
with st.spinner("Transcription..."):
transcript = transcribe_audio(audio_path)
st.markdown(
f'<div class="output-card">{transcript}<button class="copy-button" onclick="copyToClipboard(\'{transcript}\')">Copier</button></div>',
unsafe_allow_html=True)
st.markdown("</div>", unsafe_allow_html=True)
elif selected_tab == "Chatbot":
with st.container():
st.markdown('<div class="chat-window">', unsafe_allow_html=True)
st.markdown('<div class="chat-header">Grok AI</div>', unsafe_allow_html=True)
st.markdown('<div class="chat-body">', unsafe_allow_html=True)
for message in st.session_state.chat_history:
if message['role'] == 'user':
st.markdown(f'<div class="chat-message user-message">{message["content"]}</div>',
unsafe_allow_html=True)
else:
st.markdown(f'<div class="chat-message bot-message">{message["content"]}</div>', unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
st.markdown('<div class="chat-input">', unsafe_allow_html=True)
user_input = st.text_input("Votre message :", key="chat_input", placeholder="Tapez ici...")
col1, col2 = st.columns([3, 1])
with col1:
if st.button("Envoyer", key="chat_send"):
if user_input:
with st.spinner("Grok répond..."):
st.session_state.chat_history.append({"role": "user", "content": user_input})
response = chat_with_grok(user_input, st.session_state.conversation_id)
st.session_state.chat_history.append({"role": "assistant", "content": response})
st.session_state.langsmith_runs.append({
"input": user_input,
"output": response,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"input_length": len(user_input)
})
st.rerun()
with col2:
if st.button("Effacer", key="chat_clear"):
st.session_state.chat_history = []
st.session_state.conversation_id = str(uuid.uuid4())
st.session_state.langsmith_runs = []
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
elif selected_tab == "LangSmith Monitoring":
with st.container():
st.markdown("<div class='card'>", unsafe_allow_html=True)
st.subheader("LangSmith Monitoring")
if st.session_state.langsmith_runs:
df = pd.DataFrame(st.session_state.langsmith_runs)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['hour'] = df['timestamp'].dt.strftime('%Y-%m-%d %H:00')
# Chart 1: Interactions per Hour
st.markdown("<h3>Interactions par heure</h3>", unsafe_allow_html=True)
chart_type = st.selectbox("Type de graphique", ["Bar", "Line", "Area"], key="chart_type_interactions")
interaction_counts = df.groupby('hour').size().reset_index(name='count')
if chart_type == "Bar":
fig = px.bar(
interaction_counts,
x='hour',
y='count',
color_discrete_sequence=['#4f46e5'],
title="Nombre d'interactions par heure",
labels={'hour': 'Heure', 'count': 'Nombre d\'interactions'}
)
elif chart_type == "Line":
fig = px.line(
interaction_counts,
x='hour',
y='count',
color_discrete_sequence=['#4f46e5'],
title="Nombre d'interactions par heure",
labels={'hour': 'Heure', 'count': 'Nombre d\'interactions'},
markers=True
)
else: # Area
fig = px.area(
interaction_counts,
x='hour',
y='count',
color_discrete_sequence=['#4f46e5'],
title="Nombre d'interactions par heure",
labels={'hour': 'Heure', 'count': 'Nombre d\'interactions'}
)
fig.update_layout(
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(family="Inter, sans-serif", size=12, color="#1f2937"),
xaxis_tickangle=45,
showlegend=False,
margin=dict(l=40, r=40, t=80, b=40)
)
st.plotly_chart(fig, use_container_width=True)
# Chart 2: Input Length per Interaction
st.markdown("<h3>Longueur des entrées par interaction</h3>", unsafe_allow_html=True)
input_length_chart_type = st.selectbox("Type de graphique", ["Bar", "Line", "Area"],
key="chart_type_input_length")
input_lengths = df.groupby('hour')['input_length'].mean().reset_index(name='avg_length')
if input_length_chart_type == "Bar":
fig2 = px.bar(
input_lengths,
x='hour',
y='avg_length',
color_discrete_sequence=['#7c3aed'],
title="Longueur moyenne des entrées par heure",
labels={'hour': 'Heure', 'avg_length': 'Longueur moyenne (caractères)'}
)
elif input_length_chart_type == "Line":
fig2 = px.line(
input_lengths,
x='hour',
y='avg_length',
color_discrete_sequence=['#7c3aed'],
title="Longueur moyenne des entrées par heure",
labels={'hour': 'Heure', 'avg_length': 'Longueur moyenne (caractères)'},
markers=True
)
else: # Area
fig2 = px.area(
input_lengths,
x='hour',
y='avg_length',
color_discrete_sequence=['#7c3aed'],
title="Longueur moyenne des entrées par heure",
labels={'hour': 'Heure', 'avg_length': 'Longueur moyenne (caractères)'}
)
fig2.update_layout(
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
font=dict(family="Inter, sans-serif", size=12, color="#1f2937"),
xaxis_tickangle=45,
showlegend=False,
margin=dict(l=40, r=40, t=80, b=40)
)
st.plotly_chart(fig2, use_container_width=True)
# Table: Run Details
st.markdown("<h3>Détails des interactions</h3>", unsafe_allow_html=True)
st.markdown('<table class="langsmith-table">', unsafe_allow_html=True)
st.markdown('<tr><th>Timestamp</th><th>Input</th><th>Output</th><th>Longueur entrée</th></tr>',
unsafe_allow_html=True)
for run in st.session_state.langsmith_runs:
st.markdown(
f'<tr><td>{run["timestamp"]}</td><td>{run["input"]}</td><td>{run["output"]}</td><td>{run["input_length"]}</td></tr>',
unsafe_allow_html=True
)
st.markdown('</table>', unsafe_allow_html=True)
else:
st.write("Aucune donnée de monitoring disponible.")
st.markdown("</div>", unsafe_allow_html=True)
st.markdown("""
<div class="footer">
<p>© 2025 NTONGA<br>
Propulsé par <a href='https://www.huggingface.co/' target='_blank'>Hugging Face</a>, <a href='https://streamlit.io/' target='_blank'>Streamlit</a>, et <a href='https://x.ai/' target='_blank'>xAI</a></p>
<div class='social-icons'>
<a href="https://twitter.com" target="_blank"><img src='https://img.icons8.com/color/48/twitter--v1.png'></a>
<a href="https://www.linkedin.com" target="_blank"><img src='https://img.icons8.com/color/48/linkedin.png'></a>
<a href="https://github.com" target="_blank"><img src='https://img.icons8.com/color/48/github--v1.png'></a>
</div>
</div>
""", unsafe_allow_html=True)