File size: 12,824 Bytes
d26c7f3
d5ce935
d26c7f3
d5ce935
 
cc6bd3b
 
 
d26c7f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d5ce935
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26c7f3
d5ce935
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26c7f3
 
 
fae0e51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26c7f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fae0e51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d26c7f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fae0e51
 
 
 
 
 
 
 
 
 
d26c7f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fae0e51
 
 
 
 
d26c7f3
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
from llama_index.core.tools import FunctionTool
# from llama_index.tools.duckduckgo import DuckDuckGoSearchToolSpec  # I encountered some issues... Sometimes it doesn't work !


from duckduckgo_search import DDGS
import pint
import sympy as sp


class _Math:

    @staticmethod
    def symbolic_calc(expression: str) -> str:
        """
        Evaluates complex mathematical expressions using SymPy.
        
        Args:
            expression: Mathematical expression as string
        
        Returns:
            Result of the calculation
        """
        print(f"-> symbolic_math_calculator tool used (input: {expression}) !")
        try:
            result = sp.sympify(expression)
            print(f"-> (output: {result}) !")
            return str(result)
        except Exception as e:
            return f"Error in calculation: {str(e)}"

    @staticmethod
    def unit_converter(value: float, from_unit: str, to_unit: str) -> str:
        """
        Converts values between different units of measurement.
        
        Args:
            value: The numerical value to convert
            from_unit: The source unit (e.g., 'meter', 'kg', 'celsius')
            to_unit: The target unit (e.g., 'feet', 'pound', 'fahrenheit')
        
        Returns:
            The converted value with appropriate units
        """
        print(f"-> unit_converter tool used (inputs: [value: {value}, from_unit: {from_unit}, to_unit: {to_unit}]) !")
        try:
            # Create unit registry
            ureg = pint.UnitRegistry()
            
            # Create quantity with source unit
            quantity = value * ureg(from_unit)
            
            # Convert to target unit
            result = quantity.to(to_unit)
            
            answer = f"{value} {from_unit} = {result.magnitude} {to_unit}"
            print(f"-> (output: {result}) !")
            return answer
        except Exception as e:
            return f"Error in unit conversion: {str(e)}"


class _MathToolbox:
    symbolic_calc = FunctionTool.from_defaults(
        name="symbolic_calc",
        description="Evaluates complex mathematical expressions using SymPy",
        fn=_Math.symbolic_calc
    )
    unit_converter = FunctionTool.from_defaults(
        name="unit_converter",
        description="Converts values between different units of measurement",
        fn=_Math.unit_converter
    )


class _WebSearch:
    @staticmethod
    def duckduckgo_text_search(keywords, max_results=5) -> list[dict[str, str]]:
        """DuckDuckGo text search.

        Args:
            keywords: keywords for query.
            max_results: max number of results. If None, returns results only from the first response. Defaults to 5.

        Returns:
            List of dictionaries with search results, or None if there was an error.

        Raises:
            DuckDuckGoSearchException: Base exception for duckduckgo_search errors.
            RatelimitException: Inherits from DuckDuckGoSearchException, raised for exceeding API request rate limits.
            TimeoutException: Inherits from DuckDuckGoSearchException, raised for API request timeouts.
        """
        return DDGS().text(keywords, max_results=max_results)


    @staticmethod
    def duckduckgo_images_search(keywords, license = None, max_results=5) -> list[dict[str, str]]:
        """DuckDuckGo images search.

        Args:
            keywords: keywords for query.
            license: any (All Creative Commons), Public (PublicDomain),
                Share (Free to Share and Use), ShareCommercially (Free to Share and Use Commercially),
                Modify (Free to Modify, Share, and Use), ModifyCommercially (Free to Modify, Share, and
                Use Commercially). Defaults to None.
            max_results: max number of results. If None, returns results only from the first response. Defaults to 5.

        Returns:
            List of dictionaries with images search results.

        Raises:
            DuckDuckGoSearchException: Base exception for duckduckgo_search errors.
            RatelimitException: Inherits from DuckDuckGoSearchException, raised for exceeding API request rate limits.
            TimeoutException: Inherits from DuckDuckGoSearchException, raised for API request timeouts.
        """
        return DDGS().images(keywords, license_image=license, max_results=max_results)


    @staticmethod
    def duckduckgo_videos_search(keywords, license = None, max_results=5) -> list[dict[str, str]]:
        """DuckDuckGo videos search.

        Args:
            keywords: keywords for query.
            license: creativeCommon, youtube. Defaults to None.
            max_results: max number of results. If None, returns results only from the first response. Defaults to 5.

        Returns:
            List of dictionaries with videos search results.

        Raises:
            DuckDuckGoSearchException: Base exception for duckduckgo_search errors.
            RatelimitException: Inherits from DuckDuckGoSearchException, raised for exceeding API request rate limits.
            TimeoutException: Inherits from DuckDuckGoSearchException, raised for API request timeouts.
        """
        return DDGS().videos(keywords, license_videos=license, max_results=max_results)


class _WebSearchToolbox:
    # duckduckgo_tools = DuckDuckGoSearchToolSpec().to_tool_list()  # I encountered some issues... Sometimes it doesn't work !
    duckduckgo_text_search = FunctionTool.from_defaults(
        name="duckduckgo_text_search",
        description="DuckDuckGo text search",
        fn=_WebSearch.duckduckgo_text_search
    )
    duckduckgo_images_search = FunctionTool.from_defaults(
        name="duckduckgo_images_search",
        description="DuckDuckGo images search",
        fn=_WebSearch.duckduckgo_images_search
    )
    duckduckgo_videos_search = FunctionTool.from_defaults(
        name="duckduckgo_videos_search",
        description="DuckDuckGo videos search",
        fn=_WebSearch.duckduckgo_videos_search
    )


class _Encryption:
    
    @staticmethod
    def ascii_encode(text: str) -> str:
        """
        Converts each character in a string to its ASCII value.
        
        Args:
            text: The text to encode
            
        Returns:
            Space-separated ASCII values
        """
        print(f"-> ascii_encode tool used (input: {text[:30]}...) !")
        try:
            ascii_values = [str(ord(char)) for char in text]
            result = " ".join(ascii_values)
            return result
        except Exception as e:
            return f"Error in ASCII encoding: {str(e)}"
    
    @staticmethod
    def ascii_decode(text: str) -> str:
        """
        Converts space-separated ASCII values back to characters.
        
        Args:
            text: Space-separated ASCII values
            
        Returns:
            Decoded string
        """
        print(f"-> ascii_decode tool used (input: {text[:30]}...) !")
        try:
            ascii_values = text.split()
            result = "".join([chr(int(value)) for value in ascii_values])
            return result
        except Exception as e:
            return f"Error in ASCII decoding: {str(e)}"

    @staticmethod
    def base64_encode(text: str) -> str:
        """
        Encodes a string to base64.
        
        Args:
            text: The text to encode
            
        Returns:
            Base64 encoded string
        """
        print(f"-> base64_encode tool used (input: {text[:30]}...) !")
        import base64
        try:
            encoded_bytes = base64.b64encode(text.encode('utf-8'))
            encoded_text = encoded_bytes.decode('utf-8')
            return encoded_text
        except Exception as e:
            return f"Error in base64 encoding: {str(e)}"
    
    @staticmethod
    def base64_decode(encoded_text: str) -> str:
        """
        Decodes a base64 string to plain text.
        
        Args:
            encoded_text: The base64 encoded text
            
        Returns:
            Decoded string
        """
        print(f"-> base64_decode tool used (input: {encoded_text[:30]}...) !")
        import base64
        try:
            decoded_bytes = base64.b64decode(encoded_text)
            decoded_text = decoded_bytes.decode('utf-8')
            return decoded_text
        except Exception as e:
            return f"Error in base64 decoding: {str(e)}"
    
    @staticmethod
    def caesar_cipher_encode(text: str, shift: int) -> str:
        """
        Encodes text using Caesar cipher with specified shift.
        
        Args:
            text: The text to encode
            shift: Number of positions to shift each character
            
        Returns:
            Caesar cipher encoded string
        """
        print(f"-> caesar_cipher_encode tool used (input: {text[:30]}..., shift: {shift}) !")
        result = ""
        try:
            for char in text:
                if char.isalpha():
                    ascii_offset = ord('a') if char.islower() else ord('A')
                    encoded_char = chr((ord(char) - ascii_offset + shift) % 26 + ascii_offset)
                    result += encoded_char
                else:
                    result += char
            return result
        except Exception as e:
            return f"Error in Caesar cipher encoding: {str(e)}"
    
    @classmethod
    def caesar_cipher_decode(cls, encoded_text: str, shift: int) -> str:
        """
        Decodes Caesar cipher text with specified shift.
        
        Args:
            encoded_text: The encoded text
            shift: Number of positions the text was shifted
            
        Returns:
            Decoded string
        """
        print(f"-> caesar_cipher_decode tool used (input: {encoded_text[:30]}..., shift: {shift}) !")
        # To decode, we shift in the opposite direction
        return cls.caesar_cipher_encode(encoded_text, -shift)
    
    @classmethod
    def caesar_cipher_brute_force(cls, text: str) -> str:
        """
        Performs a brute force attack on a Caesar cipher by trying all 26 shifts.
        
        Args:
            text: The Caesar cipher encoded text
            
        Returns:
            All possible decoding results with their respective shifts
        """
        print(f"-> caesar_cipher_brute_force tool used (input: {text[:30]}...) !")
        results = []
        
        # Try all 26 possible shifts for English alphabet
        for shift in range(26):
            decoded = cls.caesar_cipher_decode(text, shift)
            results.append(f"Shift {shift}: {decoded}")
        
        output = "\n".join(results)
        return output

    @staticmethod
    def reverse_string(text: str) -> str:
        """
        Reverses a string.
        
        Args:
            text: The text to reverse
            
        Returns:
            Reversed string
        """
        print(f"-> reverse_string tool used (input: {text[:30]}...) !")
        reversed_text = text[::-1]
        return reversed_text


