derek commited on
Commit
e485756
·
1 Parent(s): 5e8dbb7

use google gemini

Browse files
Files changed (3) hide show
  1. app.py +168 -262
  2. my_agent.py +242 -0
  3. requirements.txt +6 -5
app.py CHANGED
@@ -1,272 +1,82 @@
1
  import os
2
  import gradio as gr
3
  import requests
4
- import inspect
5
  import pandas as pd
6
- from core_agent import GAIAAgent
7
-
8
- # Debug function to show available environment variables
9
- def debug_environment():
10
- """Print available environment variables related to API keys (with values hidden)"""
11
- debug_vars = [
12
- "HF_API_TOKEN", "HUGGINGFACEHUB_API_TOKEN",
13
- "OPENAI_API_KEY", "XAI_API_KEY",
14
- "AGENT_MODEL_TYPE", "AGENT_MODEL_ID",
15
- "AGENT_TEMPERATURE", "AGENT_VERBOSE"
16
- ]
17
-
18
- print("=== DEBUG: Environment Variables ===")
19
- for var in debug_vars:
20
- if os.environ.get(var):
21
- # Hide actual values for security
22
- print(f"{var}: [SET]")
23
- else:
24
- print(f"{var}: [NOT SET]")
25
- print("===================================")
26
 
27
  # (Keep Constants as is)
28
  # --- Constants ---
29
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
30
 
