rick commited on
Commit
22f4250
·
unverified ·
1 Parent(s): c24a90c

align to PEP8

Browse files
Files changed (1) hide show
  1. app.py +272 -195
app.py CHANGED
@@ -29,15 +29,13 @@ def load_ui_language(file_path: Optional[str] = "ui_lang_support.json") -> dict:
29
  with open(file_path, 'r', encoding='utf-8') as file:
30
  return json.load(file)
31
  except FileNotFoundError:
32
- print(get_translation("erreur_fichier_non_trouve").format(file_path))
33
  return {}
34
  except json.JSONDecodeError:
35
- print(get_translation("erreur_lecture_fichier").format(
36
- "JSON decoding error"
37
- ))
38
  return {}
39
- except Exception as e:
40
- print(get_translation("erreur_lecture_fichier").format(str(e)))
41
  return {}
42
 
43
  # Dictionary to store translations
@@ -73,9 +71,9 @@ def read_file(file_name):
73
  content = file.read()
74
  return content
75
  except FileNotFoundError:
76
- return get_translation("erreur_fichier_non_trouve").format(file_name)
77
- except Exception as e:
78
- return get_translation("erreur_lecture_fichier").format(str(e))
79
 
80
  def split_audio(audio_file, max_size_mb=25):
81
  """
@@ -88,22 +86,29 @@ def split_audio(audio_file, max_size_mb=25):
88
  Returns:
89
  list: List of paths to the split audio segments.
90
  """
91
- audio = AudioSegment.from_wav(audio_file)
92
- duration_ms = len(audio)
93
- segment_duration_ms = int(
94
- (max_size_mb * 1024 * 1024 * 8) /
95
- (audio.frame_rate * audio.sample_width * audio.channels)
96
- )
97
-
98
- segments = []
99
- for start in range(0, duration_ms, segment_duration_ms):
100
- end = min(start + segment_duration_ms, duration_ms)
101
- segment = audio[start:end]
102
- with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_segment:
103
- segment.export(temp_segment.name, format="wav")
104
- segments.append(temp_segment.name)
105
 
106
- return segments
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  # Fonction modifiée pour transcrire l'audio en texte
109
  def transcribe_audio(audio_file, language=None):
@@ -120,63 +125,82 @@ def transcribe_audio(audio_file, language=None):
120
  max_size_mb = 25
121
  file_size_mb = os.path.getsize(audio_file.name) / (1024 * 1024)
122
 
123
- if file_size_mb > max_size_mb:
124
- segments = split_audio(audio_file.name, max_size_mb)
125
- full_transcript = ""
126
- for segment in segments:
127
- with open(segment, "rb") as audio_segment:
 
 
 
 
 
 
 
 
 
 
 
128
  transcript = client.audio.transcriptions.create(
129
  model="whisper-1",
130
- file=audio_segment,
131
  language=language
132
  )
133
- full_transcript += transcript.text + " "
134
- os.unlink(segment) # Delete temporary file
135
- return full_transcript.strip()
136
- else:
137
- with open(audio_file.name, "rb") as audio_file:
138
- transcript = client.audio.transcriptions.create(
139
- model="whisper-1",
140
- file=audio_file,
141
- language=language
142
- )
143
- return transcript.text
144
 
145
  # Fonction pour détecter la langue d'un texte donné
146
  def detect_language(input_text, temperature=0.01):
147
  """
148
- Detect the language of a given text.
149
 
150
  Args:
151
- input_text (str): The text to detect the language from.
152
- temperature (float, optional): The temperature for the language model. Defaults to 0.01.
153
 
154
  Returns:
155
- str: The detected language in ISO-639-1 format.
 
 
 
 
156
  """
157
- system_prompt = "".join([
158
- "Act as a language detection function. ",
159
- "I will provide text in any language, and you will detect its language. ",
160
- "Provide the result of your detection in ISO-639-1 format. ",
161
- "Your response should represent the `language` argument and contain ",
162
- "only its string value. ",
163
- "Providing the input language in ISO-639-1 format will improve accuracy and latency."
164
- ])
165
- response = client.chat.completions.create(
166
- model="gpt-4o-mini",
167
- temperature=temperature,
168
- messages=[
169
- {
170
- "role": "system",
171
- "content": system_prompt
172
- },
173
- {
174
- "role": "user",
175
- "content": f"{input_text}"
176
- }
177
- ]
178
  )
179
- return response.choices[0].message.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  def get_duration_pydub(audio_file):
182
  """
@@ -188,8 +212,15 @@ def get_duration_pydub(audio_file):
188
  Returns:
189
  float: Duration of the audio file in seconds.
190
  """
