sawadogosalif commited on
Commit
e102b16
·
1 Parent(s): 8b2823e
.gitattributes CHANGED
@@ -18,6 +18,7 @@
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
  *.pickle filter=lfs diff=lfs merge=lfs -text
 
21
  *.pt filter=lfs diff=lfs merge=lfs -text
22
  *.pth filter=lfs diff=lfs merge=lfs -text
23
  *.rar filter=lfs diff=lfs merge=lfs -text
 
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
  *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
.gitignore DELETED
@@ -1 +0,0 @@
1
- .idea
 
 
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+
4
+ RUN apt-get update && apt-get install -y --no-install-recommends \
5
+ git \
6
+ && apt-get clean \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ RUN useradd -m -u 1000 user
10
+ USER user
11
+ ENV HOME="/home/user"
12
+ ENV PATH="${HOME}/.local/bin:$PATH"
13
+ WORKDIR $HOME/app
14
+
15
+
16
+
17
+ # --- ✅ Cloner le repo privé avec authentification
18
+ # --- https://huggingface.co/docs/hub/en/spaces-sdks-docker#secrets-and-variables-management
19
+ RUN --mount=type=secret,id=GITHUB_TOKEN,mode=0444,required=true \
20
+ git clone https://sawadogosalif:$(cat /run/secrets/GITHUB_TOKEN)@github.com/BurkimbIA/spaces.git
21
+
22
+
23
+ RUN cp -r leaderboards/mt/* .
24
+
25
+ RUN pip install -r requirements.txt
26
+
27
+
28
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,12 +1,12 @@
1
  ---
2
- title: MooreFr SaChi Demo
3
- emoji: 💬
4
  colorFrom: red
5
  colorTo: green
6
- sdk: gradio
7
- sdk_version: 5.34.2
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
- An example chatbot using [Gradio](https://gradio.app), [`huggingface_hub`](https://huggingface.co/docs/huggingface_hub/v0.22.2/en/index), and the [Hugging Face Inference API](https://huggingface.co/docs/api-inference/index).
 
1
  ---
2
+ title: Moore MT Leaderboard
3
+ emoji: 🚗
4
  colorFrom: red
5
  colorTo: green
6
+ sdk: docker
 
7
  app_file: app.py
8
  pinned: false
9
+ short_description: Text2text Machine Translation for Moore language
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py DELETED
@@ -1,285 +0,0 @@
1
- import re
2
- import gradio as gr
3
- import spaces
4
- from inference import NLLBTranslator, MistralTranslator, ParlerTTSGenerator
5
- from utils import load_css
6
- from loguru import logger
7
-
8
-
9
-
10
-
11
- translator_nllb = NLLBTranslator()
12
- translator_mistral = MistralTranslator("sawadogosalif/SaChi_by_Mistral")
13
- tts_generator = ParlerTTSGenerator("burkimbia/BIA-ParlerTTS-mini")
14
-
15
- @spaces.GPU(duration=15)
16
- def translate_text(message, model_choice, src_lang, tgt_lang):
17
- """Fonction de traduction"""
18
-
19
- if model_choice == "SaChi-MT0.5 (NLLB)":
20
- translator = translator_nllb
21
- else:
22
- translator = translator_mistral
23
-
24
- return translator.translate(
25
- text=message,
26
- src_lang=src_lang,
27
- tgt_lang=tgt_lang
28
- )
29
-
30
- @spaces.GPU(duration=15)
31
- def tts_speech(text, speaker_choice):
32
- """Fonction de génération de la synthèse vocale"""
33
-
34
- sample_rate, audio_arr = tts_generator.generate_speech(
35
- text,
36
- speaker_type=speaker_choice
37
- )
38
-
39
- if audio_arr is not None:
40
- return sample_rate, audio_arr
41
- return None
42
-
43
- @spaces.GPU(duration=30)
44
- def response_with_tts(message, history, model_choice, direction_choice, enable_tts, speaker_choice):
45
- """Fonction de réponse avec traduction et TTS optionnel"""
46
-
47
- if direction_choice == "French to Moore":
48
- src_lang, tgt_lang = "fra_Latn", "moor_Latn"
49
- else:
50
- src_lang, tgt_lang = "moor_Latn", "fra_Latn"
51
-
52
- try:
53
- translated_text = translate_text(message, model_choice, src_lang, tgt_lang)
54
- except Exception as e:
55
- logger.error(f"Erreur de traduction: {str(e)}")
56
- return f"Erreur de traduction: {str(e)}", None
57
-
58
- # Génération TTS si activée et si on traduit vers le mooré
59
- audio_output = None
60
- if enable_tts and direction_choice == "French to Moore" and translated_text:
61
- try:
62
- logger.info("Génération de la synthèse vocale...")
63
- tts_result = tts_speech(translated_text, speaker_choice)
64
-
65
- if tts_result is not None:
66
- audio_output = tts_result
67
- logger.info("Synthèse vocale générée avec succès")
68
- else:
69
- logger.warning("Échec de la génération TTS")
70
-
71
- except Exception as e:
72
- logger.error(f"Erreur TTS: {str(e)}")
73
-
74
- return translated_text, audio_output
75
-
76
-
77
- # Thème personnalisé
78
- theme = gr.themes.Soft(
79
- primary_hue=gr.themes.colors.purple,
80
- secondary_hue=gr.themes.colors.blue,
81
- neutral_hue=gr.themes.colors.slate,
82
- font=gr.themes.GoogleFont("Inter"),
83
- radius_size=gr.themes.sizes.radius_lg,
84
- )
85
-
86
- with gr.Blocks(theme=theme, css=load_css(), title="SaChi Multi-Modèles Demo") as demo:
87
-
88
- # En-tête principal
89
- with gr.Row(elem_classes="main-container"):
90
- with gr.Column():
91
- gr.HTML("""
92
- <div class="header-section">
93
- <h1 style="margin: 0; font-size: 2.5em; font-weight: 700;">
94
- 🎯 SaChi Multi-Modèles Demo avec Synthèse Vocale
95
- </h1>
96
- <div style="margin-top: 20px;">
97
- <div class="feature-item" style="justify-content: center;">
98
- <span class="feature-icon">🔄</span>
99
- <span>Traduction bidirectionnelle Français ↔ Mooré</span>
100
- </div>
101
- <div class="feature-item" style="justify-content: center;">
102
- <span class="feature-icon">🗣️</span>
103
- <span>Synthèse vocale en mooré avec différentes voix</span>
104
- </div>
105
- </div>
106
- <div style="margin-top: 25px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.2);">
107
- <div style="display: flex; justify-content: center; gap: 30px; flex-wrap: wrap;">
108
- <div class="model-item" style="color: rgba(255,255,255,0.9);">
109
- <span class="feature-icon">📝</span>
110
- <strong>Traduction:</strong> SaChi-MT0.5 (NLLB) / SaChi-MT1.5 (Mistral-Instruct)
111
- </div>
112
- <div class="model-item" style="color: rgba(255,255,255,0.9);">
113
- <span class="feature-icon">🎤</span>
114
- <strong>TTS:</strong> BIA-ParlerTTS-mini (mooré uniquement)
115
- </div>
116
- </div>
117
- </div>
118
- </div>
119
- """)
120
-
121
- # Interface principale
122
- with gr.Row(elem_classes="main-container equal-height-row"):
123
- # Colonne de gauche - Configuration
124
- with gr.Column(scale=1, elem_classes="equal-height-column"):
125
- with gr.Column(elem_classes="config-card"):
126
- gr.HTML('<h2 class="section-title"><span class="section-icon">⚙️</span>Configuration</h2>')
127
-
128
- model_choice = gr.Dropdown(
129
- choices=["SaChi-MT0.5 (NLLB)", "SaChi-MT1.5 (Mistral-Instruct)"],
130
- label="🤖 Modèle de traduction",
131
- value="SaChi-MT1.5 (Mistral-Instruct)",
132
- info="Choisissez le modèle de traduction",
133
- container=True
134
- )
135
-
136
- direction_choice = gr.Dropdown(
137
- choices=["French to Moore", "Moore to French"],
138
- label="🔄 Direction de traduction",
139
- value="French to Moore",
140
- info="Sens de la traduction",
141
- container=True
142
- )
143
-
144
- gr.HTML('<hr style="margin: 20px 0; border: none; border-top: 1px solid #e2e8f0;">')
145
-
146
- enable_tts = gr.Checkbox(
147
- label="🎤 Activer la synthèse vocale",
148
- value=True,
149
- info="Uniquement pour français → mooré"
150
- )
151
-
152
- speaker_choice = gr.Dropdown(
153
- choices=["default", "male", "female"],
154
- label="👤 Type de voix",
155
- value="default",
156
- info="Type de locuteur pour le TTS",
157
- visible=True,
158
- container=True
159
- )
160
-
161
- # Colonne de droite - Interface de traduction
162
- with gr.Column(scale=2, elem_classes="equal-height-column"):
163
- with gr.Column(elem_classes="translation-card"):
164
- gr.HTML('<h2 class="section-title"><span class="section-icon">💬</span>Traduction</h2>')
165
-
166
- text_input = gr.Textbox(
167
- label="📝 Texte à traduire",
168
- placeholder="Ex: Bonjour, comment allez-vous ?",
169
- value="Bonjour, comment allez-vous ?",
170
- lines=4,
171
- max_lines=8,
172
- container=True
173
- )
174
-
175
- with gr.Row():
176
- translate_btn = gr.Button(
177
- "🚀 Traduire",
178
- variant="primary",
179
- size="lg",
180
- elem_classes="button-primary"
181
- )
182
- clear_btn = gr.Button(
183
- "🗑️ Effacer",
184
- variant="secondary",
185
- elem_classes="button-secondary"
186
- )
187
-
188
- text_output = gr.Textbox(
189
- label="📄 Traduction",
190
- lines=4,
191
- max_lines=8,
192
- interactive=False,
193
- container=True
194
- )
195
-
196
- generate_audio_btn = gr.Button(
197
- "🎤 Générer l'audio",
198
- variant="secondary",
199
- size="lg",
200
- visible=True,
201
- elem_classes="button-secondary"
202
- )
203
-
204
- audio_output = gr.Audio(
205
- label="🔊 Synthèse vocale (mooré)",
206
- visible=True,
207
- autoplay=False
208
- )
209
-
210
- # Fonctions de traitement
211
- def translate_only(message, model_choice, direction_choice):
212
- if direction_choice == "French to Moore":
213
- src_lang, tgt_lang = "fra_Latn", "moor_Latn"
214
- else:
215
- src_lang, tgt_lang = "moor_Latn", "fra_Latn"
216
- try:
217
- translated_text = translate_text(message, model_choice, src_lang, tgt_lang)
218
- return translated_text, gr.update(visible=direction_choice == "French to Moore")
219
- except Exception as e:
220
- logger.error(f"Erreur de traduction: {str(e)}")
221
- return f"Erreur de traduction: {str(e)}", gr.update(visible=False)
222
-
223
- def generate_audio(translated_text, speaker_choice):
224
- try:
225
- logger.info("Génération de la synthèse vocale...")
226
- tts_result = tts_speech(translated_text, speaker_choice)
227
- if tts_result is not None:
228
- logger.info("Synthèse vocale générée avec succès")
229
- return tts_result
230
- else:
231
- logger.warning("Échec de la génération TTS")
232
- return None
233
- except Exception as e:
234
- logger.error(f"Erreur TTS: {str(e)}")
235
- return None
236
-
237
- def clear_fields():
238
- return (
239
- "Bonjour, comment allez-vous ?",
240
- "",
241
- None,
242
- gr.update(visible=True)
243
- )
244
-
245
- def toggle_tts_options(direction, tts_enabled):
246
- show_tts = (direction == "French to Moore" and tts_enabled)
247
- return {
248
- speaker_choice: gr.update(visible=show_tts),
249
- audio_output: gr.update(visible=show_tts),
250
- generate_audio_btn: gr.update(visible=show_tts)
251
- }
252
-
253
- # Événements
254
- translate_btn.click(
255
- fn=translate_only,
256
- inputs=[text_input, model_choice, direction_choice],
257
- outputs=[text_output, generate_audio_btn]
258
- )
259
-
260
- generate_audio_btn.click(
261
- fn=generate_audio,
262
- inputs=[text_output, speaker_choice],
263
- outputs=[audio_output]
264
- )
265
-
266
- clear_btn.click(
267
- fn=clear_fields,
268
- inputs=[],
269
- outputs=[text_input, text_output, audio_output, generate_audio_btn]
270
- )
271
-
272
- direction_choice.change(
273
- fn=lambda d, t: toggle_tts_options(d, t),
274
- inputs=[direction_choice, enable_tts],
275
- outputs=[speaker_choice, audio_output, generate_audio_btn]
276
- )
277
-
278
- enable_tts.change(
279
- fn=lambda t, d: toggle_tts_options(d, t),
280
- inputs=[enable_tts, direction_choice],
281
- outputs=[speaker_choice, audio_output, generate_audio_btn]
282
- )
283
-
284
- if __name__ == "__main__":
285
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/chatbot.jpg DELETED
Binary file (61.2 kB)
 
assets/user.jpg DELETED
Binary file (2.66 kB)
 
inference/__init__.py DELETED
@@ -1,3 +0,0 @@
1
- from .mistral import MistralTranslator
2
- from .nllb import NLLBTranslator
3
- from .parle_tts import ParlerTTSGenerator
 
 
 
 
inference/mistral.py DELETED
@@ -1,53 +0,0 @@
1
- import os
2
- import re
3
- from peft import AutoPeftModelForCausalLM
4
- from transformers import AutoTokenizer
5
-
6
-
7
- class MistralTranslator:
8
- """
9
- Wrapper around a Mistral-Instruct-based model fine-tuned for French↔Mooré translation.
10
- """
11
- def __init__(self, model_id: str, hf_token_env: str = "HF_TOKEN"):
12
- hf_token = os.environ.get(hf_token_env)
13
- if hf_token is None:
14
- raise ValueError(f"Please set the environment variable {hf_token_env} with your HuggingFace token.")
15
- self.model = AutoPeftModelForCausalLM.from_pretrained(
16
- model_id,
17
- token=hf_token,
18
- device_map="auto"
19
- )
20
- self.tokenizer = AutoTokenizer.from_pretrained(model_id)
21
- self.prompt_template = (
22
- """
23
- <s>
24
-
25
- You are an expert Moore translator. Translate the provided {src_name} text to {tgt_name}.
26
- The Moore alphabet is: a, ã, b, d, e, ẽ, ɛ, f, g, h, i, ĩ, ɩ, k, l, m, n, o, õ, p, r, s, t, u, ũ, ʋ, v, w, y, z.
27
- Based on source language ({src_name}), provide the {tgt_name} text.
28
- [INST]
29
- ### {src_name}:
30
- {text}
31
- [/INST]
32
-
33
- ### {tgt_name}:
34
- """
35
- )
36
-
37
- def translate(self, text: str, src_lang: str, tgt_lang: str) -> str:
38
- lang_map = {"fra_Latn": "French", "moor_Latn": "Moore"}
39
- src_name = lang_map.get(src_lang, src_lang)
40
- tgt_name = lang_map.get(tgt_lang, tgt_lang)
41
- prompt = self.prompt_template.format(src_name=src_name, tgt_name=tgt_name, text=text)
42
- inputs = self.tokenizer(prompt, return_tensors="pt").to("cuda")
43
- outputs = self.model.generate(
44
- input_ids=inputs.input_ids,
45
- attention_mask=inputs.attention_mask,
46
- max_new_tokens=512,
47
- do_sample=False
48
- )
49
- decoded = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
50
- pattern = rf"### {tgt_name}:\s*(.+)"
51
- match = re.search(pattern, decoded, re.DOTALL)
52
- return match.group(1).strip() if match else decoded
53
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inference/nllb.py DELETED
@@ -1,98 +0,0 @@
1
- import re
2
- import os
3
- import sys
4
- import typing as tp
5
- import unicodedata
6
-
7
- import torch
8
- from sacremoses import MosesPunctNormalizer
9
- from transformers import AutoModelForSeq2SeqLM, NllbTokenizer
10
-
11
- MODEL_URL = "sawadogosalif/SaChi-MT"
12
-
13
-
14
- from huggingface_hub import login
15
- auth_token = os.getenv('HF_TOKEN')
16
- login(token=auth_token)
17
-
18
-
19
-
20
-
21
-
22
- class TextPreprocessor:
23
- """
24
- Mimic the text preprocessing made for the NLLB model.
25
- This code is adapted from the Stopes repo of the NLLB team:
26
- https://github.com/facebookresearch/stopes/blob/main/stopes/pipelines/monolingual/monolingual_line_processor.py#L214
27
- """
28
-
29
- def __init__(self, lang="fr"):
30
- self.mpn = MosesPunctNormalizer(lang=lang)
31
- self.mpn.substitutions = [
32
- (re.compile(r), sub) for r, sub in self.mpn.substitutions
33
- ]
34
-
35
- def __call__(self, text: str) -> str:
36
- clean = self.mpn.normalize(text)
37
- clean = unicodedata.normalize("NFKC", clean)
38
- clean = clean[0].lower() + clean[1:]
39
- return clean
40
-
41
-
42
- def fix_tokenizer(tokenizer, new_lang):
43
- """
44
- Ajoute un nouveau token de langue au tokenizer et met à jour les mappings d’identifiants.
45
-
46
- - Ajoute le token spécial s'il n'existe pas déjà.
47
- - Initialise ou met à jour `lang_code_to_id` et `id_to_lang_code` en utilisant `getattr` pour éviter les vérifications répétitives.
48
- """
49
- if new_lang not in tokenizer.additional_special_tokens:
50
- tokenizer.add_special_tokens({'additional_special_tokens': [new_lang]})
51
-
52
- tokenizer.lang_code_to_id = getattr(tokenizer, 'lang_code_to_id', {})
53
- tokenizer.id_to_lang_code = getattr(tokenizer, 'id_to_lang_code', {})
54
-
55
- if new_lang not in tokenizer.lang_code_to_id:
56
- new_lang_id = tokenizer.convert_tokens_to_ids(new_lang)
57
- tokenizer.lang_code_to_id[new_lang] = new_lang_id
58
- tokenizer.id_to_lang_code[new_lang_id] = new_lang
59
-
60
- return tokenizer
61
-
62
-
63
-
64
- class NLLBTranslator:
65
- def __init__(self):
66
- self.model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_URL)
67
- if torch.cuda.is_available():
68
- self.model.cuda()
69
- self.tokenizer = NllbTokenizer.from_pretrained(MODEL_URL)
70
- self.tokenizer = fix_tokenizer(self.tokenizer, "moor_Latn")
71
-
72
- self.preprocessor = TextPreprocessor()
73
-
74
- def translate(self, text, src_lang='fr_Latn', tgt_lang='moor_Latn', a=32, b=3, max_input_length=1024, num_beams=5, **kwargs):
75
- # 🩹 temporary
76
- tmp = tgt_lang
77
- src_lang = tgt_lang
78
- tgt_lang = tmp
79
-
80
-
81
- self.tokenizer.src_lang = src_lang
82
- self.tokenizer.tgt_lang = tgt_lang
83
- text_clean = self.preprocessor(text)
84
-
85
- inputs = self.tokenizer(text_clean, return_tensors='pt', padding=True, truncation=True, max_length=max_input_length)
86
- result = self.model.generate(
87
- **inputs.to(self.model.device),
88
- forced_bos_token_id=self.tokenizer.convert_tokens_to_ids(tgt_lang),
89
- max_new_tokens=int(a + b * inputs.input_ids.shape[1]),
90
- num_beams=num_beams,
91
- **kwargs
92
- )
93
- output = self.tokenizer.batch_decode(result, skip_special_tokens=True)[0]
94
- if text.endswith('?') or text.endswith('!'):
95
- output += text[-1] # Ajouter le dernier caractère (soit "?" ou "!")
96
-
97
-
98
- return output.capitalize()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inference/parle_tts.py DELETED
@@ -1,197 +0,0 @@
1
- import os
2
- import torch
3
- import numpy as np
4
- from transformers import AutoTokenizer
5
- from parler_tts import ParlerTTSForConditionalGeneration
6
- from loguru import logger
7
-
8
- from huggingface_hub import login
9
- auth_token = os.getenv('HF_TOKEN')
10
- login(token=auth_token)
11
-
12
-
13
- class ParlerTTSGenerator:
14
- """Générateur TTS pour le mooré utilisant ParlerTTS"""
15
-
16
- def __init__(self, model_checkpoint="burkimbia/BIA-ParlerTTS-mini"):
17
- """
18
- Initialiser le générateur TTS
19
-
20
- Args:
21
- model_checkpoint (str): Nom du modèle HuggingFace
22
- """
23
- self.model_checkpoint = model_checkpoint
24
- self.device = "cuda" if torch.cuda.is_available() else "cpu"
25
- self.model = None
26
- self.tokenizer = None
27
- self.description_tokenizer = None
28
-
29
- # Configuration par défaut pour la génération
30
- self.gen_kwargs = {
31
- 'do_sample': True,
32
- 'temperature': 1.0,
33
- 'max_length': 2580,
34
- 'min_new_tokens': 10
35
- }
36
-
37
- # Descriptions des locuteurs par défaut
38
- self.speaker_descriptions = {
39
- "default": "Christian speaks very slowly but has an animated delivery in a room with background",
40
- "male": "A male voice speaking in Moore language with calm intonation and clear pronunciation.",
41
- "female": "A female voice speaking in Moore language with gentle tone and clear articulation."
42
- }
43
-
44
- self.current_speaker = "default"
45
-
46
- def load_model(self):
47
- """Charger le modèle TTS de manière lazy"""
48
- try:
49
- if self.model is None:
50
- logger.info(f"Chargement du modèle TTS: {self.model_checkpoint}")
51
-
52
- self.model = ParlerTTSForConditionalGeneration.from_pretrained(
53
- self.model_checkpoint,
54
- use_auth_token=True, # Pour les repos privés
55
- torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
56
- ).to(self.device)
57
-
58
- self.tokenizer = AutoTokenizer.from_pretrained(
59
- self.model_checkpoint,
60
- use_auth_token=True
61
- )
62
-
63
- self.description_tokenizer = AutoTokenizer.from_pretrained(
64
- self.model_checkpoint,
65
- use_auth_token=True
66
- )
67
-
68
- logger.info("Modèle TTS chargé avec succès")
69
-
70
- except Exception as e:
71
- logger.error(f"Erreur lors du chargement du modèle TTS: {str(e)}")
72
- raise
73
-
74
- def set_speaker(self, speaker_type="default"):
75
- """
76
- Définir le type de locuteur
77
-
78
- Args:
79
- speaker_type (str): Type de locuteur ("default", "male", "female")
80
- """
81
- if speaker_type in self.speaker_descriptions:
82
- self.current_speaker = speaker_type
83
- else:
84
- logger.warning(f"Type de locuteur '{speaker_type}' non reconnu, utilisation de 'default'")
85
- self.current_speaker = "default"
86
-
87
- def set_custom_description(self, description):
88
- """
89
- Définir une description personnalisée pour le locuteur
90
-
91
- Args:
92
- description (str): Description personnalisée du locuteur
93
- """
94
- self.speaker_descriptions["custom"] = description
95
- self.current_speaker = "custom"
96
-
97
- def generate_speech(self, text, speaker_type=None):
98
- """
99
- Générer la parole à partir du texte
100
-
101
- Args:
102
- text (str): Texte à synthétiser
103
- speaker_type (str, optional): Type de locuteur à utiliser
104
-
105
- Returns:
106
- tuple: (sample_rate, audio_array) ou (None, None) en cas d'erreur
107
- """
108
- try:
109
- if self.model is None:
110
- self.load_model()
111
-
112
- if speaker_type:
113
- self.set_speaker(speaker_type)
114
-
115
- speaker_description = self.speaker_descriptions[self.current_speaker]
116
-
117
- text = self._preprocess_text(text)
118
-
119
- input_ids = self.description_tokenizer(
120
- speaker_description,
121
- return_tensors="pt",
122
- padding=True,
123
- truncation=True
124
- ).input_ids.to(self.device)
125
-
126
- prompt_input_ids = self.tokenizer(
127
- text,
128
- return_tensors="pt",
129
- padding=True,
130
- truncation=True
131
- ).input_ids.to(self.device)
132
-
133
- logger.info(f"Génération TTS pour: '{text[:50]}...'")
134
-
135
- with torch.no_grad():
136
- generation = self.model.generate(
137
- input_ids=input_ids,
138
- prompt_input_ids=prompt_input_ids,
139
- **self.gen_kwargs
140
- )
141
-
142
- audio_arr = generation.cpu().numpy().squeeze()
143
- sample_rate = self.model.config.sampling_rate
144
-
145
- logger.info("Synthèse vocale réussie")
146
- return sample_rate, audio_arr
147
-
148
- except Exception as e:
149
- logger.error(f"Erreur lors de la synthèse vocale: {str(e)}")
150
- return None, None
151
-
152
- def _preprocess_text(self, text):
153
- """
154
- Préprocesser le texte avant la synthèse
155
-
156
- Args:
157
- text (str): Texte original
158
-
159
- Returns:
160
- str: Texte préprocessé
161
- """
162
- # Nettoyer le texte si nécessaire
163
- text = text.strip()
164
- # to improve
165
- return text
166
-
167
- def is_model_loaded(self):
168
- """Vérifier si le modèle est chargé"""
169
- return self.model is not None
170
-
171
- def unload_model(self):
172
- """Décharger le modèle pour libérer la mémoire"""
173
- if self.model is not None:
174
- del self.model
175
- del self.tokenizer
176
- del self.description_tokenizer
177
- self.model = None
178
- self.tokenizer = None
179
- self.description_tokenizer = None
180
-
181
- if torch.cuda.is_available():
182
- torch.cuda.empty_cache()
183
-
184
- logger.info("Modèle TTS déchargé")
185
-
186
- def get_available_speakers(self):
187
- """Obtenir la liste des types de locuteurs disponibles"""
188
- return list(self.speaker_descriptions.keys())
189
-
190
- def get_model_info(self):
191
- """Obtenir des informations sur le modèle"""
192
- return {
193
- "model_checkpoint": self.model_checkpoint,
194
- "device": self.device,
195
- "is_loaded": self.is_model_loaded(),
196
- "available_speakers": self.get_available_speakers()
197
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt DELETED
@@ -1,10 +0,0 @@
1
- accelerate==1.6.0
2
- bitsandbytes==0.45.5
3
- peft==0.15.0
4
- gradio>=3.18.0
5
- torch
6
- loguru
7
- sacremoses
8
- sentencepiece
9
- spaces
10
- git+https://github.com/huggingface/parler-tts.git
 
 
 
 
 
 
 
 
 
 
 
styles.css DELETED
@@ -1,172 +0,0 @@
1
-
2
- .main-container {
3
- max-width: 1200px;
4
- margin: 0 auto;
5
- padding: 20px;
6
- }
7
-
8
- .header-section {
9
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
10
- color: white;
11
- padding: 30px;
12
- border-radius: 15px;
13
- margin-bottom: 30px;
14
- text-align: center;
15
- box-shadow: 0 10px 30px rgba(0,0,0,0.1);
16
- }
17
-
18
- .config-card {
19
- background: white;
20
- border-radius: 15px;
21
- padding: 25px;
22
- box-shadow: 0 5px 20px rgba(0,0,0,0.08);
23
- border: 1px solid #e2e8f0;
24
- margin-bottom: 20px;
25
- height: 100%;
26
- display: flex;
27
- flex-direction: column;
28
- }
29
-
30
- .translation-card {
31
- background: white;
32
- border-radius: 15px;
33
- padding: 25px;
34
- box-shadow: 0 5px 20px rgba(0,0,0,0.08);
35
- border: 1px solid #e2e8f0;
36
- height: 100%;
37
- display: flex;
38
- flex-direction: column;
39
- }
40
-
41
- /* Assurer que les colonnes ont la même hauteur */
42
- .equal-height-row {
43
- display: flex;
44
- align-items: stretch;
45
- }
46
-
47
- .equal-height-column {
48
- display: flex;
49
- flex-direction: column;
50
- }
51
-
52
- .feature-item {
53
- display: flex;
54
- align-items: center;
55
- margin: 10px 0;
56
- font-size: 16px;
57
- }
58
-
59
- .feature-icon {
60
- margin-right: 10px;
61
- font-size: 18px;
62
- }
63
-
64
- .model-item {
65
- display: flex;
66
- align-items: center;
67
- margin: 8px 0;
68
- font-size: 14px;
69
- color: #64748b;
70
- }
71
-
72
- .section-title {
73
- font-size: 20px;
74
- font-weight: 600;
75
- margin-bottom: 20px;
76
- color: #1e293b;
77
- display: flex;
78
- align-items: center;
79
- }
80
-
81
- .section-icon {
82
- margin-right: 10px;
83
- font-size: 22px;
84
- }
85
-
86
- .button-primary {
87
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
88
- border: none !important;
89
- color: white !important;
90
- font-weight: 600 !important;
91
- padding: 12px 24px !important;
92
- border-radius: 8px !important;
93
- transition: all 0.3s ease !important;
94
- }
95
-
96
- .button-primary:hover {
97
- transform: translateY(-2px) !important;
98
- box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3) !important;
99
- }
100
-
101
- .button-secondary {
102
- background: #f8fafc !important;
103
- border: 2px solid #e2e8f0 !important;
104
- color: #64748b !important;
105
- font-weight: 600 !important;
106
- padding: 12px 24px !important;
107
- border-radius: 8px !important;
108
- transition: all 0.3s ease !important;
109
- }
110
-
111
- .button-secondary:hover {
112
- background: #f1f5f9 !important;
113
- border-color: #cbd5e1 !important;
114
- }
115
-
116
- /* Styles responsifs */
117
- @media (max-width: 768px) {
118
- .main-container {
119
- padding: 10px;
120
- }
121
-
122
- .header-section {
123
- padding: 20px;
124
- }
125
-
126
- .config-card,
127
- .translation-card {
128
- padding: 20px;
129
- }
130
-
131
- .equal-height-row {
132
- flex-direction: column;
133
- }
134
- }
135
-
136
- /* Amélioration des composants Gradio */
137
- .gradio-container {
138
- font-family: 'Inter', sans-serif;
139
- }
140
-
141
- /* Styling pour les dropdowns */
142
- .gr-dropdown {
143
- border-radius: 8px !important;
144
- border: 1px solid #e2e8f0 !important;
145
- }
146
-
147
- /* Styling pour les textboxes */
148
- .gr-textbox {
149
- border-radius: 8px !important;
150
- border: 1px solid #e2e8f0 !important;
151
- }
152
-
153
- /* Styling pour les checkboxes */
154
- .gr-checkbox {
155
- border-radius: 4px !important;
156
- }
157
-
158
- /* Animation de chargement */
159
- .loading-spinner {
160
- display: inline-block;
161
- width: 20px;
162
- height: 20px;
163
- border: 3px solid #f3f3f3;
164
- border-top: 3px solid #667eea;
165
- border-radius: 50%;
166
- animation: spin 1s linear infinite;
167
- }
168
-
169
- @keyframes spin {
170
- 0% { transform: rotate(0deg); }
171
- 100% { transform: rotate(360deg); }
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
utils.py DELETED
@@ -1,13 +0,0 @@
1
-
2
- import os
3
-
4
-
5
- def load_css():
6
- """Charge le fichier CSS externe"""
7
- css_path = os.path.join(os.path.dirname(__file__), "styles.css")
8
- try:
9
- with open(css_path, "r", encoding="utf-8") as f:
10
- return f.read()
11
- except FileNotFoundError:
12
- logger.warning(f"Fichier CSS non trouvé: {css_path}")
13
- return ""