peterpanbk95 commited on
Commit
d5a614e
·
0 Parent(s):

Initial commit - App.py, config.json, requirements.txt

Browse files
.env ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ LLM_PROVIDER=gemini
2
+ GEMINI_API_KEY=AIzaSyCqkW4OP3InUH0eaWARmPO1XrVxHIuos-U
.gitattributes ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ data/models/vncorenlp/VnCoreNLP-1.1.1.jar filter=lfs diff=lfs merge=lfs -text
2
+ D:/web/Language[[:space:]]Butler[[:space:]]Gradio/data/models/viettts/.git/objects/pack/pack-48ef822b97252b84bab0d4cd06927630c7d9fc10.rev filter=lfs diff=lfs merge=lfs -text
3
+ data/models/vncorenlp/** filter=lfs diff=lfs merge=lfs -text
4
+ data/models/viettts/** filter=lfs diff=lfs merge=lfs -text
App.py ADDED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import tempfile
4
+ import subprocess
5
+ import threading
6
+ import json
7
+ import base64
8
+ import io
9
+ import random
10
+ import logging
11
+ from queue import Queue
12
+ from threading import Thread
13
+
14
+ import gradio as gr
15
+ import torch
16
+ import librosa
17
+ import soundfile as sf
18
+ import requests
19
+ import numpy as np
20
+ from scipy import signal
21
+ from transformers import pipeline, AutoTokenizer, AutoModel
22
+
23
+ # Thiết lập logging
24
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # Tạo các thư mục cần thiết
28
+ os.makedirs("data", exist_ok=True)
29
+ os.makedirs("data/audio", exist_ok=True)
30
+ os.makedirs("data/reports", exist_ok=True)
31
+ os.makedirs("data/models", exist_ok=True)
32
+
33
+
34
+ class AsyncProcessor:
35
+ """Xử lý các tác vụ nặng trong thread riêng để không làm 'đơ' giao diện."""
36
+ def __init__(self):
37
+ self.task_queue = Queue()
38
+ self.result_queue = Queue()
39
+ self.running = True
40
+ self.worker_thread = Thread(target=self._worker)
41
+ self.worker_thread.daemon = True
42
+ self.worker_thread.start()
43
+
44
+ def _worker(self):
45
+ while self.running:
46
+ if not self.task_queue.empty():
47
+ task_id, func, args, kwargs = self.task_queue.get()
48
+ try:
49
+ result = func(*args, **kwargs)
50
+ self.result_queue.put((task_id, result, None))
51
+ except Exception as e:
52
+ logger.error(f"Lỗi trong xử lý tác vụ {task_id}: {str(e)}")
53
+ self.result_queue.put((task_id, None, str(e)))
54
+ self.task_queue.task_done()
55
+ time.sleep(0.1)
56
+
57
+ def add_task(self, task_id, func, *args, **kwargs):
58
+ self.task_queue.put((task_id, func, args, kwargs))
59
+
60
+ def get_result(self):
61
+ if not self.result_queue.empty():
62
+ return self.result_queue.get()
63
+ return None
64
+
65
+ def stop(self):
66
+ self.running = False
67
+ if self.worker_thread.is_alive():
68
+ self.worker_thread.join(timeout=1)
69
+
70
+
71
+ class VietSpeechTrainer:
72
+ def __init__(self):
73
+ # Đọc cấu hình từ file config.json và từ biến môi trường
74
+ self.config = self._load_config()
75
+
76
+ # Khởi tạo bộ xử lý bất đồng bộ
77
+ self.async_processor = AsyncProcessor()
78
+
79
+ # Lưu trữ lịch sử phiên làm việc
80
+ self.session_history = []
81
+ self.current_session_id = int(time.time())
82
+
83
+ # Các biến trạng thái hội thoại
84
+ self.current_scenario = None
85
+ self.current_prompt_index = 0
86
+
87
+ # Khởi tạo các mô hình (STT, TTS và phân tích LLM)
88
+ logger.info("Đang tải các mô hình...")
89
+ self._initialize_models()
90
+
91
+ def _load_config(self):
92
+ """Đọc file config.json và cập nhật từ biến môi trường (Secrets khi deploy)"""
93
+ config = {
94
+ "stt_model": "nguyenvulebinh/wav2vec2-base-vietnamese-250h",
95
+ "use_phowhisper": False,
96
+ "use_phobert": False,
97
+ "use_vncorenlp": False,
98
+ "llm_provider": "none", # openai, gemini, local hoặc none
99
+ "openai_api_key": "",
100
+ "gemini_api_key": "",
101
+ "local_llm_endpoint": "",
102
+ "use_viettts": False,
103
+ "default_dialect": "Bắc",
104
+ "enable_pronunciation_eval": False,
105
+ "preprocess_audio": True,
106
+ "save_history": True,
107
+ "enable_english_tts": False
108
+ }
109
+ if os.path.exists("config.json"):
110
+ try:
111
+ with open("config.json", "r", encoding="utf-8") as f:
112
+ file_config = json.load(f)
113
+ config.update(file_config)
114
+ except Exception as e:
115
+ logger.error(f"Lỗi đọc config.json: {e}")
116
+ # Cập nhật từ biến môi trường
117
+ if os.environ.get("LLM_PROVIDER"):
118
+ config["llm_provider"] = os.environ.get("LLM_PROVIDER").lower()
119
+ if os.environ.get("OPENAI_API_KEY"):
120
+ config["openai_api_key"] = os.environ.get("OPENAI_API_KEY")
121
+ if os.environ.get("GEMINI_API_KEY"):
122
+ config["gemini_api_key"] = os.environ.get("GEMINI_API_KEY")
123
+ if os.environ.get("LOCAL_LLM_ENDPOINT"):
124
+ config["local_llm_endpoint"] = os.environ.get("LOCAL_LLM_ENDPOINT")
125
+ if os.environ.get("ENABLE_ENGLISH_TTS") and os.environ.get("ENABLE_ENGLISH_TTS").lower() == "true":
126
+ config["enable_english_tts"] = True
127
+ return config
128
+
129
+ def _initialize_models(self):
130
+ """Khởi tạo mô hình STT và thiết lập CSM cho TTS tiếng Anh nếu được bật."""
131
+ try:
132
+ # Khởi tạo STT
133
+ if self.config["use_phowhisper"]:
134
+ logger.info("Loading PhoWhisper...")
135
+ self.stt_model = pipeline("automatic-speech-recognition",
136
+ model="vinai/PhoWhisper-small",
137
+ device=0 if torch.cuda.is_available() else -1)
138
+ else:
139
+ logger.info(f"Loading STT model: {self.config['stt_model']}")
140
+ self.stt_model = pipeline("automatic-speech-recognition",
141
+ model=self.config["stt_model"],
142
+ device=0 if torch.cuda.is_available() else -1)
143
+ except Exception as e:
144
+ logger.error(f"Lỗi khởi tạo STT: {e}")
145
+ self.stt_model = None
146
+
147
+ # Các mô hình NLP (PhoBERT, VnCoreNLP) nếu cần.
148
+ # ...
149
+
150
+ # Nếu bật TTS tiếng Anh thì thiết lập CSM
151
+ if self.config.get("enable_english_tts", False):
152
+ self._setup_csm()
153
+ else:
154
+ self.csm_ready = False
155
+
156
+ def _setup_csm(self):
157
+ """Cài đặt mô hình CSM (Conversational Speech Generation Model) cho TTS tiếng Anh."""
158
+ try:
159
+ csm_dir = os.path.join(os.getcwd(), "csm")
160
+ if not os.path.exists(csm_dir):
161
+ logger.info("Cloning CSM repo...")
162
+ subprocess.run(["git", "clone", "https://github.com/SesameAILabs/csm", csm_dir], check=True)
163
+ logger.info("Installing CSM requirements...")
164
+ subprocess.run(["pip", "install", "-r", os.path.join(csm_dir, "requirements.txt")], check=True)
165
+ self.csm_ready = True
166
+ logger.info("CSM đã được thiết lập thành công!")
167
+ except Exception as e:
168
+ logger.error(f"Failed to set up CSM: {e}")
169
+ self.csm_ready = False
170
+
171
+ def text_to_speech(self, text, language="vi", dialect="Bắc"):
172
+ """
173
+ Chuyển văn bản thành giọng nói:
174
+ - Nếu language == "en": sử dụng CSM để tạo TTS tiếng Anh.
175
+ - Nếu language == "vi": sử dụng API hoặc logic TTS tiếng Việt.
176
+ """
177
+ if language == "en":
178
+ if not self.csm_ready:
179
+ logger.error("CSM chưa được thiết lập hoặc không được bật.")
180
+ return None
181
+ output_file = f"data/audio/csm_{int(time.time())}.wav"
182
+ csm_script_path = os.path.join(os.getcwd(), "csm", "run_csm.py")
183
+ cmd = [
184
+ "python",
185
+ csm_script_path,
186
+ "--text", text,
187
+ "--speaker_id", "0", # Mặc định, có thể cho phép người dùng chọn
188
+ "--output", output_file
189
+ ]
190
+ try:
191
+ subprocess.run(cmd, check=True)
192
+ return output_file
193
+ except subprocess.CalledProcessError as e:
194
+ logger.error(f"CSM generation failed: {e}")
195
+ return None
196
+ else:
197
+ # Ví dụ: Nếu có API TTS tiếng Việt, gọi API đó.
198
+ tts_api_url = self.config.get("tts_api_url", "")
199
+ if tts_api_url:
200
+ try:
201
+ resp = requests.post(tts_api_url, json={"text": text, "dialect": dialect.lower()})
202
+ if resp.status_code == 200:
203
+ output_file = f"data/audio/tts_{int(time.time())}.wav"
204
+ with open(output_file, "wb") as f:
205
+ f.write(resp.content)
206
+ return output_file
207
+ else:
208
+ logger.error(f"Error calling TTS API: {resp.text}")
209
+ return None
210
+ except Exception as e:
211
+ logger.error(f"Lỗi gọi TTS API: {e}")
212
+ return None
213
+ else:
214
+ # Nếu không có API TTS, bạn có thể tích hợp VietTTS hoặc khác.
215
+ return None
216
+
217
+ def transcribe_audio(self, audio_path):
218
+ """Chuyển đổi giọng nói thành văn bản (STT)."""
219
+ if not self.stt_model:
220
+ return "STT model not available."
221
+ try:
222
+ result = self.stt_model(audio_path)
223
+ if isinstance(result, dict) and "text" in result:
224
+ return result["text"]
225
+ elif isinstance(result, list):
226
+ return " ".join([chunk.get("text", "") for chunk in result])
227
+ else:
228
+ return str(result)
229
+ except Exception as e:
230
+ logger.error(f"Lỗi chuyển giọng nói: {e}")
231
+ return f"Lỗi: {str(e)}"
232
+
233
+ def analyze_text(self, transcript, dialect="Bắc"):
234
+ """
235
+ Phân tích văn bản sử dụng LLM:
236
+ - Nếu LLM_PROVIDER là "openai", "gemini" hay "local" thì gọi API tương ứng.
237
+ - Nếu LLM_PROVIDER là "none", sử dụng phân tích rule-based.
238
+ """
239
+ llm_provider = self.config["llm_provider"]
240
+ if llm_provider == "openai" and self.config["openai_api_key"]:
241
+ return self._analyze_with_openai(transcript)
242
+ elif llm_provider == "gemini" and self.config["gemini_api_key"]:
243
+ return self._analyze_with_gemini(transcript)
244
+ elif llm_provider == "local" and self.config["local_llm_endpoint"]:
245
+ return self._analyze_with_local_llm(transcript)
246
+ else:
247
+ return self._rule_based_analysis(transcript, dialect)
248
+
249
+ def _analyze_with_openai(self, transcript):
250
+ headers = {
251
+ "Authorization": f"Bearer {self.config['openai_api_key']}",
252
+ "Content-Type": "application/json"
253
+ }
254
+ data = {
255
+ "model": "gpt-3.5-turbo",
256
+ "messages": [
257
+ {"role": "system", "content": "Bạn là trợ lý dạy tiếng Việt."},
258
+ {"role": "user", "content": transcript}
259
+ ],
260
+ "temperature": 0.5,
261
+ "max_tokens": 150
262
+ }
263
+ try:
264
+ response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=data)
265
+ if response.status_code == 200:
266
+ result = response.json()
267
+ return result["choices"][0]["message"]["content"]
268
+ else:
269
+ return "Lỗi khi gọi OpenAI API."
270
+ except Exception as e:
271
+ logger.error(f"Lỗi OpenAI: {e}")
272
+ return "Lỗi phân tích với OpenAI."
273
+
274
+ def _analyze_with_gemini(self, transcript):
275
+ # Ví dụ minh họa: Gọi Gemini API (chi tiết phụ thuộc vào tài liệu của Gemini)
276
+ return "Gemini analysis..."
277
+
278
+ def _analyze_with_local_llm(self, transcript):
279
+ # Giả sử gọi một endpoint local (nếu có) cho LLM cục bộ.
280
+ headers = {"Content-Type": "application/json"}
281
+ data = {
282
+ "model": "local-model",
283
+ "messages": [
284
+ {"role": "system", "content": "Bạn là trợ lý dạy tiếng Việt."},
285
+ {"role": "user", "content": transcript}
286
+ ],
287
+ "temperature": 0.5,
288
+ "max_tokens": 150
289
+ }
290
+ try:
291
+ response = requests.post(self.config["local_llm_endpoint"] + "/chat/completions", headers=headers, json=data)
292
+ if response.status_code == 200:
293
+ result = response.json()
294
+ return result["choices"][0]["message"]["content"]
295
+ else:
296
+ return "Lỗi khi gọi Local LLM."
297
+ except Exception as e:
298
+ logger.error(f"Lỗi local LLM: {e}")
299
+ return "Lỗi phân tích với LLM local."
300
+
301
+ def _rule_based_analysis(self, transcript, dialect):
302
+ # Phân tích đơn giản không dùng LLM
303
+ return "Phân tích rule-based: " + transcript
304
+
305
+ def clean_up(self):
306
+ self.async_processor.stop()
307
+ if torch.cuda.is_available():
308
+ torch.cuda.empty_cache()
309
+ logger.info("Clean up done.")
310
+
311
+
312
+ def create_demo():
313
+ trainer = VietSpeechTrainer()
314
+
315
+ with gr.Blocks(title="Ứng dụng Luyện Nói & TTS", theme=gr.themes.Soft(primary_hue="blue")) as demo:
316
+ gr.Markdown("## Ứng dụng Luyện Nói & TTS (Tiếng Việt & Tiếng Anh)")
317
+
318
+ with gr.Tabs():
319
+ # Tab 1: TTS Tiếng Việt
320
+ with gr.Tab("TTS Tiếng Việt"):
321
+ vi_text_input = gr.Textbox(label="Nhập văn bản tiếng Việt")
322
+ vi_audio_output = gr.Audio(label="Kết quả âm thanh")
323
+ gen_vi_btn = gr.Button("Chuyển thành giọng nói")
324
+
325
+ def gen_vi_tts(txt):
326
+ return trainer.text_to_speech(txt, language="vi", dialect=trainer.config["default_dialect"])
327
+
328
+ gen_vi_btn.click(fn=gen_vi_tts, inputs=vi_text_input, outputs=vi_audio_output)
329
+
330
+ # Tab 2: TTS Tiếng Anh (sử dụng CSM)
331
+ with gr.Tab("TTS Tiếng Anh"):
332
+ en_text_input = gr.Textbox(label="Enter English text")
333
+ en_audio_output = gr.Audio(label="Generated English Audio (CSM)")
334
+ gen_en_btn = gr.Button("Generate English Speech")
335
+
336
+ def gen_en_tts(txt):
337
+ return trainer.text_to_speech(txt, language="en")
338
+
339
+ gen_en_btn.click(fn=gen_en_tts, inputs=en_text_input, outputs=en_audio_output)
340
+
341
+ # Tab 3: Luyện phát âm (Tiếng Việt)
342
+ with gr.Tab("Luyện phát âm"):
343
+ audio_input = gr.Audio(source="microphone", type="filepath", label="Giọng nói của bạn")
344
+ transcript_output = gr.Textbox(label="Transcript")
345
+ analysis_output = gr.Markdown(label="Phân tích")
346
+ analyze_btn = gr.Button("Phân tích")
347
+
348
+ def process_audio(audio_path):
349
+ transcript = trainer.transcribe_audio(audio_path)
350
+ analysis = trainer.analyze_text(transcript, dialect=trainer.config["default_dialect"])
351
+ return transcript, analysis
352
+
353
+ analyze_btn.click(fn=process_audio, inputs=audio_input, outputs=[transcript_output, analysis_output])
354
+
355
+ # Tab 4: Thông tin & Hướng dẫn
356
+ with gr.Tab("Thông tin"):
357
+ gr.Markdown("""
358
+ ### Hướng dẫn sử dụng:
359
+ - **TTS Tiếng Việt:** Nhập văn bản tiếng Việt và nhấn "Chuyển thành giọng nói".
360
+ - **TTS Tiếng Anh (CSM):** Nhập English text và nhấn "Generate English Speech".
361
+ - **Luyện phát âm:** Thu âm giọng nói, sau đó nhấn "Phân tích" để xem transcript và phân tích.
362
+
363
+ ### Cấu hình LLM:
364
+ - **OpenAI:** Đặt biến môi trường `LLM_PROVIDER=openai` và `OPENAI_API_KEY` với key của bạn.
365
+ - **Gemini:** Đặt `LLM_PROVIDER=gemini` và `GEMINI_API_KEY`.
366
+ - **Local LLM:** Đặt `LLM_PROVIDER=local` và `LOCAL_LLM_ENDPOINT` với URL của server LLM nếu bạn có.
367
+ - **None:** Đặt `LLM_PROVIDER=none` để sử dụng phân tích rule-based.
368
+
369
+ ### Lưu ý:
370
+ - Để sử dụng TTS tiếng Anh (CSM), hãy bật biến `ENABLE_ENGLISH_TTS` (hoặc đặt `"enable_english_tts": true` trong config.json).
371
+ """)
372
+ return demo
373
+
374
+
375
+ def main():
376
+ demo = create_demo()
377
+ # Sử dụng hàng đợi Gradio để xử lý tác vụ dài (ví dụ TTS CSM)
378
+ demo.queue()
379
+ demo.launch(server_name="0.0.0.0", server_port=7860)
380
+
381
+
382
+ if __name__ == "__main__":
383
+ main()
Gemini API/.env ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ LLM_PROVIDER=gemini
2
+ GEMINI_API_KEY=your_gemini_api_key_here
OpenAI/.env ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ LLM_PROVIDER=openai
2
+ OPENAI_API_KEY=your_openai_api_key_here
config.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "stt_model": "nguyenvulebinh/wav2vec2-base-vietnamese-250h",
3
+ "use_phowhisper": false,
4
+ "use_phobert": false,
5
+ "use_vncorenlp": false,
6
+ "llm_provider": "none",
7
+ "openai_api_key": "",
8
+ "gemini_api_key": "",
9
+ "local_llm_endpoint": "",
10
+ "use_viettts": false,
11
+ "default_dialect": "Bắc",
12
+ "enable_pronunciation_eval": false,
13
+ "preprocess_audio": true,
14
+ "save_history": true,
15
+ "enable_english_tts": true
16
+ }
data/models/viettts ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit f5c5d0956d6ca2bf407011370f75a30dfd536c2f
data/models/vncorenlp/VnCoreNLP-1.1.1.jar ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c356b2baa0b83a287642b29d5c2ec5e9558c84d1c937f0aa88a5eea8748e587e
3
+ size 27412575
data/models/vncorenlp/models/wordsegmenter/vi-vocab ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0a47c5b55bbce163029d37730a67b9479740388695c29c106c112b815613eaa5
3
+ size 526544
data/models/vncorenlp/models/wordsegmenter/wordsegmenter.rdr ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9e62f96bd93e37a24f364238e8d8ae986fa5dad6dbc9f4eae622ab3651b7fa06
3
+ size 128508
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ torch
3
+ transformers
4
+ librosa
5
+ soundfile
6
+ requests
7
+ numpy
8
+ scipy