M.Natale commited on
Commit
80480f7
·
1 Parent(s): fb4c8fb

modify app

Browse files
Files changed (3) hide show
  1. app.py +83 -38
  2. requirements.txt +2 -4
  3. tools.py +126 -2
app.py CHANGED
@@ -1,57 +1,104 @@
1
  import os
2
  import gradio as gr
3
  import requests
 
4
  import pandas as pd
5
- from tools import youtube_tool
6
- from smolagents import DuckDuckGoSearchTool, CodeAgent, WikipediaSearchTool, InferenceClientModel, GoogleSearchTool, \
7
- FinalAnswerTool, ToolCallingAgent
 
8
 
9
  # (Keep Constants as is)
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
- serpapi_key = os.environ.get("SERPAPI_KEY")
13
-
14
- tool = [
15
- GoogleSearchTool(provider="serpapi"),
16
- WikipediaSearchTool(),
17
- youtube_tool,
18
- FinalAnswerTool()
19
- ]
20
 
21
- my_instructions = (
22
- "You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER]. YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string."
23
- )
24
  # --- Basic Agent Definition ---
25
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  class BasicAgent:
27
  def __init__(self):
28
- self.agent = ToolCallingAgent(
29
- model= InferenceClientModel(model_id="openai/gpt-oss-20b", max_tokens=2048, provider="together"),
30
- tools=tool,
31
- max_steps=7,
32
- add_base_tools=False,
33
- planning_interval=4,
34
- instructions=my_instructions
 
35
  )
36
  print("BasicAgent initialized.")
37
 
38
  def __call__(self, question: str) -> str:
39
  print(f"Agent received question (first 50 chars): {question[:50]}...")
40
- # fixed_answer = "This is a default answer."
41
  fixed_answer = self.agent.run(question)
42
  print(f"Agent returning fixed answer: {fixed_answer}")
43
  return fixed_answer
44
 
45
- def run_and_submit_all( profile: gr.OAuthProfile | None):
 
46
  """
47
  Fetches all questions, runs the BasicAgent on them, submits all answers,
48
  and displays the results.
49
  """
50
  # --- Determine HF Space Runtime URL and Repo URL ---
51
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
52
 
53
  if profile:
54
- username= f"{profile.username}"
55
  print(f"User logged in: {username}")
56
  else:
57
  print("User not logged in.")
@@ -78,16 +125,16 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
78
  response.raise_for_status()
79
  questions_data = response.json()
80
  if not questions_data:
81
- print("Fetched questions list is empty.")
82
- return "Fetched questions list is empty or invalid format.", None
83
  print(f"Fetched {len(questions_data)} questions.")
84
  except requests.exceptions.RequestException as e:
85
  print(f"Error fetching questions: {e}")
86
  return f"Error fetching questions: {e}", None
87
  except requests.exceptions.JSONDecodeError as e:
88
- print(f"Error decoding JSON response from questions endpoint: {e}")
89
- print(f"Response text: {response.text[:500]}")
90
- return f"Error decoding server response for questions: {e}", None
91
  except Exception as e:
92
  print(f"An unexpected error occurred fetching questions: {e}")
93
  return f"An unexpected error occurred fetching questions: {e}", None
@@ -107,8 +154,8 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
107
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
108
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
109
  except Exception as e:
110
- print(f"Error running agent on task {task_id}: {e}")
111
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
112
 
113
  if not answers_payload:
114
  print("Agent did not produce any answers to submit.")
