selim-ba commited on
Commit
adc14d8
·
verified ·
1 Parent(s): 4cfe489

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +10 -196
app.py CHANGED
@@ -6,17 +6,9 @@ import pandas as pd
6
 
7
  from langgraph.graph import StateGraph, END
8
  from typing import TypedDict
9
- import re
10
  import string
11
- from duckduckgo_search import DDGS
12
-
13
- from pytube import YouTube, exceptions as pytube_exceptions
14
- from pytube.innertube import _default_clients
15
- _default_clients["WEB"]["clientVersion"] = "2.20240401.00.00"
16
- import cv2
17
- from ultralytics import YOLO
18
- import tempfile
19
- import os
20
 
21
 
22
  # (Keep Constants as is)
@@ -36,7 +28,6 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
36
 
37
  class SuperSmartAgent:
38
  def __init__(self):
39
- self.model = YOLO("yolov8n.pt")
40
  self.graph = self._build_graph()
41
 
42
  def _build_graph(self):
@@ -134,168 +125,6 @@ class SuperSmartAgent:
134
 
135
  ###################
136
 
137
- def contains_youtube_url(text):
138
- # This regex is good for identifying the presence of a YouTube URL
139
- return re.search(r"(https?://)?(www\.)?(youtube\.com|youtu\.be)/", text) is not None
140
-
141
- def extract_youtube_url(text):
142
- # A more focused regex to *extract* the full YouTube URL
143
- # This pattern attempts to match common YouTube URL formats and capture the entire URL
144
- youtube_regex = (
145
- r'(https?://)?(www\.)?'
146
- '(?:youtube\.com|youtu\.be)/' # Non-capturing group for domain
147
- '(?:watch\?v=|embed/|v/|.+\?v=)?' # Non-capturing group for path
148
- '([a-zA-Z0-9_-]{11})' # Capturing group for the 11-character video ID
149
- '(?:[&?].*)?' # Optional: matches any query parameters after the video ID
150
- )
151
- match = re.search(youtube_regex, text)
152
- if match:
153
- # Reconstruct the standard YouTube watch URL from the video ID
154
- video_id = match.group(3) # The 11-character video ID
155
- return f"https://www.youtube.com/watch?v={video_id}"
156
- return None
157
-
158
-
159
- def check_special_tools(state):
160
- q = state["question"].lower()
161
-
162
- if "search the web" in q or "look up" in q:
163
- state["use_tool"] = "web"
164
- elif contains_youtube_url(q) or "in the video" in q or "from the video" in q:
165
- state["use_tool"] = "video"
166
- else:
167
- state["use_tool"] = None
168
-
169
- return state
170
-
171
- def use_web_search(state):
172
- question = state.get("question", "")
173
- query = question.lower().replace("search the web for", "").replace("look up", "").strip()
174
- print(f"Searching DuckDuckGo for: {query}")
175
-
176
- try:
177
- with DDGS() as ddgs:
178
- results = ddgs.text(query, max_results=3)
179
-
180
- if results:
181
- snippets = [f"{r['title']}: {r.get('body', '')}" for r in results]
182
- state["response"] = "\n".join(snippets)
183
- else:
184
- state["response"] = "No results found."
185
- except Exception as e:
186
- state["response"] = f"Web search error: {e}"
187
- return state
188
-
189
- def use_video_analysis(state):
190
- if self.model is None:
191
- state["response"] = "Video analysis skipped: YOLO model failed to load during agent initialization."
192
- return state
193
-
194
- # Use the new helper function to extract a clean YouTube URL
195
- url = extract_youtube_url(state["question"])
196
-
197
- if not url:
198
- state["response"] = "Could not find a valid YouTube video URL in the question."
199
- print("No valid YouTube URL found in question.")
200
- return state
201
-
202
- print(f"URL being passed to pytube: {url}")
203
-
204
- temp_path = None
205
-
206
- try:
207
- #yt = YouTube(url)
208
- yt = YouTube(url, use_oauth=False, allow_oauth_cache=True)
209
- print(f"YouTube video title: {yt.title}")
210
-
211
- # Select a stream. Prioritize progressive MP4.
212
- stream = yt.streams.filter(file_extension="mp4", progressive=True).order_by('resolution').desc().first()
213
-
214
- if not stream:
215
- print("No progressive MP4 stream found. Trying to find any MP4 stream.")
216
- # Fallback to any MP4 stream, but note that it might not be progressive (audio+video)
217
- stream = yt.streams.filter(file_extension="mp4").order_by('resolution').desc().first()
218
-
219
- if not stream:
220
- state["response"] = "No suitable MP4 video stream found for download."
221
- print("No suitable MP4 video stream found after trying progressive and general MP4 filters.")
222
- return state
223
-
224
- print(f"Selected stream: {stream.resolution}, {stream.mime_type}")
225
-
226
- with tempfile.TemporaryDirectory() as tmpdirname:
227
- temp_path = os.path.join(tmpdirname, "video.mp4")
228
- print(f"Downloading video to {temp_path}...")
229
- stream.download(filename=temp_path)
230
- print("Video downloaded successfully.")
231
-
232
- cap = cv2.VideoCapture(temp_path)
233
- if not cap.isOpened():
234
- state["response"] = "Error: Could not open downloaded video file with OpenCV."
235
- print(f"Error opening video file: {temp_path}")
236
- return state
237
-
238
- frames = []
239
- frame_count = 0
240
- max_frames_to_process = 5 # Limit processing to save time and resources
241
-
242
- while True:
243
- ret, frame = cap.read()
244
- if not ret or frame_count >= max_frames_to_process:
245
- break
246
- frames.append(frame)
247
- frame_count += 1
248
- cap.release()
249
- print(f"Captured {len(frames)} frames for analysis.")
250
-
251
- if not frames:
252
- state["response"] = "No frames could be read from the video for analysis."
253
- print("OpenCV failed to read any frames.")
254
- return state
255
-
256
- object_counts = []
257
- for i, frame in enumerate(frames):
258
- try:
259
- results = self.model(frame, verbose=False)
260
- count = len(results[0].boxes)
261
- object_counts.append(count)
262
- print(f"Frame {i+1}/{len(frames)}: Detected {count} objects.")
263
- except Exception as yolo_e:
264
- print(f"Error during object detection on frame {i+1}: {yolo_e}")
265
- object_counts.append("Error")
266
-
267
- state["response"] = f"Objects detected per frame (first {len(object_counts)} frames): {object_counts}"
268
-
269
- except pytube_exceptions.AgeRestrictedError:
270
- state["response"] = "Error: Video is age-restricted and cannot be downloaded without further authentication."
271
- print("Pytube Error: AgeRestrictedError")
272
- except pytube_exceptions.LiveStreamError:
273
- state["response"] = "Error: Cannot process live YouTube streams."
274
- print("Pytube Error: LiveStreamError")
275
- except pytube_exceptions.VideoUnavailable:
276
- state["response"] = "Error: YouTube video is unavailable or private."
277
- print("Pytube Error: VideoUnavailable")
278
- except pytube_exceptions.PytubeError as pe:
279
- state["response"] = f"Pytube specific error during video download: {pe}"
280
- print(f"Pytube Error: {pe}")
281
- except requests.exceptions.Timeout:
282
- state["response"] = "Error processing video: Video download timed out."
283
- print("Request Timeout during video download.")
284
- except Exception as e:
285
- import traceback
286
- error_traceback = traceback.format_exc()
287
- state["response"] = f"An unexpected error occurred during video analysis: {e}. Check logs for details."
288
- print(f"General Error in use_video_analysis: {e}\n{error_traceback}")
289
- finally:
290
- if temp_path and os.path.exists(temp_path):
291
- try:
292
- os.remove(temp_path)
293
- print(f"Cleaned up temporary video file: {temp_path}")
294
- except OSError as e:
295
- print(f"Error during cleanup of temporary file {temp_path}: {e}")
296
-
297
- return state
298
-
299
 
