File size: 14,935 Bytes
4688078
 
 
3f4fb6f
0f33e60
be0e7b0
ee77673
 
4688078
6ff8c22
6971407
 
6ff8c22
bb915d7
 
 
 
 
 
 
 
 
 
ee77673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6971407
ee77673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0f33e60
 
6971407
 
0f33e60
ee77673
0f33e60
6ff8c22
6971407
 
 
 
6ff8c22
 
6971407
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ff8c22
be0e7b0
 
 
f5b1c77
be0e7b0
 
 
 
 
 
 
 
 
 
 
 
 
6ff8c22
606dc5d
f5b1c77
 
 
0f33e60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23d2281
29e90a0
 
 
 
606dc5d
6ff8c22
ba0e382
6ff8c22
ba0e382
 
 
73b0775
6ff8c22
73b0775
 
 
6ff8c22
29e90a0
73b0775
 
29e90a0
 
6971407
6ff8c22
ba0e382
6971407
6ff8c22
73b0775
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ff8c22
4688078
6971407
4688078
6971407
 
 
ba0e382
 
 
 
29e90a0
 
 
 
 
c151267
606dc5d
 
 
 
 
 
c151267
 
 
29e90a0
 
c151267
 
 
23d2281
4688078
36c1b32
4688078
36c1b32
 
 
4688078
6ff8c22
36c1b32
 
 
 
29e90a0
 
 
 
 
 
36c1b32
29e90a0
 
 
 
 
 
 
36c1b32
 
 
 
 
c151267
 
 
 
0f33e60
29e90a0
6ff8c22
3f4fb6f
ee77673
 
 
 
6971407
 
6ff8c22
29e90a0
0f33e60
29e90a0
 
 
 
 
 
 
 
 
 
 
 
4688078
6ff8c22
6971407
 
 
 
6ff8c22
 
 
6971407
4688078
29e90a0
 
 
 
 
 
 
606dc5d
29e90a0
 
e845f52
29e90a0
e845f52
29e90a0
e845f52
29e90a0
e845f52
 
606dc5d
29e90a0
 
606dc5d
 
 
 
 
 
 
 
 
 
 
 
 
 
29e90a0
ba0e382
6ff8c22
4688078
29e90a0
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import streamlit as st
from openai import OpenAI
from os import getenv
from audiorecorder import audiorecorder
import tempfile
import base64
from pydub import AudioSegment
import os

# Configuration du client OpenAI avec la clé API
client = OpenAI(api_key=getenv("OPENAI_API_KEY"))

# Fonction pour lire et retourner le contenu de fichiers textes
def lire_fichier(nom_fichier):
    try:
        with open(nom_fichier, 'r', encoding='utf-8') as fichier:
            contenu = fichier.read()
        return contenu
    except FileNotFoundError:
        return f"Erreur : Le fichier '{nom_fichier}' n'a pas été trouvé."
    except Exception as e:
        return f"Une erreur s'est produite lors de la lecture du fichier : {str(e)}"

# Fonction pour diviser un fichier audio en segments de 25 Mo ou moins
def split_audio(audio_file, max_size_mb=25):
    audio = AudioSegment.from_wav(audio_file)
    duration_ms = len(audio)
    segment_duration_ms = int((max_size_mb * 1024 * 1024 * 8) / (audio.frame_rate * audio.sample_width * audio.channels))
    
    segments = []
    for start in range(0, duration_ms, segment_duration_ms):
        end = min(start + segment_duration_ms, duration_ms)
        segment = audio[start:end]
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_segment:
            segment.export(temp_segment.name, format="wav")
            segments.append(temp_segment.name)
    
    return segments

