From f9540ca6b6a623267dab25e336e6b6a7ed2682d9 Mon Sep 17 00:00:00 2001 From: Brian Sam-Bodden Date: Wed, 5 Feb 2025 07:28:10 -0700 Subject: [PATCH 01/11] docs: more notebook examples --- .../docs/create-react-agent-memory.ipynb | 62 +++----- langgraph/docs/cross-thread-persistence.ipynb | 143 +++++++++--------- langgraph/docs/persistence-functional.ipynb | 23 ++- langgraph/docs/persistence_redis.ipynb | 68 ++++----- 4 files changed, 138 insertions(+), 158 deletions(-) diff --git a/langgraph/docs/create-react-agent-memory.ipynb b/langgraph/docs/create-react-agent-memory.ipynb index 994cf63..736d75b 100644 --- a/langgraph/docs/create-react-agent-memory.ipynb +++ b/langgraph/docs/create-react-agent-memory.ipynb @@ -58,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 9, "id": "a213e11a-5c62-4ddb-a707-490d91add383", "metadata": {}, "outputs": [], @@ -69,18 +69,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 10, "id": "23a1885c-04ab-4750-aefa-105891fddf3e", "metadata": {}, - "outputs": [ - { - "name": "stdin", - "output_type": "stream", - "text": [ - "OPENAI_API_KEY: ········\n" - ] - } - ], + "outputs": [], "source": [ "import getpass\n", "import os\n", @@ -117,35 +109,24 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 11, "id": "7a154152-973e-4b5d-aa13-48c617744a4c", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "22:31:51 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:31:51 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:31:51 redisvl.index.index INFO Index already exists, not overwriting.\n" - ] - } - ], + "outputs": [], "source": [ - "from typing import Literal\n", - "\n", - "from langchain_core.tools import tool\n", - "\n", "# First we initialize the model we want to use.\n", "from langchain_openai import ChatOpenAI\n", "\n", - "from langgraph.checkpoint.redis import RedisSaver\n", - "from langgraph.prebuilt import create_react_agent\n", - "\n", "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", "\n", "\n", "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n", + "\n", + "from typing import Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "\n", + "\n", "@tool\n", "def get_weather(city: Literal[\"nyc\", \"sf\"]):\n", " \"\"\"Use this to get weather information.\"\"\"\n", @@ -159,18 +140,19 @@ "\n", "tools = [get_weather]\n", "\n", - "# We can add \"chat memory\" to the graph with LangGraph's Redis checkpointer\n", + "# We can add \"chat memory\" to the graph with LangGraph's Redi checkpointer\n", "# to retain the chat context between interactions\n", - "\n", + "from langgraph.checkpoint.redis import RedisSaver\n", "\n", "REDIS_URI = \"redis://redis:6379\"\n", "memory = None\n", "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n", - " cp.setup()\n", - " memory = cp\n", + " cp.setup()\n", + " memory = cp\n", "\n", "# Define the graph\n", "\n", + "from langgraph.prebuilt import create_react_agent\n", "\n", "graph = create_react_agent(model, tools=tools, checkpointer=memory)" ] @@ -187,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 12, "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28", "metadata": {}, "outputs": [], @@ -203,7 +185,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 13, "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6", "metadata": {}, "outputs": [ @@ -216,8 +198,8 @@ "What's the weather in NYC?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " get_weather (call_Edwfw0WiyIJ7vt9xzU9xvyeg)\n", - " Call ID: call_Edwfw0WiyIJ7vt9xzU9xvyeg\n", + " get_weather (call_RHv6T6OBCn7eKOlm5qEpLK4P)\n", + " Call ID: call_RHv6T6OBCn7eKOlm5qEpLK4P\n", " Args:\n", " city: nyc\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -247,7 +229,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 14, "id": "187479f9-32fa-4611-9487-cf816ba2e147", "metadata": {}, "outputs": [ @@ -260,7 +242,7 @@ "What's it known for?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "Could you please specify what \"it\" refers to? Are you asking about a specific city, person, event, or something else?\n" + "Could you please specify what \"it\" refers to? Are you asking about a specific place, person, event, or something else?\n" ] } ], diff --git a/langgraph/docs/cross-thread-persistence.ipynb b/langgraph/docs/cross-thread-persistence.ipynb index 3f7626e..ac9864c 100644 --- a/langgraph/docs/cross-thread-persistence.ipynb +++ b/langgraph/docs/cross-thread-persistence.ipynb @@ -53,18 +53,18 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "3457aadf", "metadata": {}, "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install -U langchain_openai langchain_anthropic langgraph" + "%pip install -U langchain_openai langgraph" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "aa2c64a7", "metadata": {}, "outputs": [ @@ -124,15 +124,23 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 10, "id": "a7f303d6-612e-4e34-bf36-29d4ed25d802", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "20:57:29 redisvl.index.index INFO Index already exists, not overwriting.\n", + "20:57:29 redisvl.index.index INFO Index already exists, not overwriting.\n" + ] + } + ], "source": [ + "from langgraph.store.redis import RedisStore\n", "from langchain_openai import OpenAIEmbeddings\n", - "\n", "from langgraph.store.base import IndexConfig\n", - "from langgraph.store.redis import RedisStore\n", "\n", "REDIS_URI = \"redis://redis:6379\"\n", "\n", @@ -148,7 +156,7 @@ "redis_store = None\n", "with RedisStore.from_conn_string(REDIS_URI, index=index_config) as s:\n", " s.setup()\n", - " redis_store = s" + " redis_store = s " ] }, { @@ -161,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 11, "id": "2a30a362-528c-45ee-9df6-630d2d843588", "metadata": {}, "outputs": [ @@ -169,22 +177,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "22:32:41 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:32:41 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:32:41 redisvl.index.index INFO Index already exists, not overwriting.\n" + "20:57:32 redisvl.index.index INFO Index already exists, not overwriting.\n", + "20:57:32 redisvl.index.index INFO Index already exists, not overwriting.\n", + "20:57:32 redisvl.index.index INFO Index already exists, not overwriting.\n" ] } ], "source": [ "import uuid\n", + "from typing import Annotated\n", + "from typing_extensions import TypedDict\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_core.runnables import RunnableConfig\n", - "\n", - "from langgraph.checkpoint.redis import RedisSaver\n", - "from langgraph.graph import START, MessagesState, StateGraph\n", + "from langgraph.graph import StateGraph, MessagesState, START\n", "from langgraph.store.base import BaseStore\n", "\n", + "\n", "model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n", "\n", "\n", @@ -213,12 +222,13 @@ "builder.add_node(\"call_model\", call_model)\n", "builder.add_edge(START, \"call_model\")\n", "\n", + "from langgraph.checkpoint.redis import RedisSaver\n", "\n", "REDIS_URI = \"redis://redis:6379\"\n", "checkpointer = None\n", "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n", - " cp.setup()\n", - " checkpointer = cp\n", + " cp.setup()\n", + " checkpointer = cp\n", "\n", "# NOTE: we're passing the store object here when compiling the graph\n", "graph = builder.compile(checkpointer=checkpointer, store=redis_store)\n", @@ -256,7 +266,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 12, "id": "c871a073-a466-46ad-aafe-2b870831057e", "metadata": {}, "outputs": [ @@ -266,10 +276,43 @@ "text": [ "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", - "Hi! Remember: my name is Bob\n", - "==================================\u001b[1m Ai Message \u001b[0m==================================\n", - "\n", - "Hello Bob! It's nice to meet you. I'll remember that your name is Bob. How can I assist you today?\n" + "Hi! Remember: my name is Bob\n" + ] + }, + { + "ename": "RedisSearchError", + "evalue": "Error while searching: Syntax error at offset 21 near >[", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mResponseError\u001b[0m Traceback (most recent call last)", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:640\u001b[0m, in \u001b[0;36mSearchIndex.search\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 639\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 640\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_redis_client\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mft\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mschema\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mindex\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore\u001b[39;49;00m\n\u001b[1;32m 641\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m 642\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 643\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/commands/search/commands.py:508\u001b[0m, in \u001b[0;36mSearchCommands.search\u001b[0;34m(self, query, query_params)\u001b[0m\n\u001b[1;32m 506\u001b[0m options[NEVER_DECODE] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 508\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute_command\u001b[49m\u001b[43m(\u001b[49m\u001b[43mSEARCH_CMD\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 510\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(res, Pipeline):\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:559\u001b[0m, in \u001b[0;36mRedis.execute_command\u001b[0;34m(self, *args, **options)\u001b[0m\n\u001b[1;32m 558\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mexecute_command\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions):\n\u001b[0;32m--> 559\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execute_command\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:567\u001b[0m, in \u001b[0;36mRedis._execute_command\u001b[0;34m(self, *args, **options)\u001b[0m\n\u001b[1;32m 566\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 567\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mconn\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mretry\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcall_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 568\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_send_command_parse_response\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 569\u001b[0m \u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\n\u001b[1;32m 570\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 571\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_disconnect_raise\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 572\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 573\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/retry.py:62\u001b[0m, in \u001b[0;36mRetry.call_with_retry\u001b[0;34m(self, do, fail)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 62\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdo\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 63\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_supported_errors \u001b[38;5;28;01mas\u001b[39;00m error:\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:568\u001b[0m, in \u001b[0;36mRedis._execute_command..\u001b[0;34m()\u001b[0m\n\u001b[1;32m 566\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 567\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m conn\u001b[38;5;241m.\u001b[39mretry\u001b[38;5;241m.\u001b[39mcall_with_retry(\n\u001b[0;32m--> 568\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m: \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_send_command_parse_response\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 569\u001b[0m \u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\n\u001b[1;32m 570\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 571\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m error: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_disconnect_raise(conn, error),\n\u001b[1;32m 572\u001b[0m )\n\u001b[1;32m 573\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:542\u001b[0m, in \u001b[0;36mRedis._send_command_parse_response\u001b[0;34m(self, conn, command_name, *args, **options)\u001b[0m\n\u001b[1;32m 541\u001b[0m conn\u001b[38;5;241m.\u001b[39msend_command(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions)\n\u001b[0;32m--> 542\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:581\u001b[0m, in \u001b[0;36mRedis.parse_response\u001b[0;34m(self, connection, command_name, **options)\u001b[0m\n\u001b[1;32m 580\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m NEVER_DECODE \u001b[38;5;129;01min\u001b[39;00m options:\n\u001b[0;32m--> 581\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mconnection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdisable_decoding\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 582\u001b[0m options\u001b[38;5;241m.\u001b[39mpop(NEVER_DECODE)\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/connection.py:616\u001b[0m, in \u001b[0;36mAbstractConnection.read_response\u001b[0;34m(self, disable_decoding, disconnect_on_error, push_request)\u001b[0m\n\u001b[1;32m 615\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 616\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m response\n\u001b[1;32m 617\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n", + "\u001b[0;31mResponseError\u001b[0m: Syntax error at offset 21 near >[", + "\nThe above exception was the direct cause of the following exception:\n", + "\u001b[0;31mRedisSearchError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[12], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m config \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconfigurable\u001b[39m\u001b[38;5;124m\"\u001b[39m: {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mthread_id\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m1\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser_id\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m1\u001b[39m\u001b[38;5;124m\"\u001b[39m}}\n\u001b[1;32m 2\u001b[0m input_message \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrole\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHi! Remember: my name is Bob\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mgraph\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43minput_message\u001b[49m\u001b[43m]\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mvalues\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpretty_print\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:1724\u001b[0m, in \u001b[0;36mPregel.stream\u001b[0;34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)\u001b[0m\n\u001b[1;32m 1718\u001b[0m \u001b[38;5;66;03m# Similarly to Bulk Synchronous Parallel / Pregel model\u001b[39;00m\n\u001b[1;32m 1719\u001b[0m \u001b[38;5;66;03m# computation proceeds in steps, while there are channel updates.\u001b[39;00m\n\u001b[1;32m 1720\u001b[0m \u001b[38;5;66;03m# Channel updates from step N are only visible in step N+1\u001b[39;00m\n\u001b[1;32m 1721\u001b[0m \u001b[38;5;66;03m# channels are guaranteed to be immutable for the duration of the step,\u001b[39;00m\n\u001b[1;32m 1722\u001b[0m \u001b[38;5;66;03m# with channel updates applied only at the transition between steps.\u001b[39;00m\n\u001b[1;32m 1723\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m loop\u001b[38;5;241m.\u001b[39mtick(input_keys\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39minput_channels):\n\u001b[0;32m-> 1724\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m_\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mrunner\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtick\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1725\u001b[0m \u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtasks\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalues\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1726\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstep_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1727\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1728\u001b[0m \u001b[43m \u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mget_waiter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1729\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 1730\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# emit output\u001b[39;49;00m\n\u001b[1;32m 1731\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield from\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43moutput\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1732\u001b[0m \u001b[38;5;66;03m# emit output\u001b[39;00m\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:230\u001b[0m, in \u001b[0;36mPregelRunner.tick\u001b[0;34m(self, tasks, reraise, timeout, retry_policy, get_waiter)\u001b[0m\n\u001b[1;32m 228\u001b[0m t \u001b[38;5;241m=\u001b[39m tasks[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m 229\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 230\u001b[0m \u001b[43mrun_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 231\u001b[0m \u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 232\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 233\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfigurable\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m{\u001b[49m\n\u001b[1;32m 234\u001b[0m \u001b[43m \u001b[49m\u001b[43mCONFIG_KEY_SEND\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpartial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mwriter\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 235\u001b[0m \u001b[43m \u001b[49m\u001b[43mCONFIG_KEY_CALL\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpartial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcall\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 236\u001b[0m \u001b[43m \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 237\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 238\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcommit(t, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[1;32m 239\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/pregel/retry.py:40\u001b[0m, in \u001b[0;36mrun_with_retry\u001b[0;34m(task, retry_policy, configurable)\u001b[0m\n\u001b[1;32m 38\u001b[0m task\u001b[38;5;241m.\u001b[39mwrites\u001b[38;5;241m.\u001b[39mclear()\n\u001b[1;32m 39\u001b[0m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[0;32m---> 40\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mtask\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mproc\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtask\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 41\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[1;32m 42\u001b[0m ns: \u001b[38;5;28mstr\u001b[39m \u001b[38;5;241m=\u001b[39m config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:495\u001b[0m, in \u001b[0;36mRunnableSeq.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 491\u001b[0m config \u001b[38;5;241m=\u001b[39m patch_config(\n\u001b[1;32m 492\u001b[0m config, callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mseq:step:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;250m \u001b[39m\u001b[38;5;241m+\u001b[39m\u001b[38;5;250m \u001b[39m\u001b[38;5;241m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 493\u001b[0m )\n\u001b[1;32m 494\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m i \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m--> 495\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 496\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 497\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m step\u001b[38;5;241m.\u001b[39minvoke(\u001b[38;5;28minput\u001b[39m, config)\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:259\u001b[0m, in \u001b[0;36mRunnableCallable.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 257\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 258\u001b[0m context\u001b[38;5;241m.\u001b[39mrun(_set_config_context, config)\n\u001b[0;32m--> 259\u001b[0m ret \u001b[38;5;241m=\u001b[39m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 260\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(ret, Runnable) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrecurse:\n\u001b[1;32m 261\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ret\u001b[38;5;241m.\u001b[39minvoke(\u001b[38;5;28minput\u001b[39m, config)\n", + "Cell \u001b[0;32mIn[11], line 19\u001b[0m, in \u001b[0;36mcall_model\u001b[0;34m(state, config, store)\u001b[0m\n\u001b[1;32m 17\u001b[0m user_id \u001b[38;5;241m=\u001b[39m config[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconfigurable\u001b[39m\u001b[38;5;124m\"\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser_id\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n\u001b[1;32m 18\u001b[0m namespace \u001b[38;5;241m=\u001b[39m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmemories\u001b[39m\u001b[38;5;124m\"\u001b[39m, user_id)\n\u001b[0;32m---> 19\u001b[0m memories \u001b[38;5;241m=\u001b[39m \u001b[43mstore\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamespace\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mstate\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcontent\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 20\u001b[0m info \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mjoin([d\u001b[38;5;241m.\u001b[39mvalue[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdata\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;28;01mfor\u001b[39;00m d \u001b[38;5;129;01min\u001b[39;00m memories])\n\u001b[1;32m 21\u001b[0m system_msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mYou are a helpful assistant talking to the user. User info: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00minfo\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/store/base/__init__.py:709\u001b[0m, in \u001b[0;36mBaseStore.search\u001b[0;34m(self, namespace_prefix, query, filter, limit, offset)\u001b[0m\n\u001b[1;32m 654\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21msearch\u001b[39m(\n\u001b[1;32m 655\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 656\u001b[0m namespace_prefix: \u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 662\u001b[0m offset: \u001b[38;5;28mint\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m,\n\u001b[1;32m 663\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mlist\u001b[39m[SearchItem]:\n\u001b[1;32m 664\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Search for items within a namespace prefix.\u001b[39;00m\n\u001b[1;32m 665\u001b[0m \n\u001b[1;32m 666\u001b[0m \u001b[38;5;124;03m Args:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 707\u001b[0m \u001b[38;5;124;03m and requires proper embedding configuration.\u001b[39;00m\n\u001b[1;32m 708\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 709\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbatch\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43mSearchOp\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamespace_prefix\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mfilter\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlimit\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moffset\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m[\u001b[38;5;241m0\u001b[39m]\n", + "File \u001b[0;32m~/workspace/libs/checkpoint-redis/langgraph/store/redis/__init__.py:111\u001b[0m, in \u001b[0;36mRedisStore.batch\u001b[0;34m(self, ops)\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_batch_put_ops(cast(\u001b[38;5;28mlist\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, PutOp]], grouped_ops[PutOp]))\n\u001b[1;32m 110\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m SearchOp \u001b[38;5;129;01min\u001b[39;00m grouped_ops:\n\u001b[0;32m--> 111\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_batch_search_ops\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 112\u001b[0m \u001b[43m \u001b[49m\u001b[43mcast\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;28;43mtuple\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mSearchOp\u001b[49m\u001b[43m]\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgrouped_ops\u001b[49m\u001b[43m[\u001b[49m\u001b[43mSearchOp\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mresults\u001b[49m\n\u001b[1;32m 113\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ListNamespacesOp \u001b[38;5;129;01min\u001b[39;00m grouped_ops:\n\u001b[1;32m 116\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_batch_list_namespaces_ops(\n\u001b[1;32m 117\u001b[0m cast(\n\u001b[1;32m 118\u001b[0m Sequence[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, ListNamespacesOp]],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 121\u001b[0m results,\n\u001b[1;32m 122\u001b[0m )\n", + "File \u001b[0;32m~/workspace/libs/checkpoint-redis/langgraph/store/redis/__init__.py:279\u001b[0m, in \u001b[0;36mRedisStore._batch_search_ops\u001b[0;34m(self, search_ops, results)\u001b[0m\n\u001b[1;32m 276\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m op\u001b[38;5;241m.\u001b[39mquery \u001b[38;5;129;01mand\u001b[39;00m idx \u001b[38;5;129;01min\u001b[39;00m query_vectors:\n\u001b[1;32m 277\u001b[0m \u001b[38;5;66;03m# Vector similarity search\u001b[39;00m\n\u001b[1;32m 278\u001b[0m vector \u001b[38;5;241m=\u001b[39m query_vectors[idx]\n\u001b[0;32m--> 279\u001b[0m vector_results \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvector_index\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquery\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 280\u001b[0m \u001b[43m \u001b[49m\u001b[43mVectorQuery\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 281\u001b[0m \u001b[43m \u001b[49m\u001b[43mvector\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mvector\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtolist\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mhasattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mvector\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtolist\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mvector\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 282\u001b[0m \u001b[43m \u001b[49m\u001b[43mvector_field_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43membedding\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 283\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilter_expression\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m@prefix:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43m_namespace_to_text\u001b[49m\u001b[43m(\u001b[49m\u001b[43mop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnamespace_prefix\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m*\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 284\u001b[0m \u001b[43m \u001b[49m\u001b[43mreturn_fields\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mprefix\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mkey\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mvector_distance\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 285\u001b[0m \u001b[43m \u001b[49m\u001b[43mnum_results\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlimit\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 286\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 287\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 289\u001b[0m \u001b[38;5;66;03m# Get matching store docs in pipeline\u001b[39;00m\n\u001b[1;32m 290\u001b[0m pipe \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_redis\u001b[38;5;241m.\u001b[39mpipeline()\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:678\u001b[0m, in \u001b[0;36mSearchIndex.query\u001b[0;34m(self, query)\u001b[0m\n\u001b[1;32m 653\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mquery\u001b[39m(\u001b[38;5;28mself\u001b[39m, query: BaseQuery) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m List[Dict[\u001b[38;5;28mstr\u001b[39m, Any]]:\n\u001b[1;32m 654\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Execute a query on the index.\u001b[39;00m\n\u001b[1;32m 655\u001b[0m \n\u001b[1;32m 656\u001b[0m \u001b[38;5;124;03m This method takes a BaseQuery object directly, runs the search, and\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 676\u001b[0m \n\u001b[1;32m 677\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 678\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_query\u001b[49m\u001b[43m(\u001b[49m\u001b[43mquery\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:648\u001b[0m, in \u001b[0;36mSearchIndex._query\u001b[0;34m(self, query)\u001b[0m\n\u001b[1;32m 646\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21m_query\u001b[39m(\u001b[38;5;28mself\u001b[39m, query: BaseQuery) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m List[Dict[\u001b[38;5;28mstr\u001b[39m, Any]]:\n\u001b[1;32m 647\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Execute a query and process results.\"\"\"\u001b[39;00m\n\u001b[0;32m--> 648\u001b[0m results \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mquery_params\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 649\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m process_results(\n\u001b[1;32m 650\u001b[0m results, query\u001b[38;5;241m=\u001b[39mquery, storage_type\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mschema\u001b[38;5;241m.\u001b[39mindex\u001b[38;5;241m.\u001b[39mstorage_type\n\u001b[1;32m 651\u001b[0m )\n", + "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:644\u001b[0m, in \u001b[0;36mSearchIndex.search\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 640\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_redis_client\u001b[38;5;241m.\u001b[39mft(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mschema\u001b[38;5;241m.\u001b[39mindex\u001b[38;5;241m.\u001b[39mname)\u001b[38;5;241m.\u001b[39msearch( \u001b[38;5;66;03m# type: ignore\u001b[39;00m\n\u001b[1;32m 641\u001b[0m \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs\n\u001b[1;32m 642\u001b[0m )\n\u001b[1;32m 643\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m--> 644\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m RedisSearchError(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mError while searching: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01me\u001b[39;00m\n", + "\u001b[0;31mRedisSearchError\u001b[0m: Error while searching: Syntax error at offset 21 near >[", + "\u001b[0mDuring task with name 'call_model' and id 'a732d958-d044-82a7-58e4-3eb8bb5f1a5a'" ] } ], @@ -282,23 +325,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "d862be40-1f8a-4057-81c4-b7bf073dc4c1", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "================================\u001b[1m Human Message \u001b[0m=================================\n", - "\n", - "what is my name?\n", - "==================================\u001b[1m Ai Message \u001b[0m==================================\n", - "\n", - "Your name is Bob.\n" - ] - } - ], + "outputs": [], "source": [ "config = {\"configurable\": {\"thread_id\": \"2\", \"user_id\": \"1\"}}\n", "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n", @@ -316,20 +346,12 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "76cde493-89cf-4709-a339-207d2b7e9ea7", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'data': 'User name is Bob'}\n" - ] - } - ], + "outputs": [], "source": [ - "for memory in redis_store.search((\"memories\", \"1\")):\n", + "for memory in in_memory_store.search((\"memories\", \"1\")):\n", " print(memory.value)" ] }, @@ -343,37 +365,16 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "d362350b-d730-48bd-9652-983812fd7811", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "================================\u001b[1m Human Message \u001b[0m=================================\n", - "\n", - "what is my name?\n", - "==================================\u001b[1m Ai Message \u001b[0m==================================\n", - "\n", - "I apologize, but I don't have any information about your name. As an AI assistant, I don't have access to personal information about users unless it's explicitly provided in our conversation. If you'd like, you can tell me your name and I'll be happy to use it in our discussion.\n" - ] - } - ], + "outputs": [], "source": [ "config = {\"configurable\": {\"thread_id\": \"3\", \"user_id\": \"2\"}}\n", "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n", "for chunk in graph.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n", " chunk[\"messages\"][-1].pretty_print()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60787b3c-216e-4f38-b974-ea1d7f9d8642", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/langgraph/docs/persistence-functional.ipynb b/langgraph/docs/persistence-functional.ipynb index 79eb333..845e19b 100644 --- a/langgraph/docs/persistence-functional.ipynb +++ b/langgraph/docs/persistence-functional.ipynb @@ -198,18 +198,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "22:30:59 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:30:59 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:30:59 redisvl.index.index INFO Index already exists, not overwriting.\n" + "20:20:13 redisvl.index.index INFO Index already exists, not overwriting.\n", + "20:20:13 redisvl.index.index INFO Index already exists, not overwriting.\n", + "20:20:13 redisvl.index.index INFO Index already exists, not overwriting.\n" ] } ], "source": [ "from langchain_core.messages import BaseMessage\n", - "\n", - "from langgraph.checkpoint.redis import RedisSaver\n", - "from langgraph.func import entrypoint, task\n", "from langgraph.graph import add_messages\n", + "from langgraph.func import entrypoint, task\n", + "from langgraph.checkpoint.redis import RedisSaver\n", "\n", "\n", "@task\n", @@ -217,12 +216,10 @@ " response = model.invoke(messages)\n", " return response\n", "\n", - "\n", "checkpointer = None\n", "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n", - " cp.setup()\n", - " checkpointer = cp\n", - "\n", + " cp.setup()\n", + " checkpointer = cp\n", "\n", "@entrypoint(checkpointer=checkpointer)\n", "def workflow(inputs: list[BaseMessage], *, previous: list[BaseMessage]):\n", @@ -271,7 +268,7 @@ "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "Hi Bob! I'm Claude. How can I help you today?\n" + "Hi Bob! I'm Claude. Nice to meet you. How can I help you today?\n" ] } ], @@ -302,7 +299,7 @@ "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "I don't know your name as we haven't been introduced. Feel free to tell me your name if you'd like!\n" + "I don't know your name. While I can engage in conversation with you, I don't have access to personal information about users unless they explicitly share it with me during our conversation.\n" ] } ], @@ -332,7 +329,7 @@ "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "I don't know your name. While I can engage in conversation, I don't have access to personal information about users unless they explicitly share it with me during our conversation.\n" + "I don't know your name since we just started chatting. I can't see any previous messages where you might have shared it. Would you like to tell me your name?\n" ] } ], diff --git a/langgraph/docs/persistence_redis.ipynb b/langgraph/docs/persistence_redis.ipynb index 2393d65..e89bba1 100644 --- a/langgraph/docs/persistence_redis.ipynb +++ b/langgraph/docs/persistence_redis.ipynb @@ -251,10 +251,10 @@ { "data": { "text/plain": [ - "{'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='9dd88740-f964-44ce-a42c-1fe3a518822d'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zF5Vape4mCfkIzvhE26maM6H', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-c37be841-bb51-497f-b2bf-2c6f5e69bb10-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_zF5Vape4mCfkIzvhE26maM6H', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", - " ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='e2fe1324-04b6-445c-b5e2-e8065b43896f', tool_call_id='call_zF5Vape4mCfkIzvhE26maM6H'),\n", - " AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-e0a342e3-e8f5-4f61-bc36-92ca6ea22cf2-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}" + "{'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='92a57aaf-0fd8-4e4c-aaee-715cd88c78cf'),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_6Rqi6L0zJqGsAHHEMeAhrehi', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-457f356c-81b0-4da3-91da-b8f5b6a37f61-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_6Rqi6L0zJqGsAHHEMeAhrehi', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", + " ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b1f9012d-a25c-436b-a5dc-18a444307c3c', tool_call_id='call_6Rqi6L0zJqGsAHHEMeAhrehi'),\n", + " AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-c58776f4-902b-42d8-bd63-b39b47757446-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}" ] }, "execution_count": 7, @@ -350,11 +350,11 @@ { "data": { "text/plain": [ - "[CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2355-618c-bfff-890a4384da60'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:25.752044+00:00', 'id': '1efe4d9d-2355-618c-bfff-890a4384da60', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'thread_id': '2', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('72f5084f-8889-55ee-0461-248207a14686', 'messages', [['human', \"what's the weather in sf\"]]), ('72f5084f-8889-55ee-0461-248207a14686', 'start:agent', '__start__')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2358-666d-8000-16712d7311c5'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:25.753395+00:00', 'id': '1efe4d9d-2358-666d-8000-16712d7311c5', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000002.0.34126320575230196', 'start:agent': '00000000000000000000000000000002.0.7900681980782398'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '2', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('41142cee-c7b4-d363-7b8a-e8098b1e972a', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('41142cee-c7b4-d363-7b8a-e8098b1e972a', 'agent', 'agent'), ('41142cee-c7b4-d363-7b8a-e8098b1e972a', 'branch:agent:should_continue:tools', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2a87-6823-8001-dffe02b92767'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:26.506671+00:00', 'id': '1efe4d9d-2a87-6823-8001-dffe02b92767', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000003.0.36280482926876056', 'start:agent': '00000000000000000000000000000003.0.17279995818914917', 'agent': '00000000000000000000000000000003.0.0776862858779197', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.9492534277475211'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.7900681980782398'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-04e83170-5884-4032-8120-122ac8597c43-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('5d5c810c-e9b2-0a45-f41a-fe346c69326c', 'messages', [ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='c5c1a501-ba2b-49f3-b801-2310c35b568e', tool_call_id='call_LERzljH0jlW5LDxlfNZRlyi5')]), ('5d5c810c-e9b2-0a45-f41a-fe346c69326c', 'tools', 'tools')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2a93-6fa8-8002-8bde4a0dc85d'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:26.511781+00:00', 'id': '1efe4d9d-2a93-6fa8-8002-8bde4a0dc85d', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='c5c1a501-ba2b-49f3-b801-2310c35b568e', tool_call_id='call_LERzljH0jlW5LDxlfNZRlyi5')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000004.0.278390079000582', 'start:agent': '00000000000000000000000000000003.0.17279995818914917', 'agent': '00000000000000000000000000000004.0.4200595272263641', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.13362364513036484', 'tools': '00000000000000000000000000000004.0.5902608274110829'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.7900681980782398'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.9492534277475211'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': \"It's always sunny in sf\", 'type': 'tool', 'name': 'get_weather', 'id': 'c5c1a501-ba2b-49f3-b801-2310c35b568e', 'tool_call_id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'status': 'success'}}]}}, 'thread_id': '2', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('c64c7afe-6313-ff83-6c00-726309d72083', 'messages', [AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-519f4b4b-57e7-42e4-a547-a39aaca78f53-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('c64c7afe-6313-ff83-6c00-726309d72083', 'agent', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-32bf-6211-8003-0dc8dd051cf0'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:27.368306+00:00', 'id': '1efe4d9d-32bf-6211-8003-0dc8dd051cf0', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='c5c1a501-ba2b-49f3-b801-2310c35b568e', tool_call_id='call_LERzljH0jlW5LDxlfNZRlyi5'), AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-519f4b4b-57e7-42e4-a547-a39aaca78f53-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000005.0.44349618328487184', 'start:agent': '00000000000000000000000000000003.0.17279995818914917', 'agent': '00000000000000000000000000000005.0.40977766417907246', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.13362364513036484', 'tools': '00000000000000000000000000000005.0.925545529266102'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.7900681980782398', 'tools': '00000000000000000000000000000004.0.5902608274110829'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.9492534277475211'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in San Francisco is always sunny!', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-519f4b4b-57e7-42e4-a547-a39aaca78f53-0', 'usage_metadata': {'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" + "[CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f3-f419-68a8-bfff-79467feba228'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:53.600619+00:00', 'id': '1efe32f3-f419-68a8-bfff-79467feba228', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'thread_id': '2', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('8f177dde-16a5-c002-12f8-43d52de3a2bc', 'messages', [['human', \"what's the weather in sf\"]]), ('8f177dde-16a5-c002-12f8-43d52de3a2bc', 'start:agent', '__start__')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f3-f41c-6d74-8000-66a2bcf9ab77'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:53.601972+00:00', 'id': '1efe32f3-f41c-6d74-8000-66a2bcf9ab77', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000002.0.7493166419141271', 'start:agent': '00000000000000000000000000000002.0.8843759558054323'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '2', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('d4d0b575-819e-f756-8aa7-03279d130389', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('d4d0b575-819e-f756-8aa7-03279d130389', 'agent', 'agent'), ('d4d0b575-819e-f756-8aa7-03279d130389', 'branch:agent:should_continue:tools', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0275-6012-8001-8d7bae22de83'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:55.106065+00:00', 'id': '1efe32f4-0275-6012-8001-8d7bae22de83', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000003.0.09741697468460842', 'start:agent': '00000000000000000000000000000003.0.7046308014058774', 'agent': '00000000000000000000000000000003.0.6066491420702625', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.08120128452475128'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.8843759558054323'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('92da0b25-eede-948a-645b-5e8067b93901', 'messages', [ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b61c2ced-08b3-411f-925a-7e39f5ac0c7b', tool_call_id='call_92HJ2MgBpFcA9qC3BkwVC2Bn')]), ('92da0b25-eede-948a-645b-5e8067b93901', 'tools', 'tools')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-027e-65e4-8002-0ef3b524f70c'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:55.109907+00:00', 'id': '1efe32f4-027e-65e4-8002-0ef3b524f70c', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b61c2ced-08b3-411f-925a-7e39f5ac0c7b', tool_call_id='call_92HJ2MgBpFcA9qC3BkwVC2Bn')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000004.0.6137320046825878', 'start:agent': '00000000000000000000000000000003.0.7046308014058774', 'agent': '00000000000000000000000000000004.0.2539561237928193', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.7580903550958455', 'tools': '00000000000000000000000000000004.0.9881719162793727'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.8843759558054323'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.08120128452475128'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': \"It's always sunny in sf\", 'type': 'tool', 'name': 'get_weather', 'id': 'b61c2ced-08b3-411f-925a-7e39f5ac0c7b', 'tool_call_id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'status': 'success'}}]}}, 'thread_id': '2', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('29cbcd91-1c09-4c89-43a9-b745265b5519', 'messages', [AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-45026796-3c16-4d7a-b43d-6afb869f7033-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('29cbcd91-1c09-4c89-43a9-b745265b5519', 'agent', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0b6a-61d8-8003-3550479d0f95'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:56.045243+00:00', 'id': '1efe32f4-0b6a-61d8-8003-3550479d0f95', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b61c2ced-08b3-411f-925a-7e39f5ac0c7b', tool_call_id='call_92HJ2MgBpFcA9qC3BkwVC2Bn'), AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-45026796-3c16-4d7a-b43d-6afb869f7033-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000005.0.5938877096913094', 'start:agent': '00000000000000000000000000000003.0.7046308014058774', 'agent': '00000000000000000000000000000005.0.49774395583702613', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.7580903550958455', 'tools': '00000000000000000000000000000005.0.1076149781750626'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.8843759558054323', 'tools': '00000000000000000000000000000004.0.9881719162793727'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.08120128452475128'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in San Francisco is always sunny!', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-45026796-3c16-4d7a-b43d-6afb869f7033-0', 'usage_metadata': {'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" ] }, "execution_count": 11, @@ -389,9 +389,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "22:29:27 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:29:27 redisvl.index.index INFO Index already exists, not overwriting.\n", - "22:29:27 redisvl.index.index INFO Index already exists, not overwriting.\n" + "19:35:56 redisvl.index.index INFO Index already exists, not overwriting.\n", + "19:35:56 redisvl.index.index INFO Index already exists, not overwriting.\n", + "19:35:56 redisvl.index.index INFO Index already exists, not overwriting.\n" ] } ], @@ -424,24 +424,24 @@ "text/plain": [ "{'type': 'json',\n", " 'v': 1,\n", - " 'ts': '2025-02-06T22:29:28.536731+00:00',\n", - " 'id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2',\n", - " 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", - " ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX'),\n", - " AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})],\n", + " 'ts': '2025-02-04T19:35:58.617303+00:00',\n", + " 'id': '1efe32f4-23f1-658d-8003-bbde492186f5',\n", + " 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", + " ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1'),\n", + " AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})],\n", " 'agent': 'agent'},\n", - " 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668',\n", - " 'messages': '00000000000000000000000000000005.0.0414236748232204',\n", - " 'start:agent': '00000000000000000000000000000003.0.5560335482828312',\n", - " 'agent': '00000000000000000000000000000005.0.38859013600061787',\n", - " 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668',\n", - " 'tools': '00000000000000000000000000000005.0.49822831701234793'},\n", + " 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528',\n", + " 'messages': '00000000000000000000000000000005.0.8450874109853694',\n", + " 'start:agent': '00000000000000000000000000000003.0.5115764358196082',\n", + " 'agent': '00000000000000000000000000000005.0.7750039566988334',\n", + " 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992',\n", + " 'tools': '00000000000000000000000000000005.0.22290195520047118'},\n", " 'versions_seen': {'__input__': {},\n", - " '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'},\n", - " 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563',\n", - " 'tools': '00000000000000000000000000000004.0.19215578470836003'},\n", - " 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}},\n", + " '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'},\n", + " 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759',\n", + " 'tools': '00000000000000000000000000000004.0.42098070579693636'},\n", + " 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}},\n", " 'pending_sends': []}" ] }, @@ -463,7 +463,7 @@ { "data": { "text/plain": [ - "CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.536731+00:00', 'id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000005.0.0414236748232204', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000005.0.38859013600061787', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668', 'tools': '00000000000000000000000000000005.0.49822831701234793'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563', 'tools': '00000000000000000000000000000004.0.19215578470836003'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-5976ec44-4507-4b9e-9d91-985249465669-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])" + "CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-23f1-658d-8003-bbde492186f5'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:58.617303+00:00', 'id': '1efe32f4-23f1-658d-8003-bbde492186f5', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000005.0.8450874109853694', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000005.0.7750039566988334', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992', 'tools': '00000000000000000000000000000005.0.22290195520047118'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759', 'tools': '00000000000000000000000000000004.0.42098070579693636'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])" ] }, "execution_count": 14, @@ -484,11 +484,11 @@ { "data": { "text/plain": [ - "[CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3332-6824-bfff-fa0fc6d0e969'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:27.415601+00:00', 'id': '1efe4d9d-3332-6824-bfff-fa0fc6d0e969', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'thread_id': '4', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('baebd150-c889-7137-deca-382710680841', 'messages', [['human', \"what's the weather in nyc\"]]), ('baebd150-c889-7137-deca-382710680841', 'start:agent', '__start__')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3334-6e5f-8000-11b02695c906'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:27.416577+00:00', 'id': '1efe4d9d-3334-6e5f-8000-11b02695c906', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000002.0.6668016663099026', 'start:agent': '00000000000000000000000000000002.0.523640257950563'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '4', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('4b0237c2-5822-a4ff-2fb8-c2b12190b329', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('4b0237c2-5822-a4ff-2fb8-c2b12190b329', 'agent', 'agent'), ('4b0237c2-5822-a4ff-2fb8-c2b12190b329', 'branch:agent:should_continue:tools', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3932-6f23-8001-817ed1637728'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.044905+00:00', 'id': '1efe4d9d-3932-6f23-8001-817ed1637728', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000003.0.03456479450370886', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000003.0.9629671714501796', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('dbd75f53-2eec-acbb-4801-80b4a83cd8d4', 'messages', [ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX')]), ('dbd75f53-2eec-acbb-4801-80b4a83cd8d4', 'tools', 'tools')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3942-68d8-8002-baf5f118df7f'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.051302+00:00', 'id': '1efe4d9d-3942-68d8-8002-baf5f118df7f', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000004.0.41418028440508436', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000004.0.9841598038842685', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668', 'tools': '00000000000000000000000000000004.0.19215578470836003'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': 'It might be cloudy in nyc', 'type': 'tool', 'name': 'get_weather', 'id': '9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', 'tool_call_id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'status': 'success'}}]}}, 'thread_id': '4', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('e7c46201-fe1e-60e3-1f2b-7bfac640f6ad', 'messages', [AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('e7c46201-fe1e-60e3-1f2b-7bfac640f6ad', 'agent', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.536731+00:00', 'id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000005.0.0414236748232204', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000005.0.38859013600061787', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668', 'tools': '00000000000000000000000000000005.0.49822831701234793'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563', 'tools': '00000000000000000000000000000004.0.19215578470836003'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-5976ec44-4507-4b9e-9d91-985249465669-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" + "[CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0bd7-6a10-bfff-ee8bce9ebead'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:56.090210+00:00', 'id': '1efe32f4-0bd7-6a10-bfff-ee8bce9ebead', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'thread_id': '4', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('a4588b61-8303-4b21-60cd-06b020bfe3a2', 'messages', [['human', \"what's the weather in nyc\"]]), ('a4588b61-8303-4b21-60cd-06b020bfe3a2', 'start:agent', '__start__')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0bda-61fd-8000-f17f537eb5a2'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:56.091230+00:00', 'id': '1efe32f4-0bda-61fd-8000-f17f537eb5a2', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000002.0.5949594223680105', 'start:agent': '00000000000000000000000000000002.0.07832423191348759'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '4', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('d1d58db3-5b58-0910-7499-3683e66eed74', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('d1d58db3-5b58-0910-7499-3683e66eed74', 'agent', 'agent'), ('d1d58db3-5b58-0910-7499-3683e66eed74', 'branch:agent:should_continue:tools', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-1878-6304-8001-fa73cfa2f9a4'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:57.414248+00:00', 'id': '1efe32f4-1878-6304-8001-fa73cfa2f9a4', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000003.0.5422121822202823', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000003.0.7607176558378208', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('3afdb107-4225-fb3f-62f6-dd6d22f71705', 'messages', [ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1')]), ('3afdb107-4225-fb3f-62f6-dd6d22f71705', 'tools', 'tools')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-1886-6df0-8002-a8a07c39b00b'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:57.420267+00:00', 'id': '1efe32f4-1886-6df0-8002-a8a07c39b00b', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000004.0.50691457736017', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000004.0.2506618682080841', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992', 'tools': '00000000000000000000000000000004.0.42098070579693636'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': 'It might be cloudy in nyc', 'type': 'tool', 'name': 'get_weather', 'id': 'd354213f-ae64-47a0-b56d-56ec0f040877', 'tool_call_id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'status': 'success'}}]}}, 'thread_id': '4', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('6982c293-3f81-d3f2-bb7c-637a179f75d4', 'messages', [AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('6982c293-3f81-d3f2-bb7c-637a179f75d4', 'agent', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-23f1-658d-8003-bbde492186f5'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:58.617303+00:00', 'id': '1efe32f4-23f1-658d-8003-bbde492186f5', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000005.0.8450874109853694', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000005.0.7750039566988334', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992', 'tools': '00000000000000000000000000000005.0.22290195520047118'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759', 'tools': '00000000000000000000000000000004.0.42098070579693636'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" ] }, "execution_count": 15, From 4f71edfb6f697ce0df6b604278c5b3608eed2b83 Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Thu, 6 Feb 2025 12:16:36 -0800 Subject: [PATCH 02/11] fix: Use watches on compare-and-set pipelines --- langgraph/checkpoint/redis/aio.py | 51 +++++++++------------- langgraph/checkpoint/redis/ashallow.py | 60 ++++++++++++-------------- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/langgraph/checkpoint/redis/aio.py b/langgraph/checkpoint/redis/aio.py index 8554645..52ae208 100644 --- a/langgraph/checkpoint/redis/aio.py +++ b/langgraph/checkpoint/redis/aio.py @@ -10,6 +10,7 @@ from typing import Any, List, Optional, Sequence, Tuple, Type, cast from langchain_core.runnables import RunnableConfig +from redis import WatchError from redisvl.index import AsyncSearchIndex from redisvl.query import FilterQuery from redisvl.query.filter import Num, Tag @@ -418,38 +419,28 @@ async def aput_writes( } writes_objects.append(write_obj) - # For each write, check existence and then perform appropriate operation - async with self.checkpoints_index.client.pipeline( - transaction=False - ) as pipeline: - for write_obj in writes_objects: - key = self._make_redis_checkpoint_writes_key( - thread_id, - checkpoint_ns, - checkpoint_id, - task_id, - write_obj["idx"], - ) - - # First check if key exists - key_exists = await self._redis.exists(key) == 1 - - if all(w[0] in WRITES_IDX_MAP for w in writes): - # UPSERT case - only update specific fields - if key_exists: - # Update only channel, type, and blob fields - pipeline.json().set(key, "$.channel", write_obj["channel"]) - pipeline.json().set(key, "$.type", write_obj["type"]) - pipeline.json().set(key, "$.blob", write_obj["blob"]) + upsert_case = all(w[0] in WRITES_IDX_MAP for w in writes) + for write_obj in writes_objects: + key = self._make_redis_checkpoint_writes_key( + thread_id, + checkpoint_ns, + checkpoint_id, + task_id, + write_obj["idx"], + ) + async def tx(pipe, key=key, write_obj=write_obj, upsert_case=upsert_case): + exists = await pipe.exists(key) + if upsert_case: + if exists: + await pipe.json().set(key, "$.channel", write_obj["channel"]) + await pipe.json().set(key, "$.type", write_obj["type"]) + await pipe.json().set(key, "$.blob", write_obj["blob"]) else: - # For new records, set the complete object - pipeline.json().set(key, "$", write_obj) + await pipe.json().set(key, "$", write_obj) else: - # INSERT case - only insert if doesn't exist - if not key_exists: - pipeline.json().set(key, "$", write_obj) - - await pipeline.execute() + if not exists: + await pipe.json().set(key, "$", write_obj) + await self._redis.transaction(tx, key) def put_writes( self, diff --git a/langgraph/checkpoint/redis/ashallow.py b/langgraph/checkpoint/redis/ashallow.py index be25ea8..56deeb6 100644 --- a/langgraph/checkpoint/redis/ashallow.py +++ b/langgraph/checkpoint/redis/ashallow.py @@ -8,6 +8,7 @@ from typing import Any, AsyncIterator, Dict, List, Optional, Sequence, Tuple, cast from langchain_core.runnables import RunnableConfig +from redis import WatchError from redisvl.index import AsyncSearchIndex from redisvl.query import FilterQuery from redisvl.query.filter import Num, Tag @@ -317,9 +318,9 @@ async def aget_tuple(self, config: RunnableConfig) -> Optional[CheckpointTuple]: # Ensure metadata matches CheckpointMetadata type sanitized_metadata = { - k.replace("\u0000", ""): v.replace("\u0000", "") - if isinstance(v, str) - else v + k.replace("\u0000", ""): ( + v.replace("\u0000", "") if isinstance(v, str) else v + ) for k, v in metadata_dict.items() } metadata = cast(CheckpointMetadata, sanitized_metadata) @@ -386,37 +387,32 @@ async def aput_writes( } writes_objects.append(write_obj) - # For each write, check existence and then perform appropriate operation - async with self.checkpoints_index.client.pipeline( - transaction=False - ) as pipeline: - for write_obj in writes_objects: - key = self._make_redis_checkpoint_writes_key( - thread_id, - checkpoint_ns, - checkpoint_id, - task_id, - write_obj["idx"], - ) - - # First check if key exists - key_exists = await self._redis.exists(key) == 1 - - if all(w[0] in WRITES_IDX_MAP for w in writes): - # UPSERT case - only update specific fields - if key_exists: - # Update only channel, type, and blob fields - pipeline.json().set(key, "$.channel", write_obj["channel"]) - pipeline.json().set(key, "$.type", write_obj["type"]) - pipeline.json().set(key, "$.blob", write_obj["blob"]) + upsert_case = all(w[0] in WRITES_IDX_MAP for w in writes) + for write_obj in writes_objects: + key = self._make_redis_checkpoint_writes_key( + thread_id, + checkpoint_ns, + checkpoint_id, + task_id, + write_obj["idx"], + ) + if upsert_case: + async def tx(pipe, key=key, write_obj=write_obj): + exists = await pipe.exists(key) + if exists: + await pipe.json().set( + key, "$.channel", write_obj["channel"] + ) + await pipe.json().set(key, "$.type", write_obj["type"]) + await pipe.json().set(key, "$.blob", write_obj["blob"]) else: - # For new records, set the complete object - pipeline.json().set(key, "$", write_obj) - else: - # INSERT case - only insert if doesn't exist - pipeline.json().set(key, "$", write_obj) + await pipe.json().set(key, "$", write_obj) - await pipeline.execute() + await self._redis.transaction(tx, key) + else: + # Unlike AsyncRedisSaver, the shallow implementation always overwrites + # the full object, so we don't check for key existence here. + await self._redis.json().set(key, "$", write_obj) async def aget_channel_values( self, thread_id: str, checkpoint_ns: str, checkpoint_id: str From 6a24d833aeadc4aaa31a4082c5ac4ed41a50efc8 Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Thu, 6 Feb 2025 14:51:52 -0800 Subject: [PATCH 03/11] fix: Avoid using "transactions" when they are not needed --- langgraph/store/redis/aio.py | 46 ++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/langgraph/store/redis/aio.py b/langgraph/store/redis/aio.py index f578217..7a081f9 100644 --- a/langgraph/store/redis/aio.py +++ b/langgraph/store/redis/aio.py @@ -179,23 +179,12 @@ async def from_conn_string( index: Optional[IndexConfig] = None, ) -> AsyncIterator[AsyncRedisStore]: """Create store from Redis connection string.""" - store = cls(redis_url=conn_string, index=index) - try: + async with cls(redis_url=conn_string, index=index) as store: store._task = store.loop.create_task( store._run_background_tasks(store._aqueue, weakref.ref(store)) ) await store.setup() yield store - finally: - if hasattr(store, "_task"): - store._task.cancel() - try: - await store._task - except asyncio.CancelledError: - pass - if store._owns_client: - await store._redis.aclose() # type: ignore[attr-defined] - await store._redis.connection_pool.disconnect() def create_indexes(self) -> None: """Create async indices.""" @@ -221,8 +210,9 @@ async def __aexit__( except asyncio.CancelledError: pass - # if self._owns_client: - await self._redis.aclose() # type: ignore[attr-defined] + if self._owns_client: + await self._redis.aclose() # type: ignore[attr-defined] + await self._redis.connection_pool.disconnect() async def abatch(self, ops: Iterable[Op]) -> list[Result]: """Execute batch of operations asynchronously.""" @@ -301,11 +291,27 @@ async def _batch_get_ops( ) -> None: """Execute GET operations in batch asynchronously.""" for query, _, namespace, items in self._get_batch_GET_ops_queries(get_ops): - res = await self.store_index.search(Query(query)) - # Parse JSON from each document - key_to_row = { - json.loads(doc.json)["key"]: json.loads(doc.json) for doc in res.docs - } + # Use RedisVL AsyncSearchIndex search + search_query = FilterQuery( + filter_expression=query, + return_fields=["id"], # Just need the document id + num_results=len(items), + ) + res = await self.store_index.search(search_query) + + # Use pipeline to get the actual JSON documents + pipeline = self._redis.pipeline(transaction=False) + doc_ids = [] + for doc in res.docs: + # The id is already in the correct format (store:prefix:key) + pipeline.json().get(doc.id) + doc_ids.append(doc.id) + + json_docs = await pipeline.execute() + + # Convert to dictionary format + key_to_row = {doc["key"]: doc for doc in json_docs if doc} + for idx, key in items: if key in key_to_row: results[idx] = _row_to_item(namespace, key_to_row[key]) @@ -482,7 +488,7 @@ async def _batch_search_ops( ) # Get matching store docs in pipeline - pipeline = self._redis.pipeline() + pipeline = self._redis.pipeline(transaction=False) result_map = {} # Map store key to vector result with distances for doc in vector_results: From d11566c63b29a7022d0b509c25b6b93fd9dcd463 Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Thu, 6 Feb 2025 15:23:45 -0800 Subject: [PATCH 04/11] fix: fix docs --- .../docs/create-react-agent-memory.ipynb | 63 +++++--- langgraph/docs/cross-thread-persistence.ipynb | 143 +++++++++--------- langgraph/docs/persistence-functional.ipynb | 23 +-- langgraph/docs/persistence_redis.ipynb | 68 ++++----- 4 files changed, 159 insertions(+), 138 deletions(-) diff --git a/langgraph/docs/create-react-agent-memory.ipynb b/langgraph/docs/create-react-agent-memory.ipynb index 736d75b..fdd55be 100644 --- a/langgraph/docs/create-react-agent-memory.ipynb +++ b/langgraph/docs/create-react-agent-memory.ipynb @@ -58,7 +58,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 1, "id": "a213e11a-5c62-4ddb-a707-490d91add383", "metadata": {}, "outputs": [], @@ -69,10 +69,18 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 2, "id": "23a1885c-04ab-4750-aefa-105891fddf3e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "OPENAI_API_KEY: ········\n" + ] + } + ], "source": [ "import getpass\n", "import os\n", @@ -109,24 +117,35 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 3, "id": "7a154152-973e-4b5d-aa13-48c617744a4c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "22:31:51 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:31:51 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:31:51 redisvl.index.index INFO Index already exists, not overwriting.\n" + ] + } + ], "source": [ + "from typing import Literal\n", + "\n", + "from langchain_core.tools import tool\n", + "\n", "# First we initialize the model we want to use.\n", "from langchain_openai import ChatOpenAI\n", "\n", + "from langgraph.checkpoint.redis import RedisSaver\n", + "from langgraph.prebuilt import create_react_agent\n", + "\n", "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", "\n", "\n", "# For this tutorial we will use custom tool that returns pre-defined values for weather in two cities (NYC & SF)\n", - "\n", - "from typing import Literal\n", - "\n", - "from langchain_core.tools import tool\n", - "\n", - "\n", "@tool\n", "def get_weather(city: Literal[\"nyc\", \"sf\"]):\n", " \"\"\"Use this to get weather information.\"\"\"\n", @@ -140,19 +159,18 @@ "\n", "tools = [get_weather]\n", "\n", - "# We can add \"chat memory\" to the graph with LangGraph's Redi checkpointer\n", + "# We can add \"chat memory\" to the graph with LangGraph's Redis checkpointer\n", "# to retain the chat context between interactions\n", - "from langgraph.checkpoint.redis import RedisSaver\n", + "\n", "\n", "REDIS_URI = \"redis://redis:6379\"\n", "memory = None\n", "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n", - " cp.setup()\n", - " memory = cp\n", + " cp.setup()\n", + " memory = cp\n", "\n", "# Define the graph\n", "\n", - "from langgraph.prebuilt import create_react_agent\n", "\n", "graph = create_react_agent(model, tools=tools, checkpointer=memory)" ] @@ -169,7 +187,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 4, "id": "16636975-5f2d-4dc7-ab8e-d0bea0830a28", "metadata": {}, "outputs": [], @@ -185,7 +203,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 5, "id": "9ffff6c3-a4f5-47c9-b51d-97caaee85cd6", "metadata": {}, "outputs": [ @@ -198,8 +216,8 @@ "What's the weather in NYC?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " get_weather (call_RHv6T6OBCn7eKOlm5qEpLK4P)\n", - " Call ID: call_RHv6T6OBCn7eKOlm5qEpLK4P\n", + " get_weather (call_Edwfw0WiyIJ7vt9xzU9xvyeg)\n", + " Call ID: call_Edwfw0WiyIJ7vt9xzU9xvyeg\n", " Args:\n", " city: nyc\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -229,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 6, "id": "187479f9-32fa-4611-9487-cf816ba2e147", "metadata": {}, "outputs": [ @@ -242,7 +260,7 @@ "What's it known for?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "Could you please specify what \"it\" refers to? Are you asking about a specific place, person, event, or something else?\n" + "Could you please specify what \"it\" refers to? Are you asking about a specific city, person, event, or something else?\n" ] } ], @@ -274,3 +292,4 @@ "nbformat": 4, "nbformat_minor": 5 } + diff --git a/langgraph/docs/cross-thread-persistence.ipynb b/langgraph/docs/cross-thread-persistence.ipynb index ac9864c..3f7626e 100644 --- a/langgraph/docs/cross-thread-persistence.ipynb +++ b/langgraph/docs/cross-thread-persistence.ipynb @@ -53,18 +53,18 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "3457aadf", "metadata": {}, "outputs": [], "source": [ "%%capture --no-stderr\n", - "%pip install -U langchain_openai langgraph" + "%pip install -U langchain_openai langchain_anthropic langgraph" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "aa2c64a7", "metadata": {}, "outputs": [ @@ -124,23 +124,15 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 3, "id": "a7f303d6-612e-4e34-bf36-29d4ed25d802", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "20:57:29 redisvl.index.index INFO Index already exists, not overwriting.\n", - "20:57:29 redisvl.index.index INFO Index already exists, not overwriting.\n" - ] - } - ], + "outputs": [], "source": [ - "from langgraph.store.redis import RedisStore\n", "from langchain_openai import OpenAIEmbeddings\n", + "\n", "from langgraph.store.base import IndexConfig\n", + "from langgraph.store.redis import RedisStore\n", "\n", "REDIS_URI = \"redis://redis:6379\"\n", "\n", @@ -156,7 +148,7 @@ "redis_store = None\n", "with RedisStore.from_conn_string(REDIS_URI, index=index_config) as s:\n", " s.setup()\n", - " redis_store = s " + " redis_store = s" ] }, { @@ -169,7 +161,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 4, "id": "2a30a362-528c-45ee-9df6-630d2d843588", "metadata": {}, "outputs": [ @@ -177,22 +169,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "20:57:32 redisvl.index.index INFO Index already exists, not overwriting.\n", - "20:57:32 redisvl.index.index INFO Index already exists, not overwriting.\n", - "20:57:32 redisvl.index.index INFO Index already exists, not overwriting.\n" + "22:32:41 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:32:41 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:32:41 redisvl.index.index INFO Index already exists, not overwriting.\n" ] } ], "source": [ "import uuid\n", - "from typing import Annotated\n", - "from typing_extensions import TypedDict\n", "\n", "from langchain_anthropic import ChatAnthropic\n", "from langchain_core.runnables import RunnableConfig\n", - "from langgraph.graph import StateGraph, MessagesState, START\n", - "from langgraph.store.base import BaseStore\n", "\n", + "from langgraph.checkpoint.redis import RedisSaver\n", + "from langgraph.graph import START, MessagesState, StateGraph\n", + "from langgraph.store.base import BaseStore\n", "\n", "model = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\")\n", "\n", @@ -222,13 +213,12 @@ "builder.add_node(\"call_model\", call_model)\n", "builder.add_edge(START, \"call_model\")\n", "\n", - "from langgraph.checkpoint.redis import RedisSaver\n", "\n", "REDIS_URI = \"redis://redis:6379\"\n", "checkpointer = None\n", "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n", - " cp.setup()\n", - " checkpointer = cp\n", + " cp.setup()\n", + " checkpointer = cp\n", "\n", "# NOTE: we're passing the store object here when compiling the graph\n", "graph = builder.compile(checkpointer=checkpointer, store=redis_store)\n", @@ -266,7 +256,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 5, "id": "c871a073-a466-46ad-aafe-2b870831057e", "metadata": {}, "outputs": [ @@ -276,43 +266,10 @@ "text": [ "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", - "Hi! Remember: my name is Bob\n" - ] - }, - { - "ename": "RedisSearchError", - "evalue": "Error while searching: Syntax error at offset 21 near >[", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mResponseError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:640\u001b[0m, in \u001b[0;36mSearchIndex.search\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 639\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 640\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_redis_client\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mft\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mschema\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mindex\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mname\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore\u001b[39;49;00m\n\u001b[1;32m 641\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m 642\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 643\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/commands/search/commands.py:508\u001b[0m, in \u001b[0;36mSearchCommands.search\u001b[0;34m(self, query, query_params)\u001b[0m\n\u001b[1;32m 506\u001b[0m options[NEVER_DECODE] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[0;32m--> 508\u001b[0m res \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mexecute_command\u001b[49m\u001b[43m(\u001b[49m\u001b[43mSEARCH_CMD\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 510\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(res, Pipeline):\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:559\u001b[0m, in \u001b[0;36mRedis.execute_command\u001b[0;34m(self, *args, **options)\u001b[0m\n\u001b[1;32m 558\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mexecute_command\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions):\n\u001b[0;32m--> 559\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execute_command\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:567\u001b[0m, in \u001b[0;36mRedis._execute_command\u001b[0;34m(self, *args, **options)\u001b[0m\n\u001b[1;32m 566\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 567\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mconn\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mretry\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcall_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 568\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_send_command_parse_response\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 569\u001b[0m \u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\n\u001b[1;32m 570\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 571\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_disconnect_raise\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 572\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 573\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/retry.py:62\u001b[0m, in \u001b[0;36mRetry.call_with_retry\u001b[0;34m(self, do, fail)\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 62\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mdo\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 63\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_supported_errors \u001b[38;5;28;01mas\u001b[39;00m error:\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:568\u001b[0m, in \u001b[0;36mRedis._execute_command..\u001b[0;34m()\u001b[0m\n\u001b[1;32m 566\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 567\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m conn\u001b[38;5;241m.\u001b[39mretry\u001b[38;5;241m.\u001b[39mcall_with_retry(\n\u001b[0;32m--> 568\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m: \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_send_command_parse_response\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 569\u001b[0m \u001b[43m \u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\n\u001b[1;32m 570\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 571\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m error: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_disconnect_raise(conn, error),\n\u001b[1;32m 572\u001b[0m )\n\u001b[1;32m 573\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:542\u001b[0m, in \u001b[0;36mRedis._send_command_parse_response\u001b[0;34m(self, conn, command_name, *args, **options)\u001b[0m\n\u001b[1;32m 541\u001b[0m conn\u001b[38;5;241m.\u001b[39msend_command(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions)\n\u001b[0;32m--> 542\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcommand_name\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/client.py:581\u001b[0m, in \u001b[0;36mRedis.parse_response\u001b[0;34m(self, connection, command_name, **options)\u001b[0m\n\u001b[1;32m 580\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m NEVER_DECODE \u001b[38;5;129;01min\u001b[39;00m options:\n\u001b[0;32m--> 581\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mconnection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mread_response\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdisable_decoding\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[1;32m 582\u001b[0m options\u001b[38;5;241m.\u001b[39mpop(NEVER_DECODE)\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redis/connection.py:616\u001b[0m, in \u001b[0;36mAbstractConnection.read_response\u001b[0;34m(self, disable_decoding, disconnect_on_error, push_request)\u001b[0m\n\u001b[1;32m 615\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 616\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m response\n\u001b[1;32m 617\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n", - "\u001b[0;31mResponseError\u001b[0m: Syntax error at offset 21 near >[", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mRedisSearchError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[12], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m config \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconfigurable\u001b[39m\u001b[38;5;124m\"\u001b[39m: {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mthread_id\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m1\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser_id\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m1\u001b[39m\u001b[38;5;124m\"\u001b[39m}}\n\u001b[1;32m 2\u001b[0m input_message \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrole\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHi! Remember: my name is Bob\u001b[39m\u001b[38;5;124m\"\u001b[39m}\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mgraph\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43minput_message\u001b[49m\u001b[43m]\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mvalues\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpretty_print\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:1724\u001b[0m, in \u001b[0;36mPregel.stream\u001b[0;34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)\u001b[0m\n\u001b[1;32m 1718\u001b[0m \u001b[38;5;66;03m# Similarly to Bulk Synchronous Parallel / Pregel model\u001b[39;00m\n\u001b[1;32m 1719\u001b[0m \u001b[38;5;66;03m# computation proceeds in steps, while there are channel updates.\u001b[39;00m\n\u001b[1;32m 1720\u001b[0m \u001b[38;5;66;03m# Channel updates from step N are only visible in step N+1\u001b[39;00m\n\u001b[1;32m 1721\u001b[0m \u001b[38;5;66;03m# channels are guaranteed to be immutable for the duration of the step,\u001b[39;00m\n\u001b[1;32m 1722\u001b[0m \u001b[38;5;66;03m# with channel updates applied only at the transition between steps.\u001b[39;00m\n\u001b[1;32m 1723\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m loop\u001b[38;5;241m.\u001b[39mtick(input_keys\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39minput_channels):\n\u001b[0;32m-> 1724\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m_\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mrunner\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtick\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1725\u001b[0m \u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtasks\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalues\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1726\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstep_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1727\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1728\u001b[0m \u001b[43m \u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mget_waiter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1729\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 1730\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# emit output\u001b[39;49;00m\n\u001b[1;32m 1731\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield from\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43moutput\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1732\u001b[0m \u001b[38;5;66;03m# emit output\u001b[39;00m\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:230\u001b[0m, in \u001b[0;36mPregelRunner.tick\u001b[0;34m(self, tasks, reraise, timeout, retry_policy, get_waiter)\u001b[0m\n\u001b[1;32m 228\u001b[0m t \u001b[38;5;241m=\u001b[39m tasks[\u001b[38;5;241m0\u001b[39m]\n\u001b[1;32m 229\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 230\u001b[0m \u001b[43mrun_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 231\u001b[0m \u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 232\u001b[0m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 233\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfigurable\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m{\u001b[49m\n\u001b[1;32m 234\u001b[0m \u001b[43m \u001b[49m\u001b[43mCONFIG_KEY_SEND\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpartial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mwriter\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 235\u001b[0m \u001b[43m \u001b[49m\u001b[43mCONFIG_KEY_CALL\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mpartial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcall\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 236\u001b[0m \u001b[43m \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 237\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 238\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mcommit(t, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[1;32m 239\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/pregel/retry.py:40\u001b[0m, in \u001b[0;36mrun_with_retry\u001b[0;34m(task, retry_policy, configurable)\u001b[0m\n\u001b[1;32m 38\u001b[0m task\u001b[38;5;241m.\u001b[39mwrites\u001b[38;5;241m.\u001b[39mclear()\n\u001b[1;32m 39\u001b[0m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[0;32m---> 40\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mtask\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mproc\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtask\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 41\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[1;32m 42\u001b[0m ns: \u001b[38;5;28mstr\u001b[39m \u001b[38;5;241m=\u001b[39m config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:495\u001b[0m, in \u001b[0;36mRunnableSeq.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 491\u001b[0m config \u001b[38;5;241m=\u001b[39m patch_config(\n\u001b[1;32m 492\u001b[0m config, callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mseq:step:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;250m \u001b[39m\u001b[38;5;241m+\u001b[39m\u001b[38;5;250m \u001b[39m\u001b[38;5;241m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 493\u001b[0m )\n\u001b[1;32m 494\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m i \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m--> 495\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 496\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 497\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m step\u001b[38;5;241m.\u001b[39minvoke(\u001b[38;5;28minput\u001b[39m, config)\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:259\u001b[0m, in \u001b[0;36mRunnableCallable.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 257\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 258\u001b[0m context\u001b[38;5;241m.\u001b[39mrun(_set_config_context, config)\n\u001b[0;32m--> 259\u001b[0m ret \u001b[38;5;241m=\u001b[39m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 260\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(ret, Runnable) \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrecurse:\n\u001b[1;32m 261\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m ret\u001b[38;5;241m.\u001b[39minvoke(\u001b[38;5;28minput\u001b[39m, config)\n", - "Cell \u001b[0;32mIn[11], line 19\u001b[0m, in \u001b[0;36mcall_model\u001b[0;34m(state, config, store)\u001b[0m\n\u001b[1;32m 17\u001b[0m user_id \u001b[38;5;241m=\u001b[39m config[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mconfigurable\u001b[39m\u001b[38;5;124m\"\u001b[39m][\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser_id\u001b[39m\u001b[38;5;124m\"\u001b[39m]\n\u001b[1;32m 18\u001b[0m namespace \u001b[38;5;241m=\u001b[39m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmemories\u001b[39m\u001b[38;5;124m\"\u001b[39m, user_id)\n\u001b[0;32m---> 19\u001b[0m memories \u001b[38;5;241m=\u001b[39m \u001b[43mstore\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamespace\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mstr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mstate\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mmessages\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcontent\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 20\u001b[0m info \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mjoin([d\u001b[38;5;241m.\u001b[39mvalue[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdata\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;28;01mfor\u001b[39;00m d \u001b[38;5;129;01min\u001b[39;00m memories])\n\u001b[1;32m 21\u001b[0m system_msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mYou are a helpful assistant talking to the user. User info: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00minfo\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/langgraph/store/base/__init__.py:709\u001b[0m, in \u001b[0;36mBaseStore.search\u001b[0;34m(self, namespace_prefix, query, filter, limit, offset)\u001b[0m\n\u001b[1;32m 654\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21msearch\u001b[39m(\n\u001b[1;32m 655\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 656\u001b[0m namespace_prefix: \u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mstr\u001b[39m, \u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m\u001b[38;5;241m.\u001b[39m],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 662\u001b[0m offset: \u001b[38;5;28mint\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m,\n\u001b[1;32m 663\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mlist\u001b[39m[SearchItem]:\n\u001b[1;32m 664\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Search for items within a namespace prefix.\u001b[39;00m\n\u001b[1;32m 665\u001b[0m \n\u001b[1;32m 666\u001b[0m \u001b[38;5;124;03m Args:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 707\u001b[0m \u001b[38;5;124;03m and requires proper embedding configuration.\u001b[39;00m\n\u001b[1;32m 708\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 709\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbatch\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43mSearchOp\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnamespace_prefix\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mfilter\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mlimit\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moffset\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m[\u001b[38;5;241m0\u001b[39m]\n", - "File \u001b[0;32m~/workspace/libs/checkpoint-redis/langgraph/store/redis/__init__.py:111\u001b[0m, in \u001b[0;36mRedisStore.batch\u001b[0;34m(self, ops)\u001b[0m\n\u001b[1;32m 108\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_batch_put_ops(cast(\u001b[38;5;28mlist\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, PutOp]], grouped_ops[PutOp]))\n\u001b[1;32m 110\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m SearchOp \u001b[38;5;129;01min\u001b[39;00m grouped_ops:\n\u001b[0;32m--> 111\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_batch_search_ops\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 112\u001b[0m \u001b[43m \u001b[49m\u001b[43mcast\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;28;43mtuple\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mSearchOp\u001b[49m\u001b[43m]\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgrouped_ops\u001b[49m\u001b[43m[\u001b[49m\u001b[43mSearchOp\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mresults\u001b[49m\n\u001b[1;32m 113\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m ListNamespacesOp \u001b[38;5;129;01min\u001b[39;00m grouped_ops:\n\u001b[1;32m 116\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_batch_list_namespaces_ops(\n\u001b[1;32m 117\u001b[0m cast(\n\u001b[1;32m 118\u001b[0m Sequence[\u001b[38;5;28mtuple\u001b[39m[\u001b[38;5;28mint\u001b[39m, ListNamespacesOp]],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 121\u001b[0m results,\n\u001b[1;32m 122\u001b[0m )\n", - "File \u001b[0;32m~/workspace/libs/checkpoint-redis/langgraph/store/redis/__init__.py:279\u001b[0m, in \u001b[0;36mRedisStore._batch_search_ops\u001b[0;34m(self, search_ops, results)\u001b[0m\n\u001b[1;32m 276\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m op\u001b[38;5;241m.\u001b[39mquery \u001b[38;5;129;01mand\u001b[39;00m idx \u001b[38;5;129;01min\u001b[39;00m query_vectors:\n\u001b[1;32m 277\u001b[0m \u001b[38;5;66;03m# Vector similarity search\u001b[39;00m\n\u001b[1;32m 278\u001b[0m vector \u001b[38;5;241m=\u001b[39m query_vectors[idx]\n\u001b[0;32m--> 279\u001b[0m vector_results \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvector_index\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquery\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 280\u001b[0m \u001b[43m \u001b[49m\u001b[43mVectorQuery\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 281\u001b[0m \u001b[43m \u001b[49m\u001b[43mvector\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mvector\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtolist\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mhasattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mvector\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtolist\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mvector\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 282\u001b[0m \u001b[43m \u001b[49m\u001b[43mvector_field_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43membedding\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 283\u001b[0m \u001b[43m \u001b[49m\u001b[43mfilter_expression\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m@prefix:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43m_namespace_to_text\u001b[49m\u001b[43m(\u001b[49m\u001b[43mop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mnamespace_prefix\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m*\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 284\u001b[0m \u001b[43m \u001b[49m\u001b[43mreturn_fields\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mprefix\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mkey\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mvector_distance\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 285\u001b[0m \u001b[43m \u001b[49m\u001b[43mnum_results\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mop\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlimit\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 286\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 287\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 289\u001b[0m \u001b[38;5;66;03m# Get matching store docs in pipeline\u001b[39;00m\n\u001b[1;32m 290\u001b[0m pipe \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_redis\u001b[38;5;241m.\u001b[39mpipeline()\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:678\u001b[0m, in \u001b[0;36mSearchIndex.query\u001b[0;34m(self, query)\u001b[0m\n\u001b[1;32m 653\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mquery\u001b[39m(\u001b[38;5;28mself\u001b[39m, query: BaseQuery) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m List[Dict[\u001b[38;5;28mstr\u001b[39m, Any]]:\n\u001b[1;32m 654\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Execute a query on the index.\u001b[39;00m\n\u001b[1;32m 655\u001b[0m \n\u001b[1;32m 656\u001b[0m \u001b[38;5;124;03m This method takes a BaseQuery object directly, runs the search, and\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 676\u001b[0m \n\u001b[1;32m 677\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 678\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_query\u001b[49m\u001b[43m(\u001b[49m\u001b[43mquery\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:648\u001b[0m, in \u001b[0;36mSearchIndex._query\u001b[0;34m(self, query)\u001b[0m\n\u001b[1;32m 646\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21m_query\u001b[39m(\u001b[38;5;28mself\u001b[39m, query: BaseQuery) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m List[Dict[\u001b[38;5;28mstr\u001b[39m, Any]]:\n\u001b[1;32m 647\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Execute a query and process results.\"\"\"\u001b[39;00m\n\u001b[0;32m--> 648\u001b[0m results \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msearch\u001b[49m\u001b[43m(\u001b[49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mquery_params\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mquery\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparams\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 649\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m process_results(\n\u001b[1;32m 650\u001b[0m results, query\u001b[38;5;241m=\u001b[39mquery, storage_type\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mschema\u001b[38;5;241m.\u001b[39mindex\u001b[38;5;241m.\u001b[39mstorage_type\n\u001b[1;32m 651\u001b[0m )\n", - "File \u001b[0;32m~/venv/lib/python3.11/site-packages/redisvl/index/index.py:644\u001b[0m, in \u001b[0;36mSearchIndex.search\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 640\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_redis_client\u001b[38;5;241m.\u001b[39mft(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mschema\u001b[38;5;241m.\u001b[39mindex\u001b[38;5;241m.\u001b[39mname)\u001b[38;5;241m.\u001b[39msearch( \u001b[38;5;66;03m# type: ignore\u001b[39;00m\n\u001b[1;32m 641\u001b[0m \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs\n\u001b[1;32m 642\u001b[0m )\n\u001b[1;32m 643\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m--> 644\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m RedisSearchError(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mError while searching: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01me\u001b[39;00m\n", - "\u001b[0;31mRedisSearchError\u001b[0m: Error while searching: Syntax error at offset 21 near >[", - "\u001b[0mDuring task with name 'call_model' and id 'a732d958-d044-82a7-58e4-3eb8bb5f1a5a'" + "Hi! Remember: my name is Bob\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Hello Bob! It's nice to meet you. I'll remember that your name is Bob. How can I assist you today?\n" ] } ], @@ -325,10 +282,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "d862be40-1f8a-4057-81c4-b7bf073dc4c1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "what is my name?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Your name is Bob.\n" + ] + } + ], "source": [ "config = {\"configurable\": {\"thread_id\": \"2\", \"user_id\": \"1\"}}\n", "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n", @@ -346,12 +316,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "76cde493-89cf-4709-a339-207d2b7e9ea7", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'data': 'User name is Bob'}\n" + ] + } + ], "source": [ - "for memory in in_memory_store.search((\"memories\", \"1\")):\n", + "for memory in redis_store.search((\"memories\", \"1\")):\n", " print(memory.value)" ] }, @@ -365,16 +343,37 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "d362350b-d730-48bd-9652-983812fd7811", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", + "\n", + "what is my name?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "I apologize, but I don't have any information about your name. As an AI assistant, I don't have access to personal information about users unless it's explicitly provided in our conversation. If you'd like, you can tell me your name and I'll be happy to use it in our discussion.\n" + ] + } + ], "source": [ "config = {\"configurable\": {\"thread_id\": \"3\", \"user_id\": \"2\"}}\n", "input_message = {\"role\": \"user\", \"content\": \"what is my name?\"}\n", "for chunk in graph.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n", " chunk[\"messages\"][-1].pretty_print()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60787b3c-216e-4f38-b974-ea1d7f9d8642", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/langgraph/docs/persistence-functional.ipynb b/langgraph/docs/persistence-functional.ipynb index 845e19b..79eb333 100644 --- a/langgraph/docs/persistence-functional.ipynb +++ b/langgraph/docs/persistence-functional.ipynb @@ -198,17 +198,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "20:20:13 redisvl.index.index INFO Index already exists, not overwriting.\n", - "20:20:13 redisvl.index.index INFO Index already exists, not overwriting.\n", - "20:20:13 redisvl.index.index INFO Index already exists, not overwriting.\n" + "22:30:59 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:30:59 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:30:59 redisvl.index.index INFO Index already exists, not overwriting.\n" ] } ], "source": [ "from langchain_core.messages import BaseMessage\n", - "from langgraph.graph import add_messages\n", - "from langgraph.func import entrypoint, task\n", + "\n", "from langgraph.checkpoint.redis import RedisSaver\n", + "from langgraph.func import entrypoint, task\n", + "from langgraph.graph import add_messages\n", "\n", "\n", "@task\n", @@ -216,10 +217,12 @@ " response = model.invoke(messages)\n", " return response\n", "\n", + "\n", "checkpointer = None\n", "with RedisSaver.from_conn_string(REDIS_URI) as cp:\n", - " cp.setup()\n", - " checkpointer = cp\n", + " cp.setup()\n", + " checkpointer = cp\n", + "\n", "\n", "@entrypoint(checkpointer=checkpointer)\n", "def workflow(inputs: list[BaseMessage], *, previous: list[BaseMessage]):\n", @@ -268,7 +271,7 @@ "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "Hi Bob! I'm Claude. Nice to meet you. How can I help you today?\n" + "Hi Bob! I'm Claude. How can I help you today?\n" ] } ], @@ -299,7 +302,7 @@ "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "I don't know your name. While I can engage in conversation with you, I don't have access to personal information about users unless they explicitly share it with me during our conversation.\n" + "I don't know your name as we haven't been introduced. Feel free to tell me your name if you'd like!\n" ] } ], @@ -329,7 +332,7 @@ "text": [ "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "I don't know your name since we just started chatting. I can't see any previous messages where you might have shared it. Would you like to tell me your name?\n" + "I don't know your name. While I can engage in conversation, I don't have access to personal information about users unless they explicitly share it with me during our conversation.\n" ] } ], diff --git a/langgraph/docs/persistence_redis.ipynb b/langgraph/docs/persistence_redis.ipynb index e89bba1..2393d65 100644 --- a/langgraph/docs/persistence_redis.ipynb +++ b/langgraph/docs/persistence_redis.ipynb @@ -251,10 +251,10 @@ { "data": { "text/plain": [ - "{'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='92a57aaf-0fd8-4e4c-aaee-715cd88c78cf'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_6Rqi6L0zJqGsAHHEMeAhrehi', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-457f356c-81b0-4da3-91da-b8f5b6a37f61-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_6Rqi6L0zJqGsAHHEMeAhrehi', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", - " ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b1f9012d-a25c-436b-a5dc-18a444307c3c', tool_call_id='call_6Rqi6L0zJqGsAHHEMeAhrehi'),\n", - " AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-c58776f4-902b-42d8-bd63-b39b47757446-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}" + "{'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='9dd88740-f964-44ce-a42c-1fe3a518822d'),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zF5Vape4mCfkIzvhE26maM6H', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-c37be841-bb51-497f-b2bf-2c6f5e69bb10-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_zF5Vape4mCfkIzvhE26maM6H', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", + " ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='e2fe1324-04b6-445c-b5e2-e8065b43896f', tool_call_id='call_zF5Vape4mCfkIzvhE26maM6H'),\n", + " AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-e0a342e3-e8f5-4f61-bc36-92ca6ea22cf2-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]}" ] }, "execution_count": 7, @@ -350,11 +350,11 @@ { "data": { "text/plain": [ - "[CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f3-f419-68a8-bfff-79467feba228'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:53.600619+00:00', 'id': '1efe32f3-f419-68a8-bfff-79467feba228', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'thread_id': '2', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('8f177dde-16a5-c002-12f8-43d52de3a2bc', 'messages', [['human', \"what's the weather in sf\"]]), ('8f177dde-16a5-c002-12f8-43d52de3a2bc', 'start:agent', '__start__')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f3-f41c-6d74-8000-66a2bcf9ab77'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:53.601972+00:00', 'id': '1efe32f3-f41c-6d74-8000-66a2bcf9ab77', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000002.0.7493166419141271', 'start:agent': '00000000000000000000000000000002.0.8843759558054323'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '2', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('d4d0b575-819e-f756-8aa7-03279d130389', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('d4d0b575-819e-f756-8aa7-03279d130389', 'agent', 'agent'), ('d4d0b575-819e-f756-8aa7-03279d130389', 'branch:agent:should_continue:tools', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0275-6012-8001-8d7bae22de83'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:55.106065+00:00', 'id': '1efe32f4-0275-6012-8001-8d7bae22de83', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000003.0.09741697468460842', 'start:agent': '00000000000000000000000000000003.0.7046308014058774', 'agent': '00000000000000000000000000000003.0.6066491420702625', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.08120128452475128'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.8843759558054323'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('92da0b25-eede-948a-645b-5e8067b93901', 'messages', [ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b61c2ced-08b3-411f-925a-7e39f5ac0c7b', tool_call_id='call_92HJ2MgBpFcA9qC3BkwVC2Bn')]), ('92da0b25-eede-948a-645b-5e8067b93901', 'tools', 'tools')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-027e-65e4-8002-0ef3b524f70c'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:55.109907+00:00', 'id': '1efe32f4-027e-65e4-8002-0ef3b524f70c', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b61c2ced-08b3-411f-925a-7e39f5ac0c7b', tool_call_id='call_92HJ2MgBpFcA9qC3BkwVC2Bn')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000004.0.6137320046825878', 'start:agent': '00000000000000000000000000000003.0.7046308014058774', 'agent': '00000000000000000000000000000004.0.2539561237928193', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.7580903550958455', 'tools': '00000000000000000000000000000004.0.9881719162793727'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.8843759558054323'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.08120128452475128'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': \"It's always sunny in sf\", 'type': 'tool', 'name': 'get_weather', 'id': 'b61c2ced-08b3-411f-925a-7e39f5ac0c7b', 'tool_call_id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'status': 'success'}}]}}, 'thread_id': '2', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('29cbcd91-1c09-4c89-43a9-b745265b5519', 'messages', [AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-45026796-3c16-4d7a-b43d-6afb869f7033-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('29cbcd91-1c09-4c89-43a9-b745265b5519', 'agent', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0b6a-61d8-8003-3550479d0f95'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:56.045243+00:00', 'id': '1efe32f4-0b6a-61d8-8003-3550479d0f95', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='0a67656e-01df-42cc-9221-ba0ffef5c2d1'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b41fce22-19c2-4a17-a81f-8f3a36e679aa-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_92HJ2MgBpFcA9qC3BkwVC2Bn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='b61c2ced-08b3-411f-925a-7e39f5ac0c7b', tool_call_id='call_92HJ2MgBpFcA9qC3BkwVC2Bn'), AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-45026796-3c16-4d7a-b43d-6afb869f7033-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.4212068305755119', 'messages': '00000000000000000000000000000005.0.5938877096913094', 'start:agent': '00000000000000000000000000000003.0.7046308014058774', 'agent': '00000000000000000000000000000005.0.49774395583702613', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.7580903550958455', 'tools': '00000000000000000000000000000005.0.1076149781750626'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8132658908381585'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.8843759558054323', 'tools': '00000000000000000000000000000004.0.9881719162793727'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.08120128452475128'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in San Francisco is always sunny!', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-45026796-3c16-4d7a-b43d-6afb869f7033-0', 'usage_metadata': {'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" + "[CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2355-618c-bfff-890a4384da60'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:25.752044+00:00', 'id': '1efe4d9d-2355-618c-bfff-890a4384da60', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in sf\"]]}}, 'thread_id': '2', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('72f5084f-8889-55ee-0461-248207a14686', 'messages', [['human', \"what's the weather in sf\"]]), ('72f5084f-8889-55ee-0461-248207a14686', 'start:agent', '__start__')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2358-666d-8000-16712d7311c5'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:25.753395+00:00', 'id': '1efe4d9d-2358-666d-8000-16712d7311c5', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000002.0.34126320575230196', 'start:agent': '00000000000000000000000000000002.0.7900681980782398'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '2', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('41142cee-c7b4-d363-7b8a-e8098b1e972a', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('41142cee-c7b4-d363-7b8a-e8098b1e972a', 'agent', 'agent'), ('41142cee-c7b4-d363-7b8a-e8098b1e972a', 'branch:agent:should_continue:tools', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2a87-6823-8001-dffe02b92767'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:26.506671+00:00', 'id': '1efe4d9d-2a87-6823-8001-dffe02b92767', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000003.0.36280482926876056', 'start:agent': '00000000000000000000000000000003.0.17279995818914917', 'agent': '00000000000000000000000000000003.0.0776862858779197', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.9492534277475211'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.7900681980782398'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-04e83170-5884-4032-8120-122ac8597c43-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('5d5c810c-e9b2-0a45-f41a-fe346c69326c', 'messages', [ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='c5c1a501-ba2b-49f3-b801-2310c35b568e', tool_call_id='call_LERzljH0jlW5LDxlfNZRlyi5')]), ('5d5c810c-e9b2-0a45-f41a-fe346c69326c', 'tools', 'tools')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-2a93-6fa8-8002-8bde4a0dc85d'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:26.511781+00:00', 'id': '1efe4d9d-2a93-6fa8-8002-8bde4a0dc85d', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='c5c1a501-ba2b-49f3-b801-2310c35b568e', tool_call_id='call_LERzljH0jlW5LDxlfNZRlyi5')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000004.0.278390079000582', 'start:agent': '00000000000000000000000000000003.0.17279995818914917', 'agent': '00000000000000000000000000000004.0.4200595272263641', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.13362364513036484', 'tools': '00000000000000000000000000000004.0.5902608274110829'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.7900681980782398'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.9492534277475211'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': \"It's always sunny in sf\", 'type': 'tool', 'name': 'get_weather', 'id': 'c5c1a501-ba2b-49f3-b801-2310c35b568e', 'tool_call_id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'status': 'success'}}]}}, 'thread_id': '2', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('c64c7afe-6313-ff83-6c00-726309d72083', 'messages', [AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-519f4b4b-57e7-42e4-a547-a39aaca78f53-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('c64c7afe-6313-ff83-6c00-726309d72083', 'agent', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '2', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-32bf-6211-8003-0dc8dd051cf0'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:27.368306+00:00', 'id': '1efe4d9d-32bf-6211-8003-0dc8dd051cf0', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in sf\", additional_kwargs={}, response_metadata={}, id='d46ba81f-45d9-407a-93ff-3ee8f830a1fc'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'function': {'arguments': '{\"city\":\"sf\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens': 57, 'total_tokens': 72, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-04e83170-5884-4032-8120-122ac8597c43-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'sf'}, 'id': 'call_LERzljH0jlW5LDxlfNZRlyi5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 57, 'output_tokens': 15, 'total_tokens': 72, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content=\"It's always sunny in sf\", name='get_weather', id='c5c1a501-ba2b-49f3-b801-2310c35b568e', tool_call_id='call_LERzljH0jlW5LDxlfNZRlyi5'), AIMessage(content='The weather in San Francisco is always sunny!', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-519f4b4b-57e7-42e4-a547-a39aaca78f53-0', usage_metadata={'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6230650089556942', 'messages': '00000000000000000000000000000005.0.44349618328487184', 'start:agent': '00000000000000000000000000000003.0.17279995818914917', 'agent': '00000000000000000000000000000005.0.40977766417907246', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.13362364513036484', 'tools': '00000000000000000000000000000005.0.925545529266102'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8699756920863937'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.7900681980782398', 'tools': '00000000000000000000000000000004.0.5902608274110829'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.9492534277475211'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in San Francisco is always sunny!', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 11, 'prompt_tokens': 84, 'total_tokens': 95, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-519f4b4b-57e7-42e4-a547-a39aaca78f53-0', 'usage_metadata': {'input_tokens': 84, 'output_tokens': 11, 'total_tokens': 95, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '2', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" ] }, "execution_count": 11, @@ -389,9 +389,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "19:35:56 redisvl.index.index INFO Index already exists, not overwriting.\n", - "19:35:56 redisvl.index.index INFO Index already exists, not overwriting.\n", - "19:35:56 redisvl.index.index INFO Index already exists, not overwriting.\n" + "22:29:27 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:29:27 redisvl.index.index INFO Index already exists, not overwriting.\n", + "22:29:27 redisvl.index.index INFO Index already exists, not overwriting.\n" ] } ], @@ -424,24 +424,24 @@ "text/plain": [ "{'type': 'json',\n", " 'v': 1,\n", - " 'ts': '2025-02-04T19:35:58.617303+00:00',\n", - " 'id': '1efe32f4-23f1-658d-8003-bbde492186f5',\n", - " 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", - " ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1'),\n", - " AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})],\n", + " 'ts': '2025-02-06T22:29:28.536731+00:00',\n", + " 'id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2',\n", + " 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),\n", + " ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX'),\n", + " AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})],\n", " 'agent': 'agent'},\n", - " 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528',\n", - " 'messages': '00000000000000000000000000000005.0.8450874109853694',\n", - " 'start:agent': '00000000000000000000000000000003.0.5115764358196082',\n", - " 'agent': '00000000000000000000000000000005.0.7750039566988334',\n", - " 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992',\n", - " 'tools': '00000000000000000000000000000005.0.22290195520047118'},\n", + " 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668',\n", + " 'messages': '00000000000000000000000000000005.0.0414236748232204',\n", + " 'start:agent': '00000000000000000000000000000003.0.5560335482828312',\n", + " 'agent': '00000000000000000000000000000005.0.38859013600061787',\n", + " 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668',\n", + " 'tools': '00000000000000000000000000000005.0.49822831701234793'},\n", " 'versions_seen': {'__input__': {},\n", - " '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'},\n", - " 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759',\n", - " 'tools': '00000000000000000000000000000004.0.42098070579693636'},\n", - " 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}},\n", + " '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'},\n", + " 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563',\n", + " 'tools': '00000000000000000000000000000004.0.19215578470836003'},\n", + " 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}},\n", " 'pending_sends': []}" ] }, @@ -463,7 +463,7 @@ { "data": { "text/plain": [ - "CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-23f1-658d-8003-bbde492186f5'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:58.617303+00:00', 'id': '1efe32f4-23f1-658d-8003-bbde492186f5', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000005.0.8450874109853694', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000005.0.7750039566988334', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992', 'tools': '00000000000000000000000000000005.0.22290195520047118'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759', 'tools': '00000000000000000000000000000004.0.42098070579693636'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])" + "CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.536731+00:00', 'id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000005.0.0414236748232204', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000005.0.38859013600061787', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668', 'tools': '00000000000000000000000000000005.0.49822831701234793'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563', 'tools': '00000000000000000000000000000004.0.19215578470836003'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-5976ec44-4507-4b9e-9d91-985249465669-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])" ] }, "execution_count": 14, @@ -484,11 +484,11 @@ { "data": { "text/plain": [ - "[CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0bd7-6a10-bfff-ee8bce9ebead'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:56.090210+00:00', 'id': '1efe32f4-0bd7-6a10-bfff-ee8bce9ebead', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'thread_id': '4', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('a4588b61-8303-4b21-60cd-06b020bfe3a2', 'messages', [['human', \"what's the weather in nyc\"]]), ('a4588b61-8303-4b21-60cd-06b020bfe3a2', 'start:agent', '__start__')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-0bda-61fd-8000-f17f537eb5a2'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:56.091230+00:00', 'id': '1efe32f4-0bda-61fd-8000-f17f537eb5a2', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000002.0.5949594223680105', 'start:agent': '00000000000000000000000000000002.0.07832423191348759'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '4', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('d1d58db3-5b58-0910-7499-3683e66eed74', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('d1d58db3-5b58-0910-7499-3683e66eed74', 'agent', 'agent'), ('d1d58db3-5b58-0910-7499-3683e66eed74', 'branch:agent:should_continue:tools', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-1878-6304-8001-fa73cfa2f9a4'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:57.414248+00:00', 'id': '1efe32f4-1878-6304-8001-fa73cfa2f9a4', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000003.0.5422121822202823', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000003.0.7607176558378208', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('3afdb107-4225-fb3f-62f6-dd6d22f71705', 'messages', [ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1')]), ('3afdb107-4225-fb3f-62f6-dd6d22f71705', 'tools', 'tools')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-1886-6df0-8002-a8a07c39b00b'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:57.420267+00:00', 'id': '1efe32f4-1886-6df0-8002-a8a07c39b00b', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000004.0.50691457736017', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000004.0.2506618682080841', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992', 'tools': '00000000000000000000000000000004.0.42098070579693636'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': 'It might be cloudy in nyc', 'type': 'tool', 'name': 'get_weather', 'id': 'd354213f-ae64-47a0-b56d-56ec0f040877', 'tool_call_id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'status': 'success'}}]}}, 'thread_id': '4', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('6982c293-3f81-d3f2-bb7c-637a179f75d4', 'messages', [AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('6982c293-3f81-d3f2-bb7c-637a179f75d4', 'agent', 'agent')]),\n", - " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe32f4-23f1-658d-8003-bbde492186f5'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-04T19:35:58.617303+00:00', 'id': '1efe32f4-23f1-658d-8003-bbde492186f5', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='16f5a776-d246-41fc-820a-20827c034a71'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-dc9a2b30-4888-4e30-8522-0326d1d77032-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_zlRT5aioEXiJzTBup8d38ky1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='d354213f-ae64-47a0-b56d-56ec0f040877', tool_call_id='call_zlRT5aioEXiJzTBup8d38ky1'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, id='run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.571659546194528', 'messages': '00000000000000000000000000000005.0.8450874109853694', 'start:agent': '00000000000000000000000000000003.0.5115764358196082', 'agent': '00000000000000000000000000000005.0.7750039566988334', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.6006542935710992', 'tools': '00000000000000000000000000000005.0.22290195520047118'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.485332083238627'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.07832423191348759', 'tools': '00000000000000000000000000000004.0.42098070579693636'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.043533215571942896'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_bd83329f63', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-82897287-9c5e-4557-8c3a-00eafcf896e1-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" + "[CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3332-6824-bfff-fa0fc6d0e969'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:27.415601+00:00', 'id': '1efe4d9d-3332-6824-bfff-fa0fc6d0e969', 'channel_values': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'channel_versions': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'versions_seen': {'__input__': {}}, 'pending_sends': []}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [['human', \"what's the weather in nyc\"]]}}, 'thread_id': '4', 'step': -1, 'parents': {}}, parent_config=None, pending_writes=[('baebd150-c889-7137-deca-382710680841', 'messages', [['human', \"what's the weather in nyc\"]]), ('baebd150-c889-7137-deca-382710680841', 'start:agent', '__start__')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3334-6e5f-8000-11b02695c906'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:27.416577+00:00', 'id': '1efe4d9d-3334-6e5f-8000-11b02695c906', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5')], 'start:agent': '__start__'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000002.0.6668016663099026', 'start:agent': '00000000000000000000000000000002.0.523640257950563'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': None, 'thread_id': '4', 'step': 0, 'parents': {}}, parent_config=None, pending_writes=[('4b0237c2-5822-a4ff-2fb8-c2b12190b329', 'messages', [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('4b0237c2-5822-a4ff-2fb8-c2b12190b329', 'agent', 'agent'), ('4b0237c2-5822-a4ff-2fb8-c2b12190b329', 'branch:agent:should_continue:tools', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3932-6f23-8001-817ed1637728'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.044905+00:00', 'id': '1efe4d9d-3932-6f23-8001-817ed1637728', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent', 'branch:agent:should_continue:tools': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000003.0.03456479450370886', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000003.0.9629671714501796', 'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': '', 'additional_kwargs': {'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, 'type': 'ai', 'id': 'run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', 'tool_calls': [{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], 'usage_metadata': {'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 1, 'parents': {}}, parent_config=None, pending_writes=[('dbd75f53-2eec-acbb-4801-80b4a83cd8d4', 'messages', [ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX')]), ('dbd75f53-2eec-acbb-4801-80b4a83cd8d4', 'tools', 'tools')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3942-68d8-8002-baf5f118df7f'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.051302+00:00', 'id': '1efe4d9d-3942-68d8-8002-baf5f118df7f', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX')], 'tools': 'tools'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000004.0.41418028440508436', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000004.0.9841598038842685', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668', 'tools': '00000000000000000000000000000004.0.19215578470836003'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'tools': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'ToolMessage'], 'kwargs': {'content': 'It might be cloudy in nyc', 'type': 'tool', 'name': 'get_weather', 'id': '9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', 'tool_call_id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'status': 'success'}}]}}, 'thread_id': '4', 'step': 2, 'parents': {}}, parent_config=None, pending_writes=[('e7c46201-fe1e-60e3-1f2b-7bfac640f6ad', 'messages', [AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]), ('e7c46201-fe1e-60e3-1f2b-7bfac640f6ad', 'agent', 'agent')]),\n", + " CheckpointTuple(config={'configurable': {'thread_id': '4', 'checkpoint_ns': '', 'checkpoint_id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2'}}, checkpoint={'type': 'json', 'v': 1, 'ts': '2025-02-06T22:29:28.536731+00:00', 'id': '1efe4d9d-3de3-6b2d-8003-d65f369ea5d2', 'channel_values': {'messages': [HumanMessage(content=\"what's the weather in nyc\", additional_kwargs={}, response_metadata={}, id='973d79dc-7317-42c1-bf58-8334c3aaf8a5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'function': {'arguments': '{\"city\":\"nyc\"}', 'name': 'get_weather'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 58, 'total_tokens': 74, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-93c27419-bad8-49cc-bdce-ef8442cbd72e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': 'nyc'}, 'id': 'call_D1mD8lkXMIGDiy0MQAD5sxIX', 'type': 'tool_call'}], usage_metadata={'input_tokens': 58, 'output_tokens': 16, 'total_tokens': 74, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}), ToolMessage(content='It might be cloudy in nyc', name='get_weather', id='9c79a83e-bbb7-4a40-8ec2-c4a3e97d2bd5', tool_call_id='call_D1mD8lkXMIGDiy0MQAD5sxIX'), AIMessage(content='The weather in NYC might be cloudy.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, id='run-5976ec44-4507-4b9e-9d91-985249465669-0', usage_metadata={'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})], 'agent': 'agent'}, 'channel_versions': {'__start__': '00000000000000000000000000000002.0.6080359436424668', 'messages': '00000000000000000000000000000005.0.0414236748232204', 'start:agent': '00000000000000000000000000000003.0.5560335482828312', 'agent': '00000000000000000000000000000005.0.38859013600061787', 'branch:agent:should_continue:tools': '00000000000000000000000000000004.0.8896201020861668', 'tools': '00000000000000000000000000000005.0.49822831701234793'}, 'versions_seen': {'__input__': {}, '__start__': {'__start__': '00000000000000000000000000000001.0.8415032714074774'}, 'agent': {'start:agent': '00000000000000000000000000000002.0.523640257950563', 'tools': '00000000000000000000000000000004.0.19215578470836003'}, 'tools': {'branch:agent:should_continue:tools': '00000000000000000000000000000003.0.5594797434351543'}}, 'pending_sends': []}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [{'lc': 1, 'type': 'constructor', 'id': ['langchain', 'schema', 'messages', 'AIMessage'], 'kwargs': {'content': 'The weather in NYC might be cloudy.', 'additional_kwargs': {'refusal': None}, 'response_metadata': {'token_usage': {'completion_tokens': 10, 'prompt_tokens': 88, 'total_tokens': 98, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_72ed7ab54c', 'finish_reason': 'stop', 'logprobs': None}, 'type': 'ai', 'id': 'run-5976ec44-4507-4b9e-9d91-985249465669-0', 'usage_metadata': {'input_tokens': 88, 'output_tokens': 10, 'total_tokens': 98, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}, 'tool_calls': [], 'invalid_tool_calls': []}}]}}, 'thread_id': '4', 'step': 3, 'parents': {}}, parent_config=None, pending_writes=[])]" ] }, "execution_count": 15, From a920fe6f4f3f262b87d2a30747fe816126c34e9e Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Thu, 6 Feb 2025 17:10:30 -0800 Subject: [PATCH 05/11] fix: Pipeline, async fixes --- langgraph/checkpoint/redis/aio.py | 46 +++++++++++++------------- langgraph/checkpoint/redis/ashallow.py | 33 ++++++++++-------- langgraph/store/redis/aio.py | 26 ++++----------- tests/test_async_store.py | 10 +++--- 4 files changed, 53 insertions(+), 62 deletions(-) diff --git a/langgraph/checkpoint/redis/aio.py b/langgraph/checkpoint/redis/aio.py index 52ae208..f12f18a 100644 --- a/langgraph/checkpoint/redis/aio.py +++ b/langgraph/checkpoint/redis/aio.py @@ -10,7 +10,6 @@ from typing import Any, List, Optional, Sequence, Tuple, Type, cast from langchain_core.runnables import RunnableConfig -from redis import WatchError from redisvl.index import AsyncSearchIndex from redisvl.query import FilterQuery from redisvl.query.filter import Num, Tag @@ -74,6 +73,7 @@ def create_indexes(self) -> None: async def __aenter__(self) -> AsyncRedisSaver: """Async context manager enter.""" + await self.asetup() return self async def __aexit__( @@ -83,15 +83,15 @@ async def __aexit__( exc_tb: Optional[TracebackType], ) -> None: """Async context manager exit.""" - # Close client connections - if hasattr(self, "checkpoint_index") and hasattr( - self.checkpoint_index, "client" - ): - await self.checkpoint_index.client.aclose() - if hasattr(self, "channel_index") and hasattr(self.channel_index, "client"): - await self.channel_index.client.aclose() - if hasattr(self, "writes_index") and hasattr(self.writes_index, "client"): - await self.writes_index.client.aclose() + if self._owns_its_client: + await self._redis.aclose() # type: ignore[attr-defined] + await self._redis.connection_pool.disconnect() + + # Prevent RedisVL from attempting to close the client + # on an event loop in a separate thread. + self.checkpoints_index._redis_client = None + self.checkpoint_blobs_index._redis_client = None + self.checkpoint_writes_index._redis_client = None async def asetup(self) -> None: """Initialize Redis indexes asynchronously.""" @@ -428,11 +428,16 @@ async def aput_writes( task_id, write_obj["idx"], ) - async def tx(pipe, key=key, write_obj=write_obj, upsert_case=upsert_case): + + async def tx( + pipe, key=key, write_obj=write_obj, upsert_case=upsert_case + ): exists = await pipe.exists(key) if upsert_case: if exists: - await pipe.json().set(key, "$.channel", write_obj["channel"]) + await pipe.json().set( + key, "$.channel", write_obj["channel"] + ) await pipe.json().set(key, "$.type", write_obj["type"]) await pipe.json().set(key, "$.blob", write_obj["blob"]) else: @@ -440,6 +445,7 @@ async def tx(pipe, key=key, write_obj=write_obj, upsert_case=upsert_case): else: if not exists: await pipe.json().set(key, "$", write_obj) + await self._redis.transaction(tx, key) def put_writes( @@ -533,18 +539,12 @@ async def from_conn_string( redis_client: Optional[AsyncRedis] = None, connection_args: Optional[dict[str, Any]] = None, ) -> AsyncIterator[AsyncRedisSaver]: - saver: Optional[AsyncRedisSaver] = None - try: - saver = cls( - redis_url=redis_url, - redis_client=redis_client, - connection_args=connection_args, - ) + async with cls( + redis_url=redis_url, + redis_client=redis_client, + connection_args=connection_args, + ) as saver: yield saver - finally: - if saver and saver._owns_its_client: # Ensure saver is not None - await saver._redis.aclose() # type: ignore[attr-defined] - await saver._redis.connection_pool.disconnect() async def aget_channel_values( self, thread_id: str, checkpoint_ns: str = "", checkpoint_id: str = "" diff --git a/langgraph/checkpoint/redis/ashallow.py b/langgraph/checkpoint/redis/ashallow.py index 56deeb6..8601f06 100644 --- a/langgraph/checkpoint/redis/ashallow.py +++ b/langgraph/checkpoint/redis/ashallow.py @@ -8,7 +8,6 @@ from typing import Any, AsyncIterator, Dict, List, Optional, Sequence, Tuple, cast from langchain_core.runnables import RunnableConfig -from redis import WatchError from redisvl.index import AsyncSearchIndex from redisvl.query import FilterQuery from redisvl.query.filter import Num, Tag @@ -100,9 +99,22 @@ def __init__( redis_client=redis_client, connection_args=connection_args, ) - # self.lock = asyncio.Lock() self.loop = asyncio.get_running_loop() + async def __aenter__(self) -> AsyncShallowRedisSaver: + return self + + async def __aexit__(self, exc_type, exc, tb) -> None: + if self._owns_its_client: + await self._redis.aclose() # type: ignore[attr-defined] + await self._redis.connection_pool.disconnect() + + # Prevent RedisVL from attempting to close the client + # on an event loop in a separate thread. + self.checkpoints_index._redis_client = None + self.checkpoint_blobs_index._redis_client = None + self.checkpoint_writes_index._redis_client = None + @classmethod @asynccontextmanager async def from_conn_string( @@ -113,18 +125,12 @@ async def from_conn_string( connection_args: Optional[dict[str, Any]] = None, ) -> AsyncIterator[AsyncShallowRedisSaver]: """Create a new AsyncShallowRedisSaver instance.""" - saver: Optional[AsyncShallowRedisSaver] = None - try: - saver = cls( - redis_url=redis_url, - redis_client=redis_client, - connection_args=connection_args, - ) + async with cls( + redis_url=redis_url, + redis_client=redis_client, + connection_args=connection_args, + ) as saver: yield saver - finally: - if saver and saver._owns_its_client: - await saver._redis.aclose() # type: ignore[attr-defined] - await saver._redis.connection_pool.disconnect() async def asetup(self) -> None: """Initialize Redis indexes asynchronously.""" @@ -397,6 +403,7 @@ async def aput_writes( write_obj["idx"], ) if upsert_case: + async def tx(pipe, key=key, write_obj=write_obj): exists = await pipe.exists(key) if exists: diff --git a/langgraph/store/redis/aio.py b/langgraph/store/redis/aio.py index 7a081f9..eb2b96d 100644 --- a/langgraph/store/redis/aio.py +++ b/langgraph/store/redis/aio.py @@ -194,6 +194,7 @@ def create_indexes(self) -> None: async def __aenter__(self) -> AsyncRedisStore: """Async context manager enter.""" + await self.setup() return self async def __aexit__( @@ -291,26 +292,11 @@ async def _batch_get_ops( ) -> None: """Execute GET operations in batch asynchronously.""" for query, _, namespace, items in self._get_batch_GET_ops_queries(get_ops): - # Use RedisVL AsyncSearchIndex search - search_query = FilterQuery( - filter_expression=query, - return_fields=["id"], # Just need the document id - num_results=len(items), - ) - res = await self.store_index.search(search_query) - - # Use pipeline to get the actual JSON documents - pipeline = self._redis.pipeline(transaction=False) - doc_ids = [] - for doc in res.docs: - # The id is already in the correct format (store:prefix:key) - pipeline.json().get(doc.id) - doc_ids.append(doc.id) - - json_docs = await pipeline.execute() - - # Convert to dictionary format - key_to_row = {doc["key"]: doc for doc in json_docs if doc} + res = await self.store_index.search(Query(query)) + # Parse JSON from each document + key_to_row = { + json.loads(doc.json)["key"]: json.loads(doc.json) for doc in res.docs + } for idx, key in items: if key in key_to_row: diff --git a/tests/test_async_store.py b/tests/test_async_store.py index ad266ff..747b2c4 100644 --- a/tests/test_async_store.py +++ b/tests/test_async_store.py @@ -501,8 +501,11 @@ async def test_async_store_with_memory_persistence( "distance_type": "cosine", } - async with AsyncRedisStore.from_conn_string(redis_url, index=index_config) as store: + async with AsyncRedisStore.from_conn_string( + redis_url, index=index_config + ) as store, AsyncRedisSaver.from_conn_string(redis_url) as checkpointer: await store.setup() + await checkpointer.asetup() model = ChatAnthropic(model="claude-3-5-sonnet-20240620") # type: ignore[call-arg] @@ -532,11 +535,6 @@ def call_model( builder.add_node("call_model", call_model) # type:ignore[arg-type] builder.add_edge(START, "call_model") - checkpointer = None - async with AsyncRedisSaver.from_conn_string(redis_url) as cp: - await cp.asetup() - checkpointer = cp - # Compile graph with store and checkpointer graph = builder.compile(checkpointer=checkpointer, store=store) From b55070059798a1f768d6303071a51e594e0f00a0 Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Thu, 6 Feb 2025 17:13:14 -0800 Subject: [PATCH 06/11] fix: Remove setup from __aenter__ --- langgraph/store/redis/aio.py | 1 - 1 file changed, 1 deletion(-) diff --git a/langgraph/store/redis/aio.py b/langgraph/store/redis/aio.py index eb2b96d..fa88e8f 100644 --- a/langgraph/store/redis/aio.py +++ b/langgraph/store/redis/aio.py @@ -194,7 +194,6 @@ def create_indexes(self) -> None: async def __aenter__(self) -> AsyncRedisStore: """Async context manager enter.""" - await self.setup() return self async def __aexit__( From 2fd1f0163e1d31955638695388971f655471847c Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Fri, 7 Feb 2025 10:46:52 -0800 Subject: [PATCH 07/11] chore: Add github action workflows --- .github/workflows/lint.yml | 47 ++++++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..d8d7b54 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,47 @@ + +name: Lint + +on: + pull_request: + push: + branches: + - main + +env: + POETRY_VERSION: "1.8.3" + +jobs: + check: + name: Style-check ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + matrix: + # Only lint on the min and max supported Python versions. + # It's extremely unlikely that there's a lint issue on any version in between + # that doesn't show up on the min or max versions. + # + # GitHub rate-limits how many jobs can be running at any one time. + # Starting new jobs is also relatively slow, + # so linting on fewer versions makes CI faster. + python-version: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: ${{ env.POETRY_VERSION }} + - name: Install dependencies + run: | + poetry install --all-extras + - name: run lint + run: | + make lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4bbfc57 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,47 @@ + +name: Lint + +on: + pull_request: + push: + branches: + - main + +env: + POETRY_VERSION: "1.8.3" + +jobs: + check: + name: Style-check ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + matrix: + # Only lint on the min and max supported Python versions. + # It's extremely unlikely that there's a lint issue on any version in between + # that doesn't show up on the min or max versions. + # + # GitHub rate-limits how many jobs can be running at any one time. + # Starting new jobs is also relatively slow, + # so linting on fewer versions makes CI faster. + python-version: + - "3.9" + - "3.10" + - "3.11" + - "3.12" + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + version: ${{ env.POETRY_VERSION }} + - name: Install dependencies + run: | + poetry install --all-extras + - name: runt tests + run: | + make test From c4c5ff694d5eb1e0a70a683b43ea609abe26c35c Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Fri, 7 Feb 2025 10:47:15 -0800 Subject: [PATCH 08/11] fix: Fix types and transaction usage --- langgraph/checkpoint/redis/aio.py | 42 +++++++++++++++----------- langgraph/checkpoint/redis/ashallow.py | 36 +++++++++++++--------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/langgraph/checkpoint/redis/aio.py b/langgraph/checkpoint/redis/aio.py index f12f18a..03481d1 100644 --- a/langgraph/checkpoint/redis/aio.py +++ b/langgraph/checkpoint/redis/aio.py @@ -6,6 +6,7 @@ import json from collections.abc import AsyncIterator from contextlib import asynccontextmanager +from functools import partial from types import TracebackType from typing import Any, List, Optional, Sequence, Tuple, Type, cast @@ -27,6 +28,26 @@ from langgraph.checkpoint.redis.base import BaseRedisSaver from langgraph.constants import TASKS from redis.asyncio import Redis as AsyncRedis +from redis.asyncio.client import Pipeline + + +async def _write_obj_tx( + pipe: Pipeline, + key: str, + write_obj: dict[str, Any], + upsert_case: bool, +) -> None: + exists: int = await pipe.exists(key) + if upsert_case: + if exists: + await pipe.json().set(key, "$.channel", write_obj["channel"]) + await pipe.json().set(key, "$.type", write_obj["type"]) + await pipe.json().set(key, "$.blob", write_obj["blob"]) + else: + await pipe.json().set(key, "$", write_obj) + else: + if not exists: + await pipe.json().set(key, "$", write_obj) class AsyncRedisSaver(BaseRedisSaver[AsyncRedis, AsyncSearchIndex]): @@ -428,24 +449,9 @@ async def aput_writes( task_id, write_obj["idx"], ) - - async def tx( - pipe, key=key, write_obj=write_obj, upsert_case=upsert_case - ): - exists = await pipe.exists(key) - if upsert_case: - if exists: - await pipe.json().set( - key, "$.channel", write_obj["channel"] - ) - await pipe.json().set(key, "$.type", write_obj["type"]) - await pipe.json().set(key, "$.blob", write_obj["blob"]) - else: - await pipe.json().set(key, "$", write_obj) - else: - if not exists: - await pipe.json().set(key, "$", write_obj) - + tx = partial( + _write_obj_tx, key=key, write_obj=write_obj, upsert_case=upsert_case + ) await self._redis.transaction(tx, key) def put_writes( diff --git a/langgraph/checkpoint/redis/ashallow.py b/langgraph/checkpoint/redis/ashallow.py index 8601f06..16ff6c6 100644 --- a/langgraph/checkpoint/redis/ashallow.py +++ b/langgraph/checkpoint/redis/ashallow.py @@ -5,7 +5,9 @@ import asyncio import json from contextlib import asynccontextmanager -from typing import Any, AsyncIterator, Dict, List, Optional, Sequence, Tuple, cast +from functools import partial +from types import TracebackType +from typing import Any, AsyncIterator, Dict, List, Optional, Sequence, Tuple, Type, cast from langchain_core.runnables import RunnableConfig from redisvl.index import AsyncSearchIndex @@ -30,6 +32,7 @@ ) from langgraph.constants import TASKS from redis.asyncio import Redis as AsyncRedis +from redis.asyncio.client import Pipeline SCHEMAS = [ { @@ -77,6 +80,17 @@ ] +# func: Callable[["Pipeline"], Union[Any, Awaitable[Any]]], +async def _write_obj_tx(pipe: Pipeline, key: str, write_obj: dict[str, Any]) -> None: + exists: int = await pipe.exists(key) + if exists: + await pipe.json().set(key, "$.channel", write_obj["channel"]) + await pipe.json().set(key, "$.type", write_obj["type"]) + await pipe.json().set(key, "$.blob", write_obj["blob"]) + else: + await pipe.json().set(key, "$", write_obj) + + class AsyncShallowRedisSaver(BaseRedisSaver[AsyncRedis, AsyncSearchIndex]): """Async Redis implementation that only stores the most recent checkpoint.""" @@ -104,7 +118,12 @@ def __init__( async def __aenter__(self) -> AsyncShallowRedisSaver: return self - async def __aexit__(self, exc_type, exc, tb) -> None: + async def __aexit__( + self, + exc_type: Optional[Type[BaseException]], + exc: Optional[BaseException], + tb: Optional[TracebackType], + ) -> None: if self._owns_its_client: await self._redis.aclose() # type: ignore[attr-defined] await self._redis.connection_pool.disconnect() @@ -403,18 +422,7 @@ async def aput_writes( write_obj["idx"], ) if upsert_case: - - async def tx(pipe, key=key, write_obj=write_obj): - exists = await pipe.exists(key) - if exists: - await pipe.json().set( - key, "$.channel", write_obj["channel"] - ) - await pipe.json().set(key, "$.type", write_obj["type"]) - await pipe.json().set(key, "$.blob", write_obj["blob"]) - else: - await pipe.json().set(key, "$", write_obj) - + tx = partial(_write_obj_tx, key=key, write_obj=write_obj) await self._redis.transaction(tx, key) else: # Unlike AsyncRedisSaver, the shallow implementation always overwrites From 1862de6a0529215e611739dd0d21ba97923dc0f6 Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Fri, 7 Feb 2025 12:49:27 -0800 Subject: [PATCH 09/11] chore: Skip API-dependent tests in CI --- .github/workflows/test.yml | 50 +++++++++++++++++++++++--------------- Makefile | 7 ++++-- tests/conftest.py | 23 ++++++++++++++++++ tests/test_async.py | 1 + tests/test_async_store.py | 1 + tests/test_store.py | 1 + tests/test_sync.py | 1 + 7 files changed, 63 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4bbfc57..a71bb72 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,8 +1,8 @@ - -name: Lint +name: Test Suite on: pull_request: + push: branches: - main @@ -11,37 +11,49 @@ env: POETRY_VERSION: "1.8.3" jobs: - check: - name: Style-check ${{ matrix.python-version }} + test: + name: Python ${{ matrix.python-version }} - ${{ matrix.connection }} [redis-stack ${{matrix.redis-stack-version}}] runs-on: ubuntu-latest + strategy: + fail-fast: false matrix: - # Only lint on the min and max supported Python versions. - # It's extremely unlikely that there's a lint issue on any version in between - # that doesn't show up on the min or max versions. - # - # GitHub rate-limits how many jobs can be running at any one time. - # Starting new jobs is also relatively slow, - # so linting on fewer versions makes CI faster. - python-version: - - "3.9" - - "3.10" - - "3.11" - - "3.12" + python-version: [3.9, '3.10', 3.11, 3.12] + connection: ['hiredis', 'plain'] + redis-stack-version: ['6.2.6-v9', 'latest', 'edge'] + + services: + redis: + image: redis/redis-stack-server:${{matrix.redis-stack-version}} + ports: + - 6379:6379 steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: Install Poetry uses: snok/install-poetry@v1 with: version: ${{ env.POETRY_VERSION }} + - name: Install dependencies run: | poetry install --all-extras - - name: runt tests + + - name: Install hiredis if needed + if: matrix.connection == 'hiredis' + run: | + poetry add hiredis + + - name: Set Redis version + run: | + echo "REDIS_VERSION=${{ matrix.redis-stack-version }}" >> $GITHUB_ENV + + - name: Run tests run: | - make test + make ci_test diff --git a/Makefile b/Makefile index 87a5921..69d1a8e 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,13 @@ ###################### test: - poetry run pytest tests + poetry run pytest tests --run-api-tests test_watch: poetry run ptw . + +ci_test: + poetry run pytest tests ###################### # LINTING AND FORMATTING @@ -32,4 +35,4 @@ lint lint_diff lint_package lint_tests: format format_diff: poetry run ruff format $(PYTHON_FILES) - poetry run ruff check --select I --fix $(PYTHON_FILES) \ No newline at end of file + poetry run ruff check --select I --fix $(PYTHON_FILES) diff --git a/tests/conftest.py b/tests/conftest.py index 1546898..ed08388 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -48,3 +48,26 @@ async def clear_redis(redis_url: str) -> None: client = Redis.from_url(redis_url) await client.flushall() await client.aclose() # type: ignore[attr-defined] + + +def pytest_addoption(parser): + parser.addoption( + "--run-api-tests", + action="store_true", + default=False, + help="Run tests that require API keys" + ) + +def pytest_configure(config): + config.addinivalue_line( + "markers", + "requires_api_keys: mark test as requiring API keys" + ) + +def pytest_collection_modifyitems(config, items): + if config.getoption("--run-api-tests"): + return + skip_api = pytest.mark.skip(reason="Skipping test because API keys are not provided. Use --run-api-tests to run these tests.") + for item in items: + if item.get_closest_marker("requires_api_keys"): + item.add_marker(skip_api) diff --git a/tests/test_async.py b/tests/test_async.py index 500273d..ed535fa 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -539,6 +539,7 @@ def model() -> ChatOpenAI: return ChatOpenAI(model="gpt-4-turbo-preview", temperature=0) +@pytest.mark.requires_api_keys @pytest.mark.asyncio async def test_async_redis_checkpointer( redis_url: str, tools: List[BaseTool], model: ChatOpenAI diff --git a/tests/test_async_store.py b/tests/test_async_store.py index 747b2c4..0168e3a 100644 --- a/tests/test_async_store.py +++ b/tests/test_async_store.py @@ -480,6 +480,7 @@ async def test_large_batches(store: AsyncRedisStore) -> None: ) +@pytest.mark.requires_api_keys @pytest.mark.asyncio async def test_async_store_with_memory_persistence( redis_url: str, diff --git a/tests/test_store.py b/tests/test_store.py index cad1146..fe25e10 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -426,6 +426,7 @@ def test_vector_update_with_score_verification( assert not any(r.key == "doc4" for r in results_new) +@pytest.mark.requires_api_keys def test_store_with_memory_persistence(redis_url: str) -> None: """Test store functionality with memory persistence. diff --git a/tests/test_sync.py b/tests/test_sync.py index 174a1d0..9e4b0a4 100644 --- a/tests/test_sync.py +++ b/tests/test_sync.py @@ -434,6 +434,7 @@ def model() -> ChatOpenAI: return ChatOpenAI(model="gpt-4-turbo-preview", temperature=0) +@pytest.mark.requires_api_keys def test_sync_redis_checkpointer( tools: list[BaseTool], model: ChatOpenAI, redis_url: str ) -> None: From 092b1eaf0954ebd7ae0446667692802a411cd3b8 Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Fri, 7 Feb 2025 12:51:41 -0800 Subject: [PATCH 10/11] chore: lint --- tests/conftest.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index ed08388..679f4e0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,19 +55,22 @@ def pytest_addoption(parser): "--run-api-tests", action="store_true", default=False, - help="Run tests that require API keys" + help="Run tests that require API keys", ) + def pytest_configure(config): config.addinivalue_line( - "markers", - "requires_api_keys: mark test as requiring API keys" + "markers", "requires_api_keys: mark test as requiring API keys" ) + def pytest_collection_modifyitems(config, items): if config.getoption("--run-api-tests"): return - skip_api = pytest.mark.skip(reason="Skipping test because API keys are not provided. Use --run-api-tests to run these tests.") + skip_api = pytest.mark.skip( + reason="Skipping test because API keys are not provided. Use --run-api-tests to run these tests." + ) for item in items: if item.get_closest_marker("requires_api_keys"): item.add_marker(skip_api) From 0f23646d1b53e4b3ad5260c57f781759999fb2bf Mon Sep 17 00:00:00 2001 From: Andrew Brookins Date: Fri, 7 Feb 2025 12:54:13 -0800 Subject: [PATCH 11/11] chore: fix type annotations --- tests/conftest.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 679f4e0..2d61c0c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,7 +50,7 @@ async def clear_redis(redis_url: str) -> None: await client.aclose() # type: ignore[attr-defined] -def pytest_addoption(parser): +def pytest_addoption(parser: pytest.Parser) -> None: parser.addoption( "--run-api-tests", action="store_true", @@ -59,13 +59,15 @@ def pytest_addoption(parser): ) -def pytest_configure(config): +def pytest_configure(config: pytest.Config) -> None: config.addinivalue_line( "markers", "requires_api_keys: mark test as requiring API keys" ) -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems( + config: pytest.Config, items: list[pytest.Item] +) -> None: if config.getoption("--run-api-tests"): return skip_api = pytest.mark.skip(