matheuscs commited on
Commit
ee2f945
·
1 Parent(s): 4e574ef

editing template to add my agent

Browse files
Files changed (2) hide show
  1. app.py +325 -15
  2. requirements.txt +8 -2
app.py CHANGED
@@ -1,23 +1,285 @@
1
- import os
2
  import gradio as gr
3
- import requests
4
- import inspect
5
  import pandas as pd
 
 
 
 
 
 
 
 
 
 
6
 
7
  # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
 
11
  # --- Basic Agent Definition ---
12
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
- class BasicAgent:
14
- def __init__(self):
15
- print("BasicAgent initialized.")
16
- def __call__(self, question: str) -> str:
17
- print(f"Agent received question (first 50 chars): {question[:50]}...")
18
- fixed_answer = "This is a default answer."
19
- print(f"Agent returning fixed answer: {fixed_answer}")
20
- return fixed_answer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  def run_and_submit_all( profile: gr.OAuthProfile | None):
23
  """
@@ -40,11 +302,16 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
40
 
41
  # 1. Instantiate Agent ( modify this part to create your agent)
42
  try:
43
- agent = BasicAgent()
 
 
 
 
 
44
  except Exception as e:
45
  print(f"Error instantiating agent: {e}")
46
  return f"Error initializing agent: {e}", None
47
- # 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)
48
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
49
  print(agent_code)
50
 
@@ -76,11 +343,54 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
76
  for item in questions_data:
77
  task_id = item.get("task_id")
78
  question_text = item.get("question")
 
79
  if not task_id or question_text is None:
80
  print(f"Skipping item with missing task_id or question: {item}")
81
  continue
82
  try:
83
- submitted_answer = agent(question_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
85
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
86
  except Exception as e:
 
 
1
  import gradio as gr
 
 
2
  import pandas as pd
3
+ from smolagents import CodeAgent, OpenAIServerModel, tool
4
+ import os, subprocess
5
+ from bs4 import BeautifulSoup
6
+ from duckduckgo_search import DDGS
7
+ import csv
8
+ import json
9
+ import requests
10
+ import whisper
11
+ from typing import Optional
12
+ import openpyxl
13
 
14
  # (Keep Constants as is)
15
  # --- Constants ---
16
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
17
 
18
  # --- Basic Agent Definition ---
19
+ # ----- THIS IS WHERE YOU CAN BUILD WHAT YOU WANT ------
20
+ def download_file(file_name: str) -> None:
21
+ if not os.path.exists(file_name):
22
+ url = f"{DEFAULT_API_URL}/files/{file_name.split('.')[0]}"
23
+ r = requests.get(url)
24
+ with open(file_name, "wb") as f:
25
+ f.write(r.content)
26
+
27
+ @tool
28
+ def open_file_as_text(file_name: str, filetype: Optional[str] = "txt") -> str:
29
+ """
30
+ Opens a file and returns its content as readable text.
31
+
32
+ Supports 'txt', 'json', 'csv', 'xlsx', and 'mp3' (transcribes speech to text).
33
+
34
+ Args:
35
+ file_name (str): The path or name of the file.
36
+ filetype (Optional[str]): Type of file ('txt', 'json', 'csv', 'xlsx', 'mp3'). Defaults to 'txt'.
37
+
38
+ Returns:
39
+ str: The content of the file as text, or transcribed speech if 'mp3'.
40
+ """
41
+ download_file(file_name)
42
+ try:
43
+ if filetype == "txt":
44
+ with open(file_name, "r", encoding="utf-8") as f:
45
+ return f.read()
46
+
47
+ elif filetype == "json":
48
+ with open(file_name, "r", encoding="utf-8") as f:
49
+ data = json.load(f)
50
+ return json.dumps(data, indent=2)
51
+
52
+ elif filetype == "csv":
53
+ with open(file_name, "r", encoding="utf-8") as f:
54
+ reader = csv.reader(f)
55
+ rows = list(reader)
56
+ return "\n".join([", ".join(row) for row in rows])
57
+
58
+ elif filetype == "xlsx":
59
+ wb = openpyxl.load_workbook(file_name, data_only=True)
60
+ sheet = wb.active
61
+ content = []
62
+ for row in sheet.iter_rows(values_only=True):
63
+ content.append(", ".join(str(cell) if cell is not None else "" for cell in row))
64
+ return "\n".join(content)
65
+
66
+ elif filetype == "mp3":
67
+ w = whisper.load_model("base")
68
+ res = w.transcribe(file_name)
69
+ return res["text"]
70
+
71
+ else:
72
+ return f"Unsupported filetype '{filetype}'. Supported types are 'txt', 'json', 'csv', 'xlsx', and 'mp3'."
73
+
74
+ except FileNotFoundError:
75
+ return f"File '{file_name}' not found."
76
+ except Exception as e:
77
+ return f"Error opening file '{file_name}': {str(e)}"
78
+
79
+ @tool
80
+ def web_search(query: str) -> str:
81
+ """
82
+ Searches the web using DuckDuckGo and returns top search snippets.
83
+
84
+ Args:
85
+ query (str): The search query string.
86
+
87
+ Returns:
88
+ str: A list of top search results with title, snippet, and URL.
89
+ """
90
+ try:
91
+ with DDGS() as ddgs:
92
+ results = ddgs.text(query, max_results=3)
93
+ if not results:
94
+ return "No results found."
95
+ return "\n\n".join([f"Title: {r['title']}\nSnippet: {r['body']}\nURL: {r['href']}" for r in results])
96
+ except Exception as e:
97
+ return f"Error during search: {str(e)}"
98
+
99
+ def parse_wikipedia_table(table) -> str:
100
+ """
101
+ Parses a Wikipedia table into a clean, readable text format.
102
+
103
+ Args:
104
+ table (Tag): BeautifulSoup Tag for the table.
105
+
106
+ Returns:
107
+ str: Formatted table as readable text.
108
+ """
109
+ rows = []
110
+ headers = []
111
+
112
+ # Try to get headers
113
+ thead = table.find('thead')
114
+ if thead:
115
+ for th in thead.find_all('th'):
116
+ header_text = th.get_text(separator=" ", strip=True)
117
+ headers.append(header_text)
118
+ if headers:
119
+ rows.append(" | ".join(headers))
120
+
121
+ # Parse table body rows
122
+ tbody = table.find('tbody')
123
+ if not tbody:
124
+ tbody = table # fallback: some tables have no tbody explicitly
125
+
126
+ for tr in tbody.find_all('tr'):
127
+ cells = tr.find_all(['th', 'td'])
128
+ cell_texts = []
129
+ for cell in cells:
130
+ # Clean references like [7], [note 1], etc.
131
+ for sup in cell.find_all('sup', class_='reference'):
132
+ sup.decompose()
133
+
134
+ text = cell.get_text(separator=" ", strip=True)
135
+ cell_texts.append(text)
136
+
137
+ if cell_texts:
138
+ row_text = " | ".join(cell_texts)
139
+ rows.append(row_text)
140
+
141
+ return "\n".join(rows)
142
+
143
+ @tool
144
+ def read_wikipedia_page(url: str) -> str:
145
+ """
146
+ Fetches a Wikipedia article and extracts clean sectioned text around the relevant query.
147
+
148
+ Args:
149
+ url (str): The Wikipedia page URL.
150
+
151
+ Returns:
152
+ str: Sectioned and readable snippet focused around the query.
153
+ """
154
+ headers = {
155
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
156
+ }
157
+ resp = requests.get(url, headers=headers, timeout=10)
158
+ resp.raise_for_status()
159
+ soup = BeautifulSoup(resp.text, "html.parser")
160
+
161
+ content_div = soup.find('div', id='mw-content-text')
162
+ if not content_div:
163
+ return "Content not found."
164
+
165
+ parts = []
166
+ for elem in content_div.find_all(['h2', 'h3', 'p', 'ul', 'ol', 'table']):
167
+ if elem.name in ['h2', 'h3']:
168
+ parts.append("\n\n" + elem.get_text(strip=True) + "\n")
169
+ elif elem.name in ['p', 'ul', 'ol']:
170
+ parts.append(elem.get_text(strip=True))
171
+ elif elem.name == 'table':
172
+ parts.append(parse_wikipedia_table(elem))
173
+
174
+ full_text = "\n".join(parts)
175
+
176
+ return full_text
177
+
178
+ @tool
179
+ def smart_paginate_around_query(full_text: str, query: str) -> list:
180
+ """
181
+ Splits text into windows around each occurrence of the query.
182
+
183
+ Args:
184
+ full_text (str): The full text to search within.
185
+ query (str): The search query.
186
+
187
+ Returns:
188
+ list: List of relevant text windows (pages).
189
+ """
190
+ before_chars = 1000
191
+ after_chars = 3000
192
+ full_text_lower = full_text.lower()
193
+ query_lower = query.lower()
194
+ query_len = len(query_lower)
195
+
196
+ pages = []
197
+ search_pos = 0
198
+ text_len = len(full_text)
199
+
200
+ while True:
201
+ match_pos = full_text_lower.find(query_lower, search_pos)
202
+
203
+ if match_pos == -1:
204
+ break # no more matches
205
+
206
+ # Define window around match
207
+ start = max(0, match_pos - before_chars)
208
+ end = min(text_len, match_pos + query_len + after_chars)
209
+
210
+ page = full_text[start:end]
211
+ pages.append(page)
212
+
213
+ # Move search pointer to AFTER current window
214
+ search_pos = end
215
+
216
+ return pages
217
+
218
+ @tool
219
+ def reverse_sentence(text: str) -> str:
220
+ """
221
+ Reverses the input text.
222
+
223
+ Args:
224
+ text (str): The input string to be reversed.
225
+
226
+ Returns:
227
+ str: The reversed string.
228
+ """
229
+ return text[::-1]
230
+
231
+ @tool
232
+ def run_python_code(file_name: str) -> str:
233
+ """
234
+ Executes a Python file and returns its printed final output.
235
+
236
+ Args:
237
+ file_name (str): Name of the Python file.
238
+
239
+ Returns:
240
+ str: The final printed output.
241
+ """
242
+ download_file(file_name)
243
+
244
+ try:
245
+ # Run in subprocess with timeout
246
+ result = subprocess.run(
247
+ ["python", file_name],
248
+ capture_output=True,
249
+ text=True,
250
+ timeout=10 # seconds
251
+ )
252
+
253
+ if result.returncode != 0:
254
+ return f"Error running code: {result.stderr.strip()}"
255
+
256
+ output = result.stdout.strip()
257
+ return output
258
+
259
+ except subprocess.TimeoutExpired:
260
+ return "Execution timed out."
261
+ except Exception as e:
262
+ return f"Error: {str(e)}"
263
+
264
+ tools = [
265
+ open_file_as_text,
266
+ web_search,
267
+ read_wikipedia_page,
268
+ smart_paginate_around_query,
269
+ reverse_sentence,
270
+ ]
271
+
272
+ model = OpenAIServerModel(
273
+ model_id="gpt-4o",
274
+ api_key=os.getenv("OPENAI_API_KEY"),
275
+ temperature=0
276
+ )
277
+
278
+ agent = CodeAgent(
279
+ model=model,
280
+ tools=tools,
281
+ additional_authorized_imports=["pandas", "numpy", "datetime", "json", "re", "math", "os", "requests", "csv", "urllib"]
282
+ )
283
 
284
  def run_and_submit_all( profile: gr.OAuthProfile | None):
285
  """
 
