Cristobal299 commited on
Commit
6f14400
Β·
verified Β·
1 Parent(s): e205794

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +120 -28
  2. requirements.txt +2 -1
app.py CHANGED
@@ -1,11 +1,16 @@
1
  import os
2
  import spaces
3
  import torch
 
 
4
  import gradio as gr
5
  from transformers import AutoTokenizer, AutoModelForCausalLM
 
 
6
 
7
  MODEL_ID = "Qwen/Qwen2.5-Coder-7B-Instruct"
8
  API_KEY = os.environ.get("BRAIN_API_KEY", "")
 
9
 
10
  SYSTEM_DEFAULT = (
11
  "Eres FΓ©nix Brain, experto en Python, Gradio, FastAPI y HuggingFace Spaces. "
@@ -14,17 +19,50 @@ SYSTEM_DEFAULT = (
14
 
15
  print("Cargando tokenizer...")
16
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
17
- print("Cargando modelo en CPU...")
18
  model = AutoModelForCausalLM.from_pretrained(
19
  MODEL_ID,
20
  torch_dtype=torch.bfloat16,
21
  low_cpu_mem_usage=True,
22
  trust_remote_code=True,
23
  )
24
- print("Modelo listo")
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  @spaces.GPU
27
  def generar(prompt: str, system: str, key: str, max_tokens: int = 1024) -> str:
 
28
  if API_KEY and key != API_KEY:
29
  return "❌ Unauthorized"
30
  if not prompt.strip():
@@ -37,24 +75,38 @@ def generar(prompt: str, system: str, key: str, max_tokens: int = 1024) -> str:
37
  inputs = tokenizer([text], return_tensors="pt").to(model.device)
38
  with torch.no_grad():
39
  out = model.generate(
40
- **inputs,
41
- max_new_tokens=max_tokens,
42
- temperature=0.2,
43
- do_sample=True,
44
  pad_token_id=tokenizer.eos_token_id,
45
  )
46
  gen = out[0][inputs["input_ids"].shape[1]:]
47
  return tokenizer.decode(gen, skip_special_tokens=True)
48
 
 
49
  @spaces.GPU
50
- def chat_fn(mensaje: str, historial: list, system_prompt: str) -> tuple:
51
- if not mensaje.strip():
52
- return historial, ""
 
 
 
 
 
 
 
 
 
 
 
53
  messages = [{"role": "system", "content": system_prompt or SYSTEM_DEFAULT}]
54
  for h in historial:
55
- messages.append({"role": "user", "content": h["content"] if isinstance(h, dict) else h[0]})
56
- messages.append({"role": "assistant", "content": h["content"] if isinstance(h, dict) else h[1]})
57
- messages.append({"role": "user", "content": mensaje})
 
 
 
 
58
  text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
59
  inputs = tokenizer([text], return_tensors="pt").to(model.device)
60
  with torch.no_grad():
@@ -64,32 +116,72 @@ def chat_fn(mensaje: str, historial: list, system_prompt: str) -> tuple:
64
  )
65
  gen = out[0][inputs["input_ids"].shape[1]:]
66
  respuesta = tokenizer.decode(gen, skip_special_tokens=True)
 
67
  historial = historial + [
68
- {"role": "user", "content": mensaje},
69
  {"role": "assistant", "content": respuesta},
70
  ]
71
- return historial, ""
72
 
73
- with gr.Blocks(title="🧠 Fénix Brain") as demo:
74
- gr.Markdown("# 🧠 Fénix Brain\nQwen2.5-Coder-7B · Asistente de código para La Forja")
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
  with gr.Tab("πŸ’¬ Chat"):
77
- system_box = gr.Textbox(label="System prompt", value=SYSTEM_DEFAULT, lines=2)
78
- chatbot = gr.Chatbot(height=500, type="messages")
79
- msg_box = gr.Textbox(placeholder="Escribe tu pregunta…", label="Mensaje")
80
  with gr.Row():
81
- send_btn = gr.Button("Enviar", variant="primary")
82
- clear_btn = gr.Button("Limpiar")
83
- send_btn.click(chat_fn, [msg_box, chatbot, system_box], [chatbot, msg_box])
84
- msg_box.submit(chat_fn, [msg_box, chatbot, system_box], [chatbot, msg_box])
85
- clear_btn.click(lambda: ([], ""), outputs=[chatbot, msg_box])
86
-
87
- with gr.Tab("πŸ”Œ API"):
88
- gr.Markdown("Llama a esta funciΓ³n desde La Forja con `api_name='/generar'`")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  api_prompt = gr.Textbox(label="Prompt")
90
  api_system = gr.Textbox(label="System", value=SYSTEM_DEFAULT)
91
  api_key_box = gr.Textbox(label="API Key", type="password")
92
  api_out = gr.Textbox(label="Respuesta", lines=10)
93
- gr.Button("Probar").click(generar, [api_prompt, api_system, api_key_box], api_out, api_name="generar")
 
 
94
 
95
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import os
2
  import spaces
3
  import torch
4
+ import tempfile
5
+ import asyncio
6
  import gradio as gr
7
  from transformers import AutoTokenizer, AutoModelForCausalLM
8
+ import edge_tts
9
+ import pdfplumber
10
 
11
  MODEL_ID = "Qwen/Qwen2.5-Coder-7B-Instruct"
12
  API_KEY = os.environ.get("BRAIN_API_KEY", "")
13
+ TTS_VOZ = "es-ES-AlvaroNeural"
14
 
15
  SYSTEM_DEFAULT = (
16
  "Eres FΓ©nix Brain, experto en Python, Gradio, FastAPI y HuggingFace Spaces. "
 
19
 
20
  print("Cargando tokenizer...")
21
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
22
+ print("Cargando modelo...")
23
  model = AutoModelForCausalLM.from_pretrained(
24
  MODEL_ID,
25
  torch_dtype=torch.bfloat16,
26
  low_cpu_mem_usage=True,
27
  trust_remote_code=True,
28
  )
29
+ print("Modelo listo βœ…")
30
 
31
+
32
+ # ── TTS ───────────────────────────────────────────────────────────────────────
33
+ async def _tts_async(texto: str, voz: str) -> str:
34
+ tmp = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False)
35
+ await edge_tts.Communicate(texto[:3000], voz).save(tmp.name)
36
+ return tmp.name
37
+
38
+ def texto_a_voz(texto: str, voz: str) -> str:
39
+ try:
40
+ return asyncio.new_event_loop().run_until_complete(_tts_async(texto, voz))
41
+ except Exception as e:
42
+ print(f"TTS error: {e}")
43
+ return None
44
+
45
+
46
+ # ── Leer documentos ───────────────────────────────────────────────────────────
47
+ def _leer_archivo(path: str) -> str:
48
+ if path.lower().endswith(".pdf"):
49
+ try:
50
+ with pdfplumber.open(path) as pdf:
51
+ return "\n".join(p.extract_text() or "" for p in pdf.pages[:10])
52
+ except Exception as e:
53
+ return f"[Error leyendo PDF: {e}]"
54
+ else:
55
+ try:
56
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
57
+ return f.read()[:8000]
58
+ except Exception as e:
59
+ return f"[Error leyendo archivo: {e}]"
60
+
61
+
62
+ # ── Inferencia ────────────────────────────────────────────────────────────────
63
  @spaces.GPU
64
  def generar(prompt: str, system: str, key: str, max_tokens: int = 1024) -> str:
65
+ """Endpoint API para La Forja."""
66
  if API_KEY and key != API_KEY:
67
  return "❌ Unauthorized"
68
  if not prompt.strip():
 
75
  inputs = tokenizer([text], return_tensors="pt").to(model.device)
76
  with torch.no_grad():
77
  out = model.generate(
78
+ **inputs, max_new_tokens=max_tokens,
79
+ temperature=0.2, do_sample=True,
 
 
80
  pad_token_id=tokenizer.eos_token_id,
81
  )
82
  gen = out[0][inputs["input_ids"].shape[1]:]
83
  return tokenizer.decode(gen, skip_special_tokens=True)
84
 
85
+
86
  @spaces.GPU
87
+ def chat_fn(mensaje: str, archivo, historial: list, system_prompt: str, voz: str, activar_voz: bool):
88
+ if not mensaje.strip() and archivo is None:
89
+ return historial, "", None
90
+
91
+ # AΓ±adir contenido del archivo al mensaje si existe
92
+ texto_archivo = ""
93
+ if archivo is not None:
94
+ contenido = _leer_archivo(archivo.name)
95
+ nombre = os.path.basename(archivo.name)
96
+ texto_archivo = f"\n\n[Archivo: {nombre}]\n{contenido}"
97
+
98
+ prompt_completo = mensaje + texto_archivo
99
+
100
+ # Construir historial
101
  messages = [{"role": "system", "content": system_prompt or SYSTEM_DEFAULT}]
102
  for h in historial:
103
+ if isinstance(h, dict):
104
+ messages.append(h)
105
+ else:
106
+ messages.append({"role": "user", "content": h[0]})
107
+ messages.append({"role": "assistant", "content": h[1]})
108
+ messages.append({"role": "user", "content": prompt_completo})
109
+
110
  text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
111
  inputs = tokenizer([text], return_tensors="pt").to(model.device)
112
  with torch.no_grad():
 
116
  )
