DrekFretson commited on
Commit
6fc9ff1
·
verified ·
1 Parent(s): 25f88b0

Update tools.py

Browse files
Files changed (1) hide show
  1. tools.py +52 -794
tools.py CHANGED
@@ -1,826 +1,84 @@
1
-
2
- import os
3
- import requests
4
- import tempfile
5
- from langchain.docstore.document import Document
6
- from langchain.tools import Tool, StructuredTool, tool
7
- from typing import Tuple, List
8
- from langchain_core.messages import AIMessage
9
- from langchain_community.tools import DuckDuckGoSearchResults, DuckDuckGoSearchRun
10
- from langchain_experimental.tools.python.tool import PythonREPLTool
11
- from langchain_community.agent_toolkits.playwright.toolkit import PlayWrightBrowserToolkit
12
- from langchain_community.tools.playwright.utils import create_async_playwright_browser
13
- from langchain_community.document_loaders.wikipedia import WikipediaLoader
14
- from langchain_community.document_loaders import WebBaseLoader, PyPDFLoader
15
- from langchain_community.document_loaders.arxiv import ArxivLoader
16
- from langchain_community.document_loaders.text import TextLoader
17
- from langchain_community.document_loaders.word_document import Docx2txtLoader
18
- import yt_dlp
19
- import json
20
- from Bio.PDB import PDBParser
21
  import pandas as pd
22
- from openpyxl import load_workbook
23
- import math
24
- import pdfplumber
25
- from pptx import Presentation
26
- import zipfile
27
- from ratelimit import limits, sleep_and_retry
28
- from tenacity import retry, wait_exponential, stop_after_attempt, retry_if_exception_type
29
- from duckduckgo_search.exceptions import DuckDuckGoSearchException
30
- from duckduckgo_search import DDGS
31
- from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor
32
- from qwen_vl_utils import process_vision_info
33
- import whisper
34
-
35
- model_whisper = whisper.load_model("base")
36
- model_image = Qwen2_5_VLForConditionalGeneration.from_pretrained(
37
- "Qwen/Qwen2.5-VL-3B-Instruct",
38
- torch_dtype="auto",
39
- device_map="auto"
40
- )
41
- processor_image = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct")
42
-
43
- @tool
44
- def add(a: str, b: str) -> str:
45
- """Adds two numbers provided as strings and returns the result as a string."""
46
- return str(float(a) + float(b))
47
 
48
- @tool
49
- def subtract(a: str, b: str) -> str:
50
- """Subtracts the second number from the first, both provided as strings, and returns the result as a string."""
51
- return str(float(a) - float(b))
52
 
53
- @tool
54
- def multiply(a: str, b: str) -> str:
55
- """Multiplies two numbers provided as strings and returns the result as a string."""
56
- return str(float(a) * float(b))
57
 
58
  @tool