302
 
303
  # 1. Instantiate Agent ( modify this part to create your agent)
304
  try:
305
+ agent = CodeAgent(
306
+ model=model,
307
+ tools=tools,
308
+ additional_authorized_imports=["pandas", "numpy", "datetime", "json", "re", "math", "os", "requests", "csv",
309
+ "urllib"]
310
+ )
311
  except Exception as e:
312
  print(f"Error instantiating agent: {e}")
313
  return f"Error initializing agent: {e}", None
314
+ # In the case of an app running as a hugging Face space, this link points toward your codebase (useful for others so please keep it public)
315
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
316
  print(agent_code)
317
 
 
343
  for item in questions_data:
344
  task_id = item.get("task_id")
345
  question_text = item.get("question")
346
+ file_name = item.get("file_name")
347
  if not task_id or question_text is None:
348
  print(f"Skipping item with missing task_id or question: {item}")
349
  continue
350
  try:
351
+ full_prompt = f"""You are a highly precise answering agent.
352
+
353
+ When given a question:
354
+ - If necessary, perform a web search using the tool `web_search` to find possible sources of information.
355
+ - If the web search only returns titles and short snippets, you MUST visit the actual webpage to read the full content before answering.
356
+ - Use the `read_wikipedia_page` tool to fetch and read the Wikipedia page when necessary.
357
+ - You just have the ability to read Wikipedia pages only.
358
+ - You MUST paginate the content using `smart_paginate_around_query`.
359
+ - When using `smart_paginate_around_query`, you must select a short, general query based on the main keywords only. Avoid using full questions or long phrases. Use 1–3 essential words.
360
+ - If the task requires reversing the order of words, letters, phrases, or any text, you must use the `reverse_sentence` tool to perform the operation.
361
+ - Never reverse text manually inside your code. Always call the tool instead.
362
+ - If the task requires reading, listening, or analyzing a file, you must use the file specified in the `file_name` field of the task metadata, not the file name mentioned casually inside the question text.
363
+ - Comma separated lists MUST contain a single space after each comma.
364
+ - If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise.
365
+ - If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
366
+ - If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
367
+ - Only answer after you have gathered enough information by reading the actual page contents.
368
+ - Once you have the final answer, you must call `final_answer("your_answer")` immediately after printing it.
369
+ - Do not retry or execute anything else after calling `final_answer`.
370
+ - `final_answer` must wrap the exact printed value.
371
+
372
+ Provide ONLY the precise answer requested.
373
+ Do not include explanations, steps, reasoning, or additional text.
374
+ Be direct and specific. GAIA benchmark requires exact matching answers.
375
+ Example: if asked "What is the capital of France?", respond exactly:
376
+
377
+ Thoughts: I need to retrieve the capital of France from Wikipedia and output it directly.
378
+
379
+ Code:
380
+ ```py
381
+ print("Paris")
382
+ ```<end_code>
383
+
384
+ Based on the above guidelines, answer the following question:
385
+
386
+ --begin of question--
387
+ {question_text}
388
+ --end of question--
389
+
390
+ If the questions mentions the need to use a file, use the following `file_name` value as the `file_name` parameter in any function calls:
391
+
392
+ file_name: {file_name}"""
393
+ submitted_answer = agent.run(full_prompt)
394
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
395
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
396
  except Exception as e:
requirements.txt CHANGED
@@ -1,2 +1,8 @@
1
- gradio
2
- requests
 
 
 
 
 
 
 
1
+ gradio~=5.27.1
2
+ requests~=2.32.3
3
+ pandas~=2.2.3
4
+ openai-whisper~=20240930
5
+ openpyxl~=3.1.5
6
+ smolagents~=1.14.0
7
+ beautifulsoup4~=4.13.4
8
+ duckduckgo_search~=8.0.1