Spaces:
Runtime error
Runtime error
Fix
Browse files
app.py
CHANGED
@@ -22,13 +22,13 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
|
22 |
|
23 |
@tool
|
24 |
def serper_search(query: str) -> str:
|
25 |
-
"""Enhanced web search using Serper API with
|
26 |
|
27 |
Args:
|
28 |
query (str): The search query to be executed.
|
29 |
|
30 |
Returns:
|
31 |
-
str:
|
32 |
"""
|
33 |
try:
|
34 |
api_key = os.getenv("SERPER_API_KEY")
|
@@ -36,51 +36,82 @@ def serper_search(query: str) -> str:
|
|
36 |
return "SERPER_API_KEY environment variable not found"
|
37 |
|
38 |
url = "https://google.serper.dev/search"
|
39 |
-
payload = json.dumps({
|
|
|
|
|
|
|
|
|
|
|
40 |
headers = {
|
41 |
'X-API-KEY': api_key,
|
42 |
'Content-Type': 'application/json'
|
43 |
}
|
|
|
44 |
response = requests.post(url, headers=headers, data=payload, timeout=30)
|
45 |
response.raise_for_status()
|
46 |
|
47 |
data = response.json()
|
48 |
results = []
|
49 |
|
|
|
50 |
if 'knowledgeGraph' in data:
|
51 |
kg = data['knowledgeGraph']
|
52 |
-
kg_info = f"KNOWLEDGE GRAPH: {kg.get('title', '')}
|
53 |
-
|
54 |
-
|
55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
results.append(kg_info + "\n")
|
57 |
|
|
|
58 |
if 'organic' in data:
|
59 |
-
for i, item in enumerate(data['organic'][:
|
60 |
-
title = item.get('title', '')
|
61 |
-
snippet = item.get('snippet', '')
|
62 |
-
link = item.get('link', '')
|
63 |
-
result_text = f"RESULT {i+1}:\nTitle: {title}\nSnippet: {snippet}\nURL: {link}\n"
|
64 |
|
65 |
-
|
|
|
|
|
|
|
66 |
years = re.findall(r'\b(19|20)\d{2}\b', snippet)
|
67 |
-
|
68 |
-
|
|
|
|
|
|
|
69 |
|
70 |
-
if re.search(r'
|
71 |
-
|
72 |
-
|
73 |
-
result_text += f"Amounts: {', '.join(amounts)}\n"
|
74 |
|
75 |
results.append(result_text)
|
76 |
|
|
|
77 |
if 'peopleAlsoAsk' in data:
|
78 |
-
paa = "\nPEOPLE ALSO ASK
|
79 |
-
for item in data['peopleAlsoAsk'][:
|
80 |
-
|
|
|
|
|
81 |
results.append(paa)
|
82 |
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
except Exception as e:
|
86 |
return f"Search error: {str(e)}"
|
@@ -88,17 +119,19 @@ def serper_search(query: str) -> str:
|
|
88 |
|
89 |
@tool
|
90 |
def wikipedia_search(query: str) -> str:
|
91 |
-
"""
|
92 |
|
93 |
Args:
|
94 |
-
query (str): Wikipedia search query
|
95 |
|
96 |
Returns:
|
97 |
-
str:
|
98 |
"""
|
99 |
try:
|
100 |
results = []
|
101 |
-
|
|
|
|
|
102 |
direct_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{clean_query}"
|
103 |
|
104 |
try:
|
@@ -106,33 +139,42 @@ def wikipedia_search(query: str) -> str:
|
|
106 |
if response.status_code == 200:
|
107 |
data = response.json()
|
108 |
if data.get('type') != 'disambiguation':
|
109 |
-
summary = f"WIKIPEDIA DIRECT MATCH:\nTitle: {data.get('title', '')}
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
112 |
if 'coordinates' in data:
|
113 |
coords = data['coordinates']
|
114 |
-
summary += f"
|
115 |
-
|
116 |
-
extract = data.get('extract', '')
|
117 |
-
birth_match = re.search(r'born[^)]*(\d{1,2}\s+\w+\s+\d{4})', extract, re.IGNORECASE)
|
118 |
-
if birth_match:
|
119 |
-
summary += f"Birth date found: {birth_match.group(1)}\n"
|
120 |
-
|
121 |
-
death_match = re.search(r'died[^)]*(\d{1,2}\s+\w+\s+\d{4})', extract, re.IGNORECASE)
|
122 |
-
if death_match:
|
123 |
-
summary += f"Death date found: {death_match.group(1)}\n"
|
124 |
|
125 |
results.append(summary)
|
126 |
except:
|
127 |
pass
|
128 |
|
|
|
129 |
search_url = "https://en.wikipedia.org/w/api.php"
|
130 |
search_params = {
|
131 |
"action": "query",
|
132 |
"format": "json",
|
133 |
"list": "search",
|
134 |
"srsearch": query,
|
135 |
-
"srlimit":
|
|
|
136 |
}
|
137 |
|
138 |
try:
|
@@ -140,34 +182,42 @@ def wikipedia_search(query: str) -> str:
|
|
140 |
data = response.json()
|
141 |
|
142 |
if 'query' in data and 'search' in data['query']:
|
143 |
-
search_results = "WIKIPEDIA SEARCH RESULTS
|
144 |
-
for item in data['query']['search']:
|
|
|
145 |
snippet = re.sub(r'<[^>]+>', '', item.get('snippet', ''))
|
146 |
-
|
|
|
|
|
|
|
|
|
|
|
147 |
results.append(search_results)
|
148 |
except:
|
149 |
pass
|
150 |
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
|
|
|
|
169 |
|
170 |
-
return "\n".join(results) if results else "No Wikipedia results found"
|
171 |
|
172 |
except Exception as e:
|
173 |
return f"Wikipedia search error: {str(e)}"
|
@@ -175,7 +225,7 @@ def wikipedia_search(query: str) -> str:
|
|
175 |
|
176 |
@tool
|
177 |
def youtube_analyzer(url: str) -> str:
|
178 |
-
"""
|
179 |
|
180 |
Args:
|
181 |
url (str): YouTube video URL to analyze.
|
@@ -184,6 +234,7 @@ def youtube_analyzer(url: str) -> str:
|
|
184 |
str: Comprehensive video analysis.
|
185 |
"""
|
186 |
try:
|
|
|
187 |
video_id_match = re.search(r'(?:v=|/|youtu\.be/)([A-Za-z0-9_-]{11})', url)
|
188 |
if not video_id_match:
|
189 |
return "Invalid YouTube URL format"
|
@@ -191,70 +242,126 @@ def youtube_analyzer(url: str) -> str:
|
|
191 |
video_id = video_id_match.group(1)
|
192 |
results = []
|
193 |
|
|
|
194 |
try:
|
195 |
oembed_url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
|
196 |
response = requests.get(oembed_url, timeout=15)
|
197 |
|
198 |
if response.status_code == 200:
|
199 |
data = response.json()
|
200 |
-
basic_info = f"VIDEO
|
201 |
|
|
|
202 |
title = data.get('title', '').lower()
|
203 |
-
|
204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
if duration_match:
|
206 |
-
basic_info += f"
|
|
|
207 |
|
208 |
results.append(basic_info)
|
209 |
-
except:
|
210 |
-
|
211 |
|
|
|
212 |
try:
|
213 |
-
video_url = f"https://www.youtube.com/watch?v={video_id}"
|
214 |
headers = {
|
215 |
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
|
|
|
|
|
|
|
|
|
216 |
}
|
217 |
|
218 |
-
|
|
|
|
|
219 |
if response.status_code == 200:
|
220 |
content = response.text
|
221 |
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
|
|
226 |
|
227 |
-
|
228 |
-
|
229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
|
231 |
-
|
|
|
|
|
|
|
|
|
232 |
|
233 |
-
|
234 |
-
|
235 |
-
if
|
236 |
-
results.append(f"
|
|
|
237 |
|
238 |
-
|
|
|
239 |
if duration_match:
|
240 |
-
|
241 |
-
|
242 |
-
|
|
|
243 |
|
|
|
244 |
desc_patterns = [
|
245 |
r'"description":{"simpleText":"([^"]+)"}',
|
246 |
-
r'"shortDescription":"([^"]+)"'
|
|
|
247 |
]
|
248 |
|
249 |
for pattern in desc_patterns:
|
250 |
desc_match = re.search(pattern, content)
|
251 |
if desc_match:
|
252 |
-
description = desc_match.group(1)
|
253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
255 |
|
256 |
except Exception as e:
|
257 |
-
results.append(f"Enhanced
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
259 |
return "\n".join(results) if results else "Could not analyze video"
|
260 |
|
@@ -264,12 +371,11 @@ def youtube_analyzer(url: str) -> str:
|
|
264 |
|
265 |
@tool
|
266 |
def text_processor(text: str, operation: str = "analyze") -> str:
|
267 |
-
"""Advanced text processing
|
268 |
|
269 |
Args:
|
270 |
text (str): Text to process.
|
271 |
-
operation (str
|
272 |
-
Defaults to "analyze".
|
273 |
|
274 |
Returns:
|
275 |
str: Processed text results.
|
@@ -279,21 +385,32 @@ def text_processor(text: str, operation: str = "analyze") -> str:
|
|
279 |
return text[::-1]
|
280 |
|
281 |
elif operation == "decode":
|
|
|
282 |
if text.startswith("base64:"):
|
283 |
try:
|
284 |
decoded = base64.b64decode(text[7:]).decode('utf-8')
|
285 |
return f"Base64 decoded: {decoded}"
|
286 |
-
except:
|
287 |
-
return "
|
288 |
|
|
|
289 |
if '%' in text:
|
290 |
try:
|
291 |
decoded = urllib.parse.unquote(text)
|
292 |
return f"URL decoded: {decoded}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
293 |
except:
|
294 |
-
|
295 |
|
296 |
-
return f"No encoding
|
297 |
|
298 |
elif operation == "extract_numbers":
|
299 |
patterns = {
|
@@ -301,39 +418,60 @@ def text_processor(text: str, operation: str = "analyze") -> str:
|
|
301 |
'decimals': re.findall(r'\b\d+\.\d+\b', text),
|
302 |
'years': re.findall(r'\b(19|20)\d{2}\b', text),
|
303 |
'percentages': re.findall(r'\b\d+(?:\.\d+)?%', text),
|
304 |
-
'currencies': re.findall(r'\$[\d,]+(?:\.\d{2})?', text)
|
|
|
|
|
305 |
}
|
306 |
|
307 |
result = "EXTRACTED NUMBERS:\n"
|
308 |
for category, matches in patterns.items():
|
309 |
if matches:
|
310 |
-
|
|
|
311 |
|
312 |
-
return result
|
313 |
|
314 |
elif operation == "parse":
|
315 |
words = text.split()
|
316 |
sentences = re.split(r'[.!?]+', text)
|
|
|
317 |
|
318 |
analysis = f"TEXT ANALYSIS:\n"
|
319 |
analysis += f"Character count: {len(text)}\n"
|
320 |
analysis += f"Word count: {len(words)}\n"
|
321 |
-
analysis += f"Sentence count: {len(
|
322 |
|
323 |
if words:
|
324 |
-
analysis += f"First word: {words[0]}\n"
|
325 |
-
analysis += f"Last word: {words[-1]}\n"
|
326 |
-
analysis += f"Longest word: {max(words, key=len)}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
|
|
|
328 |
if re.search(r'[А-Яа-я]', text):
|
329 |
-
analysis += "Cyrillic characters detected (Russian/Slavic)\n"
|
330 |
-
|
331 |
-
analysis += "Extended Latin characters detected\n"
|
|
|
|
|
|
|
|
|
332 |
|
333 |
return analysis
|
334 |
|
335 |
-
else:
|
336 |
-
|
|
|
|
|
337 |
|
338 |
except Exception as e:
|
339 |
return f"Text processing error: {str(e)}"
|
@@ -341,90 +479,185 @@ def text_processor(text: str, operation: str = "analyze") -> str:
|
|
341 |
|
342 |
@tool
|
343 |
def math_solver(problem: str) -> str:
|
344 |
-
"""Advanced mathematical problem solver with
|
345 |
|
346 |
Args:
|
347 |
problem (str): Mathematical problem or structure to analyze.
|
348 |
|
349 |
Returns:
|
350 |
-
str: Mathematical analysis and solution
|
351 |
"""
|
352 |
try:
|
353 |
problem_lower = problem.lower()
|
354 |
|
355 |
if "commutative" in problem_lower:
|
356 |
-
return """COMMUTATIVITY ANALYSIS:
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
363 |
|
364 |
elif "chess" in problem_lower:
|
365 |
-
return """CHESS ANALYSIS FRAMEWORK:
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
417 |
|
418 |
else:
|
|
|
419 |
numbers = re.findall(r'-?\d+(?:\.\d+)?', problem)
|
|
|
|
|
|
|
420 |
if numbers:
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
Consider: arithmetic operations, algebraic manipulation,
|
425 |
-
pattern recognition, or formula application"""
|
426 |
|
427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
|
429 |
except Exception as e:
|
430 |
return f"Math solver error: {str(e)}"
|
|
|
22 |
|
23 |
@tool
|
24 |
def serper_search(query: str) -> str:
|
25 |
+
"""Enhanced web search using Serper API with comprehensive result processing.
|
26 |
|
27 |
Args:
|
28 |
query (str): The search query to be executed.
|
29 |
|
30 |
Returns:
|
31 |
+
str: Detailed search results with structured information.
|
32 |
"""
|
33 |
try:
|
34 |
api_key = os.getenv("SERPER_API_KEY")
|
|
|
36 |
return "SERPER_API_KEY environment variable not found"
|
37 |
|
38 |
url = "https://google.serper.dev/search"
|
39 |
+
payload = json.dumps({
|
40 |
+
"q": query,
|
41 |
+
"num": 12,
|
42 |
+
"hl": "en",
|
43 |
+
"gl": "us"
|
44 |
+
})
|
45 |
headers = {
|
46 |
'X-API-KEY': api_key,
|
47 |
'Content-Type': 'application/json'
|
48 |
}
|
49 |
+
|
50 |
response = requests.post(url, headers=headers, data=payload, timeout=30)
|
51 |
response.raise_for_status()
|
52 |
|
53 |
data = response.json()
|
54 |
results = []
|
55 |
|
56 |
+
# Knowledge Graph extraction
|
57 |
if 'knowledgeGraph' in data:
|
58 |
kg = data['knowledgeGraph']
|
59 |
+
kg_info = f"KNOWLEDGE GRAPH:\nTitle: {kg.get('title', 'N/A')}\nDescription: {kg.get('description', 'N/A')}"
|
60 |
+
|
61 |
+
if 'attributes' in kg and kg['attributes']:
|
62 |
+
kg_info += "\nKey Facts:"
|
63 |
+
for key, value in list(kg['attributes'].items())[:5]:
|
64 |
+
kg_info += f"\n• {key}: {value}"
|
65 |
+
|
66 |
+
if 'entityType' in kg:
|
67 |
+
kg_info += f"\nType: {kg['entityType']}"
|
68 |
+
|
69 |
results.append(kg_info + "\n")
|
70 |
|
71 |
+
# Organic search results
|
72 |
if 'organic' in data:
|
73 |
+
for i, item in enumerate(data['organic'][:8]):
|
74 |
+
title = item.get('title', 'No title')
|
75 |
+
snippet = item.get('snippet', 'No snippet')
|
76 |
+
link = item.get('link', 'No link')
|
|
|
77 |
|
78 |
+
result_text = f"RESULT {i+1}:\nTitle: {title}\nSnippet: {snippet}\nURL: {link}"
|
79 |
+
|
80 |
+
# Extract specific data patterns
|
81 |
+
if re.search(r'\b(19|20)\d{2}\b', snippet):
|
82 |
years = re.findall(r'\b(19|20)\d{2}\b', snippet)
|
83 |
+
result_text += f"\nYears mentioned: {', '.join(set(years))}"
|
84 |
+
|
85 |
+
if re.search(r'\$[\d,]+(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:million|billion|thousand)', snippet, re.IGNORECASE):
|
86 |
+
amounts = re.findall(r'\$[\d,]+(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:million|billion|thousand)', snippet, re.IGNORECASE)
|
87 |
+
result_text += f"\nAmounts: {', '.join(amounts[:3])}"
|
88 |
|
89 |
+
if re.search(r'\b\d+(?:\.\d+)?\s*(?:albums?|songs?|tracks?|records?)\b', snippet, re.IGNORECASE):
|
90 |
+
music_counts = re.findall(r'\b\d+(?:\.\d+)?\s*(?:albums?|songs?|tracks?|records?)\b', snippet, re.IGNORECASE)
|
91 |
+
result_text += f"\nMusic counts: {', '.join(music_counts[:3])}"
|
|
|
92 |
|
93 |
results.append(result_text)
|
94 |
|
95 |
+
# People Also Ask section
|
96 |
if 'peopleAlsoAsk' in data:
|
97 |
+
paa = "\nPEOPLE ALSO ASK:"
|
98 |
+
for item in data['peopleAlsoAsk'][:4]:
|
99 |
+
question = item.get('question', '')
|
100 |
+
answer = item.get('snippet', '')
|
101 |
+
paa += f"\nQ: {question}\nA: {answer[:150]}..."
|
102 |
results.append(paa)
|
103 |
|
104 |
+
# News results if available
|
105 |
+
if 'news' in data:
|
106 |
+
news_section = "\nNEWS RESULTS:"
|
107 |
+
for item in data['news'][:3]:
|
108 |
+
title = item.get('title', '')
|
109 |
+
snippet = item.get('snippet', '')
|
110 |
+
date = item.get('date', '')
|
111 |
+
news_section += f"\n• {title} ({date}): {snippet[:100]}..."
|
112 |
+
results.append(news_section)
|
113 |
+
|
114 |
+
return "\n\n".join(results) if results else "No search results found"
|
115 |
|
116 |
except Exception as e:
|
117 |
return f"Search error: {str(e)}"
|
|
|
119 |
|
120 |
@tool
|
121 |
def wikipedia_search(query: str) -> str:
|
122 |
+
"""Comprehensive Wikipedia search with multiple API endpoints.
|
123 |
|
124 |
Args:
|
125 |
+
query (str): Wikipedia search query.
|
126 |
|
127 |
Returns:
|
128 |
+
str: Detailed Wikipedia information.
|
129 |
"""
|
130 |
try:
|
131 |
results = []
|
132 |
+
|
133 |
+
# Direct page lookup
|
134 |
+
clean_query = urllib.parse.quote(query.replace(" ", "_"))
|
135 |
direct_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{clean_query}"
|
136 |
|
137 |
try:
|
|
|
139 |
if response.status_code == 200:
|
140 |
data = response.json()
|
141 |
if data.get('type') != 'disambiguation':
|
142 |
+
summary = f"WIKIPEDIA DIRECT MATCH:\nTitle: {data.get('title', 'N/A')}"
|
143 |
+
extract = data.get('extract', '')
|
144 |
+
summary += f"\nExtract: {extract}"
|
145 |
+
|
146 |
+
# Extract key dates and facts
|
147 |
+
if extract:
|
148 |
+
birth_dates = re.findall(r'born[^)]*?(\d{1,2}\s+\w+\s+\d{4})', extract, re.IGNORECASE)
|
149 |
+
if birth_dates:
|
150 |
+
summary += f"\nBirth: {birth_dates[0]}"
|
151 |
+
|
152 |
+
death_dates = re.findall(r'died[^)]*?(\d{1,2}\s+\w+\s+\d{4})', extract, re.IGNORECASE)
|
153 |
+
if death_dates:
|
154 |
+
summary += f"\nDeath: {death_dates[0]}"
|
155 |
+
|
156 |
+
# Extract discography info
|
157 |
+
album_counts = re.findall(r'(\d+)\s+(?:studio\s+)?albums?', extract, re.IGNORECASE)
|
158 |
+
if album_counts:
|
159 |
+
summary += f"\nAlbums mentioned: {', '.join(album_counts)}"
|
160 |
|
161 |
if 'coordinates' in data:
|
162 |
coords = data['coordinates']
|
163 |
+
summary += f"\nCoordinates: {coords.get('lat', '')}, {coords.get('lon', '')}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
|
165 |
results.append(summary)
|
166 |
except:
|
167 |
pass
|
168 |
|
169 |
+
# Search API
|
170 |
search_url = "https://en.wikipedia.org/w/api.php"
|
171 |
search_params = {
|
172 |
"action": "query",
|
173 |
"format": "json",
|
174 |
"list": "search",
|
175 |
"srsearch": query,
|
176 |
+
"srlimit": 8,
|
177 |
+
"srprop": "snippet|titlesnippet|size|wordcount"
|
178 |
}
|
179 |
|
180 |
try:
|
|
|
182 |
data = response.json()
|
183 |
|
184 |
if 'query' in data and 'search' in data['query']:
|
185 |
+
search_results = "WIKIPEDIA SEARCH RESULTS:"
|
186 |
+
for i, item in enumerate(data['query']['search']):
|
187 |
+
title = item.get('title', '')
|
188 |
snippet = re.sub(r'<[^>]+>', '', item.get('snippet', ''))
|
189 |
+
wordcount = item.get('wordcount', 0)
|
190 |
+
|
191 |
+
search_results += f"\n{i+1}. {title} ({wordcount} words)"
|
192 |
+
if snippet:
|
193 |
+
search_results += f"\n {snippet[:200]}..."
|
194 |
+
|
195 |
results.append(search_results)
|
196 |
except:
|
197 |
pass
|
198 |
|
199 |
+
# Category search for specific topics
|
200 |
+
if any(term in query.lower() for term in ['dinosaur', 'paleontology', 'fossil']):
|
201 |
+
try:
|
202 |
+
category_params = {
|
203 |
+
"action": "query",
|
204 |
+
"format": "json",
|
205 |
+
"list": "categorymembers",
|
206 |
+
"cmtitle": "Category:Dinosaurs",
|
207 |
+
"cmlimit": 5
|
208 |
+
}
|
209 |
+
response = requests.get(search_url, params=category_params, timeout=10)
|
210 |
+
cat_data = response.json()
|
211 |
+
|
212 |
+
if 'query' in cat_data and 'categorymembers' in cat_data['query']:
|
213 |
+
cat_results = "\nDINOSAUR CATEGORY RESULTS:"
|
214 |
+
for item in cat_data['query']['categorymembers']:
|
215 |
+
cat_results += f"\n• {item.get('title', '')}"
|
216 |
+
results.append(cat_results)
|
217 |
+
except:
|
218 |
+
pass
|
219 |
|
220 |
+
return "\n\n".join(results) if results else "No Wikipedia results found"
|
221 |
|
222 |
except Exception as e:
|
223 |
return f"Wikipedia search error: {str(e)}"
|
|
|
225 |
|
226 |
@tool
|
227 |
def youtube_analyzer(url: str) -> str:
|
228 |
+
"""Advanced YouTube video analyzer with transcript and metadata extraction.
|
229 |
|
230 |
Args:
|
231 |
url (str): YouTube video URL to analyze.
|
|
|
234 |
str: Comprehensive video analysis.
|
235 |
"""
|
236 |
try:
|
237 |
+
# Extract video ID
|
238 |
video_id_match = re.search(r'(?:v=|/|youtu\.be/)([A-Za-z0-9_-]{11})', url)
|
239 |
if not video_id_match:
|
240 |
return "Invalid YouTube URL format"
|
|
|
242 |
video_id = video_id_match.group(1)
|
243 |
results = []
|
244 |
|
245 |
+
# Basic video info via oEmbed
|
246 |
try:
|
247 |
oembed_url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
|
248 |
response = requests.get(oembed_url, timeout=15)
|
249 |
|
250 |
if response.status_code == 200:
|
251 |
data = response.json()
|
252 |
+
basic_info = f"VIDEO METADATA:\nTitle: {data.get('title', 'N/A')}\nAuthor: {data.get('author_name', 'N/A')}"
|
253 |
|
254 |
+
# Extract duration from title if mentioned
|
255 |
title = data.get('title', '').lower()
|
256 |
+
duration_patterns = [
|
257 |
+
r'(\d+)\s*(?:minutes?|mins?)',
|
258 |
+
r'(\d+)\s*(?:hours?|hrs?)',
|
259 |
+
r'(\d+:\d+)'
|
260 |
+
]
|
261 |
+
|
262 |
+
for pattern in duration_patterns:
|
263 |
+
duration_match = re.search(pattern, title)
|
264 |
if duration_match:
|
265 |
+
basic_info += f"\nDuration mentioned in title: {duration_match.group(1)}"
|
266 |
+
break
|
267 |
|
268 |
results.append(basic_info)
|
269 |
+
except Exception as e:
|
270 |
+
results.append(f"oEmbed error: {str(e)}")
|
271 |
|
272 |
+
# Enhanced page scraping
|
273 |
try:
|
|
|
274 |
headers = {
|
275 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
276 |
+
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
277 |
+
'Accept-Language': 'en-US,en;q=0.5',
|
278 |
+
'Accept-Encoding': 'gzip, deflate',
|
279 |
+
'Connection': 'keep-alive',
|
280 |
+
'Upgrade-Insecure-Requests': '1'
|
281 |
}
|
282 |
|
283 |
+
video_url = f"https://www.youtube.com/watch?v={video_id}"
|
284 |
+
response = requests.get(video_url, headers=headers, timeout=25)
|
285 |
+
|
286 |
if response.status_code == 200:
|
287 |
content = response.text
|
288 |
|
289 |
+
# Extract view count
|
290 |
+
view_patterns = [
|
291 |
+
r'"viewCount":"(\d+)"',
|
292 |
+
r'"viewCount":{"simpleText":"([\d,]+)\s+views"}'
|
293 |
+
]
|
294 |
|
295 |
+
for pattern in view_patterns:
|
296 |
+
view_match = re.search(pattern, content)
|
297 |
+
if view_match:
|
298 |
+
views = view_match.group(1).replace(',', '')
|
299 |
+
try:
|
300 |
+
view_count = int(views)
|
301 |
+
results.append(f"VIEW COUNT: {view_count:,}")
|
302 |
+
except:
|
303 |
+
results.append(f"VIEW COUNT: {views}")
|
304 |
+
break
|
305 |
|
306 |
+
# Extract upload date
|
307 |
+
upload_patterns = [
|
308 |
+
r'"uploadDate":"([^"]+)"',
|
309 |
+
r'"publishDate":"([^"]+)"'
|
310 |
+
]
|
311 |
|
312 |
+
for pattern in upload_patterns:
|
313 |
+
upload_match = re.search(pattern, content)
|
314 |
+
if upload_match:
|
315 |
+
results.append(f"UPLOAD DATE: {upload_match.group(1)}")
|
316 |
+
break
|
317 |
|
318 |
+
# Extract exact duration
|
319 |
+
duration_match = re.search(r'"lengthSeconds":"(\d+)"', content)
|
320 |
if duration_match:
|
321 |
+
seconds = int(duration_match.group(1))
|
322 |
+
minutes = seconds // 60
|
323 |
+
secs = seconds % 60
|
324 |
+
results.append(f"DURATION: {minutes}:{secs:02d} ({seconds} seconds)")
|
325 |
|
326 |
+
# Enhanced description extraction
|
327 |
desc_patterns = [
|
328 |
r'"description":{"simpleText":"([^"]+)"}',
|
329 |
+
r'"shortDescription":"([^"]+)"',
|
330 |
+
r'"attributedDescription":{"content":"([^"]+)"}'
|
331 |
]
|
332 |
|
333 |
for pattern in desc_patterns:
|
334 |
desc_match = re.search(pattern, content)
|
335 |
if desc_match:
|
336 |
+
description = desc_match.group(1)
|
337 |
+
# Look for specific content patterns
|
338 |
+
if 'bird' in description.lower():
|
339 |
+
bird_numbers = re.findall(r'\b(\d+)\s+(?:bird|species|individual)', description.lower())
|
340 |
+
if bird_numbers:
|
341 |
+
results.append(f"BIRD COUNTS IN DESCRIPTION: {', '.join(bird_numbers)}")
|
342 |
+
|
343 |
+
results.append(f"DESCRIPTION EXCERPT: {description[:300]}...")
|
344 |
break
|
345 |
+
|
346 |
+
# Look for transcript indicators
|
347 |
+
if 'transcript' in content.lower() or 'captions' in content.lower():
|
348 |
+
results.append("TRANSCRIPT: Available (captions detected)")
|
349 |
+
|
350 |
+
# Extract channel info
|
351 |
+
channel_match = re.search(r'"author":"([^"]+)"', content)
|
352 |
+
if channel_match:
|
353 |
+
results.append(f"CHANNEL: {channel_match.group(1)}")
|
354 |
|
355 |
except Exception as e:
|
356 |
+
results.append(f"Enhanced scraping error: {str(e)}")
|
357 |
+
|
358 |
+
# Attempt to find related content
|
359 |
+
try:
|
360 |
+
search_query = f"site:youtube.com \"{video_id}\" transcript OR captions OR subtitles"
|
361 |
+
# This would be handled by the main search function
|
362 |
+
results.append(f"SEARCH SUGGESTION: {search_query}")
|
363 |
+
except:
|
364 |
+
pass
|
365 |
|
366 |
return "\n".join(results) if results else "Could not analyze video"
|
367 |
|
|
|
371 |
|
372 |
@tool
|
373 |
def text_processor(text: str, operation: str = "analyze") -> str:
|
374 |
+
"""Advanced text processing with multiple linguistic operations.
|
375 |
|
376 |
Args:
|
377 |
text (str): Text to process.
|
378 |
+
operation (str): Operation type (reverse, decode, analyze, extract_numbers, parse).
|
|
|
379 |
|
380 |
Returns:
|
381 |
str: Processed text results.
|
|
|
385 |
return text[::-1]
|
386 |
|
387 |
elif operation == "decode":
|
388 |
+
# Base64 decoding
|
389 |
if text.startswith("base64:"):
|
390 |
try:
|
391 |
decoded = base64.b64decode(text[7:]).decode('utf-8')
|
392 |
return f"Base64 decoded: {decoded}"
|
393 |
+
except Exception as e:
|
394 |
+
return f"Base64 decode failed: {str(e)}"
|
395 |
|
396 |
+
# URL decoding
|
397 |
if '%' in text:
|
398 |
try:
|
399 |
decoded = urllib.parse.unquote(text)
|
400 |
return f"URL decoded: {decoded}"
|
401 |
+
except Exception as e:
|
402 |
+
return f"URL decode failed: {str(e)}"
|
403 |
+
|
404 |
+
# Hex decoding
|
405 |
+
if re.match(r'^[0-9a-fA-F]+$', text.replace(' ', '')):
|
406 |
+
try:
|
407 |
+
hex_text = text.replace(' ', '')
|
408 |
+
decoded = bytes.fromhex(hex_text).decode('utf-8')
|
409 |
+
return f"Hex decoded: {decoded}"
|
410 |
except:
|
411 |
+
pass
|
412 |
|
413 |
+
return f"No recognized encoding in: {text[:100]}"
|
414 |
|
415 |
elif operation == "extract_numbers":
|
416 |
patterns = {
|
|
|
418 |
'decimals': re.findall(r'\b\d+\.\d+\b', text),
|
419 |
'years': re.findall(r'\b(19|20)\d{2}\b', text),
|
420 |
'percentages': re.findall(r'\b\d+(?:\.\d+)?%', text),
|
421 |
+
'currencies': re.findall(r'\$[\d,]+(?:\.\d{2})?', text),
|
422 |
+
'ranges': re.findall(r'\b\d+[-–]\d+\b', text),
|
423 |
+
'ordinals': re.findall(r'\b\d+(?:st|nd|rd|th)\b', text, re.IGNORECASE)
|
424 |
}
|
425 |
|
426 |
result = "EXTRACTED NUMBERS:\n"
|
427 |
for category, matches in patterns.items():
|
428 |
if matches:
|
429 |
+
unique_matches = list(set(matches))
|
430 |
+
result += f"{category.title()}: {', '.join(unique_matches)}\n"
|
431 |
|
432 |
+
return result if any(patterns.values()) else "No numbers found"
|
433 |
|
434 |
elif operation == "parse":
|
435 |
words = text.split()
|
436 |
sentences = re.split(r'[.!?]+', text)
|
437 |
+
clean_sentences = [s.strip() for s in sentences if s.strip()]
|
438 |
|
439 |
analysis = f"TEXT ANALYSIS:\n"
|
440 |
analysis += f"Character count: {len(text)}\n"
|
441 |
analysis += f"Word count: {len(words)}\n"
|
442 |
+
analysis += f"Sentence count: {len(clean_sentences)}\n"
|
443 |
|
444 |
if words:
|
445 |
+
analysis += f"First word: '{words[0]}'\n"
|
446 |
+
analysis += f"Last word: '{words[-1]}'\n"
|
447 |
+
analysis += f"Longest word: '{max(words, key=len)}' ({len(max(words, key=len))} chars)\n"
|
448 |
+
|
449 |
+
# Word frequency
|
450 |
+
word_freq = {}
|
451 |
+
for word in words:
|
452 |
+
word_lower = word.lower().strip('.,!?";')
|
453 |
+
word_freq[word_lower] = word_freq.get(word_lower, 0) + 1
|
454 |
+
|
455 |
+
if word_freq:
|
456 |
+
most_common = max(word_freq.items(), key=lambda x: x[1])
|
457 |
+
analysis += f"Most frequent word: '{most_common[0]}' ({most_common[1]} times)\n"
|
458 |
|
459 |
+
# Language detection patterns
|
460 |
if re.search(r'[А-Яа-я]', text):
|
461 |
+
analysis += "Language: Cyrillic characters detected (Russian/Slavic)\n"
|
462 |
+
elif re.search(r'[À-ÿ]', text):
|
463 |
+
analysis += "Language: Extended Latin characters detected\n"
|
464 |
+
elif re.search(r'[一-龯]', text):
|
465 |
+
analysis += "Language: Chinese characters detected\n"
|
466 |
+
else:
|
467 |
+
analysis += "Language: Appears to be English/Latin script\n"
|
468 |
|
469 |
return analysis
|
470 |
|
471 |
+
else: # default analyze
|
472 |
+
length = len(text)
|
473 |
+
preview = text[:200] + ('...' if length > 200 else '')
|
474 |
+
return f"TEXT PREVIEW:\nLength: {length} characters\nContent: {preview}"
|
475 |
|
476 |
except Exception as e:
|
477 |
return f"Text processing error: {str(e)}"
|
|
|
479 |
|
480 |
@tool
|
481 |
def math_solver(problem: str) -> str:
|
482 |
+
"""Advanced mathematical problem solver with domain-specific strategies.
|
483 |
|
484 |
Args:
|
485 |
problem (str): Mathematical problem or structure to analyze.
|
486 |
|
487 |
Returns:
|
488 |
+
str: Mathematical analysis and solution guidance.
|
489 |
"""
|
490 |
try:
|
491 |
problem_lower = problem.lower()
|
492 |
|
493 |
if "commutative" in problem_lower:
|
494 |
+
return """COMMUTATIVITY ANALYSIS GUIDE:
|
495 |
+
For operation * on set S to be commutative, a*b = b*a must hold for ALL pairs (a,b).
|
496 |
+
|
497 |
+
SYSTEMATIC CHECK METHOD:
|
498 |
+
1. Create operation table if not given
|
499 |
+
2. For each entry (i,j), check if it equals entry (j,i)
|
500 |
+
3. The table should be symmetric across the main diagonal
|
501 |
+
4. If ANY single pair fails, operation is NOT commutative
|
502 |
+
|
503 |
+
COMMON COUNTEREXAMPLE PATTERNS:
|
504 |
+
- Look for asymmetric entries: if a*b ≠ b*a
|
505 |
+
- Check corner cases and boundary elements
|
506 |
+
- Pay attention to identity elements and inverses
|
507 |
+
- Matrix multiplication is classic non-commutative example
|
508 |
+
|
509 |
+
TO PROVE NON-COMMUTATIVITY: Find ONE counterexample where a*b ≠ b*a
|
510 |
+
TO PROVE COMMUTATIVITY: Verify ALL pairs satisfy a*b = b*a"""
|
511 |
|
512 |
elif "chess" in problem_lower:
|
513 |
+
return """CHESS POSITION ANALYSIS FRAMEWORK:
|
514 |
+
|
515 |
+
IMMEDIATE ASSESSMENT:
|
516 |
+
1. Check for checks/threats to both kings
|
517 |
+
2. Identify all possible legal moves
|
518 |
+
3. Look for immediate tactical opportunities
|
519 |
+
|
520 |
+
TACTICAL PATTERNS TO EXAMINE:
|
521 |
+
- Pins: pieces unable to move due to exposing king/valuable piece
|
522 |
+
- Forks: single piece attacking multiple targets
|
523 |
+
- Skewers: forcing valuable piece to move, exposing less valuable one
|
524 |
+
- Discovered attacks: moving one piece reveals attack from another
|
525 |
+
- Double attacks: attacking two targets simultaneously
|
526 |
+
|
527 |
+
STRATEGIC CONSIDERATIONS:
|
528 |
+
- King safety and escape squares
|
529 |
+
- Piece activity and coordination
|
530 |
+
- Control of key squares (center, weak squares)
|
531 |
+
- Pawn structure advantages/disadvantages
|
532 |
+
- Material balance and exchanges
|
533 |
+
|
534 |
+
MOVE EVALUATION PRIORITY:
|
535 |
+
1. Forced moves (checks, captures, threats)
|
536 |
+
2. Tactical shots (combinations)
|
537 |
+
3. Improving piece positions
|
538 |
+
4. Prophylactic moves (preventing opponent threats)"""
|
539 |
+
|
540 |
+
elif any(term in problem_lower for term in ["prime", "factor", "divisible", "gcd", "lcm"]):
|
541 |
+
return """NUMBER THEORY PROBLEM SOLVING:
|
542 |
+
|
543 |
+
PRIMALITY TESTING:
|
544 |
+
- Check divisibility by primes up to √n
|
545 |
+
- Use divisibility rules (2,3,5,7,11...)
|
546 |
+
- For large numbers, use probabilistic tests
|
547 |
+
|
548 |
+
FACTORIZATION STRATEGIES:
|
549 |
+
1. Trial division by small primes
|
550 |
+
2. Look for perfect square factors
|
551 |
+
3. Use difference of squares: a² - b² = (a+b)(a-b)
|
552 |
+
4. Check for patterns in number sequences
|
553 |
+
|
554 |
+
GCD/LCM PROBLEMS:
|
555 |
+
- Use Euclidean algorithm for GCD
|
556 |
+
- LCM = (a×b)/GCD(a,b)
|
557 |
+
- Prime factorization method for multiple numbers
|
558 |
+
|
559 |
+
MODULAR ARITHMETIC:
|
560 |
+
- Use when dealing with remainders
|
561 |
+
- Fermat's Little Theorem for prime moduli
|
562 |
+
- Chinese Remainder Theorem for system of congruences"""
|
563 |
+
|
564 |
+
elif any(term in problem_lower for term in ["triangle", "circle", "area", "volume", "angle", "geometry"]):
|
565 |
+
return """GEOMETRY PROBLEM SOLVING APPROACH:
|
566 |
+
|
567 |
+
VISUALIZATION:
|
568 |
+
1. Draw accurate diagram if possible
|
569 |
+
2. Mark known values and unknowns
|
570 |
+
3. Identify geometric relationships
|
571 |
+
|
572 |
+
KEY FORMULAS TO CONSIDER:
|
573 |
+
- Triangle: Area = ½bh, Pythagorean theorem
|
574 |
+
- Circle: Area = πr², Circumference = 2πr
|
575 |
+
- Volume formulas for 3D shapes
|
576 |
+
- Trigonometric ratios (SOH-CAH-TOA)
|
577 |
+
|
578 |
+
SOLUTION STRATEGIES:
|
579 |
+
1. Similar triangles and proportions
|
580 |
+
2. Coordinate geometry when helpful
|
581 |
+
3. Law of sines/cosines for non-right triangles
|
582 |
+
4. Circle theorems and properties
|
583 |
+
5. Symmetry and transformation properties
|
584 |
+
|
585 |
+
COMMON TECHNIQUES:
|
586 |
+
- Auxiliary lines and constructions
|
587 |
+
- Angle chasing in polygons
|
588 |
+
- Using properties of special triangles (30-60-90, 45-45-90)"""
|
589 |
+
|
590 |
+
elif any(term in problem_lower for term in ["probability", "statistics", "combination", "permutation"]):
|
591 |
+
return """PROBABILITY & STATISTICS SOLUTION GUIDE:
|
592 |
+
|
593 |
+
PROBABILITY FUNDAMENTALS:
|
594 |
+
- P(A) = favorable outcomes / total outcomes
|
595 |
+
- P(A or B) = P(A) + P(B) - P(A and B)
|
596 |
+
- P(A and B) = P(A) × P(B|A) for dependent events
|
597 |
+
- P(A and B) = P(A) × P(B) for independent events
|
598 |
+
|
599 |
+
COUNTING PRINCIPLES:
|
600 |
+
- Permutations: P(n,r) = n!/(n-r)! (order matters)
|
601 |
+
- Combinations: C(n,r) = n!/(r!(n-r)!) (order doesn't matter)
|
602 |
+
- Multiplication principle for sequential choices
|
603 |
+
|
604 |
+
STATISTICS MEASURES:
|
605 |
+
- Mean: sum of values / count
|
606 |
+
- Median: middle value when ordered
|
607 |
+
- Mode: most frequent value
|
608 |
+
- Standard deviation: measure of spread
|
609 |
+
|
610 |
+
COMMON PROBLEM TYPES:
|
611 |
+
- Conditional probability (Bayes' theorem)
|
612 |
+
- Binomial distribution
|
613 |
+
- Normal distribution applications"""
|
614 |
+
|
615 |
+
elif any(term in problem_lower for term in ["sequence", "series", "pattern", "recursive"]):
|
616 |
+
return """SEQUENCE & PATTERN ANALYSIS:
|
617 |
+
|
618 |
+
PATTERN IDENTIFICATION:
|
619 |
+
1. Look for arithmetic progression: constant difference
|
620 |
+
2. Check for geometric progression: constant ratio
|
621 |
+
3. Examine polynomial patterns (quadratic, cubic)
|
622 |
+
4. Consider Fibonacci-type recursive relations
|
623 |
+
|
624 |
+
ANALYSIS METHODS:
|
625 |
+
- First differences, second differences
|
626 |
+
- Ratio between consecutive terms
|
627 |
+
- Look for alternating patterns
|
628 |
+
- Check for periodic behavior
|
629 |
+
|
630 |
+
COMMON SEQUENCES:
|
631 |
+
- Arithmetic: a, a+d, a+2d, ...
|
632 |
+
- Geometric: a, ar, ar², ...
|
633 |
+
- Quadratic: differences form arithmetic sequence
|
634 |
+
- Fibonacci: F(n) = F(n-1) + F(n-2)
|
635 |
+
|
636 |
+
FORMULA DERIVATION:
|
637 |
+
- Use known formulas for standard sequences
|
638 |
+
- Set up recurrence relations
|
639 |
+
- Use generating functions for complex patterns"""
|
640 |
|
641 |
else:
|
642 |
+
# Extract numbers and suggest general approach
|
643 |
numbers = re.findall(r'-?\d+(?:\.\d+)?', problem)
|
644 |
+
operations = re.findall(r'[+\-*/^=<>]', problem)
|
645 |
+
|
646 |
+
analysis = f"GENERAL MATHEMATICAL ANALYSIS:\n"
|
647 |
if numbers:
|
648 |
+
analysis += f"Numbers identified: {', '.join(numbers)}\n"
|
649 |
+
if operations:
|
650 |
+
analysis += f"Operations found: {', '.join(set(operations))}\n"
|
|
|
|
|
651 |
|
652 |
+
analysis += f"\nProblem excerpt: {problem[:150]}...\n"
|
653 |
+
analysis += "\nSUGGESTED APPROACH:\n"
|
654 |
+
analysis += "1. Identify the mathematical domain (algebra, geometry, etc.)\n"
|
655 |
+
analysis += "2. List known information and what needs to be found\n"
|
656 |
+
analysis += "3. Apply relevant formulas and theorems\n"
|
657 |
+
analysis += "4. Work step-by-step with clear reasoning\n"
|
658 |
+
analysis += "5. Verify the solution makes sense"
|
659 |
+
|
660 |
+
return analysis
|
661 |
|
662 |
except Exception as e:
|
663 |
return f"Math solver error: {str(e)}"
|