import streamlit as st
from huggingface_hub import InferenceClient
import re
import edge_tts
import asyncio
from concurrent.futures import ThreadPoolExecutor
import tempfile
from pydub import AudioSegment
# Initialize Hugging Face InferenceClient
client_hf = InferenceClient("mistralai/Mixtral-8x7B-Instruct-v0.1")
# Define the async function for text-to-speech conversion using Edge TTS
async def text_to_speech_edge(text, language_code):
voice = {"fr": "fr-FR-RemyMultilingualNeural"}[language_code]
communicate = edge_tts.Communicate(text, voice)
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp_file:
tmp_path = tmp_file.name
await communicate.save(tmp_path)
return tmp_path
# Helper function to run async functions from within Streamlit (synchronous context)
def run_in_threadpool(func, *args, **kwargs):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
future = asyncio.ensure_future(func(*args, **kwargs))
return loop.run_until_complete(future)
def concatenate_audio(paths):
combined = AudioSegment.empty()
for path in paths:
audio = AudioSegment.from_mp3(path)
combined += audio
combined_path = tempfile.mktemp(suffix=".mp3")
combined.export(combined_path, format="mp3")
return combined_path
# Modified function to work with async Edge TTS
def dictee_to_audio_segmented(dictee):
sentences = segmenter_texte(dictee)
audio_urls = []
with ThreadPoolExecutor() as executor:
for sentence in sentences:
processed_sentence = replace_punctuation(sentence)
audio_path = executor.submit(run_in_threadpool, text_to_speech_edge, processed_sentence, "fr").result()
audio_urls.append(audio_path)
return audio_urls
def generer_dictee(classe, longueur):
prompt = f"Créer une dictée pour la classe {classe} d'une longueur d'environ {longueur} mots. Il est important de créer le texte uniquement de la dictée et de ne pas ajouter de consignes ou d'indications supplémentaires."
generate_kwargs = {
"temperature": 0.7,
"max_new_tokens": 1000,
"top_p": 0.95,
"repetition_penalty": 1.2,
"do_sample": True,
}
formatted_prompt = f"[INST] {prompt} [/INST]"
stream = client_hf.text_generation(formatted_prompt, **generate_kwargs, stream=True, details=True, return_full_text=False)
dictee = ""
for response in stream:
dictee += response.token.text
dictee = dictee.replace("", "").strip()
return dictee
def correction_dictee(dictee, dictee_user):
prompt = f"""
Utilise les syntax du markdown pour un meilleur rendu.
Introduction:
Vous avez deux textes importants à analyser et à comparer. Le premier, nommé dictee, est la version correcte et officielle d'une dictée. Le second, dictee_user, est une tentative de reproduction de cette dictée par un utilisateur, qui peut contenir des erreurs.
Objectif:
Votre tâche consiste à identifier les erreurs dans dictee_user en le comparant à dictee, et à fournir une version corrigée de dictee_user qui corrige ces erreurs tout en expliquant les corrections effectuées.
Instructions détaillées:
Comparaison: Examinez attentivement dictee_user et comparez-le à dictee pour détecter toutes les différences. Notez que dictee est la version exacte et sans erreur, tandis que dictee_user peut contenir des fautes d'orthographe, de grammaire, ou de syntaxe.
Identification des Erreurs: Identifiez spécifiquement les erreurs dans dictee_user. Cela peut inclure des mots mal orthographiés, des erreurs grammaticales, des problèmes de ponctuation, ou des maladresses de style.
Correction et Explication: Pour chaque erreur identifiée, corrigez-la et fournissez une courte explication ou la règle grammaticale pertinente. Cela aidera l'utilisateur à comprendre ses fautes et à apprendre de ses erreurs.
Rendu Final: Présentez une version corrigée de dictee_user qui intègre toutes vos corrections. Assurez-vous que cette version est désormais conforme à dictee tant sur le plan du contenu que de la forme.
Exemple:
Dictée (dictee): "Les forêts anciennes abritent une biodiversité riche et variée."
Dictée de l'Utilisateur (dictee_user): "Les forets anciennes abritent une biodiversitée riche et variés."
Corrections:
"forets" devrait être "forêts" (ajout d'un accent circonflexe sur le "e" pour respecter la règle d'orthographe).
"biodiversitée" est incorrect, la forme correcte est "biodiversité" (pas de "e" à la fin, erreur courante de suffixe).
"variés" devrait être "variée" pour s'accorder en genre et en nombre avec "biodiversité".
Voici la dictée :
{dictee}
Voici la dictée de l'utilisateur :
{dictee_user}
"""
generate_kwargs = {
"temperature": 0.7,
"max_new_tokens": 2000, # Ajustez selon la longueur attendue de la correction
"top_p": 0.95,
"repetition_penalty": 1.2,
"do_sample": True,
}
formatted_prompt = f"[INST] {prompt} [/INST]"
stream = client_hf.text_generation(formatted_prompt, **generate_kwargs, stream=True, details=True, return_full_text=False)
correction = ""
for response in stream:
correction += response.token.text
correction = correction.replace("", "").strip()
return correction
def replace_punctuation(text):
replacements = {
".": " point.",
",": " virgule,",
";": " point-virgule;",
":": " deux-points:",
"!": " point d'exclamation!",
"?": " point d'interrogation?",
}
for key, value in replacements.items():
text = text.replace(key, value)
return text
def segmenter_texte(texte):
sentences = re.split(r'(?<=[.!?]) +', texte)
return sentences
# Streamlit App Interface
st.title('🎓 Entrainement de Dictée')
# Initializing session state variables
if 'expanded' not in st.session_state:
st.session_state.expanded = True
if 'dicteecreation' not in st.session_state:
st.session_state.dicteecreation = False
if 'creationmodified' not in st.session_state:
st.session_state.creationmodified = False
if 'dictee' not in st.session_state:
st.session_state.dictee = None
if 'audio_urls' not in st.session_state:
st.session_state.audio_urls = []
if 'concatenated_audio_path' not in st.session_state:
st.session_state.concatenated_audio_path = None
if 'correction' not in st.session_state:
st.session_state.correction = None
# Settings Dictee
with st.expander("📝 Génération de la dictée", expanded=st.session_state.expanded):
with st.form("dictation_form"):
st.markdown("### 🚀 Choisissez votre mode de dictée")
mode = st.radio("Mode:", ["S'entrainer: Vous aurez uniquement les audios suivi d'une correction par IA (Pour 1 seul personne)", "Entrainer: Vous aurez uniquement le texte de la dictée pour entrainer quelqu'un d'autre (Pour 2 ou + personnes)"])
st.markdown("### 🎒 Sélectionnez la classe")
classe = st.selectbox("Classe", ["CP", "CE1", "CE2", "CM1", "CM2", "6ème", "5ème", "4ème", "3ème", "Seconde", "Premiere", "Terminale"], index=2)
st.markdown("### 📏 Définissez la longueur de la dictée")
longueur = st.slider("Longueur de la dictée (nombre de mots)", 50, 500, 200)
submitted = st.form_submit_button("🔮 Générer la Dictée", disabled=st.session_state.dicteecreation)
st.divider()
if submitted or st.session_state.dictee != None:
with st.spinner("🚀 Dictée en cours de création..."):
if st.session_state.creationmodified == False:
st.session_state.expandedmodified = True
st.session_state.dicteecreation = True
if 'dictee' != None:
st.session_state.dictee = generer_dictee(classe, longueur)
st.session_state.creationmodified = True
st.rerun()
dictee = st.session_state.dictee
if mode.startswith("S'entrainer"):
if 'audio_urls' != None:
with st.spinner("🔊 Préparation des audios..."):
st.session_state.audio_urls = dictee_to_audio_segmented(dictee)
audio_urls = st.session_state.audio_urls
if 'concatenated_audio_path' != None:
with st.spinner("🎵 Assemblage de l'audio complet..."):
st.session_state.concatenated_audio_path = concatenate_audio(audio_urls)
concatenated_audio_path = st.session_state.concatenated_audio_path
st.markdown("## ✍️ Votre Dictée")
with st.form("dictee_form"):
dictee_user = st.text_area("Écrivez la dictée ici:", key="dictee_user", height=350)
correct = st.form_submit_button("📝 Correction")
if correct:
st.session_state.correction = correction_dictee(dictee, dictee_user)
st.rerun()
st.sidebar.markdown("## 📖 Dictée en entier")
st.sidebar.audio(concatenated_audio_path, format='audio/wav', start_time=0)
st.sidebar.markdown("## 📖 Phrases de la Dictée")
st.sidebar.caption("Le bouton sert a cocher les phrases que vous avez fait.")
for idx, audio_url in enumerate(audio_urls):
st.sidebar.markdown(f"**Phrase {idx + 1}:**")
st.sidebar.audio(audio_url, format='audio/wav')
if st.session_state.correction != None:
st.divider()
st.markdown("### 🎉 Voici la correction (*Par IA*) :")
st.markdown(st.session_state.correction)
if st.button("En faire une nouvelle"):
del st.session_state['expandedmodified']
del st.session_state['dictee']
del st.session_state['audio_urls']
del st.session_state['concatenated_audio_path']
del st.session_state['correction']
st.session_state.dicteecreation = False
st.session_state.creationmodified = False
st.rerun()
elif mode.startswith("Entrainer"):
st.markdown("### 📚 Voici la dictée :")
st.markdown(dictee)
if st.button("En faire une nouvelle"):
del st.session_state['expandedmodified']
del st.session_state['dictee']
st.session_state.dicteecreation = False
st.session_state.creationmodified = False
st.rerun()