alex16052G commited on
Commit
68d6bb1
verified
1 Parent(s): c0a2aa1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +345 -37
app.py CHANGED
@@ -1,46 +1,354 @@
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
2
  from transformers import AutoModelForCausalLM, AutoTokenizer
3
- import subprocess
4
- from f5_infer import F5TTS
5
 
6
- # Instalar Spanish-F5 autom谩ticamente desde GitHub
7
- def install_spanish_f5():
8
- subprocess.run(["pip", "install", "--upgrade", "git+https://github.com/jpgallegoar/Spanish-F5", "--no-cache-dir"], check=True)
 
 
 
 
 
9
 
10
- # Instalar Spanish-F5
11
- install_spanish_f5()
 
 
 
 
12
 
13
- # Cargar el modelo Qwen2.5-3B-Instruct
14
- tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-2.5B-Instruct")
15
- model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen-2.5B-Instruct", device_map="auto")
 
 
 
16
 
17
- # Inicializar Spanish-F5 para s铆ntesis de voz
18
- tts = F5TTS()
19
 
20
- # Funci贸n principal para el flujo del chat con voz
21
- def chat_with_voice(input_text):
22
- # Generar respuesta con Qwen
23
- inputs = tokenizer(input_text, return_tensors="pt").to("cuda")
24
- outputs = model.generate(**inputs, max_length=200)
25
- response_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
26
-
27
- # Convertir respuesta a audio usando Spanish-F5
28
- audio_path = tts.generate_tts(response_text, output_path="response.wav")
29
-
30
- return response_text, audio_path
31
-
32
- # Interfaz de Gradio
33
- with gr.Blocks() as demo:
34
- gr.Markdown("# Chat AI con Voz (Qwen y Spanish-F5)")
35
- with gr.Row():
36
- input_text = gr.Textbox(label="Escribe tu mensaje:", placeholder="驴C贸mo puedo ayudarte hoy?")
37
- with gr.Row():
38
- response_text = gr.Textbox(label="Respuesta del modelo")
39
- response_audio = gr.Audio(label="Respuesta en voz", type="filepath")
40
- send_btn = gr.Button("Enviar")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- # Conectar eventos
43
- send_btn.click(chat_with_voice, inputs=input_text, outputs=[response_text, response_audio])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
- # Ejecutar la app
46
- demo.launch()
 
1
+ # ruff: noqa: E402
2
+ # Above allows ruff to ignore E402: module level import not at top of file
3
+
4
+ import re
5
+ import tempfile
6
+
7
  import gradio as gr
8
+ import numpy as np
9
+ import soundfile as sf
10
+ import torchaudio
11
+ from cached_path import cached_path
12
  from transformers import AutoModelForCausalLM, AutoTokenizer
13
+ import torch
 
14
 
15
+ from f5_tts.model import DiT
16
+ from f5_tts.infer.utils_infer import (
17
+ load_vocoder,
18
+ load_model,
19
+ preprocess_ref_audio_text,
20
+ infer_process,
21
+ remove_silence_for_generated_wav,
22
+ )
23
 
24
+ # Intentar importar 'spaces' para determinar si se est谩 usando Hugging Face Spaces
25
+ try:
26
+ import spaces
27
+ USING_SPACES = True
28
+ except ImportError:
29
+ USING_SPACES = False
30
 
31
+ # Decorador para utilizar GPU si est谩 disponible
32
+ def gpu_decorator(func):
33
+ if USING_SPACES:
34
+ return spaces.GPU(func)
35
+ else:
36
+ return func
37
 
38
+ # Cargar el vocoder
39
+ vocoder = load_vocoder()
40
 
