0f3dy commited on
Commit
ed5a0e4
·
verified ·
1 Parent(s): a0a5b34

Upload 3 files

Browse files
Files changed (3) hide show
  1. app (1).py +319 -0
  2. gitattributes (1) +35 -0
  3. requirements (1).txt +6 -0
app (1).py ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import requests
4
+ import inspect
5
+ import pandas as pd
6
+ import time
7
+ import re
8
+ from markdownify import markdownify
9
+ from smolagents import Tool, DuckDuckGoSearchTool, CodeAgent, WikipediaSearchTool, LiteLLMModel
10
+
11
+ # (Keep Constants as is)
12
+ # --- Constants ---
13
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
14
+
15
+
16
+ class DownloadTaskAttachmentTool(Tool):
17
+ name = "download_file"
18
+ description = "Downloads the file attached to the task ID"
19
+ inputs = {'task_id': {'type': 'string', 'description': 'The task id to download attachment from.'}}
20
+ output_type = "string"
21
+
22
+ def forward(self, task_id: str) -> str:
23
+ """
24
+ Downloads a file associated with the given task ID.
25
+ Returns the file path where the file is saved locally.
26
+ """
27
+ file_url = f"{DEFAULT_API_URL}/files/{task_id}"
28
+ local_file_path = f"downloads/{task_id}.file"
29
+
30
+ print(f"Downloading file for task ID {task_id} from {file_url}...")
31
+ try:
32
+ response = requests.get(file_url, stream=True, timeout=15)
33
+ response.raise_for_status()
34
+
35
+ os.makedirs("downloads", exist_ok=True)
36
+ with open(local_file_path, "wb") as file:
37
+ for chunk in response.iter_content(chunk_size=8192):
38
+ file.write(chunk)
39
+
40
+ print(f"File downloaded successfully: {local_file_path}")
41
+ return local_file_path
42
+ except requests.exceptions.RequestException as e:
43
+ print(f"Error downloading file for task {task_id}: {e}")
44
+ raise
45
+
46
+ def __init__(self, *args, **kwargs):
47
+ self.is_initialized = False
48
+
49
+
50
+ class VisitWebpageTool(Tool):
51
+ name = "visit_webpage"
52
+ description = "Visits a webpage at the given url and reads its content as a markdown string. Use this to browse webpages."
53
+ inputs = {'url': {'type': 'string', 'description': 'The url of the webpage to visit.'}}
54
+ output_type = "string"
55
+
56
+ def forward(self, url: str) -> str:
57
+ try:
58
+ import requests
59
+ from markdownify import markdownify
60
+ from requests.exceptions import RequestException
61
+
62
+ from smolagents.utils import truncate_content
63
+ except ImportError as e:
64
+ raise ImportError(
65
+ "You must install packages `markdownify` and `requests` to run this tool: for instance run `pip install markdownify requests`."
66
+ ) from e
67
+ try:
68
+ # Send a GET request to the URL with a 20-second timeout
69
+ response = requests.get(url, timeout=20)
70
+ response.raise_for_status() # Raise an exception for bad status codes
71
+
72
+ # Convert the HTML content to Markdown
73
+ markdown_content = markdownify(response.text).strip()
74
+
75
+ # Remove multiple line breaks
76
+ markdown_content = re.sub(r"\n{3,}", "\n\n", markdown_content)
77
+
78
+ return truncate_content(markdown_content, 10000)
79
+
80
+ except requests.exceptions.Timeout:
81
+ return "The request timed out. Please try again later or check the URL."
82
+ except RequestException as e:
83
+ return f"Error fetching the webpage: {str(e)}"
84
+ except Exception as e:
85
+ return f"An unexpected error occurred: {str(e)}"
86
+
87
+ def __init__(self, *args, **kwargs):
88
+ self.is_initialized = False
89
+
90
+
91
+ # --- Basic Agent Definition ---
92
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
93
+ class BasicAgent:
94
+ def __init__(self):
95
+ self.agent = CodeAgent(
96
+ model=LiteLLMModel(model_id="openrouter/meta-llama/llama-4-maverick:free", api_key=os.getenv("OPENROUTER_KEY")),
97
+ tools=[DuckDuckGoSearchTool(), WikipediaSearchTool(), VisitWebpageTool(), DownloadTaskAttachmentTool()],
98
+ add_base_tools=True,
99
+ additional_authorized_imports=['pandas','numpy','csv','subprocess', 'exec']
100
+ )
101
+ print("BasicAgent initialized.")
102
+ def __call__(self, question: str) -> str:
103
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
104
+ agent_answer = self.agent.run(question)
105
+ print(f"Agent returning answer: {agent_answer}")
106
+ return agent_answer
107
+
108
+ def download_file(self, task_id: str) -> str:
109
+ """
110
+ Downloads a file associated with the given task ID.
111
+ Returns the file path where the file is saved locally.
112
+ """
113
+ file_url = f"{DEFAULT_API_URL}/files/{task_id}"
114
+ local_file_path = f"downloads/{task_id}.file"
115
+
116
+ print(f"Downloading file for task ID {task_id} from {file_url}...")
117
+ try:
118
+ response = requests.get(file_url, stream=True, timeout=15)
119
+ response.raise_for_status()
120
+
121
+ os.makedirs("downloads", exist_ok=True)
122
+ with open(local_file_path, "wb") as file:
123
+ for chunk in response.iter_content(chunk_size=8192):
124
+ file.write(chunk)
125
+
126
+ print(f"File downloaded successfully: {local_file_path}")
127
+ return local_file_path
128
+ except requests.exceptions.RequestException as e:
129
+ print(f"Error downloading file for task {task_id}: {e}")
130
+ raise
131
+
132
+ def run_and_submit_all( profile: gr.OAuthProfile | None):
133
+ """
134
+ Fetches all questions, runs the BasicAgent on them, submits all answers,
135
+ and displays the results.
136
+ """
137
+ # --- Determine HF Space Runtime URL and Repo URL ---
138
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
139
+
140
+ if profile:
141
+ username= f"{profile.username}"
142
+ print(f"User logged in: {username}")
143
+ else:
144
+ print("User not logged in.")
145
+ return "Please Login to Hugging Face with the button.", None
146
+
147
+ api_url = DEFAULT_API_URL
148
+ questions_url = f"{api_url}/questions"
149
+ submit_url = f"{api_url}/submit"
150
+
151
+ # 1. Instantiate Agent ( modify this part to create your agent)
152
+ try:
153
+ agent = BasicAgent()
154
+ except Exception as e:
155
+ print(f"Error instantiating agent: {e}")
156
+ return f"Error initializing agent: {e}", None
157
+ # 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)
158
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
159
+ print(agent_code)
160
+
161
+ # 2. Fetch Questions
162
+ print(f"Fetching questions from: {questions_url}")
163
+ try:
164
+ response = requests.get(questions_url, timeout=15)
165
+ response.raise_for_status()
166
+ questions_data = response.json()
167
+ if not questions_data:
168
+ print("Fetched questions list is empty.")
169
+ return "Fetched questions list is empty or invalid format.", None
170
+ print(f"Fetched {len(questions_data)} questions.")
171
+ except requests.exceptions.RequestException as e:
172
+ print(f"Error fetching questions: {e}")
173
+ return f"Error fetching questions: {e}", None
174
+ except requests.exceptions.JSONDecodeError as e:
175
+ print(f"Error decoding JSON response from questions endpoint: {e}")
176
+ print(f"Response text: {response.text[:500]}")
177
+ return f"Error decoding server response for questions: {e}", None
178
+ except Exception as e:
179
+ print(f"An unexpected error occurred fetching questions: {e}")
180
+ return f"An unexpected error occurred fetching questions: {e}", None
181
+
182
+ # 3. Run your Agent
183
+ results_log = []
184
+ answers_payload = []
185
+ print(f"Running agent on {len(questions_data)} questions...")
186
+ for item in questions_data:
187
+ task_id = item.get("task_id")
188
+ question_text = item.get("question")
189
+ requires_file = item.get("requires_file", False)
190
+
191
+ if not task_id or question_text is None:
192
+ print(f"Skipping item with missing task_id or question: {item}")
193
+ continue
194
+
195
+ try:
196
+ # Download file if required
197
+ if requires_file:
198
+ file_path = agent.download_file(task_id)
199
+ print(f"File for task {task_id} saved at: {file_path}")
200
+ # Optionally, pass the file path to the agent if needed
201
+ submitted_answer = agent(f"{question_text} (File: {file_path})")
202
+ else:
203
+ submitted_answer = agent(question_text)
204
+
205
+ answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
206
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
207
+ time.sleep(2)
208
+ except Exception as e:
209
+ print(f"Error running agent on task {task_id}: {e}")
210
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
211
+
212
+
213
+ if not answers_payload:
214
+ print("Agent did not produce any answers to submit.")
215
+ return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
216
+
217
+ # 4. Prepare Submission
218
+ submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
219
+ status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
220
+ print(status_update)
221
+
222
+ # 5. Submit
223
+ print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
224
+ try:
225
+ response = requests.post(submit_url, json=submission_data, timeout=60)
226
+ response.raise_for_status()
227
+ result_data = response.json()
228
+ final_status = (
229
+ f"Submission Successful!\n"
230
+ f"User: {result_data.get('username')}\n"
231
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
232
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
233
+ f"Message: {result_data.get('message', 'No message received.')}"
234
+ )
235
+ print("Submission successful.")
236
+ results_df = pd.DataFrame(results_log)
237
+ return final_status, results_df
238
+ except requests.exceptions.HTTPError as e:
239
+ error_detail = f"Server responded with status {e.response.status_code}."
240
+ try:
241
+ error_json = e.response.json()
242
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
243
+ except requests.exceptions.JSONDecodeError:
244
+ error_detail += f" Response: {e.response.text[:500]}"
245
+ status_message = f"Submission Failed: {error_detail}"
246
+ print(status_message)
247
+ results_df = pd.DataFrame(results_log)
248
+ return status_message, results_df
249
+ except requests.exceptions.Timeout:
250
+ status_message = "Submission Failed: The request timed out."
251
+ print(status_message)
252
+ results_df = pd.DataFrame(results_log)
253
+ return status_message, results_df
254
+ except requests.exceptions.RequestException as e:
255
+ status_message = f"Submission Failed: Network error - {e}"
256
+ print(status_message)
257
+ results_df = pd.DataFrame(results_log)
258
+ return status_message, results_df
259
+ except Exception as e:
260
+ status_message = f"An unexpected error occurred during submission: {e}"
261
+ print(status_message)
262
+ results_df = pd.DataFrame(results_log)
263
+ return status_message, results_df
264
+
265
+
266
+ # --- Build Gradio Interface using Blocks ---
267
+ with gr.Blocks() as demo:
268
+ gr.Markdown("# Basic Agent Evaluation Runner")
269
+ gr.Markdown(
270
+ """
271
+ **Instructions:**
272
+
273
+ 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
274
+ 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
275
+ 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
276
+
277
+ ---
278
+ **Disclaimers:**
279
+ 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).
280
+ 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.
281
+ """
282
+ )
283
+
284
+ gr.LoginButton()
285
+
286
+ run_button = gr.Button("Run Evaluation & Submit All Answers")
287
+
288
+ status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
289
+ # Removed max_rows=10 from DataFrame constructor
290
+ results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
291
+
292
+ run_button.click(
293
+ fn=run_and_submit_all,
294
+ outputs=[status_output, results_table]
295
+ )
296
+
297
+ if __name__ == "__main__":
298
+ print("\n" + "-"*30 + " App Starting " + "-"*30)
299
+ # Check for SPACE_HOST and SPACE_ID at startup for information
300
+ space_host_startup = os.getenv("SPACE_HOST")
301
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
302
+
303
+ if space_host_startup:
304
+ print(f"✅ SPACE_HOST found: {space_host_startup}")
305
+ print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
306
+ else:
307
+ print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
308
+
309
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
310
+ print(f"✅ SPACE_ID found: {space_id_startup}")
311
+ print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
312
+ print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
313
+ else:
314
+ print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
315
+
316
+ print("-"*(60 + len(" App Starting ")) + "\n")
317
+
318
+ print("Launching Gradio Interface for Basic Agent Evaluation...")
319
+ demo.launch(debug=True, share=False)
gitattributes (1) ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
requirements (1).txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio
2
+ requests
3
+ smolagents
4
+ litellm
5
+ wikipedia-api
6
+ google.auth