31
- # --- Basic Agent Definition ---
32
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
33
- class BasicAgent:
34
- def __init__(self):
35
- print("BasicAgent initialized.")
36
-
37
- # Call debug function to show available environment variables
38
- debug_environment()
39
-
40
- # Initialize the GAIAAgent with local execution
41
- try:
42
- # Load environment variables if dotenv is available
43
- try:
44
- import dotenv
45
- dotenv.load_dotenv()
46
- print("Loaded environment variables from .env file")
47
- except ImportError:
48
- print("python-dotenv not installed, continuing with environment as is")
49
-
50
- # Try to load API keys from environment
51
- # Check both HF_API_TOKEN and HUGGINGFACEHUB_API_TOKEN (HF Spaces uses HF_API_TOKEN)
52
- hf_token = os.environ.get("HF_API_TOKEN") or os.environ.get("HUGGINGFACEHUB_API_TOKEN")
53
- openai_key = os.environ.get("OPENAI_API_KEY")
54
- xai_key = os.environ.get("XAI_API_KEY")
55
-
56
- # If we have at least one API key, use a model-based approach
57
- if hf_token or openai_key or xai_key:
58
- # Default model parameters - read directly from environment
59
- model_type = os.environ.get("AGENT_MODEL_TYPE", "OpenAIServerModel")
60
- model_id = os.environ.get("AGENT_MODEL_ID", "gpt-4o")
61
- temperature = float(os.environ.get("AGENT_TEMPERATURE", "0.2"))
62
- verbose = os.environ.get("AGENT_VERBOSE", "false").lower() == "true"
63
-
64
- print(f"Agent config - Model Type: {model_type}, Model ID: {model_id}")
65
-
66
- try:
67
- if xai_key:
68
- # Use X.AI API with OpenAIServerModel
69
- api_base = os.environ.get("XAI_API_BASE", "https://api.x.ai/v1")
70
- self.gaia_agent = GAIAAgent(
71
- model_type="OpenAIServerModel",
72
- model_id="grok-3-latest", # X.AI's model
73
- api_key=xai_key,
74
- api_base=api_base,
75
- temperature=temperature,
76
- executor_type="local",
77
- verbose=verbose
78
- )
79
- print(f"Using OpenAIServerModel with X.AI API at {api_base}")
80
- elif model_type == "HfApiModel" and hf_token:
81
- # Use Hugging Face API
82
- self.gaia_agent = GAIAAgent(
83
- model_type="HfApiModel",
84
- model_id=model_id,
85
- api_key=hf_token,
86
- temperature=temperature,
87
- executor_type="local",
88
- verbose=verbose
89
- )
90
- print(f"Using HfApiModel with model_id: {model_id}")
91
- elif openai_key:
92
- # Default to OpenAI API
93
- api_base = os.environ.get("AGENT_API_BASE")
94
- kwargs = {
95
- "model_type": "OpenAIServerModel",
96
- "model_id": model_id,
97
- "api_key": openai_key,
98
- "temperature": temperature,
99
- "executor_type": "local",
100
- "verbose": verbose
101
- }
102
- if api_base:
103
- kwargs["api_base"] = api_base
104
- print(f"Using custom API base: {api_base}")
105
-
106
- self.gaia_agent = GAIAAgent(**kwargs)
107
- print(f"Using OpenAIServerModel with model_id: {model_id}")
108
- else:
109
- # Fallback to using whatever token we have
110
- print("WARNING: Using fallback initialization with available token")
111
- if hf_token:
112
- self.gaia_agent = GAIAAgent(
113
- model_type="HfApiModel",
114
- model_id="mistralai/Mistral-7B-Instruct-v0.2",
115
- api_key=hf_token,
116
- temperature=temperature,
117
- executor_type="local",
118
- verbose=verbose
119
- )
120
- elif openai_key:
121
- self.gaia_agent = GAIAAgent(
122
- model_type="OpenAIServerModel",
123
- model_id="gpt-3.5-turbo",
124
- api_key=openai_key,
125
- temperature=temperature,
126
- executor_type="local",
127
- verbose=verbose
128
- )
129
- else:
130
- self.gaia_agent = GAIAAgent(
131
- model_type="OpenAIServerModel",
132
- model_id="grok-3-latest",
133
- api_key=xai_key,
134
- api_base=os.environ.get("XAI_API_BASE", "https://api.x.ai/v1"),
135
- temperature=temperature,
136
- executor_type="local",
137
- verbose=verbose
138
- )
139
- except ImportError as ie:
140
- # Handle OpenAI module errors specifically
141
- if "openai" in str(ie).lower() and hf_token:
142
- print(f"OpenAI module error: {ie}. Falling back to HfApiModel.")
143
- self.gaia_agent = GAIAAgent(
144
- model_type="HfApiModel",
145
- model_id="mistralai/Mistral-7B-Instruct-v0.2",
146
- api_key=hf_token,
147
- temperature=temperature,
148
- executor_type="local",
149
- verbose=verbose
150
- )
151
- print(f"Using HfApiModel with model_id: mistralai/Mistral-7B-Instruct-v0.2 (fallback)")
152
- else:
153
- raise
154
- else:
155
- # No API keys available, log the error
156
- print("ERROR: No API keys found. Please set at least one of these environment variables:")
157
- print("- HUGGINGFACEHUB_API_TOKEN or HF_API_TOKEN")
158
- print("- OPENAI_API_KEY")
159
- print("- XAI_API_KEY")
160
- self.gaia_agent = None
161
- print("WARNING: No API keys found. Agent will not be able to answer questions.")
162
-
163
- except Exception as e:
164
- print(f"Error initializing GAIAAgent: {e}")
165
- self.gaia_agent = None
166
- print("WARNING: Failed to initialize agent. Falling back to basic responses.")
167
-
168
- def __call__(self, question: str) -> str:
169
- print(f"Agent received question (first 50 chars): {question[:50]}...")
170
-
171
- # Check if we have a functioning GAIA agent
172
- if self.gaia_agent:
173
- try:
174
- # Process the question using the GAIA agent
175
- answer = self.gaia_agent.answer_question(question)
176
- print(f"Agent generated answer: {answer[:50]}..." if len(answer) > 50 else f"Agent generated answer: {answer}")
177
- return answer
178
- except Exception as e:
179
- print(f"Error processing question: {e}")
180
- # Fall back to a simple response on error
181
- return "An error occurred while processing your question. Please check the agent logs for details."
182
- else:
183
- # We don't have a valid agent, provide a basic response
184
- return "The agent is not properly initialized. Please check your API keys and configuration."
185
-
186
- def run_and_submit_all( profile: gr.OAuthProfile | None):
187
- """
188
- Fetches all questions, runs the BasicAgent on them, submits all answers,
189
- and displays the results.
190
- """
191
- # --- Determine HF Space Runtime URL and Repo URL ---
192
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
193
 
194
- if profile:
195
- username= f"{profile.username}"
196
- print(f"User logged in: {username}")
197
- else:
198
- print("User not logged in.")
199
- return "Please Login to Hugging Face with the button.", None
200
-
201
- api_url = DEFAULT_API_URL
202
- questions_url = f"{api_url}/questions"
203
- submit_url = f"{api_url}/submit"
204
 
205
- # 1. Instantiate Agent ( modify this part to create your agent)
206
- try:
207
- agent = BasicAgent()
208
-
209
- # Check if agent is properly initialized
210
- if not agent.gaia_agent:
211
- print("ERROR: Agent was not properly initialized")
212
- return "ERROR: Agent was not properly initialized. Check the logs for details on missing API keys or configuration.", None
213
-
214
- except Exception as e:
215
- print(f"Error instantiating agent: {e}")
216
- return f"Error initializing agent: {e}", None
217
- # 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)
218
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
219
- print(agent_code)
220
-
221
- # 2. Fetch Questions
222
  print(f"Fetching questions from: {questions_url}")
223
  try:
224
  response = requests.get(questions_url, timeout=15)
225
  response.raise_for_status()
226
  questions_data = response.json()
227
  if not questions_data:
228
- print("Fetched questions list is empty.")
229
- return "Fetched questions list is empty or invalid format.", None
230
  print(f"Fetched {len(questions_data)} questions.")
231
  except requests.exceptions.RequestException as e:
232
  print(f"Error fetching questions: {e}")
233
- return f"Error fetching questions: {e}", None
234
  except requests.exceptions.JSONDecodeError as e:
235
  print(f"Error decoding JSON response from questions endpoint: {e}")
236
  print(f"Response text: {response.text[:500]}")
237
- return f"Error decoding server response for questions: {e}", None
238
  except Exception as e:
239
  print(f"An unexpected error occurred fetching questions: {e}")
240
- return f"An unexpected error occurred fetching questions: {e}", None
 
241
 
242
- # 3. Run your Agent
243
- results_log = []
244
- answers_payload = []
245
- print(f"Running agent on {len(questions_data)} questions...")
246
- for item in questions_data:
247
- task_id = item.get("task_id")
248
- question_text = item.get("question")
 
 
 
 
 
249
  if not task_id or question_text is None:
250
- print(f"Skipping item with missing task_id or question: {item}")
251
- continue
252
- try:
253
- submitted_answer = agent(question_text)
254
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
255
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
256
- except Exception as e:
257
- print(f"Error running agent on task {task_id}: {e}")
258
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
259
-
260
- if not answers_payload:
261
- print("Agent did not produce any answers to submit.")
262
- return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
263
-
264
- # 4. Prepare Submission
 
 
 
 
 
265
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
266
- status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
267
  print(status_update)
268
 
269
- # 5. Submit
270
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
271
  try:
272
  response = requests.post(submit_url, json=submission_data, timeout=60)
@@ -279,9 +89,8 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
279
  f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
280
  f"Message: {result_data.get('message', 'No message received.')}"
281
  )
282
- print("Submission successful.")
283
- results_df = pd.DataFrame(results_log)
284
- return final_status, results_df
285
  except requests.exceptions.HTTPError as e:
286
  error_detail = f"Server responded with status {e.response.status_code}."
287
  try:
@@ -291,23 +100,104 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
291
  error_detail += f" Response: {e.response.text[:500]}"
292
  status_message = f"Submission Failed: {error_detail}"
293
  print(status_message)
294
- results_df = pd.DataFrame(results_log)
295
- return status_message, results_df
296
  except requests.exceptions.Timeout:
297
  status_message = "Submission Failed: The request timed out."
298
  print(status_message)