@@ -169,11 +216,9 @@ with gr.Blocks() as demo:
169
  gr.Markdown(
170
  """
171
  **Instructions:**
172
-
173
  1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
174
  2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
175
  3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
176
-
177
  ---
178
  **Disclaimers:**
179
  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).
@@ -195,10 +240,10 @@ with gr.Blocks() as demo:
195
  )
196
 
197
  if __name__ == "__main__":
198
- print("\n" + "-"*30 + " App Starting " + "-"*30)
199
  # Check for SPACE_HOST and SPACE_ID at startup for information
200
  space_host_startup = os.getenv("SPACE_HOST")
201
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
202
 
203
  if space_host_startup:
204
  print(f"✅ SPACE_HOST found: {space_host_startup}")
@@ -206,14 +251,14 @@ if __name__ == "__main__":
206
  else:
207
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
208
 
209
- if space_id_startup: # Print repo URLs if SPACE_ID is found
210
  print(f"✅ SPACE_ID found: {space_id_startup}")
211
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
212
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
213
  else:
214
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
215
 
216
- print("-"*(60 + len(" App Starting ")) + "\n")
217
 
218
  print("Launching Gradio Interface for Basic Agent Evaluation...")
219
  demo.launch(debug=True, share=False)
 
1
  import os
2
  import gradio as gr
3
  import requests
4
+ import inspect
5
  import pandas as pd
6
+ from smolagents import CodeAgent, InferenceClientModel, ToolCallingAgent
7
+ from smolagents import GoogleSearchTool, VisitWebpageTool, WikipediaSearchTool
8
+ from tools import FinalAnswerTool, get_current_time, calculate_basic_math, save_note, list_saved_notes
9
+
10
 
11
  # (Keep Constants as is)
12
  # --- Constants ---
13
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
 
 
 
 
 
 
14
 
 
 
 
15
  # --- Basic Agent Definition ---
16
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
17
+
18
+ final_answer = FinalAnswerTool()
19
+
20
+ manager_model = InferenceClientModel(
21
+ model_id='deepseek-ai/DeepSeek-R1', # it is possible that this model may be overloaded
22
+ # max_tokens=8096,
23
+ provider="together",
24
+ custom_role_conversions=None,
25
+ )
26
+
27
+ agent_model = InferenceClientModel(
28
+ model_id='Qwen/Qwen2.5-Coder-32B-Instruct', # it is possible that this model may be overloaded
29
+ provider="together",
30
+ max_tokens=8096,
31
+ custom_role_conversions=None,
32
+ )
33
+
34
+ web_agent = ToolCallingAgent(
35
+ model=agent_model,
36
+ tools=[
37
+ GoogleSearchTool("serper"), VisitWebpageTool()
38
+ ],
39
+ name="web_agent",
40
+ description="Browses the web to find information",
41
+ max_steps=10,
42
+ )
43
+
44
+ managed_web_agent = CodeAgent(
45
+ model=agent_model,
46
+ managed_agents=[web_agent],
47
+ tools=[],
48
+ name='search_agent',
49
+ description="Runs web searches for you. Give it your query as an argument."
50
+ )
51
+
52
+ wiki_agent = ToolCallingAgent(
53
+ model=agent_model,
54
+ tools=[
55
+ WikipediaSearchTool(), VisitWebpageTool()
56
+ ],
57
+ name="web_agent",
58
+ description="Browses the web to find information",
59
+ max_steps=10,
60
+ )
61
+
62
+ managed_wiki_agent = CodeAgent(
63
+ model=agent_model,
64
+ managed_agents=[wiki_agent],
65
+ tools=[],
66
+ name='wiki_agent',
67
+ description="Runs wikipedia searches for you. Give it your query as an argument."
68
+ )
69
+
70
+
71
  class BasicAgent:
72
  def __init__(self):
73
+ self.agent = CodeAgent(
74
+ model=manager_model,
75
+ tools=[final_answer, get_current_time, calculate_basic_math, save_note, list_saved_notes],
76
+ managed_agents=[managed_web_agent, managed_wiki_agent],
77
+ additional_authorized_imports=['datetime', 'pandas', 'numpy'],
78
+ planning_interval=6,
79
+ max_steps=16,
80
+ add_base_tools=True,
81
  )
82
  print("BasicAgent initialized.")
83
 
84
  def __call__(self, question: str) -> str:
85
  print(f"Agent received question (first 50 chars): {question[:50]}...")
86
+
87
  fixed_answer = self.agent.run(question)
88
  print(f"Agent returning fixed answer: {fixed_answer}")
89
  return fixed_answer
90
 
91
+
92
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
93
  """
94
  Fetches all questions, runs the BasicAgent on them, submits all answers,
95
  and displays the results.
96
  """
97
  # --- Determine HF Space Runtime URL and Repo URL ---
98
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
99
 
100
  if profile:
101
+ username = f"{profile.username}"
102
  print(f"User logged in: {username}")
103
  else:
104
  print("User not logged in.")
 
125
  response.raise_for_status()
126
  questions_data = response.json()
127
  if not questions_data:
128
+ print("Fetched questions list is empty.")
129
+ return "Fetched questions list is empty or invalid format.", None
130
  print(f"Fetched {len(questions_data)} questions.")
131
  except requests.exceptions.RequestException as e:
132
  print(f"Error fetching questions: {e}")
133
  return f"Error fetching questions: {e}", None
134
  except requests.exceptions.JSONDecodeError as e:
135
+ print(f"Error decoding JSON response from questions endpoint: {e}")
136
+ print(f"Response text: {response.text[:500]}")
137
+ return f"Error decoding server response for questions: {e}", None
138
  except Exception as e:
139
  print(f"An unexpected error occurred fetching questions: {e}")
140
  return f"An unexpected error occurred fetching questions: {e}", None
 
154
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
155
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
156
  except Exception as e:
157
+ print(f"Error running agent on task {task_id}: {e}")
158
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
159
 
160
  if not answers_payload:
161
  print("Agent did not produce any answers to submit.")
 
216
  gr.Markdown(
217
  """
218
  **Instructions:**
 
219
  1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
220
  2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
221
  3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
 
222
  ---
223
  **Disclaimers:**
224
  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).
 