# Fonction modifiée pour transcrire l'audio en texte
def transcribe_audio(audio_file, language=None):
    max_size_mb = 25
    file_size_mb = os.path.getsize(audio_file.name) / (1024 * 1024)
    
    if file_size_mb > max_size_mb:
        segments = split_audio(audio_file.name, max_size_mb)
        full_transcript = ""
        for segment in segments:
            with open(segment, "rb") as audio_segment:
                transcript = client.audio.transcriptions.create(
                    model="whisper-1", 
                    file=audio_segment,
                    language=language
                )
                full_transcript += transcript.text + " "
            os.unlink(segment)  # Supprimer le fichier temporaire
        return full_transcript.strip()
    else:
        with open(audio_file.name, "rb") as audio_file:
            transcript = client.audio.transcriptions.create(
                model="whisper-1", 
                file=audio_file,
                language=language
            )
        return transcript.text

# Fonction pour détecter la langue d'un texte donné
def language_detection(input_text, temperature=0.01):
    system_prompt = "".join([
        "Je souhaite que vous agissiez en tant que fonction linguistique.",
        "Je m'exprimerai dans n'importe quelle langue, et vous en détecterez la langue.",
        "Vous fournirez le résultat de votre détection au format ISO-639-1.",
        "Votre réponse doit représenter l'argument `language` et contenir seulement sa valeur de type chaîne de caractères.",
        "La langue de l'audio d'entrée. Fournir la langue d'entrée au format ISO-639-1 améliorera la précision et la latence."
    ])
    response = client.chat.completions.create(
        model="gpt-4o",
        temperature=temperature,
        messages=[
            {
                "role": "system",
                "content": system_prompt
            },
            {
                "role": "user",
                "content": f"{input_text}"
            }
        ]
    )
    return response.choices[0].message.content

# Fonction pour convertir du texte en parole
def text_to_speech(text):
    response = client.audio.speech.create(
        model="tts-1",
        voice="onyx",
        input=text
    )
    
    # Sauvegarder l'audio dans un fichier temporaire
    with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_audio:
        response.stream_to_file(temp_audio.name)
        
        # Lire le contenu du fichier audio
        with open(temp_audio.name, "rb") as audio_file:
            audio_bytes = audio_file.read()
    
    return audio_bytes

# Fonction pour traiter les messages de l'utilisateur et générer une réponse
def process_message(message, operation_prompt="", tts_enabled=False):
    payload_content = f'{operation_prompt} :\n\"\"\"\n{message}\n\"\"\"'

    st.session_state.messages.append({"role": "user", "content": payload_content})
    with st.chat_message("user"):
        st.markdown(message)

    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        full_response = ""
        for response in client.chat.completions.create(
            model="gpt-4o",
            messages=st.session_state.messages,
            stream=True,
            temperature=0.1,
        ):
            full_response += (response.choices[0].delta.content or "")
            message_placeholder.markdown(full_response + "▌")
        message_placeholder.markdown(full_response)
    
    st.session_state.messages.append({"role": "assistant", "content": full_response})

    if tts_enabled:
        tts_audio = text_to_speech(full_response)
        st.audio(tts_audio, format="audio/mp3", autoplay=True)


# Classe pour stocker les prompts système globaux
class GlobalSystemPrompts:
    # Méthode pour récupérer le prompt système pour la fonctionnalité Linguascribe
    def linguascribe():
        SYSTEM_PROMPT = f"{lire_fichier('linguascribe.prompt')}"
        return SYSTEM_PROMPT

# Variables globales pour les prompts
SYSTEM_PROMPT=""
OP_PROMPT=""

# Fonction pour configurer le mode de traduction
def set_mode_translation(from_lang, dest_lang):
    global SYSTEM_PROMPT
    global OP_PROMPT
    SYSTEM_PROMPT=GlobalSystemPrompts.linguascribe()
    OP_PROMPT = f"Translate({from_lang} to {dest_lang})"

# Liste des langues supportées par l'application
SUPPORTED_LANGUAGES=["Afrikaans", "Arabic", "Armenian", "Azerbaijani", "Belarusian", "Bosnian", "Bulgarian", "Catalan", "Chinese", "Croatian", "Czech", "Danish", "Dutch", "English", "Estonian", "Finnish", "French", "Galician", "German", "Greek", "Hebrew", "Hindi", "Hungarian", "Icelandic", "Indonesian", "Italian", "Japanese", "Kannada", "Kazakh", "Korean", "Latvian", "Lithuanian", "Macedonian", "Malay", "Marathi", "Maori", "Nepali", "Norwegian", "Persian", "Polish", "Portuguese", "Romanian", "Russian", "Serbian", "Slovak", "Slovenian", "Spanish", "Swahili", "Swedish", "Tagalog", "Tamil", "Thai", "Turkish", "Ukrainian", "Urdu", "Vietnamese", "Welsh"]