41
+ # Cargar el modelo F5-TTS
42
+ F5TTS_model_cfg = dict(dim=1024, depth=22, heads=16, ff_mult=2, text_dim=512, conv_layers=4)
43
+ F5TTS_ema_model = load_model(
44
+ DiT, F5TTS_model_cfg, str(cached_path("hf://jpgallegoar/F5-Spanish/model_1200000.safetensors"))
45
+ )
46
+
47
+ # Variables globales para el modelo de chat
48
+ chat_model_state = None
49
+ chat_tokenizer_state = None
50
+
51
+ @gpu_decorator
52
+ def generate_response(messages, model, tokenizer):
53
+ """Genera una respuesta usando el modelo de chat"""
54
+ text = tokenizer.apply_chat_template(
55
+ messages,
56
+ tokenize=False,
57
+ add_generation_prompt=True,
58
+ )
59
+
60
+ model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
61
+ generated_ids = model.generate(
62
+ **model_inputs,
63
+ max_new_tokens=512,
64
+ temperature=0.7,
65
+ top_p=0.95,
66
+ )
67
+
68
+ generated_ids = [
69
+ output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)
70
+ ]
71
+ return tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
72
+
73
+ def traducir_numero_a_texto(texto):
74
+ """Convierte n煤meros en texto a su representaci贸n en palabras en espa帽ol"""
75
+ texto_separado = re.sub(r'([A-Za-z])(\d)', r'\1 \2', texto)
76
+ texto_separado = re.sub(r'(\d)([A-Za-z])', r'\1 \2', texto_separado)
77
 