240
  )
241
 
242
  if __name__ == "__main__":
243
+ print("\n" + "-" * 30 + " App Starting " + "-" * 30)
244
  # Check for SPACE_HOST and SPACE_ID at startup for information
245
  space_host_startup = os.getenv("SPACE_HOST")
246
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
247
 
248
  if space_host_startup:
249
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
251
  else:
252
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
253
 
254
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
255
  print(f"✅ SPACE_ID found: {space_id_startup}")
256
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
257
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
258
  else:
259
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
260
 
261
+ print("-" * (60 + len(" App Starting ")) + "\n")
262
 
263
  print("Launching Gradio Interface for Basic Agent Evaluation...")
264
  demo.launch(debug=True, share=False)
requirements.txt CHANGED
@@ -1,9 +1,7 @@
1
  gradio
2
  requests
3
  smolagents
4
- ddgs
5
- transformers
6
- wikipedia-api
7
- pandas
8
  duckduckgo-search
 
 
9
  youtube_transcript_api
 
1
  gradio
2
  requests
3
  smolagents
 
 
 
 
4
  duckduckgo-search
5
+ wikipedia-api
6
+ markdownify
7
  youtube_transcript_api
tools.py CHANGED
@@ -1,6 +1,12 @@
1
- from smolagents import tool
2
  from youtube_transcript_api import YouTubeTranscriptApi
3
  from urllib.parse import urlparse, parse_qs
 
 
 
 
 
 
4
 
5
  @tool
6
  def youtube_tool(video_url: str) -> str:
@@ -33,4 +39,122 @@ def youtube_tool(video_url: str) -> str:
33
  return full_text
34
 
35
  except Exception as e:
36
- return f"Error analyzing YouTube video: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import tool, Tool
2
  from youtube_transcript_api import YouTubeTranscriptApi
3
  from urllib.parse import urlparse, parse_qs
4
+ import os
5
+ import ast
6
+ import operator as op
7
+ from typing import Any, Optional
8
+ from datetime import datetime
9
+ from smolagents import tool
10
 
11
  @tool
12
  def youtube_tool(video_url: str) -> str:
 
39
  return full_text
40
 
41
  except Exception as e:
