patopla commited on
Commit
9825718
·
verified ·
1 Parent(s): 10d67ec

Upload 4 files

Browse files
Files changed (4) hide show
  1. README.md +6 -8
  2. app.py +21 -370
  3. requirements.txt +5 -15
  4. tools.py +62 -0
README.md CHANGED
@@ -1,15 +1,13 @@
1
  ---
2
- title: Template Final Assignment
3
- emoji: 🕵🏻‍♂️
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: gradio
7
- sdk_version: 5.25.2
8
  app_file: app.py
9
  pinned: false
10
- hf_oauth: true
11
- # optional, default duration is 8 hours/480 minutes. Max duration is 30 days/43200 minutes.
12
- hf_oauth_expiration_minutes: 480
13
  ---
14
 
15
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: AgenticRAG
3
+ emoji: 🌖
4
+ colorFrom: pink
5
+ colorTo: pink
6
  sdk: gradio
7
+ sdk_version: 5.29.1
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
 
 
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py CHANGED
@@ -1,380 +1,31 @@
1
- import os
2
  import gradio as gr
3
- import requests
4
- import inspect
5
- import pandas as pd
6
- from text_analyzer import TextAnalyzer
7
- import json
8
 
9
- # (Keep Constants as is)
10
- # --- Constants ---
11
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
 
13
- # --- Basic Agent Definition ---
14
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
15
- class BasicAgent:
16
- def __init__(self):
17
- print("BasicAgent initialized.")
18
- self.analyzer = TextAnalyzer()
19
-
20
- def __call__(self, text: str) -> str:
21
- print(f"Agent received text (first 50 chars): {text[:50]}...")
22
- try:
23
- resultado = self.analyzer(text)
24
-
25
- # Formatear la respuesta según el tipo de texto
26
- if resultado.get("tipo") == "historia_usuario":
27
- return self._format_user_story_response(resultado)
28
- elif resultado.get("tipo") == "pregunta_general":
29
- return self._format_general_question_response(resultado)
30
- else:
31
- return f"Error: {resultado.get('error', 'Tipo de texto no reconocido')}"
32
- except Exception as e:
33
- error_msg = f"Error analizando el texto: {str(e)}"
34
- print(error_msg)
35
- return error_msg
36
-
37
- def _format_user_story_response(self, resultado: dict) -> str:
38
- """Formatea la respuesta para una historia de usuario."""
39
- respuesta = []
40
- if resultado["tiene_ambiguedad"]:
41
- respuesta.append("Se encontraron las siguientes ambigüedades:")
42
-
43
- if resultado["ambiguedad_lexica"]:
44
- respuesta.append("\nAmbigüedades léxicas:")
45
- for amb in resultado["ambiguedad_lexica"]:
46
- respuesta.append(f"- {amb}")
47
-
48
- if resultado["ambiguedad_sintactica"]:
49
- respuesta.append("\nAmbigüedades sintácticas:")
50
- for amb in resultado["ambiguedad_sintactica"]:
51
- respuesta.append(f"- {amb}")
52
-
53
- respuesta.append(f"\nScore de ambigüedad: {resultado['score_ambiguedad']}")
54
- respuesta.append("\nSugerencias de mejora:")
55
- for sug in resultado["sugerencias"]:
56
- respuesta.append(f"- {sug}")
57
- else:
58
- respuesta.append("No se encontraron ambigüedades en la historia de usuario.")
59
- respuesta.append(f"Score de ambigüedad: {resultado['score_ambiguedad']}")
60
-
61
- return "\n".join(respuesta)
62
-
63
- def _format_general_question_response(self, resultado: dict) -> str:
64
- """Formatea la respuesta para una pregunta general."""
65
- analisis = resultado["analisis"]
66
- respuesta = []
67
-
68
- respuesta.append("📝 Análisis de la pregunta:")
69
- if analisis["is_question"]:
70
- respuesta.append(f"• Tipo de pregunta: {analisis['question_type'] or 'No identificado'}")
71
-
72
- if analisis["entities"]:
73
- respuesta.append("\n🏷️ Entidades identificadas:")
74
- for ent, label in analisis["entities"]:
75
- respuesta.append(f"• {ent} ({label})")
76
-
77
- if analisis["key_phrases"]:
78
- respuesta.append("\n🔑 Frases clave identificadas:")
79
- for phrase in analisis["key_phrases"]:
80
- respuesta.append(f"• {phrase}")
81
-
82
- if resultado["sugerencias"]:
83
- respuesta.append("\n💡 Sugerencias:")
84
- for sug in resultado["sugerencias"]:
85
- respuesta.append(f"• {sug}")
86
-
87
- return "\n".join(respuesta)
88
 