299
- results_df = pd.DataFrame(results_log)
300
- return status_message, results_df
301
  except requests.exceptions.RequestException as e:
302
  status_message = f"Submission Failed: Network error - {e}"
303
  print(status_message)
304
- results_df = pd.DataFrame(results_log)
305
- return status_message, results_df
306
  except Exception as e:
307
  status_message = f"An unexpected error occurred during submission: {e}"
308
  print(status_message)
309
- results_df = pd.DataFrame(results_log)
310
- return status_message, results_df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
 
313
  # --- Build Gradio Interface using Blocks ---
@@ -317,30 +207,45 @@ with gr.Blocks() as demo:
317
  """
318
  **Instructions:**
319
 
320
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
321
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
322
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
323
-
324
- ---
325
- **Disclaimers:**
326
- 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).
327
- 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.
328
- """
329
  )
330
 
331
  gr.LoginButton()
332
 
333
- run_button = gr.Button("Run Evaluation & Submit All Answers")
 
 
 
 
 
 
 
 
 
 
334
 
335
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
336
- # Removed max_rows=10 from DataFrame constructor
337
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
338
 
339
  run_button.click(
340
- fn=run_and_submit_all,
 
341
  outputs=[status_output, results_table]
342
  )
343
 
 
 
 
 
 
 
 
 
 
 
344
  if __name__ == "__main__":
345
  print("\n" + "-"*30 + " App Starting " + "-"*30)
346
  # Check for SPACE_HOST and SPACE_ID at startup for information
@@ -359,6 +264,7 @@ if __name__ == "__main__":
359
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
360
  else:
361
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
 
362
 
363
  print("-"*(60 + len(" App Starting ")) + "\n")
364
 
 
1
  import os
2
  import gradio as gr
3
  import requests
 
4
  import pandas as pd
5
+ from my_agent import GeminiAgentContainer
6
+ from markdownify import markdownify as to_markdown
7
+ import time
8
+ import json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  # (Keep Constants as is)
11
  # --- Constants ---
12
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ # --- Global Variables ---
16
+ questions = None
17
+ results_log = []
18
+ answers_by_task = {}
 
 
 
 
 
 
19
 
20
+ def load_questions(questions_url):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  print(f"Fetching questions from: {questions_url}")
22
  try:
23
  response = requests.get(questions_url, timeout=15)
24
  response.raise_for_status()
25
  questions_data = response.json()
26
  if not questions_data:
27
+ print("Fetched questions list is empty or invalid.")
28
+ return None
29
  print(f"Fetched {len(questions_data)} questions.")
30
  except requests.exceptions.RequestException as e:
31
  print(f"Error fetching questions: {e}")
32
+ return None
33
  except requests.exceptions.JSONDecodeError as e:
34
  print(f"Error decoding JSON response from questions endpoint: {e}")
35
  print(f"Response text: {response.text[:500]}")
36
+ return None
37
  except Exception as e:
38
  print(f"An unexpected error occurred fetching questions: {e}")
39
+ return None
40
+ return questions_data
41
 
42
+ def answer_one(agent, question_data):
43
+ """
44
+ Runs the agent on a single question and returns the result.
45
+ """
46
+ task_id = question_data.get("task_id")
47
+ question_text = question_data.get("question")
48
+ filename = question_data.get("file_name")
49
+ payload = None
50
+ submitted_answer = None
51
+ agent_error = None
52
+
53
+ try:
54
  if not task_id or question_text is None:
55
+ raise ValueError(f"Missing task_id or question in item: {question_data}")
56
+ if filename:
57
+ file_prompt = f"\nThere is an attached file with task id `{task_id}` available.\n"
58
+ question_text = file_prompt + question_text
59
+ submitted_answer = agent(question_text)
60
+ payload = {"task_id": task_id, "submitted_answer": submitted_answer}
61
+ except Exception as e:
62
+ print(agent)
63
+ print(f"Error running agent on task {task_id}: {e}")
64
+ agent_error = f"AGENT ERROR: {e}"
65
+ finally:
66
+ log_entry = {
67
+ "Task ID": task_id,
68
+ "Question": question_text,
69
+ "Submitted Answer": submitted_answer or agent_error,
70
+ }
71
+ return payload, log_entry
72
+
73
+ def _submit_all(username, agent_code, answers_payload, submit_url):
74
+ # Prepare Submission
75
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
76
+ status_update = f"Submitting {len(answers_payload)} answers for user '{username}'..."
77
  print(status_update)
