jonathan9879 commited on
Commit
0b26a35
·
verified ·
1 Parent(s): 7d49ce1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +213 -36
app.py CHANGED
@@ -3,50 +3,230 @@ 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
  """
24
- Fetches all questions, runs the BasicAgent on them, submits all answers,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  and displays the results.
26
  """
27
  # --- Determine HF Space Runtime URL and Repo URL ---
28
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
29
 
30
  if profile:
31
- username= f"{profile.username}"
32
  print(f"User logged in: {username}")
33
  else:
34
  print("User not logged in.")
35
  return "Please Login to Hugging Face with the button.", None
36
 
 
 
 
 
 
 
 
 
 
37
  api_url = DEFAULT_API_URL
38
  questions_url = f"{api_url}/questions"
39
  submit_url = f"{api_url}/submit"
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
 
51
  # 2. Fetch Questions
52
  print(f"Fetching questions from: {questions_url}")
@@ -80,7 +260,8 @@ def run_and_submit_all( profile: gr.OAuthProfile | 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:
@@ -99,7 +280,7 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
99
  # 5. Submit
100
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
101
  try:
102
- response = requests.post(submit_url, json=submission_data, timeout=60)
103
  response.raise_for_status()
104
  result_data = response.json()
105
  final_status = (
@@ -142,19 +323,16 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
142
 
143
  # --- Build Gradio Interface using Blocks ---
144
  with gr.Blocks() as demo:
145
- gr.Markdown("# Basic Agent Evaluation Runner")
146
  gr.Markdown(
147
  """
148
  **Instructions:**
149
-
150
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
151
  2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
152
  3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
153
-
154
  ---
155
  **Disclaimers:**
156
- Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
157
- This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
158
  """
159
  )
160
 
@@ -163,34 +341,33 @@ with gr.Blocks() as demo:
163
  run_button = gr.Button("Run Evaluation & Submit All Answers")
164
 
165
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
166
- # Removed max_rows=10 from DataFrame constructor
167
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
168
 
 
169
  run_button.click(
170
  fn=run_and_submit_all,
 
 
171
  outputs=[status_output, results_table]
172
  )
173
 
174
  if __name__ == "__main__":
175
  print("\n" + "-"*30 + " App Starting " + "-"*30)
176
- # Check for SPACE_HOST and SPACE_ID at startup for information
177
  space_host_startup = os.getenv("SPACE_HOST")
178
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
179
 
180
  if space_host_startup:
181
  print(f"✅ SPACE_HOST found: {space_host_startup}")
182
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
183
  else:
184
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
185
 
186
- if space_id_startup: # Print repo URLs if SPACE_ID is found
187
  print(f"✅ SPACE_ID found: {space_id_startup}")
188
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
189
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
190
  else:
191
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
192
 
193
  print("-"*(60 + len(" App Starting ")) + "\n")
194
 
195
- print("Launching Gradio Interface for Basic Agent Evaluation...")
196
  demo.launch(debug=True, share=False)
 
3
  import requests
4
  import inspect
5
  import pandas as pd
6
+ import google.generativeai as genai
7
+ import re
8
+ import time
9
 
 
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
+ MAX_ITERATIONS = 7 # Set a limit to prevent infinite loops
13
 
14
+ # --- Tool Definitions ---
15
+
16
+ class WebSearchTool:
17
+ """
18
+ A tool to search the web using the Perplexity API.
19
+ It returns a concise answer to a given query.
 
 
 
 
 
 
20
  """
21
+ def __init__(self, api_key):
22
+ self.api_key = api_key
23
+ self.url = "https://api.perplexity.ai/chat/completions"
24
+ print("WebSearchTool initialized.")
25
+
26
+ def execute(self, query: str) -> str:
27
+ print(f"Executing WebSearchTool with query: {query}")
28
+ payload = {
29
+ "model": "llama-3-sonar-small-32k-online",
30
+ "messages": [
31
+ {"role": "system", "content": "You are an assistant that provides a concise and factual answer to the user's query."},
32
+ {"role": "user", "content": query}
33
+ ]
34
+ }
35
+ headers = {
36
+ "accept": "application/json",
37
+ "content-type": "application/json",
38
+ "Authorization": f"Bearer {self.api_key}"
39
+ }
40
+ try:
41
+ response = requests.post(self.url, json=payload, headers=headers, timeout=30)
42
+ response.raise_for_status()
43
+ result = response.json()
44
+ answer = result['choices'][0]['message']['content']
45
+ print(f"WebSearchTool result: {answer[:150]}...")
46
+ return answer
47
+ except requests.exceptions.RequestException as e:
48
+ print(f"Error calling Perplexity API: {e}")
49
+ return f"Error: Could not get a response from the web search tool. {e}"
50
+
51
+ class FileDownloaderTool:
52
+ """
53
+ A tool to download and read the content of a file associated with a task.
54
+ The input should be the task_id.
55
+ """
56
+ def __init__(self, api_url: str):
57
+ self.api_url = api_url
58
+ print("FileDownloaderTool initialized.")
59
+
60
+ def execute(self, task_id: str) -> str:
61
+ print(f"Executing FileDownloaderTool for task_id: {task_id}")
62
+ file_url = f"{self.api_url}/files/{task_id}"
63
+ try:
64
+ response = requests.get(file_url, timeout=20)
65
+ response.raise_for_status()
66
+ # Assuming the file content is text
67
+ content = response.text
68
+ print(f"FileDownloaderTool successfully read file for task {task_id}. Content length: {len(content)}")
69
+ # Return a summary or a portion if the content is too long
70
+ if len(content) > 5000:
71
+ return f"File content (first 5000 chars):\n{content[:5000]}"
72
+ return f"File content:\n{content}"
73
+ except requests.exceptions.HTTPError as e:
74
+ if e.response.status_code == 404:
75
+ print(f"No file found for task_id: {task_id}")
76
+ return "No file is associated with this task."
77
+ else:
78
+ print(f"HTTP error downloading file for task_id {task_id}: {e}")
79
+ return f"Error: Failed to download file due to an HTTP error: {e}"
80
+ except requests.exceptions.RequestException as e:
81
+ print(f"Network error downloading file for task_id {task_id}: {e}")
82
+ return f"Error: Failed to download file due to a network error: {e}"
83
+
84
+
85
+ # --- GAIA Agent Definition ---
86
+ class GAIAAgent:
87
+ def __init__(self, gemini_api_key: str, pplx_api_key: str, api_url: str):
88
+ print("Initializing GAIAAgent...")
89
+ # Configure Gemini
90
+ genai.configure(api_key=gemini_api_key)
91
+ self.model = genai.GenerativeModel('gemini-1.5-flash-latest')
92
+
93
+ # Initialize Tools
94
+ self.tools = {
95
+ "WebSearch": WebSearchTool(api_key=pplx_api_key),
96
+ "FileDownloader": FileDownloaderTool(api_url=api_url),
97
+ }
98
+
99
+ # Define the ReAct prompt template
100
+ self.prompt_template = """
101
+ You are a helpful assistant designed to answer questions accurately.
102
+
103
+ To solve the user's question, you must use a sequence of thoughts and actions.
104
+ You have access to the following tools:
105
+
106
+ - **WebSearch[query]**: Use this to search the internet for up-to-date information, facts, or general knowledge.
107
+ - **FileDownloader[task_id]**: Use this to download and read a file associated with the current task. The task_id is '{task_id}'.
108
+
109
+ Your reasoning process should follow this format:
110
+
111
+ Thought: I need to figure out what information is missing. I will use a tool to find it.
112
+ Action: ToolName[input for the tool]
113
+ Observation: [The result from the tool will be inserted here]
114
+
115
+ ... (this Thought/Action/Observation cycle can repeat multiple times)
116
+
117
+ Thought: I have now gathered enough information to answer the user's question.
118
+ Final Answer: The final answer to the original question.
119
+
120
+ **Important Rules:**
121
+ 1. The `Action` line must be *exactly* in the format `ToolName[input]`. For example: `WebSearch[When was the Eiffel Tower built?]`.
122
+ 2. The `task_id` for the current question is '{task_id}'. Use it ONLY with the FileDownloader tool.
123
+ 3. If no file is associated with the task, the FileDownloader tool will return 'No file is associated with this task.'.
124
+ 4. Once you have the final answer, do not use any more tools. State the final answer clearly after "Final Answer:".
125
+
126
+ Here is the question:
127
+ {question}
128
+ """
129
+ print("GAIAAgent initialized successfully.")
130
+
131
+ def __call__(self, question: str, task_id: str) -> str:
132
+ print(f"Agent received question for task {task_id}: {question[:100]}...")
133
+
134
+ # Format the initial prompt with the question and task_id
135
+ prompt = self.prompt_template.format(question=question, task_id=task_id)
136
+
137
+ # Start the ReAct loop
138
+ for i in range(MAX_ITERATIONS):
139
+ print(f"\n--- Iteration {i+1} ---")
140
+
141
+ try:
142
+ print("Generating response from Gemini...")
143
+ # Add a small delay to avoid hitting rate limits too quickly
144
+ time.sleep(1)
145
+ response = self.model.generate_content(prompt)
146
+ response_text = response.text
147
+ print(f"LLM Response:\n{response_text}")
148
+
149
+ except Exception as e:
150
+ print(f"Error calling Gemini API: {e}")
151
+ return f"Error: Could not get a response from the reasoning model. {e}"
152
+
153
+ # Check for Final Answer
154
+ final_answer_match = re.search(r"Final Answer:\s*(.*)", response_text, re.DOTALL)
155
+ if final_answer_match:
156
+ final_answer = final_answer_match.group(1).strip()
157
+ print(f"Found Final Answer: {final_answer}")
158
+ # Per instructions, return only the answer itself
159
+ return final_answer
160
+
161
+ # Check for Action
162
+ action_match = re.search(r"Action:\s*(\w+)\[(.*?)\]", response_text)
163
+ if action_match:
164
+ tool_name = action_match.group(1).strip()
165
+ tool_input = action_match.group(2).strip()
166
+
167
+ if tool_name in self.tools:
168
+ print(f"Executing tool '{tool_name}' with input '{tool_input}'")
169
+ tool = self.tools[tool_name]
170
+ try:
171
+ # Special handling for FileDownloader which needs task_id
172
+ if tool_name == "FileDownloader":
173
+ observation = tool.execute(task_id)
174
+ else:
175
+ observation = tool.execute(tool_input)
176
+ except Exception as e:
177
+ observation = f"Error executing tool: {e}"
178
+
179
+ # Append the observation to the prompt for the next turn
180
+ prompt += f"{response_text}\nObservation: {observation}\n"
181
+ else:
182
+ print(f"Error: Agent tried to use an unknown tool: {tool_name}")
183
+ prompt += f"{response_text}\nObservation: Error - The tool '{tool_name}' does not exist. Available tools are: {list(self.tools.keys())}.\n"
184
+ else:
185
+ print("Error: Agent did not provide a valid Action or Final Answer. Ending loop.")
186
+ # If the model just gives a response without the proper format, return it as a last resort.
187
+ return response_text.strip()
188
+
189
+ print("Agent reached max iterations without finding a final answer.")
190
+ return "Agent could not determine the answer within the allowed number of steps."
191
+
192
+
193
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
194
+ """
195
+ Fetches all questions, runs the GAIAAgent on them, submits all answers,
196
  and displays the results.
197
  """
198
  # --- Determine HF Space Runtime URL and Repo URL ---
199
+ space_id = os.getenv("SPACE_ID")
200
 
201
  if profile:
202
+ username = f"{profile.username}"
203
  print(f"User logged in: {username}")
204
  else:
205
  print("User not logged in.")
206
  return "Please Login to Hugging Face with the button.", None
207
 
208
+ # --- Get API Keys from Secrets ---
209
+ pplx_key = os.getenv("PPLX_API_KEY")
210
+ gemini_key = os.getenv("GEMINI_API_KEY")
211
+
212
+ if not pplx_key or not gemini_key:
213
+ error_msg = "API keys not found in Space secrets. Please set PPLX_API_KEY and GEMINI_API_KEY in your Space settings."
214
+ print(error_msg)
215
+ return error_msg, None
216
+
217
  api_url = DEFAULT_API_URL
218
  questions_url = f"{api_url}/questions"
219
  submit_url = f"{api_url}/submit"
220
 
221
+ # 1. Instantiate Agent
222
  try:
223
+ agent = GAIAAgent(gemini_api_key=gemini_key, pplx_api_key=pplx_key, api_url=api_url)
224
  except Exception as e:
225
  print(f"Error instantiating agent: {e}")
226
  return f"Error initializing agent: {e}", None
227
+
228
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
229
+ print(f"Agent code link: {agent_code}")
230
 
231
  # 2. Fetch Questions
232
  print(f"Fetching questions from: {questions_url}")
 
260
  print(f"Skipping item with missing task_id or question: {item}")
261
  continue
262
  try:
263
+ # Pass both question and task_id to the agent
264
+ submitted_answer = agent(question_text, task_id)
265
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
266
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
267
  except Exception as e:
 
280
  # 5. Submit
281
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
282
  try:
283
+ response = requests.post(submit_url, json=submission_data, timeout=120) # Increased timeout for submission
284
  response.raise_for_status()
285
  result_data = response.json()
286
  final_status = (
 
323
 
324
  # --- Build Gradio Interface using Blocks ---
325
  with gr.Blocks() as demo:
326
+ gr.Markdown("# GAIA Agent Evaluation Runner")
327
  gr.Markdown(
328
  """
329
  **Instructions:**
330
+ 1. Ensure you have added your `PPLX_API_KEY` and `GEMINI_API_KEY` to this Space's **Settings > Secrets**.
 
331
  2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
332
  3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
 
333
  ---
334
  **Disclaimers:**
335
+ This process can take a significant amount of time (10-20 minutes) as the agent processes each of the 20 questions. The UI will be blocked during this time. Please be patient.
 
336
  """
337
  )
338
 
 
341
  run_button = gr.Button("Run Evaluation & Submit All Answers")
342
 
343
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
344
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
345
 
346
+ # Add the profile object from the LoginButton to the click function's inputs
347
  run_button.click(
348
  fn=run_and_submit_all,
349
+ # The login button automatically provides the profile object to functions that need it.
350
+ # We just need to ensure the function signature matches.
351
  outputs=[status_output, results_table]
352
  )
353
 
354
  if __name__ == "__main__":
355
  print("\n" + "-"*30 + " App Starting " + "-"*30)
 
356
  space_host_startup = os.getenv("SPACE_HOST")
357
+ space_id_startup = os.getenv("SPACE_ID")
358
 
359
  if space_host_startup:
360
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
361
  else:
362
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
363
 
364
+ if space_id_startup:
365
  print(f"✅ SPACE_ID found: {space_id_startup}")
366
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
 
367
  else:
368
+ print("ℹ️ SPACE_ID environment variable not found (running locally?).")
369
 
370
  print("-"*(60 + len(" App Starting ")) + "\n")
371
 
372
+ print("Launching Gradio Interface for GAIA Agent Evaluation...")
373
  demo.launch(debug=True, share=False)