| 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: |
| |
| 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." |
|
|
| |
| transcript_list = YouTubeTranscriptApi.fetch(video_id) |
|
|
| |
| 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_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): |
| return node.n |
| elif isinstance(node, ast.Constant): |
| return node.value |
| elif isinstance(node, ast.BinOp): |
| return SAFE_OPS[type(node.op)](_safe_eval(node.left), _safe_eval(node.right)) |
| elif isinstance(node, ast.UnaryOp): |
| return SAFE_OPS[type(node.op)](_safe_eval(node.operand)) |
| else: |
| raise ValueError(f"Unsupported operation: {type(node)}") |
|
|
| try: |
| |
| 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: |
| 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: |
| |
| 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 |