This commit is contained in:
William Fu-Hinthorn
2024-02-15 23:09:59 -08:00
parent c3942874eb
commit e9a7ad8b69
20 changed files with 265 additions and 173 deletions
@@ -242,9 +242,10 @@
"import json\n",
"from langchain_core.messages import FunctionMessage\n",
"\n",
"\n",
"# Define the function that determines whether to continue or not\n",
"def should_continue(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" # If there is no function call, then we finish\n",
" if \"function_call\" not in last_message.additional_kwargs:\n",
@@ -253,23 +254,27 @@
" else:\n",
" return \"continue\"\n",
"\n",
"\n",
"# Define the function that calls the model\n",
"def call_model(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" response = model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": [response]}\n",
"\n",
"\n",
"# Define the function to execute tools\n",
"def call_tool(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" # Based on the continue condition\n",
" # we know the last message involves a function call\n",
" last_message = messages[-1]\n",
" # We construct an ToolInvocation from the function_call\n",
" action = ToolInvocation(\n",
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
" tool_input=json.loads(\n",
" last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n",
" ),\n",
" )\n",
" # We call the tool_executor and get back a response\n",
" response = tool_executor.invoke(action)\n",
@@ -297,6 +302,7 @@
"outputs": [],
"source": [
"from langgraph.graph import StateGraph, END\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(AgentState)\n",
"\n",
@@ -325,13 +331,13 @@
" # If `tools`, then we call the tool node.\n",
" \"continue\": \"action\",\n",
" # Otherwise we finish.\n",
" \"end\": END\n",
" }\n",
" \"end\": END,\n",
" },\n",
")\n",
"\n",
"# We now add a normal edge from `tools` to `agent`.\n",
"# This means that after `tools` is called, `agent` node is called next.\n",
"workflow.add_edge('action', 'agent')\n",
"workflow.add_edge(\"action\", \"agent\")\n",
"\n",
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",
@@ -100,12 +100,14 @@
"source": [
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
"\n",
"\n",
"class SearchTool(BaseModel):\n",
" \"\"\"Look up things online, optionally returning directly\"\"\"\n",
"\n",
" query: str = Field(description=\"query to look up online\")\n",
" return_direct: bool = Field(\n",
" description=\"Whether or the result of this should be returned directly to the user without you seeing what it is\", \n",
" default = False\n",
" return_direct: bool = Field(\n",
" description=\"Whether or the result of this should be returned directly to the user without you seeing what it is\",\n",
" default=False,\n",
" )"
]
},
@@ -289,14 +291,16 @@
"source": [
"# Define the function that determines whether to continue or not\n",
"def should_continue(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" # If there is no function call, then we finish\n",
" if \"function_call\" not in last_message.additional_kwargs:\n",
" return \"end\"\n",
" # Otherwise if there is, we check if it's suppose to return direct\n",
" else:\n",
" arguments = json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"])\n",
" arguments = json.loads(\n",
" last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n",
" )\n",
" if arguments.get(\"return_direct\", False):\n",
" return \"final\"\n",
" else:\n",
@@ -312,7 +316,7 @@
"source": [
"# Define the function that calls the model\n",
"def call_model(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" response = model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": [response]}"
@@ -337,7 +341,7 @@
"source": [
"# Define the function to execute tools\n",
"def call_tool(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" # Based on the continue condition\n",
" # we know the last message involves a function call\n",
" last_message = messages[-1]\n",
@@ -381,6 +385,7 @@
"outputs": [],
"source": [
"from langgraph.graph import StateGraph, END\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(AgentState)\n",
"\n",
@@ -412,14 +417,14 @@
" # Final call\n",
" \"final\": \"final\",\n",
" # Otherwise we finish.\n",
" \"end\": END\n",
" }\n",
" \"end\": END,\n",
" },\n",
")\n",
"\n",
"# We now add a normal edge from `tools` to `agent`.\n",
"# This means that after `tools` is called, `agent` node is called next.\n",
"workflow.add_edge('action', 'agent')\n",
"workflow.add_edge('final', END)\n",
"workflow.add_edge(\"action\", \"agent\")\n",
"workflow.add_edge(\"final\", END)\n",
"\n",
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",
@@ -522,7 +527,13 @@
"source": [
"from langchain_core.messages import HumanMessage\n",
"\n",
"inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf? return this result directly by setting return_direct = True\")]}\n",
"inputs = {\n",
" \"messages\": [\n",
" HumanMessage(\n",
" content=\"what is the weather in sf? return this result directly by setting return_direct = True\"\n",
" )\n",
" ]\n",
"}\n",
"for output in app.stream(inputs):\n",
" # stream() yields dictionaries with output keyed by node name\n",
" for key, value in output.items():\n",
@@ -246,9 +246,10 @@
"import json\n",
"from langchain_core.messages import FunctionMessage\n",
"\n",
"\n",
"# Define the function that determines whether to continue or not\n",
"def should_continue(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" # If there is no function call, then we finish\n",
" if \"function_call\" not in last_message.additional_kwargs:\n",
@@ -257,23 +258,27 @@
" else:\n",
" return \"continue\"\n",
"\n",
"\n",
"# Define the function that calls the model\n",
"def call_model(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" response = model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": [response]}\n",
"\n",
"\n",
"# Define the function to execute tools\n",
"def call_tool(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" # Based on the continue condition\n",
" # we know the last message involves a function call\n",
" last_message = messages[-1]\n",
" # We construct an ToolInvocation from the function_call\n",
" action = ToolInvocation(\n",
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
" tool_input=json.loads(\n",
" last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n",
" ),\n",
" )\n",
" # We call the tool_executor and get back a response\n",
" response = tool_executor.invoke(action)\n",
@@ -304,20 +309,21 @@
"from langchain_core.messages import AIMessage\n",
"import json\n",
"\n",
"\n",
"def first_model(state):\n",
" human_input = state['messages'][-1].content\n",
" human_input = state[\"messages\"][-1].content\n",
" return {\n",
" \"messages\": [\n",
" AIMessage(\n",
" content=\"\", \n",
" content=\"\",\n",
" additional_kwargs={\n",
" \"function_call\": {\n",
" \"name\": \"tavily_search_results_json\", \n",
" \"arguments\": json.dumps({\"query\": human_input})\n",
" }\n",
" \"name\": \"tavily_search_results_json\",\n",
" \"arguments\": json.dumps({\"query\": human_input}),\n",
" }\n",
" )\n",
" ]\n",
" },\n",
" )\n",
" ]\n",
" }"
]
},
@@ -343,6 +349,7 @@
"outputs": [],
"source": [
"from langgraph.graph import StateGraph, END\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(AgentState)\n",
"\n",
@@ -374,16 +381,16 @@
" # If `tools`, then we call the tool node.\n",
" \"continue\": \"action\",\n",
" # Otherwise we finish.\n",
" \"end\": END\n",
" }\n",
" \"end\": END,\n",
" },\n",
")\n",
"\n",
"# We now add a normal edge from `tools` to `agent`.\n",
"# This means that after `tools` is called, `agent` node is called next.\n",
"workflow.add_edge('action', 'agent')\n",
"workflow.add_edge(\"action\", \"agent\")\n",
"\n",
"# After we call the first agent, we know we want to go to action\n",
"workflow.add_edge('first_agent', 'action')\n",
"workflow.add_edge(\"first_agent\", \"action\")\n",
"\n",
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",
@@ -263,9 +263,10 @@
"import json\n",
"from langchain_core.messages import FunctionMessage\n",
"\n",
"\n",
"# Define the function that determines whether to continue or not\n",
"def should_continue(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" # If there is no function call, then we finish\n",
" if \"function_call\" not in last_message.additional_kwargs:\n",
@@ -274,9 +275,10 @@
" else:\n",
" return \"continue\"\n",
"\n",
"\n",
"# Define the function that calls the model\n",
"def call_model(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" response = model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": [response]}"
@@ -301,14 +303,16 @@
"source": [
"# Define the function to execute tools\n",
"def call_tool(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" # Based on the continue condition\n",
" # we know the last message involves a function call\n",
" last_message = messages[-1]\n",
" # We construct an ToolInvocation from the function_call\n",
" action = ToolInvocation(\n",
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
" tool_input=json.loads(\n",
" last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n",
" ),\n",
" )\n",
" response = input(f\"[y/n] continue with: {action}?\")\n",
" if response == \"n\":\n",
@@ -339,6 +343,7 @@
"outputs": [],
"source": [
"from langgraph.graph import StateGraph, END\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(AgentState)\n",
"\n",
@@ -367,13 +372,13 @@
" # If `tools`, then we call the tool node.\n",
" \"continue\": \"action\",\n",
" # Otherwise we finish.\n",
" \"end\": END\n",
" }\n",
" \"end\": END,\n",
" },\n",
")\n",
"\n",
"# We now add a normal edge from `tools` to `agent`.\n",
"# This means that after `tools` is called, `agent` node is called next.\n",
"workflow.add_edge('action', 'agent')\n",
"workflow.add_edge(\"action\", \"agent\")\n",
"\n",
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",
@@ -246,9 +246,10 @@
"import json\n",
"from langchain_core.messages import FunctionMessage\n",
"\n",
"\n",
"# Define the function that determines whether to continue or not\n",
"def should_continue(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" # If there is no function call, then we finish\n",
" if \"function_call\" not in last_message.additional_kwargs:\n",
@@ -277,7 +278,7 @@
"source": [
"# Define the function that calls the model\n",
"def call_model(state):\n",
" messages = state['messages'][-5:]\n",
" messages = state[\"messages\"][-5:]\n",
" response = model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": [response]}"
@@ -292,14 +293,16 @@
"source": [
"# Define the function to execute tools\n",
"def call_tool(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" # Based on the continue condition\n",
" # we know the last message involves a function call\n",
" last_message = messages[-1]\n",
" # We construct an ToolInvocation from the function_call\n",
" action = ToolInvocation(\n",
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
" tool_input=json.loads(\n",
" last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n",
" ),\n",
" )\n",
" # We call the tool_executor and get back a response\n",
" response = tool_executor.invoke(action)\n",
@@ -327,6 +330,7 @@
"outputs": [],
"source": [
"from langgraph.graph import StateGraph, END\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(AgentState)\n",
"\n",
@@ -355,13 +359,13 @@
" # If `tools`, then we call the tool node.\n",
" \"continue\": \"action\",\n",
" # Otherwise we finish.\n",
" \"end\": END\n",
" }\n",
" \"end\": END,\n",
" },\n",
")\n",
"\n",
"# We now add a normal edge from `tools` to `agent`.\n",
"# This means that after `tools` is called, `agent` node is called next.\n",
"workflow.add_edge('action', 'agent')\n",
"workflow.add_edge(\"action\", \"agent\")\n",
"\n",
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",
@@ -177,8 +177,10 @@
"from langchain_core.pydantic_v1 import BaseModel, Field\n",
"from langchain_core.utils.function_calling import convert_pydantic_to_openai_function\n",
"\n",
"\n",
"class Response(BaseModel):\n",
" \"\"\"Final response to the user\"\"\"\n",
"\n",
" temperature: float = Field(description=\"the temperature\")\n",
" other_notes: str = Field(description=\"any other notes about the weather\")\n",
"\n",
@@ -264,9 +266,10 @@
"import json\n",
"from langchain_core.messages import FunctionMessage\n",
"\n",
"\n",
"# Define the function that determines whether to continue or not\n",
"def should_continue(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" last_message = messages[-1]\n",
" # If there is no function call, then we finish\n",
" if \"function_call\" not in last_message.additional_kwargs:\n",
@@ -278,23 +281,27 @@
" else:\n",
" return \"continue\"\n",
"\n",
"\n",
"# Define the function that calls the model\n",
"def call_model(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" response = model.invoke(messages)\n",
" # We return a list, because this will get added to the existing list\n",
" return {\"messages\": [response]}\n",
"\n",
"\n",
"# Define the function to execute tools\n",
"def call_tool(state):\n",
" messages = state['messages']\n",
" messages = state[\"messages\"]\n",
" # Based on the continue condition\n",
" # we know the last message involves a function call\n",
" last_message = messages[-1]\n",
" # We construct an ToolInvocation from the function_call\n",
" action = ToolInvocation(\n",
" tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
" tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
" tool_input=json.loads(\n",
" last_message.additional_kwargs[\"function_call\"][\"arguments\"]\n",
" ),\n",
" )\n",
" # We call the tool_executor and get back a response\n",
" response = tool_executor.invoke(action)\n",
@@ -322,6 +329,7 @@
"outputs": [],
"source": [
"from langgraph.graph import StateGraph, END\n",
"\n",
"# Define a new graph\n",
"workflow = StateGraph(AgentState)\n",
"\n",
@@ -350,13 +358,13 @@
" # If `tools`, then we call the tool node.\n",
" \"continue\": \"action\",\n",
" # Otherwise we finish.\n",
" \"end\": END\n",
" }\n",
" \"end\": END,\n",
" },\n",
")\n",
"\n",
"# We now add a normal edge from `tools` to `agent`.\n",
"# This means that after `tools` is called, `agent` node is called next.\n",
"workflow.add_edge('action', 'agent')\n",
"workflow.add_edge(\"action\", \"agent\")\n",
"\n",
"# Finally, we compile it!\n",
"# This compiles it into a LangChain Runnable,\n",