hassenhamdi commited on
Commit
33eedd4
·
verified ·
1 Parent(s): b8b96cc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -29
app.py CHANGED
@@ -7,6 +7,9 @@ from smolagents import OpenAIServerModel
7
  from smolagents import CodeAgent, Tool, tool
8
  from smolagents import DuckDuckGoSearchTool, VisitWebpageTool
9
  from smolagents import PythonInterpreterTool # Import the built-in Python Interpreter Tool
 
 
 
10
 
11
  # --- Constants ---
12
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
@@ -19,8 +22,8 @@ class GaiaFileTool(Tool):
19
  """
20
  name = "download_gaia_file"
21
  description = "Downloads a file associated with a given GAIA task ID and returns its content. It takes 'task_id' as input and returns the file content as a string. Use this when a question refers to an external file."
22
- inputs = {"task_id": {"type": "string", "description": "The task ID for which to download the file (e.g., '2345')."}} # Corrected type to "string"
23
- output_type = "string" # Corrected type to "string"
24
 
25
  def __init__(self, api_base_url=DEFAULT_API_URL):
26
  super().__init__()
@@ -43,8 +46,6 @@ class GaiaFileTool(Tool):
43
  print(f"Error downloading file for task_id {task_id}: {e}")
44
  return f"Error downloading file: {e}"
45
 
46
- # Removed the custom python_repl function as we are using the built-in tool
47
-
48
  # --- Custom GAIA Agent Definition ---
49
  class GaiaAgent(CodeAgent):
50
  """
@@ -74,8 +75,6 @@ class GaiaAgent(CodeAgent):
74
  visit_webpage_tool = VisitWebpageTool()
75
 
76
  # Initialize the built-in Python Interpreter Tool
77
- # By default, PythonInterpreterTool uses a local executor, which is generally safe
78
- # for controlled environments like Hugging Face Spaces.
79
  python_interpreter_tool = PythonInterpreterTool()
80
 
81
  # Define the tools available to the agent
@@ -85,38 +84,95 @@ class GaiaAgent(CodeAgent):
85
  duckduckgo_search_tool,
86
  visit_webpage_tool
87
  ]
88
- super().__init__(model=self.llm_model, tools=agent_tools)
89
  print("GaiaAgent initialized successfully with Gemini Flash and built-in tools.")
90
 
91
  def __call__(self, question: str) -> str:
92
  """
93
  The main method for the agent to process a question and return an answer.
94
  This will involve the agent's internal reasoning, tool use, and planning.
 
95
  """
96
  print(f"\n--- Agent received question (first 100 chars): {question[:100]}...")
97
 
98
- try:
99
- prompt = (
100
- f"You are an AI agent designed to solve GAIA benchmark questions. "
101
- f"Your goal is to provide the exact answer as a string, without any additional text, "
102
- f"explanation, or the phrase 'FINAL ANSWER:'. "
103
- f"Break down the problem, use the available tools (python_interpreter, download_gaia_file, "
104
- f"duckduckgo_search_tool, visit_webpage_tool) as needed, and think step-by-step. "
105
- f"Use 'python_interpreter' for any calculations or code execution. "
106
- f"Use 'duckduckgo_search_tool' to find information on the web. "
107
- f"Use 'visit_webpage_tool' to read the content of a specific URL. "
108
- f"When you have the final answer, output ONLY the answer string.\n\n"
109
- f"Question: {question}"
110
- )
111
-
112
- result = self.run(prompt)
113
-
114
- final_answer = self._extract_exact_answer(result)
115
- print(f"--- Agent returning final answer (first 100 chars): {final_answer[:100]}...")
116
- return final_answer
117
- except Exception as e:
118
- print(f"--- Error during agent execution: {e}")
119
- return "Agent encountered an error and could not provide an answer."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  def _extract_exact_answer(self, raw_output: str) -> str:
122
  """
@@ -124,6 +180,8 @@ class GaiaAgent(CodeAgent):
124
  Ensures no "FINAL ANSWER" text is included and handles any
125
  extraneous formatting. This function is crucial for GAIA's exact match scoring.
126
  """
 
 
127
  cleaned_output = raw_output.replace("FINAL ANSWER:", "").strip()
128
  cleaned_output = cleaned_output.replace("Answer:", "").strip()
