Spaces:
Sleeping
Sleeping
hammaad-swe
commited on
Commit
·
87394ed
1
Parent(s):
ff5ced3
feat: added Excel Tool
Browse files- agent.py +76 -9
- logic.py +44 -1
- requirements.txt +2 -1
agent.py
CHANGED
@@ -1,37 +1,104 @@
|
|
1 |
import os
|
|
|
|
|
2 |
|
|
|
3 |
from dotenv import load_dotenv
|
4 |
-
from smolagents import (
|
5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
|
|
7 |
load_dotenv()
|
8 |
|
|
|
9 |
model = LiteLLMModel(
|
10 |
model_id=os.getenv("GEMINI_MODEL"),
|
11 |
api_key=os.getenv("GEMINI_API_KEY")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
)
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
class GaiaAgent:
|
16 |
-
"""
|
17 |
-
|
18 |
-
including search engines, web page access, a Python interpreter, and more.
|
19 |
-
"""
|
20 |
def __init__(self):
|
21 |
print("GaiaAgent initialized with tools.")
|
22 |
|
23 |
tools = [
|
24 |
DuckDuckGoSearchTool(),
|
25 |
-
VisitWebpageTool(),
|
26 |
WikipediaSearchTool(),
|
|
|
27 |
PythonInterpreterTool(),
|
28 |
FinalAnswerTool(),
|
29 |
]
|
30 |
|
31 |
-
self.agent = CodeAgent(
|
|
|
|
|
|
|
|
|
|
|
32 |
|
33 |
def __call__(self, task_id: str, question: str) -> str:
|
34 |
-
print(f"Agent received {task_id=
|
35 |
answer = self.agent.run(question)
|
36 |
print(f"Agent returning answer: {answer}")
|
37 |
return answer
|
|
|
1 |
import os
|
2 |
+
from pathlib import Path
|
3 |
+
from typing import Optional, Union
|
4 |
|
5 |
+
import pandas as pd
|
6 |
from dotenv import load_dotenv
|
7 |
+
from smolagents import (
|
8 |
+
CodeAgent,
|
9 |
+
DuckDuckGoSearchTool,
|
10 |
+
FinalAnswerTool,
|
11 |
+
LiteLLMModel,
|
12 |
+
PythonInterpreterTool,
|
13 |
+
WikipediaSearchTool,
|
14 |
+
)
|
15 |
+
from smolagents.tools import Tool
|
16 |
+
from tabulate import tabulate
|
17 |
|
18 |
+
# Load environment variables
|
19 |
load_dotenv()
|
20 |
|
21 |
+
# Initialize the model
|
22 |
model = LiteLLMModel(
|
23 |
model_id=os.getenv("GEMINI_MODEL"),
|
24 |
api_key=os.getenv("GEMINI_API_KEY")
|
25 |
+
)
|
26 |
+
|
27 |
+
|
28 |
+
class ExcelToTextTool(Tool):
|
29 |
+
"""Render an Excel worksheet as a Markdown table."""
|
30 |
+
|
31 |
+
name = "excel_to_text"
|
32 |
+
description = (
|
33 |
+
"Read an Excel file and return a Markdown table of the requested sheet. "
|
34 |
+
"Accepts either the sheet name or a zero-based index (as a string)."
|
35 |
)
|
36 |
|
37 |
+
inputs = {
|
38 |
+
"excel_path": {
|
39 |
+
"type": "string",
|
40 |
+
"description": "Path to the Excel file (.xlsx or .xls).",
|
41 |
+
},
|
42 |
+
"sheet_name": {
|
43 |
+
"type": "string",
|
44 |
+
"description": (
|
45 |
+
"Worksheet name or zero-based index (as a string). "
|
46 |
+
"Optional; defaults to the first sheet."
|
47 |
+
),
|
48 |
+
"nullable": True,
|
49 |
+
},
|
50 |
+
}
|
51 |
+
|
52 |
+
output_type = "string"
|
53 |
+
|
54 |
+
def forward(self, excel_path: str, sheet_name: Optional[str] = None) -> str:
|
55 |
+
"""Load the Excel file and return the sheet as a Markdown table."""
|
56 |
+
|
57 |
+
file_path = Path(excel_path).expanduser().resolve()
|
58 |
+
if not file_path.is_file():
|
59 |
+
return f"Error: Excel file not found at {file_path}"
|
60 |
+
|
61 |
+
try:
|
62 |
+
sheet: Union[str, int] = (
|
63 |
+
int(
|
64 |
+
sheet_name
|
65 |
+
) if sheet_name and sheet_name.isdigit() else sheet_name or 0
|
66 |
+
)
|
67 |
+
|
68 |
+
df = pd.read_excel(file_path, sheet_name=sheet)
|
69 |
+
|
70 |
+
if hasattr(df, "to_markdown"):
|
71 |
+
return df.to_markdown(index=False)
|
72 |
+
|
73 |
+
return tabulate(df, headers="keys", tablefmt="github", showindex=False)
|
74 |
+
|
75 |
+
except Exception as e:
|
76 |
+
return f"Error reading Excel file: {e}"
|
77 |
+
|
78 |
|
79 |
class GaiaAgent:
|
80 |
+
"""An agent capable of using tools to answer general questions."""
|
81 |
+
|
|
|
|
|
82 |
def __init__(self):
|
83 |
print("GaiaAgent initialized with tools.")
|
84 |
|
85 |
tools = [
|
86 |
DuckDuckGoSearchTool(),
|
|
|
87 |
WikipediaSearchTool(),
|
88 |
+
ExcelToTextTool(),
|
89 |
PythonInterpreterTool(),
|
90 |
FinalAnswerTool(),
|
91 |
]
|
92 |
|
93 |
+
self.agent = CodeAgent(
|
94 |
+
model=model,
|
95 |
+
tools=tools,
|
96 |
+
add_base_tools=True,
|
97 |
+
additional_authorized_imports=["pandas", "numpy", "csv", "subprocess"],
|
98 |
+
)
|
99 |
|
100 |
def __call__(self, task_id: str, question: str) -> str:
|
101 |
+
print(f"Agent received task_id='{task_id}' | question='{question[:50]}...'")
|
102 |
answer = self.agent.run(question)
|
103 |
print(f"Agent returning answer: {answer}")
|
104 |
return answer
|
logic.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1 |
from typing import Dict, List, Tuple
|
2 |
-
|
|
|
|
|
3 |
import pandas as pd
|
4 |
import requests
|
5 |
from agent import GaiaAgent
|
@@ -9,6 +11,7 @@ from pandas import DataFrame
|
|
9 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
10 |
QUESTIONS_URL = f"{DEFAULT_API_URL}/questions"
|
11 |
SUBMIT_URL = f"{DEFAULT_API_URL}/submit"
|
|
|
12 |
|
13 |
|
14 |
# --- Helper Methods ---
|
@@ -142,6 +145,7 @@ def run_agent(
|
|
142 |
for item in questions_data:
|
143 |
task_id = item.get("task_id")
|
144 |
question_text = item.get("question")
|
|
|
145 |
if not task_id or question_text is None:
|
146 |
print(f"⚠️ Skipping invalid item (missing task_id or question): {item}")
|
147 |
continue
|
@@ -162,3 +166,42 @@ def run_agent(
|
|
162 |
}
|
163 |
)
|
164 |
return results_log, answers_payload
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
from typing import Dict, List, Tuple
|
2 |
+
import re
|
3 |
+
import tempfile
|
4 |
+
from pathlib import Path
|
5 |
import pandas as pd
|
6 |
import requests
|
7 |
from agent import GaiaAgent
|
|
|
11 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
12 |
QUESTIONS_URL = f"{DEFAULT_API_URL}/questions"
|
13 |
SUBMIT_URL = f"{DEFAULT_API_URL}/submit"
|
14 |
+
FILE_PATH = f"{DEFAULT_API_URL}/files/"
|
15 |
|
16 |
|
17 |
# --- Helper Methods ---
|
|
|
145 |
for item in questions_data:
|
146 |
task_id = item.get("task_id")
|
147 |
question_text = item.get("question")
|
148 |
+
question_text = process_file(task_id, question_text)
|
149 |
if not task_id or question_text is None:
|
150 |
print(f"⚠️ Skipping invalid item (missing task_id or question): {item}")
|
151 |
continue
|
|
|
166 |
}
|
167 |
)
|
168 |
return results_log, answers_payload
|
169 |
+
|
170 |
+
|
171 |
+
def process_file(task_id: str, question_text: str) -> str:
|
172 |
+
"""
|
173 |
+
Attempt to download a file associated with a task from the API.
|
174 |
+
|
175 |
+
- If the file exists (HTTP 200), it is saved to a temp directory and the local file path is returned.
|
176 |
+
- If no file is found (HTTP 404), returns None.
|
177 |
+
- For all other HTTP errors, the exception is propagated to the caller.
|
178 |
+
"""
|
179 |
+
file_url = f"{FILE_PATH}{task_id}"
|
180 |
+
|
181 |
+
try:
|
182 |
+
response = requests.get(file_url, timeout=30)
|
183 |
+
response.raise_for_status()
|
184 |
+
except requests.exceptions.RequestException as exc:
|
185 |
+
print(f"Exception in download_file>> {str(exc)}")
|
186 |
+
return question_text # Unable to get the file
|
187 |
+
|
188 |
+
# Determine filename from 'Content-Disposition' header, fallback to task_id
|
189 |
+
content_disposition = response.headers.get("content-disposition", "")
|
190 |
+
filename = task_id
|
191 |
+
match = re.search(r'filename="([^"]+)"', content_disposition)
|
192 |
+
if match:
|
193 |
+
filename = match.group(1)
|
194 |
+
|
195 |
+
# Save file in a temp directory
|
196 |
+
temp_storage_dir = Path(tempfile.gettempdir()) / "gaia_cached_files"
|
197 |
+
temp_storage_dir.mkdir(parents=True, exist_ok=True)
|
198 |
+
|
199 |
+
file_path = temp_storage_dir / filename
|
200 |
+
file_path.write_bytes(response.content)
|
201 |
+
return (
|
202 |
+
f"{question_text}\n\n"
|
203 |
+
f"---\n"
|
204 |
+
f"A file was downloaded for this task and saved locally at:\n"
|
205 |
+
f"{str(file_path)}\n"
|
206 |
+
f"---\n\n"
|
207 |
+
)
|
requirements.txt
CHANGED
@@ -6,4 +6,5 @@ pandas
|
|
6 |
smolagents
|
7 |
wikipedia-api
|
8 |
google-generativeai
|
9 |
-
smolagents[litellm]
|
|
|
|
6 |
smolagents
|
7 |
wikipedia-api
|
8 |
google-generativeai
|
9 |
+
smolagents[litellm]
|
10 |
+
tabulate
|