junaid17 commited on
Commit
02073ee
·
verified ·
1 Parent(s): 745c08b

Update tools.py

Browse files
Files changed (1) hide show
  1. tools.py +269 -282
tools.py CHANGED
@@ -1,282 +1,269 @@
1
- from langchain_core.tools import tool
2
- from langchain_text_splitters import RecursiveCharacterTextSplitter
3
- from langchain_community.vectorstores import FAISS
4
- from langchain_community.document_loaders import PyPDFLoader
5
- from langchain_openai import OpenAIEmbeddings
6
- from langchain_community.tools import WikipediaQueryRun, ArxivQueryRun
7
- from langchain_community.utilities import WikipediaAPIWrapper, ArxivAPIWrapper
8
- from langchain_core.tools import tool
9
- from langchain_community.tools.tavily_search import TavilySearchResults
10
- from dotenv import load_dotenv
11
- import os
12
- import requests
13
-
14
- load_dotenv()
15
-
16
- API_KEY = os.getenv("ALPHAVANTAGE_API_KEY")
17
- NEWS_API_KEY = os.getenv("NEWS_API_KEY")
18
- WEATHER_API_KEY = os.getenv("WEATHER_API_KEY")
19
- NEWS_API_KEY = os.getenv("NEWS_API_KEY")
20
-
21
- # -------------------------------
22
- # GLOBAL RETRIEVER
23
- # -------------------------------
24
- retriever = None
25
-
26
-
27
- def build_vectorstore(path: str):
28
- loader = PyPDFLoader(path)
29
- docs = loader.load()
30
-
31
- splitter = RecursiveCharacterTextSplitter(
32
- chunk_size=500,
33
- chunk_overlap=100
34
- )
35
-
36
- split_docs = splitter.split_documents(docs)
37
-
38
- embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
39
- return FAISS.from_documents(split_docs, embeddings)
40
-
41
-
42
- def update_retriever(pdf_path: str):
43
- global retriever
44
- vectorstore = build_vectorstore(pdf_path)
45
- retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
46
-
47
-
48
- def create_rag_tool():
49
-
50
- @tool
51
- def rag_search(query: str) -> str:
52
- """
53
- Retrieve relevant information from the uploaded document.
54
-
55
- Use this tool when the user asks questions related to the uploaded PDF
56
- or any document-based knowledge. If no document is available or no
57
- relevant information is found, return an appropriate message.
58
- """
59
- global retriever
60
-
61
- if retriever is None:
62
- return "No document uploaded yet."
63
-
64
- docs = retriever.invoke(query)
65
-
66
- if not docs:
67
- return "No relevant information found in the uploaded document."
68
-
69
- context = "\n\n".join(d.page_content for d in docs)
70
-
71
- return f"""
72
- You are given extracted content from a document.
73
-
74
- Your task:
75
- - Summarize the content clearly
76
- - Use bullet points where appropriate
77
- - Keep formatting clean and readable
78
- - Do NOT repeat unnecessary text
79
- - Do NOT mention that this came from a document
80
-
81
- DOCUMENT CONTENT:
82
- {context}
83
- """
84
-
85
-
86
- return rag_search
87
-
88
-
89
-
90
- @tool
91
- def arxiv_search(query: str) -> dict:
92
- """
93
- Search arXiv for academic papers related to the query.
94
- """
95
- try:
96
- arxiv = ArxivQueryRun(api_wrapper=ArxivAPIWrapper())
97
- results = arxiv.run(query)
98
- return {"query": query, "results": results}
99
- except Exception as e:
100
- return {"error": str(e)}
101
-
102
- @tool
103
- def calculator(first_num: float, second_num: float, operation: str) -> dict:
104
- """
105
- Perform a basic arithmetic operation on two numbers.
106
- Supported operations: add, sub, mul, div
107
- """
108
- try:
109
- if operation == "add":
110
- result = first_num + second_num
111
- elif operation == "sub":
112
- result = first_num - second_num
113
- elif operation == "mul":
114
- result = first_num * second_num
115
- elif operation == "div":
116
- if second_num == 0:
117
- return {"error": "Division by zero is not allowed"}
118
- result = first_num / second_num
119
- else:
120
- return {"error": f"Unsupported operation '{operation}'"}
121
-
122
- return {"first_num": first_num, "second_num": second_num, "operation": operation, "result": result}
123
- except Exception as e:
124
- return {"error": str(e)}
125
- @tool
126
- def tavily_search(query: str) -> dict:
127
- """
128
- Perform a web search using Tavily,
129
- also use it to get weather information,
130
- Returns up to 5 search results.
131
- """
132
- try:
133
- search = TavilySearchResults(max_results=5)
134
- results = search.run(query)
135
- return {"query": query, "results": results}
136
- except Exception as e:
137
- return {"error": str(e)}
138
-
139
-
140
- @tool
141
- def get_stock_price(symbol: str) -> dict:
142
- """
143
- Fetch latest stock price for a given symbol (e.g. 'AAPL', 'TSLA')
144
- using Alpha Vantage with API key in the URL.
145
- """
146
- url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={symbol}&apikey={API_KEY}"
147
- r = requests.get(url)
148
- return r.json()
149
-
150
- @tool
151
- def wikipedia_search(query: str) -> dict:
152
- """
153
- Search Wikipedia for a given query and return results.
154
- """
155
- try:
156
- wiki = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
157
- results = wiki.run(query)
158
- return {"query": query, "results": results}
159
- except Exception as e:
160
- return {"error": str(e)}
161
-
162
- @tool
163
- def convert_currency(amount: float, from_currency: str, to_currency: str) -> dict:
164
- """
165
- Convert amount from one currency to another using Frankfurter API.
166
- Example: convert_currency(100, "USD", "EUR")
167
- """
168
- try:
169
- url = f"https://api.frankfurter.app/latest?amount={amount}&from={from_currency}&to={to_currency}"
170
- r = requests.get(url)
171
- return r.json()
172
- except Exception as e:
173
- return {"error": str(e)}
174
- @tool
175
-
176
-
177
- def unit_converter(value: float, from_unit: str, to_unit: str) -> dict:
178
- """
179
- Convert between metric/imperial units (supports: km<->miles, kg<->lbs, C<->F).
180
- Example: unit_converter(10, "km", "miles")
181
- """
182
- try:
183
- conversions = {
184
- ("km", "miles"): lambda x: x * 0.621371,
185
- ("miles", "km"): lambda x: x / 0.621371,
186
- ("kg", "lbs"): lambda x: x * 2.20462,
187
- ("lbs", "kg"): lambda x: x / 2.20462,
188
- ("C", "F"): lambda x: (x * 9/5) + 32,
189
- ("F", "C"): lambda x: (x - 32) * 5/9
190
- }
191
- if (from_unit, to_unit) not in conversions:
192
- return {"error": f"Unsupported conversion: {from_unit} -> {to_unit}"}
193
- result = conversions[(from_unit, to_unit)](value)
194
- return {"value": value, "from": from_unit, "to": to_unit, "result": result}
195
- except Exception as e:
196
- return {"error": str(e)}
197
-
198
-
199
-
200
- @tool
201
- def get_news(query: str) -> dict:
202
- """
203
- Fetch latest news headlines for a given query.
204
- Example: get_news("artificial intelligence")
205
- """
206
- try:
207
- url = f"https://newsapi.org/v2/everything?q={query}&apiKey={NEWS_API_KEY}&language=en"
208
- r = requests.get(url)
209
- return r.json()
210
- except Exception as e:
211
- return {"error": str(e)}
212
-
213
-
214
- @tool
215
- def get_joke(category: str = "Any") -> dict:
216
- """
217
- Get a random joke. Categories: Programming, Misc, Pun, Spooky, Christmas, Any
218
- Example: get_joke("Programming")
219
- """
220
- try:
221
- url = f"https://v2.jokeapi.dev/joke/{category}"
222
- r = requests.get(url)
223
- return r.json()
224
- except Exception as e:
225
- return {"error": str(e)}
226
-
227
- @tool
228
- def get_quote(tag: str = "") -> dict:
229
- """
230
- Fetch a random quote. Optionally filter by tag (e.g., 'inspirational', 'technology').
231
- Example: get_quote("inspirational")
232
- """
233
- try:
234
- url = f"https://api.quotable.io/random"
235
- if tag:
236
- url += f"?tags={tag}"
237
- r = requests.get(url)
238
- return r.json()
239
- except Exception as e:
240
- return {"error": str(e)}
241
-
242
- @tool
243
- def get_weather(city: str) -> dict:
244
- """
245
- Get current weather for a given city using WeatherAPI.com.
246
- Example: get_weather("London")
247
- """
248
- try:
249
- url = f"http://api.weatherapi.com/v1/current.json?key={WEATHER_API_KEY}&q={city}&aqi=no"
250
- r = requests.get(url)
251
- data = r.json()
252
-
253
- if "error" in data:
254
- return {"error": data["error"]["message"]}
255
-
256
- return {
257
- "location": data["location"]["name"],
258
- "country": data["location"]["country"],
259
- "temperature_c": data["current"]["temp_c"],
260
- "temperature_f": data["current"]["temp_f"],
261
- "condition": data["current"]["condition"]["text"],
262
- "humidity": data["current"]["humidity"],
263
- "wind_kph": data["current"]["wind_kph"],
264
- "wind_dir": data["current"]["wind_dir"]
265
- }
266
- except Exception as e:
267
- return {"error": str(e)}
268
-
269
-
270
-
271
- @tool
272
- def get_news(query: str) -> dict:
273
- """
274
- Fetch latest news headlines for a given query.
275
- Example: get_news("artificial intelligence")
276
- """
277
- try:
278
- url = f"https://newsapi.org/v2/everything?q={query}&apiKey={NEWS_API_KEY}&language=en"
279
- r = requests.get(url)
280
- return r.json()
281
- except Exception as e:
282
- return {"error": str(e)}
 
