Carolzinha2010 commited on
Commit
1508ff2
·
verified ·
1 Parent(s): bf4c445

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -455
app.py CHANGED
@@ -5,59 +5,20 @@ import gradio as gr
5
  import requests
6
  import inspect
7
  import pandas as pd
8
- import cv2 # Import opencv-python for video processing
9
- import speech_recognition as sr # Import SpeechRecognition for audio processing
10
- from pydub import AudioSegment # Import pydub for audio manipulation
11
- import tempfile # Import tempfile for temporary file handling
12
- import numpy as np # Import numpy for image processing
13
 
14
-
15
- # Import libraries for SerpAPI
16
  from serpapi import GoogleSearch
17
- import google.generativeai as genai # Keep the import as the user might add LLM functionality back later
18
-
19
-
20
- # --- Get API Keys from Environment Variables ---
21
- # SERPAPI_API_KEY and GOOGLE_API_KEY should be set as secrets in your Hugging Face Space
22
- SERPAPI_API_KEY = os.getenv('SERPAPI_API_KEY')
23
- print(f"SERPAPI_API_KEY (first 5 chars): {SERPAPI_API_KEY[:5] if SERPAPI_API_KEY else 'None'}...") # Debugging API key
24
-
25
- # Keep GOOGLE_API_KEY handling as the user might add LLM functionality back later
26
- GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')
27
- print(f"GOOGLE_API_KEY (first 5 chars): {GOOGLE_API_KEY[:5] if GOOGLE_API_KEY else 'None'}...") # Debugging API key
28
-
29
- # --- Define the default API URL ---
30
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" # Updated API URL
31
-
32
-
33
- # --- Google Generative AI LLM Initialization ---
34
- # Keep LLM initialization but handle potential errors and None state
35
- print("Attempting to initialize Google Generative AI model...") # Debugging print before loading
36
-
37
- gemini_model = None # Initialize to None
38
-
39
- if not GOOGLE_API_KEY:
40
- print("Warning: GOOGLE_API_KEY environment variable not set. LLM will not be available.")
41
- else:
42
- try:
43
- # Configure the generative AI library
44
- genai.configure(api_key=GOOGLE_API_KEY)
45
- print("Google Generative AI configured.")
46
-
47
- # Initialize the Generative Model
48
- # Using a fast and efficient model like gemini-1.5-flash
49
- # You can explore other models like 'gemini-1.5-pro' for potentially better results
50
- gemini_model = genai.GenerativeModel('gemini-1.5-flash')
51
- print("Gemini model initialized successfully.") # Debugging print after successful init
52
-
53
- except Exception as e:
54
- print(f"An error occurred during Google Generative AI initialization: {e}")
55
- gemini_model = None # Ensure model is None if initialization fails
56
 
 
 
 
57
 
58
  # --- Web Search Function (using SerpAPI) ---
59
  def web_search(query: str) -> list[dict]:
60
- # Removed global gemini_model declaration as it's not used here
61
  """
62
  Performs a web search using SerpAPI and returns relevant information.
63
 
@@ -65,13 +26,12 @@ def web_search(query: str) -> list[dict]:
65
  query: The search query string.
66
 
67
  Returns:
68
- A list of dictionaries, where each dictionary represents a search result
69
- with keys 'title', 'snippet', and 'url'. Returns an empty list if no
70
- results are found or an error occurs.
71
- """
72
- print(f"web_search called with query: {query[:50]}...") # Debugging web_search call
73
  if not SERPAPI_API_KEY:
74
- print("SerpAPI key not found in environment variables.")
75
  return []
76
 