89
- def run_and_submit_all( profile: gr.OAuthProfile | None):
90
- """
91
- Fetches all questions, runs the BasicAgent on them, submits all answers,
92
- and displays the results.
93
- """
94
- # --- Determine HF Space Runtime URL and Repo URL ---
95
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
96
 
97
- if profile:
98
- username= f"{profile.username}"
99
- print(f"User logged in: {username}")
100
- else:
101
- print("User not logged in.")
102
- return "Please Login to Hugging Face with the button.", None
103
 
104
- api_url = DEFAULT_API_URL
105
- questions_url = f"{api_url}/questions"
106
- submit_url = f"{api_url}/submit"
107
 
108
- # 1. Instantiate Agent ( modify this part to create your agent)
109
- try:
110
- agent = BasicAgent()
111
- except Exception as e:
112
- print(f"Error instantiating agent: {e}")
113
- return f"Error initializing agent: {e}", None
114
- # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
115
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
116
- print(agent_code)
117
 
118
- # 2. Fetch Questions
119
- print(f"Fetching questions from: {questions_url}")
120
- try:
121
- response = requests.get(questions_url, timeout=15)
122
- response.raise_for_status()
123
- questions_data = response.json()
124
- if not questions_data:
125
- print("Fetched questions list is empty.")
126
- return "Fetched questions list is empty or invalid format.", None
127
- print(f"Fetched {len(questions_data)} questions.")
128
- except requests.exceptions.RequestException as e:
129
- print(f"Error fetching questions: {e}")
130
- return f"Error fetching questions: {e}", None
131
- except requests.exceptions.JSONDecodeError as e:
132
- print(f"Error decoding JSON response from questions endpoint: {e}")
133
- print(f"Response text: {response.text[:500]}")
134
- return f"Error decoding server response for questions: {e}", None
135
- except Exception as e:
136
- print(f"An unexpected error occurred fetching questions: {e}")
137
- return f"An unexpected error occurred fetching questions: {e}", None
138
-
139
- # 3. Run your Agent
140
- results_log = []
141
- answers_payload = []
142
- print(f"Running agent on {len(questions_data)} questions...")
143
- for item in questions_data:
144
- task_id = item.get("task_id")
145
- question_text = item.get("question")
146
- if not task_id or question_text is None:
147
- print(f"Skipping item with missing task_id or question: {item}")
148
- continue
149
- try:
150
- submitted_answer = agent(question_text)
151
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
152
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
153
- except Exception as e:
154
- print(f"Error running agent on task {task_id}: {e}")
155
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
156
-
157
- if not answers_payload:
158
- print("Agent did not produce any answers to submit.")
159
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
160
-
161
- # 4. Prepare Submission
162
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
163
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
164
- print(status_update)
165
-
166
- # 5. Submit
167
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
168
- try:
169
- response = requests.post(submit_url, json=submission_data, timeout=60)
170
- response.raise_for_status()
171
- result_data = response.json()
172
- final_status = (
173
- f"Submission Successful!\n"
174
- f"User: {result_data.get('username')}\n"
175
- f"Overall Score: {result_data.get('score', 'N/A')}% "
176
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
177
- f"Message: {result_data.get('message', 'No message received.')}"
178
- )
179
- print("Submission successful.")
180
- results_df = pd.DataFrame(results_log)
181
- return final_status, results_df
182
- except requests.exceptions.HTTPError as e:
183
- error_detail = f"Server responded with status {e.response.status_code}."
184
- try:
185
- error_json = e.response.json()
186
- error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
187
- except requests.exceptions.JSONDecodeError:
188
- error_detail += f" Response: {e.response.text[:500]}"
189
- status_message = f"Submission Failed: {error_detail}"
190
- print(status_message)
191
- results_df = pd.DataFrame(results_log)
192
- return status_message, results_df
193
- except requests.exceptions.Timeout:
194
- status_message = "Submission Failed: The request timed out."
195
- print(status_message)
196
- results_df = pd.DataFrame(results_log)
197
- return status_message, results_df
198
- except requests.exceptions.RequestException as e:
199
- status_message = f"Submission Failed: Network error - {e}"
200
- print(status_message)
201
- results_df = pd.DataFrame(results_log)
202
- return status_message, results_df
203
- except Exception as e:
204
- status_message = f"An unexpected error occurred during submission: {e}"
205
- print(status_message)
206
- results_df = pd.DataFrame(results_log)
207
- return status_message, results_df
208
-
209
-
210
- # Inicializar el clasificador
211
- classifier = TextAnalyzer()
212
-
213
- def analyze_text(text: str) -> str:
214
- """Analiza un texto y determina automáticamente si es una historia de usuario o una pregunta general."""
215
- if not text.strip():
216
- return "Por favor, ingrese un texto para analizar."
217
-
218
- # Analizar el texto
219
- result = classifier(text)
220
-
221
- # Formatear resultados según el tipo
222
- output = []
223
- output.append(f"📝 Texto analizado:\n{text}\n")
224
-
225
- if result.get("tipo") == "historia_usuario":
226
- output.append("📋 Tipo: Historia de Usuario")
227
- output.append(f"🎯 Score de ambigüedad: {result['score_ambiguedad']}")
228
-
229
- if result['ambiguedad_lexica']:
230
- output.append("\n📚 Ambigüedades léxicas encontradas:")
231
- for amb in result['ambiguedad_lexica']:
232
- output.append(f"• {amb}")
233
-
234
- if result['ambiguedad_sintactica']:
235
- output.append("\n🔍 Ambigüedades sintácticas encontradas:")
236
- for amb in result['ambiguedad_sintactica']:
237
- output.append(f"• {amb}")
238
-
239
- if result['sugerencias']:
240
- output.append("\n💡 Sugerencias de mejora:")
241
- for sug in result['sugerencias']:
242
- output.append(f"• {sug}")
243
-
244
- elif result.get("tipo") == "pregunta_general":
245
- output.append("📋 Tipo: Pregunta General")
246
- analisis = result['analisis']
247
-
248
- if analisis['is_question']:
249
- output.append(f"❓ Tipo de pregunta: {analisis['question_type'] or 'No identificado'}")
250
-
251
- if analisis['entities']:
252
- output.append("\n🏷️ Entidades identificadas:")
253
- for ent, label in analisis['entities']:
254
- output.append(f"• {ent} ({label})")
255
-
256
- if analisis['key_phrases']:
257
- output.append("\n🔑 Frases clave:")
258
- for phrase in analisis['key_phrases']:
259
- output.append(f"• {phrase}")
260
-
261
- output.append("\n💡 Sugerencias:")
262
- for sug in result['sugerencias']:
263
- output.append(f"• {sug}")
264
-
265
- else:
266
- output.append("❌ Error: No se pudo determinar el tipo de texto")
267
-
268
- return "\n".join(output)
269
-
270
- def analyze_multiple_texts(texts: str) -> str:
271
- """Analiza múltiples textos separados por líneas."""
272
- if not texts.strip():
273
- return "Por favor, ingrese al menos un texto para analizar."
274
-
275
- texts_list = [t.strip() for t in texts.split('\n') if t.strip()]
276
- all_results = []
277
-
278
- for text in texts_list:
279
- result = classifier(text)
280
- result["texto_original"] = text
281
- all_results.append(result)
282
-
283
- return json.dumps(all_results, indent=2, ensure_ascii=False)
284
-
285
- # --- Build Gradio Interface using Blocks ---
286
- with gr.Blocks(title="Analizador de Textos") as demo:
287
- gr.Markdown("""
288
- # 🔍 Analizador de Textos
289
-
290
- Esta herramienta analiza dos tipos de texto:
291
- 1. **Historias de Usuario**: Detecta ambigüedades léxicas y sintácticas
292
- 2. **Preguntas Generales**: Analiza estructura y contexto
293
-
294
- ## 📝 Instrucciones:
295
- 1. Ingrese su texto en el campo correspondiente
296
- 2. El sistema detectará automáticamente el tipo de texto
297
- 3. Revise el análisis detallado y las sugerencias
298
- """)
299
-
300
- with gr.Tab("Análisis Individual"):
301
- input_text = gr.Textbox(
302
- label="Texto a Analizar",
303
- placeholder="Ingrese una historia de usuario o una pregunta general...",
304
- lines=3
305
- )
306
- analyze_btn = gr.Button("Analizar")
307
- output = gr.Textbox(
308
- label="Resultados del Análisis",
309
- lines=10
310
- )
311
- analyze_btn.click(
312
- analyze_text,
313
- inputs=[input_text],
314
- outputs=[output]
315
- )
316
-
317
- with gr.Tab("Análisis Múltiple"):
318
- input_texts = gr.Textbox(
319
- label="Textos a Analizar (uno por línea)",
320
- placeholder="Como usuario quiero...\n¿Cuál es el proceso para...?\n",
321
- lines=5
322
- )
323
- analyze_multi_btn = gr.Button("Analizar Todos")
324
- output_json = gr.JSON(label="Resultados del Análisis")
325
- analyze_multi_btn.click(
326
- analyze_multiple_texts,
327
- inputs=[input_texts],
328
- outputs=[output_json]
329
- )
330
-
331
- gr.Markdown("""
332
- ## 🚀 Ejemplos
333
-
334
- ### Historias de Usuario:
335
- - Como usuario quiero un sistema rápido y eficiente para gestionar mis tareas
336
- - El sistema debe permitir exportar varios tipos de archivos
337
- - Como administrador necesito acceder fácilmente a los reportes
338
-
339
- ### Preguntas Generales:
340
- - ¿Cuál es el proceso para recuperar una contraseña olvidada?
341
- - ¿Cómo puedo generar un reporte mensual de ventas?
342
- - ¿Dónde encuentro la documentación del API?
343
- """)
344
-
345
- gr.LoginButton()
346
-
347
- run_button = gr.Button("Run Evaluation & Submit All Answers")
348
-
349
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
350
- # Removed max_rows=10 from DataFrame constructor
351
- results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
352
-
353
- run_button.click(
354
- fn=run_and_submit_all,
355
- outputs=[status_output, results_table]
356
- )
357
 