78
 
79
+ # Submit Answers
80
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
81
  try:
82
  response = requests.post(submit_url, json=submission_data, timeout=60)
 
89
  f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
90
  f"Message: {result_data.get('message', 'No message received.')}"
91
  )
92
+ print(final_status)
93
+ return final_status
 
94
  except requests.exceptions.HTTPError as e:
95
  error_detail = f"Server responded with status {e.response.status_code}."
96
  try:
 
100
  error_detail += f" Response: {e.response.text[:500]}"
101
  status_message = f"Submission Failed: {error_detail}"
102
  print(status_message)
103
+ return status_message
 
104
  except requests.exceptions.Timeout:
105
  status_message = "Submission Failed: The request timed out."
106
  print(status_message)
107
+ return status_message
 
108
  except requests.exceptions.RequestException as e:
109
  status_message = f"Submission Failed: Network error - {e}"
110
  print(status_message)
111
+ return status_message
 
112
  except Exception as e:
113
  status_message = f"An unexpected error occurred during submission: {e}"
114
  print(status_message)
115
+ return status_message
116
+
117
+
118
+ def prepare_agent(api_key=None):
119
+ # 1. Instantiate Agent ( modify this part to create your agent)
120
+ try:
121
+ agent = GeminiAgentContainer(api_key=api_key)
122
+ print(agent.system_prompt)
123
+ except Exception as e:
124
+ print(f"Error instantiating agent: {e}")
125
+ return None
126
+
127
+ return agent
128
+
129
+ def save_answers_to_file():
130
+ """
131
+ Submits the answers to a local file named with the current epoch time.
132
+ """
133
+ if not answers_by_task:
134
+ return ("Nothing to save, no answers found.")
135
+ answers_payload = list(answers_by_task.values())
136
+
137
+ file_path = f"answers-{int(time.time())}.json"
138
+ print(f"Saving answers to file: {file_path}")
139
+ try:
140
+ with open(file_path, "w") as file:
141
+ json.dump(answers_payload, file, indent=4)
142
+ submit_status = (f"Answers successfully written to {file_path}")
143
+ except Exception as e:
144
+ submit_status = (f"Error writing answers to file: {e}")
145
+ print(submit_status)
146
+ return submit_status
147
+
148
+
149
+ def run_all(api_key: str | None = None):
150
+ """
151
+ Fetches all questions, runs the BasicAgent on them,
152
+ """
153
+
154
+ questions_url = f"{DEFAULT_API_URL}/questions"
155
+
156
+ agent = prepare_agent(api_key)
157
+
158
+ questions_data = load_questions(questions_url)
159
+ # 3. Run your Agent
160
+
161
+ print(f"Running agent on {len(questions_data)} questions...")
162
+ for item in questions_data:
163
+ payload_data, log_entry = answer_one(agent, item)
164
+ if payload_data:
165
+ task_id = payload_data.get("task_id")
166
+ answers_by_task[task_id] = payload_data
167
+ results_log.append(log_entry)
168
+ time.sleep(1)
169
+ if not answers_by_task:
170
+ final_status = "Agent did not produce any answers to submit."
171
+ else:
172
+ final_status = f"Agent finished, {len(answers_by_task)} answers produced."
173
+ print(final_status)
174
+ return final_status, pd.DataFrame(results_log)
175
+
176
+ def submit_all( profile: gr.OAuthProfile | None):
177
+ """
178
+ Submits all answers and displays the results.
179
+ """
180
+ submit_url = f"{DEFAULT_API_URL}/submit"
181
+
182
+ if profile:
183
+ username= f"{profile.username}"
184
+ print(f"User logged in: {username}")
185
+ else:
186
+ print("User not logged in.")
187
+ return "Please Login to Hugging Face with the button."
188
+ # --- Determine HF Space Runtime URL and Repo URL ---
189
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
190
+
191
+ if not answers_by_task:
192
+ submit_status = "No answers to submit."
193
+ else:
194
+ # 4. Submit all answers
195
+ # 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)
196
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
197
+
198
+ submit_status = _submit_all(username, agent_code, list(answers_by_task.values()), submit_url)
199
+
200
+ return submit_status
201
 
