File size: 9,720 Bytes
6692c96
12c47a4
783d369
12c47a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
783d369
12c47a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fc21e56
 
12c47a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8eea4d3
 
 
 
 
 
 
 
 
 
 
 
12c47a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8eea4d3
 
12c47a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8eea4d3
12c47a4
 
 
 
 
 
 
 
 
 
 
 
 
 
8eea4d3
 
 
 
 
 
 
 
 
12c47a4
 
 
8eea4d3
 
 
12c47a4
 
 
 
 
 
 
 
 
 
 
 
 
 
783d369
12c47a4
 
 
 
 
 
 
 
 
783d369
12c47a4
783d369
12c47a4
 
887480a
 
 
 
 
 
 
 
 
 
 
 
 
25e2c90
 
 
887480a
 
 
 
25e2c90
887480a
 
 
 
 
25e2c90
887480a
 
b477d60
12c47a4
 
 
 
 
 
 
 
36a7313
 
 
 
12c47a4
b477d60
55eb2ba
12c47a4
 
8a5e198
12c47a4
36a7313
 
 
 
12c47a4
 
 
 
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
from smolagents import DuckDuckGoSearchTool, GoogleSearchTool
from youtube_transcript_api import YouTubeTranscriptApi
from supadata import Supadata, SupadataError
import wikipedia
from wikipedia_tables_parser import fetch_wikipedia_tables
import pandas as pd
from typing import Any
import os
from dotenv import load_dotenv

load_dotenv()
import importlib.util
import sys
import io
import contextlib
from llama_index.llms.openrouter import OpenRouter
from llama_index.core.types import ChatMessage


llm = OpenRouter(
    api_key=os.getenv("OPENROUTER_API_KEY"),
    model="google/gemini-2.5-flash-preview",
    temperature=0.7,
)
supadata = Supadata(api_key=os.getenv("SUPADATA_API_KEY"))


def reverse_text(text: str, **kwargs) -> str:
    """
    Returns the reversed version of the text.
    If you receive some unknown text, that can't be recognized and analyzed, then you need to use this tool to make it clear.

    Args:
        text: text to be reversed

    Return:
        The reversed text.
    """
    try:
        print(text[::-1])
        return text[::-1]
    except Exception as e:
        raise ValueError(f"Can't reverse text: {e}")


def fetch_historical_event_data(event_name: str, year: str, **kwargs) -> str:
    """
    Fetches data about historical event that occured in certain year.
    Some examples of events: Olympics games, Footbal games, NBA etc.

    Args:
        event_name: String name of the event
        year: String year of the event

    Return:
        String with data about the event
    """
    result = wikipedia.page(f"{event_name} in {year}")

    url = result.url
    content = result.content
    try:
        tables = pd.read_html(url)
    except Exception as e:
        tables = fetch_wikipedia_tables(url)

    result = f"Content: {content}\nTables: {tables}"

    return result


def classify_fruit_vegitable(item: str, **kwargs) -> str:
    """
    Classifies items to fruits and vegitables

    Args:
        item: Item to classify

    Returns:
        Text with explanation whether it is a fruit or vegetable.
    """
    response = llm.chat(
        messages=[
            ChatMessage(
                content=f"Classify whether it is fruit or vegetable: {item}. Return only `fruit` or `vegetable` without explanations"
            )
        ]
    )
    return response.message.content


def web_search(query: str, **kwargs) -> str:
    """
    Returns web search results for the provided query.
    Don't use it for Wikipedia queries. For Wikipedia queries use wikipedia_search tool.
    Important, query is human-language string input, not the URL or key.

    Args:
        query: query to search in WEB

    Return:
        String with web search results.
    """
    result = DuckDuckGoSearchTool().forward(query)
    # result = GoogleSearchTool(provider="serpapi").forward(query)
    print(result)
    return result


def wikipedia_search(query: str, **kwargs) -> Any:
    """
    Returns wikipedia search results for the provided query.

    Args:
    query: query to search in WIKIPEDIA

    Return:
    Wikipedia search results.
    """
    result = wikipedia.page(query)

    url = result.url
    content = result.content
    try:
        tables = pd.read_html(url)
    except:
        tables = fetch_wikipedia_tables(url)

    result = f"Content: {content}\nTables: {tables}"

    return result


def multiply(a: float, b: float, **kwargs) -> float:
    """
    Multiply two numbers.

    Args:
    a: First number
    b: Second number

    Return:
    The product of the two numbers.
    """
    return a * b


def compute_sum(values: list[int | float], **kwargs) -> float:
    """
    Computes sum of provided values

    Args:
        values: list of integer or float values
    Return:
        Sum of the values
    """
    return sum(values)