117
  gen = out[0][inputs["input_ids"].shape[1]:]
118
  respuesta = tokenizer.decode(gen, skip_special_tokens=True)
119
+
120
  historial = historial + [
121
+ {"role": "user", "content": mensaje + (" πŸ“„" if archivo else "")},
122
  {"role": "assistant", "content": respuesta},
123
  ]
 
124
 
125
+ audio_path = texto_a_voz(respuesta, voz) if activar_voz else None
126
+ return historial, "", audio_path
127
+
128
+
129
+ # ── UI ────────────────────────────────────────────────────────────────────────
130
+ VOCES = {
131
+ "πŸ‡ͺπŸ‡Έ Álvaro (ES)": "es-ES-AlvaroNeural",
132
+ "πŸ‡ͺπŸ‡Έ Elvira (ES)": "es-ES-ElviraNeural",
133
+ "πŸ‡²πŸ‡½ Jorge (MX)": "es-MX-JorgeNeural",
134
+ "πŸ‡²πŸ‡½ Dalia (MX)": "es-MX-DaliaNeural",
135
+ "πŸ‡¦πŸ‡· TomΓ‘s (AR)": "es-AR-TomasNeural",
136
+ }
137
+
138
+ with gr.Blocks(title="🧠 Fénix Brain", theme=gr.themes.Base()) as demo:
139
+ gr.Markdown("# 🧠 Fénix Brain\nQwen2.5-Coder-7B · Documentos · Voz")
140
 