300
 
301
 
@@ -310,11 +139,8 @@ class SuperSmartAgent:
310
  response: str
311
 
312
  builder = StateGraph(AgentState)
313
-
314
- # Nodes
315
- builder.add_node("check_special_tools", check_special_tools)
316
- builder.add_node("use_web_search", use_web_search)
317
- builder.add_node("use_video_analysis", use_video_analysis)
318
  builder.add_node("check_reversed", check_reversed)
319
  builder.add_node("fix_question", fix_question)
320
  builder.add_node("check_riddle_or_trick", check_riddle_or_trick)
@@ -322,30 +148,20 @@ class SuperSmartAgent:
322
  builder.add_node("check_python_suitability", check_python_suitability)
323
  builder.add_node("generate_code", generate_code)
324
  builder.add_node("fallback", fallback)
325
-
326
- # Entry point: special tool detection first
327
- builder.set_entry_point("check_special_tools")
328
 
329
- # Tool-based branching
330
- builder.add_conditional_edges(
331
- "check_special_tools",
332
- lambda s: "use_web_search" if s.get("use_tool") == "web"
333
- else "use_video_analysis" if s.get("use_tool") == "video"
334
- else "check_reversed"
335
- )
336
- builder.add_edge("use_web_search", END)
337
- builder.add_edge("use_video_analysis", END)
338
-
339
- # Reasoning pipeline (non-tool questions)
340
  builder.add_edge("check_reversed", "fix_question")