77
  params = {
@@ -84,23 +44,11 @@ def web_search(query: str) -> list[dict]:
84
 
85
  try:
86
  search = GoogleSearch(params)
87
- search_results_dict = search.get_dict() # Get results as a dictionary
88
- print(f"SerpAPI raw response keys: {search_results_dict.keys() if isinstance(search_results_dict, dict) else 'Response is not a dictionary'}") # Debugging response keys
89
-
90
- # Log the full SerpAPI response for debugging if organic_results is missing or empty
91
- if not isinstance(search_results_dict, dict) or "organic_results" not in search_results_dict or not isinstance(search_results_dict["organic_results"], list) or not search_results_dict["organic_results"]:
92
- print(f"SerpAPI response did not contain organic results or had invalid format. Response: {search_results_dict}")
93
-
94
 
95
  # Extract organic results
96
- # Add check that search_results_dict and organic_results are valid
97
- if isinstance(search_results_dict, dict) and "organic_results" in search_results_dict and isinstance(search_results_dict["organic_results"], list):
98
- print(f"Found {len(search_results_dict['organic_results'])} organic results.") # Debugging result count
99
- for result in search_results_dict["organic_results"]:
100
- # Add check for None or non-dict result item
101
- if result is None or not isinstance(result, dict):
102
- print(f"Skipping invalid search result item: {result}")
103
- continue
104
  item = {
105
  'title': result.get('title'),
106
  'url': result.get('link'),
@@ -108,335 +56,59 @@ def web_search(query: str) -> list[dict]:
108
  }
109
  results.append(item)
110
  else:
111
- print(f"No 'organic_results' key found or invalid format in SerpAPI response. Response type: {type(search_results_dict)}")
112
- # Print the whole response if no organic_results are found for debugging
113
- # print(f"SerpAPI response (no organic results): {search_results_dict}")
114
 
115
 
116
  except Exception as e:
117
  print(f"An error occurred during SerpAPI web search: {e}")
118
- # Ensure an empty list is returned on error
119
- return []
120
 
121
- print(f"web_search returning {len(results)} results.") # Debugging return count
122
- return results # Always return a list (empty or with results)
123
 
 
 
124
 
125
- # --- Basic Agent Definition (Modified to remove LLM dependency for now) ---
126
  class BasicAgent:
127
 
128
  def __init__(self):
129
- print("BasicAgent initialized.") # Debugging print before init
130
- # Removed LLM check as it's not used here
131
- # global gemini_model # Access global variable
132
- # if gemini_model is None:
133
- # print("Warning: Google Generative AI model not successfully loaded before agent initialization.")
134
- # else:
135
- # print("Google Generative AI model found and ready.") # Debugging print after successful init
136
-
137
- def process_video(self, video_source: str) -> str:
138
- """
139
- Processes a video source (file path or URL), extracts frames, and
140
- performs placeholder visual analysis.
141
-
142
- Args:
143
- video_source: Path to the video file or a video URL.
144
-
145
- Returns:
146
- A string summarizing the video processing result or an error message.
147
- """
148
- print(f"Processing video source: {video_source}")
149
- cap = None
150
- try:
151
- # Attempt to open the video source
152
- # Using cv2.CAP_FFMPEG might help with URLs, but requires FFmpeg
153
- # cap = cv2.VideoCapture(video_source, cv2.CAP_FFMPEG)
154
- cap = cv2.VideoCapture(video_source)
155
-
156
-
157
- # Check if the video was opened successfully
158
- if not cap.isOpened():
159
- print(f"Error: Could not open video source {video_source}")
160
- return f"Error: Could not open video source {video_source}"
161
-
162
- frame_count = 0
163
- while True:
164
- # Read a frame from the video
165
- ret, frame = cap.read()
166
-
167
- # If frame was not read successfully, we've reached the end of the video
168
- if not ret:
169
- print("End of video stream.")
170
- break
171
-
172
- frame_count += 1
173
- # --- Placeholder for visual analysis ---
174
- # In a real application, you would perform analysis on the 'frame' object here.
175
- # This could involve object detection, scene recognition, etc.
176
- # Example placeholder:
177
- # gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
178
- # Perform analysis on gray_frame
179
-
180
- if frame_count % 100 == 0: # Print progress every 100 frames
181
- print(f"Processed {frame_count} frames.")
182
-
183
- print(f"Finished processing video. Total frames extracted: {frame_count}")
184
- return f"Successfully processed video. Extracted {frame_count} frames."
185
-
186
- except Exception as e:
187
- print(f"An error occurred during video processing: {e}")
188
- return f"An error occurred during video processing: {e}"
189
- finally:
190
- # Release the video capture object
191
- if cap:
192
- cap.release()
193
- print("Video capture released.")
194
-
195
- def process_audio(self, audio_source: str) -> str:
196
- """
197
- Processes an audio source (file path), extracts speech, and performs
198
- placeholder audio analysis.
199
-
200
- Args:
201
- audio_source: Path to the audio file.
202
-
203
- Returns:
204
- A string summarizing the audio processing result or an error message.
205
- """
206
- print(f"Processing audio source: {audio_source}")
207
- recognizer = sr.Recognizer()
208
- try:
209
- # Load the audio file
210
- audio = AudioSegment.from_file(audio_source)
211
- print(f"Audio loaded. Duration: {len(audio)} ms")
212
-
213
- # Export to a format SpeechRecognition can handle (e.g., WAV)
214
- with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as fp:
215
- audio.export(fp.name, format="wav")
216
- temp_wav_file = fp.name
217
- print(f"Audio exported to temporary WAV: {temp_wav_file}")
218
-
219
- # Use SpeechRecognition to transcribe the audio
220
- with sr.AudioFile(temp_wav_file) as source:
221
- print("Reading audio file for transcription...")
222
- audio_data = recognizer.record(source) # read the entire audio file
223
- print("Audio data recorded.")
224
-
225
- # Attempt to recognize speech
226
- try:
227
- print("Attempting speech recognition...")
228
- text = recognizer.recognize_google(audio_data) # Using Google Web Speech API
229
- print(f"Transcription result: {text}")
230
- return f"Audio processed. Transcription: '{text}'"
231
- except sr.UnknownValueError:
232
- print("Speech Recognition could not understand audio")
233
- return "Audio processed, but could not understand speech."
234
- except sr.RequestError as e:
235
- print(f"Could not request results from Google Speech Recognition service; {e}")
236
- return f"Audio processed, but speech recognition service failed: {e}"
237
- except Exception as e:
238
- print(f"An unexpected error occurred during speech recognition: {e}")
239
- return f"An unexpected error occurred during speech recognition: {e}"
240
 
241
- except Exception as e:
242
- print(f"An error occurred during audio processing: {e}")
243
- return f"An error occurred during audio processing: {e}"
244
- finally:
245
- # Clean up the temporary WAV file
246
- if 'temp_wav_file' in locals() and os.path.exists(temp_wav_file):
247
- os.remove(temp_wav_file)
248
- print(f"Temporary WAV file removed: {temp_wav_file}")
249
-
250
-
251
- def __call__(self, question: str, video_source: str | None = None, audio_source: str | None = None) -> str:
252
- # Removed global gemini_model declaration as it's not used here
253
  print(f"Agent received question (first 50 chars): {question[:50]}...")
254
- print(f"Video source provided: {video_source}")
255
- print(f"Audio source provided: {audio_source}")
256
-
257
- # --- Check for media processing tasks ---
258
- media_processing_results = []
259
- if video_source:
260
- print("Video source provided. Attempting video processing.")
261
- video_processing_result = self.process_video(video_source)
262
- media_processing_results.append(f"Video processing result: {video_processing_result}")
263
 
264
- if audio_source:
265
- print("Audio source provided. Attempting audio processing.")
266
- audio_processing_result = self.process_audio(audio_source)
267
- media_processing_results.append(f"Audio processing result: {audio_processing_result}")
268
-
269
- # If media was processed, return the results for now
270
- if media_processing_results:
271
- return "\n".join(media_processing_results)
272
-
273
-
274
- # Simple logic to determine if a web search is needed (only if no media source)
275
  question_lower = question.lower()
276
  search_keywords = ["what is", "how to", "where is", "who is", "when did", "define", "explain", "tell me about"]
277
  needs_search = any(keyword in question_lower for keyword in search_keywords) or "?" in question
278
- print(f"Needs search: {needs_search}") # Debugging search decision
279
 
280
- # --- Analyze question and refine search query ---
281
- # Simplified search query generation - removed LLM query generation
282
- search_query = question # Default search query is the original question
283
  if needs_search:
284
- print("Analyzing question for keywords and refining search query...")
285
- # Basic keyword extraction: split by common question words and take the rest
286
- parts = question_lower.split("what is", 1)
287
- if len(parts) > 1:
288
- search_query = parts[1].strip()
289
- else:
290
- parts = question_lower.split("how to", 1)
291
- if len(parts) > 1:
292
- search_query = parts[1].strip()
 
 
 
 
 
 
 
 
 
 
 
293
  else:
294
- parts = question_lower.split("where is", 1)
295
- if len(parts) > 1:
296
- search_query = parts[1].strip()
297
- else:
298
- parts = question_lower.split("who is", 1)
299
- if len(parts) > 1:
300
- search_query = parts[1].strip()
301
- else:
302
- parts = question_lower.split("when did", 1)
303
- if len(parts) > 1:
304
- search_query = parts[1].strip()
305
- else:
306
- parts = question_lower.split("define", 1)
307
- if len(parts) > 1:
308
- search_query = parts[1].strip()
309
- else:
310
- parts = question_lower.split("explain", 1)
311
- if len(parts) > 1:
312
- search_query = parts[1].strip()
313
- else:
314
- parts = question_lower.split("tell me about", 1)
315
- if len(parts) > 1:
316
- search_query = parts[1].strip()
317
- else:
318
- # If no specific question keyword found, use the whole question
319
- search_query = question_lower.strip()
320
-
321
-
322
- # Optional: Add quotation marks for multi-word phrases if identified
323
- # This simple approach just uses the extracted part as is.
324
- # A more complex approach would identify multi-word entities (e.g., "New York City")
325
- # and wrap them in quotes.
326
-
327
- # Optional: Add contextual terms
328
- # Example: If "musician" or "band" is in the question, add "discography"
329
- if any(word in question_lower for word in ["musician", "band", "artist", "singer"]):
330
- search_query += " discography"
331
- elif any(word in question_lower for word in ["movie", "film", "actor", "actress"]):
332
- search_query += " plot summary"
333
- elif any(word in question_lower for word in ["book", "author", "novel"]):
334
- search_query += " plot summary"
335
-
336
-
337
- print(f"Final search query used: {search_query}") # Debugging final query
338
-
339
- search_results = [] # Initialize search_results to an empty list before the try block
340
- if needs_search:
341
- print(f"Question likely requires search. Searching for: {search_query}")
342
- try:
343
- search_results = web_search(search_query) # Call the web_search function with the generated query
344
- print(f"Received {len(search_results)} search results from web_search.") # Debugging results received
345
- print(f"Type of search_results: {type(search_results)}") # Debugging type of search_results
346
- except Exception as e:
347
- print(f"An error occurred during web search: {e}")
348
- return f"An error occurred during web search: {e}"
349
-
350
- # --- Use LLM to process search results if available (Removed LLM Synthesis) ---
351
- # Check that search_results is a list and is not empty
352
- if isinstance(search_results, list) and search_results and gemini_model is not None:
353
- print("Using Google LLM to process search results.") # Debugging print before LLM call
354
-
355
- # Format search results for the LLM
356
- context = ""
357
- for i, result in enumerate(search_results[:5]): # Use top 5 results for context
358
- # Add check for None or non-dict result item before accessing keys
359
- if result is None or not isinstance(result, dict):
360
- print(f"Skipping invalid result at index {i} in LLM context formatting: {result}")
361
- continue
362
- context += f"Source {i+1}:\n"
363
- if result.get('title'):
364
- context += f"Title: {result['title']}\n"
365
- if result.get('snippet'):
366
- context += f"Snippet: {result['snippet']}\n"
367
- if result.get('url'):
368
- context += f"URL: {result['url']}\n"
369
- context += "---\n" # Separator
370
-
371
- # Refined prompt for the LLM
372
- prompt = f"""Carefully read the following search results and answer the user's question based *only* on the information provided in these results.
373
- If the search results do not contain sufficient information to fully answer the question, explicitly state that you could not find enough information in the provided results.
374
- Do not use any outside knowledge. Structure your answer clearly and concisely.
375
-
376
- Question: {question}
377
-
378
- Search Results:
379
- {context}
380
-
381
- Answer:"""
382
-
383
- print(f"LLM Prompt (first 500 chars):\n{prompt[:500]}...") # Debugging prompt
384
-
385
- try:
386
- # Generate content using the Gemini model
387
- response = gemini_model.generate_content(prompt)
388
- generated_text = response.text # Get the generated text
389
-
390
- # Add check for empty or whitespace generated text
391
- if generated_text and generated_text.strip():
392
- llm_answer = generated_text.strip()
393
- print(f"LLM generated text (first 100 chars): {generated_text[:100]}...") # Debugging raw output
394
- print(f"Agent returning LLM-based answer (first 100 chars): {llm_answer[:100]}...") # Debugging final answer
395
- return llm_answer
396
- else:
397
- print("LLM generated empty or whitespace answer.")
398
- return "I couldn't generate a specific answer based on the search results."
399
-
400
-
401
- except Exception as llm_e:
402
- print(f"An error occurred during LLM generation: {llm_e}")
403
- return f"An error occurred while processing search results with the LLM: {llm_e}"
404
-
405
- # Fallback if search results are empty or not a list, or LLM is None
406
- elif isinstance(search_results, list) and search_results: # Search results exist and is a list, but LLM is not available or failed
407
- print("Google Generative AI model not loaded or search results empty or LLM failed. Cannot use LLM for synthesis.")
408
- # Return the old style answer if LLM is not available, but only if search results exist
409
- print("Returning basic answer based on search results (LLM not available).")
410
- answer_parts = []
411
- for i, result in enumerate(search_results[:3]):
412
- # Add check for None or non-dict result item before accessing keys
413
- if result is None or not isinstance(result, dict):
414
- print(f"Skipping invalid result at index {i} in basic answer formatting: {result}")
415
- continue
416
- if result.get('snippet'):
417
- # Limit snippet length to avoid overly long responses
418
- snippet = result['snippet']
419
- if len(snippet) > 200:
420
- snippet = snippet[:200] + "..."
421
- answer_parts.append(f"Snippet {i+1}: {snippet}")
422
- elif result.get('title'):
423
- answer_parts.append(f"Result {i+1} Title: {result['title']}")
424
- if answer_parts:
425
- return "Based on web search (LLM not available):\n" + "\n".join(answer_parts)
426
- else:
427
- # Fallback if no useful snippets/titles found in search results
428
- print("No useful snippets/titles found in search results.")
429
- return "I couldn't find useful information in the search results (LLM not available)."
430
- else: # search_results is None or not a list, or empty
431
- print(f"Web search returned no results or results in invalid format. Type: {type(search_results)}")
432
- return "I couldn't find any relevant information on the web for your question."
433
-
434
- else: # needs_search is True but no search results were returned (this case is now covered by the try-except around web_search)
435
- # This else block should ideally not be reached if needs_search is True and web_search is called
436
- print("Question required search, but no search was performed or it failed.")
437
- return "I couldn't perform a web search for your question."
438
-
439
 
 
 
 
440
  else:
441
  # If no search is needed, return a default or simple response
442
  print("Question does not appear to require search. Returning fixed answer.")
@@ -449,7 +121,6 @@ def run_and_submit_all( profile: gr.OAuthProfile | None, other_arg=None): # Modi
449
  Fetches all questions, runs the BasicAgent on them, submits all answers,
450
  and displays the results.
451
  """
452
- print("run_and_submit_all function started.") # Debugging print at function start
453
  # --- Determine HF Space Runtime URL and Repo URL ---
454
  space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
455
 
@@ -461,14 +132,12 @@ def run_and_submit_all( profile: gr.OAuthProfile | None, other_arg=None): # Modi
461
  return "Please Login to Hugging Face with the button.", None
462
 
463
  api_url = DEFAULT_API_URL
464
- questions_url = f"{api_url}/agent_challenge/questions" # Corrected endpoint
465
- submit_url = f"{api_url}/agent_challenge/submit" # Corrected endpoint
466
 
467
  # 1. Instantiate Agent ( modify this part to create your agent)
468
- print("Attempting to instantiate BasicAgent...") # Debugging print before instantiation
469
  try:
470
  agent = BasicAgent()
471
- print("BasicAgent instantiated successfully.") # Debugging print after instantiation
472
  except Exception as e:
473
  print(f"Error instantiating agent: {e}")
474
  return f"Error initializing agent: {e}", None
@@ -478,14 +147,12 @@ def run_and_submit_all( profile: gr.OAuthProfile | None, other_arg=None): # Modi
478
 
479
  # 2. Fetch Questions
480
  print(f"Fetching questions from: {questions_url}")
481
- questions_data = None # Initialize to None
482
  try:
483
  response = requests.get(questions_url, timeout=15)
484
  response.raise_for_status()
485
  questions_data = response.json()
486
- # Add check for empty or non-list questions_data immediately after fetching
487
- if not isinstance(questions_data, list) or not questions_data:
488
- print(f"Fetched questions_data is empty or not a list. Type: {type(questions_data)}")
489
  return "Fetched questions list is empty or invalid format.", None
490
  print(f"Fetched {len(questions_data)} questions.")
491
  except requests.exceptions.RequestException as e:
@@ -493,8 +160,7 @@ def run_and_submit_all( profile: gr.OAuthProfile | None, other_arg=None): # Modi
493
  return f"Error fetching questions: {e}", None
494
  except requests.exceptions.JSONDecodeError as e:
495
  print(f"Error decoding JSON response from questions endpoint: {e}")
496
- # Print the response text for debugging if JSON decoding fails
497
- print(f"Response text: {response.text[:500] if 'response' in locals() else 'No response object'}")
498
  return f"Error decoding server response for questions: {e}", None
499
  except Exception as e:
500
  print(f"An unexpected error occurred fetching questions: {e}")
@@ -505,23 +171,14 @@ def run_and_submit_all( profile: gr.OAuthProfile | None, other_arg=None): # Modi
505
  results_log = []
506
  answers_payload = []
507
  print(f"Running agent on {len(questions_data)} questions...")
508
- # The check that questions_data is a list is now done immediately after fetching
509
  for item in questions_data:
510
- # Add check for None or non-dict item before accessing keys
511
- if item is None or not isinstance(item, dict):
512
- print(f"Skipping invalid item in questions_data: {item}")
513
- continue
514
  task_id = item.get("task_id")
515
  question_text = item.get("question")
516
- if not task_id or not isinstance(task_id, (str, int)) or not question_text or not isinstance(question_text, str):
517
- print(f"Skipping item with missing or invalid task_id or question: {item}")
518
  continue
519
- print(f"Processing Task ID: {task_id}") # Debugging task ID
520
  try:
521
- # Here, we only pass the question text for now, as the API doesn't support video input
522
- # The video processing logic is added but not triggered by this function
523
  submitted_answer = agent(question_text)
524
- print(f"Agent returned answer for {task_id}: {submitted_answer[:50]}...") # Debugging returned answer
525
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
526
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
527
  except Exception as e:
@@ -558,17 +215,8 @@ def run_and_submit_all( profile: gr.OAuthProfile | None, other_arg=None): # Modi
558
  try:
559
  error_json = e.response.json()
560
  error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
561
- status_message = f"Submission Failed: {error_detail}"
562
- print(status_message)
563
- # If submission fails, also return the results log so the user can see what was attempted
564
- results_df = pd.DataFrame(results_log)
565
- return status_message, results_df
566
  except requests.exceptions.JSONDecodeError:
567
  error_detail += f" Response: {e.response.text[:500]}"
568
- status_message = f"Submission Failed: {error_detail}"
569
- print(status_message)
570
- results_df = pd.DataFrame(results_log)
571
- return status_message, results_df
572
  status_message = f"Submission Failed: {error_detail}"
573
  print(status_message)
574
  results_df = pd.DataFrame(results_log)
@@ -590,25 +238,6 @@ def run_and_submit_all( profile: gr.OAuthProfile | None, other_arg=None): # Modi
590
  return status_message, results_df
591
 
592
 
593
- # Function to call process_video directly for testing
594
- def test_video_processing(video_source: str) -> str:
595
- print(f"Testing video processing with source: {video_source}")
596
- try:
597
- agent = BasicAgent()
598
- return agent.process_video(video_source)
599
- except Exception as e:
600
- return f"Error during video processing test: {e}"
601
-
602
- # Function to call process_audio directly for testing
603
- def test_audio_processing(audio_source: str) -> str:
604
- print(f"Testing audio processing with source: {audio_source}")
605
- try:
606
- agent = BasicAgent()
607
- return agent.process_audio(audio_source)
608
- except Exception as e:
609
- return f"Error during audio processing test: {e}"
610
-
611
-
612
  # Move Gradio interface definition and launch outside the function
613
  with gr.Blocks(theme=gr.themes.Soft(), title="Basic Agent Evaluation Runner") as demo:
614
  gr.Markdown(
@@ -619,16 +248,15 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Basic Agent Evaluation Runner") as
619
 
620
  **Instructions:**
621
  1. Ensure your agent logic is defined in the `BasicAgent` class above.
622
- 2. **Get a SerpAPI key and a Google AI API key and add them as environment variables in your runtime environment (e.g., as secrets in your Hugging Face Space settings).**
623
  3. Log in to Hugging Face using the button below.
624
- 4. Click the "Run Evaluation & Submit All Answers" button to run on predefined questions.
625
- 5. Use the "Test Video Processing" and "Test Audio Processing" sections to test media analysis.
626
  """
627
  )
