{ "cells": [ { "cell_type": "markdown", "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53", "metadata": {}, "source": [ "# Get/Update State\n", "\n", "When running LangGraph agents, you can easily save good threads and use them in the future.\n", "\n", "**Note:** this requires passing in a checkpointer.\n" ] }, { "cell_type": "markdown", "id": "7cbd446a-808f-4394-be92-d45ab818953c", "metadata": {}, "source": [ "## Setup\n", "\n", "First we need to install the packages required\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.3.2\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" ] } ], "source": [ "!%pip install --quiet -U langgraph langchain langchain_openai tavily-pythonvily-python" ] }, { "cell_type": "markdown", "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d", "metadata": {}, "source": [ "Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OpenAI API Key: ········\n", "Tavily API Key: ········\n" ] } ], "source": [ "import getpass\n", "import os\n", "\n", "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" ] }, { "cell_type": "markdown", "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c", "metadata": {}, "source": [ "Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability.\n" ] }, { "cell_type": "code", "execution_count": null, "id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3", "metadata": {}, "outputs": [], "source": [ "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", "os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass(\"LangSmith API Key:\")" ] }, { "cell_type": "markdown", "id": "21ac643b-cb06-4724-a80c-2862ba4773f1", "metadata": {}, "source": [ "## Set up the tools\n", "\n", "We will first define the tools we want to use.\n", "For this simple example, we will use a built-in search tool via Tavily.\n", "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "d7ef57dd-5d6e-4ad3-9377-a92201c1310e", "metadata": {}, "outputs": [], "source": [ "from langchain_community.tools.tavily_search import TavilySearchResults\n", "\n", "tools = [TavilySearchResults(max_results=1)]" ] }, { "cell_type": "markdown", "id": "01885785-b71a-44d1-b1d6-7b5b14d53b58", "metadata": {}, "source": [ "We can now wrap these tools in a simple ToolNode.\n", "This is a prebuilt node that extracts tool calls from the most recent AIMessage, executes them, and returns a ToolMessage with the results.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "5cf3331e-ccb3-41c8-aeb9-a840a94d41e7", "metadata": {}, "outputs": [], "source": [ "from langgraph.prebuilt import ToolNode\n", "\n", "tool_node = ToolNode(tools)" ] }, { "cell_type": "markdown", "id": "5497ed70-fce3-47f1-9cad-46f912bad6a5", "metadata": {}, "source": [ "## Set up the model\n", "\n", "Now we need to load the chat model we want to use.\n", "Importantly, this should satisfy two criteria:\n", "\n", "1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.\n", "2. It should work with OpenAI function calling. This means it should either be an OpenAI model or a model that exposes a similar interface.\n", "\n", "Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "892b54b9-75f0-4804-9ed0-88b5e5532989", "metadata": {}, "outputs": [], "source": [ "from langchain_openai import ChatOpenAI\n", "\n", "model = ChatOpenAI(temperature=0)" ] }, { "cell_type": "markdown", "id": "a77995c0-bae2-4cee-a036-8688a90f05b9", "metadata": {}, "source": [ "After we've done this, we should make sure the model knows that it has these tools available to call.\n", "We can do this using the `.bind_tools()` method, common to many of LangChain's chat models.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "cd3cbae5-d92c-4559-a4aa-44721b80d107", "metadata": {}, "outputs": [], "source": [ "model = model.bind_tools(tools)" ] }, { "cell_type": "markdown", "id": "e03c5094-9297-4d19-a04e-3eedc75cefb4", "metadata": {}, "source": [ "## Define the nodes\n", "\n", "We now need to define a few different nodes in our graph.\n", "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", "There are two main nodes we need for this:\n", "\n", "1. The agent: responsible for deciding what (if any) actions to take.\n", "2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.\n", "\n", "We will also need to define some edges.\n", "Some of these edges may be conditional.\n", "The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n", "The path that is taken is not known until that node is run (the LLM decides).\n", "\n", "1. Conditional Edge: after the agent is called, we should either:\n", " a. If the agent said to take an action, then the function to invoke tools should be called\n", " b. If the agent said that it was finished, then it should finish\n", "2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n", "\n", "Let's define the nodes, as well as a function to decide how what conditional edge to take.\n" ] }, { "cell_type": "code", "execution_count": 77, "id": "3b541bb9-900c-40d0-964d-7b5dfee30667", "metadata": {}, "outputs": [], "source": [ "# Define the function that determines whether to continue or not\n", "def should_continue(state):\n", " last_message = state[\"messages\"][-1]\n", " # If there is no function call, then we finish\n", " if not last_message.tool_calls:\n", " return \"end\"\n", " # Otherwise if there is, we continue\n", " else:\n", " return \"continue\"" ] }, { "cell_type": "markdown", "id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b", "metadata": {}, "source": [ "## Define the graph\n", "\n", "We can now put it all together and define the graph!\n" ] }, { "cell_type": "code", "execution_count": 154, "id": "812b4e70-4956-4415-8880-db48b3dcbad2", "metadata": {}, "outputs": [], "source": [ "from typing import Annotated, TypedDict\n", "\n", "from langchain_core.messages import (\n", " AIMessage,\n", " AnyMessage,\n", " HumanMessage,\n", " SystemMessage,\n", " ToolMessage,\n", ")\n", "\n", "from langgraph.graph import END, StateGraph\n", "from langgraph.graph.message import add_messages\n", "from langgraph.managed.few_shot import FewShotExamples\n", "\n", "\n", "class BaseState(TypedDict):\n", " messages: Annotated[list[AnyMessage], add_messages]\n", " examples: Annotated[list, FewShotExamples]\n", "\n", "\n", "def _render_message(m):\n", " if isinstance(m, HumanMessage):\n", " return \"Human: \" + m.content\n", " elif isinstance(m, AIMessage):\n", " _m = \"AI: \" + m.content\n", " if len(m.tool_calls) > 0:\n", " _m += f\" Tools: {m.tool_calls}\"\n", " return _m\n", " elif isinstance(m, ToolMessage):\n", " return \"Tool Result: ...\"\n", " else:\n", " raise ValueError\n", "\n", "\n", "def _render_messages(ms):\n", " m_string = [_render_message(m) for m in ms]\n", " return \"\\n\".join(m_string)\n", "\n", "\n", "# Define a new graph\n", "workflow = StateGraph(BaseState)\n", "\n", "\n", "def _agent(state: BaseState):\n", " if len(state[\"examples\"]) > 0:\n", " _examples = \"\\n\\n\".join(\n", " [\n", " f\"Example {i}: \" + _render_messages(e[\"messages\"])\n", " for i, e in enumerate(state[\"examples\"])\n", " ]\n", " )\n", " system_message = \"\"\"You are a helpful assistant. Below are some examples of interactions you had with users. \\\n", "These were good interactions where the final result they got was the desired one. As much as possible, you should learn from these interactions and mimic them in the future. \\\n", "Pay particularly close attention to when tools are called, and what the inputs are.!\n", "\n", "{examples}\n", "\n", "Assist the user as they require!\"\"\".format(examples=_examples)\n", "\n", " else:\n", " system_message = \"\"\"You are a helpful assistant\"\"\"\n", " output = model.invoke([SystemMessage(content=system_message)] + state[\"messages\"])\n", " return {\"messages\": [output]}\n", "\n", "\n", "# Define the two nodes we will cycle between\n", "workflow.add_node(\"agent\", _agent)\n", "workflow.add_node(\"action\", tool_node)\n", "\n", "# Set the entrypoint as `agent`\n", "# This means that this node is the first one called\n", "workflow.set_entry_point(\"agent\")\n", "\n", "# We now add a conditional edge\n", "workflow.add_conditional_edges(\n", " # First, we define the start node. We use `agent`.\n", " # This means these are the edges taken after the `agent` node is called.\n", " \"agent\",\n", " # Next, we pass in the function that will determine which node is called next.\n", " should_continue,\n", " # Finally we pass in a mapping.\n", " # The keys are strings, and the values are other nodes.\n", " # END is a special node marking that the graph should finish.\n", " # What will happen is we will call `should_continue`, and then the output of that\n", " # will be matched against the keys in this mapping.\n", " # Based on which one it matches, that node will then be called.\n", " {\n", " # If `tools`, then we call the tool node.\n", " \"continue\": \"action\",\n", " # Otherwise we finish.\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\")" ] }, { "cell_type": "markdown", "id": "bc9c8536-f90b-44fa-958d-5df016c66d8f", "metadata": {}, "source": [ "**Persistence**\n", "\n", "To add in persistence, we pass in a checkpoint when compiling the graph\n" ] }, { "cell_type": "code", "execution_count": 115, "id": "6845ed6a-d155-4105-9160-28849877248b", "metadata": {}, "outputs": [], "source": [ "from langgraph.checkpoint.sqlite import SqliteSaver\n", "\n", "memory = SqliteSaver.from_conn_string(\":memory:\")" ] }, { "cell_type": "code", "execution_count": 155, "id": "79d29875-8aa8-434c-9f20-1c58346a6249", "metadata": {}, "outputs": [], "source": [ "# Finally, we compile it!\n", "# This compiles it into a LangChain Runnable,\n", "# meaning you can use it as you would any other runnable\n", "app = workflow.compile(checkpointer=memory, interrupt_before=[\"action\"])" ] }, { "cell_type": "markdown", "id": "e8aff75b-563e-42b1-969b-742201514fc3", "metadata": {}, "source": [ "## Preview the graph\n" ] }, { "cell_type": "code", "execution_count": 156, "id": "c9ab60eb-679b-4eef-9e64-5ffbf3dffc70", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 156, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import Image\n", "\n", "Image(app.get_graph().draw_png())" ] }, { "cell_type": "markdown", "id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159", "metadata": {}, "source": [ "## Interacting with the Agent\n", "\n", "We can now interact with the agent. Between interactions you can get and update state.\n" ] }, { "cell_type": "code", "execution_count": 118, "id": "cfd140f0-a5a6-4697-8115-322242f197b5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 94, 'total_tokens': 115}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-3764f79e-17b4-4aa3-bbe4-4f92b11ca52c-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h'}])]}\n" ] } ], "source": [ "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n", "for event in app.stream(\n", " {\"messages\": [HumanMessage(content=\"whats the weather in sf?\")]}, thread\n", "):\n", " for v in event.values():\n", " print(v)" ] }, { "cell_type": "code", "execution_count": 120, "id": "4479f8ae-7c46-4117-8ca2-0a9c2ef9785b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'messages': [HumanMessage(content='whats the weather in sf?', id='1cfd0c2f-9b60-48da-8938-408fd6aeda13'),\n", " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 94, 'total_tokens': 115}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-3764f79e-17b4-4aa3-bbe4-4f92b11ca52c-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h'}])]}" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "current_values = app.get_state(thread)\n", "current_values.values" ] }, { "cell_type": "code", "execution_count": 122, "id": "1a0cdb78-40c6-4550-8c27-8f1b02d9e678", "metadata": {}, "outputs": [], "source": [ "current_values.values[\"messages\"][-1].tool_calls[0][\"args\"][\n", " \"query\"\n", "] = \"weather in San Francisco, Accuweather\"" ] }, { "cell_type": "code", "execution_count": 126, "id": "652f699a-89bc-4277-b37a-c3d94b835df5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'configurable': {'thread_id': '1',\n", " 'thread_ts': '2024-04-20T01:13:15.108790+00:00'}}" ] }, "execution_count": 126, "metadata": {}, "output_type": "execute_result" } ], "source": [ "app.update_state(thread, current_values.values)" ] }, { "cell_type": "code", "execution_count": 127, "id": "e2b29825-a108-4d40-b377-22e8f4629d64", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "StateSnapshot(values={'messages': [HumanMessage(content='whats the weather in sf?', id='1cfd0c2f-9b60-48da-8938-408fd6aeda13'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 94, 'total_tokens': 115}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-3764f79e-17b4-4aa3-bbe4-4f92b11ca52c-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco, Accuweather'}, 'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h'}])]}, next=('action',), config={'configurable': {'thread_id': '1', 'thread_ts': '2024-04-20T01:13:15.108790+00:00'}}, parent_config=None)" ] }, "execution_count": 127, "metadata": {}, "output_type": "execute_result" } ], "source": [ "app.get_state(thread)" ] }, { "cell_type": "code", "execution_count": 128, "id": "f0aad8a6-056e-42ca-bbc2-b45f768da75f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'messages': [ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1713575495, \\'localtime\\': \\'2024-04-19 18:11\\'}, \\'current\\': {\\'last_updated_epoch\\': 1713574800, \\'last_updated\\': \\'2024-04-19 18:00\\', \\'temp_c\\': 16.1, \\'temp_f\\': 61.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Sunny\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/113.png\\', \\'code\\': 1000}, \\'wind_mph\\': 16.1, \\'wind_kph\\': 25.9, \\'wind_degree\\': 300, \\'wind_dir\\': \\'WNW\\', \\'pressure_mb\\': 1015.0, \\'pressure_in\\': 29.97, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 67, \\'cloud\\': 0, \\'feelslike_c\\': 16.1, \\'feelslike_f\\': 61.0, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 4.0, \\'gust_mph\\': 20.6, \\'gust_kph\\': 33.1}}\"}]', name='tavily_search_results_json', id='8c7e9af3-6569-4982-a83d-be1ec02f828a', tool_call_id='call_yQrJa8CEOfKBdpVl80jzWf5h')]}\n", "{'messages': [AIMessage(content='The current weather in San Francisco is sunny with a temperature of 61.0°F (16.1°C). The wind speed is 25.9 km/h coming from the west-northwest direction. The humidity is at 67%, and there is no precipitation at the moment.', response_metadata={'token_usage': {'completion_tokens': 59, 'prompt_tokens': 476, 'total_tokens': 535}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'stop', 'logprobs': None}, id='run-7d652a0d-cb00-4857-b9fb-d3e37b0a6d23-0')]}\n" ] } ], "source": [ "for event in app.stream(None, thread):\n", " for v in event.values():\n", " print(v)" ] }, { "cell_type": "code", "execution_count": 129, "id": "84748206-975e-4a33-a178-d43df683298c", "metadata": {}, "outputs": [], "source": [ "chkpnt_tuple = memory.get_tuple({\"configurable\": {\"thread_id\": \"1\"}})\n", "config = chkpnt_tuple.config\n", "checkpoint = chkpnt_tuple.checkpoint\n", "metadata = chkpnt_tuple.metadata\n", "\n", "# mark as \"good\"\n", "metadata[\"score\"] = 1\n", "memory.put(config, checkpoint, metadata)" ] }, { "cell_type": "code", "execution_count": 130, "id": "ce7fa228-8c37-4001-afd4-0001b268e1db", "metadata": {}, "outputs": [], "source": [ "examples = list(memory.search({\"score\": 1}))" ] }, { "cell_type": "code", "execution_count": 131, "id": "0543a501-b4cb-4890-8236-3350ebee5af9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[CheckpointTuple(config={'configurable': {'thread_id': '1', 'thread_ts': '2024-04-20T01:13:36.933600+00:00'}}, checkpoint={'v': 1, 'ts': '2024-04-20T01:13:36.933600+00:00', 'channel_values': {'messages': [HumanMessage(content='whats the weather in sf?', id='1cfd0c2f-9b60-48da-8938-408fd6aeda13'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 94, 'total_tokens': 115}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-3764f79e-17b4-4aa3-bbe4-4f92b11ca52c-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco, Accuweather'}, 'id': 'call_yQrJa8CEOfKBdpVl80jzWf5h'}]), ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1713575495, \\'localtime\\': \\'2024-04-19 18:11\\'}, \\'current\\': {\\'last_updated_epoch\\': 1713574800, \\'last_updated\\': \\'2024-04-19 18:00\\', \\'temp_c\\': 16.1, \\'temp_f\\': 61.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Sunny\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/113.png\\', \\'code\\': 1000}, \\'wind_mph\\': 16.1, \\'wind_kph\\': 25.9, \\'wind_degree\\': 300, \\'wind_dir\\': \\'WNW\\', \\'pressure_mb\\': 1015.0, \\'pressure_in\\': 29.97, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 67, \\'cloud\\': 0, \\'feelslike_c\\': 16.1, \\'feelslike_f\\': 61.0, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 4.0, \\'gust_mph\\': 20.6, \\'gust_kph\\': 33.1}}\"}]', name='tavily_search_results_json', id='8c7e9af3-6569-4982-a83d-be1ec02f828a', tool_call_id='call_yQrJa8CEOfKBdpVl80jzWf5h'), AIMessage(content='The current weather in San Francisco is sunny with a temperature of 61.0°F (16.1°C). The wind speed is 25.9 km/h coming from the west-northwest direction. The humidity is at 67%, and there is no precipitation at the moment.', response_metadata={'token_usage': {'completion_tokens': 59, 'prompt_tokens': 476, 'total_tokens': 535}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'stop', 'logprobs': None}, id='run-7d652a0d-cb00-4857-b9fb-d3e37b0a6d23-0')], 'agent': {'messages': [AIMessage(content='The current weather in San Francisco is sunny with a temperature of 61.0°F (16.1°C). The wind speed is 25.9 km/h coming from the west-northwest direction. The humidity is at 67%, and there is no precipitation at the moment.', response_metadata={'token_usage': {'completion_tokens': 59, 'prompt_tokens': 476, 'total_tokens': 535}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'stop', 'logprobs': None}, id='run-7d652a0d-cb00-4857-b9fb-d3e37b0a6d23-0')]}}, 'channel_versions': defaultdict(, {'__start__': 1, 'messages': 6, 'start:agent': 2, 'action': 5, 'agent': 6, 'branch:agent:should_continue:action': 4}), 'versions_seen': defaultdict(, {'__start__': defaultdict(, {'__start__': 1}), 'agent': defaultdict(, {'start:agent': 2, 'action': 5}), 'action': defaultdict(, {'branch:agent:should_continue:action': 4}), '__interrupt__': defaultdict(, {'messages': 4})})}, parent_config={'configurable': {'thread_id': '1', 'thread_ts': '2024-04-20T01:13:35.392072+00:00'}})]" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "examples" ] }, { "cell_type": "code", "execution_count": 157, "id": "336a70d3-d8c7-4310-a373-df2be3320030", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_amNrYLgitup6hCDiHUYKwodH', 'function': {'arguments': '{\"query\":\"weather in Los Angeles, Accuweather\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 296, 'total_tokens': 321}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-6b852685-e84f-48e7-b8d0-5b0a44ac9776-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in Los Angeles, Accuweather'}, 'id': 'call_amNrYLgitup6hCDiHUYKwodH'}])]}\n" ] } ], "source": [ "thread = {\"configurable\": {\"thread_id\": \"7\"}}\n", "for event in app.stream(\n", " {\"messages\": [HumanMessage(content=\"whats the weather in la?\")]}, thread\n", "):\n", " for v in event.values():\n", " print(v)" ] }, { "cell_type": "code", "execution_count": 158, "id": "ab245e7c-e47d-45e4-8b79-361fab359e76", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'messages': [ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'Los Angeles\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 34.05, \\'lon\\': -118.24, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1713576177, \\'localtime\\': \\'2024-04-19 18:22\\'}, \\'current\\': {\\'last_updated_epoch\\': 1713575700, \\'last_updated\\': \\'2024-04-19 18:15\\', \\'temp_c\\': 17.8, \\'temp_f\\': 64.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 4.3, \\'wind_kph\\': 6.8, \\'wind_degree\\': 250, \\'wind_dir\\': \\'WSW\\', \\'pressure_mb\\': 1014.0, \\'pressure_in\\': 29.94, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 65, \\'cloud\\': 50, \\'feelslike_c\\': 17.8, \\'feelslike_f\\': 64.0, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 5.0, \\'gust_mph\\': 10.3, \\'gust_kph\\': 16.6}}\"}]', name='tavily_search_results_json', id='6477bd73-bdf1-46c5-ab0a-cc808b1a183e', tool_call_id='call_amNrYLgitup6hCDiHUYKwodH')]}\n", "{'messages': [AIMessage(content='The current weather in Los Angeles is partly cloudy with a temperature of 64.0°F (17.8°C). The wind speed is 6.8 km/h coming from the west-southwest direction. The humidity is at 65%, and there is no precipitation at the moment.', response_metadata={'token_usage': {'completion_tokens': 60, 'prompt_tokens': 679, 'total_tokens': 739}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_c2295e73ad', 'finish_reason': 'stop', 'logprobs': None}, id='run-a604c04e-c3c3-4545-804f-a89aee6bf516-0')]}\n" ] } ], "source": [ "for event in app.stream(None, thread):\n", " for v in event.values():\n", " print(v)" ] }, { "cell_type": "code", "execution_count": null, "id": "9ab115de-9b11-4e8b-8ace-c23e1369300b", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }