File size: 7,557 Bytes
5dfcdef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6109e7b
5dfcdef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from langchain_community.document_loaders import WikipediaLoader
from langchain_community.document_loaders import ArxivLoader
from langchain_community.retrievers import WikipediaRetriever
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.tools import DuckDuckGoSearchResults
from langchain_core.tools import tool

import io
import openpyxl
import os
# from smolagents import tool
import requests
from PIL import Image
from bs4 import BeautifulSoup

@tool
def web_search(query: str) -> dict:
    """Search Tavily for a query and return maximum 3 results.
    Args:
        query: The search query."""
    search_docs = DuckDuckGoSearchResults(max_results=3, output_format='list').invoke(query)
    formatted_search_docs = "\n\n---\n\n".join(
        [
            f'<Document source="{doc.get("link", "")}" title="{doc.get("title", "")}"/>\n{doc.get("snippet", "")}\n</Document>'
            for doc in search_docs
        ]
    )
    return {"web_results": formatted_search_docs}

# @tool
# def wiki_search(query: str) -> dict:
#     """Search Wikipedia for a query and return maximum 2 results.
    
#     Args:
#         query: The search query."""
#     search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
#     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
#         ])
#     return {"wiki_results": formatted_search_docs}

# @tool
# def wiki_search(query: str) -> dict:
#     """Search Wikipedia for a query and return maximum 2 results.
    
#     Args:
#         query: The search query."""
#     search_docs = WikipediaRetriever(load_max_docs=5).invoke(query)
#     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
#         ])
#     return {"wiki_results": formatted_search_docs}

@tool
def wiki_search(query: str) -> dict:
    """Search Wikipedia for a query and return maximum 1 results.
    
    Args:
        query: The search query."""
    search_docs = WikipediaRetriever(load_max_docs=1, top_k_results=2).invoke(query)
    wiki_results = []

    for doc in search_docs:
        url = doc.metadata["source"] if doc else ""
        print(url)

        response = requests.get(url)
        response_text = response.text

        soup = BeautifulSoup(response_text, "html.parser")
        wiki_results.append(' '.join(soup.get_text().split())[:20000])

    return {"wiki_results": wiki_results}

@tool
def reverse_string(query: str) -> dict:
    """Reverse the input string.
    
    Args:
        query: The input string to reverse."""
    return {"reversed_string": query[::-1]}

@tool
def calculator(expression: str) -> dict:
    """Perform mathematical calculations and return the result.
    
    This calculator can handle:
    - Basic arithmetic: +, -, *, /, % (modulus)
    - Parentheses for order of operations
    - Decimal numbers
    - Multiple operations in one expression
    
    Args:
        expression: A mathematical expression as a string
    
    Returns:
        A string containing the calculation result
    
    Examples:
        calculator("25 * 4") -> "100"
        calculator("100 / 5") -> "20.0"
        calculator("(15 + 30) * 2") -> "90"
        calculator("50 - 20 + 10") -> "40"
        calculator("17 % 5") -> "2"
        calculator("100 % 7") -> "2"
        calculator("(20 + 5) % 8") -> "5"
    """
    try:
        # Clean the expression
        expression = expression.strip()
        
        # Validate that the expression only contains safe characters (now including %)
        allowed_chars = set('0123456789+-*/.()% ')
        if not all(c in allowed_chars for c in expression):
            raise ValueError("Expression contains invalid characters. Only numbers and +, -, *, /, %, (, ) are allowed.")
        
        result = eval(expression)
        
        # Format the result
        if isinstance(result, float) and result.is_integer():
            return str(int(result))
        else:
            return str(result)
            
    except ZeroDivisionError:
        return "Error: Cannot divide by zero or modulus by zero"
    except SyntaxError:
        return f"Error: Invalid mathematical expression: {expression}"
    except Exception as e:
        return f"Error: {str(e)}"

@tool
def tool_read_files(filepath: str) -> str:
    """
    Downloads a .py or .xlsx file from a remote URL and returns its contents as plain text.
    Raises a recoverable exception if the file does not end with .py or .xlsx.
    Args:
        filepath: The path to the Python (.py) or Excel (.xlsx) file.
    """
    root_url = "https://agents-course-unit4-scoring.hf.space/files/"
    # Strip the file extension from the url before downloading
    base, ext = os.path.splitext(filepath)
    url = root_url + base

    if filepath.endswith('.py'):
        response = requests.get(url)
        if response.status_code != 200:
            raise Exception(f"Recoverable: Failed to download file from {url}")
        return response.text

    elif filepath.endswith('.xlsx'):
        response = requests.get(url)
        if response.status_code != 200:
            raise Exception(f"Recoverable: Failed to download file from {url}")

        wb = openpyxl.load_workbook(io.BytesIO(response.content), data_only=True)
        result = []
        for sheet in wb.worksheets:
            result.append(f"# Sheet: {sheet.title}")
            for row in sheet.iter_rows(values_only=True):
                result.append(','.join([str(cell) if cell is not None else '' for cell in row]))
        return '\n'.join(result)

    else:
        raise Exception("Recoverable: Only .py and .xlsx files can be read with this tool.")

@tool
def tool_download_image(filepath: str) -> str:
    """
    Downloads an image file (.png, .jpg, .jpeg) from a remote URL and returns useful information about the image.
    This includes the image URL and basic metadata like dimensions and format.
    Raises a recoverable exception if the file is not a supported image type.
    Args:
        filepath: The path to the image file.
    """
    root_url = "https://agents-course-unit4-scoring.hf.space/files/"
    base, ext = os.path.splitext(filepath)
    url = root_url + base
    
    if ext.lower() in ['.png', '.jpg', '.jpeg']:
        response = requests.get(url)
        if response.status_code != 200:
            raise Exception(f"Recoverable: Failed to download image from {url}")
        
        # Get image metadata using Pillow
        try:
            
            img = Image.open(io.BytesIO(response.content))
            width, height = img.size
            format = img.format
            mode = img.mode
            
            # Return useful information about the image
            return f"Image URL: {url}\nFormat: {format}\nDimensions: {width}x{height}\nMode: {mode}"
        except ImportError:
            # Fallback if PIL is not available
            content_type = response.headers.get('Content-Type', 'unknown')
            content_length = response.headers.get('Content-Length', 'unknown')
            return f"Content-Type: {content_type}\nSize: {content_length} bytes"
    else:
        raise Exception("Recoverable: Only .png, .jpg, and .jpeg files can be processed with this tool.")