78
+ def reemplazar_numero(match):
79
+ numero = match.group()
80
+ return num2words(int(numero), lang='es')
81
+
82
+ texto_traducido = re.sub(r'\b\d+\b', reemplazar_numero, texto_separado)
83
+
84
+ return texto_traducido
85
+
86
+ @gpu_decorator
87
+ def infer(
88
+ ref_audio_orig, ref_text, gen_text, model, remove_silence, cross_fade_duration=0.15, speed=1, show_info=gr.Info
89
+ ):
90
+ """Genera el audio sintetizado a partir del texto"""
91
+ ref_audio, ref_text = preprocess_ref_audio_text(ref_audio_orig, ref_text, show_info=show_info)
92
+
93
+ ema_model = F5TTS_ema_model
94
+
95
+ if not gen_text.startswith(" "):
96
+ gen_text = " " + gen_text
97
+ if not gen_text.endswith(". "):
98
+ gen_text += ". "
99
+
100
+ gen_text = gen_text.lower()
101
+ gen_text = traducir_numero_a_texto(gen_text)
102
+
103
+ final_wave, final_sample_rate, combined_spectrogram = infer_process(
104
+ ref_audio,
105
+ ref_text,
106
+ gen_text,
107
+ ema_model,
108
+ vocoder,
109
+ cross_fade_duration=cross_fade_duration,
110
+ speed=speed,
111
+ show_info=show_info,
112
+ progress=gr.Progress(),
113
+ )
114
+
115
+ # Eliminar silencios si est谩 activado
116
+ if remove_silence:
117
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as f:
118
+ sf.write(f.name, final_wave, final_sample_rate)
119
+ remove_silence_for_generated_wav(f.name)
120
+ final_wave, _ = torchaudio.load(f.name)
121
+ final_wave = final_wave.squeeze().cpu().numpy()
122
+
123
+ return (final_sample_rate, final_wave)
124
+
125
+ def load_chat_model():
126
+ """Carga el modelo de chat y el tokenizer"""
127
+ global chat_model_state, chat_tokenizer_state
128
+ if chat_model_state is None:
129
+ model_name = "Qwen/Qwen2.5-3B-Instruct"
130
+ chat_model_state = AutoModelForCausalLM.from_pretrained(
131
+ model_name, torch_dtype=torch.float16, device_map="auto"
132
+ )
133
+ chat_tokenizer_state = AutoTokenizer.from_pretrained(model_name)
134
+ return chat_model_state, chat_tokenizer_state
135
+
136
+ with gr.Blocks() as app_chat:
137
+ gr.Markdown(
138
+ """
139
+ # Chat de Voz
140
+ 隆Mant茅n una conversaci贸n con una IA usando tu voz de referencia!
141
+ 1. Sube un clip de audio de referencia y opcionalmente su transcripci贸n.
142
+ 2. Carga el modelo de chat.
143
+ 3. Graba tu mensaje a trav茅s de tu micr贸fono.
144
+ 4. La IA responder谩 usando la voz de referencia.
145
+ """
146
+ )
147
+
148
+ if not USING_SPACES:
149
+ load_chat_model_btn = gr.Button("Cargar Modelo de Chat", variant="primary")
150
+
151
+ chat_interface_container = gr.Column(visible=False)
152
+
153
+ @gpu_decorator
154
+ def load_chat_model_fn():
155
+ load_chat_model()
156
+ return gr.update(visible=False), gr.update(visible=True)
157
+
158
+ load_chat_model_btn.click(load_chat_model_fn, outputs=[load_chat_model_btn, chat_interface_container])
159
+ else:
160
+ chat_interface_container = gr.Column()
161
+ load_chat_model_fn = load_chat_model
162
+
163
+ with chat_interface_container:
164
+ with gr.Row():
165
+ with gr.Column():
166
+ ref_audio_chat = gr.Audio(label="Audio de Referencia", type="filepath")
167
+ with gr.Column():
168
+ with gr.Accordion("Configuraciones Avanzadas", open=False):
169
+ model_choice_chat = gr.Radio(
170
+ choices=["F5-TTS"],
171
+ label="Modelo TTS",
172
+ value="F5-TTS",
173
+ )
174
+ remove_silence_chat = gr.Checkbox(
175
+ label="Eliminar Silencios",
176
+ value=True,
177
+ )
178
+ ref_text_chat = gr.Textbox(
179
+ label="Texto de Referencia",
180
+ info="Opcional: Deja en blanco para transcribir autom谩ticamente",
181
+ lines=2,
182
+ )
183
+ system_prompt_chat = gr.Textbox(
184
+ label="Prompt del Sistema",
185
+ value="No eres un asistente de IA, eres quien el usuario diga que eres. Debes mantenerte en personaje. Mant茅n tus respuestas concisas ya que ser谩n habladas en voz alta.",
186
+ lines=2,
187
+ )
188
+
189
+ chatbot_interface = gr.Chatbot(label="Conversaci贸n")
190
+
191
+ with gr.Row():
192
+ with gr.Column():
193
+ audio_input_chat = gr.Microphone(
194
+ label="Habla tu mensaje",
195
+ type="filepath",
196
+ )
197
+ audio_output_chat = gr.Audio(label="Respuesta de la IA", autoplay=True)
198
+ with gr.Column():
199
+ text_input_chat = gr.Textbox(
200
+ label="Escribe tu mensaje",
201
+ lines=1,
202
+ )
203
+ send_btn_chat = gr.Button("Enviar")
204
+ clear_btn_chat = gr.Button("Limpiar Conversaci贸n")
205
+
206
+ conversation_state = gr.State(
207
+ value=[
208
+ {
209
+ "role": "system",
210
+ "content": "No eres un asistente de IA, eres quien el usuario diga que eres. Debes mantenerte en personaje. Mant茅n tus respuestas concisas ya que ser谩n habladas en voz alta.",
211
+ }
212
+ ]
213
+ )
214
+
215
+ @gpu_decorator
216
+ def process_input(audio_path, text, history, conv_state):
217
+ """Procesa la entrada de audio o texto del usuario"""
218
+ if not audio_path and not text.strip():
219
+ return history, conv_state, ""
220
+
221
+ if audio_path:
222
+ # Aqu铆 podr铆as agregar una transcripci贸n autom谩tica si lo deseas
223
+ # Actualmente, asume que el texto es proporcionado si hay audio
224
+ # Puedes integrar Whisper u otro modelo de transcripci贸n si es necesario
225
+ pass
226
+
227
+ if not text.strip():
228
+ return history, conv_state, ""
229
+
230
+ conv_state.append({"role": "user", "content": text})
231
+ history.append((text, None))
232
+
233
+ response = generate_response(conv_state, chat_model_state, chat_tokenizer_state)
234
+
235
+ conv_state.append({"role": "assistant", "content": response})
236
+ history[-1] = (text, response)
237
+
238
+ return history, conv_state, response
239
+
240
+ @gpu_decorator
241
+ def generate_audio_response(response, ref_audio, ref_text, model, remove_silence):
242
+ """Genera el audio de respuesta para la IA"""
243
+ if not response or not ref_audio:
244
+ return None
245
+
246
+ audio_result, _ = infer(
247
+ ref_audio,
248
+ ref_text,
249
+ response,
250
+ model,
251
+ remove_silence,
252
+ cross_fade_duration=0.15,
253
+ speed=1.0,
254
+ show_info=print,
255
+ )
256
+ return audio_result
257
+
258
+ def clear_conversation_fn():
259
+ """Limpia la conversaci贸n"""
260
+ return [], [
261
+ {
262
+ "role": "system",
263
+ "content": "No eres un asistente de IA, eres quien el usuario diga que eres. Debes mantenerte en personaje. Mant茅n tus respuestas concisas ya que ser谩n habladas en voz alta.",
264
+ }
265
+ ]
266
+
267
+ def update_system_prompt_fn(new_prompt):
268
+ """Actualiza el prompt del sistema y reinicia la conversaci贸n"""
269
+ new_conv_state = [{"role": "system", "content": new_prompt}]
270
+ return [], new_conv_state
271
+
272
+ # Manejar la entrada de audio
273
+ audio_input_chat.stop_recording(
274
+ process_input,
275
+ inputs=[audio_input_chat, text_input_chat, chatbot_interface, conversation_state],
276
+ outputs=[chatbot_interface, conversation_state, text_input_chat],
277
+ ).then(
278
+ generate_audio_response,
279
+ inputs=[text_input_chat, ref_audio_chat, ref_text_chat, model_choice_chat, remove_silence_chat],
280
+ outputs=[audio_output_chat],
281
+ ).then(
282
+ lambda: None,
283
+ None,
284
+ audio_input_chat,
285
+ )
286
+
287
+ # Manejar la entrada de texto
288
+ text_input_chat.submit(
289
+ process_input,
290
+ inputs=[audio_input_chat, text_input_chat, chatbot_interface, conversation_state],
291
+ outputs=[chatbot_interface, conversation_state, text_input_chat],
292
+ ).then(
293
+ generate_audio_response,
294
+ inputs=[text_input_chat, ref_audio_chat, ref_text_chat, model_choice_chat, remove_silence_chat],
295
+ outputs=[audio_output_chat],
296
+ ).then(
297
+ lambda: None,
298
+ None,
299
+ text_input_chat,
300
+ )
301
+
302
+ # Manejar el bot贸n de enviar
303
+ send_btn_chat.click(
304
+ process_input,
305
+ inputs=[audio_input_chat, text_input_chat, chatbot_interface, conversation_state],
306
+ outputs=[chatbot_interface, conversation_state, text_input_chat],
307
+ ).then(
308
+ generate_audio_response,
309
+ inputs=[text_input_chat, ref_audio_chat, ref_text_chat, model_choice_chat, remove_silence_chat],
310
+ outputs=[audio_output_chat],
311
+ ).then(
312
+ lambda: None,
313
+ None,
314
+ text_input_chat,
315
+ )
316
+
317
+ # Manejar el bot贸n de limpiar conversaci贸n
318
+ clear_btn_chat.click(
319
+ clear_conversation_fn,
320
+ outputs=[chatbot_interface, conversation_state],
321
+ )
322
+
323
+ # Manejar cambios en el prompt del sistema
324
+ system_prompt_chat.change(
325
+ update_system_prompt_fn,
326
+ inputs=system_prompt_chat,
327
+ outputs=[chatbot_interface, conversation_state],
328
+ )
329
+
330
+ def main():
331
+ if not USING_SPACES:
332
+ import click
333
+
334
+ @click.command()
335
+ @click.option("--port", "-p", default=None, type=int, help="Puerto para ejecutar la aplicaci贸n")
336
+ @click.option("--host", "-H", default=None, help="Host para ejecutar la aplicaci贸n")
337
+ @click.option(
338
+ "--share",
339
+ "-s",
340
+ default=False,
341
+ is_flag=True,
342
+ help="Compartir la aplicaci贸n a trav茅s de un enlace compartido de Gradio",
343
+ )
344
+ @click.option("--api", "-a", default=True, is_flag=True, help="Permitir acceso a la API")
345
+ def run_app(port, host, share, api):
346
+ print("Iniciando la aplicaci贸n de Chat AI...")
347
+ app_chat.queue(api_open=api).launch(server_name=host, server_port=port, share=share, show_api=api)
348
+
349
+ run_app()
350
+ else:
351
+ app_chat.queue().launch()
352
 
353
+ if __name__ == "__main__":
354
+ main()