628
  login_btn = gr.LoginButton()
629
 
630
  run_button = gr.Button("Run Evaluation & Submit All Answers")
631
- run_button.interactive = True # Re-enable the button
632
 
633
  status_output = gr.Textbox(label="Run Status", interactive=False, lines=5)
634
  results_output = gr.DataFrame(label="Evaluation Results")
@@ -639,30 +267,6 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Basic Agent Evaluation Runner") as
639
  outputs=[status_output, results_output]
640
  )
641
 
642
- gr.Markdown("---") # Separator
643
- gr.Markdown("## Test Media Processing")
644
-
645
- video_test_input = gr.Video(label="Upload Video or Paste URL")
646
- video_test_button = gr.Button("Test Video Processing")
647
- video_test_output = gr.Textbox(label="Video Processing Result", interactive=False)
648
-
649
- video_test_button.click(
650
- test_video_processing,
651
- inputs=[video_test_input],
652
- outputs=[video_test_output]
653
- )
654
-
655
- audio_test_input = gr.Audio(label="Upload Audio or Paste URL")
656
- audio_test_button = gr.Button("Test Audio Processing")
657
- audio_test_output = gr.Textbox(label="Audio Processing Result", interactive=False)
658
-
659
- audio_test_button.click(
660
- test_audio_processing,
661
- inputs=[audio_test_input],
662
- outputs=[audio_test_output]
663
- )
664
-
665
-
666
  # Ensure the app launches when the script is run
