JMAA00 commited on
Commit
dd5ab79
·
1 Parent(s): d9fe66d
Files changed (1) hide show
  1. app.py +180 -96
app.py CHANGED
@@ -3,16 +3,20 @@ import gradio as gr
3
  import requests
4
  from huggingface_hub import InferenceClient
5
 
6
- # ============= SERPER CONFIG ====================
 
 
 
 
 
 
 
7
  SERPER_API_KEY = os.getenv("SERPER_API_KEY")
8
 
9
  def do_websearch(query: str) -> str:
10
- """
11
- Si SERPER_API_KEY está definido, llama a serper.dev para obtener resultados,
12
- de lo contrario, indica que no está configurado.
13
- """
14
  if not SERPER_API_KEY:
15
- return "(SERPER_API_KEY no configurado)"
16
 
17
  url = "https://google.serper.dev/search"
18
  headers = {"X-API-KEY": SERPER_API_KEY, "Content-Type": "application/json"}
@@ -26,126 +30,206 @@ def do_websearch(query: str) -> str:
26
 
27
  if "organic" not in data:
28
  return "No se encontraron resultados en serper.dev."
29
-
30
  results = data["organic"]
31
  if not results:
32
  return "No hay resultados relevantes."
33
 
34
- lines = []
35
  for i, item in enumerate(results, start=1):
36
  title = item.get("title", "Sin título")
37
  link = item.get("link", "Sin enlace")
38
- lines.append(f"{i}. {title}\n {link}")
39
- return "\n".join(lines)
 
40
 
41
- # ============= LLM CONFIG (meta-llama) ============
42
- hf_api_token = os.getenv("HF_API_TOKEN") # Necesitas un token con permiso de lectura
 
 
 
43
  client = InferenceClient(
44
  model="meta-llama/Llama-3.1-8B-Instruct",
45
  token=hf_api_token
46
  )
47
 
48
- # ============= FUNCIONES PRINCIPALES =============
49
- def send_message(user_input, use_search, system_msg, history):
 
 
 
 
 
 
 
 
 
 
 
50
  """
51
- Cuando se pulsa 'Enviar', esta función:
52
- 1. Si use_search está activo, llama a do_websearch y concatena resultados al prompt.
53
- 2. Construye la lista 'messages': un 'system' + historial + nuevo user.
54
- 3. Llama al LLM (chat_completion) y obtiene la respuesta.
55
- 4. Actualiza el historial con (user_input, respuesta).
56
- 5. Retorna el nuevo historial.
57
  """
58
 
59
- # Si el usuario dejó en blanco, no hacemos nada
60
- if not user_input.strip():
61
- return history
62
-
63
- final_user_text = user_input
64
  if use_search:
65
- web_info = do_websearch(user_input)
66
- final_user_text += f"\n[Info web]:\n{web_info}"
67
-
68
- # Construimos los mensajes (aquí no hacemos streaming para simplificar)
69
- messages = [{"role": "system", "content": system_msg}]
70
- for (u, a) in history:
71
- messages.append({"role": "user", "content": u})
72
- messages.append({"role": "assistant", "content": a})
73
-
74
- # Mensaje nuevo
75
- messages.append({"role": "user", "content": final_user_text})
76
-
77
- # Llamamos a chat_completion
78
- response = client.chat_completion(
 
 
 
 
 
 
79
  messages=messages,
80
- max_tokens=256, # Ajusta según prefieras
81
- temperature=0.7, # ...
82
- top_p=0.95, # ...
83
- stream=False # Sin streaming para que sea más sencillo
84
- )
85
- # Extraemos el texto devuelto
86
- answer = response.choices[0].message["content"]
87
-
88
- # Actualizamos historial
89
- new_history = history + [(user_input, answer)]
90
- return new_history
91
-
92
- def update_chat(history):
93
- """
94
- Transforma el 'history' (lista de (usuario, asistente)) en un formato
95
- que gr.Chatbot pueda mostrar: [(user, bot), ...].
96
- """
97
- return history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
- # ============= INTERFAZ GRADIO ====================
100
- with gr.Blocks() as demo:
101
- gr.Markdown("## Chat con Llama + WebSearch (versión sencilla)")
102
 