341
  builder.add_edge("fix_question", "check_riddle_or_trick")
342
-
343
  builder.add_conditional_edges(
344
  "check_riddle_or_trick",
345
  lambda s: "solve_riddle" if s.get("is_riddle") else "check_python_suitability"
346
  )
347
  builder.add_edge("solve_riddle", END)
348
-
349
  builder.add_conditional_edges(
350
  "check_python_suitability",
351
  lambda s: "generate_code" if s.get("is_python") else "fallback"
@@ -355,8 +171,6 @@ class SuperSmartAgent:
355
 
356
 
357
 
358
-
359
-
360
  graph = builder.compile()
361
  return graph
362
 
 
6
 
7
  from langgraph.graph import StateGraph, END
8
  from typing import TypedDict
 
9
  import string
10
+
11
+
 
 
 
 
 
 
 
12
 
13
 
14
  # (Keep Constants as is)
 
28
 
29
  class SuperSmartAgent:
30
  def __init__(self):
 
31
  self.graph = self._build_graph()
32
 
33
  def _build_graph(self):
 
125
 
126
  ###################
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
 
130
 
 
139
  response: str
140
 
141
  builder = StateGraph(AgentState)
142
+
143
+ # --- Nodes ---
 
 
 
144
  builder.add_node("check_reversed", check_reversed)
145
  builder.add_node("fix_question", fix_question)
146
  builder.add_node("check_riddle_or_trick", check_riddle_or_trick)
 
148
  builder.add_node("check_python_suitability", check_python_suitability)
149
  builder.add_node("generate_code", generate_code)
150
  builder.add_node("fallback", fallback)
 
 
 
151
 
152
+ # --- Entry point ---
153
+ builder.set_entry_point("check_reversed")
154
+
155
+ # --- Reasoning pipeline ---
 
 
 
 
 
 
 
156
  builder.add_edge("check_reversed", "fix_question")
157
  builder.add_edge("fix_question", "check_riddle_or_trick")
158
+
159
  builder.add_conditional_edges(
160
  "check_riddle_or_trick",
161
  lambda s: "solve_riddle" if s.get("is_riddle") else "check_python_suitability"
162
  )
163
  builder.add_edge("solve_riddle", END)
164
+
165
  builder.add_conditional_edges(
166
  "check_python_suitability",
167
  lambda s: "generate_code" if s.get("is_python") else "fallback"
 
171
 
172
 
173
 
 
 
174
  graph = builder.compile()
175
  return graph
176