Spaces:
Sleeping
Sleeping
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_community.document_loaders import TextLoader | |
from langchain.indexes import VectorstoreIndexCreator | |
from langchain_community.utilities import SerpAPIWrapper, DuckDuckGoSearchAPIWrapper | |
from langchain_community.utilities 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) | |
try: | |
# Detect if input is in Vietnamese | |
import re | |
is_vietnamese = bool(re.search(r'[àáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ]', input.lower())) | |
tool = SerpAPIWrapper() | |
# Add language hint for better results | |
if is_vietnamese: | |
input = input + " lang:vi" | |
evidence = tool.run(input) | |
if evidence is None: | |
evidence = "Không tìm thấy kết quả." if is_vietnamese else "No results found from Google search." | |
except Exception as e: | |
evidence = f"Lỗi tìm kiếm Google: {str(e)}" if is_vietnamese else f"Error performing Google search: {str(e)}" | |
evidence = str(evidence).strip() | |
if log: | |
return {"input": input, "output": evidence} | |
return evidence | |
class DuckduckgoWorker(Node): | |
def __init__(self, name="Duckduckgo"): | |
super().__init__(name, input_type=str, output_type=str) | |
self.isLLMBased = False | |
self.description = "Worker that searches results from the web. 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) | |
try: | |
# Detect if input is in Vietnamese | |
import re | |
is_vietnamese = bool( | |
re.search(r'[àáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ]', input.lower())) | |
tool = DuckDuckGoSearchAPIWrapper( | |
backend="html", | |
region="vn-vi" if is_vietnamese else "wt-wt", | |
time=None, | |
max_results=5, | |
) | |
evidence = tool.run(input) | |
if evidence is None: | |
evidence = "Không tìm thấy kết quả." if is_vietnamese else "No results found from DDG search." | |
except Exception as e: | |
evidence = f"Lỗi tìm kiếm DDG: {str(e)}" if is_vietnamese else f"Error performing DDG search: {str(e)}" | |
evidence = str(evidence).strip() | |
if log: | |
return {"input": input, "output": evidence} | |
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) | |
try: | |
from langchain_openai import OpenAI | |
llm = OpenAI(temperature=0) | |
# Create math chain with latest components | |
from langchain_core.prompts import PromptTemplate | |
template = """Given the math problem: | |
{question} | |
Let's solve this step by step: | |
1) First, let's understand what we're calculating | |
2) Then, perform the calculation | |
3) Finally, provide the numeric answer | |
The answer is:""" | |
prompt = PromptTemplate(template=template, input_variables=["question"]) | |
chain = prompt | llm | |
response = chain.invoke({"question": input}) | |
evidence = str(response).strip() | |
# Try to extract just the numeric answer if possible | |
import re | |
numeric_match = re.search(r'[-+]?\d*\.?\d+', evidence) | |
if numeric_match: | |
evidence = numeric_match.group() | |
except Exception as e: | |
evidence = f"Error in calculation: {str(e)}" | |
assert isinstance(evidence, self.output_type) | |
if log: | |
return {"input": input, "output": evidence} | |
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) | |
try: | |
from langchain_openai import OpenAI | |
llm = OpenAI(temperature=0) | |
import re | |
is_vietnamese = bool(re.search(r'[àáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ]', input.lower())) | |
# Create a more detailed prompt template | |
if is_vietnamese: | |
template = """Dựa trên yêu cầu sau đây, hãy cung cấp câu trả lời rõ ràng và trực tiếp. | |
Nếu sử dụng thông tin có sẵn, hãy phân tích cẩn thận. | |
Nếu không chắc chắn, hãy giải thích những gì bạn biết về chủ đề này. | |
Yêu cầu: {request} | |
Trả lời: """ | |
else: | |
template = """Based on the following request, provide a clear and direct answer. | |
If using context, analyze it carefully. | |
If you don't know or aren't sure, explain what you do know about the topic. | |
Request: {request} | |
Answer: """ | |
prompt = PromptTemplate(template=template, input_variables=["request"]) | |
chain = prompt | llm | |
# Run the chain and get response | |
response = chain.invoke({"request": input}) | |
evidence = str(response).strip() | |
# If response is too short or says "I don't know", try to provide more detail | |
if len(evidence) < 10 or "don't know" in evidence.lower() or "không thể" in evidence.lower(): | |
if is_vietnamese: | |
fallback_prompt = f"Hãy phân tích và cung cấp thông tin chi tiết về: {input}" | |
else: | |
fallback_prompt = f"Please provide detailed analysis and information about: {input}" | |
fallback_response = llm.invoke(fallback_prompt) | |
evidence = str(fallback_response).strip() | |
except Exception as e: | |
if is_vietnamese: | |
evidence = f"Lỗi xử lý LLM: {str(e)}" | |
else: | |
evidence = f"Error in LLM processing: {str(e)}" | |
assert isinstance(evidence, self.output_type) | |
if log: | |
return {"input": input, "output": evidence} | |
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(), | |
"Duckduckgo": DuckduckgoWorker() | |
} | |