ReWOO-Demo / nodes /Worker.py
billxbf's picture
init
926675f
import requests
from geopy.geocoders import Nominatim
from langchain import OpenAI, LLMMathChain, LLMChain, PromptTemplate, Wikipedia
from langchain.agents import Tool
from langchain.agents.react.base import DocstoreExplorer
from langchain.document_loaders import TextLoader
from langchain.indexes import VectorstoreIndexCreator
from langchain.utilities import SerpAPIWrapper
from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper
from nodes.Node import Node
class GoogleWorker(Node):
def __init__(self, name="Google"):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = False
self.description = "Worker that searches results from Google. Useful when you need to find short " \
"and succinct answers about a specific topic. Input should be a search query."
def run(self, input, log=False):
assert isinstance(input, self.input_type)
tool = SerpAPIWrapper()
evidence = tool.run(input)
assert isinstance(evidence, self.output_type)
if log:
print(f"Running {self.name} with input {input}\nOutput: {evidence}\n")
return evidence
class WikipediaWorker(Node):
def __init__(self, name="Wikipedia", docstore=None):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = False
self.description = "Worker that search for similar page contents from Wikipedia. Useful when you need to " \
"get holistic knowledge about people, places, companies, historical events, " \
"or other subjects. The response are long and might contain some irrelevant information. " \
"Input should be a search query."
self.docstore = docstore
def run(self, input, log=False):
if not self.docstore:
self.docstore = DocstoreExplorer(Wikipedia())
assert isinstance(input, self.input_type)
tool = Tool(
name="Search",
func=self.docstore.search,
description="useful for when you need to ask with search"
)
evidence = tool.run(input)
assert isinstance(evidence, self.output_type)
if log:
print(f"Running {self.name} with input {input}\nOutput: {evidence}\n")
return evidence
class DocStoreLookUpWorker(Node):
def __init__(self, name="LookUp", docstore=None):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = False
self.description = "Worker that search the direct sentence in current Wikipedia result page. Useful when you " \
"need to find information about a specific keyword from a existing Wikipedia search " \
"result. Input should be a search keyword."
self.docstore = docstore
def run(self, input, log=False):
if not self.docstore:
raise ValueError("Docstore must be provided for lookup")
assert isinstance(input, self.input_type)
tool = Tool(
name="Lookup",
func=self.docstore.lookup,
description="useful for when you need to ask with lookup"
)
evidence = tool.run(input)
assert isinstance(evidence, self.output_type)
if log:
print(f"Running {self.name} with input {input}\nOutput: {evidence}\n")
return evidence
class CustomWolframAlphaAPITool(WolframAlphaAPIWrapper):
def __init__(self):
super().__init__()
def run(self, query: str) -> str:
"""Run query through WolframAlpha and parse result."""
res = self.wolfram_client.query(query)
try:
answer = next(res.results).text
except StopIteration:
return "Wolfram Alpha wasn't able to answer it"
if answer is None or answer == "":
return "No good Wolfram Alpha Result was found"
else:
return f"Answer: {answer}"
class WolframAlphaWorker(Node):
def __init__(self, name="WolframAlpha"):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = False
self.description = "A WolframAlpha search engine. Useful when you need to solve a complicated Mathematical or " \
"Algebraic equation. Input should be an equation or function."
def run(self, input, log=False):
assert isinstance(input, self.input_type)
tool = CustomWolframAlphaAPITool()
evidence = tool.run(input).replace("Answer:", "").strip()
assert isinstance(evidence, self.output_type)
if log:
print(f"Running {self.name} with input {input}\nOutput: {evidence}\n")
return evidence
class CalculatorWorker(Node):
def __init__(self, name="Calculator"):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = True
self.description = "A calculator that can compute arithmetic expressions. Useful when you need to perform " \
"math calculations. Input should be a mathematical expression"
def run(self, input, log=False):
assert isinstance(input, self.input_type)
llm = OpenAI(temperature=0)
tool = LLMMathChain(llm=llm, verbose=False)
response = tool(input)
evidence = response["answer"].replace("Answer:", "").strip()
assert isinstance(evidence, self.output_type)
if log:
return {"input": response["question"], "output": response["answer"]}
return evidence
class LLMWorker(Node):
def __init__(self, name="LLM"):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = True
self.description = "A pretrained LLM like yourself. Useful when you need to act with general world " \
"knowledge and common sense. Prioritize it when you are confident in solving the problem " \
"yourself. Input can be any instruction."
def run(self, input, log=False):
assert isinstance(input, self.input_type)
llm = OpenAI(temperature=0)
prompt = PromptTemplate(template="Respond in short directly with no extra words.\n\n{request}",
input_variables=["request"])
tool = LLMChain(prompt=prompt, llm=llm, verbose=False)
response = tool(input)
evidence = response["text"].strip("\n")
assert isinstance(evidence, self.output_type)
if log:
return {"input": response["request"], "output": response["text"]}
return evidence
class ZipCodeRetriever(Node):
def __init__(self, name="ZipCodeRetriever"):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = False
self.description = "A zip code retriever. Useful when you need to get users' current zip code. Input can be " \
"left blank."
def get_ip_address(self):
response = requests.get("https://ipinfo.io/json")
data = response.json()
return data["ip"]
def get_location_data(sefl, ip_address):
url = f"https://ipinfo.io/{ip_address}/json"
response = requests.get(url)
data = response.json()
return data
def get_zipcode_from_lat_long(self, lat, long):
geolocator = Nominatim(user_agent="zipcode_locator")
location = geolocator.reverse((lat, long))
return location.raw["address"]["postcode"]
def get_current_zipcode(self):
ip_address = self.get_ip_address()
location_data = self.get_location_data(ip_address)
lat, long = location_data["loc"].split(",")
zipcode = self.get_zipcode_from_lat_long(float(lat), float(long))
return zipcode
def run(self, input):
assert isinstance(input, self.input_type)
evidence = self.get_current_zipcode()
assert isinstance(evidence, self.output_type)
class SearchDocWorker(Node):
def __init__(self, doc_name, doc_path, name="SearchDoc"):
super().__init__(name, input_type=str, output_type=str)
self.isLLMBased = True
self.doc_path = doc_path
self.description = f"A vector store that searches for similar and related content in document: {doc_name}. " \
f"The result is a huge chunk of text related to your search but can also " \
f"contain irrelevant info. Input should be a search query."
def run(self, input, log=False):
assert isinstance(input, self.input_type)
loader = TextLoader(self.doc_path)
vectorstore = VectorstoreIndexCreator().from_loaders([loader]).vectorstore
evidence = vectorstore.similarity_search(input, k=1)[0].page_content
assert isinstance(evidence, self.output_type)
if log:
print(f"Running {self.name} with input {input}\nOutput: {evidence}\n")
return evidence
class SearchSOTUWorker(SearchDocWorker):
def __init__(self, name="SearchSOTU"):
super().__init__(name=name, doc_name="state_of_the_union", doc_path="data/docs/state_of_the_union.txt")
WORKER_REGISTRY = {"Google": GoogleWorker(),
"Wikipedia": WikipediaWorker(),
"LookUp": DocStoreLookUpWorker(),
"WolframAlpha": WolframAlphaWorker(),
"Calculator": CalculatorWorker(),
"LLM": LLMWorker(),
"SearchSOTU": SearchSOTUWorker()}