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

Create app.py

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