pavan-d commited on
Commit
7076db1
·
verified ·
1 Parent(s): 4f74ec3

Upload 6 files

Browse files
tools/__init__.py ADDED
File without changes
tools/basic_calculator.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.tools import tool
2
+
3
+
4
+ @tool
5
+ def multiply(a: float, b: float) -> float:
6
+ """
7
+ Multiplies two numbers.
8
+ Args:
9
+ a (float): the first number
10
+ b (float): the second number
11
+ """
12
+ return a * b
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
+ @tool
25
+ def subtract(a: float, b: float) -> int:
26
+ """
27
+ Subtracts two numbers.
28
+ Args:
29
+ a (float): the first number
30
+ b (float): the second number
31
+ """
32
+ return a - b
33
+
34
+ @tool
35
+ def divide(a: float, b: float) -> float:
36
+ """
37
+ Divides two numbers.
38
+ Args:
39
+ a (float): the first float number
40
+ b (float): the second float number
41
+ """
42
+ if b == 0:
43
+ raise ValueError("Cannot divided by zero.")
44
+ return a / b
45
+
46
+ @tool
47
+ def modulus(a: int, b: int) -> int:
48
+ """
49
+ Get the modulus of two numbers.
50
+ Args:
51
+ a (int): the first number
52
+ b (int): the second number
53
+ """
54
+ return a % b
55
+
56
+ @tool
57
+ def power(a: float, b: float) -> float:
58
+ """
59
+ Get the power of two numbers.
60
+ Args:
61
+ a (float): the first number
62
+ b (float): the second number
63
+ """
64
+ return a**b
65
+
66
+ @tool
67
+ def square_root(a: float) -> float | complex:
68
+ """
69
+ Get the square root of a number.
70
+ Args:
71
+ a (float): the number to get the square root of
72
+ """
73
+ if a >= 0:
74
+ return a**0.5
75
+ return cmath.sqrt(a)
76
+
77
+ @tool
78
+ def count_substring(substring:str, text:str) -> int:
79
+ """
80
+ Get the number of occurences of a substring within some text. Useful for 'How many (substring) are in (text)?'
81
+ Args:
82
+ substring (str): the substring to check for.
83
+ text (str): the text to search through.
84
+ """
85
+ return text.count(substring)
tools/code_interpreter.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import sys
4
+ import uuid
5
+ import base64
6
+ import traceback
7
+ import contextlib
8
+ import tempfile
9
+ import subprocess
10
+ import sqlite3
11
+ from typing import Dict, List, Any, Optional, Union
12
+ import numpy as np
13
+ import pandas as pd
14
+ import matplotlib.pyplot as plt
15
+ from PIL import Image
16
+ from langchain_core.tools import tool
17
+
18
+ class CodeInterpreter:
19
+ def __init__(self, allowed_modules=None, max_execution_time=30, working_directory=None):
20
+ """Initialize the code interpreter with safety measures."""
21
+
22
+ self.allowed_modules = allowed_modules or ["numpy", "pandas", "matplotlib", "scipy", "sklearn", "math", "random", "statistics", "datetime", "collections",
23
+ "itertools", "functools", "operator", "re", "json", "sympy", "networkx", "nltk", "PIL", "pytesseract", "cmath", "uuid", "tempfile", "requests", "urllib"]
24
+
25
+ self.max_execution_time = max_execution_time
26
+ self.working_directory = working_directory or os.path.join(os.getcwd())
27
+ if not os.path.exists(self.working_directory):
28
+ os.makedirs(self.working_directory)
29
+
30
+ self.globals = {"__builtins__": __builtins__, "np": np, "pd": pd, "plt": plt, "Image": Image}
31
+ self.temp_sqlite_db = os.path.join(tempfile.gettempdir(), "code_exec.db")
32
+
33
+ def execute_code(self, code: str, language: str = "python") -> Dict[str, Any]:
34
+ """Execute the provided code in the selected programming language."""
35
+ language = language.lower()
36
+ execution_id = str(uuid.uuid4())
37
+
38
+ result = {"execution_id": execution_id, "status": "error", "stdout": "", "stderr": "", "result": None, "plots": [], "dataframes": []}
39
+
40
+ try:
41
+ if language == "python":
42
+ return self._execute_python(code, execution_id)
43
+ elif language == "bash":
44
+ return self._execute_bash(code, execution_id)
45
+ elif language == "sql":
46
+ return self._execute_sql(code, execution_id)
47
+ elif language == "c":
48
+ return self._execute_c(code, execution_id)
49
+ elif language == "java":
50
+ return self._execute_java(code, execution_id)
51
+ else:
52
+ result["stderr"] = f"Unsupported language: {language}"
53
+ except Exception as e:
54
+ result["stderr"] = str(e)
55
+
56
+ return result
57
+
58
+ def _execute_python(self, code: str, execution_id: str) -> dict:
59
+ output_buffer = io.StringIO()
60
+ error_buffer = io.StringIO()
61
+ result = {"execution_id": execution_id, "status": "error", "stdout": "", "stderr": "", "result": None, "plots": [], "dataframes": []}
62
+
63
+ try:
64
+ exec_dir = os.path.join(self.working_directory, execution_id)
65
+ os.makedirs(exec_dir, exist_ok=True)
66
+ plt.switch_backend('Agg')
67
+
68
+ with contextlib.redirect_stdout(output_buffer), contextlib.redirect_stderr(error_buffer):
69
+ exec_result = exec(code, self.globals)
70
+
71
+ if plt.get_fignums():
72
+ for i, fig_num in enumerate(plt.get_fignums()):
73
+ fig = plt.figure(fig_num)
74
+ img_path = os.path.join(exec_dir, f"plot_{i}.png")
75
+ fig.savefig(img_path)
76
+ with open(img_path, "rb") as img_file:
77
+ img_data = base64.b64encode(img_file.read()).decode('utf-8')
78
+ result["plots"].append({"figure_number": fig_num, "data": img_data})
79
+
80
+ for var_name, var_value in self.globals.items():
81
+ if isinstance(var_value, pd.DataFrame) and len(var_value) > 0:
82
+ result["dataframes"].append({"name": var_name, "head": var_value.head().to_dict(), "shape": var_value.shape, "dtypes": str(var_value.dtypes)})
83
+
84
+ result["status"] = "success"
85
+ result["stdout"] = output_buffer.getvalue()
86
+ result["result"] = exec_result
87
+
88
+ except Exception as e:
89
+ result["status"] = "error"
90
+ result["stderr"] = f"{error_buffer.getvalue()}\n{traceback.format_exc()}"
91
+
92
+ return result
93
+
94
+ def _execute_bash(self, code: str, execution_id: str) -> dict:
95
+ try:
96
+ completed = subprocess.run(code, shell=True, capture_output=True, text=True, timeout=self.max_execution_time)
97
+ return {"execution_id": execution_id, "status": "success" if completed.returncode == 0 else "error", "stdout": completed.stdout, "stderr": completed.stderr, "result": None, "plots": [], "dataframes": []}
98
+ except subprocess.TimeoutExpired:
99
+ return {"execution_id": execution_id, "status": "error", "stdout": "", "stderr": "Execution timed out.", "result": None, "plots": [], "dataframes": []}
100
+
101
+ def _execute_sql(self, code: str, execution_id: str) -> dict:
102
+ result = {"execution_id": execution_id, "status": "error", "stdout": "", "stderr": "", "result": None, "plots": [], "dataframes": []}
103
+ try:
104
+ conn = sqlite3.connect(self.temp_sqlite_db)
105
+ cur = conn.cursor()
106
+ cur.execute(code)
107
+ if code.strip().lower().startswith("select"):
108
+ columns = [description[0] for description in cur.description]
109
+ rows = cur.fetchall()
110
+ df = pd.DataFrame(rows, columns=columns)
111
+ result["dataframes"].append({"name": "query_result", "head": df.head().to_dict(), "shape": df.shape, "dtypes": str(df.dtypes)})
112
+ else:
113
+ conn.commit()
114
+ result["status"] = "success"
115
+ result["stdout"] = "Query executed successfully."
116
+
117
+ except Exception as e:
118
+ result["stderr"] = str(e)
119
+ finally:
120
+ conn.close()
121
+
122
+ return result
123
+
124
+ def _execute_c(self, code: str, execution_id: str) -> dict:
125
+ temp_dir = tempfile.mkdtemp()
126
+ source_path = os.path.join(temp_dir, "program.c")
127
+ binary_path = os.path.join(temp_dir, "program")
128
+
129
+ try:
130
+ with open(source_path, "w") as f:
131
+ f.write(code)
132
+
133
+ compile_proc = subprocess.run(["gcc", source_path, "-o", binary_path], capture_output=True, text=True, timeout=self.max_execution_time)
134
+ if compile_proc.returncode != 0:
135
+ return {"execution_id": execution_id, "status": "error", "stdout": compile_proc.stdout, "stderr": compile_proc.stderr, "result": None, "plots": [], "dataframes": []}
136
+
137
+ run_proc = subprocess.run([binary_path], capture_output=True, text=True, timeout=self.max_execution_time)
138
+ return {"execution_id": execution_id, "status": "success" if run_proc.returncode == 0 else "error", "stdout": run_proc.stdout, "stderr": run_proc.stderr, "result": None, "plots": [], "dataframes": []}
139
+ except Exception as e: return {"execution_id": execution_id, "status": "error", "stdout": "", "stderr": str(e), "result": None, "plots": [], "dataframes": []}
140
+
141
+ def _execute_java(self, code: str, execution_id: str) -> dict:
142
+ temp_dir = tempfile.mkdtemp()
143
+ source_path = os.path.join(temp_dir, "Main.java")
144
+
145
+ try:
146
+ with open(source_path, "w") as f:
147
+ f.write(code)
148
+
149
+ compile_proc = subprocess.run(["javac", source_path], capture_output=True, text=True, timeout=self.max_execution_time)
150
+ if compile_proc.returncode != 0:
151
+ return {"execution_id": execution_id, "status": "error", "stdout": compile_proc.stdout, "stderr": compile_proc.stderr, "result": None, "plots": [], "dataframes": []}
152
+
153
+ run_proc = subprocess.run(["java", "-cp", temp_dir, "Main"], capture_output=True, text=True, timeout=self.max_execution_time)
154
+ return {"execution_id": execution_id, "status": "success" if run_proc.returncode == 0 else "error", "stdout": run_proc.stdout, "stderr": run_proc.stderr, "result": None, "plots": [], "dataframes": []}
155
+ except Exception as e:
156
+ return {"execution_id": execution_id, "status": "error", "stdout": "", "stderr": str(e), "result": None, "plots": [], "dataframes": []}
157
+
158
+ interpreter_instance = CodeInterpreter()
159
+
160
+ @tool
161
+ def execute_code_multilang(code: str, language: str = "python") -> str:
162
+ """Execute code in multiple languages (Python, Bash, SQL, C, Java) and return results.
163
+ Args:
164
+ code (str): The source code to execute.
165
+ language (str): The language of the code. Supported: "python", "bash", "sql", "c", "java".
166
+ Returns:
167
+ A string summarizing the execution results (stdout, stderr, errors, plots, dataframes if any).
168
+ """
169
+ supported_languages = ["python", "bash", "sql", "c", "java"]
170
+ language = language.lower()
171
+
172
+ if language not in supported_languages:
173
+ return f"❌ Unsupported language: {language}. Supported languages are: {', '.join(supported_languages)}"
174
+
175
+ result = interpreter_instance.execute_code(code, language=language)
176
+
177
+ response = []
178
+
179
+ if result["status"] == "success":
180
+ response.append(f"✅ Code executed successfully in **{language.upper()}**")
181
+
182
+ if result.get("stdout"):
183
+ response.append("\n**Standard Output:**\n```\n" + result["stdout"].strip() + "\n```")
184
+
185
+ if result.get("stderr"):
186
+ response.append(
187
+ "\n**Standard Error (if any):**\n```\n"
188
+ + result["stderr"].strip() + "\n```")
189
+
190
+ if result.get("result") is not None:
191
+ response.append(
192
+ "\n**Execution Result:**\n```\n"
193
+ + str(result["result"]).strip() + "\n```")
194
+
195
+ if result.get("dataframes"):
196
+ for df_info in result["dataframes"]:
197
+ response.append(f"\n**DataFrame `{df_info['name']}` (Shape: {df_info['shape']})**")
198
+ df_preview = pd.DataFrame(df_info["head"])
199
+ response.append("First 5 rows:\n```\n" + str(df_preview) + "\n```")
200
+
201
+ if result.get("plots"):
202
+ response.append(f"\n**Generated {len(result['plots'])} plot(s)** (Image data returned separately)")
203
+
204
+ else:
205
+ response.append(f"❌ Code execution failed in **{language.upper()}**")
206
+ if result.get("stderr"):
207
+ response.append("\n**Error Log:**\n```\n" + result["stderr"].strip() + "\n```")
208
+
209
+ return "\n".join(response)
tools/document_processing.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import requests
4
+ import tempfile
5
+ from PIL import Image
6
+ import pytesseract
7
+ import pandas as pd
8
+ from urllib.parse import urlparse
9
+ from langchain_core.tools import tool
10
+ from typing import List, Dict, Any, Optional
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
+ # Parse URL to get filename if not provided
42
+ if not filename:
43
+ path = urlparse(url).path
44
+ filename = os.path.basename(path)
45
+ if not filename:
46
+ filename = f"downloaded_{uuid.uuid4().hex[:8]}"
47
+
48
+ # Create temporary file
49
+ temp_dir = tempfile.gettempdir()
50
+ filepath = os.path.join(temp_dir, filename)
51
+
52
+ # Download the file
53
+ response = requests.get(url, stream=True)
54
+ response.raise_for_status()
55
+
56
+ # Save the file
57
+ with open(filepath, "wb") as f:
58
+ for chunk in response.iter_content(chunk_size=8192):
59
+ f.write(chunk)
60
+
61
+ return f"File downloaded to {filepath}. You can read this file to process its contents."
62
+ except Exception as e:
63
+ return f"Error downloading file: {str(e)}"
64
+
65
+ @tool
66
+ def extract_text_from_image(image_path: str) -> str:
67
+ """
68
+ Extract text from an image using OCR library pytesseract (if available).
69
+ Args:
70
+ image_path (str): the path to the image file.
71
+ """
72
+ try:
73
+ # Open the image
74
+ image = Image.open(image_path)
75
+
76
+ # Extract text from the image
77
+ text = pytesseract.image_to_string(image)
78
+
79
+ return f"Extracted text from image:\n\n{text}"
80
+ except Exception as e:
81
+ return f"Error extracting text from image: {str(e)}"
82
+
83
+ @tool
84
+ def analyze_csv_file(file_path: str, query: str) -> str:
85
+ """
86
+ Analyze a CSV file using pandas and answer a question about it.
87
+ Args:
88
+ file_path (str): the path to the CSV file.
89
+ query (str): Question about the data
90
+ """
91
+ try:
92
+ # Read the CSV file
93
+ df = pd.read_csv(file_path)
94
+
95
+ # Run various analyses based on the query
96
+ result = f"CSV file loaded with {len(df)} rows and {len(df.columns)} columns.\n"
97
+ result += f"Columns: {', '.join(df.columns)}\n\n"
98
+
99
+ # Add summary statistics
100
+ result += "Summary statistics:\n"
101
+ result += str(df.describe())
102
+
103
+ return result
104
+
105
+ except Exception as e:
106
+ return f"Error analyzing CSV file: {str(e)}"
107
+
108
+ @tool
109
+ def analyze_excel_file(file_path: str, query: str) -> str:
110
+ """
111
+ Analyze an Excel file using pandas and answer a question about it.
112
+ Args:
113
+ file_path (str): the path to the Excel file.
114
+ query (str): Question about the data
115
+ """
116
+ try:
117
+ # Read the Excel file
118
+ df = pd.read_excel(file_path)
119
+
120
+ # Run various analyses based on the query
121
+ result = (
122
+ f"Excel file loaded with {len(df)} rows and {len(df.columns)} columns.\n"
123
+ )
124
+ result += f"Columns: {', '.join(df.columns)}\n\n"
125
+
126
+ # Add summary statistics
127
+ result += "Summary statistics:\n"
128
+ result += str(df.describe())
129
+
130
+ return result
131
+
132
+ except Exception as e:
133
+ return f"Error analyzing Excel file: {str(e)}"
tools/image_processing.py ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import uuid
4
+ import base64
5
+ import numpy as np
6
+ from PIL import Image
7
+ from langchain_core.tools import tool
8
+ from typing import List, Dict, Any, Optional
9
+ from PIL import Image, ImageDraw, ImageFont, ImageEnhance, ImageFilter
10
+
11
+ # Helper functions for image processing
12
+ def encode_image(image_path: str) -> str:
13
+ """Convert an image file to base64 string."""
14
+ with open(image_path, "rb") as image_file:
15
+ return base64.b64encode(image_file.read()).decode("utf-8")
16
+
17
+
18
+ def decode_image(base64_string: str) -> Image.Image:
19
+ """Convert a base64 string to a PIL Image."""
20
+ image_data = base64.b64decode(base64_string)
21
+ return Image.open(io.BytesIO(image_data))
22
+
23
+
24
+ def save_image(image: Image.Image, directory: str = "image_outputs") -> str:
25
+ """Save a PIL Image to disk and return the path."""
26
+ os.makedirs(directory, exist_ok=True)
27
+ image_id = str(uuid.uuid4())
28
+ image_path = os.path.join(directory, f"{image_id}.png")
29
+ image.save(image_path)
30
+ return image_path
31
+
32
+
33
+ @tool
34
+ def analyze_image(image_base64: str) -> Dict[str, Any]:
35
+ """
36
+ Analyze basic properties of an image (size, mode, color analysis, thumbnail preview).
37
+ Args:
38
+ image_base64 (str): Base64 encoded image string
39
+ Returns:
40
+ Dictionary with analysis result
41
+ """
42
+ try:
43
+ img = decode_image(image_base64)
44
+ width, height = img.size
45
+ mode = img.mode
46
+
47
+ if mode in ("RGB", "RGBA"):
48
+ arr = np.array(img)
49
+ avg_colors = arr.mean(axis=(0, 1))
50
+ dominant = ["Red", "Green", "Blue"][np.argmax(avg_colors[:3])]
51
+ brightness = avg_colors.mean()
52
+ color_analysis = {
53
+ "average_rgb": avg_colors.tolist(),
54
+ "brightness": brightness,
55
+ "dominant_color": dominant,
56
+ }
57
+ else:
58
+ color_analysis = {"note": f"No color analysis for mode {mode}"}
59
+
60
+ thumbnail = img.copy()
61
+ thumbnail.thumbnail((100, 100))
62
+ thumb_path = save_image(thumbnail, "thumbnails")
63
+ thumbnail_base64 = encode_image(thumb_path)
64
+
65
+ return {
66
+ "dimensions": (width, height),
67
+ "mode": mode,
68
+ "color_analysis": color_analysis,
69
+ "thumbnail": thumbnail_base64,
70
+ }
71
+ except Exception as e:
72
+ return {"error": str(e)}
73
+
74
+
75
+ @tool
76
+ def transform_image(
77
+ image_base64: str, operation: str, params: Optional[Dict[str, Any]] = None
78
+ ) -> Dict[str, Any]:
79
+ """
80
+ Apply transformations: resize, rotate, crop, flip, brightness, contrast, blur, sharpen, grayscale.
81
+ Args:
82
+ image_base64 (str): Base64 encoded input image
83
+ operation (str): Transformation operation
84
+ params (Dict[str, Any], optional): Parameters for the operation
85
+ Returns:
86
+ Dictionary with transformed image (base64)
87
+ """
88
+ try:
89
+ img = decode_image(image_base64)
90
+ params = params or {}
91
+
92
+ if operation == "resize":
93
+ img = img.resize(
94
+ (
95
+ params.get("width", img.width // 2),
96
+ params.get("height", img.height // 2),
97
+ )
98
+ )
99
+ elif operation == "rotate":
100
+ img = img.rotate(params.get("angle", 90), expand=True)
101
+ elif operation == "crop":
102
+ img = img.crop(
103
+ (
104
+ params.get("left", 0),
105
+ params.get("top", 0),
106
+ params.get("right", img.width),
107
+ params.get("bottom", img.height),
108
+ )
109
+ )
110
+ elif operation == "flip":
111
+ if params.get("direction", "horizontal") == "horizontal":
112
+ img = img.transpose(Image.FLIP_LEFT_RIGHT)
113
+ else:
114
+ img = img.transpose(Image.FLIP_TOP_BOTTOM)
115
+ elif operation == "adjust_brightness":
116
+ img = ImageEnhance.Brightness(img).enhance(params.get("factor", 1.5))
117
+ elif operation == "adjust_contrast":
118
+ img = ImageEnhance.Contrast(img).enhance(params.get("factor", 1.5))
119
+ elif operation == "blur":
120
+ img = img.filter(ImageFilter.GaussianBlur(params.get("radius", 2)))
121
+ elif operation == "sharpen":
122
+ img = img.filter(ImageFilter.SHARPEN)
123
+ elif operation == "grayscale":
124
+ img = img.convert("L")
125
+ else:
126
+ return {"error": f"Unknown operation: {operation}"}
127
+
128
+ result_path = save_image(img)
129
+ result_base64 = encode_image(result_path)
130
+ return {"transformed_image": result_base64}
131
+
132
+ except Exception as e:
133
+ return {"error": str(e)}
134
+
135
+
136
+ @tool
137
+ def draw_on_image(
138
+ image_base64: str, drawing_type: str, params: Dict[str, Any]
139
+ ) -> Dict[str, Any]:
140
+ """
141
+ Draw shapes (rectangle, circle, line) or text onto an image.
142
+ Args:
143
+ image_base64 (str): Base64 encoded input image
144
+ drawing_type (str): Drawing type
145
+ params (Dict[str, Any]): Drawing parameters
146
+ Returns:
147
+ Dictionary with result image (base64)
148
+ """
149
+ try:
150
+ img = decode_image(image_base64)
151
+ draw = ImageDraw.Draw(img)
152
+ color = params.get("color", "red")
153
+
154
+ if drawing_type == "rectangle":
155
+ draw.rectangle(
156
+ [params["left"], params["top"], params["right"], params["bottom"]],
157
+ outline=color,
158
+ width=params.get("width", 2),
159
+ )
160
+ elif drawing_type == "circle":
161
+ x, y, r = params["x"], params["y"], params["radius"]
162
+ draw.ellipse(
163
+ (x - r, y - r, x + r, y + r),
164
+ outline=color,
165
+ width=params.get("width", 2),
166
+ )
167
+ elif drawing_type == "line":
168
+ draw.line(
169
+ (
170
+ params["start_x"],
171
+ params["start_y"],
172
+ params["end_x"],
173
+ params["end_y"],
174
+ ),
175
+ fill=color,
176
+ width=params.get("width", 2),
177
+ )
178
+ elif drawing_type == "text":
179
+ font_size = params.get("font_size", 20)
180
+ try:
181
+ font = ImageFont.truetype("arial.ttf", font_size)
182
+ except IOError:
183
+ font = ImageFont.load_default()
184
+ draw.text(
185
+ (params["x"], params["y"]),
186
+ params.get("text", "Text"),
187
+ fill=color,
188
+ font=font,
189
+ )
190
+ else:
191
+ return {"error": f"Unknown drawing type: {drawing_type}"}
192
+
193
+ result_path = save_image(img)
194
+ result_base64 = encode_image(result_path)
195
+ return {"result_image": result_base64}
196
+
197
+ except Exception as e:
198
+ return {"error": str(e)}
199
+
200
+
201
+ @tool
202
+ def generate_simple_image(
203
+ image_type: str,
204
+ width: int = 500,
205
+ height: int = 500,
206
+ params: Optional[Dict[str, Any]] = None,
207
+ ) -> Dict[str, Any]:
208
+ """
209
+ Generate a simple image (gradient, noise, pattern, chart).
210
+ Args:
211
+ image_type (str): Type of image
212
+ width (int), height (int)
213
+ params (Dict[str, Any], optional): Specific parameters
214
+ Returns:
215
+ Dictionary with generated image (base64)
216
+ """
217
+ try:
218
+ params = params or {}
219
+
220
+ if image_type == "gradient":
221
+ direction = params.get("direction", "horizontal")
222
+ start_color = params.get("start_color", (255, 0, 0))
223
+ end_color = params.get("end_color", (0, 0, 255))
224
+
225
+ img = Image.new("RGB", (width, height))
226
+ draw = ImageDraw.Draw(img)
227
+
228
+ if direction == "horizontal":
229
+ for x in range(width):
230
+ r = int(
231
+ start_color[0] + (end_color[0] - start_color[0]) * x / width
232
+ )
233
+ g = int(
234
+ start_color[1] + (end_color[1] - start_color[1]) * x / width
235
+ )
236
+ b = int(
237
+ start_color[2] + (end_color[2] - start_color[2]) * x / width
238
+ )
239
+ draw.line([(x, 0), (x, height)], fill=(r, g, b))
240
+ else:
241
+ for y in range(height):
242
+ r = int(
243
+ start_color[0] + (end_color[0] - start_color[0]) * y / height
244
+ )
245
+ g = int(
246
+ start_color[1] + (end_color[1] - start_color[1]) * y / height
247
+ )
248
+ b = int(
249
+ start_color[2] + (end_color[2] - start_color[2]) * y / height
250
+ )
251
+ draw.line([(0, y), (width, y)], fill=(r, g, b))
252
+
253
+ elif image_type == "noise":
254
+ noise_array = np.random.randint(0, 256, (height, width, 3), dtype=np.uint8)
255
+ img = Image.fromarray(noise_array, "RGB")
256
+
257
+ else:
258
+ return {"error": f"Unsupported image_type {image_type}"}
259
+
260
+ result_path = save_image(img)
261
+ result_base64 = encode_image(result_path)
262
+ return {"generated_image": result_base64}
263
+
264
+ except Exception as e:
265
+ return {"error": str(e)}
266
+
267
+
268
+ @tool
269
+ def combine_images(
270
+ images_base64: List[str], operation: str, params: Optional[Dict[str, Any]] = None
271
+ ) -> Dict[str, Any]:
272
+ """
273
+ Combine multiple images (collage, stack, blend).
274
+ Args:
275
+ images_base64 (List[str]): List of base64 images
276
+ operation (str): Combination type
277
+ params (Dict[str, Any], optional)
278
+ Returns:
279
+ Dictionary with combined image (base64)
280
+ """
281
+ try:
282
+ images = [decode_image(b64) for b64 in images_base64]
283
+ params = params or {}
284
+
285
+ if operation == "stack":
286
+ direction = params.get("direction", "horizontal")
287
+ if direction == "horizontal":
288
+ total_width = sum(img.width for img in images)
289
+ max_height = max(img.height for img in images)
290
+ new_img = Image.new("RGB", (total_width, max_height))
291
+ x = 0
292
+ for img in images:
293
+ new_img.paste(img, (x, 0))
294
+ x += img.width
295
+ else:
296
+ max_width = max(img.width for img in images)
297
+ total_height = sum(img.height for img in images)
298
+ new_img = Image.new("RGB", (max_width, total_height))
299
+ y = 0
300
+ for img in images:
301
+ new_img.paste(img, (0, y))
302
+ y += img.height
303
+ else:
304
+ return {"error": f"Unsupported combination operation {operation}"}
305
+
306
+ result_path = save_image(new_img)
307
+ result_base64 = encode_image(result_path)
308
+ return {"combined_image": result_base64}
309
+
310
+ except Exception as e:
311
+ return {"error": str(e)}
tools/web_search.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from supabase.client import Client, create_client
3
+ from langchain_core.tools import tool
4
+ from langchain_community.tools.tavily_search import TavilySearchResults
5
+ from langchain_community.document_loaders import WikipediaLoader
6
+ from langchain_community.document_loaders import ArxivLoader
7
+ from langchain_huggingface import HuggingFaceEmbeddings
8
+ from langchain_community.vectorstores import SupabaseVectorStore
9
+ from langchain.tools.retriever import create_retriever_tool
10
+
11
+
12
+ @tool
13
+ def wiki_search(query: str) -> str:
14
+ """Search Wikipedia for a query and return maximum 2 results.
15
+
16
+ Args:
17
+ query: The search query."""
18
+ search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
19
+ formatted_search_docs = "\n\n---\n\n".join([f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>' for doc in search_docs])
20
+ return {"wiki_results": formatted_search_docs}
21
+
22
+ @tool
23
+ def web_search(query: str) -> str:
24
+ """Search Tavily for a query and return maximum 3 results.
25
+
26
+ Args:
27
+ query: The search query."""
28
+ search_docs = TavilySearchResults(max_results=3).invoke(query=query)
29
+ formatted_search_docs = "\n\n---\n\n".join([f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>' for doc in search_docs])
30
+ return {"web_results": formatted_search_docs}
31
+
32
+ @tool
33
+ def arxiv_search(query: str) -> str:
34
+ """Search Arxiv for a query and return maximum 3 result.
35
+
36
+ Args:
37
+ query: The search query."""
38
+ search_docs = ArxivLoader(query=query, load_max_docs=3).load()
39
+ formatted_search_docs = "\n\n---\n\n".join([f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>' for doc in search_docs])
40
+ return {"arxiv_results": formatted_search_docs}
41
+
42
+ @tool
43
+ def similar_question_search(question: str) -> str:
44
+ """Search the vector database for similar questions and return the first results.
45
+
46
+ Args:
47
+ question: the question human provided."""
48
+ matched_docs = vector_store.similarity_search(question, 3)
49
+ formatted_search_docs = "\n\n---\n\n".join([f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>' for doc in matched_docs])
50
+ return {"similar_questions": formatted_search_docs}