191
- audio = AudioSegment.from_file(audio_file)
192
- return audio.duration_seconds
 
 
 
 
 
 
 
193
 
194
  def text_to_speech(text):
195
  """
@@ -201,60 +232,71 @@ def text_to_speech(text):
201
  Returns:
202
  tuple: A tuple containing the audio bytes and the duration of the audio in seconds.
203
  """
204
- response = client.audio.speech.create(
205
- model="tts-1",
206
- voice=st.session_state.tts_voice,
207
- input=text
208
- )
209
-
210
- # Save the audio to a temporary file
211
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_audio:
212
- response.stream_to_file(temp_audio.name)
213
 
214
- # Read the content of the audio file
215
- with open(temp_audio.name, "rb") as audio_file:
216
- audio_bytes = audio_file.read()
 
 
 
 
217
 
218
- # Get the duration of the audio in seconds
219
- audio_duration = get_duration_pydub(temp_audio.name)
220
 
221
- return audio_bytes, audio_duration
 
 
 
222
 
223
  def concatenate_audio_files(audio_list):
224
  """
225
- Concatenate multiple audio files with sound effects.
226
 
227
  Args:
228
- audio_list (list): A list of tuples, each containing audio bytes and duration.
229
 
230
  Returns:
231
- bytes: The concatenated audio as bytes.
232
  """
233
- # Create an empty audio segment
234
  final_audio = AudioSegment.empty()
235
 
236
- # Load sound effects
237
- begin_sound = AudioSegment.from_mp3(
238
- "sound-effects/voice-message-play-begin/voice-message-play-begin-1.mp3"
239
- )
240
- end_sound = AudioSegment.from_mp3(
241
- "sound-effects/voice-message-play-ending/voice-message-play-ending-1.mp3"
242
- )
243
-
244
- # 5 seconds of silence
245
- silence = AudioSegment.silent(duration=5000) # 5000 ms = 5 seconds
246
-
247
- for audio_bytes, _ in audio_list:
248
- # Convert bytes to an audio segment
249
- segment = AudioSegment.from_mp3(io.BytesIO(audio_bytes))
250
 
251
- # Add the start sound, TTS segment, end sound, and silence
252
- final_audio += begin_sound + segment + end_sound + silence
253
-
254
- # Convert the final audio segment to bytes
255
- buffer = io.BytesIO()
256
- final_audio.export(buffer, format="mp3")
257
- return buffer.getvalue()
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
  def process_message(message, operation_prompt="", tts_enabled=False):
260
  """
@@ -268,7 +310,7 @@ def process_message(message, operation_prompt="", tts_enabled=False):
268
  Returns:
269
  tuple: A tuple containing the TTS audio and its duration, or (None, None) if TTS is disabled.
270
  """
271
- payload_content = f'{operation_prompt} :\n"""\n{message}\n"""'
272
 
273
  st.session_state.messages.append({"role": "user", "content": payload_content})
274
  with st.chat_message("user"):
@@ -277,24 +319,32 @@ def process_message(message, operation_prompt="", tts_enabled=False):
277
  with st.chat_message("assistant"):
278
  message_placeholder = st.empty()
279
  full_response = ""
280
- for response in client.chat.completions.create(
281
- model="gpt-4o-mini",
282
- messages=st.session_state.messages,
283
- stream=True,
284
- temperature=0.1):
285
- full_response += (response.choices[0].delta.content or "")
286
- message_placeholder.markdown(full_response + "")
287
- # Use regex to remove the first and last three double quotes
288
- full_response = re.sub(r'^"{3}|"{3}$', '', full_response.strip())
289
- message_placeholder.markdown(full_response)
 
 
 
 
290
 
291
  st.session_state.messages.append(
292
  {"role": "assistant", "content": full_response}
293
  )
294
 
295
  if tts_enabled:
296
- tts_audio, tts_duration = text_to_speech(full_response)
297
- return tts_audio, tts_duration
 
 
 
 
298
  return None, None
299
 
300
  class GlobalSystemPrompts:
@@ -308,8 +358,15 @@ class GlobalSystemPrompts:
308
  Returns:
309
  str: The system prompt for Linguascribe.
310
  """
311
- system_prompt = f"{read_file('linguascribe.prompt')}"
312
- return system_prompt
 
 
 
 
 
 
 
313
 
314
  # Function to configure the translation mode
315
  def set_translation_mode(from_lang, dest_lang):
@@ -321,7 +378,7 @@ def set_translation_mode(from_lang, dest_lang):
321
  dest_lang (str): The destination language.
322
  """