59
- def divide(a: str, b: str) -> str:
60
- """Divides the first number by the second, both provided as strings, and returns the result as a string. The divisor must not be zero."""
61
- return str(float(a) / float(b))
62
-
63
- @tool
64
- def power(a: str, b: str) -> str:
65
- """Raises the first number (base) to the power of the second number (exponent), both provided as strings, and returns the result as a string."""
66
- return str(float(a) ** float(b))
67
-
68
- @tool
69
- def square_root(a: str) -> str:
70
- """Calculates the square root of a number provided as a string and returns the result as a string. The number must be non-negative."""
71
- return str(math.sqrt(float(a)))
72
-
73
- @tool
74
- def get_information_from_wikipedia(query: str) -> str:
75
- """
76
- Search for relevant Wikipedia pages based on a user query and return their URLs.
77
- This tool uses WikipediaLoader to retrieve up to 10 Wikipedia articles related to the input query.
78
- The output consists of a formatted list of URLs pointing to these articles. These URLs can be used
79
- by web automation tools or agents to navigate, scrape, and extract valuable information such as
80
- text, tables, infoboxes, images, and references that may help answer the user's question.
81
- Args:
82
- query (str): A user-provided search query.
83
- Returns:
84
- str: A formatted explanation with a list of Wikipedia URLs related to the query.
85
- """
86
- search_docs = WikipediaLoader(query=query, load_max_docs=10).load()
87
- print(search_docs)
88
- content = '\n\n'.join([f"{i}. Title: {doc.metadata['title']}\nSummary: {doc.metadata['summary']}\nSource: {doc.metadata['source']}\n" for i, doc in enumerate(search_docs)])
89
-
90
- return f"""Wikipedia search completed for query: "{query}".
91
- You must now browse the following Wikipedia pages to extract detailed information. Use `navigate_browser` to explore each URL:
92
- Relevant Wikipedia searches:
93
- {content}
94
- Instructions:
95
- - Visit each URL using `navigate_browser`.
96
- - Extract full page content, infoboxes, tables, and references.
97
- - Use hyperlinks from the page to recursively follow relevant internal links if necessary."""
98
-
99
- @tool
100
- def get_information_from_arxiv(query: str) -> str:
101
- """
102
- Searches the arXiv database for academic papers related to a given query and returns up to 3 relevant results,
103
- including source metadata and a preview of each paper's content.
104
- This function queries the arXiv database for papers matching the provided search term. It retrieves up to 3 papers
105
- with content limited to 10,000 characters each. The results are formatted in XML-like structure with metadata
106
- such as the title, authors, and page number (if available).
107
- Args:
108
- query (str): The search term or topic to query on arXiv (e.g., "machine learning").
109
- Returns:
110
- str: A formatted string containing the metadata (title, authors, page number) and content preview
111
- for each of the top 3 relevant papers found on arXiv.
112
- """
113
- search_docs = ArxivLoader(query=query, load_max_docs=3, doc_content_chars_max=10000).load()
114
- content = ""
115
- for doc in search_docs:
116
- content += f"Title: {doc.metadata['Title']}\n"
117
- content += f"Authors: {doc.metadata['Authors']}\n"
118
- content += f"Summary: {doc.metadata['Summary']}\n"
119
- content += f"Published: {doc.metadata['Published']}\n\n"
120
-
121
- return f"""Arxiv search completed for query: "{query}".
122
- Relevant Arxiv papers:
123
- {content}
124
- Instructions:
125
- - Search the paper link throught it's title.
126
- - Visit each URL using `navigate_browser`.
127
- - Extract full page content, infoboxes, tables, and references.
128
- - Use hyperlinks from the page to recursively follow relevant internal links if necessary."""
129
-
130
- @tool
131
- def get_web_page(url: str) -> str:
132
- """
133
- Use this to extract text from basic HTML pages. Works well for static websites without JavaScript.
134
- This function retrieves the content of a webpage at the specified URL and extracts all the visible text.
135
- Args:
136
- url (str): The URL of the public webpage to fetch (e.g., "https://example.com").
137
- Returns:
138
- str: A string containing the visible text content extracted from the webpage.
139
- Example:
140
- url = "https://en.wikipedia.org/wiki/Python_(programming_language)"
141
- page_text = get_web_page(url)
142
- print(page_text)
143
- In this example, the function will return the visible text from the Wikipedia page about "Python programming language",
144
- excluding JavaScript-generated or hidden content, formatted as plain text.
145
- """
146
- loader = WebBaseLoader(url)
147
- docs = loader.load()
148
- content = "\n\n".join([doc.page_content for doc in docs])
149
- return f"""The web page content is:
150
- {content}
151
- Instructions:
152
- If the information is not sufficient:
153
- - Visit the URL {url} using `navigate_browser`.
154
- - Extract full page content, infoboxes, tables, and references.
155
- - Use hyperlinks from the page to recursively follow relevant internal links if necessary."""
156
-
157
- def download_video_from_youtube(url, tempdirname):
158
- """
159
- Downloads the best quality video from YouTube (MP4 format) and saves it in the specified directory.
160
- This function fetches the video from the provided YouTube URL, ensuring that the video format is MP4 with a
161
- resolution of at least 1080p. It also downloads subtitles if available, saving them in English as SRT files.
162
- Args:
163
- url (str): The YouTube video URL to download (e.g., "https://www.youtube.com/watch?v=example").
164
- tempdirname (str): The directory where the downloaded video and subtitles will be saved.
165
- Returns:
166
- str: The path to the downloaded video file (MP4 format).
167
- Example:
168
- url = "https://www.youtube.com/watch?v=example"
169
- tempdirname = "/tmp/videos"
170
- video_filepath = download_video_from_youtube(url, tempdirname)
171
- print(video_filepath)
172
- In this example, the function will download the best quality video and subtitles from the given YouTube URL
173
- and save them in the specified directory.
174
- """
175
-
176
- ydl_opts = {
177
- 'format': '(bestvideo[width>=1080][ext=mp4]/bestvideo)+bestaudio/best', #Ensures best settings
178
- 'writesubtitles': True, #Adds a subtitles file if it exists
179
- 'writeautomaticsub': True, #Adds auto-generated subtitles file
180
- 'subtitle': '--write-sub --sub-lang en', #writes subtitles file in english
181
- 'subtitlesformat':'srt', #writes the subtitles file in "srt" or "ass/srt/best"
182
- 'skip_download': False, #skips downloading the video file
183
- "merge_output_format": "mp4",
184
- 'outtmpl':f"{tempdirname}/%(title)s.%(ext)s",
185
- 'quiet': True,
186
- 'cookiefile': "./cookies.txt"
187
- }
188
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
189
- info_dict = ydl.extract_info(url, download=True)
190
- video_title = info_dict.get('title', None)
191
- filepath = ydl.prepare_filename(info_dict)
192
- print(f"Download {filepath.split('/')[-1]} Successful!")
193
- return filepath
194
-
195
- def download_audio_from_youtube(url, tempdirname):
196
- """
197
- Downloads the best quality audio (MP3 format) from a YouTube video and saves it in the specified directory.
198
- This function fetches the audio from the provided YouTube URL in the best available format and saves it
199
- as an MP3 file. It does not download the video.
200
- Args:
201
- url (str): The YouTube video URL to extract audio from (e.g., "https://www.youtube.com/watch?v=example").
202
- tempdirname (str): The directory where the audio file will be saved.
203
- Returns:
204
- str: The path to the downloaded audio file (MP3 format).
205
- Example:
206
- url = "https://www.youtube.com/watch?v=example"
207
- tempdirname = "/tmp/audio"
208
- audio_filepath = download_audio_from_youtube(url, tempdirname)
209
- print(audio_filepath)
210
- In this example, the function will download the best quality audio (MP3) from the YouTube URL
211
- and save it in the specified directory.
212
- """
213
-
214
- ydl_opts = {
215
- 'extract_audio': True,
216
- 'format': 'bestaudio',
217
- 'outtmpl': f"{tempdirname}/%(title)s.mp3",
218
- 'quiet': True,
219
- 'cookiefile': "./cookies.txt"
220
- }
221
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
222
- info_dict = ydl.extract_info(url, download=True)
223
- audio_title = info_dict.get('title', None)
224
- filepath = ydl.prepare_filename(info_dict)
225
- print(f"Download {filepath.split('/')[-1]} Successful!")
226
- return filepath
227
-
228
- def download_youtube_data(url: str) -> str:
229
  """
230
- Downloads the description and comments of a YouTube video using the yt-dlp Python API.
231
  Args:
232
- url (str): The URL of the YouTube video.
233
  Returns:
234
- str: File paths and summary of downloaded content.
 
 
235
  """