42
+ return f"Error analyzing YouTube video: {str(e)}"
43
+
44
+
45
+ @tool
46
+ def get_current_time() -> str:
47
+ """Get the current date and time in a readable format."""
48
+ now = datetime.now()
49
+ return now.strftime("%Y-%m-%d %H:%M:%S")
50
+
51
+
52
+ @tool
53
+ def calculate_basic_math(expression: str) -> str:
54
+ """
55
+ Safely evaluate basic mathematical expressions using AST.
56
+
57
+ Args:
58
+ expression: A string containing a mathematical expression like "2+2" or "10*5"
59
+
60
+ Returns:
61
+ The result of the calculation as a string
62
+ """
63
+ # Safe operations mapping
64
+ SAFE_OPS = {
65
+ ast.Add: op.add,
66
+ ast.Sub: op.sub,
67
+ ast.Mult: op.mul,
68
+ ast.Div: op.truediv,
69
+ ast.Pow: op.pow,
70
+ ast.USub: op.neg
71
+ }
72
+
73
+ def _safe_eval(node):
74
+ """Recursively evaluate AST nodes safely."""
75
+ if isinstance(node, ast.Num): # Numbers
76
+ return node.n
77
+ elif isinstance(node, ast.Constant): # Python 3.8+ constant nodes
78
+ return node.value
79
+ elif isinstance(node, ast.BinOp): # Binary operations
80
+ return SAFE_OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right))
81
+ elif isinstance(node, ast.UnaryOp): # Unary operations
82
+ return SAFE_OPS[type(node.op)](_safe_eval(node.operand))
83
+ else:
84
+ raise ValueError(f"Unsupported operation: {type(node)}")
85
+
86
+ try:
87
+ # Parse expression into AST and evaluate safely
88
+ node = ast.parse(expression, mode='eval')
89
+ result = _safe_eval(node.body)
90
+ return f"Result: {result}"
91
+ except Exception as e:
92
+ return f"Error calculating '{expression}': {str(e)}"
93
+
94
+
95
+ @tool
96
+ def save_note(content: str, filename: Optional[str] = None) -> str:
97
+ """
98
+ Save a note to a text file.
99
+
100
+ Args:
101
+ content: The content to save in the note
102
+ filename: Optional filename. If not provided, uses timestamp
103
+
104
+ Returns:
105
+ Success message with filename
106
+ """
107
+ try:
108
+ if filename is None:
109
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
110
+ filename = f"note_{timestamp}.txt"
111
+
112
+ # Try to create notes directory, fall back to /tmp if permission denied
113
+ try:
114
+ os.makedirs("notes", exist_ok=True)
115
+ notes_dir = "notes"
116
+ except PermissionError:
117
+ notes_dir = "/tmp/notes"
118
+ os.makedirs(notes_dir, exist_ok=True)
119
+
120
+ filepath = os.path.join(notes_dir, filename)
121
+
122
+ with open(filepath, 'w', encoding='utf-8') as f:
123
+ f.write(content)
124
+
125
+ return f"✅ Note saved successfully to: {filepath}"
126
+ except Exception as e:
127
+ return f"❌ Error saving note: {str(e)}"
128
+
129
+
130
+ @tool
131
+ def list_saved_notes() -> str:
132
+ """List all saved notes in the notes directory."""
133
+ try:
134
+ # Check both possible notes directories
135
+ all_files = []
136
+ for notes_dir in ["notes", "/tmp/notes"]:
137
+ if os.path.exists(notes_dir):
138
+ files = os.listdir(notes_dir)
139
+ txt_files = [f for f in files if f.endswith('.txt')]
140
+ all_files.extend([(f, notes_dir) for f in txt_files])
141
+
142
+ if not all_files:
143
+ return "📂 No notes found. Save a note first!"
144
+
145
+ file_list = "\n".join([f"- {file} ({location})" for file, location in sorted(all_files)])
146
+ return f"📂 Saved notes:\n{file_list}"
147
+ except Exception as e:
148
+ return f"❌ Error listing notes: {str(e)}"
149
+
150
+ class FinalAnswerTool(Tool):
151
+ name = "final_answer"
152
+ description = "Provides a final answer to the given problem."
153
+ inputs = {'answer': {'type': 'any', 'description': 'The final answer to the problem'}}
154
+ output_type = "any"
155
+
156
+ def forward(self, answer: Any) -> Any:
157
+ return answer
158
+
159
+ def __init__(self, *args, **kwargs):
160
+ self.is_initialized = False