Files changed (1) hide show
  1. app.py +96 -27
app.py CHANGED
@@ -1,34 +1,106 @@
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
  """
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.")
@@ -38,13 +110,12 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
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
 
@@ -55,16 +126,16 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
55
  response.raise_for_status()
56
  questions_data = response.json()
57
  if not questions_data:
58
- print("Fetched questions list is empty.")
59
- return "Fetched questions list is empty or invalid format.", None
60
  print(f"Fetched {len(questions_data)} questions.")
61
  except requests.exceptions.RequestException as e:
62
  print(f"Error fetching questions: {e}")
63
  return f"Error fetching questions: {e}", None
64
  except requests.exceptions.JSONDecodeError as e:
65
- print(f"Error decoding JSON response from questions endpoint: {e}")
66
- print(f"Response text: {response.text[:500]}")
67
- return f"Error decoding server response for questions: {e}", None
68
  except Exception as e:
69
  print(f"An unexpected error occurred fetching questions: {e}")
70
  return f"An unexpected error occurred fetching questions: {e}", None
@@ -80,18 +151,18 @@ 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:
87
- print(f"Error running agent on task {task_id}: {e}")
88
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
89
 
90
  if not answers_payload:
91
  print("Agent did not produce any answers to submit.")
92
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
93
 
94
- # 4. Prepare Submission
95
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
96
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
97
  print(status_update)
@@ -163,7 +234,6 @@ 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(
@@ -172,10 +242,9 @@ with gr.Blocks() as demo:
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}")
@@ -183,14 +252,14 @@ if __name__ == "__main__":
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)
 
1
  import os
2
+ import re
3
  import gradio as gr
4
  import requests
5
  import inspect
6
  import pandas as pd
7
+ from io import BytesIO
8
+
9
+ from smolagents import CodeAgent, DuckDuckGoSearchTool, VisitWebpageTool, InferenceClientModel
10
 
 
11
  # --- Constants ---
12
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
13
 
14
+
15
  # --- Basic Agent Definition ---
 
16
  class BasicAgent:
17
+ """
18
+ Agent logic:
19
+ - CodeAgent (not ToolCallingAgent) because chained multi-step reasoning
20
+ (search -> extract -> compute -> format) survives code execution far
21
+ better than JSON tool-call chains, which fail silently on malformed
22
+ structured output.
23
+ - DuckDuckGoSearchTool + VisitWebpageTool cover the web-lookup questions
24
+ (e.g. "Mercedes Sosa albums 2000-2009").
25
+ - _fetch_file pulls any attached file for a task from {api_url}/files/{task_id}
26
+ and hands the raw bytes into the CodeAgent's execution namespace via
27
+ additional_args, so generated code can parse it (pandas/openpyxl etc.)
28
+ if the question needs it.
29
+ - _format_answer is a safety net, not the primary defense. The prompt
30
+ instructs exact-match-aware output; this regex cleanup just strips
31
+ stray quotes/periods/whitespace that slip through.
32
+ - Known gap: questions that require actual video frame analysis (e.g. the
33
+ YouTube bird-count question) are NOT solved by this tool set. Search
34
+ can find a video's title/description but not analyze its frames. That
35
+ would need a separate vision-capable step - deliberately out of scope
36
+ for this baseline.
37
+ """
38
+
39
  def __init__(self):
40
  print("BasicAgent initialized.")
41
+ self.model = InferenceClientModel()
42
+ self.agent = CodeAgent(
43
+ tools=[DuckDuckGoSearchTool(), VisitWebpageTool()],
44
+ model=self.model,
45
+ )
46
+
47
+ def _fetch_file(self, task_id: str, api_url: str = DEFAULT_API_URL):
48
+ """Fetch any file attached to this task. Returns raw bytes or None."""
49
+ if not task_id:
50
+ return None
51
+ try:
52
+ resp = requests.get(f"{api_url}/files/{task_id}", timeout=15)
53
+ if resp.status_code == 404:
54
+ # No file attached to this task - not an error.
55
+ return None
56
+ resp.raise_for_status()
57
+ return resp.content
58
+ except requests.exceptions.RequestException as e:
59
+ print(f"File fetch failed for task {task_id}: {e}")
60
+ return None
61
+
62
+ def _format_answer(self, raw: str) -> str:
63
+ """Strip surrounding quotes/whitespace/trailing punctuation - safety net only."""
64
+ answer = str(raw).strip()
65
+ answer = re.sub(r'^["\']|["\']$', '', answer).strip()
66
+ if answer.endswith('.') and not answer.replace('.', '').isdigit():
67
+ answer = answer.rstrip('.')
68
+ return answer.strip()
69
+
70
+ def __call__(self, question: str, task_id: str = None) -> str:
71
  print(f"Agent received question (first 50 chars): {question[:50]}...")
72
+ try:
73
+ file_bytes = self._fetch_file(task_id)
74
+
75
+ prompt = question
76
+ additional_args = {}
77
+ if file_bytes is not None:
78
+ additional_args["file_bytes"] = file_bytes
79
+ prompt += (
80
+ "\n\n(An attached file for this task is available in your "
81
+ "execution namespace as the variable `file_bytes` (raw bytes). "
82
+ "Use pandas/openpyxl/BytesIO as needed to parse it if it's "
83
+ "relevant to answering the question.)"
84
+ )
85
+
86
+ result = self.agent.run(prompt, additional_args=additional_args or None)
87
+ answer = self._format_answer(result)
88
+ print(f"Agent returning answer: {answer}")
89
+ return answer
90
+ except Exception as e:
91
+ print(f"Agent error on task {task_id}: {e}")
92
+ return f"AGENT_ERROR: {e}"
93
+
94
 
95
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
96
  """