129
  cleaned_output = cleaned_output.replace("The answer is:", "").strip()
@@ -144,6 +202,7 @@ class GaiaAgent(CodeAgent):
144
  if cleaned_output.startswith("'") and cleaned_output.endswith("'"):
145
  cleaned_output = cleaned_output[1:-1]
146
 
 
147
  return cleaned_output.strip()
148
 
149
 
@@ -170,6 +229,9 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
170
  try:
171
  agent = GaiaAgent()
172
  except Exception as e:
 
 
 
173
  return f"Error initializing agent: {e}", None
174
 
175
  try:
@@ -179,6 +241,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
179
  questions = questions_response.json()
180
  print(f"Fetched {len(questions)} questions.")
181
  except requests.exceptions.RequestException as e:
 
182
  return f"Error fetching questions: {e}", None
183
 
184
  all_answers = []
 
7
  from smolagents import CodeAgent, Tool, tool
8
  from smolagents import DuckDuckGoSearchTool, VisitWebpageTool
9
  from smolagents import PythonInterpreterTool # Import the built-in Python Interpreter Tool
10
+ from smolagents.agents.base import LogLevel # Import LogLevel for verbosity
11
+ import time # Import time for sleep
12
+ from requests.exceptions import HTTPError # Import HTTPError for specific exception handling
13
 
14
  # --- Constants ---
15
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
22
  """
23
  name = "download_gaia_file"
24
  description = "Downloads a file associated with a given GAIA task ID and returns its content. It takes 'task_id' as input and returns the file content as a string. Use this when a question refers to an external file."
25
+ inputs = {"task_id": {"type": "string", "description": "The task ID for which to download the file (e.g., '2345')."}}
26
+ output_type = "string"
27
 
28
  def __init__(self, api_base_url=DEFAULT_API_URL):
29
  super().__init__()
 
46
  print(f"Error downloading file for task_id {task_id}: {e}")
47
  return f"Error downloading file: {e}"
48
 
 
 
49
  # --- Custom GAIA Agent Definition ---
50
  class GaiaAgent(CodeAgent):
51
  """
 
75
  visit_webpage_tool = VisitWebpageTool()
76
 
77
  # Initialize the built-in Python Interpreter Tool
 
 
78
  python_interpreter_tool = PythonInterpreterTool()
79
 
80
  # Define the tools available to the agent
 
84
  duckduckgo_search_tool,
85
  visit_webpage_tool
86
  ]
87
+ super().__init__(model=self.llm_model, tools=agent_tools, verbosity_level=LogLevel.DEBUG)
88
  print("GaiaAgent initialized successfully with Gemini Flash and built-in tools.")
89
 
90
  def __call__(self, question: str) -> str:
91
  """
92
  The main method for the agent to process a question and return an answer.
93
  This will involve the agent's internal reasoning, tool use, and planning.
94
+ Includes retry logic for LLM calls to handle rate limits.
95
  """
96
  print(f"\n--- Agent received question (first 100 chars): {question[:100]}...")
97
 
