File size: 4,938 Bytes
9319e6f
78e2723
 
 
2f2bdcf
 
004a8b0
0c3e951
9319e6f
 
 
78e2723
9319e6f
78e2723
 
 
 
9319e6f
78e2723
 
9319e6f
78e2723
 
9319e6f
78e2723
 
9319e6f
78e2723
 
 
 
9319e6f
 
 
 
 
 
004a8b0
9319e6f
 
 
 
 
 
 
 
 
 
 
004a8b0
 
 
 
 
 
9319e6f
004a8b0
7f6547d
9319e6f
77bac94
2f2bdcf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77bac94
 
 
2f2bdcf
9319e6f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from typing import List, TypedDict, Annotated, Optional, Dict, Union
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AnyMessage
from langgraph.graph.message import add_messages
from langchain_community.vectorstores import SupabaseVectorStore
from supabase.client import  create_client
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_huggingface import HuggingFaceEmbeddings, ChatHuggingFace, HuggingFaceEndpoint
from serpapi import GoogleSearch
from dotenv import load_dotenv
import os

load_dotenv()
class AgentState(TypedDict):
    """Agent state to be passed to the tool."""
    messages: Annotated[List[AnyMessage], add_messages]
    
def add(a: Union[float , int], b: Union[float , int]) -> Union[float , int]:
    """Add two numbers."""
    return a + b
def subtract(a: Union[float , int], b: Union[float , int]) -> Union[float , int]:
    """Subtract two numbers."""
    return a - b
def multiply(a: Union[float , int], b: Union[float , int]) -> Union[float , int]:
    """Multiply two numbers."""
    return a * b
def divide(a: Union[float , int], b: Union[float , int]) -> Union[float , int , None]:
    """Divide two numbers."""
    if b == 0:
        return None
    return a / b

def web_search(query: str) -> str: 
    """Perform a web search using SerpAPI."""
    params = {
        "engine": "google",
        "q": query,
        "api_key": os.getenv("SERPAPI_KEY"),
        "num": 5
    }
    search = GoogleSearch(params)
    results = search.get_dict()["organic_results"]
    context = "\n---\n".join([
        "Title: " + result['title'] + "\nLink: " + result['link'] + "\nSnippet: " + result.get('snippet', 'No snippet available')
        for result in results if 'title' in result and 'link' in result
    ]
    )
    return context if context else "No results found."

# llm = ChatHuggingFace(llm = HuggingFaceEndpoint(
#                 repo_id = "meta-llama/Llama-2-7b-chat-hf",
#                 temperature=0,
#                 huggingfacehub_api_token=os.environ.get("HUGGING_FACE_API_KEY")))


tools = [add, subtract, divide, web_search]
llm =ChatGoogleGenerativeAI(model = "gemini-2.0-flash")
llm_with_tools = llm.bind_tools(tools)

def retriever(state: AgentState) -> Dict:
    """
    Retrieve the answer fom vector database instead of searching if we found a user query similar to which is already found in the dataset
    
    """
    supabase_url = os.environ.get("SUPABASE_URL")
    supabase_key = os.environ.get("SUPABASE_KEY")
    supabase = create_client(supabase_url, supabase_key)
    embeddings = HuggingFaceEmbeddings(model_name = "sentence-transformers/all-mpnet-base-v2")
    vector_store = SupabaseVectorStore(
    embedding=embeddings,
    client=supabase,
    table_name="documents",
    query_name="match_documents",
)
    
    docs = vector_store.similarity_search(query = state["messages"][-1].content, k = 1)
    humanmessage = HumanMessage(content = f"Here are some of the questions and answers relevant to user query \n\n {docs[0].page_content}")
    return {"messages":[humanmessage]}

def assistant(state: AgentState) -> Dict:
    
    system_message = """
    You are a helpful assistant tasked with answering questions using a set of tools. 
Now, I will ask you a question. Report your thoughts, and finish your answer with the following template: 
FINAL ANSWER: [YOUR FINAL ANSWER]. 
YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
Your answer should only start with "FINAL ANSWER: ", then follows with the answer. 
"""
    tools_description = """
    
    You have the following tools available to perform actions
    
    websearch(query: str) -> str:
    Args:
        query: Search query 
    Returns:
        A string containing 5 relevant search results
    
    add(a: Union[float , int], b: Union[float , int]) -> Union[float , int]:
        Add two numbers
    
    subtract(a: Union[float , int], b: Union[float , int]) -> Union[float , int]:
        Subtract two numbers
    
    multiply(a: Union[float , int], b: Union[float , int]) -> Union[float , int]:
        Multiply two numbers
    
    divide(a: Union[float , int], b: Union[float , int]) -> Union[float , int , None]:
        Divide two numbers
    """
    
    sys_msg = SystemMessage(content=system_message + tools_description)
    
    return {"messages": [llm_with_tools.invoke([sys_msg] + state["messages"])]}