358
  if __name__ == "__main__":
359
- print("\n" + "-"*30 + " App Starting " + "-"*30)
360
- # Check for SPACE_HOST and SPACE_ID at startup for information
361
- space_host_startup = os.getenv("SPACE_HOST")
362
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
363
-
364
- if space_host_startup:
365
- print(f"✅ SPACE_HOST found: {space_host_startup}")
366
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
367
- else:
368
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
369
-
370
- if space_id_startup: # Print repo URLs if SPACE_ID is found
371
- print(f"✅ SPACE_ID found: {space_id_startup}")
372
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
373
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
374
- else:
375
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
376
-
377
- print("-"*(60 + len(" App Starting ")) + "\n")
378
-
379
- print("Launching Gradio Interface for Basic Agent Evaluation...")
380
- demo.launch(debug=True, share=False)
 
 
1
  import gradio as gr
2
+ import random
3
+ from smolagents import GradioUI, CodeAgent, HfApiModel
 
 
 
4
 
5
+ # Import our custom tools from their modules
6
+ from tools import DuckDuckGoSearchTool, CurrencyConverterTool
7
+ from retriever import load_guest_dataset
8
 
9
+ # Initialize the Hugging Face model
10
+ model = HfApiModel()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # Initialize the web search tool
13
+ search_tool = DuckDuckGoSearchTool()
 
 
 
 
 