236
- ydl_opts = {
237
- 'quiet': True,
238
- 'extract_flat': False,
239
- 'skip_download': True,
240
- 'getcomments': True,
241
- }
242
-
243
  try:
244
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
245
- info = ydl.extract_info(url, download=False)
246
-
247
- description = info.get("description", "No description found.")
248
-
249
- comments = info.get("comments", [])
250
- comment_texts = [c["text"] for c in comments if "text" in c]
251
-
252
- return (
253
- f"Downloaded video data:\n\n"
254
- f"Description: {description}\n"
255
- f"Comments: {comment_texts}\n"
256
- )
257
-
258
  except Exception as e:
259
- return f"Error extracting video data: {str(e)}"
260
-
261
- @tool
262
- def get_information_from_youtube(url: str) -> str:
263
- """
264
- Downloads a YouTube video, extracts the audio, and returns a transcription of the audio.
265
- This function first downloads the video and audio from the given YouTube URL. Then, it transcribes the
266
- speech from the audio file using a transcription service.
267
- Args:
268
- url (str): The YouTube video URL to download and transcribe (e.g., "https://www.youtube.com/watch?v=example").
269
- Returns:
270
- str: The transcription text of the audio content extracted from the YouTube video.
271
- Example:
272
- url = "https://www.youtube.com/watch?v=example"
273
- transcription = get_information_from_youtube(url)
274
- print(transcription)
275
- In this example, the function will download the video and audio from YouTube, extract the audio, and
276
- return a transcription of the spoken content in the video.
277
- """
278
-
279
- tempdirname = tempfile.TemporaryDirectory()
280
- video_filepath = download_video_from_youtube(url, tempdirname)
281
- audio_filepath = download_audio_from_youtube(url, tempdirname)
282
- youtube_data = download_youtube_data(url)
283
-
284
- return youtube_data + get_information_from_audio.invoke(audio_filepath)
285
-
286
- @tool
287
- def get_information_from_audio(filepath: str) -> str:
288
- """
289
- Transcribes speech from an audio file into text using a speech-to-text model.
290
- This function takes an audio file and converts the spoken content into written text using a speech-to-text model.
291
- It is useful when you need to extract information from voice recordings.
292
- Args:
293
- filepath (str): The path to the audio file (e.g., ".wav", ".mp3", etc.).
294
- The file should contain spoken content to be transcribed.
295
- Returns:
296
- str: The transcribed text from the audio file.
297
- Example:
298
- filepath = "/path/to/audiofile.wav"
299
- transcription = get_information_from_audio(filepath)
300
- print(transcription)
301
- In this example, the function will transcribe the speech from the specified audio file and return the
302
- transcribed text that can be further analyzed or used in other processes.
303
- """
304
- result = model_whisper.transcribe(filepath)
305
- return f"""Transcription of audio file:
306
- {result['text']}
307
- Please verify this information by cross-checking with reliable external sources such as web searches, Wikipedia, or other knowledge bases before finalizing your response.
308
- If necessary, supplement the transcription with additional relevant information to ensure completeness and accuracy."""
309
-
310
- @tool
311
- def get_information_from_pdf(file_path: str) -> str:
312
- """
313
- Extracts structured text content from a PDF file.
314
- This function reads the content of a PDF document located at the specified file path and extracts the text
315
- from each page. The text is returned as a single string, with the order of pages preserved.
316
- Args:
317
- file_path (str): The local path to the PDF file to be processed (e.g., "/path/to/document.pdf").
318
- Returns:
319
- str: A single string containing the combined text extracted from all pages of the PDF.
320
- The text is ordered by the pages as they appear in the document.
321
- Example:
322
- file_path = "/path/to/document.pdf"
323
- extracted_text = get_information_from_pdf(file_path)
324
- print(extracted_text)
325
- In this example, the function will extract all text content from the PDF document,
326
- maintaining the page order, and return it as one continuous string.
327
- """
328
-
329
- with pdfplumber.open(file_path) as pdf:
330
- for page in pdf.pages:
331
- content = page.extract_tables()
332
- if content and len(content) > 0:
333
- print("Tables were detected in the PDF.")
334
- else:
335
- print("No tables were detected in the PDF..")
336
- loader = PyPDFLoader(file_path = file_path)
337
- documents = loader.load()
338
- content = '\n'.join([doc.page_content for doc in documents])
339
-
340
- return f"""The PDF file content:
341
- {content}
342
- Use this data to answer the question or perform any required analysis.
343
- Remember, you can use all available tools to supplement with additional relevant information."""
344
-
345
- @tool
346
- def get_information_from_csv(file_path: str) -> str:
347
- """
348
- Loads a CSV file and returns a structured summary of its tabular content.
349
- This function reads the entire CSV file and returns a textual representation of the data
350
- intended for use by AI agents or downstream systems that operate on text-based tables.
351
- Args:
352
- file_path (str): The path to the CSV file to load.
353
- Returns:
354
- str: A formatted string containing the CSV content as a table under the label `data_table`,
355
- along with metadata such as the number of rows and columns.
356
- """
357
- df = pd.read_csv(file_path)
358
-
359
- return f"""You are given a table extracted from a CSV file with {df.shape[0]} rows and {df.shape[1]} columns.
360
- You are given a table extracted from a CSV file.
361
- - Columns: {df.columns.tolist()}
362
- - Shape: {df.shape[0]} rows × {df.shape[1]} columns
363
- - Missing values: {df.isna().sum().sum()} total
364
- You must answer user questions using Python code with pandas-like logic. Perform all necessary computations, filtering, and aggregation using the Python interpreter tool.
365
- This dataset may contain missing values. If necessary, handle them appropriately (e.g., by filtering or imputing them) before analysis."""
366
-
367
- @tool
368
- def get_information_from_excel(file_path: str) -> str:
369
- """
370
- Extracts data and, when available, background color metadata from an Excel file (.xlsx or .xls).
371
- This function handles both Excel formats:
372
- - For .xlsx: extracts the full table and cell background colors.
373
- - For .xls: extracts only the tabular data (background colors not supported).
374
- Args:
375
- file_path (str): Path to the Excel file (.xlsx or .xls).
376
- Returns:
377
- str: A detailed string containing:
378
- - Table data extracted from the first sheet.
379
- - If available, a matrix of background cell colors (hex format).
380
- - Summary about number of rows and columns.
381
- """
382
- ext = os.path.splitext(file_path)[-1].lower()
383
-
384
- if ext == ".xlsx":
385
- workbook = load_workbook(file_path, data_only=True)
386
- sheet = workbook.active
387
- color_data = []
388
-
389
- for row in sheet.iter_rows():
390
- row_colors = []
391
- for cell in row:
392
- fill = cell.fill
393
- color = fill.start_color.rgb if fill and fill.start_color and fill.start_color.rgb else "None"
394
- row_colors.append(f"#{color}" if color != "None" else "None")
395
- color_data.append(row_colors)
396
-
397
- df = pd.read_excel(file_path, engine="openpyxl", header=None)
398
- return f"""You are given two tables extracted from an Excel file (.xlsx):
399
- - `data_table` contains the content of each cell.
400
- - `color_table` contains the background color of each cell in ARGB hex format (or "None" if not set).
401
- "data_table": {df.values.tolist()}
402
- "color_table": {color_data}
403
- Both tables are the same size: {df.shape[0]} rows × {df.shape[1]} columns.
404
- Use this data to analyze the spreadsheet and answer user questions."""
405
-
406
- elif ext == ".xls":
407
- # Load with xlrd (colors not supported)
408
- df = pd.read_excel(file_path, engine="xlrd", header=None)
409
- return f"""You are given a table extracted from an Excel file (.xls):
410
- - Background color metadata is not available for .xls files.
411
- "data_table": {df.values.tolist()}
412
- The table has {df.shape[0]} rows × {df.shape[1]} columns.
413
- Use this data to analyze the spreadsheet and answer user questions."""
414
 
