File size: 5,871 Bytes
9b1db64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f47d72
 
 
9b1db64
 
 
4f47d72
9b1db64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import datetime
import time

import xml.etree.ElementTree as ET
from http.client import responses

import pandas as pd
import pytz
import requests
import os
import base64
import io

from PIL import Image
from openai import AzureOpenAI
from six import binary_type
from smolagents import tool, DuckDuckGoSearchTool
from tavily import TavilyClient
from langchain_community.document_loaders import WikipediaLoader

# === Tools ===
@tool
def wiki_search(query: str) -> str:
    """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 formatted_search_docs

# tool for websearch capabilities
# must improve fall back for timeout errors
client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])

@tool
def web_search(query: str) -> str:
    """Search Tavily for a query and return up to 3 results.
    Args:
        query: The search query.
    """
    try:
        results = client.search(query=query, max_results=3)
        formatted = "\n\n---\n\n".join(
            f"<Document source='{item.get('url', '')}'>\n{item.get('content', '').strip()}\n</Document>"
            for item in results.get("results", [])
        )
        return formatted or "No relevant search results found."
    except Exception as e:
        return f"[web_search error]: {str(e)}"


# tool to obtain real current time zone
@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """Fetches the current local time in a specified timezone.
    Args:
        timezone: A string representing a valid timezone (e.g., 'America/New_York').
    """
    try:
        tz = pytz.timezone(timezone)
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
        return f"The current local time in {timezone} is: {local_time}"
    except Exception as e:
        return f"Error fetching time for timezone '{timezone}': {str(e)}"

# tool to get the HTML content of a web page
@tool
def visit_webpage(url: str) -> str:
    """Fetches raw HTML content of a web page.
    Args:
        url: The url of the webpage.
    """
    try:
        response = requests.get(url, timeout=5)
        return response.text#[:5000]  # Limit length
    except Exception as e:
        return f"[ERROR fetching {url}]: {str(e)}"

# tool for add operations
@tool
def calculator_add(a: int, b: int) -> int:
    """Add two numbers.
    Args:
        a: first int
        b: second int
    """
    return a + b

# tool for image understanding
@tool
def ocr(base64_image: str) -> str:
    """Analyzes the content of an image using gpt-4o.
    Args:
        base64_image: A base64-encoded string of the image.
    Returns: a string summary or description of what the image contains.
    """
    client = AzureOpenAI(
        azure_endpoint=os.environ.get("AZendpoint"),
        api_version=os.environ.get("api_version"),
        api_key=os.environ.get("api_key")
    )

    response = client.chat.completions.create(
        model=os.environ["model"],
        messages=[
            {"role": "user", "content": [
                {"type": "text", "text": "Describe the image"},
                {"type": "image_url", "image_url": {
                    "url": "data:image/jpeg;base64," + base64_image
                }}
            ]}
        ]
    )

    return response.choices[0].message.content

# tool for data parsing
@tool
def parse_excel(base64_excel: str) -> str:
    """
    Parses a base64-encoded Excel file and returns the first few rows as text.
    Args:
        base64_excel: Base64-encoded Excel file (.xlxs or .xls)
    Returns: a preview of the Excel data (first 5 rows).
    """
    try:
        # decode base64 and read into a df
        binary_data = base64.b64decode(base64_excel)
        df = pd.read_excel(io.BytesIO(binary_data))

        #optional customize logic based on column names
        preview = df.head().to_string(index=False)
        return f"Excel preview: \n{preview}"

    except Exception as e:
        return f"[ERROR] Failed to parse Excel file: {str(e)}"

@tool
def arxiv_search(query: str) -> str:
    """
    Search ArXiv for a query and return a summary of up to 3 papers.
    Args:
        query: The search string used to find relevant papers on ArXiv.
    Returns:
        A formatted string summarizing up to 3 relevant papers.
    """
    try:
        # api url and query parameters
        url = "http://export.arxiv.org/api/query"
        params = {
        "search_query": query,
        "start": 0,
        "max_results": 3,
        "sortBy": "relevance"
        }
        # making the api request
        response = requests.get(url, params=params, timeout=10)
        response.raise_for_status()

        # parse the xml response
        root = ET.fromstring(response.text)                 # converts the xml string into an element tree object
        ns = {"atom": "http://www.w3.org/2005/Atom"}        # declares xml namespaces (required for correct parsing)
        entries = root.findall("atom:entry", ns)       # retrieves all <entry> elements from the feed

        if not entries:
            return "No results found on ArXiv"

        results = []
        for entry in entries:
            title = entry.find("atom:title", ns).text.strip()
            summary = entry.find("atom:summary", ns).text.strip()
            link = entry.find("atom:id", ns).text.strip()

            results.append(f"📄 **{title}**\n🔗 {link}\n\n{summary[:1000]}")

        return "\n\n---\n\n".join(results)

    except Exception as e:
        return f"[ArXiv tool error]: {str(e)}"