File size: 4,985 Bytes
80480f7 cbf2549 80480f7 cbf2549 d0ba1a4 cbf2549 80480f7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | from smolagents import tool, Tool
from youtube_transcript_api import YouTubeTranscriptApi
from urllib.parse import urlparse, parse_qs
import os
import ast
import operator as op
from typing import Any, Optional
from datetime import datetime
from smolagents import tool
@tool
def youtube_tool(video_url: str) -> str:
"""
Analyzes a YouTube video by extracting its transcript.
Args:
video_url (str): The full URL of the YouTube video.
Returns:
str: The transcript text of the video or an error message.
"""
try:
# Extract video ID from URL
parsed_url = urlparse(video_url)
if parsed_url.hostname == 'youtu.be':
video_id = parsed_url.path[1:]
else:
video_id = parse_qs(parsed_url.query).get('v', [None])[0]
if not video_id:
return "Error: Could not extract video ID from URL."
# Fetch transcript
transcript_list = YouTubeTranscriptApi.fetch(video_id)
# Combine transcript parts into a single string
full_text = " ".join([item['text'] for item in transcript_list])
return full_text
except Exception as e:
return f"Error analyzing YouTube video: {str(e)}"
@tool
def get_current_time() -> str:
"""Get the current date and time in a readable format."""
now = datetime.now()
return now.strftime("%Y-%m-%d %H:%M:%S")
@tool
def calculate_basic_math(expression: str) -> str:
"""
Safely evaluate basic mathematical expressions using AST.
Args:
expression: A string containing a mathematical expression like "2+2" or "10*5"
Returns:
The result of the calculation as a string
"""
# Safe operations mapping
SAFE_OPS = {
ast.Add: op.add,
ast.Sub: op.sub,
ast.Mult: op.mul,
ast.Div: op.truediv,
ast.Pow: op.pow,
ast.USub: op.neg
}
def _safe_eval(node):
"""Recursively evaluate AST nodes safely."""
if isinstance(node, ast.Num): # Numbers
return node.n
elif isinstance(node, ast.Constant): # Python 3.8+ constant nodes
return node.value
elif isinstance(node, ast.BinOp): # Binary operations
return SAFE_OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right))
elif isinstance(node, ast.UnaryOp): # Unary operations
return SAFE_OPS[type(node.op)](_safe_eval(node.operand))
else:
raise ValueError(f"Unsupported operation: {type(node)}")
try:
# Parse expression into AST and evaluate safely
node = ast.parse(expression, mode='eval')
result = _safe_eval(node.body)
return f"Result: {result}"
except Exception as e:
return f"Error calculating '{expression}': {str(e)}"
@tool
def save_note(content: str, filename: Optional[str] = None) -> str:
"""
Save a note to a text file.
Args:
content: The content to save in the note
filename: Optional filename. If not provided, uses timestamp
Returns:
Success message with filename
"""
try:
if filename is None:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"note_{timestamp}.txt"
# Try to create notes directory, fall back to /tmp if permission denied
try:
os.makedirs("notes", exist_ok=True)
notes_dir = "notes"
except PermissionError:
notes_dir = "/tmp/notes"
os.makedirs(notes_dir, exist_ok=True)
filepath = os.path.join(notes_dir, filename)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
return f"✅ Note saved successfully to: {filepath}"
except Exception as e:
return f"❌ Error saving note: {str(e)}"
@tool
def list_saved_notes() -> str:
"""List all saved notes in the notes directory."""
try:
# Check both possible notes directories
all_files = []
for notes_dir in ["notes", "/tmp/notes"]:
if os.path.exists(notes_dir):
files = os.listdir(notes_dir)
txt_files = [f for f in files if f.endswith('.txt')]
all_files.extend([(f, notes_dir) for f in txt_files])
if not all_files:
return "📂 No notes found. Save a note first!"
file_list = "\n".join([f"- {file} ({location})" for file, location in sorted(all_files)])
return f"📂 Saved notes:\n{file_list}"
except Exception as e:
return f"❌ Error listing notes: {str(e)}"
class FinalAnswerTool(Tool):
name = "final_answer"
description = "Provides a final answer to the given problem."
inputs = {'answer': {'type': 'any', 'description': 'The final answer to the problem'}}
output_type = "any"
def forward(self, answer: Any) -> Any:
return answer
def __init__(self, *args, **kwargs):
self.is_initialized = False |