323
  system_prompt = GlobalSystemPrompts.linguascribe()
324
- operation_prompt = f"Translate({from_lang} to {dest_lang})"
325
  return system_prompt, operation_prompt
326
 
327
  # List of languages supported by the application
@@ -337,17 +394,17 @@ SUPPORTED_LANGUAGES = [
337
  "Turkish", "Ukrainian", "Urdu", "Vietnamese", "Welsh"
338
  ]
339
 
340
- def convert_language_name_to_iso6391(language_data):
341
  """
342
- Convert a language name to its ISO 639-1 code.
343
 
344
  Args:
345
- language_data (Union[str, dict]): The language name or a dictionary containing the language name.
346
 
347
  Returns:
348
- str: The ISO 639-1 code for the given language, or 'en' if not found.
349
  """
350
- # Dictionary mapping language names to ISO 639-1 codes
351
  language_to_iso = {
352
  "Afrikaans": "af", "Arabic": "ar", "Armenian": "hy", "Azerbaijani": "az",
353
  "Belarusian": "be", "Bosnian": "bs", "Bulgarian": "bg", "Catalan": "ca",
@@ -366,21 +423,26 @@ def convert_language_name_to_iso6391(language_data):
366
  "Welsh": "cy"
367
  }
368
 
369
- # Check if language_data is a dictionary
370
  if isinstance(language_data, dict):
371
  language_name = language_data.get('language')
372
  else:
373
  language_name = language_data
374
 
375
- # Return the ISO 639-1 code corresponding to the language name
376
- return language_to_iso.get(language_name, "en") # Default to 'en' if the language is not found
377
-
378
- # Main function of the application
 
 
 
 
 
379
  def main():
380
- """Main function that sets up and runs the Streamlit application."""
381
  st.title("------- DEMORRHA -------")
382
 
383
- # Initialize session state variables
384
  if "language_detected" not in st.session_state:
385
  st.session_state["language_detected"] = None
386
 
@@ -402,10 +464,10 @@ def main():
402
  st.session_state["enable_tts_for_input_from_audio_record"] = True
403
 
404
  if "interface_language" not in st.session_state:
405
- st.session_state.interface_language = "French" # Default language
406
 
407
  def init_process_mode():
408
- """Initialize the process mode for translation if necessary."""
409
  if "translation" == st.session_state["process_mode"]:
410
  system_prompt, operation_prompt = set_translation_mode(
411
  from_lang=st.session_state.language_detected,
@@ -416,17 +478,17 @@ def main():
416
 
417
  system_prompt, operation_prompt = init_process_mode()
418
 
419
- # Initialize message history with system prompt
420
  if "messages" not in st.session_state:
421
  st.session_state.messages = []
422
 
423
- # Check if a system message already exists in st.session_state.messages
424
  if not any(message["role"] == "system" for message in st.session_state.messages):
425
  st.session_state.messages.insert(0, {"role": "system", "content": system_prompt})
426
 
427
- # User interface for text chat
428
  if user_input := st.chat_input(get_translation("entrez_message")):
429
- # Process user's text message
430
  if st.session_state.language_detected is None:
431
  st.session_state.language_detected = detect_language(
432
  input_text=user_input, temperature=0.01
@@ -436,24 +498,30 @@ def main():
436
  for cursor_selected_lang in st.session_state.selected_languages:
437
  st.session_state.target_language = cursor_selected_lang["iso-639-1"]
438
 
439
- # Initialize processing mode for current target language
440
  system_prompt, operation_prompt = init_process_mode()
441
 
442
- # Process user message for current target language
443
- tts_audio, tts_duration = process_message(
444
- user_input,
445
- operation_prompt=f"{operation_prompt}",
446
- tts_enabled=st.session_state.enable_tts_for_input_from_text_field
447
- )
448
- if tts_audio is not None:
449
- audio_list.append((tts_audio, tts_duration))
 
 
 
450
 
451
  if audio_list:
452
- final_audio = concatenate_audio_files(audio_list)
453
- st.audio(final_audio, format="audio/mp3", autoplay=True)
 
 
 
454
 
455
  with st.container(border=True):
456
- # User interface for audio recording
457
  st.write(get_translation("enregistrez_message"))
458
  audio = audiorecorder(
459
  start_prompt=get_translation("cliquez_enregistrer"),
@@ -463,47 +531,56 @@ def main():
463
  key="vocal_chat_input"
464
  )
465
 
466
- # Process user's audio input
467
  if len(audio) > 0:
468
- with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_audio:
469
- audio.export(temp_audio.name, format="wav")
470
- transcription = transcribe_audio(temp_audio, language=st.session_state.language_detected)
471
- os.unlink(temp_audio.name) # Delete temporary file
472
- if st.session_state.language_detected is None:
473
- st.session_state.language_detected = detect_language(
474
- input_text=transcription, temperature=0.01
475
- )
476
- st.write(get_translation("langue_detectee").format(st.session_state.language_detected))
477
-
478
- st.write(get_translation("transcription").format(transcription))
479
-
480
- audio_list = []
481
- for cursor_selected_lang in st.session_state.selected_languages:
482
- st.session_state.target_language = cursor_selected_lang["iso-639-1"]
483
-
484
- # Initialize processing mode for current target language
485
- system_prompt, operation_prompt = init_process_mode()
486
-
487
- # Process user message for current target language
488
- tts_audio, tts_duration = process_message(
489
- transcription,
490
- operation_prompt=f"{operation_prompt}",
491
- tts_enabled=st.session_state.enable_tts_for_input_from_audio_record
492
- )
493
- if tts_audio is not None:
494
- audio_list.append((tts_audio, tts_duration))
495
- if audio_list:
496
- final_audio = concatenate_audio_files(audio_list)
497
- st.audio(final_audio, format="audio/mp3", autoplay=True)
498
-
499
- # Sidebar configuration
 
 
 
 
 
 
 
 
 
500
  with st.sidebar:
501
  st.header(get_translation("sidebar_titre"))
502
- st.markdown("## " + get_translation("a_propos"))
503
  st.info(get_translation("info_app"))
504
 
505
  def on_languages_change():
506
- """Callback function for destination language(s) change."""
507
  selected_language_names = st.session_state.language_selector
508
  st.session_state.selected_languages = [
509
  {"language": lang, "iso-639-1": convert_language_name_to_iso6391(lang)}
@@ -512,7 +589,7 @@ def main():
512
 
513
  with st.container(border=True):
514
  st.subheader(get_translation("langue_interface"))
515
- # Interface language selection
516
  st.selectbox(
517
  label=get_translation("choix_langue_interface"),
518
  options=list(translations.keys()),
@@ -525,10 +602,10 @@ def main():
525
  )
526
 
527
  with st.container(border=True):
528
- # Container for language selection
529
  st.subheader(get_translation("selection_langue"))
530
 
531
- # Multiple selection of destination languages
532
  st.multiselect(
533
  label=get_translation("langues_destination"),
534
  placeholder=get_translation("placeholder_langues"),
@@ -544,7 +621,7 @@ def main():
544
  st.selectbox(
545
  get_translation("choix_voix_tts"),
546
  options=["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
547
- index=3, # "onyx" is at index 3
548
  key="tts_voice"
549
  )
550
  st.checkbox(
@@ -556,6 +633,6 @@ def main():
556
  key="enable_tts_for_input_from_audio_record"
557
  )
558
 
559
- # Application entry point
560
  if __name__ == "__main__":
561
  main()
 
29
  with open(file_path, 'r', encoding='utf-8') as file:
30
  return json.load(file)
31
  except FileNotFoundError:
32
+ print(f"{get_translation('erreur_fichier_non_trouve')} {file_path}")
33
  return {}
34
  except json.JSONDecodeError:
35
+ print(f"{get_translation('erreur_lecture_fichier')} JSON decoding error")
 
 
36
  return {}
37
+ except IOError as e:
38
+ print(f"{get_translation('erreur_lecture_fichier')} {e}")
39
  return {}
40
 
41
  # Dictionary to store translations
 
71
  content = file.read()
72
  return content
73
  except FileNotFoundError:
74
+ return f"{get_translation('erreur_fichier_non_trouve')} {file_name}"
75
+ except IOError as e:
76
+ return f"{get_translation('erreur_lecture_fichier')} {str(e)}"
77
 
78
  def split_audio(audio_file, max_size_mb=25):
79
  """
 
86
  Returns:
87
  list: List of paths to the split audio segments.
88
  """
89
+ try:
90
+ audio = AudioSegment.from_wav(audio_file)
91
+ duration_ms = len(audio)
92
+ segment_duration_ms = int(
93
+ (max_size_mb * 1024 * 1024 * 8) /
94
+ (audio.frame_rate * audio.sample_width * audio.channels)
95
+ )
 
 
 
 
 
 
 
96
 
97
+ segments = []
98
+ for start in range(0, duration_ms, segment_duration_ms):
99
+ end = min(start + segment_duration_ms, duration_ms)
100
+ segment = audio[start:end]
101
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_segment:
102
+ segment.export(temp_segment.name, format="wav")
103
+ segments.append(temp_segment.name)
104
+
105
+ return segments
106
+ except IOError as e:
107
+ print(f"Erreur lors de la lecture ou de l'écriture du fichier audio : {e}")
108
+ return []
109
+ except ValueError as e:
110
+ print(f"Erreur de valeur lors du traitement de l'audio : {e}")
111
+ return []
112
 
113
  # Fonction modifiée pour transcrire l'audio en texte
114
  def transcribe_audio(audio_file, language=None):
 
125
  max_size_mb = 25
126
  file_size_mb = os.path.getsize(audio_file.name) / (1024 * 1024)
127
 
128
+ try:
129
+ if file_size_mb > max_size_mb:
130
+ segments = split_audio(audio_file.name, max_size_mb)
131
+ full_transcript = ""
132
+ for segment in segments:
133
+ with open(segment, "rb") as audio_segment:
134
+ transcript = client.audio.transcriptions.create(
135
+ model="whisper-1",
136
+ file=audio_segment,
137
+ language=language
138
+ )
139
+ full_transcript += f"{transcript.text} "
140
+ os.unlink(segment) # Delete temporary file
141
+ return full_transcript.strip()
142
+ else:
143
+ with open(audio_file.name, "rb") as audio_file:
144
  transcript = client.audio.transcriptions.create(
145
  model="whisper-1",
146
+ file=audio_file,
147
  language=language
148
  )
149
+ return transcript.text
150
+ except IOError as e:
151
+ print(f"Erreur d'entrée/sortie lors de la transcription : {e}")
152
+ return ""
153
+ except client.APIError as e:
154
+ print(f"Erreur API lors de la transcription : {e}")
155
+ return ""
 
 
 
 
156
 
157
  # Fonction pour détecter la langue d'un texte donné
158
  def detect_language(input_text, temperature=0.01):
159
  """
160
+ Détecte la langue d'un texte donné.
161
 
162
  Args:
163
+ input_text (str): Le texte dont il faut détecter la langue.
164
+ temperature (float, optional): La température pour le modèle de langage. Par défaut à 0.01.
165
 
166
  Returns:
167
+ str: La langue détectée au format ISO-639-1.
168
+
169
+ Raises:
170
+ ValueError: Si la réponse de l'API est invalide.
171
+ requests.RequestException: En cas d'erreur de communication avec l'API.
172
  """
173
+ system_prompt = (
174
+ "Agissez comme une fonction de détection de langue. "
175
+ "Je fournirai du texte dans n'importe quelle langue, et vous détecterez sa langue. "
176
+ "Fournissez le résultat de votre détection au format ISO-639-1. "
177
+ "Votre réponse doit représenter l'argument `language` et ne contenir "
178
+ "que sa valeur sous forme de chaîne. "
179
+ "Fournir la langue d'entrée au format ISO-639-1 améliorera la précision et la latence."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  )
181
+ try:
182
+ response = client.chat.completions.create(
183
+ model="gpt-4o-mini",
184
+ temperature=temperature,
185
+ messages=[
186
+ {
187
+ "role": "system",
188
+ "content": system_prompt
189
+ },
190
+ {
191
+ "role": "user",
192
+ "content": input_text
193
+ }
194
+ ]
195
+ )
196
+ detected_language = response.choices[0].message.content
197
+ if not detected_language:
198
+ raise ValueError("La réponse de l'API est vide")
199
+ return detected_language
200
+ except requests.RequestException as e:
201
+ raise requests.RequestException(f"Erreur de communication avec l'API : {str(e)}")
202
+ except Exception as e:
203
+ raise ValueError(f"Erreur inattendue lors de la détection de la langue : {str(e)}")
204
 
205
  def get_duration_pydub(audio_file):
206
  """
 
212
  Returns:
213
  float: Duration of the audio file in seconds.
214
  """
215
+ try:
216
+ audio = AudioSegment.from_file(audio_file)
217
+ return audio.duration_seconds
218
+ except FileNotFoundError:
219
+ print(f"Erreur : Le fichier audio '{audio_file}' n'a pas été trouvé.")
220
+ return 0.0
221
+ except Exception as e:
222
+ print(f"Erreur lors de la lecture du fichier audio : {str(e)}")
223
+ return 0.0
224
 
225
  def text_to_speech(text):
226
  """
 
232
  Returns:
233
  tuple: A tuple containing the audio bytes and the duration of the audio in seconds.
234
  """
235
+ try:
236
+ response = client.audio.speech.create(
237
+ model="tts-1",
238
+ voice=st.session_state.tts_voice,
239
+ input=text
240
+ )
 
 
 
241
 
242
+ # Save the audio to a temporary file
243
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_audio:
244
+ response.stream_to_file(temp_audio.name)
245
+
246
+ # Read the content of the audio file
247
+ with open(temp_audio.name, "rb") as audio_file:
248
+ audio_bytes = audio_file.read()
249
 
250
+ # Get the duration of the audio in seconds
251
+ audio_duration = get_duration_pydub(temp_audio.name)
252
 
253
+ return audio_bytes, audio_duration
254
+ except Exception as e:
255
+ print(f"Erreur lors de la conversion texte-parole : {str(e)}")
256
+ return None, 0.0
257
 
258
  def concatenate_audio_files(audio_list):
259
  """
260
+ Concatène plusieurs fichiers audio avec des effets sonores.
261
 
262
  Args:
263
+ audio_list (list): Une liste de tuples, chacun contenant des octets audio et la durée.
264
 
265
  Returns:
266
+ bytes: L'audio concaténé sous forme d'octets.
267
  """
268
+ # Créer un segment audio vide
269
  final_audio = AudioSegment.empty()
270
 
271
+ try:
272
+ # Charger les effets sonores
273
+ begin_sound = AudioSegment.from_mp3(
274
+ "sound-effects/voice-message-play-begin/voice-message-play-begin-1.mp3"
275
+ )
276
+ end_sound = AudioSegment.from_mp3(
277
+ "sound-effects/voice-message-play-ending/voice-message-play-ending-1.mp3"
278
+ )
 
 
 
 
 
 
279
 
280
+ # 5 secondes de silence
281
+ silence = AudioSegment.silent(duration=5000) # 5000 ms = 5 secondes
282
+
283
+ for audio_bytes, _ in audio_list:
284
+ # Convertir les octets en un segment audio
285
+ segment = AudioSegment.from_mp3(io.BytesIO(audio_bytes))
286
+
287
+ # Ajouter le son de début, le segment TTS, le son de fin et le silence
288
+ final_audio += begin_sound + segment + end_sound + silence
289
+
290
+ # Convertir le segment audio final en octets
291
+ buffer = io.BytesIO()
292
+ final_audio.export(buffer, format="mp3")
293
+ return buffer.getvalue()
294
+ except IOError as e:
295
+ print(f"Erreur lors de la lecture ou de l'écriture des fichiers audio : {e}")
296
+ return None
297
+ except Exception as e:
298
+ print(f"Une erreur inattendue s'est produite : {e}")
299
+ return None
300
 
301
  def process_message(message, operation_prompt="", tts_enabled=False):
302
  """
 
310
  Returns:
311
  tuple: A tuple containing the TTS audio and its duration, or (None, None) if TTS is disabled.
312
  """
313
+ payload_content = '{} :\n"""\n{}\n"""'.format(operation_prompt, message)
314
 
315
  st.session_state.messages.append({"role": "user", "content": payload_content})
316
  with st.chat_message("user"):
 
319
  with st.chat_message("assistant"):
320
  message_placeholder = st.empty()
321
  full_response = ""
322
+ try:
323
+ for response in client.chat.completions.create(
324
+ model="gpt-4o-mini",
325
+ messages=st.session_state.messages,
326
+ stream=True,
327
+ temperature=0.1):
328
+ full_response += (response.choices[0].delta.content or "")
329
+ message_placeholder.markdown(full_response + "▌")
330
+ # Use regex to remove the first and last three double quotes
331
+ full_response = re.sub(r'^"{3}|"{3}$', '', full_response.strip())
332
+ message_placeholder.markdown(full_response)
333
+ except Exception as e:
334
+ st.error("Une erreur s'est produite lors de la génération de la réponse : {}".format(str(e)))
335
+ return None, None
336
 
337
  st.session_state.messages.append(
338
  {"role": "assistant", "content": full_response}
339
  )
340
 
341
  if tts_enabled:
342
+ try:
343
+ tts_audio, tts_duration = text_to_speech(full_response)
344
+ return tts_audio, tts_duration
345
+ except Exception as e:
346
+ st.error("Une erreur s'est produite lors de la conversion texte-parole : {}".format(str(e)))
347
+ return None, None
348
  return None, None
349
 
350
  class GlobalSystemPrompts:
 
358
  Returns:
359
  str: The system prompt for Linguascribe.
360
  """
361
+ try:
362
+ system_prompt = read_file('linguascribe.prompt')
363
+ return system_prompt
364
+ except FileNotFoundError:
365
+ print("Le fichier 'linguascribe.prompt' n'a pas été trouvé.")
366
+ return ""
367
+ except IOError as e:
368
+ print(f"Erreur lors de la lecture du fichier 'linguascribe.prompt': {e}")
369
+ return ""
370
 
371
  # Function to configure the translation mode
372
  def set_translation_mode(from_lang, dest_lang):
 
378
  dest_lang (str): The destination language.
379
  """
380
  system_prompt = GlobalSystemPrompts.linguascribe()
381
+ operation_prompt = "Translate({} to {})".format(from_lang, dest_lang)
382
  return system_prompt, operation_prompt
383
 
384
  # List of languages supported by the application
 
394
  "Turkish", "Ukrainian", "Urdu", "Vietnamese", "Welsh"
395
  ]
396
 
397
+ def convert_language_name_to_iso6391(language_data: Union[str, dict]) -> str:
398
  """
399
+ Convertit un nom de langue en son code ISO 639-1.
400
 
401
  Args:
402
+ language_data (Union[str, dict]): Le nom de la langue ou un dictionnaire contenant le nom de la langue.
403
 
404
  Returns:
405
+ str: Le code ISO 639-1 pour la langue donnée, ou 'en' si non trouvé.
406
  """
407
+ # Dictionnaire associant les noms de langues aux codes ISO 639-1
408
  language_to_iso = {
409
  "Afrikaans": "af", "Arabic": "ar", "Armenian": "hy", "Azerbaijani": "az",
410
  "Belarusian": "be", "Bosnian": "bs", "Bulgarian": "bg", "Catalan": "ca",
 
423
  "Welsh": "cy"
424
  }
425
 
426
+ # Vérifier si language_data est un dictionnaire
427
  if isinstance(language_data, dict):
428
  language_name = language_data.get('language')
429
  else:
430
  language_name = language_data
431
 
432
+ try:
433
+ # Retourner le code ISO 639-1 correspondant au nom de la langue
434
+ return language_to_iso[language_name]
435
+ except KeyError:
436
+ # Gérer spécifiquement l'exception KeyError
437
+ print(f"Langue non trouvée : {language_name}")
438
+ return "en" # Par défaut 'en' si la langue n'est pas trouvée
439
+
440
+ # Fonction principale de l'application
441
  def main():
442
+ """Fonction principale qui configure et exécute l'application Streamlit."""
443
  st.title("------- DEMORRHA -------")
444
 
445
+ # Initialisation des variables d'état de session
446
  if "language_detected" not in st.session_state:
447
  st.session_state["language_detected"] = None
448
 
 
464
  st.session_state["enable_tts_for_input_from_audio_record"] = True
465
 
466
  if "interface_language" not in st.session_state:
467
+ st.session_state.interface_language = "French" # Langue par défaut
468
 
469
  def init_process_mode():
470
+ """Initialise le mode de traitement pour la traduction si nécessaire."""
471
  if "translation" == st.session_state["process_mode"]:
472
  system_prompt, operation_prompt = set_translation_mode(
473
  from_lang=st.session_state.language_detected,
 
478
 
479
  system_prompt, operation_prompt = init_process_mode()
480
 
481
+ # Initialisation de l'historique des messages avec le prompt système
482
  if "messages" not in st.session_state:
483
  st.session_state.messages = []
484
 
485
+ # Vérification de l'existence d'un message système dans st.session_state.messages
486
  if not any(message["role"] == "system" for message in st.session_state.messages):
487
  st.session_state.messages.insert(0, {"role": "system", "content": system_prompt})
488
 
489
+ # Interface utilisateur pour le chat textuel
490
  if user_input := st.chat_input(get_translation("entrez_message")):
491
+ # Traitement du message texte de l'utilisateur
492
  if st.session_state.language_detected is None:
493
  st.session_state.language_detected = detect_language(
494
  input_text=user_input, temperature=0.01
 
498
  for cursor_selected_lang in st.session_state.selected_languages:
499
  st.session_state.target_language = cursor_selected_lang["iso-639-1"]
500
 
501
+ # Initialisation du mode de traitement pour la langue cible actuelle
502
  system_prompt, operation_prompt = init_process_mode()
503
 
504
+ # Traitement du message utilisateur pour la langue cible actuelle
505
+ try:
506
+ tts_audio, tts_duration = process_message(
507
+ user_input,
508
+ operation_prompt=f"{operation_prompt}",
509
+ tts_enabled=st.session_state.enable_tts_for_input_from_text_field
510
+ )
511
+ if tts_audio is not None:
512
+ audio_list.append((tts_audio, tts_duration))
513
+ except Exception as e:
514
+ st.error(f"Erreur lors du traitement du message : {str(e)}")
515
 
516
  if audio_list:
517
+ try:
518
+ final_audio = concatenate_audio_files(audio_list)
519
+ st.audio(final_audio, format="audio/mp3", autoplay=True)
520
+ except Exception as e:
521
+ st.error(f"Erreur lors de la concaténation des fichiers audio : {str(e)}")
522
 
523
  with st.container(border=True):
524
+ # Interface utilisateur pour l'enregistrement audio
525
  st.write(get_translation("enregistrez_message"))
526
  audio = audiorecorder(
527
  start_prompt=get_translation("cliquez_enregistrer"),
 
531
  key="vocal_chat_input"
532
  )
533
 
534
+ # Traitement de l'entrée audio de l'utilisateur
535
  if len(audio) > 0:
536
+ try:
537
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as temp_audio:
538
+ audio.export(temp_audio.name, format="wav")
539
+ transcription = transcribe_audio(temp_audio, language=st.session_state.language_detected)
540
+ os.unlink(temp_audio.name) # Suppression du fichier temporaire
541
+ if st.session_state.language_detected is None:
542
+ st.session_state.language_detected = detect_language(
543
+ input_text=transcription, temperature=0.01
544
+ )
545
+ st.write(get_translation("langue_detectee").format(st.session_state.language_detected))
546
+
547
+ st.write(get_translation("transcription").format(transcription))
548
+
549
+ audio_list = []
550
+ for cursor_selected_lang in st.session_state.selected_languages:
551
+ st.session_state.target_language = cursor_selected_lang["iso-639-1"]
552
+
553
+ # Initialisation du mode de traitement pour la langue cible actuelle
554
+ system_prompt, operation_prompt = init_process_mode()
555
+
556
+ # Traitement du message utilisateur pour la langue cible actuelle
557
+ try:
558
+ tts_audio, tts_duration = process_message(
559
+ transcription,
560
+ operation_prompt=f"{operation_prompt}",
561
+ tts_enabled=st.session_state.enable_tts_for_input_from_audio_record
562
+ )
563
+ if tts_audio is not None:
564
+ audio_list.append((tts_audio, tts_duration))
565
+ except Exception as e:
566
+ st.error(f"Erreur lors du traitement du message audio : {str(e)}")
567
+ if audio_list:
568
+ try:
569
+ final_audio = concatenate_audio_files(audio_list)
570
+ st.audio(final_audio, format="audio/mp3", autoplay=True)
571
+ except Exception as e:
572
+ st.error(f"Erreur lors de la concaténation des fichiers audio : {str(e)}")
573
+ except Exception as e:
574
+ st.error(f"Erreur lors du traitement de l'audio : {str(e)}")
575
+
576
+ # Configuration de la barre latérale
577
  with st.sidebar:
578
  st.header(get_translation("sidebar_titre"))
579
+ st.markdown(f"## {get_translation('a_propos')}")
580
  st.info(get_translation("info_app"))
581
 
582
  def on_languages_change():
583
+ """Fonction de rappel pour le changement de langue(s) de destination."""
584
  selected_language_names = st.session_state.language_selector
585
  st.session_state.selected_languages = [
586
  {"language": lang, "iso-639-1": convert_language_name_to_iso6391(lang)}
 
589
 
590
  with st.container(border=True):
591
  st.subheader(get_translation("langue_interface"))
592
+ # Sélection de la langue de l'interface
593
  st.selectbox(
594
  label=get_translation("choix_langue_interface"),
595
  options=list(translations.keys()),
 
602
  )
603
 
604
  with st.container(border=True):
605
+ # Conteneur pour la sélection de langue
606
  st.subheader(get_translation("selection_langue"))
607
 
608
+ # Sélection multiple des langues de destination
609
  st.multiselect(
610
  label=get_translation("langues_destination"),
611
  placeholder=get_translation("placeholder_langues"),
 
621
  st.selectbox(
622
  get_translation("choix_voix_tts"),
623
  options=["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
624
+ index=3, # "onyx" est à l'index 3
625
  key="tts_voice"
626
  )
627
  st.checkbox(
 
633
  key="enable_tts_for_input_from_audio_record"
634
  )
635
 
636
+ # Point d'entrée de l'application
637
  if __name__ == "__main__":
638
  main()