junaid17 commited on
Commit
aa4ce92
·
verified ·
1 Parent(s): a27d18b

Update tools.py

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