415
- else:
416
- return "Unsupported file format. Please provide a .xls or .xlsx Excel file."
417
 
418
  @tool
419
- def get_information_from_xml(file_path: str) -> str:
420
  """
421
- Reads the contents of an XML file and returns it as plain text.
422
- This function loads and parses an XML file from the specified file path and returns
423
- its content as a plain text string. The text includes the structure and data contained
424
- within the XML, which can help in understanding the layout and details of the document.
425
  Args:
426
- file_path (str): The local path to the XML file (e.g., "/path/to/document.xml").
427
  Returns:
428
- str: A plain text representation of the XML file's contents, including its structure and data.
 
 
429
  """
430
- with open(file_path, 'r', encoding='utf-8') as f:
431
- xml_string = f.read()
432
- xml_string = xml_string.replace(">", ">\n")
433
- #data_dict = xmltodict.parse(xml_string)
434
- #pretty_json = json.dumps(data_dict, indent=2)
435
- return f"""You are given an XML document converted into JSON format:
436
- {xml_string}
437
- Use this to answer questions about the document's structure, contents, or metadata.
438
- If relevant, identify key entities, attributes, or relationships. """
439
-
440
- @tool
441
- def get_information_from_json(file_path: str) -> str:
442
- """
443
- Loads and returns JSON content as a formatted string.
444
- This function opens a JSON file from the specified file path, loads its contents,
445
- and returns the entire JSON structure as a string. The data is formatted as valid JSON,
446
- which is helpful for examining structured data in key-value pairs.
447
- Args:
448
- file_path (str): The local path to the .json file (e.g., "/path/to/data.json").
449
- Returns:
450
- str: A stringified version of the entire JSON data, formatted as a valid JSON string.
451
- Example:
452
- file_path = "/path/to/data.json"
453
- json_content = get_information_from_json(file_path)
454
- print(json_content)
455
- In this example, the function will:
456
- - Open the specified JSON file.
457
- - Load and parse its contents.
458
- - Return the data as a JSON-formatted string that can be used for further processing or analysis.
459
- """
460
-
461
- with open(file_path, 'r', encoding='utf-8') as f:
462
- data = json.load(f)
463
- return f"""The content of the JSON/JSON-LD file has been successfully extracted:
464
- {json.dumps(data, indent=2)}
465
- Instruction for the agent:
466
- 1. Scan the JSON content for any fields containing URLs (e.g., 'url', '@id', 'source', 'link').
467
- 2. For each URL found, you must explore the associated website as thoroughly as possible.
468
- - Do not limit the navigation to the landing page.
469
- - Recursively follow internal links, sections, and dynamically loaded content.
470
- - Extract valuable text, metadata, references, and any structured content.
471
- 3. Prioritize using the tools in the following order:
472
- TOOL PRIORITY ORDER:
473
- 1. `navigate_browser`– Use for navigating to external websites.
474
- 2. `get_web_page` – Use for static HTML websites without JavaScript.
475
- 3. `duckduckgo_web_search_run` – Use when a URL is missing or additional context is needed.
476
- If the JSON contains names, terms, or identifiers without direct links, consider searching Wikipedia or arXiv for background knowledge.
477
- Important:
478
- You must fully explore the websites found in the JSON, including internal resources and linked data. Don't stop at the home or landing page."""
479
-
480
- @tool
481
- def get_information_from_pdb(file_path: str) -> str:
482
- """
483
- Extracts 3D structural data from a PDB (Protein Data Bank) file.
484
- This function parses a PDB file and generates a human-readable summary of its
485
- molecular structure, including information about chains, residues, and atom positions
486
- in 3D space. The output is particularly useful for understanding the structural
487
- layout of proteins or other molecules stored in the PDB format.
488
- Args:
489
- file_path (str): The local path to the .pdb file (e.g., "/path/to/structure.pdb").
490
- Returns:
491
- str: A detailed, human-readable summary of the PDB file, listing the chains,
492
- residues, and atoms with their 3D coordinates.
493
- Example:
494
- file_path = "/path/to/structure.pdb"
495
- pdb_info = get_information_from_pdb(file_path)
496
- print(pdb_info)
497
- In this example, the function will:
498
- - Parse the PDB file located at the given path.
499
- - Extract information about chains, residues, and atoms, along with their coordinates in 3D space.
500
- - Return a summary of the molecular structure with the positions of atoms in x, y, and z coordinates.
501
- """
502
- parser = PDBParser(QUIET=True)
503
- structure = parser.get_structure("my_protein", file_path)
504
-
505
- info = []
506
-
507
- for model in structure:
508
- for chain in model:
509
- for residue in chain:
510
- res_info = f"Chain {chain.id} Residue {residue.resname} {residue.id[1]}:\n"
511
- for atom in residue:
512
- coords = atom.get_coord()
513
- res_info += f" Atom {atom.name}: x={coords[0]:.2f}, y={coords[1]:.2f}, z={coords[2]:.2f}\n"
514
- info.append(res_info)
515
 