103
- # Caja de texto para "system"
104
  system_box = gr.Textbox(
105
- label="Mensaje del sistema (rol system)",
106
  value=(
107
- "Eres un asistente virtual, amable y empático. Responde en español."
108
- ),
109
- lines=2
110
- )
111
-
112
- # Historial de chat en un gr.State (lista de pares)
113
- chat_history = gr.State([])
114
-
115
- # Muestra la conversación
116
- chatbot = gr.Chatbot(
117
- label="Conversación",
118
- value=[] # Se actualizará con la función update_chat
119
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- # Checkbox para use_search
122
- search_checkbox = gr.Checkbox(
123
- value=False,
124
- label="Usar búsqueda web en serper.dev"
125
- )
126
-
127
- # Campo de texto para la entrada del usuario
128
- user_input = gr.Textbox(
129
- label="Tu mensaje",
130
- placeholder="Escribe algo...",
131
- lines=3
132
- )
133
-
134
- # Botón de enviar
135
- send_btn = gr.Button("Enviar")
136
-
137
- # Al pulsar "Enviar":
138
- # 1) Llamamos a send_message
139
- # 2) Actualizamos chatbot con el nuevo historial
140
  send_btn.click(
141
- fn=send_message,
142
- inputs=[user_input, search_checkbox, system_box, chat_history],
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  outputs=chat_history
144
  ).then(
145
- fn=update_chat,
 
146
  inputs=chat_history,
147
  outputs=chatbot
148
  ).then(
 
149
  fn=lambda: "",
150
  inputs=None,
151
  outputs=user_input
 
3
  import requests
4
  from huggingface_hub import InferenceClient
5
 
6
+ """
7
+ Para más info sobre la Inference API de huggingface_hub:
8
+ https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
9
+ """
10
+
11
+ # ============================
12
+ # CONFIGURACIÓN DE SERPER
13
+ # ============================
14
  SERPER_API_KEY = os.getenv("SERPER_API_KEY")
15
 
16
  def do_websearch(query: str) -> str:
17
+ """ Llama a serper.dev para la búsqueda en Google y retorna texto. """
 
 
 
18
  if not SERPER_API_KEY:
19
+ return "(SERPER_API_KEY no está configurado)"
20
 
21
  url = "https://google.serper.dev/search"
22
  headers = {"X-API-KEY": SERPER_API_KEY, "Content-Type": "application/json"}
 
30
 
31
  if "organic" not in data:
32
  return "No se encontraron resultados en serper.dev."
 
33
  results = data["organic"]
34
  if not results:
35
  return "No hay resultados relevantes."
36
 
37
+ text = []
38
  for i, item in enumerate(results, start=1):
39
  title = item.get("title", "Sin título")
40
  link = item.get("link", "Sin enlace")
41
+ text.append(f"{i}. {title}\n {link}")
42
+
43
+ return "\n".join(text)
44
 
45
+
46
+ # ============================
47
+ # CONFIGURACIÓN DEL MODELO
48
+ # ============================
49
+ hf_api_token = os.getenv("HF_API_TOKEN")
50
  client = InferenceClient(
51
  model="meta-llama/Llama-3.1-8B-Instruct",
52
  token=hf_api_token
53
  )
54
 
55
+
56
+ # ============================
57
+ # LÓGICA DE RESPUESTA
58
+ # ============================
59
+ def respond(
60
+ message: str,
61
+ history: list[tuple[str, str]],
62
+ system_message: str,
63
+ max_tokens: int,
64
+ temperature: float,
65
+ top_p: float,
66
+ use_search: bool
67
+ ):
68
  """
69
+ - 'message': Mensaje del usuario en este turno.
70
+ - 'use_search': si está activo, mezclamos el resultado de do_websearch() con 'message'.
71
+ - 'history': lista [(usuario, asistente), ...]
72
+ - 'system_message': texto rol 'system'
73
+ Generamos la respuesta en streaming con 'chat_completion(..., stream=True)'.
 
74
  """
75
 
76
+ # 1) Si la búsqueda está activa, combinamos user_input + info web
 
 
 
 
77
  if use_search:
78
+ web_info = do_websearch(message)
79
+ # El prompt final será: [user_message] + [resultado web]
80
+ merged_input = f"{message}\nInformación de la web:\n{web_info}"
81
+ else:
82
+ merged_input = message
83
+
84
+ # 2) Reconstruimos la conversación para la API
85
+ messages = [{"role": "system", "content": system_message}]
86
+ for user_msg, assistant_msg in history:
87
+ if user_msg:
88
+ messages.append({"role": "user", "content": user_msg})
89
+ if assistant_msg:
90
+ messages.append({"role": "assistant", "content": assistant_msg})
91
+
92
+ # Agregamos el nuevo turno del usuario
93
+ messages.append({"role": "user", "content": merged_input})
94
+
95
+ # 3) Llamamos a la API en modo streaming
96
+ response_text = ""
97
+ for chunk in client.chat_completion(
98
  messages=messages,
99
+ max_tokens=max_tokens,
100
+ temperature=temperature,
101
+ top_p=top_p,
102
+ stream=True
103
+ ):
104
+ token = chunk.choices[0].delta.get("content", "")
105
+ response_text += token
106
+ yield response_text
107
+
108
+
109
+ # ============================
110
+ # CSS PARA EL TOGGLE EN INPUT
111
+ # ============================
112
+ css_code = """
113
+ #input_row {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: 0.5rem;
117
+ margin-top: 1rem;
118
+ }
119
+ /* Caja contenedora */
120
+ #input_container {
121
+ position: relative;
122
+ flex: 1;
123
+ }
124
+
125
+ /* Checkbox estilo toggle en la parte inferior izq. */
126
+ #search_toggle {
127
+ position: absolute;
128
+ left: 0;
129
+ bottom: -2rem;
130
+ display: inline-flex;
131
+ align-items: center;
132
+ cursor: pointer;
133
+ padding: 0.2rem 0.4rem;
134
+ border-radius: 0.25rem;
135
+ font-size: 0.9rem;
136
+ border: 1px solid #ccc;
137
+ background-color: #eee;
138
+ color: #333;
139
+ }
140
+ #search_toggle input[type="checkbox"]:checked + label {
141
+ background-color: #0272f5; /* color de botón gradio */
142
+ color: white;
143
+ border: none;
144
+ }
145
+
146
+ /* Ajustes al label 'search_toggle_label' */
147
+ #search_toggle_label {
148
+ margin-left: 0.3rem;
149
+ }
150
+
151
+ /* Botón 'send' con estilo minimal */
152
+ #send_button {
153
+ background-color: #0272f5;
154
+ color: white;
155
+ border: none;
156
+ border-radius: 0.25rem;
157
+ padding: 0.5rem 1rem;
158
+ cursor: pointer;
159
+ }
160
+ #send_button:hover {
161
+ background-color: #005dc4;
162
+ }
163
+ """
164
+
165
+ # ============================
166
+ # INTERFAZ GRADIO
167
+ # ============================
168
+ with gr.Blocks(css=css_code) as demo:
169
+ gr.Markdown("# Chat con WebSearch (toggle en la parte inferior del input)")
170
+
171
+ # Historial en un gr.State (lista de pares (user, asst))
172
+ chat_history = gr.State([])
173
 
174
+ # Chat principal
175
+ chatbot = gr.Chatbot(label="Conversación", value=[])
 
176
 
177
+ # Elementos de configuración
178
  system_box = gr.Textbox(
179
+ label="Mensaje del sistema",
180
  value=(
181
+ "Eres Juan, un asistente virtual en español. "
182
+ "Debes responder con paciencia y empatía a usuarios con dificultades cognitivas."
183
+ )
 
 
 
 
 
 
 
 
 
184
  )
185
+ max_tokens_slider = gr.Slider(1, 2048, 512, step=1, label="Máxima cantidad de tokens")
186
+ temp_slider = gr.Slider(0.1, 4.0, 0.7, step=0.1, label="Temperatura")
187
+ top_p_slider = gr.Slider(0.1, 1.0, 0.95, step=0.05, label="Top-p (muestreo por núcleo)")
188
+
189
+ # Fila con input y send
190
+ with gr.Row(elem_id="input_row"):
191
+ with gr.Column(elem_id="input_container"):
192
+ user_input = gr.Textbox(
193
+ show_label=False,
194
+ placeholder="Tu mensaje aquí...",
195
+ lines=4
196
+ )
197
+ # Checkbox '🌐 Búsqueda' en la parte inferior izq. del input
198
+ with gr.Box(elem_id="search_toggle"):
199
+ search_checkbox = gr.Checkbox(value=False, interactive=True)
200
+ gr.Label("🌐 Búsqueda", elem_id="search_toggle_label")
201
+
202
+ send_btn = gr.Button("Enviar", elem_id="send_button")
203
+
204
+ # Función para actualizar el historial en el Chatbot
205
+ def update_history(history):
206
+ return history
207
 
208
+ # Al pulsar "Enviar", llamamos a respond en streaming
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  send_btn.click(
210
+ fn=respond,
211
+ inputs=[
212
+ user_input,
213
+ chat_history,
214
+ system_box,
215
+ max_tokens_slider,
216
+ temp_slider,
217
+ top_p_slider,
218
+ search_checkbox # use_search
219
+ ],
220
+ outputs=None # Output en streaming
221
+ ).then(
222
+ # Recogemos la respuesta final generada y la metemos en el historial
223
+ fn=lambda out, message, hist: hist + [(message, out)],
224
+ inputs=[gr.Button.stream_output, user_input, chat_history],
225
  outputs=chat_history
226
  ).then(
227
+ # Actualizamos el Chatbot
228
+ fn=update_history,
229
  inputs=chat_history,
230
  outputs=chatbot
231
  ).then(
232
+ # Limpiamos la caja de texto
233
  fn=lambda: "",
234
  inputs=None,
235
  outputs=user_input