141
  with gr.Tab("πŸ’¬ Chat"):
 
 
 
142
  with gr.Row():
143
+ with gr.Column(scale=3):
144
+ system_box = gr.Textbox(label="System prompt", value=SYSTEM_DEFAULT, lines=2)
145
+ chatbot = gr.Chatbot(height=450, type="messages", label="FΓ©nix Brain")
146
+ msg_box = gr.Textbox(placeholder="Escribe tu mensaje…", label="Mensaje", lines=2)
147
+ with gr.Row():
148
+ send_btn = gr.Button("πŸ“¨ Enviar", variant="primary")
149
+ clear_btn = gr.Button("πŸ—‘οΈ Limpiar")
150
+ with gr.Column(scale=1):
151
+ archivo_in = gr.File(label="πŸ“„ Archivo", file_types=[".pdf", ".txt", ".py", ".md", ".json"])
152
+ activar_voz = gr.Checkbox(label="πŸ”Š Voz activada", value=True)
153
+ voz_sel = gr.Dropdown(list(VOCES.keys()), value="πŸ‡ͺπŸ‡Έ Álvaro (ES)", label="Voz")
154
+ audio_out = gr.Audio(label="πŸ”Š Respuesta", autoplay=True)
155
+
156
+ def _chat(mensaje, archivo, historial, system, voz_nombre, activar):
157
+ voz = VOCES.get(voz_nombre, TTS_VOZ)
158
+ return chat_fn(mensaje, archivo, historial, system, voz, activar)
159
+
160
+ send_btn.click(_chat, [msg_box, archivo_in, chatbot, system_box, voz_sel, activar_voz], [chatbot, msg_box, audio_out])
161
+ msg_box.submit(_chat, [msg_box, archivo_in, chatbot, system_box, voz_sel, activar_voz], [chatbot, msg_box, audio_out])
162
+ clear_btn.click(lambda: ([], "", None), outputs=[chatbot, msg_box, audio_out])
163
+
164
+ with gr.Tab("πŸ”Œ API (La Forja)"):
165
+ gr.Markdown("""
166
+ ### Llamada desde La Forja
167
+ ```python
168
+ from gradio_client import Client
169
+ client = Client("Cristobal299/Chat")
170
+ result = client.predict(
171
+ prompt="Tu pregunta",
172
+ system="",
173
+ key="TU_BRAIN_API_KEY",
174
+ max_tokens=1024,
175
+ api_name="/generar"
176
+ )
177
+ ```
178
+ """)
179
  api_prompt = gr.Textbox(label="Prompt")
180
  api_system = gr.Textbox(label="System", value=SYSTEM_DEFAULT)
181
  api_key_box = gr.Textbox(label="API Key", type="password")
182
  api_out = gr.Textbox(label="Respuesta", lines=10)
183
+ gr.Button("Probar API").click(
184
+ generar, [api_prompt, api_system, api_key_box], api_out, api_name="generar"
185
+ )
186
 
187
  demo.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt CHANGED
@@ -4,4 +4,5 @@ torch>=2.1.0
4
  accelerate>=0.26.0
5
  spaces
6
  gradio_client
7
-
 
 
4
  accelerate>=0.26.0
5
  spaces
6
  gradio_client
7
+ edge-tts
8
+ pdfplumber