202
 
203
  # --- Build Gradio Interface using Blocks ---
 
207
  """
208
  **Instructions:**
209
 
210
+ 1. Please use your own Gemini API key to run the agent. You can find your API key in your [Gemini account settings](https://gemini.com/account/settings).
211
+ 2. Click 'Run Evaluation' to fetch questions, run the agent, and see the answers.
212
+ 3. Click 'Submit All Answers' to submit the answers to the server.
213
+ """
 
 
 
 
 
214
  )
215
 
216
  gr.LoginButton()
217
 
218
+ api_key_input = gr.Textbox(
219
+ label="Gemini API Key",
220
+ placeholder="Enter your Gemini API key here",
221
+ type="password",
222
+ lines=1,
223
+ visible=True
224
+ )
225
+
226
+ run_button = gr.Button("Run Evaluation")
227
+ save_button = gr.Button("Save Answers to File")
228
+ submit_button = gr.Button("Submit All Answers")
229
 
230
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
231
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
232
 
233
  run_button.click(
234
+ fn=run_all,
235
+ inputs=[api_key_input],
236
  outputs=[status_output, results_table]
237
  )
238
 
239
+ save_button.click(
240
+ fn=save_answers_to_file,
241
+ outputs=[status_output]
242
+ )
243
+
244
+ submit_button.click(
245
+ fn=submit_all,
246
+ outputs=[status_output]
247
+ )
248
+
249
  if __name__ == "__main__":
250
  print("\n" + "-"*30 + " App Starting " + "-"*30)
251
  # Check for SPACE_HOST and SPACE_ID at startup for information
 
264
  print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
265
  else:
266
  print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
267
+ print(f"API KEY: {os.getenv('GOOGLE_API_KEY')}")
268
 
269
  print("-"*(60 + len(" App Starting ")) + "\n")
270
 
