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")