Francesco-A commited on
Commit
f4c14e9
·
1 Parent(s): 856f7b8

app_build_v1

Browse files
agent.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from smolagents import tool
3
+ import pandas as pd
4
+
5
+ from smolagents import (
6
+ CodeAgent,
7
+ InferenceClientModel,
8
+ Tool,
9
+ DuckDuckGoSearchTool,
10
+ VisitWebpageTool,
11
+ WikipediaSearchTool,
12
+ PythonInterpreterTool,
13
+ FinalAnswerTool,
14
+ )
15
+
16
+ # Import your custom tools (to be used in app, not in local notebook)
17
+ from tools.download_file import download_file_from_url
18
+ from tools.files_to_text import image_to_text, pdf_to_text, text_file_to_string
19
+
20
+ def create_agent(
21
+ model_path: str = "Qwen/Qwen2.5-Coder-32B-Instruct"
22
+ ):
23
+ """
24
+ Creates and configures a CodeAgent.
25
+
26
+ This function initializes a smolagents CodeAgent equipped with the
27
+ recommended default tools (web search, browser, and Python interpreter),
28
+ together with any custom tools you may define.
29
+
30
+ Args:
31
+ model_path (str): The identifier or local path of the Hugging Face
32
+ model to be loaded. By default, it uses `Qwen/Qwen2.5-32B-Instruct`,
33
+ but any compatible model can be substituted.
34
+
35
+ Returns:
36
+ CodeAgent: A fully initialized agent ready to run code, query tools,
37
+ and perform multi-step reasoning using the selected model.
38
+ """
39
+
40
+ # Choose a lightweight but reasoning-capable model
41
+ model = InferenceClientModel(
42
+ model_id=model_path,
43
+ temperature = 0.0,
44
+ top_p = 1.0, # NEW
45
+ )
46
+
47
+ # Default smolagents tools (high-level)
48
+ default_tools = [
49
+ DuckDuckGoSearchTool(), # Internet search
50
+ VisitWebpageTool(), # Retrieve webpage content
51
+ PythonInterpreterTool(), # Executes agent-generated Python code
52
+ FinalAnswerTool(), # Ends agent reasoning and returns final answer
53
+ ]
54
+
55
+ # Custom tools (critical for GAIA)
56
+ custom_tools = [
57
+ download_file_from_url, # file downloader
58
+ text_file_to_string, # .txt, .md, .json, etc.
59
+ pdf_to_text, # PyMuPDF-based safe PDF parser
60
+ image_to_text, # OCR for images
61
+ ]
62
+
63
+ tools = default_tools + custom_tools
64
+
65
+ # Create the CodeAgent (best for GAIA because it supports Python)
66
+ agent = CodeAgent(
67
+ model=model,
68
+ tools=tools,
69
+ add_base_tools=True, # probably redundant, but it does not hurt
70
+ max_steps=7,
71
+ additional_authorized_imports = ['numpy','subprocess', 're', 'pandas',
72
+ 'json', 'os', 'pathlib', 'tempfile',
73
+ 'matplotlib.pyplot', 'seaborn'],
74
+ verbosity_level = 1,
75
+ max_print_outputs_length=1_000_000
76
+ )
77
+
78
+ return agent
79
+
80
+ # WIP: Agentic RAG Systems
app.py CHANGED
@@ -3,21 +3,77 @@ 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
  """
 
3
  import requests
4
  import inspect
5
  import pandas as pd
6
+ from agent import create_agent
7
+ from typing import Optional
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
  class BasicAgent:
15
  def __init__(self):
16
+ self.agent = create_agent()
17
+ self.system_prompt = """
18
+ You are an expert **General AI Assistant** and **Python Programmer** tasked with solving complex GAIA benchmark problems.
19
+
20
+ ### 1. Reason-Act-Observe
21
+ Follow a **PLAN → ACT → OBSERVE** loop:
22
+ - **PLAN:** Break the task into 1–3 logical steps. Identify tools for each step.
23
+ - **ACT:** Write and run one self-contained Python block per step.
24
+ - **OBSERVE:** Examine outputs or errors before proceeding.
25
+
26
+ ### 2. File Handling
27
+ - When a tool like `download_file_from_url` returns a local file path (e.g., `/tmp/data.csv`), you **MUST** save this path to a descriptive variable (e.g., `filepath`) and **immediately use that variable** as the argument for the next file-reading tool.
28
+
29
+ You must select the reading method based strictly on the file extension:
30
+ | File Extension | Tool / Method to Use |
31
+ | :--- | :--- |
32
+ | .csv | `pd.read_csv(filepath)` |
33
+ | .xlsx, .xls | `pd.read_excel(filepath)` |
34
+ | .pdf | `pdf_to_text(filepath)` |
35
+ | .txt, .md, .json | `text_file_to_string(filepath)` |
36
+ | .png, .jpg, .jpeg | `image_to_text(filepath)` |
37
+
38
+ ### 3. Data Analysis & Answer
39
+ - Inspect loaded datasets first (`.head()`, `.info()`, `.describe()`) before analysis.
40
+ - Write clean, idiomatic Python code. Before that, check if there is any pre-made tool that would work for the task.
41
+ - Use `FinalAnswerTool` **only once the problem is fully solved** to give a concise final answer.
42
+
43
+ ### 4. Additional instructions for the following tasks provided by GAIA team
44
+ - You are a general AI assistant. I will ask you a question. Do not reveal your internal reasoning. Only the content inside FinalAnswerTool will be evaluated.
45
+ - Finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. 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. 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. 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.
46
+
47
+ ### 5. To provide the final answer, you MUST call the final_answer tool inside a <code> block.
48
+
49
+ - Example of how to end the task:
50
+
51
+ Thought: I have found the answer. I will now provide it.
52
+ <code>
53
+ final_answer("FINAL ANSWER: The capital of France is Paris")
54
+ </code>
55
+
56
+ \n\n
57
+ """
58
+ # print("Agent initialized.")
59
+
60
+ def __call__(self, question: str, file_path: Optional[str] = None) -> str:
61
+
62
+ if file_path:
63
+ # Inject system prompt + question and (optional) file path
64
+ prompt = (
65
+ f"{self.system_prompt}\n\n"
66
+ f"Question: {question}\n\n"
67
+ f"There is an associated file at path: {file_path}.\n"
68
+ f"Use the appropriate tool to download it (if necessary) and read it before answering"
69
+ )
70
+ else:
71
+ prompt = (
72
+ f"{self.system_prompt}\n\n"
73
+ f"Question: {question}\n\n"
74
+ )
75
+
76
+ return self.agent.run(prompt)
77
 
78
  def run_and_submit_all( profile: gr.OAuthProfile | None):
79
  """