516
- return "\n".join(info)
517
-
518
- @tool
519
- def get_information_from_txt(file_path: str) -> str:
520
- """
521
- Extracts plain text content from a .txt file.
522
- This function loads the full textual content from a local .txt file and returns it
523
- as a single string. It preserves the original formatting and is useful for processing
524
- documents that contain unstructured or natural language text.
525
- Args:
526
- file_path (str): Absolute or relative path to the .txt file (e.g., "documents/file.txt").
527
- Returns:
528
- str: The complete plain text extracted from the file.
529
- Example:
530
- file_path = "notes/lecture.txt"
531
- text = get_information_from_txt(file_path)
532
- print(text)
533
- Use this tool when:
534
- - You need to read or analyze the full content of a text file.
535
- - The input is stored in a standard plain-text format (.txt).
536
- """
537
- loader = TextLoader(file_path)
538
- documents = loader.load()
539
- content = "\n".join([doc.page_content for doc in documents])
540
- return f"The TXT file content is:\n\n {content}"
541
-
542
- @tool
543
- def get_information_from_python(file_path: str) -> str:
544
- """
545
- Loads and returns the source code from a Python (.py) file.
546
- This function opens a local Python script from the specified file path, reads its entire
547
- source code, and returns it as a formatted string. It's useful for inspecting, analyzing,
548
- or executing the contents of Python files in later steps.
549
- Args:
550
- file_path (str): Absolute or relative path to the Python file (e.g., "scripts/my_script.py").
551
- Returns:
552
- str: The full Python source code from the file as a string.
553
- Example:
554
- file_path = "models/model_utils.py"
555
- code = get_information_from_python(file_path)
556
- print(code)
557
- Use this tool when:
558
- - You need to analyze or execute the contents of a Python script.
559
- - The input is a .py file and contains valid Python code.
560
- """
561
- with open(file_path, 'r', encoding='utf-8') as f:
562
- code = f.read()
563
- return f"""Python source code extracted from: {file_path}
564
- {code}
565
- Suggested next step:
566
- If you want to analyze or run this code, use the python_code_executor tool and pass the code directly as input.
567
- """
568
 