def length(iterable: Any, **kwargs) -> int:
    """
    Return the length of an iterable.

    Args:
    iterable: Any iterable

    Return:
    The length of the iterable.
    """
    return len(iterable)


def execute_python_file(file_path: str) -> Any:
    """
    Executes a Python file and returns its result.

    This function takes a path to a Python file, executes it by importing it as a module,
    and returns the result. The file should contain a function call that produces
    the result to be returned. This version also executes code under the
    'if __name__ == "__main__":' block.

    Args:
        file_path (str): Path to the Python file to execute.

    Returns:
        Any: The result of executing the Python file. If the file sets a variable
             named 'result', that value will be returned.

    Raises:
        FileNotFoundError: If the specified file does not exist.
        ImportError: If there was an error importing the Python file.

    Example:
        >>> # If example.py contains: result = 2 + 3
        >>> execute_python_file('example.py')
        5
    """
    # Verify file exists
    if not os.path.isfile(file_path):
        raise FileNotFoundError(f"File not found: {file_path}")

    # Get the directory and filename
    file_dir = os.path.dirname(os.path.abspath(file_path))
    file_name = os.path.basename(file_path)
    module_name = file_name.replace(".py", "")

    # Store original sys.path and add the file's directory
    original_sys_path = sys.path.copy()
    sys.path.insert(0, file_dir)

    # Prepare stdout/stderr capture
    stdout_capture = io.StringIO()
    stderr_capture = io.StringIO()

    # First approach: Import normally to get module definitions
    try:
        spec = importlib.util.spec_from_file_location(module_name, file_path)
        if spec is None or spec.loader is None:
            raise ImportError(f"Could not load module spec from {file_path}")

        module = importlib.util.module_from_spec(spec)
        sys.modules[module_name] = module

        # Execute the module
        with contextlib.redirect_stdout(stdout_capture), contextlib.redirect_stderr(
            stderr_capture
        ):
            spec.loader.exec_module(module)

            # After module is loaded, directly execute the main block by reading the file
            # and executing the content with __name__ = "__main__"
            with open(file_path, "r") as f:
                file_content = f.read()
                # Create a namespace with everything from the module
                namespace = {**module.__dict__}
                namespace["__name__"] = "__main__"
                exec(file_content, namespace)

        if hasattr(module, "result"):
            return module.result
        else:
            output = stdout_capture.getvalue().strip()
            print(f"RESULT PYTHON: {output}")
            return output

    except Exception as e:
        error_output = stderr_capture.getvalue()
        if error_output:
            raise type(e)(f"{str(e)}\nProgram output: {error_output}") from None
        else:
            raise
    finally:
        sys.path = original_sys_path

        if module_name in sys.modules:
            del sys.modules[module_name]


def trascript_youtube(video_id: str, **kwargs) -> str:
    """
    Returns transcript of YouTube video.

    Args:
        video_id: ID of youtube video (Pass in the video ID, NOT the video URL. For a video with the URL https://www.youtube.com/watch?v=12345 the ID is 12345.)

    Return:
    Transcript of YouTube video.
    """
    transcript = supadata.youtube.transcript(video_id=video_id, lang="en")

    return transcript.content


def read_excel(path: str, **kwargs) -> pd.DataFrame:
    """
    Reads xlsx file

    Args:
        path: path to xlsx file

    Return:
    Pandas dataframe
    """
    return pd.read_excel(path)


def pandas_column_sum(
    pandas_column_values: list[int | float], column_name: str, **kwargs
) -> float:
    """
    Computes sum on pandas dataframe column

    Args:
        pandas_column_values: List with float or integer pandas values
        column_name: Name of the column

    Return:
    Sum of the column
    """
    return sum(pandas_column_values)


def final_answer(answer: str, **kwargs) -> str:
    """
    Prepare the final answer for the user. It should be always used as a last step.

    Args:
        answer: The answer to format and return to the user
    Return:
        The final answer.
    """
    resp = llm.chat(
        messages=[
            ChatMessage(
                content=f"""
    Final answer from agent: {answer}
    Make final answer as short as possible.
    Final answer should be a number or as few words as possible or a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
    There might be requested exact number, then you need to compress the output so that it was only number without any comments or explanations (float or integer).
    And on the other hand, the question might request some exact string value. Don't explain it, just return this value (For example, insted of `In response to the question, desired person is X` return only `X`)
    Again, you don't need to modify or solve answer, you just need to format it properly.
    """
            )
        ]
    )
    return resp.message.content


# print(wikipedia_search("Mercedes Sosa studio albums"))
# execute_python_file("f918266a-b3e0-4914-865d-4faa564f1aef.py")