requirements.txt CHANGED
@@ -1,11 +1,23 @@
1
- smolagents
 
 
 
 
 
2
  gradio
3
- requests
 
 
 
 
 
4
  pandas
5
- openpyxl
6
- pdfplumber
7
- PyMuPDF
8
- Pillow
9
- requests
10
- ddgs
11
- pytesseract
 
 
 
1
+ # Core agent framework (PINNED)
2
+ smolagents==1.23.0
3
+ transformers==4.53.3
4
+ huggingface-hub==0.36.0
5
+
6
+ # UI
7
  gradio
8
+
9
+ # Networking & retrieval
10
+ requests==2.32.5
11
+ ddgs==9.10.0
12
+
13
+ # Data handling
14
  pandas
15
+ openpyxl==3.1.5
16
+
17
+ # File & document parsing (PINNED: brittle)
18
+ Pillow==11.3.0
19
+ pdfplumber==0.11.8
20
+ PyMuPDF==1.26.7
21
+
22
+ # OCR (OPTIONAL, disabled)
23
+ # pytesseract==0.3.13
tools/PLACEHOLDER.txt DELETED
File without changes
tools/download_file.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ @tool
4
+ def download_file_from_url(url: str, filename: Optional[str] = None) -> str:
5
+ """
6
+ Downloads a file from the given URL to a temporary local location.
7
+
8
+ The file is saved in the system's temporary directory. The filename is either passed as argument or
9
+ inferred from the URL's path; if it cannot be determined, a generic name is used.
10
+
11
+ Args:
12
+ url: The URL of the file to download (str).
13
+ filename: Optional filename, will generate one based on URL if not provided
14
+
15
+ Returns:
16
+ The full local file path (str) of the downloaded file if successful,
17
+ or an error message string detailing the failure.
18
+ """
19
+
20
+ import requests, tempfile, os
21
+ from urllib.parse import urlparse
22
+
23
+ try:
24
+ if not filename:
25
+ filename = os.path.basename(urlparse(url).path) or "downloaded_file"
26
+ filepath = os.path.join(tempfile.gettempdir(), filename)
27
+
28
+ response = requests.get(url, stream=True)
29
+ response.raise_for_status()
30
+
31
+ with open(filepath, "wb") as f:
32
+ for chunk in response.iter_content(chunk_size=8192):
33
+ f.write(chunk)
34
+
35
+ return filepath
36
+ except Exception as e:
37
+ return f"Download error: {e}"
tools/files_to_dict.py DELETED
@@ -1,62 +0,0 @@
1
- from smolagents import tool
2
- import pandas as pd
3
- import pymupdf
4
-
5
- @tool
6
- def csv_to_dict(csv_file_path: str) -> str:
7
- """
8
- Reads a CSV file from the given path and returns:
9
- - the data as a list of dictionaries,
10
- - the list of column names,
11
- - a basic descriptive summary of numeric columns.
12
-
13
- Args:
14
- csv_file_path (str): Path to the CSV file.
15
-
16
- Returns:
17
- str: A dictionary-like structure containing:
18
- "data", "columns", and "describe".
19
- """
20
- try:
21
- df = pd.read_csv(csv_file_path)
22
-
23
- output = {
24
- "columns" : df.columns.tolist(),
25
- "describe": df.describe(include="all",percentiles=[.5]).to_dict(),
26
- "data" : df.to_dict(orient="records")
27
- }
28
-
29
- return output
30
- except FileNotFoundError:
31
- return f"Error: The file at '{csv_file_path}' was not found."
32
- except Exception as e:
33
- return f"An error occurred: {e}"
34
-
35
- @tool
36
- def excel_to_dict(xlsx_file_path: str) -> str:
37
- """
38
- Reads an Excel (xlsx) file from the given path and returns:
39
- - the data as a list of dictionaries,
40
- - the list of column names,
41
- - a basic descriptive summary of numeric columns.
42
-
43
- Args:
44
- xlsx_file_path (str): Path to the Excel file.
45
-
46
- Returns:
47
- str: A dictionary-like structure containing:
48
- "data", "columns", and "describe".
49
- """
50
- try:
51
- df = pd.read_excel(xlsx_file_path)
52
- output = {
53
- "columns" : df.columns.tolist(),
54
- "describe": df.describe(include="all",percentiles=[.5]).to_dict(),
55
- "data" : df.to_dict(orient="records")
56
- }
57
-
58
- return output
59
- except FileNotFoundError:
60
- return f"Error: The file at '{xlsx_file_path}' was not found."
61
- except Exception as e:
62
- return f"An error occurred: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tools/files_to_text.py CHANGED
@@ -10,16 +10,16 @@ def image_to_text(image_path: str) -> str:
10
  Extracted text or error message
