Itz-Amethyst commited on
Commit
a36de18
·
unverified ·
1 Parent(s): 81917a3

feat: add new featured tools

Browse files
tools/browse.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ from langchain_community.tools.tavily_search import TavilySearchResults
3
+ from langchain_community.document_loaders import WikipediaLoader
4
+ from langchain_community.document_loaders import ArxivLoader
5
+ from langchain_core.tools import tool
6
+ def format_search_docs(search_docs):
7
+ """Format search documents into a consistent string format.
8
+
9
+ Args:
10
+ search_docs: List of document objects with metadata and page_content.
11
+
12
+ Returns:
13
+ Formatted string with document sources and content.
14
+ """
15
+ return "\n\n---\n\n".join(
16
+ [
17
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
18
+ for doc in search_docs
19
+ ]
20
+ )
21
+
22
+ @tool
23
+ def wiki_search(query: str) -> str:
24
+ """Search Wikipedia for a query and return maximum 2 results.
25
+ Args:
26
+ query: The search query."""
27
+ search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
28
+ formatted_search_docs = format_search_docs(search_docs)
29
+ return {"wiki_results": formatted_search_docs}
30
+
31
+ @tool
32
+ def web_search(query: str) -> str:
33
+ """Search Tavily for a query and return maximum 3 results.
34
+ Args:
35
+ query: The search query."""
36
+ search_docs = TavilySearchResults(max_results=3).invoke(query=query)
37
+ formatted_search_docs = format_search_docs(search_docs)
38
+ return {"web_results": formatted_search_docs}
39
+
40
+ @tool
41
+ def arxiv_search(query: str) -> str:
42
+ """Search Arxiv for a query and return maximum 3 result.
43
+ Args:
44
+ query: The search query."""
45
+ search_docs = ArxivLoader(query=query, load_max_docs=3).load()
46
+ truncated_docs = []
47
+ for doc in search_docs:
48
+ doc_copy = copy.copy(doc)
49
+ doc_copy.page_content = doc.page_content[:1000]
50
+ truncated_docs.append(doc_copy)
51
+
52
+ formatted_search_docs = format_search_docs(truncated_docs)
53
+ return {"arxiv_results": formatted_search_docs}
tools/document_process.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.tools import tool
2
+ import os
3
+ from typing import List, Dict, Any, Optional
4
+ import tempfile
5
+ import requests
6
+ from urllib.parse import urlparse
7
+ import pytesseract
8
+ from PIL import Image
9
+ import pandas as pd
10
+ import uuid
11
+
12
+ @tool
13
+ def save_and_read_file(content: str, filename: Optional[str] = None) -> str:
14
+ """
15
+ Save content to a file and return the path.
16
+ Args:
17
+ content (str): the content to save to the file
18
+ filename (str, optional): the name of the file. If not provided, a random name file will be created.
19
+ """
20
+ temp_dir = tempfile.gettempdir()
21
+ if filename is None:
22
+ temp_file = tempfile.NamedTemporaryFile(delete=False, dir=temp_dir)
23
+ filepath = temp_file.name
24
+ else:
25
+ filepath = os.path.join(temp_dir, filename)
26
+
27
+ with open(filepath, "w") as f:
28
+ f.write(content)
29
+
30
+ return f"File saved to {filepath}. You can read this file to process its contents."
31
+
32
+ @tool
33
+ def download_file_from_url(url: str, filename: Optional[str] = None) -> str:
34
+ """
35
+ Download a file from a URL and save it to a temporary location.
36
+ Args:
37
+ url (str): the URL of the file to download.
38
+ filename (str, optional): the name of the file. If not provided, a random name file will be created.
39
+ """
40
+ try:
41
+ if not filename:
42
+ path = urlparse(url).path
43
+ filename = os.path.basename(path)
44
+ if not filename:
45
+ filename = f"downloaded_{uuid.uuid4().hex[:8]}"
46
+
47
+ temp_dir = tempfile.gettempdir()
48
+ filepath = os.path.join(temp_dir, filename)
49
+
50
+ response = requests.get(url, stream=True)
51
+ response.raise_for_status()
52
+
53
+ # Save the file
54
+ with open(filepath, "wb") as f:
55
+ for chunk in response.iter_content(chunk_size=8192):
56
+ f.write(chunk)
57
+
58
+ return f"File downloaded to {filepath}. You can read this file to process its contents."
59
+ except Exception as e:
60
+ return f"Error downloading file: {str(e)}"
61
+
62
+ @tool
63
+ def extract_text_from_image(image_path: str) -> str:
64
+ """
65
+ Extract text from an image using OCR library pytesseract (if available).
66
+ Args:
67
+ image_path (str): the path to the image file.
68
+ """
69
+ try:
70
+ image = Image.open(image_path)
71
+
72
+ # Extract text from the image
73
+ text = pytesseract.image_to_string(image)
74
+
75
+ return f"Extracted text from image:\n\n{text}"
76
+ except Exception as e:
77
+ return f"Error extracting text from image: {str(e)}"
78
+
79
+ @tool
80
+ def analyze_csv_file(file_path: str, query: str) -> str:
81
+ """
82
+ Analyze a CSV file using pandas and answer a question about it.
83
+ Args:
84
+ file_path (str): the path to the CSV file.
85
+ query (str): Question about the data
86
+ """
87
+ try:
88
+ df = pd.read_csv(file_path)
89
+
90
+ result = f"CSV file loaded with {len(df)} rows and {len(df.columns)} columns.\n"
91
+ result += f"Columns: {', '.join(df.columns)}\n\n"
92
+
93
+ result += "Summary statistics:\n"
94
+ result += str(df.describe())
95
+
96
+ return result
97
+
98
+ except Exception as e:
99
+ return f"Error analyzing CSV file: {str(e)}"
100
+
101
+ @tool
102
+ def analyze_excel_file(file_path: str, query: str) -> str:
103
+ """
104
+ Analyze an Excel file using pandas and answer a question about it.
105
+ Args:
106
+ file_path (str): the path to the Excel file.
107
+ query (str): Question about the data
108
+ """
109
+ try:
110
+ df = pd.read_excel(file_path)
111
+
112
+ result = (
113
+ f"Excel file loaded with {len(df)} rows and {len(df.columns)} columns.\n"
114
+ )
115
+ result += f"Columns: {', '.join(df.columns)}\n\n"
116
+
117
+ result += "Summary statistics:\n"
118
+ result += str(df.describe())
119
+
120
+ return result
121
+
122
+ except Exception as e:
123
+ return f"Error analyzing Excel file: {str(e)}"
tools/image.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import base64
4
+ import uuid
5
+ from PIL import Image
6
+
7
+
8
+ def encode_image(image_path: str) -> str:
9
+ """Convert an image file to base64 string."""
10
+ with open(image_path, "rb") as image_file:
11
+ return base64.b64encode(image_file.read()).decode("utf-8")
12
+
13
+
14
+ def decode_image(image_path: str) -> Image.Image:
15
+ """Convert a base64 string to a PIL Image."""
16
+ image_data = base64.b64decode(image_path)
17
+ return Image.open(io.BytesIO(image_data))
18
+
19
+ def save_image(image: Image.Image, directory:str = "images") -> str:
20
+ """Save a PIL Image to disk and return the path."""
21
+ os.makedirs(directory, exist_ok = True)
22
+ image_id = str(uuid.uuid4())
23
+ image_path = os.path.join(directory, f"{image_id}.png")
24
+ image.save(image_path)
25
+ return image_path
tools/image_tools.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.tools import tool
2
+ from tools.image import decode_image, encode_image, save_image
3
+
4
+ @tool
5
+ def analyze_image(image_base64: str) -> Dict[str, Any]:
6
+ """
7
+ Analyze basic properties of an image (size, mode, color analysis, thumbnail preview).
8
+ Args:
9
+ image_base64 (str): Base64 encoded image string
10
+ Returns:
11
+ Dictionary with analysis result
12
+ """
13
+ try:
14
+ img = decode_image(image_base64)
15
+ width, height = img.size
16
+ mode = img.mode
17
+
18
+ if mode in ("RGB", "RGBA"):
19
+ arr = np.array(img)
20
+ avg_colors = arr.mean(axis=(0, 1))
21
+ dominant = ["Red", "Green", "Blue"][np.argmax(avg_colors[:3])]
22
+ brightness = avg_colors.mean()
23
+ color_analysis = {
24
+ "average_rgb": avg_colors.tolist(),
25
+ "brightness": brightness,
26
+ "dominant_color": dominant,
27
+ }
28
+ else:
29
+ color_analysis = {"note": f"No color analysis for mode {mode}"}
30
+
31
+ thumbnail = img.copy()
32
+ thumbnail.thumbnail((100, 100))
33
+ thumb_path = save_image(thumbnail, "thumbnails")
34
+ thumbnail_base64 = encode_image(thumb_path)
35
+
36
+ return {
37
+ "dimensions": (width, height),
38
+ "mode": mode,
39
+ "color_analysis": color_analysis,
40
+ "thumbnail": thumbnail_base64,
41
+ }
42
+ except Exception as e:
43
+ return {"error": str(e)}
44
+
45
+ @tool
46
+ def generate_simple_image(
47
+ image_type: str,
48
+ width: int = 500,
49
+ height: int = 500,
50
+ params: Optional[Dict[str, Any]] = None,
51
+ ) -> Dict[str, Any]:
52
+ """
53
+ Generate a simple image (gradient, noise, pattern, chart).
54
+ Args:
55
+ image_type (str): Type of image
56
+ width (int), height (int)
57
+ params (Dict[str, Any], optional): Specific parameters
58
+ Returns:
59
+ Dictionary with generated image (base64)
60
+ """
61
+ try:
62
+ params = params or {}
63
+
64
+ if image_type == "gradient":
65
+ direction = params.get("direction", "horizontal")
66
+ start_color = params.get("start_color", (255, 0, 0))
67
+ end_color = params.get("end_color", (0, 0, 255))
68
+
69
+ img = Image.new("RGB", (width, height))
70
+ draw = ImageDraw.Draw(img)
71
+
72
+ if direction == "horizontal":
73
+ for x in range(width):
74
+ r = int(
75
+ start_color[0] + (end_color[0] - start_color[0]) * x / width
76
+ )
77
+ g = int(
78
+ start_color[1] + (end_color[1] - start_color[1]) * x / width
79
+ )
80
+ b = int(
81
+ start_color[2] + (end_color[2] - start_color[2]) * x / width
82
+ )
83
+ draw.line([(x, 0), (x, height)], fill=(r, g, b))
84
+ else:
85
+ for y in range(height):
86
+ r = int(
87
+ start_color[0] + (end_color[0] - start_color[0]) * y / height
88
+ )
89
+ g = int(
90
+ start_color[1] + (end_color[1] - start_color[1]) * y / height
91
+ )
92
+ b = int(
93
+ start_color[2] + (end_color[2] - start_color[2]) * y / height
94
+ )
95
+ draw.line([(0, y), (width, y)], fill=(r, g, b))
96
+
97
+ elif image_type == "noise":
98
+ noise_array = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)
99
+ img = Image.fromarray(noise_array, "RGB")
100
+
101
+ else:
102
+ return {"error": f"Unsupported image_type {image_type}"}
103
+
104
+ result_path = save_image(img)
105
+ result_base64 = encode_image(result_path)
106
+ return {"generated_image": result_base64}
107
+
108
+ except Exception as e:
109
+ return {"error": str(e)}
tools/python_interpreter.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import uuid
4
+ import base64
5
+ import traceback
6
+ import contextlib
7
+ from typing import Dict, Any
8
+ import numpy as np
9
+ import pandas as pd
10
+ import matplotlib.pyplot as plt
11
+ from PIL import Image
12
+ from code_interpreter import CodeInterpreter
13
+
14
+ interpreter_instance = CodeInterpreter()
15
+ class CodeInterpreter:
16
+
17
+ def __init__(self, allowed_modules = None, max_execution_time = 30, working_directory = None):
18
+ """Initialize the code interpreter with safety measures."""
19
+ self.allowed_modules = allowed_modules or [
20
+ "numpy", "pandas", "matplotlib", "scipy", "sklearn",
21
+ "math", "random", "statistics", "datetime", "collections",
22
+ "itertools", "functools", "operator", "re", "json",
23
+ "sympy", "networkx", "nltk", "PIL", "pytesseract",
24
+ "cmath", "uuid", "tempfile", "requests", "urllib"
25
+ ]
26
+ self.max_execution_time = max_execution_time
27
+ self.working_directory = working_directory or os.path.join(os.getcwd())
28
+ if not os.path.exists(self.working_directory):
29
+ os.makedirs(self.working_directory)
30
+
31
+ self.globals = {
32
+ "__builtins__": __builtins__,
33
+ "np": np,
34
+ "pd": pd,
35
+ "plt": plt,
36
+ "Image": Image,
37
+ }
38
+
39
+ def execute_code(self, code: str, language: str = "python") -> Dict[str, Any]:
40
+ """Execute the provided code in the selected programming language."""
41
+ language = language.lower()
42
+ execution_id = str(uuid.uuid4())
43
+
44
+ result = {
45
+ "execution_id": execution_id,
46
+ "status": "error",
47
+ "stdout": "",
48
+ "stderr": "",
49
+ "result": None,
50
+ "plots": [],
51
+ "dataframes": []
52
+ }
53
+
54
+ try:
55
+ return self._execute_python(code, execution_id)
56
+ except Exception as e:
57
+ result["stderr"] = f"Unsupported Language: {str(e)}"
58
+
59
+ return result
60
+
61
+ def _execute_python(self, code: str, execution_id: str) -> dict:
62
+ output_buffer = io.StringIO()
63
+ error_buffer = io.StringIO()
64
+ result = {
65
+ "execution_id": execution_id,
66
+ "status": "error",
67
+ "stdout": "",
68
+ "stderr": "",
69
+ "result": None,
70
+ "plots": [],
71
+ "dataframes": []
72
+ }
73
+
74
+ try:
75
+ exec_dir = os.path.join(self.working_directory, execution_id)
76
+ os.makedirs(exec_dir, exist_ok=True)
77
+ plt.switch_backend('Agg')
78
+
79
+ with contextlib.redirect_stdout(output_buffer), contextlib.redirect_stderr(error_buffer):
80
+ exec_result = exec(code, self.globals)
81
+
82
+ if plt.get_fignums():
83
+ for i, fig_num in enumerate(plt.get_fignums()):
84
+ fig = plt.figure(fig_num)
85
+ img_path = os.path.join(exec_dir, f"plot_{i}.png")
86
+ fig.savefig(img_path)
87
+ with open(img_path, "rb") as img_file:
88
+ img_data = base64.b64encode(img_file.read()).decode('utf-8')
89
+ result["plots"].append({
90
+ "figure_number": fig_num,
91
+ "data": img_data
92
+ })
93
+
94
+ for var_name, var_value in self.globals.items():
95
+ if isinstance(var_value, pd.DataFrame) and len(var_value) > 0:
96
+ result["dataframes"].append({
97
+ "name": var_name,
98
+ "head": var_value.head().to_dict(),
99
+ "shape": var_value.shape,
100
+ "dtypes": str(var_value.dtypes)
101
+ })
102
+
103
+ result["status"] = "success"
104
+ result["stdout"] = output_buffer.getvalue()
105
+ result["result"] = exec_result
106
+
107
+ except Exception as e:
108
+ result["status"] = "error"
109
+ result["stderr"] = f"{error_buffer.getvalue()}\n{traceback.format_exc()}"
110
+
111
+ return result
112
+
113
+
114
+ @tool
115
+ def execute_code_lang(code: str, language: str = "python") -> str:
116
+ """Execute code in python
117
+ Args:
118
+ code (str): The source code to execute.
119
+ language (str): The language of the code. Supported: "python".
120
+ Returns:
121
+ A string summarizing the execution results (stdout, stderr, errors, plots, dataframes if any).
122
+ """
123
+ supported_language = "python"
124
+ language = language.lower()
125
+
126
+ if language != supported_language:
127
+ return f"❌ Unsupported language: {language}."
128
+
129
+ result = interpreter_instance.execute_code(code, language=language)
130
+
131
+ response = []
132
+
133
+ if result["status"] == "success":
134
+ response.append(f"✅ Code executed successfully in **{language.upper()}**")
135
+
136
+ if result.get("stdout"):
137
+ response.append(
138
+ "\n**Standard Output:**\n```\n" + result["stdout"].strip() + "\n```"
139
+ )
140
+
141
+ if result.get("stderr"):
142
+ response.append(
143
+ "\n**Standard Error (if any):**\n```\n"
144
+ + result["stderr"].strip()
145
+ + "\n```"
146
+ )
147
+
148
+ if result.get("result") is not None:
149
+ response.append(
150
+ "\n**Execution Result:**\n```\n"
151
+ + str(result["result"]).strip()
152
+ + "\n```"
153
+ )
154
+
155
+ if result.get("dataframes"):
156
+ for df_info in result["dataframes"]:
157
+ response.append(
158
+ f"\n**DataFrame `{df_info['name']}` (Shape: {df_info['shape']})**"
159
+ )
160
+ df_preview = pd.DataFrame(df_info["head"])
161
+ response.append("First 5 rows:\n```\n" + str(df_preview) + "\n```")
162
+
163
+ if result.get("plots"):
164
+ response.append(
165
+ f"\n**Generated {len(result['plots'])} plot(s)** (Image data returned separately)"
166
+ )
167
+
168
+ else:
169
+ response.append(f"❌ Code execution failed in **{language.upper()}**")
170
+ if result.get("stderr"):
171
+ response.append(
172
+ "\n**Error Log:**\n```\n" + result["stderr"].strip() + "\n```"
173
+ )
174
+
175
+ return "\n".join(response)
tools/simple_math.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.tools import tool
2
+
3
+ @tool
4
+ def multiply(a: float, b: float) -> float:
5
+ """
6
+ Multiplies two numbers.
7
+ Args:
8
+ a (float): the first number
9
+ b (float): the second number
10
+ """
11
+ return a * b
12
+
13
+
14
+ @tool
15
+ def add(a: float, b: float) -> float:
16
+ """
17
+ Adds two numbers.
18
+ Args:
19
+ a (float): the first number
20
+ b (float): the second number
21
+ """
22
+ return a + b
23
+
24
+
25
+ @tool
26
+ def subtract(a: float, b: float) -> int:
27
+ """
28
+ Subtracts two numbers.
29
+ Args:
30
+ a (float): the first number
31
+ b (float): the second number
32
+ """
33
+ return a - b
34
+
35
+
36
+ @tool
37
+ def divide(a: float, b: float) -> float:
38
+ """
39
+ Divides two numbers.
40
+ Args:
41
+ a (float): the first float number
42
+ b (float): the second float number
43
+ """
44
+ if b == 0:
45
+ raise ValueError("Cannot divided by zero.")
46
+ return a / b
47
+
48
+
49
+ @tool
50
+ def modulus(a: int, b: int) -> int:
51
+ """
52
+ Get the modulus of two numbers.
53
+ Args:
54
+ a (int): the first number
55
+ b (int): the second number
56
+ """
57
+ return a % b
58
+
59
+
60
+ @tool
61
+ def power(a: float, b: float) -> float:
62
+ """
63
+ Get the power of two numbers.
64
+ Args:
65
+ a (float): the first number
66
+ b (float): the second number
67
+ """
68
+ return a**b
69
+
70
+
71
+ @tool
72
+ def square_root(a: float) -> float | complex:
73
+ """
74
+ Get the square root of a number.
75
+ Args:
76
+ a (float): the number to get the square root of
77
+ """
78
+ if a >= 0:
79
+ return a**0.5
80
+ return cmath.sqrt(a)