AlanRocha commited on
Commit
038ed0c
·
verified ·
1 Parent(s): d79066e

Create tools.py

Browse files
Files changed (1) hide show
  1. tools.py +199 -0
tools.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_community.document_loaders import WikipediaLoader
2
+ from langchain_community.document_loaders import ArxivLoader
3
+ from langchain_core.tools import tool
4
+
5
+ from youtube_transcript_api import YouTubeTranscriptApi
6
+
7
+ import os
8
+
9
+ @tool
10
+ def multiply(a: int, b: int) -> int:
11
+ """Multiply two numbers.
12
+ Args:
13
+ a: first int
14
+ b: second int
15
+ """
16
+ return a * b
17
+
18
+ @tool
19
+ def wiki_search(query: str) -> str:
20
+ """Search Wikipedia for a query and return up to 4 articles.
21
+
22
+ Args:
23
+ query: The search query."""
24
+ try:
25
+ import wikipedia
26
+ wikipedia.API_URL = "https://en.wikipedia.org/w/api.php"
27
+ wikipedia.set_rate_limiting(True)
28
+ search_docs = WikipediaLoader(query=query, load_max_docs=4).load()
29
+ except Exception as e:
30
+ return f"Wikipedia search failed: {e}"
31
+ formatted_search_docs = "\n\n---\n\n".join(
32
+ [
33
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
34
+ for doc in search_docs
35
+ ])
36
+ return formatted_search_docs or "(no Wikipedia results)"
37
+
38
+ @tool
39
+ def web_search(query: str) -> str:
40
+ """Search the public web via DuckDuckGo (no API key). Returns titles, URLs and short snippets.
41
+
42
+ Args:
43
+ query: The search query."""
44
+ try:
45
+ from ddgs import DDGS
46
+ except ImportError as e:
47
+ return f"Web search unavailable (install ddgs): {e}"
48
+ max_results = int(os.getenv("DDG_MAX_RESULTS", "8"))
49
+ q = (query or "").strip()
50
+ if not q:
51
+ return "(empty query)"
52
+ timeout = int(os.getenv("DDG_TIMEOUT", "25"))
53
+ try:
54
+ with DDGS(timeout=timeout) as ddgs:
55
+ hits = list(ddgs.text(q, max_results=max_results))
56
+ except Exception as e:
57
+ return f"DuckDuckGo search failed: {e}"
58
+ if not hits:
59
+ return "(no web results)"
60
+ parts: list[str] = []
61
+ for r in hits:
62
+ title = (r.get("title") or "").strip()
63
+ url = (r.get("href") or r.get("url") or "").strip()
64
+ body = (r.get("body") or "")[:1500]
65
+ parts.append(f'<Document source="{url}" page=""/>\n{title}\n{body}\n</Document>')
66
+ return "\n\n---\n\n".join(parts)
67
+
68
+ @tool
69
+ def arvix_search(query: str) -> str:
70
+ """Search Arxiv for a query and return maximum 3 result.
71
+
72
+ Args:
73
+ query: The search query."""
74
+ try:
75
+ search_docs = ArxivLoader(query=query, load_max_docs=3).load()
76
+ except Exception as e:
77
+ return f"Arxiv search failed: {e}"
78
+ formatted_search_docs = "\n\n---\n\n".join(
79
+ [
80
+ f'<Document source="{doc.metadata.get("source", doc.metadata.get("entry_id", ""))}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>'
81
+ for doc in search_docs
82
+ ])
83
+ return formatted_search_docs or "(no Arxiv results)"
84
+
85
+
86
+ @tool
87
+ def execute_python_code(source: str) -> str:
88
+ """Run Python source in an isolated subprocess (same interpreter). Returns stdout; includes stderr if non-zero exit.
89
+
90
+ Use when the question embeds or attaches Python code and you need the actual printed/numeric output.
91
+ Args:
92
+ source: Python source code to execute as a single string."""
93
+ import subprocess
94
+ import sys
95
+ import os
96
+ proc = subprocess.run(
97
+ [sys.executable, "-c", source],
98
+ capture_output=True,
99
+ text=True,
100
+ timeout=int(os.getenv("PYTHON_TOOL_TIMEOUT", "45")),
101
+ )
102
+ out = (proc.stdout or "").strip()
103
+ err = (proc.stderr or "").strip()
104
+ if proc.returncode != 0:
105
+ combined = f"exit={proc.returncode}\nSTDOUT:\n{out}\nSTDERR:\n{err}".strip()
106
+ return combined[:8000]
107
+ text = out if out else "(empty stdout)"
108
+ if err:
109
+ text = f"{text}\nSTDERR:\n{err}"
110
+ return text[:8000]
111
+
112
+ @tool
113
+ def read_excel_format(file_path: str) -> str:
114
+ """Read an Excel (.xlsx) file and return all its sheets as Markdown tables.
115
+
116
+ Use this tool whenever the question references a spreadsheet or .xlsx file.
117
+ Prefer this over execute_python_code when you just need to read and reason about
118
+ tabular data — no need to write any code.
119
+
120
+ Args:
121
+ file_path: Absolute path to the .xlsx file as provided in the 'file_path' field of the question.
122
+ """
123
+ try:
124
+ import pandas as pd
125
+ except ImportError:
126
+ return "pandas is not installed. Run: pip install pandas openpyxl"
127
+
128
+ if not os.path.exists(file_path):
129
+ return f"File not found: {file_path}"
130
+
131
+ try:
132
+ xl = pd.ExcelFile(file_path)
133
+ except Exception as e:
134
+ return f"Failed to open Excel file: {e}"
135
+
136
+ filename = os.path.basename(file_path)
137
+ parts: list[str] = [f"**File:** `{filename}`\n"]
138
+
139
+ for sheet_name in xl.sheet_names:
140
+ try:
141
+ df = xl.parse(sheet_name)
142
+ except Exception as e:
143
+ parts.append(f"### Sheet: {sheet_name}\n(error reading sheet: {e})\n")
144
+ continue
145
+
146
+ parts.append(f"### Sheet: `{sheet_name}` — {df.shape[0]} rows × {df.shape[1]} columns\n")
147
+ parts.append(df.to_markdown(index=False))
148
+ parts.append("")
149
+
150
+ return "\n".join(parts)
151
+
152
+
153
+ @tool
154
+ def YouTubeVideoAnalysisTool(video_id: str) -> str:
155
+ """
156
+ Fetches the transcript of a YouTube video by its ID and performs.
157
+ Args:
158
+ video_id: The ID of the YouTube video.
159
+
160
+ Returns:
161
+ video transcript in text format.
162
+ """
163
+
164
+ try:
165
+ fetched = YouTubeTranscriptApi().fetch(video_id)
166
+ full_transcript = " ".join([snippet.text for snippet in fetched])
167
+ except Exception as e:
168
+ return f"An error occurred while fetching the YouTube transcript: {e}"
169
+
170
+ return "the transcript of the youtube video is the following: "+ full_transcript
171
+
172
+ @tool
173
+ def transcribe_mp3(file_path: str) -> str:
174
+ """Transcribe an MP3 audio file to text using Whisper (Hugging Face Inference API).
175
+
176
+ Use this tool when the question references an .mp3 audio file.
177
+
178
+ Args:
179
+ file_path: Absolute path to the .mp3 file.
180
+ """
181
+ if not os.path.exists(file_path):
182
+ return f"File not found: {file_path}"
183
+
184
+ token = os.getenv("HF_TOKEN")
185
+ if not token:
186
+ return "HF_TOKEN is not set in the environment."
187
+
188
+ try:
189
+ from huggingface_hub import InferenceClient
190
+
191
+ client = InferenceClient(api_key=token)
192
+ with open(file_path, "rb") as f:
193
+ output = client.automatic_speech_recognition(
194
+ f.read(),
195
+ model="openai/whisper-large-v3",
196
+ )
197
+ return output.text or "(empty transcription)"
198
+ except Exception as e:
199
+ return f"Transcription failed: {e}"