Update tools.py
Browse files
tools.py
CHANGED
|
@@ -1,826 +1,84 @@
|
|
| 1 |
-
|
| 2 |
-
import
|
| 3 |
-
import
|
| 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 |
-
|
| 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
|
| 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 |
-
|
| 231 |
Args:
|
| 232 |
-
|
| 233 |
Returns:
|
| 234 |
-
str:
|
|
|
|
|
|
|
| 235 |
"""
|
| 236 |
-
ydl_opts = {
|
| 237 |
-
'quiet': True,
|
| 238 |
-
'extract_flat': False,
|
| 239 |
-
'skip_download': True,
|
| 240 |
-
'getcomments': True,
|
| 241 |
-
}
|
| 242 |
-
|
| 243 |
try:
|
| 244 |
-
|
| 245 |
-
|
| 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
|
| 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
|
| 420 |
"""
|
| 421 |
-
|
| 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 |
-
|
| 427 |
Returns:
|
| 428 |
-
str:
|
|
|
|
|
|
|
| 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 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 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
|
| 594 |
"""
|
| 595 |
-
|
| 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 |
-
|
|
|
|
| 601 |
Returns:
|
| 602 |
-
str:
|
|
|
|
|
|
|
| 603 |
"""
|
| 604 |
-
prs = Presentation(file_path)
|
| 605 |
-
all_text = []
|
| 606 |
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 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 |
-
|
| 621 |
-
|
| 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 |
-
"
|
| 646 |
-
"
|
| 647 |
-
|
| 648 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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"
|
| 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)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|