Daniil Bogdanov commited on
Commit
c531eac
·
1 Parent(s): 81917a3

Release v1

Browse files
Files changed (6) hide show
  1. agent.py +68 -0
  2. app.py +77 -30
  3. model.py +53 -0
  4. requirements.txt +11 -1
  5. tools.py +35 -0
  6. utils/logger.py +20 -0
agent.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Optional
2
+
3
+ from smolagents import CodeAgent
4
+
5
+ from utils.logger import get_logger
6
+
7
+ logger = get_logger(__name__)
8
+
9
+
10
+ class Agent:
11
+ def __init__(
12
+ self, model: Any, tools: Optional[list] = None, prompt: Optional[str] = None
13
+ ):
14
+ logger.info("Initializing Agent")
15
+
16
+ self.model = model
17
+
18
+ self.tools = tools
19
+
20
+ self.imports = [
21
+ "pandas",
22
+ "numpy",
23
+ "os",
24
+ "requests",
25
+ "tempfile",
26
+ "datetime",
27
+ "json",
28
+ "time",
29
+ "re",
30
+ "openpyxl",
31
+ ]
32
+
33
+ self.agent = CodeAgent(
34
+ model=self.model,
35
+ tools=self.tools,
36
+ add_base_tools=True,
37
+ additional_authorized_imports=self.imports,
38
+ )
39
+
40
+ self.prompt = prompt or (
41
+ """
42
+ You are an advanced AI assistant specialized in solving complex, real-world tasks that require multi-step reasoning, factual accuracy, and use of external tools.
43
+
44
+ Follow these principles:
45
+ - Be precise and concise. The final answer must strictly match the required format with no extra commentary.
46
+ - Use tools intelligently. If a question involves external information, structured data, images, or audio, call the appropriate tool to retrieve or process it.
47
+ - Reason step-by-step. Think through the solution logically and plan your actions carefully before answering.
48
+ - Validate information. Always verify facts when possible instead of guessing.
49
+ - Use code if needed. For calculations, parsing, or transformations, generate Python code and execute it.
50
+
51
+ IMPORTANT: When giving the final answer, output only the direct required result without any extra text like "Final Answer:" or explanations. YOU MUST RESPOND IN THE EXACT FORMAT AS THE QUESTION.
52
+
53
+ QUESTION: {question}
54
+
55
+ CONTEXT: {context}
56
+
57
+ ANSWER:
58
+ """
59
+ )
60
+
61
+ logger.info("Agent initialized")
62
+
63
+ def __call__(self, question: str, file_path: Optional[str] = None) -> str:
64
+ answer = self.agent.run(
65
+ self.prompt.format(question=question, context=file_path)
66
+ )
67
+ answer = str(answer).strip("'").strip('"').strip()
68
+ return answer
app.py CHANGED
@@ -1,34 +1,43 @@
 
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.")
@@ -37,10 +46,13 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
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
@@ -55,16 +67,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,19 +92,51 @@ 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)
98
 
@@ -162,20 +206,19 @@ with gr.Blocks() as demo:
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
- 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}")
@@ -183,14 +226,18 @@ 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 inspect
2
  import os
3
+ import tempfile
4
+
5
  import gradio as gr
 
 
6
  import pandas as pd
7
+ import requests
8
+
9
+ from agent import Agent
10
+ from model import get_model
11
+ from tools import get_tools
12
 
13
  # (Keep Constants as is)
14
  # --- Constants ---
15
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
16
 
17
+
18
  # --- Basic Agent Definition ---
19
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
20
  class BasicAgent:
21
  def __init__(self):
22
  print("BasicAgent initialized.")
23
+
24
  def __call__(self, question: str) -> str:
25
  print(f"Agent received question (first 50 chars): {question[:50]}...")
26
  fixed_answer = "This is a default answer."
27
  print(f"Agent returning fixed answer: {fixed_answer}")
28
  return fixed_answer
29
 
30
+
31
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
32
  """
33
  Fetches all questions, runs the BasicAgent on them, submits all answers,
34
  and displays the results.
35
  """
36
  # --- Determine HF Space Runtime URL and Repo URL ---
37
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
38
 
39
  if profile:
40
+ username = f"{profile.username}"
41
  print(f"User logged in: {username}")
42
  else:
43
  print("User not logged in.")
 
46
  api_url = DEFAULT_API_URL
47
  questions_url = f"{api_url}/questions"
48
  submit_url = f"{api_url}/submit"
49
+ files_url = f"{api_url}/files"
50
 
51
  # 1. Instantiate Agent ( modify this part to create your agent)
52
  try:
53
+ agent = Agent(
54
+ model=get_model("OpenAIServerModel", "gpt-4.1-mini"), tools=get_tools()
55
+ )
56
  except Exception as e:
57
  print(f"Error instantiating agent: {e}")
58
  return f"Error initializing agent: {e}", None
 
67
  response.raise_for_status()
68
  questions_data = response.json()
69
  if not questions_data:
70
+ print("Fetched questions list is empty.")
71
+ return "Fetched questions list is empty or invalid format.", None
72
  print(f"Fetched {len(questions_data)} questions.")
73
  except requests.exceptions.RequestException as e:
74
  print(f"Error fetching questions: {e}")
75
  return f"Error fetching questions: {e}", None
76
  except requests.exceptions.JSONDecodeError as e:
77
+ print(f"Error decoding JSON response from questions endpoint: {e}")
78
+ print(f"Response text: {response.text[:500]}")
79
+ return f"Error decoding server response for questions: {e}", None
80
  except Exception as e:
81
  print(f"An unexpected error occurred fetching questions: {e}")
82
  return f"An unexpected error occurred fetching questions: {e}", None
 
92
  print(f"Skipping item with missing task_id or question: {item}")
93
  continue
94
  try:
95
+ file_path = None
96
+ try:
97
+ file_response = requests.get(f"{files_url}/{task_id}", timeout=15)
98
+ if file_response.status_code == 200 and file_response.content:
99
+ with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
100
+ tmp_file.write(file_response.content)
101
+ file_path = tmp_file.name
102
+ print(f"Downloaded file for task {task_id} to {file_path}")
103
+ else:
104
+ print(f"No file for task {task_id} or file is empty.")
105
+ except Exception as e:
106
+ print(f"Error downloading file for task {task_id}: {e}")
107
+ file_path = None
108
+
109
+ submitted_answer = agent(question_text, file_path)
110
+ answers_payload.append(
111
+ {"task_id": task_id, "submitted_answer": submitted_answer}
112
+ )
113
+ results_log.append(
114
+ {
115
+ "Task ID": task_id,
116
+ "Question": question_text,
117
+ "Submitted Answer": submitted_answer,
118
+ }
119
+ )
120
  except Exception as e:
121
+ print(f"Error running agent on task {task_id}: {e}")
122
+ results_log.append(
123
+ {
124
+ "Task ID": task_id,
125
+ "Question": question_text,
126
+ "Submitted Answer": f"AGENT ERROR: {e}",
127
+ }
128
+ )
129
 
130
  if not answers_payload:
131
  print("Agent did not produce any answers to submit.")
132
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
133
 
134
+ # 4. Prepare Submission
135
+ submission_data = {
136
+ "username": username.strip(),
137
+ "agent_code": agent_code,
138
+ "answers": answers_payload,
139
+ }
140
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
141
  print(status_update)
142
 
 
206
 
207
  run_button = gr.Button("Run Evaluation & Submit All Answers")
208
 
209
+ status_output = gr.Textbox(
210
+ label="Run Status / Submission Result", lines=5, interactive=False
211
+ )
212
  # Removed max_rows=10 from DataFrame constructor
213
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
214
 
215
+ run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
 
 
 
216
 
217
  if __name__ == "__main__":
218
+ print("\n" + "-" * 30 + " App Starting " + "-" * 30)
219
  # Check for SPACE_HOST and SPACE_ID at startup for information
220
  space_host_startup = os.getenv("SPACE_HOST")
221
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
222
 
223
  if space_host_startup:
224
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
226
  else:
227
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
228
 
229
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
230
  print(f"✅ SPACE_ID found: {space_id_startup}")
231
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
232
+ print(
233
+ f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main"
234
+ )
235
  else:
236
+ print(
237
+ "ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined."
238
+ )
239
 
240
+ print("-" * (60 + len(" App Starting ")) + "\n")
241
 
242
  print("Launching Gradio Interface for Basic Agent Evaluation...")
243
+ demo.launch(debug=True, share=False)
model.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Any
3
+
4
+ from smolagents import HfApiModel, InferenceClientModel, LiteLLMModel, OpenAIServerModel
5
+
6
+
7
+ def get_huggingface_api_model(model_id: str, **kwargs) -> Any:
8
+ api_key = os.getenv("HUGGINGFACEHUB_API_TOKEN")
9
+ if not api_key:
10
+ raise ValueError("HUGGINGFACEHUB_API_TOKEN is not set")
11
+
12
+ return HfApiModel(model_id=model_id, token=api_key, **kwargs)
13
+
14
+
15
+ def get_inference_client_model(model_id: str, **kwargs) -> Any:
16
+ api_key = os.getenv("HUGGINGFACEHUB_API_TOKEN")
17
+ if not api_key:
18
+ raise ValueError("HUGGINGFACEHUB_API_TOKEN is not set")
19
+
20
+ return InferenceClientModel(model_id=model_id, token=api_key, **kwargs)
21
+
22
+
23
+ def get_openai_server_model(model_id: str, **kwargs) -> Any:
24
+ api_key = os.getenv("OPENAI_API_KEY")
25
+ if not api_key:
26
+ raise ValueError("OPENAI_API_KEY is not set")
27
+
28
+ api_base = os.getenv("OPENAI_API_BASE")
29
+ if not api_base:
30
+ raise ValueError("OPENAI_API_BASE is not set")
31
+
32
+ return OpenAIServerModel(
33
+ model_id=model_id, api_key=api_key, api_base=api_base, **kwargs
34
+ )
35
+
36
+
37
+ def get_lite_llm_model(model_id: str, **kwargs) -> Any:
38
+ return LiteLLMModel(model_id=model_id, **kwargs)
39
+
40
+
41
+ def get_model(model_type: str, model_id: str, **kwargs) -> Any:
42
+
43
+ models = {
44
+ "HfApiModel": get_huggingface_api_model,
45
+ "InferenceClientModel": get_inference_client_model,
46
+ "OpenAIServerModel": get_openai_server_model,
47
+ "LiteLLMModel": get_lite_llm_model,
48
+ }
49
+
50
+ if model_type not in models:
51
+ raise ValueError(f"Unknown model type: {model_type}")
52
+
53
+ return models[model_type](model_id, **kwargs)
requirements.txt CHANGED
@@ -1,2 +1,12 @@
1
  gradio
2
- requests
 
 
 
 
 
 
 
 
 
 
 
1
  gradio
2
+ numpy
3
+ openpyxl
4
+ pandas
5
+ requests
6
+ smolagents
7
+ smolagents[audio]
8
+ smolagents[openai]
9
+ smolagents[transformers]
10
+ transformers
11
+ wikipedia-api
12
+ youtube-transcript-api
tools.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import (
2
+ DuckDuckGoSearchTool,
3
+ PythonInterpreterTool,
4
+ SpeechToTextTool,
5
+ Tool,
6
+ VisitWebpageTool,
7
+ WikipediaSearchTool,
8
+ )
9
+ from youtube_transcript_api import YouTubeTranscriptApi
10
+
11
+
12
+ class YouTubeTranscriptionTool(Tool):
13
+ name = "youtube_transcription"
14
+ description = "Fetches the transcript of a YouTube video given its URL"
15
+ inputs = {
16
+ "video_url": {"type": "string", "description": "YouTube video URL"},
17
+ }
18
+ output_type = "string"
19
+
20
+ def forward(self, video_url: str) -> str:
21
+ video_id = video_url.strip().split("v=")[-1]
22
+ transcript = YouTubeTranscriptApi.get_transcript(video_id)
23
+ return " ".join([entry["text"] for entry in transcript])
24
+
25
+
26
+ def get_tools():
27
+ tools = [
28
+ DuckDuckGoSearchTool(),
29
+ PythonInterpreterTool(),
30
+ WikipediaSearchTool(),
31
+ VisitWebpageTool(),
32
+ SpeechToTextTool(),
33
+ YouTubeTranscriptionTool(),
34
+ ]
35
+ return tools
utils/logger.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+
3
+
4
+ def get_logger(name: str = __name__) -> logging.Logger:
5
+ """
6
+ Create and configure a logger.
7
+
8
+ Args:
9
+ name (str, optional): Name of the logger. Defaults to the module name.
10
+
11
+ Returns:
12
+ logging.Logger: Configured logger instance.
13
+ """
14
+ logging.basicConfig(
15
+ format="%(asctime)s:%(module)s:%(funcName)s:%(levelname)s: %(message)s",
16
+ datefmt="%Y-%m-%d %H:%M:%S",
17
+ )
18
+ logger = logging.getLogger(name)
19
+ logger.setLevel(logging.INFO)
20
+ return logger