667
  if __name__ == "__main__":
668
  demo.launch(server_name="0.0.0.0") # Ensure binding to all interfaces
 
5
  import requests
6
  import inspect
7
  import pandas as pd
8
+ # Removed bs4 and BeautifulSoup as we'll use SerpAPI
9
+ # from bs4 import BeautifulSoup
10
+ # import requests
 
 
11
 
12
+ # Import SerpAPI
 
13
  from serpapi import GoogleSearch
14
+ from google.colab import userdata # To access the API key from secrets
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ # --- Constants ---
17
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
18
+ SERPAPI_API_KEY = userdata.get('SERPAPI_API_KEY') # Get SerpAPI key from Colab secrets
19
 
20
  # --- Web Search Function (using SerpAPI) ---
21
  def web_search(query: str) -> list[dict]:
 
22
  """
23
  Performs a web search using SerpAPI and returns relevant information.
24
 
 
26
  query: The search query string.
27
 
28
  Returns:
29
+ A list of dictionaries, where each dictionary represents a search result
30
+ with keys 'title', 'snippet', and 'url'. Returns an empty list if no
31
+ results are found or an error occurs.
32
+ """
 
33
  if not SERPAPI_API_KEY:
34
+ print("SerpAPI key not found in Colab secrets.")
35
  return []