14
 
15
+ # Initialize the weather tool
16
+ currency_converter_tool = CurrencyConverterTool()
 
 
 
 
17
 
 
 
 
18
 
19
+ # Load the guest dataset and initialize the guest info tool
20
+ guest_info_tool = load_guest_dataset()
 
 
 
 
 
 
 
21
 
22
+ # Create Alfred with all the tools
23
+ alfred = CodeAgent(
24
+ tools=[currency_converter_tool, search_tool],
25
+ model=model,
26
+ add_base_tools=True, # Add any additional base tools
27
+ planning_interval=3 # Enable planning every 3 steps
28
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  if __name__ == "__main__":
31
+ GradioUI(alfred).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,15 +1,5 @@
1
- gradio>=5.25.2
2
- requests>=2.31.0
3
- spacy>=3.7.0
4
- es-core-news-sm @ https://github.com/explosion/spacy-models/releases/download/es_core_news_sm-3.7.0/es_core_news_sm-3.7.0-py3-none-any.whl
5
- transformers>=4.30.0
6
- torch>=2.0.0
7
- numpy>=1.24.0
8
- pandas>=2.2.0
9
- pytesseract>=0.3.10
10
- opencv-python>=4.8.0
11
- librosa>=0.10.1
12
- Pillow>=10.0.0
13
- openpyxl>=3.1.2
14
- scikit-learn>=1.3.0
15
- setuptools>=69.1.0
 
1
+ datasets
2
+ smolagents
3
+ langchain-community
4
+ rank_bm25
5
+ duckduckgo-search
 
 
 
 
 
 
 
 
 
 
tools.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import DuckDuckGoSearchTool
2
+ from smolagents import Tool
3
+
4
+
5
+ # Initialize the DuckDuckGo search tool
6
+ search_tool = DuckDuckGoSearchTool()
7
+
8
+
9
+ import random
10
+
11
+ class CurrencyConverterTool(Tool):
12
+ name = "currency_converter"
13
+ description = "Converts amounts between different currencies using dummy exchange rates."
14
+ inputs = {
15
+ "amount": {
16
+ "type": "float",
17
+ "description": "The amount to convert."
18
+ },
19
+ "from_currency": {
20
+ "type": "string",
21
+ "description": "The source currency code (e.g., USD, EUR, CLP)."
22
+ },
23
+ "to_currency": {
24
+ "type": "string",
25
+ "description": "The target currency code (e.g., USD, EUR, CLP)."
26
+ }
27
+ }
28
+ output_type = "string"
29
+
30
+ def forward(self, amount: float, from_currency: str, to_currency: str):
31
+ # Dummy exchange rates (base: USD)
32
+ exchange_rates = {
33
+ "USD": 1.0,
34
+ "EUR": 0.85,
35
+ "CLP": 800.0,
36
+ "ARS": 350.0,
37
+ "BRL": 5.2,
38
+ "MXN": 18.5,
39
+ "GBP": 0.75,
40
+ "JPY": 110.0,
41
+ "CAD": 1.25
42
+ }
43
+
44
+ # Validate currencies
45
+ if from_currency not in exchange_rates or to_currency not in exchange_rates:
46
+ available_currencies = ", ".join(exchange_rates.keys())
47
+ return f"Error: Moneda no soportada. Divisas disponibles: {available_currencies}"
48
+
49
+ # Same currency conversion
50
+ if from_currency == to_currency:
51
+ return f"{amount:.2f} {from_currency} = {amount:.2f} {to_currency} (misma divisa)"
52
+
53
+ # Convert to USD first, then to target currency
54
+ usd_amount = amount / exchange_rates[from_currency]
55
+ converted_amount = usd_amount * exchange_rates[to_currency]
56
+
57
+ # Add small random variation to simulate real market fluctuations (±2%)
58
+ variation = random.uniform(-0.02, 0.02)
59
+ converted_amount *= (1 + variation)
60
+
61
+ return f"{amount:.2f} {from_currency} = {converted_amount:.2f} {to_currency} (tasa simulada)"
62
+