File size: 3,907 Bytes
6859205
 
 
 
 
 
 
 
 
 
 
 
 
852ec49
 
6859205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
852ec49
 
 
6859205
 
 
 
 
 
852ec49
6859205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from typing import TypedDict, Annotated, Sequence
from langgraph.graph.message import add_messages
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage, SystemMessage, BaseMessage
from langgraph.prebuilt import ToolNode
from langgraph.graph import START, StateGraph, MessagesState, END
from langgraph.prebuilt import tools_condition
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace
from langchain_core.runnables import RunnableConfig

from langchain_community.tools import DuckDuckGoSearchRun, WikipediaQueryRun, ArxivQueryRun
from langchain_community.utilities import WikipediaAPIWrapper, ArxivAPIWrapper
from langchain_community.tools.wikidata.tool import WikidataAPIWrapper, WikidataQueryRun

from langchain_openai import ChatOpenAI

from tools import GetYouTubeTranscriptTool, ImageRecognitionTool
from dotenv import load_dotenv
import os

load_dotenv()
HUGGINGFACEHUB_API_TOKEN = os.getenv("HF_TOKEN")

# Define your state class if needed
class AgentState(TypedDict):
    """The state of the agent."""

    messages: Annotated[Sequence[BaseMessage], add_messages]



def build_graph():
    """
    Build and return the compiled LangGraph Runnable agent.
    """

    def call_model(
        state: AgentState,
        config: RunnableConfig,
    ):
        system_prompt = SystemMessage("You are a general AI assistant. I will ask you a question. Report only your final answer without the thoughts or any other text. 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. Also be direct when doing a tool call. Try using other tools before using the DuckDuckGoTool. If you can't find informations, answer based on your personal knowledge.")
        
        response = model.invoke([system_prompt] + state["messages"], config)
        # We return a list, because this will get added to the existing list
        return {"messages": [response]}

    def should_continue(state: AgentState):
        messages = state["messages"]
        last_message = messages[-1]
        print(last_message)
        # If there is no function call, then we finish
        if not last_message.tool_calls:
            return "end"
        # Otherwise if there is, we continue
        else:
            return "continue"
        
    model = ChatOpenAI(model="o1")
    image_model = ChatOpenAI(model="gpt-4o")

    tools = [GetYouTubeTranscriptTool(), 
             WikidataQueryRun(api_wrapper=WikidataAPIWrapper()), 
             DuckDuckGoSearchRun(),
             ArxivQueryRun(api_wrapper=ArxivAPIWrapper()),
             WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()), 
             ImageRecognitionTool(hf_endpoint=image_model)]
    model = model.bind_tools(tools)

    tool_node = ToolNode(tools)
    
    workflow = StateGraph(AgentState)

    # Define the two nodes we will cycle between
    workflow.add_node("agent", call_model)
    workflow.add_node("tools", tool_node)
    
    # Set the entrypoint as `agent`
    # This means that this node is the first one called
    workflow.set_entry_point("agent")
    
    # We now add a conditional edge
    workflow.add_conditional_edges(
        "agent",
        should_continue,
        {
            # If `tools`, then we call the tool node.
            "continue": "tools",
            "end": END
        },
    )
    
    workflow.add_edge("tools", "agent")
    
    graph = workflow.compile()

    return graph