11
  """
12
  try:
13
- import pytesseract
14
- from PIL import Image
15
 
16
- # Open the image using PIL
17
- img = Image.open(image_path)
18
 
19
- # Use pytesseract to extract text from the image
20
- extracted_text = pytesseract.image_to_string(img)
21
 
22
- return f"Extracted text from image: {extracted_text}"
23
  except ImportError:
24
  return "Error: pytesseract is not installed. Please install it with 'pip install pytesseract' and ensure Tesseract OCR is installed on your system."
25
  except Exception as e:
@@ -34,13 +34,15 @@ def pdf_to_text(pdf_file_path: str) -> str:
34
  Returns:
35
  str: The text content of the PDF.
36
  """
 
37
  try:
38
- doc = pymupdf.open(pdf_file_path)
39
- text = ""
40
- for page in doc:
41
- text += page.get_text("text")
42
- text += "\n"
43
- return text
 
44
  except FileNotFoundError:
45
  return f"Error: The file at '{pdf_file_path}' was not found."
46
  except Exception as e:
@@ -65,10 +67,10 @@ def text_file_to_string(path: str) -> str:
65
  If the file contains binary data, the returned string may be partially decoded.
66
  """
67
  try:
68
- with open(path, "r", encoding="utf-8", errors="ignore") as f:
69
- content = f.read()
70
- return content
71
  except FileNotFoundError:
72
- return f"Error: The file at '{path}' was not found."
73
  except Exception as e:
74
- return f"An error occurred: {e}"
 
10
  Extracted text or error message
11
  """
12
  try:
13
+ import pytesseract
14
+ from PIL import Image
15
 
16
+ # Open the image using PIL
17
+ img = Image.open(image_path)
18
 
19
+ # Use pytesseract to extract text from the image
20
+ extracted_text = pytesseract.image_to_string(img)
21
 
22
+ return f"Extracted text from image: {extracted_text}"
23
  except ImportError:
24
  return "Error: pytesseract is not installed. Please install it with 'pip install pytesseract' and ensure Tesseract OCR is installed on your system."
25
  except Exception as e:
 
34
  Returns:
35
  str: The text content of the PDF.
36
  """
37
+
38
  try:
39
+ import pymupdf
40
+ doc = pymupdf.open(pdf_file_path)
41
+ text = ""
42
+ for page in doc:
43
+ text += page.get_text("text")
44
+ text += "\n"
45
+ return text
46
  except FileNotFoundError:
47
  return f"Error: The file at '{pdf_file_path}' was not found."
48
  except Exception as e:
 
67
  If the file contains binary data, the returned string may be partially decoded.
68
  """
69
  try:
70
+ with open(path, "r", encoding="utf-8", errors="ignore") as f:
71
+ content = f.read()
72
+ return content
73
  except FileNotFoundError:
74
+ return f"Error: The file at '{path}' was not found."
75
  except Exception as e:
76
+ return f"An error occurred: {e}"