class _EncryptionToolbox:
    ascii_encode = FunctionTool.from_defaults(
        name="ascii_encode",
        description="Convert each character in a string to its ASCII value",
        fn=_Encryption.ascii_encode
    )
    ascii_decode = FunctionTool.from_defaults(
        name="ascii_decode",
        description="Convert space-separated ASCII values back to characters",
        fn=_Encryption.ascii_decode
    )
    base64_encode = FunctionTool.from_defaults(
        name="base64_encode",
        description="Encode a string to base64",
        fn=_Encryption.base64_encode
    )
    base64_decode = FunctionTool.from_defaults(
        name="base64_decode",
        description="Decode a base64 string to plain text",
        fn=_Encryption.base64_decode
    )
    caesar_cipher_encode = FunctionTool.from_defaults(
        name="caesar_cipher_encode",
        description="Encode a string using Caesar cipher with specified shift",
        fn=_Encryption.caesar_cipher_encode
    )
    caesar_cipher_decode = FunctionTool.from_defaults(
        name="caesar_cipher_decode",
        description="Decode a Caesar cipher string with specified shift",
        fn=_Encryption.caesar_cipher_decode
    )
    caesar_cipher_brute_force = FunctionTool.from_defaults(
        name="caesar_cipher_brute_force",
        description="Try all 26 possible shifts to decode a Caesar cipher text",
        fn=_Encryption.caesar_cipher_brute_force
    )
    reverse_string = FunctionTool.from_defaults(
        name="reverse_string",
        description="Reverse a string",
        fn=_Encryption.reverse_string
    )


class Toolbox:
    math = _MathToolbox()
    web_search = _WebSearchToolbox()
    encryption = _EncryptionToolbox()