36
 
37
  params = {
 
44
 
45
  try:
46
  search = GoogleSearch(params)
47
+ search_results = search.get_dict() # Get results as a dictionary
 
 
 
 
 
 
48
 
49
  # Extract organic results
50
+ if "organic_results" in search_results:
51
+ for result in search_results["organic_results"]:
 
 
 
 
 
 
52
  item = {
53
  'title': result.get('title'),
54
  'url': result.get('link'),
 
56
  }
57
  results.append(item)
58
  else:
59
+ print("No organic results found in SerpAPI response.")
 
 
60
 
61
 
62
  except Exception as e:
63
  print(f"An error occurred during SerpAPI web search: {e}")
 
 
64
 
65
+ return results
66
+
67
 
68
+ # --- Basic Agent Definition ---
69
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
70
 
 
71
  class BasicAgent:
72
 
73
  def __init__(self):
74
+ print("BasicAgent initialized.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
+ def __call__(self, question: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
77
  print(f"Agent received question (first 50 chars): {question[:50]}...")
 
 
 
 
 
 
 
 
 
78
 
79
+ # Simple logic to determine if a web search is needed
 
 
 
 
 
 
 
 
 
 
80
  question_lower = question.lower()
81
  search_keywords = ["what is", "how to", "where is", "who is", "when did", "define", "explain", "tell me about"]
82
  needs_search = any(keyword in question_lower for keyword in search_keywords) or "?" in question
 
83
 
 
 
 
84
  if needs_search:
85
+ print(f"Question likely requires search. Searching for: {question}")
86
+ search_results = web_search(question) # Call the web_search function
87
+
88
+ if search_results:
89
+ # Process search results to formulate an answer
90
+ answer_parts = []
91
+ for i, result in enumerate(search_results[:3]): # Use top 3 results
92
+ if result.get('snippet'):
93
+ answer_parts.append(f"Snippet {i+1}: {result['snippet']}")
94
+ elif result.get('title'):
95
+ answer_parts.append(f"Result {i+1} Title: {result['title']}")
96
+ # Optional: add URL
97
+ # if result.get('url'):
98
+ # answer_parts.append(f"URL {i+1}: {result['url']}")
99
+
100
+
101
+ if answer_parts:
102
+ formulated_answer = "Based on web search:\n" + "\n".join(answer_parts)
103
+ print(f"Agent returning search-based answer: {formulated_answer[:100]}...")
104
+ return formulated_answer
105
  else:
106
+ print("Web search returned results but no useful snippets/titles found.")
107
+ return "I couldn't find a specific answer from the web search results."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
+ else:
110
+ print("Web search returned no results.")
111
+ return "I couldn't find any relevant information on the web for your question."
112
  else:
113
  # If no search is needed, return a default or simple response
114
  print("Question does not appear to require search. Returning fixed answer.")
 
121
  Fetches all questions, runs the BasicAgent on them, submits all answers,
122
  and displays the results.
123
  """
 
124
  # --- Determine HF Space Runtime URL and Repo URL ---
125
  space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
126
 
 
132
  return "Please Login to Hugging Face with the button.", None
133
 
134
  api_url = DEFAULT_API_URL
135
+ questions_url = f"{api_url}/questions"
136
+ submit_url = f"{api_url}/submit"
137
 
138
  # 1. Instantiate Agent ( modify this part to create your agent)
 
139
  try:
140
  agent = BasicAgent()
 
141
  except Exception as e:
142
  print(f"Error instantiating agent: {e}")
143
  return f"Error initializing agent: {e}", None
 
147
 
148
  # 2. Fetch Questions
149
  print(f"Fetching questions from: {questions_url}")
 
150
  try:
151
  response = requests.get(questions_url, timeout=15)
152
  response.raise_for_status()
153
  questions_data = response.json()
154
+ if not questions_data:
155
+ print("Fetched questions list is empty.")
 
156
  return "Fetched questions list is empty or invalid format.", None
157
  print(f"Fetched {len(questions_data)} questions.")
158
  except requests.exceptions.RequestException as e:
 
160
  return f"Error fetching questions: {e}", None
161
  except requests.exceptions.JSONDecodeError as e:
162
  print(f"Error decoding JSON response from questions endpoint: {e}")
163
+ print(f"Response text: {response.text[:500]}")
 
164
  return f"Error decoding server response for questions: {e}", None
165
  except Exception as e:
166
  print(f"An unexpected error occurred fetching questions: {e}")
 
171
  results_log = []
172
  answers_payload = []
173
  print(f"Running agent on {len(questions_data)} questions...")
 
174
  for item in questions_data:
 
 
 
 
175
  task_id = item.get("task_id")
176
  question_text = item.get("question")
177
+ if not task_id or question_text is None:
178
+ print(f"Skipping item with missing task_id or question: {item}")
179
  continue
 
180
  try:
 
 
181
  submitted_answer = agent(question_text)
 
182
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
183
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
184
  except Exception as e:
 
215
  try:
216
  error_json = e.response.json()
217
  error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
 
 
 
 
 
218
  except requests.exceptions.JSONDecodeError:
219
  error_detail += f" Response: {e.response.text[:500]}"
 
 
 
 
220
  status_message = f"Submission Failed: {error_detail}"
221
  print(status_message)
222
  results_df = pd.DataFrame(results_log)
 
238
  return status_message, results_df
239
 
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  # Move Gradio interface definition and launch outside the function
242
  with gr.Blocks(theme=gr.themes.Soft(), title="Basic Agent Evaluation Runner") as demo:
243
  gr.Markdown(
 
248
 
249
  **Instructions:**
250
  1. Ensure your agent logic is defined in the `BasicAgent` class above.
251
+ 2. **Get a SerpAPI key and add it to Colab Secrets (name it `SERPAPI_API_KEY`).**
252
  3. Log in to Hugging Face using the button below.
253
+ 4. Click the "Run Evaluation & Submit All Answers" button.
254
+ 5. The application will fetch questions, run your agent, submit answers, and display the results below.
255
  """
256
  )
257
  login_btn = gr.LoginButton()
258
 
259
  run_button = gr.Button("Run Evaluation & Submit All Answers")
 
260
 
261
  status_output = gr.Textbox(label="Run Status", interactive=False, lines=5)
262
  results_output = gr.DataFrame(label="Evaluation Results")
 
267
  outputs=[status_output, results_output]
268
  )
269
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  # Ensure the app launches when the script is run
271
  if __name__ == "__main__":
272
  demo.launch(server_name="0.0.0.0") # Ensure binding to all interfaces