# Fonction pour convertir le nom d'une langue en code ISO 639-1
def convert_language_name_to_iso6391(language_name):
    # Dictionnaire de correspondance entre les noms de langues et les codes ISO 639-1
    language_to_iso = {
        "Afrikaans": "af", "Arabic": "ar", "Armenian": "hy", "Azerbaijani": "az",
        "Belarusian": "be", "Bosnian": "bs", "Bulgarian": "bg", "Catalan": "ca",
        "Chinese": "zh", "Croatian": "hr", "Czech": "cs", "Danish": "da",
        "Dutch": "nl", "English": "en", "Estonian": "et", "Finnish": "fi",
        "French": "fr", "Galician": "gl", "German": "de", "Greek": "el",
        "Hebrew": "he", "Hindi": "hi", "Hungarian": "hu", "Icelandic": "is",
        "Indonesian": "id", "Italian": "it", "Japanese": "ja", "Kannada": "kn",
        "Kazakh": "kk", "Korean": "ko", "Latvian": "lv", "Lithuanian": "lt",
        "Macedonian": "mk", "Malay": "ms", "Marathi": "mr", "Maori": "mi",
        "Nepali": "ne", "Norwegian": "no", "Persian": "fa", "Polish": "pl",
        "Portuguese": "pt", "Romanian": "ro", "Russian": "ru", "Serbian": "sr",
        "Slovak": "sk", "Slovenian": "sl", "Spanish": "es", "Swahili": "sw",
        "Swedish": "sv", "Tagalog": "tl", "Tamil": "ta", "Thai": "th",
        "Turkish": "tr", "Ukrainian": "uk", "Urdu": "ur", "Vietnamese": "vi",
        "Welsh": "cy"
    }
    
    # Retourne le code ISO 639-1 correspondant au nom de la langue
    return language_to_iso.get(language_name, "en")  # Par défaut, retourne 'en' si la langue n'est pas trouvée