98
+ prompt = (
99
+ f"You are an AI agent designed to solve GAIA benchmark questions. "
100
+ f"Your goal is to provide the exact answer as a string, without any additional text, "
101
+ f"explanation, or the phrase 'FINAL ANSWER:'. "
102
+ f"Break down the problem, use the available tools (python_interpreter, download_gaia_file, "
103
+ f"duckduckgo_search_tool, visit_webpage_tool) as needed, and think step-by-step. "
104
+ f"When using web search or webpage visit tools, be highly efficient. "
105
+ f"Formulate comprehensive search queries to get as much relevant information as possible in one go. "
106
+ f"Only visit a webpage if absolutely necessary and when you expect it to contain the direct answer or crucial data. "
107
+ f"Avoid redundant searches or visiting multiple pages for the same piece of information. "
108
+ f"Use 'python_interpreter' for any calculations or code execution. "
109
+ f"Use 'duckduckgo_search_tool' to find information on the web. "
110
+ f"Use 'visit_webpage_tool' to read the content of a specific URL. "
111
+ f"When you have the final answer, output ONLY the answer string.\n\n"
112
+ f"Question: {question}"
113
+ )
114
+
115
+ print(f"Agent running with prompt (first 200 chars): {prompt[:200]}...")
116
+
117
+ max_retries = 5
118
+ initial_retry_delay = 30
119
+ retry_delay = initial_retry_delay
120
+ result = None
121
+
122
+ for attempt in range(max_retries):
123
+ try:
124
+ result = self.run(prompt)
125
+ print(f"Agent raw output from self.run():\n{result}") # Log the raw output
126
+ break # Break loop if successful
127
+ except HTTPError as e:
128
+ if e.response.status_code == 429:
129
+ error_details = ""
130
+ try:
131
+ # Attempt to parse more specific error details from the response
132
+ error_json = e.response.json()
133
+ if 'error' in error_json and 'details' in error_json['error']:
134
+ for detail in error_json['error']['details']:
135
+ if detail.get('@type') == 'type.googleapis.com/google.rpc.QuotaFailure':
136
+ quota_metric = detail.get('quotaMetric', 'N/A')
137
+ quota_id = detail.get('quotaId', 'N/A')
138
+ quota_value = detail.get('quotaValue', 'N/A')
139
+ error_details = f"Quota Metric: {quota_metric}, Quota ID: {quota_id}, Value: {quota_value}. "
140
+ break # Found relevant detail
141
+ except Exception as parse_error:
142
+ print(f"Could not parse detailed error from 429 response: {parse_error}")
143
+ error_details = "Check Google Cloud Console for details. "
144
+
145
+ error_message = (
146
+ f"Gemini API Rate limit hit (429) on attempt {attempt + 1}/{max_retries}. "
147
+ f"{error_details}"
148
+ f"Retrying in {retry_delay} seconds... "
149
+ f"This could be due to the 15 RPM or 200 RPD free tier limits. "
150
+ f"If this persists, your daily quota might be exhausted."
151
+ )
152
+ print(error_message)
153
+ time.sleep(retry_delay)
154
+ retry_delay *= 2 # Exponential backoff
155
+ else:
156
+ # Re-raise other HTTP errors
157
+ raise
158
+ except Exception as e:
159
+ # Log the full traceback for better debugging
160
+ import traceback
161
+ print(f"--- Error during agent execution on attempt {attempt + 1}/{max_retries}: {e}")
162
+ traceback.print_exc()
163
+ if attempt < max_retries - 1:
164
+ print(f"Retrying in {retry_delay} seconds...")
165
+ time.sleep(retry_delay)
166
+ retry_delay *= 2
167
+ else:
168
+ return "Agent encountered an error and could not provide an answer after multiple retries." # Final failure after retries
169
+
170
+ if result is None:
171
+ return "Agent failed after multiple retries due to an unknown error or persistent rate limits."
172
+
173
+ final_answer = self._extract_exact_answer(result)
174
+ print(f"--- Agent returning final answer (first 100 chars): {final_answer[:100]}...")
175
+ return final_answer
176
 
177
  def _extract_exact_answer(self, raw_output: str) -> str:
178
  """
 
180
  Ensures no "FINAL ANSWER" text is included and handles any
181
  extraneous formatting. This function is crucial for GAIA's exact match scoring.
182
  """
183
+ print(f"Attempting to extract exact answer from raw output (first 200 chars):\n{raw_output[:200]}...")
184
+
185
  cleaned_output = raw_output.replace("FINAL ANSWER:", "").strip()
186
  cleaned_output = cleaned_output.replace("Answer:", "").strip()
187
  cleaned_output = cleaned_output.replace("The answer is:", "").strip()
 
202
  if cleaned_output.startswith("'") and cleaned_output.endswith("'"):
203
  cleaned_output = cleaned_output[1:-1]
204
 
205
+ print(f"Extracted and cleaned answer: {cleaned_output[:100]}...")
206
  return cleaned_output.strip()
207
 
208
 
 
229
  try:
230
  agent = GaiaAgent()
231
  except Exception as e:
232
+ print(f"Error during agent initialization in run_and_submit_all: {e}")
233
+ import traceback
234
+ traceback.print_exc()
235
  return f"Error initializing agent: {e}", None
236
 
237
  try:
 
241
  questions = questions_response.json()
242
  print(f"Fetched {len(questions)} questions.")
243
  except requests.exceptions.RequestException as e:
244
+ print(f"Error fetching questions: {e}")
245
  return f"Error fetching questions: {e}", None
246
 
247
  all_answers = []