AlanRocha commited on
Commit
20b36c4
·
verified ·
1 Parent(s): bfe529a

Create tools.py

Browse files
Files changed (1) hide show
  1. tools/tools.py +261 -0
tools/tools.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import numpy
3
+ import tempfile
4
+ import requests
5
+ import whisper
6
+ import imageio
7
+ import yt_dlp
8
+
9
+ from PIL import Image
10
+ from typing import List, Optional
11
+ from urllib.parse import urlparse
12
+ from dotenv import load_dotenv
13
+ from smolagents import tool, LiteLLMModel
14
+ import google.generativeai as genai
15
+ from pytesseract import image_to_string
16
+
17
+ load_dotenv()
18
+
19
+ MODEL_ID = "gemini/gemini-2.5-flash-preview-05-20"
20
+
21
+ # Vision Tool
22
+ @tool
23
+ def vision_tool(prompt: str, image_list: List[Image.Image]) -> str:
24
+ """
25
+ Analyzes one or more images using a multimodal model.
26
+ Args:
27
+ prompt (str): The user question or task.
28
+ image_list (List[PIL.Image.Image]): A list of image objects.
29
+ Returns:
30
+ str: Model's response to the prompt about the images.
31
+ """
32
+ model = LiteLLMModel(model_id=MODEL_ID, api_key=os.getenv("GEMINI_API"), temperature=0.2)
33
+
34
+ payload = [{"type": "text", "text": prompt}] + [{"type": "image", "image": img} for img in image_list]
35
+ return model([{"role": "user", "content": payload}]).content
36
+
37
+
38
+ # YouTube Frame Sampler
39
+ @tool
40
+ def youtube_frames_to_images(url: str, every_n_seconds: int = 5) -> List[Image.Image]:
41
+ """
42
+ Downloads a YouTube video and extracts frames at regular intervals.
43
+
44
+ Args:
45
+ url (str): The URL of the YouTube video to process.
46
+ every_n_seconds (int): The time interval in seconds between extracted frames.
47
+
48
+ Returns:
49
+ List[Image.Image]: A list of sampled frames as PIL images.
50
+ """
51
+ with tempfile.TemporaryDirectory() as temp_dir:
52
+ ydl_cfg = {
53
+ "format": "bestvideo+bestaudio/best",
54
+ "outtmpl": os.path.join(temp_dir, "yt_video.%(ext)s"),
55
+ "merge_output_format": "mp4",
56
+ "quiet": True,
57
+ "force_ipv4": True
58
+ }
59
+ with yt_dlp.YoutubeDL(ydl_cfg) as ydl:
60
+ ydl.extract_info(url, download=True)
61
+
62
+ video_file = next((os.path.join(temp_dir, f) for f in os.listdir(temp_dir) if f.endswith('.mp4')), None)
63
+ reader = imageio.get_reader(video_file)
64
+ fps = reader.get_meta_data().get("fps", 30)
65
+ interval = int(fps * every_n_seconds)
66
+
67
+ return [Image.fromarray(frame) for i, frame in enumerate(reader) if i % interval == 0]
68
+
69
+
70
+ # YouTube QA via File URI
71
+ @tool
72
+ def ask_youtube_video(url: str, question: str) -> str:
73
+ """
74
+ Sends a YouTube video to a multimodal model and asks a question about it.
75
+
76
+ Args:
77
+ url (str): The URI of the video file (already uploaded and hosted).
78
+ question (str): The natural language question to ask about the video.
79
+
80
+ Returns:
81
+ str: The model's answer to the question.
82
+ """
83
+
84
+ try:
85
+ client = genai.Client(api_key=os.getenv('GEMINI_API'))
86
+ response = client.generate_content(
87
+ model=MODEL_ID,
88
+ contents=[
89
+ {"role": "user", "parts": [
90
+ {"text": question},
91
+ {"file_data": {"file_uri": url}}
92
+ ]}
93
+ ]
94
+ )
95
+ return response.text
96
+ except Exception as e:
97
+ return f"Error asking {MODEL_ID} about video: {str(e)}"
98
+
99
+
100
+ # File Reading Tool
101
+ @tool
102
+ def read_text_file(file_path: str) -> str:
103
+ """
104
+ Reads plain text content from a file.
105
+
106
+ Args:
107
+ file_path (str): The full path to the text file.
108
+
109
+ Returns:
110
+ str: The contents of the file, or an error message.
111
+ """
112
+ try:
113
+ with open(file_path, "r", encoding="utf-8") as f:
114
+ return f.read()
115
+ except Exception as e:
116
+ return f"Error reading file: {e}"
117
+
118
+
119
+ # File Downloader
120
+ @tool
121
+ def file_from_url(url: str, save_as: Optional[str] = None) -> str:
122
+ """
123
+ Downloads a file from a URL and saves it locally.
124
+
125
+ Args:
126
+ url (str): The URL of the file to download.
127
+ save_as (Optional[str]): Optional filename to save the file as.
128
+
129
+ Returns:
130
+ str: The local file path or an error message.
131
+ """
132
+ try:
133
+ if not save_as:
134
+ parsed = urlparse(url)
135
+ save_as = os.path.basename(parsed.path) or f"file_{os.urandom(4).hex()}"
136
+
137
+ file_path = os.path.join(tempfile.gettempdir(), save_as)
138
+ response = requests.get(url, stream=True)
139
+ response.raise_for_status()
140
+
141
+ with open(file_path, "wb") as f:
142
+ for chunk in response.iter_content(1024):
143
+ f.write(chunk)
144
+
145
+ return f"File saved to {file_path}"
146
+ except Exception as e:
147
+ return f"Download failed: {e}"
148
+
149
+
150
+ # Audio Transcription (YouTube)
151
+ @tool
152
+ def transcribe_youtube(yt_url: str) -> str:
153
+ """
154
+ Transcribes the audio from a YouTube video using Whisper.
155
+
156
+ Args:
157
+ yt_url (str): The URL of the YouTube video.
158
+
159
+ Returns:
160
+ str: The transcribed text of the video.
161
+ """
162
+ model = whisper.load_model("small")
163
+
164
+ with tempfile.TemporaryDirectory() as tempdir:
165
+ ydl_opts = {
166
+ "format": "bestaudio",
167
+ "outtmpl": os.path.join(tempdir, "audio.%(ext)s"),
168
+ "postprocessors": [{
169
+ "key": "FFmpegExtractAudio",
170
+ "preferredcodec": "wav"
171
+ }],
172
+ "quiet": True,
173
+ "force_ipv4": True
174
+ }
175
+
176
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
177
+ ydl.extract_info(yt_url, download=True)
178
+
179
+ wav_file = next((os.path.join(tempdir, f) for f in os.listdir(tempdir) if f.endswith(".wav")), None)
180
+ return model.transcribe(wav_file)['text']
181
+
182
+
183
+ # Audio File Transcriber
184
+ @tool
185
+ def audio_to_text(audio_path: str) -> str:
186
+ """
187
+ Transcribes an uploaded audio file into text using Whisper.
188
+
189
+ Args:
190
+ audio_path (str): The local file path to the audio file.
191
+
192
+ Returns:
193
+ str: The transcribed text or an error message.
194
+ """
195
+ try:
196
+ model = whisper.load_model("small")
197
+ result = model.transcribe(audio_path)
198
+ return result['text']
199
+ except Exception as e:
200
+ return f"Failed to transcribe: {e}"
201
+
202
+
203
+ # OCR
204
+ @tool
205
+ def extract_text_via_ocr(image_path: str) -> str:
206
+ """
207
+ Extracts text from an image using Optical Character Recognition (OCR).
208
+
209
+ Args:
210
+ image_path (str): The local path to the image file.
211
+
212
+ Returns:
213
+ str: The extracted text or an error message.
214
+ """
215
+ try:
216
+ img = Image.open(image_path)
217
+ return image_to_string(img)
218
+ except Exception as e:
219
+ return f"OCR failed: {e}"
220
+
221
+
222
+ # CSV Analyzer
223
+ @tool
224
+ def summarize_csv_data(path: str, query: str = "") -> str:
225
+ """
226
+ Provides a summary of the contents of a CSV file.
227
+
228
+ Args:
229
+ path (str): The file path to the CSV file.
230
+ query (str): Optional query to run on the data.
231
+
232
+ Returns:
233
+ str: Summary statistics and column details or an error message.
234
+ """
235
+ try:
236
+ import pandas as pd
237
+ df = pd.read_csv(path)
238
+ return f"Loaded CSV with {len(df)} rows. Columns: {list(df.columns)}\n\n{df.describe()}"
239
+ except Exception as e:
240
+ return f"CSV error: {e}"
241
+
242
+
243
+ # Excel Analyzer
244
+ @tool
245
+ def summarize_excel_data(path: str, query: str = "") -> str:
246
+ """
247
+ Provides a summary of the contents of an Excel file.
248
+
249
+ Args:
250
+ path (str): The file path to the Excel file (.xls or .xlsx).
251
+ query (str): Optional query to run on the data.
252
+
253
+ Returns:
254
+ str: Summary statistics and column details or an error message.
255
+ """
256
+ try:
257
+ import pandas as pd
258
+ df = pd.read_excel(path)
259
+ return f"Excel file with {len(df)} rows. Columns: {list(df.columns)}\n\n{df.describe()}"
260
+ except Exception as e:
261
+ return f"Excel error: {e}"