569
- @tool
570
- def get_information_from_docx(file_path: str) -> str:
571
- """
572
- Extracts text content from a Microsoft Word (.docx) file.
573
- This function reads a .docx file from the specified file path and returns all
574
- human-readable text as a single string, preserving the logical order of the document.
575
- It is useful when you need to analyze or summarize documents created in Microsoft Word.
576
- Args:
577
- file_path (str): Full or relative path to the .docx file (e.g., "reports/report.docx").
578
- Returns:
579
- str: The complete extracted text from the Word document.
580
- Example:
581
- file_path = "contracts/agreement.docx"
582
- text = get_information_from_docx(file_path)
583
- print(text)
584
- Use this tool when:
585
- - The input file is a .docx Word document.
586
- - You want to extract the entire textual content for processing, querying, or summarization.
587
- """
588
- loader = Docx2txtLoader(file_path)
589
- documents = loader.load()
590
- return "\n".join([doc.page_content for doc in documents])
591
 
592
  @tool
593
- def get_information_from_pptx(file_path: str) -> str:
594
  """
595
- Extracts all text from a PowerPoint (.pptx) file slide by slide.
596
- Use this tool when:
597
- - You need to read and analyze the text content of a presentation.
598
- - You want to understand slide structure or extract meaningful information from slides.
599
  Args:
600
- file_path (str): Path to the .pptx file.
 
601
  Returns:
602
- str: Extracted text content from the presentation.
 
 
603
  """
604
- prs = Presentation(file_path)
605
- all_text = []
606
 
607
- for i, slide in enumerate(prs.slides):
608
- slide_text = []
609
- for shape in slide.shapes:
610
- if hasattr(shape, "text"):
611
- slide_text.append(shape.text.strip())
612
- if slide_text:
613
- all_text.append(f"- Slide {i + 1}\n" + "\n".join(slide_text))
614
- all_text = '\n\n'.join(all_text)
615
- return f"""The PPTX file content is:
616
- {all_text}
617
- You can use this information to answer the user question.
618
- """
619
 