my_agent.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ from smolagents import LiteLLMModel, ToolCallingAgent, Tool
4
+ from typing import Optional
5
+ from google import genai
6
+ from google.genai import types
7
+ import wikipedia as wiki
8
+ from markdownify import markdownify as to_markdown
9
+
10
+ # --- Tools ---
11
+
12
+ class VideoWatchingTool(Tool):
13
+ name = "watch_video"
14
+ description ="""
15
+ A tool for watching videos and answering questions about them.
16
+ """
17
+ inputs = {
18
+ "video_url": {
19
+ "type": "string",
20
+ "description": "The URL of the video to watch."
21
+ },
22
+ "user_query": {
23
+ "type": "string",
24
+ "description": "The question to answer about the video."
25
+ }
26
+ }
27
+ output_type = "string"
28
+
29
+ def __init__(self, model_name, *args, **kwargs):
30
+ super().__init__(*args, **kwargs)
31
+ self.model_name = model_name
32
+
33
+ def forward(self, video_url: str, user_query: str) -> str:
34
+ request_json = {
35
+ 'model': f'models/{self.model_name}',
36
+ 'contents': [{
37
+ "parts": [
38
+ {
39
+ 'fileData': {
40
+ 'fileUri': video_url
41
+ }
42
+ },
43
+ {
44
+ 'text': f"Please watch the video and answer the following question: {user_query}"
45
+ }
46
+ ]
47
+ }]
48
+ }
49
+ api_url = f'https://generativelanguage.googleapis.com/v1beta/models/{self.model_name}:generateContent?key={os.getenv("GOOGLE_API_KEY")}'
50
+ response = requests.post(
51
+ api_url,
52
+ json=request_json,
53
+ headers={
54
+ 'Content-Type': 'application/json',
55
+ }
56
+ )
57
+ if response.status_code != 200:
58
+ return f"Error: {response.status_code} - {response.text}"
59
+ response_json = response.json()
60
+ result_parts = response_json['candidates'][0]['content']['parts']
61
+ result = "".join([_.get('text', '') for _ in result_parts])
62
+ return result
63
+
64
+ class GoogleSearchTool(Tool):
65
+ name = "google_search"
66
+ description = """
67
+ Performs a Google search and returns the results.
68
+ """
69
+ inputs = {
70
+ "query": {
71
+ "type": "string",
72
+ "description": "The search query."
73
+ }
74
+ }
75
+ output_type = "string"
76
+
77
+ def __init__(self, client, model_name, *args, **kwargs):
78
+ super().__init__(*args, **kwargs)
79
+ self.client = client
80
+ self.model_name = model_name
81
+
82
+ def forward(self, query: str) -> str:
83
+ google_search_tool = types.Tool(
84
+ google_search=types.GoogleSearch()
85
+ )
86
+ response = self.client.models.generate_content(
87
+ model=self.model_name,
88
+ contents=f"Please search the internet for: {query}",
89
+ config=types.GenerateContentConfig(
90
+ tools=[google_search_tool],
91
+ response_modalities=['TEXT'],
92
+ )
93
+ )
94
+ return response.text
95
+
96
+ class WikipediaTitleSearchTool(Tool):
97
+ name = "check_wikipedia_page_titles"
98
+ description = """
99
+ Searches for Wikipedia pages related to the query and returns the canonical titles of the related pages.
100
+ """
101
+ inputs = {
102
+ "query": {
103
+ "type": "string",
104
+ "description": "The search query."
105
+ }
106
+ }
107
+ output_type = "string"
108
+
109
+ def forward(self, query: str) -> str:
110
+ response = wiki.search(query)
111
+ if len(response) > 0:
112
+ result = ", ".join(response)
113
+ else:
114
+ result = "No results found."
115
+ return result
116
+
117
+ class WikipediaPageTool(Tool):
118
+ name = "get_wikipedia_page"
119
+ description = """
120
+ Gets the content of a Wikipedia page.
121
+ """
122
+ inputs = {
123
+ "page_title": {
124
+ "type": "string",
125
+ "description": "The canonical title of the Wikipedia page."
126
+ }
127
+ }
128
+ output_type = "string"
129
+
130
+ def forward(self, page_title: str) -> str:
131
+ # TODO: may need to do caching of the HTML ourselves?
132
+ try:
133
+ page = wiki.page(page_title)
134
+ except wiki.exceptions.PageError:
135
+ return f"Page '{page_title}' not found."
136
+ md_content = to_markdown(page.html())
137
+ return md_content
138
+
139
+ class FileAttachmentQueryTool(Tool):
140
+ name = "run_query_with_file"
141
+ description = """
142
+ Downloads a file mentioned in a user prompt, adds it to the context, and runs a query on it.
143
+ This assumes the file is 20MB or less.
144
+ """
145
+ inputs = {
146
+ "task_id": {
147
+ "type": "string",
148
+ "description": "A unique identifier for the task related to this file, used to download it."
149
+ },
150
+ "mime_type": {
151
+ "type": "string",
152
+ "nullable": True,
153
+ "description": "The MIME type of the file, or the best guess if unknown."
154
+ },
155
+ "user_query": {
156
+ "type": "string",
157
+ "description": "The question to answer about the file."
158
+ }
159
+ }
160
+ output_type = "string"
161
+ def __init__(self, client, model_name, *args, **kwargs):
162
+ super().__init__(*args, **kwargs)
163
+ self.client = client
164
+ self.model_name = model_name
165
+
166
+ def forward(self, task_id: str, mime_type: str | None, user_query: str) -> str:
167
+ # Download the file
168
+ file_url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
169
+ file_response = requests.get(file_url)
170
+ if file_response.status_code != 200:
171
+ raise Exception(f"Failed to download file: {file_response.status_code} - {file_response.text}")
172
+ file_data = file_response.content
173
+ mime_type = mime_type or file_response.headers.get('Content-Type', 'application/octet-stream')
174
+ response = self.client.models.generate_content(
175
+ model=self.model_name,
176
+ contents=[
177
+ types.Part.from_bytes(
178
+ data=file_data,
179
+ mime_type=mime_type,
180
+ ),
181
+ user_query,
182
+ ]
183
+ )
184
+
185
+ return response.text
186
+
187
+ # --- Agent Management ---
188
+
189
+ class GeminiAgentContainer:
190
+ """
191
+ A container for the Gemini agent.
192
+ """
193
+ # TODO: make it easier to chnge the model
194
+ MODEL_NAME = "gemini-2.0-flash"
195
+
196
+ def __init__(self, api_key: Optional[str] = None):
197
+ api_key = api_key or os.getenv("GOOGLE_API_KEY")
198
+ self.model = LiteLLMModel(model_id=f"gemini/{self.MODEL_NAME}", api_key=api_key)
199
+ self.client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY"))
200
+ system_prompt = """
201
+ You are a general AI assistant. I will ask you a question.
202
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
203
+ If your answer is a number and you are not explicitly asked for a string, write it in numerals instead of words, and don't use comma to write your number nor use units such as $ or percent sign unless specified otherwise.
204
+ 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.
205
+ 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.
206
+
207
+ Answer questions as literally as you can, making as few assumptions as possible. Restrict the answer to the narrowest definition that still satifies the question.
208
+ If you are provied with a video, please watch and summarize the entire video before answering the question. The correct answer may be present only in a few frames of the video.
209
+ If you have difficulty finding an answer on Wikipedia, you may search the internet using Google Search.
210
+ If you are asked to prove something, first state your assumptions and think step by step before giving your final answer.
211
+
212
+ """
213
+
214
+ self.agent = ToolCallingAgent(
215
+ model=self.model,
216
+ tools = [
217
+ VideoWatchingTool(model_name=self.MODEL_NAME),
218
+ GoogleSearchTool(client=self.client, model_name=self.MODEL_NAME),
219
+ WikipediaTitleSearchTool(),
220
+ WikipediaPageTool(),
221
+ FileAttachmentQueryTool(client=self.client, model_name=self.MODEL_NAME),
222
+ ],
223
+ max_steps=14,
224
+ planning_interval=2,
225
+ )
226
+ self.system_prompt = system_prompt
227
+
228
+
229
+ def __call__(self, question: str) -> str:
230
+ response = self.agent.run(f"{self.system_prompt}\n{question}")
231
+ return response
232
+
233
+ if __name__ == "__main__":
234
+ agent_container = GeminiAgentContainer()
235
+ agent = agent_container.agent
236
+ #my_query = "How many studio albums were published by Mercedes Sosa between 2000 and 2009 (included)? You can use the latest 2022 version of english wikipedia."
237
+ #my_query = "In the video https://www.youtube.com/watch?v=L1vXCYZAYYM, what is the highest number of bird species to be on camera simultaneously?"
238
+ my_query= "Examine the video at https://www.youtube.com/watch?v=1htKBjuUWec.\n\nWhat does Teal'c say in response to the question \"Isn't that hot?\""
239
+ response = agent.run(agent_container.system_prompt+my_query, max_steps=5)
240
+ print(response)
241
+ #print(my_query)
242
+
requirements.txt CHANGED
@@ -1,8 +1,9 @@
1
  gradio
2
  requests
3
- smolagents[openai]
4
- python-dotenv
5
- pandas
6
- numpy
7
- openai
 
8
 
 
1
  gradio
2
  requests
3
+ smolagents[litellm]
4
+ gradio[oauth]
5
+ google-api-python-client
6
+ google-genai
7
+ wikipedia
8
+ markdownify
9