97
  Fetches all questions, runs the BasicAgent on them, submits all answers,
98
  and displays the results.
99
  """
100
+ space_id = os.getenv("SPACE_ID")
 
101
 
102
  if profile:
103
+ username = f"{profile.username}"
104
  print(f"User logged in: {username}")
105
  else:
106
  print("User not logged in.")
 
110
  questions_url = f"{api_url}/questions"
111
  submit_url = f"{api_url}/submit"
112
 
113
+ # 1. Instantiate Agent
114
  try:
115
  agent = BasicAgent()
116
  except Exception as e:
117
  print(f"Error instantiating agent: {e}")
118
  return f"Error initializing agent: {e}", None
 
119
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
120
  print(agent_code)
121
 
 
126
  response.raise_for_status()
127
  questions_data = response.json()
128
  if not questions_data:
129
+ print("Fetched questions list is empty.")
130
+ return "Fetched questions list is empty or invalid format.", None
131
  print(f"Fetched {len(questions_data)} questions.")
132
  except requests.exceptions.RequestException as e:
133
  print(f"Error fetching questions: {e}")
134
  return f"Error fetching questions: {e}", None
135
  except requests.exceptions.JSONDecodeError as e:
136
+ print(f"Error decoding JSON response from questions endpoint: {e}")
137
+ print(f"Response text: {response.text[:500]}")
138
+ return f"Error decoding server response for questions: {e}", None
139
  except Exception as e:
140
  print(f"An unexpected error occurred fetching questions: {e}")
141
  return f"An unexpected error occurred fetching questions: {e}", None
 
151
  print(f"Skipping item with missing task_id or question: {item}")
152
  continue
153
  try:
154
+ submitted_answer = agent(question_text, task_id)
155
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
156
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
157
  except Exception as e:
158
+ print(f"Error running agent on task {task_id}: {e}")
159
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
160
 
161
  if not answers_payload:
162
  print("Agent did not produce any answers to submit.")
163
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
164
 
165
+ # 4. Prepare Submission
166
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
167
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
168
  print(status_update)
 
234
  run_button = gr.Button("Run Evaluation & Submit All Answers")
235
 
236
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
237
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
238
 
239
  run_button.click(
 
242
  )
243
 
244
  if __name__ == "__main__":
245
+ print("\n" + "-" * 30 + " App Starting " + "-" * 30)
 
246
  space_host_startup = os.getenv("SPACE_HOST")
247
+ space_id_startup = os.getenv("SPACE_ID")
248
 
249
  if space_host_startup:
250
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
252
  else:
253
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
254
 
255
+ if space_id_startup:
256
  print(f"✅ SPACE_ID found: {space_id_startup}")
257
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
258
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
259
  else:
260
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
261
 
262
+ print("-" * (60 + len(" App Starting ")) + "\n")
263
 
264
  print("Launching Gradio Interface for Basic Agent Evaluation...")
265
  demo.launch(debug=True, share=False)