620
- @tool
621
- def get_information_from_image(file_path: str, question: str) -> str:
622
- """
623
- Performs visual question answering (VQA) on an image file.
624
- This tool allows the agent to reason about the contents of an image. It takes a path to an image file
625
- and a natural language question, processes the image and text using a vision-language model,
626
- and returns a text-based answer derived from the visual information.
627
- Args:
628
- file_path (str): Path to the image file (e.g., "images/photo.png").
629
- question (str): Natural language question to ask about the image (e.g., "What is the person holding?").
630
- Returns:
631
- str: Answer generated by the vision-language model based on the image and the question.
632
- Example:
633
- answer = get_information_from_image("cat.jpg", "What color is the cat?")
634
- print(answer)
635
- Use this tool when:
636
- - You need to analyze or describe visual content in an image.
637
- - The user asks a question involving a photo, diagram, chart, screenshot, or other image file.
638
- - Visual reasoning is required to answer the question.
639
- """
640
- messages = [
641
- {
642
- "role": "user",
643
- "content": [
644
  {
645
- "type": "image",
646
- "image": file_path,
647
- },
648
- {"type": "text", "text": question},
 
 
 
 
 
649
  ],
650
- }
651
- ]
652
-
653
- text = processor_image.apply_chat_template(
654
- messages, tokenize=False, add_generation_prompt=True
655
- )
656
- image_inputs, video_inputs = process_vision_info(messages)
657
- inputs = processor_image(
658
- text=[text],
659
- images=image_inputs,
660
- videos=video_inputs,
661
- padding=True,
662
- return_tensors="pt",
663
- )
664
- inputs = inputs.to("cuda")
665
-
666
- generated_ids = model_image.generate(**inputs, max_new_tokens=1024)
667
- generated_ids_trimmed = [
668
- out_ids[len(in_ids) :] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
669
- ]
670
- output_text = processor_image.batch_decode(
671
- generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
672
- )
673
- return f"VISUAL ANSWER: {output_text}"
674
-
675
-
676
- @tool
677
- def get_all_files_from_zip(file_path: str) -> Tuple[List[str], str]:
678
- """
679
- Extracts all files from a ZIP archive to a temporary directory and returns their paths.
680
- Args:
681
- file_path (str): Path to the .zip archive.
682
- Returns:
683
- Tuple[List[str], str]:
684
- - A list of full paths to the extracted files.
685
- - A summary string indicating how many files were extracted.
686
- """
687
- files = []
688
- temp_dir = tempfile.gettempdir()
689
- zip_ref = zipfile.ZipFile(file_path, 'r')
690
- zip_ref.extractall(temp_dir)
691
- zip_files = zip_ref.namelist()
692
- for filename in zip_ref.namelist():
693
- temp_file_path = os.path.join(temp_dir, filename)
694
- files.append(temp_file_path)
695
-
696
- prompt = f"The ZIP file contains {len(files)} file(s)."
697
- return files, prompt
698
-
699
- duckduckgosearchrun_tool = DuckDuckGoSearchRun()
700
-
701
- @retry(
702
- wait=wait_exponential(multiplier=1, min=15, max=60),
703
- stop=stop_after_attempt(3),
704
- retry=retry_if_exception_type(DuckDuckGoSearchException)
705
- )
706
- @sleep_and_retry
707
- @limits(calls=1, period=30)
708
- def get_dgg_search(query: str, mode: str):
709
- if mode == "run":
710
- return duckduckgosearchrun_tool.run(query)
711
- elif mode == "results":
712
- with DDGS() as ddgs:
713
- return ddgs.text(query, max_results=5)
714
-
715
- def get_information_from_searches(query: str) -> str:
716
- """
717
- Perform a DuckDuckGo search and return a textual summary and structured top results with guidance for the agent.
718
- Args:
719
- query (str): The search query string.
720
- Returns:
721
- str: Combined summary and list of links with step-by-step instructions.
722
- """
723
- text_summary = get_dgg_search(query, mode="run")
724
- structured_results = get_dgg_search(query, mode="results")
725
-
726
- if not structured_results or len(structured_results) == 0:
727
- structured_block = "No results found."
728
- else:
729
- structured_block = "\n\n".join([
730
- f"{i+1}. Title: {res.get('title', 'No title')}\n"
731
- f"Snippet: {res.get('body', 'No snippet')}\n"
732
- f"Link: {res.get('href', 'No link')}"
733
- for i, res in enumerate(structured_results)
734
- ])
735
-
736
- text_block = text_summary if text_summary and len(text_summary) > 5 else "No summary available."
737
-
738
- return f"""DuckDuckGo Search Results for query: `{query}`
739
- Summary:
740
- {text_block}
741
- Top Search Results:
742
- {structured_block}
743
- Instructions for the Agent:
744
- 1. Analyze the summary first. If it contains the direct answer to the question, return it.
745
- 2. If the summary lacks detail, examine the top links above.
746
- 3. Use `navigate_browser` to open promising links.
747
- - Prioritize links whose titles or snippets closely match the key terms in the question.
748
- - Focus on extracting structured information like main content, tables, or lists.
749
- 4. If a page lacks sufficient data, explore internal links within that domain.
750
- 5. Combine information from multiple sources if needed to answer the original question accurately.
751
- """
752
-
753
- search_tool = Tool(
754
- name="duckduckgo_search",
755
- func=get_information_from_searches,
756
- description=(
757
- "Use this tool to perform a live DuckDuckGo web search and retrieve both a summary and a list of relevant links "
758
- "with snippets. Ideal for finding general information from public websites that are well indexed by DuckDuckGo.\n\n"
759
- "Limitations:\n"
760
- "- DuckDuckGo may not return results for sites like Google.\n"
761
- "Fallback Instructions:\n"
762
- "If this tool returns no useful information or fails to retrieve results:\n"
763
- "- Use `navigate_browser` to open relevant links or perform a direct search in a browser context.\n"
764
- "- Extract detailed information from the visited web pages, including main text, sidebars, and tables.\n"
765
- "- Explore internal links within the pages if needed to answer the question."
766
- )
767
- )
768
-
769
- def verbose_python_executor(code: str) -> str:
770
- """Executes Python code and returns both the code and the output."""
771
- tool = PythonREPLTool()
772
- result = tool.run(code)
773
- return f"Code executed:\n```python\n{code}\n```\n\nOutput:\n{result}"
774
-
775
- python_tool = Tool(
776
- name="python_code_executor",
777
- func=verbose_python_executor,
778
- description=(
779
- "Use this tool to execute raw Python code.\n\n"
780
- "Input: A Python code snippet as a single string.\n\n"
781
- "Important:\n"
782
- "- This is NOT a remote API call. Do NOT include calls to `default_api`, `__arg1`, or any tool inside the code.\n"
783
- "- The code should be ready to run directly in Python, using standard libraries."
784
- )
785
- )
786
-
787
- def download_file_temp(url: str) -> str:
788
- """
789
- Downloads a file from the given URL and saves it into a temporary directory.
790
- Args:
791
- url (str): URL of the file to download.
792
- Returns:
793
- str: Path to the downloaded file or an error message.
794
- """
795
- try:
796
- response = requests.get(url, stream=True)
797
- response.raise_for_status()
798
-
799
- # Create a temporary directory
800
- temp_dir = tempfile.mkdtemp()
801
-
802
- # Extract filename from URL or fallback to a generic name
803
- filename = url.split("/")[-1] or "downloaded_file"
804
-
805
- # Full path for saving file
806
- file_path = os.path.join(temp_dir, filename)
807
-
808
- with open(file_path, "wb") as f:
809
- for chunk in response.iter_content(chunk_size=8192):
810
- f.write(chunk)
811
 