1
+ from langchain_core.tools import tool
2
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
3
+ from langchain_community.vectorstores import FAISS
4
+ from langchain_community.document_loaders import PyPDFLoader
5
+ from langchain_openai import OpenAIEmbeddings
6
+ from langchain_community.tools import WikipediaQueryRun, ArxivQueryRun
7
+ from langchain_community.utilities import WikipediaAPIWrapper, ArxivAPIWrapper
8
+ from langchain_community.tools.tavily_search import TavilySearchResults
9
+ from dotenv import load_dotenv
10
+ import os
11
+ import requests
12
+
13
+ # =========================
14
+ # ENV SETUP
15
+ # =========================
16
+
17
+ load_dotenv()
18
+
19
+ API_KEY = os.getenv("ALPHAVANTAGE_API_KEY")
20
+ NEWS_API_KEY = os.getenv("NEWS_API_KEY")
21
+ WEATHER_API_KEY = os.getenv("WEATHER_API_KEY")
22
+
23
+ # =========================
24
+ # STORAGE CONFIG (HF SAFE)
25
+ # =========================
26
+
27
+ VECTORSTORE_DIR = "/data/vectorstore"
28
+ os.makedirs(VECTORSTORE_DIR, exist_ok=True)
29
+
30
+ retriever = None
31
+
32
+
33
+ # =========================
34
+ # VECTORSTORE FUNCTIONS
35
+ # =========================
36
+
37
+ def build_vectorstore(pdf_path: str):
38
+ loader = PyPDFLoader(pdf_path)
39
+ docs = loader.load()
40
+
41
+ splitter = RecursiveCharacterTextSplitter(
42
+ chunk_size=500,
43
+ chunk_overlap=100
44
+ )
45
+
46
+ chunks = splitter.split_documents(docs)
47
+
48
+ embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
49
+
50
+ vectorstore = FAISS.from_documents(chunks, embeddings)
51
+ vectorstore.save_local(VECTORSTORE_DIR)
52
+
53
+ return vectorstore
54
+
55
+
56
+ def load_vectorstore():
57
+ embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
58
+ return FAISS.load_local(
59
+ VECTORSTORE_DIR,
60
+ embeddings,
61
+ allow_dangerous_deserialization=True
62
+ )
63
+
64
+
65
+ def update_retriever(pdf_path: str):
66
+ global retriever
67
+ vectorstore = build_vectorstore(pdf_path)
68
+ retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
69
+
70
+
71
+ def get_retriever():
72
+ global retriever
73
+ if retriever is None and os.path.exists(VECTORSTORE_DIR):
74
+ retriever = load_vectorstore().as_retriever(search_kwargs={"k": 4})
75
+ return retriever
76
+
77
+
78
+ # =========================
79
+ # RAG TOOL
80
+ # =========================
81
+
82
+ def create_rag_tool():
83
+
84
+ @tool
85
+ def rag_search(query: str) -> str:
86
+ """
87
+ Retrieve and summarize information from uploaded documents.
88
+
89
+ Use this tool whenever the user asks about uploaded documents.
90
+ Summarize clearly and avoid repeating raw content.
91
+ """
92
+
93
+ retriever = get_retriever()
94
+
95
+ if retriever is None:
96
+ return "No document has been uploaded yet."
97
+
98
+ docs = retriever.invoke(query)
99
+
100
+ if not docs:
101
+ return "No relevant information found in the document."
102
+
103
+ context = "\n\n".join(d.page_content for d in docs)
104
+
105
+ return f"""
106
+ You are given extracted content from a document.
107
+
108
+ Your task:
109
+ - Summarize clearly
110
+ - Use bullet points when useful
111
+ - Avoid repetition
112
+ - Do NOT mention that this came from a document
113
+
114
+ DOCUMENT CONTENT:
115
+ {context}
116
+ """
117
+
118
+ return rag_search
119
+
120
+
121
+ # =========================
122
+ # EXTERNAL TOOLS
123
+ # =========================
124
+
125
+ @tool
126
+ def arxiv_search(query: str) -> dict:
127
+ """Search arXiv for academic papers."""
128
+ try:
129
+ arxiv = ArxivQueryRun(api_wrapper=ArxivAPIWrapper())
130
+ return {"query": query, "results": arxiv.run(query)}
131
+ except Exception as e:
132
+ return {"error": str(e)}
133
+
134
+
135
+ @tool
136
+ def calculator(first_num: float, second_num: float, operation: str) -> dict:
137
+ """Perform basic arithmetic operations."""
138
+ try:
139
+ ops = {
140
+ "add": first_num + second_num,
141
+ "sub": first_num - second_num,
142
+ "mul": first_num * second_num,
143
+ "div": first_num / second_num if second_num != 0 else "Division by zero"
144
+ }
145
+ return {"result": ops.get(operation, "Invalid operation")}
146
+ except Exception as e:
147
+ return {"error": str(e)}
148
+
149
+
150
+ @tool
151
+ def tavily_search(query: str) -> dict:
152
+ """Search the web using Tavily."""
153
+ try:
154
+ search = TavilySearchResults(max_results=5)
155
+ return {"query": query, "results": search.run(query)}
156
+ except Exception as e:
157
+ return {"error": str(e)}
158
+
159
+
160
+ @tool
161
+ def get_stock_price(symbol: str) -> dict:
162
+ """Fetch latest stock price."""
163
+ try:
164
+ url = f"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol={symbol}&apikey={API_KEY}"
165
+ return requests.get(url).json()
166
+ except Exception as e:
167
+ return {"error": str(e)}
168
+
169
+
170
+ @tool
171
+ def wikipedia_search(query: str) -> dict:
172
+ """Search Wikipedia."""
173
+ try:
174
+ wiki = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
175
+ return {"query": query, "results": wiki.run(query)}
176
+ except Exception as e:
177
+ return {"error": str(e)}
178
+
179
+
180
+ @tool
181
+ def convert_currency(amount: float, from_currency: str, to_currency: str) -> dict:
182
+ """Convert currency using Frankfurter API."""
183
+ try:
184
+ url = f"https://api.frankfurter.app/latest?amount={amount}&from={from_currency}&to={to_currency}"
185
+ return requests.get(url).json()
186
+ except Exception as e:
187
+ return {"error": str(e)}
188
+
189
+
190
+ @tool
191
+ def unit_converter(value: float, from_unit: str, to_unit: str) -> dict:
192
+ """Convert between common units."""
193
+ try:
194
+ conversions = {
195
+ ("km", "miles"): lambda x: x * 0.621371,
196
+ ("miles", "km"): lambda x: x / 0.621371,
197
+ ("kg", "lbs"): lambda x: x * 2.20462,
198
+ ("lbs", "kg"): lambda x: x / 2.20462,
199
+ ("C", "F"): lambda x: (x * 9 / 5) + 32,
200
+ ("F", "C"): lambda x: (x - 32) * 5 / 9,
201
+ }
202
+
203
+ if (from_unit, to_unit) not in conversions:
204
+ return {"error": "Unsupported conversion"}
205
+
206
+ return {
207
+ "value": value,
208
+ "from": from_unit,
209
+ "to": to_unit,
210
+ "result": conversions[(from_unit, to_unit)](value),
211
+ }
212
+ except Exception as e:
213
+ return {"error": str(e)}
214
+
215
+
216
+ @tool
217
+ def get_news(query: str) -> dict:
218
+ """Fetch latest news headlines."""
219
+ try:
220
+ url = f"https://newsapi.org/v2/everything?q={query}&apiKey={NEWS_API_KEY}&language=en"
221
+ return requests.get(url).json()
222
+ except Exception as e:
223
+ return {"error": str(e)}
224
+
225
+
226
+ @tool
227
+ def get_joke(category: str = "Any") -> dict:
228
+ """Get a random joke."""
229
+ try:
230
+ url = f"https://v2.jokeapi.dev/joke/{category}"
231
+ return requests.get(url).json()
232
+ except Exception as e:
233
+ return {"error": str(e)}
234
+
235
+
236
+ @tool
237
+ def get_quote(tag: str = "") -> dict:
238
+ """Fetch a random quote."""
239
+ try:
240
+ url = f"https://api.quotable.io/random"
241
+ if tag:
242
+ url += f"?tags={tag}"
243
+ return requests.get(url).json()
244
+ except Exception as e:
245
+ return {"error": str(e)}
246
+
247
+
248
+ @tool
249
+ def get_weather(city: str) -> dict:
250
+ """Get current weather for a city."""
251
+ try:
252
+ url = f"http://api.weatherapi.com/v1/current.json?key={WEATHER_API_KEY}&q={city}&aqi=no"
253
+ data = requests.get(url).json()
254
+
255
+ if "error" in data:
256
+ return {"error": data["error"]["message"]}
257
+
258
+ return {
259
+ "location": data["location"]["name"],
260
+ "country": data["location"]["country"],
261
+ "temperature_c": data["current"]["temp_c"],
262
+ "temperature_f": data["current"]["temp_f"],
263
+ "condition": data["current"]["condition"]["text"],
264
+ "humidity": data["current"]["humidity"],
265
+ "wind_kph": data["current"]["wind_kph"],
266
+ "wind_dir": data["current"]["wind_dir"],
267
+ }
268
+ except Exception as e:
269
+ return {"error": str(e)}