# Fonction principale de l'application
def main():
    st.title("------- DEMORRHA -------")

    # Initialisation des variables d'état de la session
    if "language_detected" not in st.session_state:
        st.session_state["language_detected"] = None

    if "process_mode" not in st.session_state:
        st.session_state["process_mode"] = "translation"

    if "target_language" not in st.session_state:
        st.session_state.target_language = "en"

    if "selected_languages" not in st.session_state:
        st.session_state.selected_languages = [{"language": "English", "iso-639-1": "en"}]

    if "enable_tts_for_input_from_text_field" not in st.session_state:
        st.session_state["enable_tts_for_input_from_text_field"] = True

    if "enable_tts_for_input_from_audio_record" not in st.session_state:
        st.session_state["enable_tts_for_input_from_audio_record"] = True

    def init_process_mode():
        # Configuration du mode de traduction si nécessaire
        if "translation" == st.session_state["process_mode"]:
            set_mode_translation(from_lang=st.session_state.language_detected, dest_lang=st.session_state.target_language)
        

    init_process_mode()

    # Initialisation de l'historique des messages avec le prompt système
    if "messages" not in st.session_state:
        st.session_state.messages = []

    # Vérifier si un message système existe déjà dans st.session_state.messages
    if not any(message["role"] == "system" for message in st.session_state.messages):
        st.session_state.messages.insert(0, {"role": "system", "content": SYSTEM_PROMPT})

    # Interface utilisateur pour le chat textuel
    if user_input := st.chat_input("Entrez votre message ici:"):
        # Traitement du message textuel de l'utilisateur
        if None == st.session_state.language_detected:
            st.session_state.language_detected = language_detection(input_text=user_input, temperature=0.01)
        
        for cursor_selected_lang in st.session_state.selected_languages:
            # Mise à jour de la langue cible avec le code ISO 639-1 de la langue sélectionnée
            st.session_state.target_language = cursor_selected_lang["iso-639-1"]
            
            # Initialisation du mode de traitement pour la langue cible actuelle
            init_process_mode()

            # Traitement du message de l'utilisateur pour la langue cible actuelle
            process_message(user_input, 
                            operation_prompt=f"{OP_PROMPT}", 
                            tts_enabled=st.session_state.enable_tts_for_input_from_text_field)
            
        # #################################################################
        # Affichage de l'historique des messages (sauf le message système)
        for message in st.session_state.messages[1:]:
            with st.chat_message(message["role"]):
                st.markdown(message["content"])

    with st.container(border=True):
        # Interface utilisateur pour l'enregistrement audio
        st.write("Ou enregistrez votre message audio :")
        audio = audiorecorder("Cliquez pour enregistrer", "Cliquez pour arrêter l'enregistrement")


    # Traitement de l'entrée audio de l'utilisateur
    if len(audio) > 0:
        with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_audio:
            audio.export(temp_audio.name, format="wav")
            transcription = transcribe_audio(temp_audio, language=st.session_state.language_detected)
        os.unlink(temp_audio.name)  # Supprimer le fichier temporaire
        if None == st.session_state.language_detected:
            st.session_state.language_detected = language_detection(input_text=transcription, temperature=0.01)
            st.write(f"Langue détectée : {st.session_state.language_detected}")

        st.write(f"Transcription : {transcription}")

        for cursor_selected_lang in st.session_state.selected_languages:
            # Mise à jour de la langue cible avec le code ISO 639-1 de la langue sélectionnée
            st.session_state.target_language = cursor_selected_lang["iso-639-1"]
            
            # Initialisation du mode de traitement pour la langue cible actuelle
            init_process_mode()
            
            # Traitement du message de l'utilisateur pour la langue cible actuelle
            process_message(transcription, 
                            operation_prompt=f"{OP_PROMPT}", 
                            tts_enabled=st.session_state.enable_tts_for_input_from_audio_record)

    # Configuration de la barre latérale
    with st.sidebar:
        st.header("DEMORRHA - v1")
        st.markdown("## À propos")
        st.info("\n".join([
            "Cette application utilise Streamlit et l'API d'OpenAI pour créer un chat interactif avec des modèles de langages avancés dans le but de fournir un outil permettant la communication entre les êtres humains.",
            "Cet outil a pour objectif de montrer la voie dans un acte saint de la volonté de son auteur : ",
            "Abattre les barrières linguistiques entre les hommes."
        ]))

        # Fonction de rappel pour le changement de(s) langue(s) de destination selectionnée(s)
        def on_languages_change():
            print(type(st.session_state.selected_languages))
            print(st.session_state.selected_languages)
            selected_languages = [ {"language": selected_language, "iso-639-1":convert_language_name_to_iso6391(selected_language) } for selected_language in st.session_state.selected_languages ]
            st.session_state.selected_languages = selected_languages

        with st.container(border=True):
            # Conteneur pour la sélection de la langue
            st.subheader("Sélection de la langue")

 
            # Sélection multiple des langues de destination
            st.multiselect(
                label="Choisissez les langues de destination",
                placeholder="Sélectionnez une a quatre langue(s)",
                options=SUPPORTED_LANGUAGES,
                default=["English"],
                key="language_selector",
                max_selections=4,
                on_change=on_languages_change
            )

        with st.container(border=True):
            st.subheader("Paramètres TTS")
            st.checkbox(
                "Activer TTS pour les entrées textuelles",
                key="enable_tts_for_input_from_text_field",
                value=st.session_state.get("enable_tts_for_input_from_text_field", True)
            )
            st.checkbox(
                "Activer TTS pour les entrées audio",
                key="enable_tts_for_input_from_audio_record",
                value=st.session_state.get("enable_tts_for_input_from_audio_record", True)
            )
        

# Point d'entrée de l'application
if __name__ == "__main__":
    main()