gabriel-melki commited on
Commit
f1eb571
·
1 Parent(s): b4b094c

Package modification

Browse files
__pycache__/agent.cpython-313.pyc DELETED
Binary file (1.09 kB)
 
__pycache__/prompt.cpython-313.pyc DELETED
Binary file (4 kB)
 
agent.py CHANGED
@@ -1,11 +1,51 @@
 
 
1
  from smolagents import CodeAgent
2
  from prompt import get_prompt
3
 
4
  class QuestionAnsweringAgent(CodeAgent):
5
  def __init__(self, *args, **kwargs):
6
  super().__init__(*args, **kwargs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  def __call__(self, question_text, file_name) -> str:
9
- enhanced_question = get_prompt(question_text, file_name)
10
- response = self.run(enhanced_question, reset=True)
11
- return response
 
 
 
 
 
 
 
 
1
+ import os
2
+ import glob
3
  from smolagents import CodeAgent
4
  from prompt import get_prompt
5
 
6
  class QuestionAnsweringAgent(CodeAgent):
7
  def __init__(self, *args, **kwargs):
8
  super().__init__(*args, **kwargs)
9
+
10
+ def get_current_files(self):
11
+ """Get a set of all files in the current working directory"""
12
+ try:
13
+ # Get all files in current directory (including hidden files)
14
+ all_files = set()
15
+ for pattern in ['*', '.*']:
16
+ all_files.update(glob.glob(pattern))
17
+ # Filter to only include actual files (not directories)
18
+ files = {f for f in all_files if os.path.isfile(f)}
19
+ return files
20
+ except Exception as e:
21
+ print(f"Error getting current files: {e}")
22
+ return set()
23
+
24
+ def cleanup_created_files(self, files_before):
25
+ """Remove files that were created during execution"""
26
+ try:
27
+ files_after = self.get_current_files()
28
+ newly_created_files = files_after - files_before
29
+
30
+ for file_path in newly_created_files:
31
+ try:
32
+ if os.path.exists(file_path):
33
+ os.remove(file_path)
34
+ print(f"Cleaned up file: {file_path}")
35
+ except Exception as e:
36
+ print(f"Error cleaning up file {file_path}: {e}")
37
+
38
+ except Exception as e:
39
+ print(f"Error during cleanup: {e}")
40
 
41
  def __call__(self, question_text, file_name) -> str:
42
+ # Take snapshot of files before execution
43
+ files_before = self.get_current_files()
44
+
45
+ try:
46
+ enhanced_question = get_prompt(question_text, file_name)
47
+ response = self.run(enhanced_question, reset=True)
48
+ return response
49
+ finally:
50
+ # Always clean up files, even if there's an exception
51
+ self.cleanup_created_files(files_before)
app.py CHANGED
@@ -1,8 +1,3 @@
1
- import os
2
- import gradio as gr
3
- import requests
4
- import pandas as pd
5
-
6
  from smolagents import (
7
  InferenceClientModel,
8
  FinalAnswerTool
@@ -15,14 +10,14 @@ from tools.image_processing_tools import ask_question_about_image
15
 
16
  from agent import QuestionAnsweringAgent
17
 
18
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
19
 
20
  model = InferenceClientModel(
21
  provider="auto",
22
  model_id="Qwen/Qwen3-Coder-30B-A3B-Instruct",
23
  temperature=0,
24
  top_p=1.0,
25
- seed=42,
26
  )
27
 
28
  agent_tools = [
@@ -37,204 +32,12 @@ agent = QuestionAnsweringAgent(
37
  name="question_answering_expert",
38
  model=model,
39
  tools=agent_tools,
40
- add_base_tools=True, # Disable auto base tools to avoid overriding custom visit_webpage
41
- planning_interval=None, # Disable planning to ensure immediate stop after final_answer
42
- additional_authorized_imports=["bs4"],
43
  max_steps=10,
44
  verbosity_level=2, # For better debugging
45
  )
46
-
47
- def run_and_submit_all( profile: gr.OAuthProfile | None):
48
- """
49
- Fetches all questions, runs the BasicAgent on them, submits all answers,
50
- and displays the results.
51
- """
52
- # --- Determine HF Space Runtime URL and Repo URL ---
53
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
54
-
55
- if profile:
56
- username= f"{profile.username}"
57
- print(f"User logged in: {username}")
58
- else:
59
- print("User not logged in.")
60
- return "Please Login to Hugging Face with the button.", None
61
-
62
- api_url = DEFAULT_API_URL
63
- questions_url = f"{api_url}/questions"
64
- submit_url = f"{api_url}/submit"
65
-
66
- # 1. Instantiate Agent ( modify this part to create your agent)
67
- try:
68
- # Use the global agent that was already initialized
69
- pass # agent is already defined globally
70
- except Exception as e:
71
- print(f"Error instantiating agent: {e}")
72
- return f"Error initializing agent: {e}", None
73
- # 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)
74
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
75
- print(agent_code)
76
-
77
- # 2. Fetch Questions
78
- print(f"Fetching questions from: {questions_url}")
79
- try:
80
- response = requests.get(questions_url, timeout=15)
81
- response.raise_for_status()
82
- questions_data = response.json()
83
- if not questions_data:
84
- print("Fetched questions list is empty.")
85
- return "Fetched questions list is empty or invalid format.", None
86
- print(f"Fetched {len(questions_data)} questions.")
87
- except requests.exceptions.RequestException as e:
88
- print(f"Error fetching questions: {e}")
89
- return f"Error fetching questions: {e}", None
90
- except requests.exceptions.JSONDecodeError as e:
91
- print(f"Error decoding JSON response from questions endpoint: {e}")
92
- print(f"Response text: {response.text[:500]}")
93
- return f"Error decoding server response for questions: {e}", None
94
- except Exception as e:
95
- print(f"An unexpected error occurred fetching questions: {e}")
96
- return f"An unexpected error occurred fetching questions: {e}", None
97
-
98
- # 3. Run your Agent
99
- results_log = []
100
- answers_payload = []
101
- print(f"Running agent on {len(questions_data)} questions...")
102
- for item in questions_data[:]:
103
- task_id = item.get("task_id")
104
- question_text = item.get("question")
105
- file_name = item.get("file_name")
106
- if not task_id or question_text is None:
107
- print(f"Skipping item with missing task_id or question: {item}")
108
- continue
109
- try:
110
- submitted_answer = agent(question_text, file_name)
111
-
112
- # Log the execution timeline
113
- print(f"Task {task_id} execution steps: {len(agent.memory.steps)}")
114
- timeline_details = []
115
- for i, step in enumerate(agent.memory.steps):
116
- step_type = type(step).__name__
117
- print(f" Step {i+1}: {step_type}")
118
- timeline_details.append(f"Step {i+1}: {step_type}")
119
-
120
- # Add timeline information to results
121
- timeline_summary = "; ".join(timeline_details)
122
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
123
- results_log.append({
124
- "Task ID": task_id,
125
- "Question": question_text,
126
- "Submitted Answer": submitted_answer,
127
- "Timeline": timeline_summary,
128
- "Total Steps": len(agent.memory.steps)
129
- })
130
- except Exception as e:
131
- print(f"Error running agent on task {task_id}: {e}")
132
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
133
-
134
- if not answers_payload:
135
- print("Agent did not produce any answers to submit.")
136
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
137
-
138
- # 4. Prepare Submission
139
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
140
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
141
- print(status_update)
142
-
143
- # 5. Submit
144
- print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
145
- try:
146
- response = requests.post(submit_url, json=submission_data, timeout=60)
147
- response.raise_for_status()
148
- result_data = response.json()
149
- final_status = (
150
- f"Submission Successful!\n"
151
- f"User: {result_data.get('username')}\n"
152
- f"Overall Score: {result_data.get('score', 'N/A')}% "
153
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
154
- f"Message: {result_data.get('message', 'No message received.')}"
155
- )
156
- print("Submission successful.")
157
- results_df = pd.DataFrame(results_log)
158
- return final_status, results_df
159
- except requests.exceptions.HTTPError as e:
160
- error_detail = f"Server responded with status {e.response.status_code}."
161
- try:
162
- error_json = e.response.json()
163
- error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
164
- except requests.exceptions.JSONDecodeError:
165
- error_detail += f" Response: {e.response.text[:500]}"
166
- status_message = f"Submission Failed: {error_detail}"
167
- print(status_message)
168
- results_df = pd.DataFrame(results_log)
169
- return status_message, results_df
170
- except requests.exceptions.Timeout:
171
- status_message = "Submission Failed: The request timed out."
172
- print(status_message)
173
- results_df = pd.DataFrame(results_log)
174
- return status_message, results_df
175
- except requests.exceptions.RequestException as e:
176
- status_message = f"Submission Failed: Network error - {e}"
177
- print(status_message)
178
- results_df = pd.DataFrame(results_log)
179
- return status_message, results_df
180
- except Exception as e:
181
- status_message = f"An unexpected error occurred during submission: {e}"
182
- print(status_message)
183
- results_df = pd.DataFrame(results_log)
184
- return status_message, results_df
185
-
186
-
187
- # --- Build Gradio Interface using Blocks ---
188
- with gr.Blocks() as demo:
189
- gr.Markdown("# Basic Agent Evaluation Runner")
190
- gr.Markdown(
191
- """
192
- **Instructions:**
193
-
194
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
195
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
196
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
197
-
198
- ---
199
- **Disclaimers:**
200
- 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).
201
- 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.
202
- """
203
- )
204
-
205
- gr.LoginButton()
206
-
207
- run_button = gr.Button("Run Evaluation & Submit All Answers")
208
-
209
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
210
- # Removed max_rows=10 from DataFrame constructor
211
- results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
212
-
213
- run_button.click(
214
- fn=run_and_submit_all,
215
- outputs=[status_output, results_table]
216
- )
217
 
218
  if __name__ == "__main__":
219
- print("\n" + "-"*30 + " App Starting " + "-"*30)
220
- # Check for SPACE_HOST and SPACE_ID at startup for information
221
- space_host_startup = os.getenv("SPACE_HOST")
222
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
223
-
224
- if space_host_startup:
225
- print(f"✅ SPACE_HOST found: {space_host_startup}")
226
- print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
227
- else:
228
- print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
229
-
230
- if space_id_startup: # Print repo URLs if SPACE_ID is found
231
- print(f"✅ SPACE_ID found: {space_id_startup}")
232
- print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
233
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
234
- else:
235
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
236
-
237
- print("-"*(60 + len(" App Starting ")) + "\n")
238
-
239
- print("Launching Gradio Interface for Basic Agent Evaluation...")
240
- demo.launch(debug=True, share=False)
 
 
 
 
 
 
1
  from smolagents import (
2
  InferenceClientModel,
3
  FinalAnswerTool
 
10
 
11
  from agent import QuestionAnsweringAgent
12
 
13
+ from submission import build_gradio_interface
14
 
15
  model = InferenceClientModel(
16
  provider="auto",
17
  model_id="Qwen/Qwen3-Coder-30B-A3B-Instruct",
18
  temperature=0,
19
  top_p=1.0,
20
+ seed=42
21
  )
22
 
23
  agent_tools = [
 
32
  name="question_answering_expert",
33
  model=model,
34
  tools=agent_tools,
35
+ add_base_tools=True,
36
+ planning_interval=None,
37
+ additional_authorized_imports=["os", "bs4", "PIL", "transformers", "torch", "requests", "glob"],
38
  max_steps=10,
39
  verbosity_level=2, # For better debugging
40
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  if __name__ == "__main__":
43
+ build_gradio_interface(agent)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
prompt.py CHANGED
@@ -7,13 +7,15 @@ def get_prompt(question_text, file_name):
7
  NEVER include words like: career, albums, list, biography, years, numbers, prepositions, or date ranges.
8
  Examples:
9
  - "Lionel Messi career" → use: wikipedia_summary("Lionel Messi")
10
- - "Mercedes Sosa discography" → use: wikipedia_summary("Mercedes Sosa")
11
  - "Battle of Hastings timeline" → use: wikipedia_summary("Battle of Hastings")
12
  - "Population of Paris in 2010" → use: wikipedia_summary("Paris")
13
  - If necessary, visit the wikipedia page listed in the wikipedia summary tool to read the full content. You will find the page url in the output of the wikipedia summary tool at the end after the **Read more:** section. Use the `read_wikipedia_page` tool to visit the page.
14
  - When using the `read_wikipedia_page` tool, you may find tables in the page. To analyze the tables, please use a code snippet to read the tables into a pandas dataframe and analyze the data.
15
- - If necessary, perform a web search using the `web_search` tool to find possible sources of information.
16
- - If the web search only returns titles and short snippets, you MUST visit the actual webpage using the `read_wikipedia_page` tool to read the full content before answering.
 
 
 
17
  - If the task requires reading, listening, or analyzing a file, you must use the file specified after the question, NOT the file name mentioned casually inside the question text.
18
  - Comma separated lists MUST contain a single space after each comma.
19
  - If you are asked for a number, don't use comma to write your number, nor use units such as $$ or percent sign unless specified otherwise.
@@ -37,8 +39,8 @@ def get_prompt(question_text, file_name):
37
  -- beginning of question --
38
  {question_text}
39
  -- end of question --
40
- If the questions mentions the need to use a file, use the following `file_name` value below as the `file_name` parameter in any function calls:
41
- file_name: {file_name}
42
- """
43
 
44
  return PROMPT
 
7
  NEVER include words like: career, albums, list, biography, years, numbers, prepositions, or date ranges.
8
  Examples:
9
  - "Lionel Messi career" → use: wikipedia_summary("Lionel Messi")
 
10
  - "Battle of Hastings timeline" → use: wikipedia_summary("Battle of Hastings")
11
  - "Population of Paris in 2010" → use: wikipedia_summary("Paris")
12
  - If necessary, visit the wikipedia page listed in the wikipedia summary tool to read the full content. You will find the page url in the output of the wikipedia summary tool at the end after the **Read more:** section. Use the `read_wikipedia_page` tool to visit the page.
13
  - When using the `read_wikipedia_page` tool, you may find tables in the page. To analyze the tables, please use a code snippet to read the tables into a pandas dataframe and analyze the data.
14
+ - If necessary, download a youtube video using the `download_youtube_url_audio` or `download_youtube_url_images` tool to find possible sources of information. For the parameter `num_images`, use a large number if you need to have comprehensive information about the video.
15
+ - If necessary, analyze the audio or images downloaded from youtube using the `ask_question_about_image` tool to find possible sources of information.
16
+ - If necessary, perform a web search using the `web_search` tool to find possible sources of information.
17
+ - If necessary, please analyze the images downloaded using the `ask_question_about_image` tool to find possible sources of information.
18
+ - If the web search only returns titles and short snippets, you MUST visit the actual webpage using the `visit_webpage` tool to read the full content before answering.
19
  - If the task requires reading, listening, or analyzing a file, you must use the file specified after the question, NOT the file name mentioned casually inside the question text.
20
  - Comma separated lists MUST contain a single space after each comma.
21
  - If you are asked for a number, don't use comma to write your number, nor use units such as $$ or percent sign unless specified otherwise.
 
39
  -- beginning of question --
40
  {question_text}
41
  -- end of question --
42
+
43
+ IMPORTANT: If the question mentions the need to use a file, the file name is provided below.
44
+ file_name: {file_name}"""
45
 
46
  return PROMPT
requirements.txt CHANGED
@@ -1,6 +1,6 @@
1
  gradio[oauth]==5.44.1
2
  requests==2.32.5
3
- smolagents==1.21.3
4
  duckduckgo-search==8.1.1
5
  ddgs==9.5.5
6
  markdownify==0.11.0
@@ -11,5 +11,4 @@ beautifulsoup4==4.12.3
11
  langchain_community==0.3.2
12
  wikipedia==1.4.0
13
  tabulate==0.9.0
14
- yt-dlp==2025.9.5
15
- transformers==4.46.1
 
1
  gradio[oauth]==5.44.1
2
  requests==2.32.5
3
+ smolagents[transformers]==1.21.3
4
  duckduckgo-search==8.1.1
5
  ddgs==9.5.5
6
  markdownify==0.11.0
 
11
  langchain_community==0.3.2
12
  wikipedia==1.4.0
13
  tabulate==0.9.0
14
+ yt-dlp==2025.9.5
 
submission.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import requests
4
+ import pandas as pd
5
+ import numpy as np
6
+
7
+
8
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
9
+ SELECTED_QUESTIONS = [3]
10
+ def run_and_submit_all(agent, profile: gr.OAuthProfile | None):
11
+ """
12
+ Fetches all questions, runs the BasicAgent on them, submits all answers,
13
+ and displays the results.
14
+ """
15
+ # --- Determine HF Space Runtime URL and Repo URL ---
16
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
17
+
18
+ if profile:
19
+ username= f"{profile.username}"
20
+ print(f"User logged in: {username}")
21
+ else:
22
+ print("User not logged in.")
23
+ return "Please Login to Hugging Face with the button.", None
24
+
25
+ api_url = DEFAULT_API_URL
26
+ questions_url = f"{api_url}/questions"
27
+ submit_url = f"{api_url}/submit"
28
+
29
+ # 1. Instantiate Agent ( modify this part to create your agent)
30
+ try:
31
+ agent = agent
32
+ except Exception as e:
33
+ print(f"Error instantiating agent: {e}")
34
+ return f"Error initializing agent: {e}", None
35
+ # 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)
36
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
37
+ print(agent_code)
38
+
39
+ # 2. Fetch Questions
40
+ print(f"Fetching questions from: {questions_url}")
41
+ try:
42
+ response = requests.get(questions_url, timeout=15)
43
+ response.raise_for_status()
44
+ questions_data = response.json()
45
+ if not questions_data:
46
+ print("Fetched questions list is empty.")
47
+ return "Fetched questions list is empty or invalid format.", None
48
+ print(f"Fetched {len(questions_data)} questions.")
49
+ except requests.exceptions.RequestException as e:
50
+ print(f"Error fetching questions: {e}")
51
+ return f"Error fetching questions: {e}", None
52
+ except requests.exceptions.JSONDecodeError as e:
53
+ print(f"Error decoding JSON response from questions endpoint: {e}")
54
+ print(f"Response text: {response.text[:500]}")
55
+ return f"Error decoding server response for questions: {e}", None
56
+ except Exception as e:
57
+ print(f"An unexpected error occurred fetching questions: {e}")
58
+ return f"An unexpected error occurred fetching questions: {e}", None
59
+
60
+ # 3. Run your Agent
61
+ results_log = []
62
+ answers_payload = []
63
+ is_correct_answers = []
64
+ print(f"Running agent on {len(questions_data)} questions...")
65
+ for item in np.array(questions_data).take(SELECTED_QUESTIONS):
66
+ task_id = item.get("task_id")
67
+ question_text = item.get("question")
68
+ file_name = item.get("file_name")
69
+ if not task_id or question_text is None:
70
+ print(f"Skipping item with missing task_id or question: {item}")
71
+ continue
72
+ try:
73
+ submitted_answer = agent(question_text, file_name)
74
+ answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
75
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
76
+ except Exception as e:
77
+ print(f"Error running agent on task {task_id}: {e}")
78
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
79
+ individual_submission_data = {
80
+ "username": username.strip(),
81
+ "agent_code": agent_code,
82
+ "answers": [{"task_id": task_id, "submitted_answer": submitted_answer}]
83
+ }
84
+
85
+ individual_response = requests.post(submit_url, json=individual_submission_data, timeout=60)
86
+ individual_response.raise_for_status()
87
+ individual_result_data = individual_response.json()
88
+ is_correct_answers.append(True if individual_result_data.get("correct_count", 0) == 1 else False)
89
+ if not answers_payload:
90
+ print("Agent did not produce any answers to submit.")
91
+ return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
92
+
93
+ # 4. Prepare Submission
94
+ submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
95
+ status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
96
+ print(status_update)
97
+
98
+ # 5. Submit
99
+ print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
100
+ try:
101
+ response = requests.post(submit_url, json=submission_data, timeout=60)
102
+ response.raise_for_status()
103
+ result_data = response.json()
104
+ final_status = (
105
+ f"Submission Successful!\n"
106
+ f"User: {result_data.get('username')}\n"
107
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
108
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
109
+ f"Message: {result_data.get('message', 'No message received.')}"
110
+ )
111
+ print("Submission successful.")
112
+ results_df = pd.DataFrame(results_log)
113
+ results_df["Is Correct"] = is_correct_answers
114
+ return final_status, results_df
115
+ except requests.exceptions.HTTPError as e:
116
+ error_detail = f"Server responded with status {e.response.status_code}."
117
+ try:
118
+ error_json = e.response.json()
119
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
120
+ except requests.exceptions.JSONDecodeError:
121
+ error_detail += f" Response: {e.response.text[:500]}"
122
+ status_message = f"Submission Failed: {error_detail}"
123
+ print(status_message)
124
+ results_df = pd.DataFrame(results_log)
125
+ results_df["Is Correct"] = is_correct_answers
126
+ return status_message, results_df
127
+ except requests.exceptions.Timeout:
128
+ status_message = "Submission Failed: The request timed out."
129
+ print(status_message)
130
+ results_df = pd.DataFrame(results_log)
131
+ return status_message, results_df
132
+ except requests.exceptions.RequestException as e:
133
+ status_message = f"Submission Failed: Network error - {e}"
134
+ print(status_message)
135
+ results_df = pd.DataFrame(results_log)
136
+ return status_message, results_df
137
+ except Exception as e:
138
+ status_message = f"An unexpected error occurred during submission: {e}"
139
+ print(status_message)
140
+ results_df = pd.DataFrame(results_log)
141
+ return status_message, results_df
142
+
143
+
144
+ def build_gradio_interface(agent):
145
+ # --- Build Gradio Interface using Blocks ---
146
+ with gr.Blocks() as demo:
147
+ gr.Markdown("# Basic Agent Evaluation Runner")
148
+ gr.Markdown(
149
+ """
150
+ **Instructions:**
151
+ 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
152
+ 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
153
+ 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
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
+
161
+ gr.LoginButton()
162
+
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
+ def run_with_login_state(profile: gr.OAuthProfile):
170
+ return run_and_submit_all(agent, profile)
171
+
172
+ run_button.click(
173
+ fn=run_with_login_state,
174
+ outputs=[status_output, results_table]
175
+ )
176
+
177
+ print("\n" + "-"*30 + " App Starting " + "-"*30)
178
+ # Check for SPACE_HOST and SPACE_ID at startup for information
179
+ space_host_startup = os.getenv("SPACE_HOST")
180
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
181
+
182
+ if space_host_startup:
183
+ print(f"✅ SPACE_HOST found: {space_host_startup}")
184
+ print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
185
+ else:
186
+ print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
187
+
188
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
189
+ print(f"✅ SPACE_ID found: {space_id_startup}")
190
+ print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
191
+ print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
192
+ else:
193
+ print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
194
+
195
+ print("-"*(60 + len(" App Starting ")) + "\n")
196
+
197
+ print("Launching Gradio Interface for Basic Agent Evaluation...")
198
+ demo.launch(debug=True, share=False)
tools/audio_processing_tools.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from whisper import load_model
2
+ from smolagents.tools import tool
3
+
4
+ @tool
5
+ def ask_question_about_audio(question: str, path_to_audio: str) -> str:
6
+ """
7
+ Ask a question about an audio and return the answer.
8
+ """
9
+ model = load_model("base")
10
+ res = model.transcribe(path_to_audio)
11
+ return res["text"]
tools/image_processing_tools.py CHANGED
@@ -1,6 +1,24 @@
1
  from PIL import Image
2
- from transformers import pipeline
3
  from smolagents.tools import tool
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  @tool
6
  def ask_question_about_image(question: str, path_to_image: str) -> str:
@@ -12,24 +30,30 @@ def ask_question_about_image(question: str, path_to_image: str) -> str:
12
  Returns:
13
  A string with the answer to the question.
14
  """
15
- pipe = pipeline("image-text-to-text", model="llava-hf/llava-interleave-qwen-0.5b-hf")
 
16
 
17
- image = Image.open(fp=path_to_image)
 
18
 
19
- messages = [
20
- {
21
- "role": "user",
22
- "content": [
23
- {
24
- "type": "image",
25
- "image": image,
26
- },
27
- {"type": "text", "text": question},
28
- ],
29
- }
30
- ]
31
-
32
- outputs = pipe(text=messages, max_new_tokens=60, return_full_text=False)
33
-
34
- return outputs[0]["generated_text"]
 
 
 
 
35
 
 
1
  from PIL import Image
2
+ from transformers import BlipProcessor, BlipForQuestionAnswering
3
  from smolagents.tools import tool
4
+ import torch
5
+ import requests
6
+ import os
7
+
8
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
9
+
10
+ def _download_file(file_name: str) -> None:
11
+ """Download file if it doesn't exist locally"""
12
+ try:
13
+ # Try to open the file to check if it exists
14
+ with open(file_name, 'rb') as f:
15
+ pass # File exists, do nothing
16
+ except FileNotFoundError:
17
+ # File doesn't exist, download it
18
+ url = f"{DEFAULT_API_URL}/files/{file_name.split('.')[-2]}"
19
+ r = requests.get(url)
20
+ with open(file_name, "wb") as f:
21
+ f.write(r.content)
22
 
23
  @tool
24
  def ask_question_about_image(question: str, path_to_image: str) -> str:
 
30
  Returns:
31
  A string with the answer to the question.
32
  """
33
+ # Download the file if it doesn't exist
34
+ _download_file(path_to_image)
35
 
36
+ # Check if CUDA is available and use GPU if possible, otherwise use CPU
37
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
38
 
39
+ # Load the processor and model (using BLIP for more stable VQA)
40
+ processor = BlipProcessor.from_pretrained("Salesforce/blip-vqa-base")
41
+ model = BlipForQuestionAnswering.from_pretrained("Salesforce/blip-vqa-base")
42
+ model = model.to(device)
43
+
44
+ # Load and process the image
45
+ image = Image.open(path_to_image).convert('RGB')
46
+
47
+ # Process the inputs
48
+ inputs = processor(image, question, return_tensors="pt")
49
+ inputs = {k: v.to(device) for k, v in inputs.items()}
50
+
51
+ # Generate the answer
52
+ with torch.no_grad():
53
+ outputs = model.generate(**inputs, max_length=50, num_beams=5)
54
+
55
+ # Decode and return the answer
56
+ answer = processor.decode(outputs[0], skip_special_tokens=True)
57
+
58
+ return answer
59
 
tools/youtube_tools.py CHANGED
@@ -3,8 +3,6 @@ import os
3
  import subprocess
4
  from yt_dlp import YoutubeDL
5
 
6
-
7
-
8
  from smolagents.tools import tool
9
 
10
  # Use FFmpeg to extract frames from the video
@@ -54,13 +52,13 @@ def extract_frames_with_ffmpeg(video_path: str, num_frames: int) -> [str]:
54
  @tool
55
  def download_youtube_url_audio(url: str) -> str:
56
  """
57
- Download a YouTube video and return the path to the downloaded file.
58
 
59
  Args:
60
  url (str): The URL of the YouTube video to download.
61
 
62
  Returns:
63
- str: The path to the downloaded file.
64
  """
65
  ydl_audio_opts = {
66
  'format': 'bestaudio/best',
@@ -76,20 +74,21 @@ def download_youtube_url_audio(url: str) -> str:
76
 
77
  with YoutubeDL(ydl_audio_opts) as ydl:
78
  file_path = ydl.extract_info(url)
 
79
  return file_path['requested_downloads'][0]['filepath']
80
 
81
 
82
  @tool
83
  def download_youtube_url_images(url: str, num_images: int = 3) -> str:
84
  """
85
- Download a YouTube video and return the path to the downloaded file.
86
 
87
  Args:
88
  url (str): The URL of the YouTube video to download.
89
- num_images (int): The number of images to download.
90
 
91
  Returns:
92
- str: The different paths to the downloaded files, separated by newlines.
93
  """
94
  # First, download the video
95
  ydl_images_opts = {
 
3
  import subprocess
4
  from yt_dlp import YoutubeDL
5
 
 
 
6
  from smolagents.tools import tool
7
 
8
  # Use FFmpeg to extract frames from the video
 
52
  @tool
53
  def download_youtube_url_audio(url: str) -> str:
54
  """
55
+ Download a YouTube video using the url, extract the audio and return the path to the downloaded file.
56
 
57
  Args:
58
  url (str): The URL of the YouTube video to download.
59
 
60
  Returns:
61
+ str: The path to the downloaded audio file.
62
  """
63
  ydl_audio_opts = {
64
  'format': 'bestaudio/best',
 
74
 
75
  with YoutubeDL(ydl_audio_opts) as ydl:
76
  file_path = ydl.extract_info(url)
77
+
78
  return file_path['requested_downloads'][0]['filepath']
79
 
80
 
81
  @tool
82
  def download_youtube_url_images(url: str, num_images: int = 3) -> str:
83
  """
84
+ Download a YouTube video using the url, extract the frames and return the path to the downloaded files.
85
 
86
  Args:
87
  url (str): The URL of the YouTube video to download.
88
+ num_images (int): The number of images to download. The images are extracted from the video at regular intervals.
89
 
90
  Returns:
91
+ str: The different paths to the downloaded frames, separated by newlines.
92
  """
93
  # First, download the video
94
  ydl_images_opts = {