812
- return f"File downloaded successfully and saved at {file_path}"
813
  except Exception as e:
814
- return f"Failed to download file: {e}"
815
-
816
- download_tool = Tool(
817
- name="file_downloader_temp",
818
- func=download_file_temp,
819
- description="Downloads a file from a URL and saves it in a temporary folder."
820
- )
821
-
822
- # Create a Playwright browser instance
823
- def initialize_web_tools():
824
- async_browser = create_async_playwright_browser()
825
- toolkit = PlayWrightBrowserToolkit.from_browser(async_browser=async_browser)
826
- return toolkit.get_tools()
 
1
+ import base64
2
+ from openai import OpenAI
3
+ from smolagents import tool
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import pandas as pd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
+ OPEN_AI_CLIENT = OpenAI()
 
 
 
7
 
 
 
 
 
8
 
9
  @tool
10
+ def extract_data(filename: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """
12
+ Extracts data from an Excel file and returns it as a CSV-formatted string.
13
  Args:
14
+ filename: The path to the Excel file to extract data from.
15
  Returns:
16
+ str: The extracted data in CSV format or an error message.
17
+ Raises:
18
+ Exception: If there is an issue with reading the Excel file.
19
  """
 
 
 
 
 
 
 
20
  try:
21
+ df = pd.read_excel(filename)
22
+ return df.to_csv(index=False)
 
 
 
 
 
 
 
 
 
 
 
 
23
  except Exception as e:
24
+ return f"Error extracting data from Excel: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
 
 
26
 
27
  @tool
28
+ def transcribe_mp3(filename: str) -> str:
29
  """
30
+ Transcribes speech from an MP3 file using the OpenAI Whisper API.
 
 
 
31
  Args:
32
+ filename: The path to the MP3 file to transcribe.
33
  Returns:
34
+ str: The transcribed text or an error message.
35
+ Raises:
36
+ Exception: If there is an issue with the transcription process.
37
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ try:
40
+ with open(filename, "rb") as audio_file:
41
+ transcript = OPEN_AI_CLIENT.audio.transcriptions.create(
42
+ model="whisper-1", file=audio_file
43
+ )
44
+ return transcript.text
45
+ except Exception as e:
46
+ return f"Error transcribing audio: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  @tool
50
+ def describe_image(prompt: str, filename: str) -> str:
51
  """
52
+ Describes the content of an image using the OpenAI Vision API.
 
 
 
53
  Args:
54
+ prompt: A prompt to guide the description.
55
+ filename: The path to the image file to describe.
56
  Returns:
57
+ str: The description of the image or an error message.
58
+ Raises:
59
+ Exception: If there is an issue with the description process.
60
  """
 
 
61
 
62
+ try:
63
+ with open(filename, "rb") as image_file:
64
+ base64_image = base64.b64encode(image_file.read()).decode("utf-8")
 
 
 
 
 
 
 
 
 
65
 
66
+ response = OPEN_AI_CLIENT.responses.create(
67
+ model="gpt-4.1",
68
+ input=[
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  {
70
+ "role": "user",
71
+ "content": [
72
+ {"type": "input_text", "text": prompt},
73
+ {
74
+ "type": "input_image",
75
+ "image_url": f"data:image/jpeg;base64,{base64_image}",
76
+ },
77
+ ],
78
+ }
79
  ],
80
+ )
81
+ return response.output_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
 
83
  except Exception as e:
84
+ return f"Error describing image: {str(e)}"