Skip to content

Instantly share code, notes, and snippets.

@WorksOnMyVM
Created April 2, 2025 08:58
Show Gist options
  • Select an option

  • Save WorksOnMyVM/51845482edb6a591fa965d9453aee6a5 to your computer and use it in GitHub Desktop.

Select an option

Save WorksOnMyVM/51845482edb6a591fa965d9453aee6a5 to your computer and use it in GitHub Desktop.
Issue with “Using with a custom agent” example when using models that support multi-tool calling (e.g., gpt-4o-mini)
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 84,
"metadata": {},
"outputs": [],
"source": [
"from typing_extensions import Literal\n",
"from langchain_core.messages import ToolMessage, SystemMessage, HumanMessage\n",
"from langchain_core.tools import tool\n",
"from langchain_openai import ChatOpenAI\n",
"from langgraph.graph import MessagesState, StateGraph, START\n",
"from langgraph.types import Command\n",
"import os\n",
"from dotenv import load_dotenv\n",
"\n",
"load_dotenv()\n",
"\n",
"\n",
"model = ChatOpenAI(**{\n",
" \"model\": \"qwen-plus\",\n",
" \"base_url\": os.getenv(\"DASHSCOPE_BASE_URL\"),\n",
" \"api_key\": os.getenv(\"DASHSCOPE_API_KEY\")\n",
"})\n"
]
},
{
"cell_type": "code",
"execution_count": 85,
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.messages import convert_to_messages\n",
"\n",
"def pretty_print_messages(update):\n",
" if isinstance(update, tuple):\n",
" ns, update = update\n",
" # skip parent graph updates in the printouts\n",
" if len(ns) == 0:\n",
" return\n",
"\n",
" graph_id = ns[-1].split(\":\")[0]\n",
" print(f\"Update from subgraph {graph_id}:\")\n",
" print(\"\\n\")\n",
"\n",
" for node_name, node_update in update.items():\n",
" print(f\"Update from node {node_name}:\")\n",
" print(\"\\n\")\n",
"\n",
" for m in convert_to_messages(node_update[\"messages\"]):\n",
" m.pretty_print()\n",
" print(\"\\n\")"
]
},
{
"cell_type": "code",
"execution_count": 86,
"metadata": {},
"outputs": [],
"source": [
"from typing import Annotated\n",
"\n",
"from langchain_core.tools import tool\n",
"from langchain_core.tools.base import InjectedToolCallId\n",
"from langgraph.prebuilt import InjectedState\n",
"\n",
"\n",
"def make_handoff_tool(*, agent_name: str):\n",
" \"\"\"Create a tool that can return handoff via a Command\"\"\"\n",
" tool_name = f\"transfer_to_{agent_name}\"\n",
"\n",
" @tool(tool_name)\n",
" def handoff_to_agent(\n",
" # # optionally pass current graph state to the tool (will be ignored by the LLM)\n",
" state: Annotated[dict, InjectedState],\n",
" # optionally pass the current tool call ID (will be ignored by the LLM)\n",
" tool_call_id: Annotated[str, InjectedToolCallId],\n",
" ):\n",
" \"\"\"Ask another agent for help.\"\"\"\n",
" tool_message = {\n",
" \"role\": \"tool\",\n",
" \"content\": f\"Successfully transferred to {agent_name}\",\n",
" \"name\": tool_name,\n",
" \"tool_call_id\": tool_call_id,\n",
" }\n",
" print(\"Before tranfer the messages is: \", state[\"messages\"])\n",
" return Command(\n",
" # navigate to another agent node in the PARENT graph\n",
" goto=agent_name,\n",
" graph=Command.PARENT,\n",
" # This is the state update that the agent `agent_name` will see when it is invoked.\n",
" # We're passing agent's FULL internal message history AND adding a tool message to make sure\n",
" # the resulting chat history is valid. See the paragraph above for more information.\n",
" update={\"messages\": state[\"messages\"] + [tool_message]},\n",
" )\n",
"\n",
" return handoff_to_agent"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {},
"outputs": [],
"source": [
"from typing_extensions import Literal\n",
"from langchain_core.messages import ToolMessage\n",
"from langchain_core.tools import tool\n",
"from langgraph.graph import MessagesState, StateGraph, START\n",
"from langgraph.types import Command\n",
"\n",
"\n",
"def make_agent(model, tools, system_prompt=None):\n",
" model_with_tools = model.bind_tools(tools)\n",
" tools_by_name = {tool.name: tool for tool in tools}\n",
"\n",
" def call_model(state: MessagesState) -> Command[Literal[\"call_tools\", \"__end__\"]]:\n",
" messages = state[\"messages\"]\n",
" if system_prompt:\n",
" messages = [{\"role\": \"system\", \"content\": system_prompt}] + messages\n",
" \n",
" print(\"Before Call LLM: \", messages)\n",
" response = model_with_tools.invoke(messages)\n",
" if len(response.tool_calls) > 0:\n",
" return Command(goto=\"call_tools\", update={\"messages\": [response]})\n",
"\n",
" return {\"messages\": [response]}\n",
"\n",
" # NOTE: this is a simplified version of the prebuilt ToolNode\n",
" # If you want to have a tool node that has full feature parity, please refer to the source code\n",
" def call_tools(state: MessagesState) -> Command[Literal[\"call_model\"]]:\n",
" tool_calls = state[\"messages\"][-1].tool_calls\n",
" results = []\n",
" for tool_call in tool_calls:\n",
" tool_ = tools_by_name[tool_call[\"name\"]]\n",
" tool_input_fields = tool_.get_input_schema().model_json_schema()[\n",
" \"properties\"\n",
" ]\n",
"\n",
" # this is simplified for demonstration purposes and\n",
" # is different from the ToolNode implementation\n",
" if \"state\" in tool_input_fields:\n",
" # inject state\n",
" tool_call = {**tool_call, \"args\": {**tool_call[\"args\"], \"state\": state}}\n",
"\n",
" tool_response = tool_.invoke(tool_call)\n",
" if isinstance(tool_response, ToolMessage):\n",
" results.append(Command(update={\"messages\": [tool_response]}))\n",
"\n",
" # handle tools that return Command directly\n",
" elif isinstance(tool_response, Command):\n",
" results.append(tool_response)\n",
" print(\"Results: \", results)\n",
" # NOTE: nodes in LangGraph allow you to return list of updates, including Command objects\n",
" return results\n",
"\n",
" graph = StateGraph(MessagesState)\n",
" graph.add_node(call_model)\n",
" graph.add_node(call_tools)\n",
" graph.add_edge(START, \"call_model\")\n",
" graph.add_edge(\"call_tools\", \"call_model\")\n",
"\n",
" return graph.compile()"
]
},
{
"cell_type": "code",
"execution_count": 88,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
"<IPython.core.display.Image object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"@tool\n",
"def add(a: int, b: int) -> int:\n",
" \"\"\"Add two numbers.\"\"\"\n",
" result = a + b\n",
" return result\n",
"\n",
"@tool\n",
"def multiply(a: int, b: int) -> int:\n",
" \"\"\"Multiply two numbers.\"\"\"\n",
" return a * b\n",
"\n",
"agent = make_agent(model, [add, multiply])\n",
"\n",
"from IPython.display import Image, display\n",
"agent = make_agent(model, [add, multiply])\n",
"display(Image(agent.get_graph().draw_mermaid_png()))"
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Before Call LLM: [HumanMessage(content=\"what's (3 + 5) * 12\", additional_kwargs={}, response_metadata={}, id='ee2ba540-119e-4779-b271-deddcbbf3c43')]\n",
"Chunk: {'call_model': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ab22e9eee99e45519ca8f5', 'function': {'arguments': '{\"a\": 3, \"b\": 5}', 'name': 'add'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 239, 'total_tokens': 261, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-ae98c963-2d16-4a10-8af6-a2c65e492594-0', tool_calls=[{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_ab22e9eee99e45519ca8f5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 239, 'output_tokens': 22, 'total_tokens': 261, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}}\n",
"Results: [Command(update={'messages': [ToolMessage(content='8', name='add', tool_call_id='call_ab22e9eee99e45519ca8f5')]})]\n",
"Chunk: {'call_tools': {'messages': [ToolMessage(content='8', name='add', id='b516d81b-811c-4f49-b6ea-a51639008377', tool_call_id='call_ab22e9eee99e45519ca8f5')]}}\n",
"Before Call LLM: [HumanMessage(content=\"what's (3 + 5) * 12\", additional_kwargs={}, response_metadata={}, id='ee2ba540-119e-4779-b271-deddcbbf3c43'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ab22e9eee99e45519ca8f5', 'function': {'arguments': '{\"a\": 3, \"b\": 5}', 'name': 'add'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 239, 'total_tokens': 261, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-ae98c963-2d16-4a10-8af6-a2c65e492594-0', tool_calls=[{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_ab22e9eee99e45519ca8f5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 239, 'output_tokens': 22, 'total_tokens': 261, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}), ToolMessage(content='8', name='add', id='b516d81b-811c-4f49-b6ea-a51639008377', tool_call_id='call_ab22e9eee99e45519ca8f5')]\n",
"Chunk: {'call_model': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_9b1ff84764b442bc80887a', 'function': {'arguments': '{\"a\": 8, \"b\": 12}', 'name': 'multiply'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 271, 'total_tokens': 296, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 128}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-358dda61-1682-4812-ac95-42a727149ad9-0', tool_calls=[{'name': 'multiply', 'args': {'a': 8, 'b': 12}, 'id': 'call_9b1ff84764b442bc80887a', 'type': 'tool_call'}], usage_metadata={'input_tokens': 271, 'output_tokens': 25, 'total_tokens': 296, 'input_token_details': {'cache_read': 128}, 'output_token_details': {}})]}}\n",
"Results: [Command(update={'messages': [ToolMessage(content='96', name='multiply', tool_call_id='call_9b1ff84764b442bc80887a')]})]\n",
"Chunk: {'call_tools': {'messages': [ToolMessage(content='96', name='multiply', id='5fec0836-bc6b-4461-983a-1ebc652f0adc', tool_call_id='call_9b1ff84764b442bc80887a')]}}\n",
"Before Call LLM: [HumanMessage(content=\"what's (3 + 5) * 12\", additional_kwargs={}, response_metadata={}, id='ee2ba540-119e-4779-b271-deddcbbf3c43'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_ab22e9eee99e45519ca8f5', 'function': {'arguments': '{\"a\": 3, \"b\": 5}', 'name': 'add'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 22, 'prompt_tokens': 239, 'total_tokens': 261, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-ae98c963-2d16-4a10-8af6-a2c65e492594-0', tool_calls=[{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_ab22e9eee99e45519ca8f5', 'type': 'tool_call'}], usage_metadata={'input_tokens': 239, 'output_tokens': 22, 'total_tokens': 261, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}), ToolMessage(content='8', name='add', id='b516d81b-811c-4f49-b6ea-a51639008377', tool_call_id='call_ab22e9eee99e45519ca8f5'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_9b1ff84764b442bc80887a', 'function': {'arguments': '{\"a\": 8, \"b\": 12}', 'name': 'multiply'}, 'type': 'function', 'index': 0}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 271, 'total_tokens': 296, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 128}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-358dda61-1682-4812-ac95-42a727149ad9-0', tool_calls=[{'name': 'multiply', 'args': {'a': 8, 'b': 12}, 'id': 'call_9b1ff84764b442bc80887a', 'type': 'tool_call'}], usage_metadata={'input_tokens': 271, 'output_tokens': 25, 'total_tokens': 296, 'input_token_details': {'cache_read': 128}, 'output_token_details': {}}), ToolMessage(content='96', name='multiply', id='5fec0836-bc6b-4461-983a-1ebc652f0adc', tool_call_id='call_9b1ff84764b442bc80887a')]\n",
"Chunk: {'call_model': {'messages': [AIMessage(content='The result of (3 + 5) * 12 is 96.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 307, 'total_tokens': 326, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-69411af5-d7d2-454f-84eb-35663940c445-0', usage_metadata={'input_tokens': 307, 'output_tokens': 19, 'total_tokens': 326, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]}}\n"
]
}
],
"source": [
"for chunk in agent.stream({\"messages\": [HumanMessage(content=\"what's (3 + 5) * 12\")]}):\n",
" print(\"Chunk: \", chunk)\n",
" # pretty_print_messages(chunk)"
]
},
{
"cell_type": "code",
"execution_count": 82,
"metadata": {},
"outputs": [],
"source": [
"model = ChatOpenAI(**{\n",
" # \"model\": \"qwen-plus\",\n",
" # \"base_url\": os.getenv(\"DASHSCOPE_BASE_URL\"),\n",
" # \"api_key\": os.getenv(\"DASHSCOPE_API_KEY\")\n",
" \"model\": \"gpt-4o-mini\",\n",
"})\n",
"\n",
"addition_expert = make_agent(\n",
" model, \n",
" [add, make_handoff_tool(agent_name=\"multiplication_expert\")], \n",
" system_prompt=\"You are an addition expert, you can ask the multiplication expert for help with multiplication. Always do your portion of calculation before the handoff.\"\n",
")\n",
"\n",
"multiplication_expert = make_agent(\n",
" model, \n",
" [multiply, make_handoff_tool(agent_name=\"addition_expert\")], \n",
" system_prompt=\"You are a multiplication expert, you can ask the addition expert for help with addition. Always do your portion of calculation before the handoff.\"\n",
")\n",
"\n",
"builder = StateGraph(MessagesState)\n",
"builder.add_node(\"addition_expert\", addition_expert)\n",
"builder.add_node(\"multiplication_expert\", multiplication_expert)\n",
"builder.add_edge(START, \"addition_expert\")\n",
"\n",
"graph = builder.compile()\n",
"\n",
"\n",
"\n",
"# display(Image(graph.get_graph().draw_mermaid_png()))"
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Update from subgraph addition_expert:Before tranfer the messages is: [HumanMessage(content=\"what's (3 + 5) * 12\", additional_kwargs={}, response_metadata={}, id='e2357bc8-bfd2-498b-9135-1ca218161c74'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_mI6KgowJUS0AJO8A4aVd0PFc', 'function': {'arguments': '{\"a\": 3, \"b\": 5}', 'name': 'add'}, 'type': 'function'}, {'id': 'call_obT1jz66ptdQAMBU9uCznqkJ', 'function': {'arguments': '{}', 'name': 'transfer_to_multiplication_expert'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_tokens': 91, 'total_tokens': 142, '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_b705f0c291', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-d91bcf38-bd35-42e9-a8fd-92600ceeee34-0', tool_calls=[{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_mI6KgowJUS0AJO8A4aVd0PFc', 'type': 'tool_call'}, {'name': 'transfer_to_multiplication_expert', 'args': {}, 'id': 'call_obT1jz66ptdQAMBU9uCznqkJ', 'type': 'tool_call'}], usage_metadata={'input_tokens': 91, 'output_tokens': 51, 'total_tokens': 142, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]\n",
"\n",
"\n",
"\n",
"Update from node agent:\n",
"\n",
"\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" add (call_mI6KgowJUS0AJO8A4aVd0PFc)\n",
" Call ID: call_mI6KgowJUS0AJO8A4aVd0PFc\n",
" Args:\n",
" a: 3\n",
" b: 5\n",
" transfer_to_multiplication_expert (call_obT1jz66ptdQAMBU9uCznqkJ)\n",
" Call ID: call_obT1jz66ptdQAMBU9uCznqkJ\n",
" Args:\n",
"\n",
"\n"
]
},
{
"ename": "ValueError",
"evalue": "Found AIMessages with tool_calls that do not have a corresponding ToolMessage. Here are the first few of those tool calls: [{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_mI6KgowJUS0AJO8A4aVd0PFc', 'type': 'tool_call'}].\n\nEvery tool call (LLM requesting to call a tool) in the message history MUST have a corresponding ToolMessage (result of a tool invocation to return to the LLM) - this is required by most LLM providers.\nFor troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/INVALID_CHAT_HISTORY",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[91]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \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[43m.\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43muser\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mwhat\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[33;43ms (3 + 5) * 12\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubgraphs\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\n\u001b[32m 3\u001b[39m \u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[43m \u001b[49m\u001b[43mpretty_print_messages\u001b[49m\u001b[43m(\u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:1993\u001b[39m, in \u001b[36mPregel.stream\u001b[39m\u001b[34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)\u001b[39m\n\u001b[32m 1987\u001b[39m \u001b[38;5;66;03m# Similarly to Bulk Synchronous Parallel / Pregel model\u001b[39;00m\n\u001b[32m 1988\u001b[39m \u001b[38;5;66;03m# computation proceeds in steps, while there are channel updates.\u001b[39;00m\n\u001b[32m 1989\u001b[39m \u001b[38;5;66;03m# Channel updates from step N are only visible in step N+1\u001b[39;00m\n\u001b[32m 1990\u001b[39m \u001b[38;5;66;03m# channels are guaranteed to be immutable for the duration of the step,\u001b[39;00m\n\u001b[32m 1991\u001b[39m \u001b[38;5;66;03m# with channel updates applied only at the transition between steps.\u001b[39;00m\n\u001b[32m 1992\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m loop.tick(input_keys=\u001b[38;5;28mself\u001b[39m.input_channels):\n\u001b[32m-> \u001b[39m\u001b[32m1993\u001b[39m \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[43m.\u001b[49m\u001b[43mtick\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1994\u001b[39m \u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtasks\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalues\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1995\u001b[39m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstep_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1996\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1997\u001b[39m \u001b[43m \u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m=\u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1998\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 1999\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# emit output\u001b[39;49;00m\n\u001b[32m 2000\u001b[39m \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[32m 2001\u001b[39m \u001b[38;5;66;03m# emit output\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:302\u001b[39m, in \u001b[36mPregelRunner.tick\u001b[39m\u001b[34m(self, tasks, reraise, timeout, retry_policy, get_waiter)\u001b[39m\n\u001b[32m 300\u001b[39m \u001b[38;5;28;01myield\u001b[39;00m\n\u001b[32m 301\u001b[39m \u001b[38;5;66;03m# panic on failure or timeout\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m302\u001b[39m \u001b[43m_panic_or_proceed\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 303\u001b[39m \u001b[43m \u001b[49m\u001b[43mfutures\u001b[49m\u001b[43m.\u001b[49m\u001b[43mdone\u001b[49m\u001b[43m.\u001b[49m\u001b[43munion\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mfutures\u001b[49m\u001b[43m.\u001b[49m\u001b[43mitems\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[43mt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 304\u001b[39m \u001b[43m \u001b[49m\u001b[43mpanic\u001b[49m\u001b[43m=\u001b[49m\u001b[43mreraise\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 305\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:619\u001b[39m, in \u001b[36m_panic_or_proceed\u001b[39m\u001b[34m(futs, timeout_exc_cls, panic)\u001b[39m\n\u001b[32m 617\u001b[39m \u001b[38;5;66;03m# raise the exception\u001b[39;00m\n\u001b[32m 618\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m panic:\n\u001b[32m--> \u001b[39m\u001b[32m619\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m exc\n\u001b[32m 620\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m inflight:\n\u001b[32m 621\u001b[39m \u001b[38;5;66;03m# if we got here means we timed out\u001b[39;00m\n\u001b[32m 622\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m inflight:\n\u001b[32m 623\u001b[39m \u001b[38;5;66;03m# cancel all pending tasks\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/executor.py:83\u001b[39m, in \u001b[36mBackgroundExecutor.done\u001b[39m\u001b[34m(self, task)\u001b[39m\n\u001b[32m 81\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Remove the task from the tasks dict when it's done.\"\"\"\u001b[39;00m\n\u001b[32m 82\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m83\u001b[39m \u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43mresult\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 84\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m GraphBubbleUp:\n\u001b[32m 85\u001b[39m \u001b[38;5;66;03m# This exception is an interruption signal, not an error\u001b[39;00m\n\u001b[32m 86\u001b[39m \u001b[38;5;66;03m# so we don't want to re-raise it on exit\u001b[39;00m\n\u001b[32m 87\u001b[39m \u001b[38;5;28mself\u001b[39m.tasks.pop(task)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.11.0/lib/python3.11/concurrent/futures/_base.py:449\u001b[39m, in \u001b[36mFuture.result\u001b[39m\u001b[34m(self, timeout)\u001b[39m\n\u001b[32m 447\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n\u001b[32m 448\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._state == FINISHED:\n\u001b[32m--> \u001b[39m\u001b[32m449\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__get_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 451\u001b[39m \u001b[38;5;28mself\u001b[39m._condition.wait(timeout)\n\u001b[32m 453\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.11.0/lib/python3.11/concurrent/futures/_base.py:401\u001b[39m, in \u001b[36mFuture.__get_result\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 399\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._exception:\n\u001b[32m 400\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m401\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m._exception\n\u001b[32m 402\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m 403\u001b[39m \u001b[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001b[39;00m\n\u001b[32m 404\u001b[39m \u001b[38;5;28mself\u001b[39m = \u001b[38;5;28;01mNone\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.11.0/lib/python3.11/concurrent/futures/thread.py:58\u001b[39m, in \u001b[36m_WorkItem.run\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[32m 57\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m58\u001b[39m result = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 59\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 60\u001b[39m \u001b[38;5;28mself\u001b[39m.future.set_exception(exc)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/retry.py:40\u001b[39m, in \u001b[36mrun_with_retry\u001b[39m\u001b[34m(task, retry_policy, configurable)\u001b[39m\n\u001b[32m 38\u001b[39m task.writes.clear()\n\u001b[32m 39\u001b[39m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m40\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43mproc\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43minput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 41\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 42\u001b[39m ns: \u001b[38;5;28mstr\u001b[39m = config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:546\u001b[39m, in \u001b[36mRunnableSeq.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 542\u001b[39m config = patch_config(\n\u001b[32m 543\u001b[39m config, callbacks=run_manager.get_child(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mseq:step:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;250m \u001b[39m+\u001b[38;5;250m \u001b[39m\u001b[32m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 544\u001b[39m )\n\u001b[32m 545\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m i == \u001b[32m0\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m546\u001b[39m \u001b[38;5;28minput\u001b[39m = \u001b[43mstep\u001b[49m\u001b[43m.\u001b[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[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 547\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 548\u001b[39m \u001b[38;5;28minput\u001b[39m = step.invoke(\u001b[38;5;28minput\u001b[39m, config)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:2336\u001b[39m, in \u001b[36mPregel.invoke\u001b[39m\u001b[34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, **kwargs)\u001b[39m\n\u001b[32m 2334\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 2335\u001b[39m chunks = []\n\u001b[32m-> \u001b[39m\u001b[32m2336\u001b[39m \u001b[43m\u001b[49m\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[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2337\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 2338\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2339\u001b[39m \u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2340\u001b[39m \u001b[43m \u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2341\u001b[39m \u001b[43m \u001b[49m\u001b[43minterrupt_before\u001b[49m\u001b[43m=\u001b[49m\u001b[43minterrupt_before\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2342\u001b[39m \u001b[43m \u001b[49m\u001b[43minterrupt_after\u001b[49m\u001b[43m=\u001b[49m\u001b[43minterrupt_after\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2343\u001b[39m \u001b[43m \u001b[49m\u001b[43mdebug\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdebug\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2344\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2345\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 2346\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m \u001b[49m\u001b[43m==\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mvalues\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\n\u001b[32m 2347\u001b[39m \u001b[43m \u001b[49m\u001b[43mlatest\u001b[49m\u001b[43m \u001b[49m\u001b[43m=\u001b[49m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:1993\u001b[39m, in \u001b[36mPregel.stream\u001b[39m\u001b[34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)\u001b[39m\n\u001b[32m 1987\u001b[39m \u001b[38;5;66;03m# Similarly to Bulk Synchronous Parallel / Pregel model\u001b[39;00m\n\u001b[32m 1988\u001b[39m \u001b[38;5;66;03m# computation proceeds in steps, while there are channel updates.\u001b[39;00m\n\u001b[32m 1989\u001b[39m \u001b[38;5;66;03m# Channel updates from step N are only visible in step N+1\u001b[39;00m\n\u001b[32m 1990\u001b[39m \u001b[38;5;66;03m# channels are guaranteed to be immutable for the duration of the step,\u001b[39;00m\n\u001b[32m 1991\u001b[39m \u001b[38;5;66;03m# with channel updates applied only at the transition between steps.\u001b[39;00m\n\u001b[32m 1992\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m loop.tick(input_keys=\u001b[38;5;28mself\u001b[39m.input_channels):\n\u001b[32m-> \u001b[39m\u001b[32m1993\u001b[39m \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[43m.\u001b[49m\u001b[43mtick\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1994\u001b[39m \u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtasks\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalues\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1995\u001b[39m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstep_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1996\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1997\u001b[39m \u001b[43m \u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m=\u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1998\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 1999\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# emit output\u001b[39;49;00m\n\u001b[32m 2000\u001b[39m \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[32m 2001\u001b[39m \u001b[38;5;66;03m# emit output\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:230\u001b[39m, in \u001b[36mPregelRunner.tick\u001b[39m\u001b[34m(self, tasks, reraise, timeout, retry_policy, get_waiter)\u001b[39m\n\u001b[32m 228\u001b[39m t = tasks[\u001b[32m0\u001b[39m]\n\u001b[32m 229\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m230\u001b[39m \u001b[43mrun_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 231\u001b[39m \u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 232\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 233\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfigurable\u001b[49m\u001b[43m=\u001b[49m\u001b[43m{\u001b[49m\n\u001b[32m 234\u001b[39m \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[32m 235\u001b[39m \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[32m 236\u001b[39m \u001b[43m \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 237\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 238\u001b[39m \u001b[38;5;28mself\u001b[39m.commit(t, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[32m 239\u001b[39m \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",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/retry.py:40\u001b[39m, in \u001b[36mrun_with_retry\u001b[39m\u001b[34m(task, retry_policy, configurable)\u001b[39m\n\u001b[32m 38\u001b[39m task.writes.clear()\n\u001b[32m 39\u001b[39m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m40\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43mproc\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43minput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 41\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 42\u001b[39m ns: \u001b[38;5;28mstr\u001b[39m = config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:546\u001b[39m, in \u001b[36mRunnableSeq.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 542\u001b[39m config = patch_config(\n\u001b[32m 543\u001b[39m config, callbacks=run_manager.get_child(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mseq:step:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;250m \u001b[39m+\u001b[38;5;250m \u001b[39m\u001b[32m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 544\u001b[39m )\n\u001b[32m 545\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m i == \u001b[32m0\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m546\u001b[39m \u001b[38;5;28minput\u001b[39m = \u001b[43mstep\u001b[49m\u001b[43m.\u001b[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[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 547\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 548\u001b[39m \u001b[38;5;28minput\u001b[39m = step.invoke(\u001b[38;5;28minput\u001b[39m, config)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:302\u001b[39m, in \u001b[36mRunnableCallable.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 300\u001b[39m context = copy_context()\n\u001b[32m 301\u001b[39m context.run(_set_config_context, child_config)\n\u001b[32m--> \u001b[39m\u001b[32m302\u001b[39m ret = \u001b[43mcontext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 303\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 304\u001b[39m run_manager.on_chain_error(e)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/prebuilt/chat_agent_executor.py:656\u001b[39m, in \u001b[36mcreate_react_agent.<locals>.call_model\u001b[39m\u001b[34m(state, config)\u001b[39m\n\u001b[32m 655\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcall_model\u001b[39m(state: AgentState, config: RunnableConfig) -> AgentState:\n\u001b[32m--> \u001b[39m\u001b[32m656\u001b[39m \u001b[43m_validate_chat_history\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstate\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 657\u001b[39m response = cast(AIMessage, model_runnable.invoke(state, config))\n\u001b[32m 658\u001b[39m \u001b[38;5;66;03m# add agent name to the AIMessage\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/prebuilt/chat_agent_executor.py:241\u001b[39m, in \u001b[36m_validate_chat_history\u001b[39m\u001b[34m(messages)\u001b[39m\n\u001b[32m 232\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[32m 234\u001b[39m error_message = create_error_message(\n\u001b[32m 235\u001b[39m message=\u001b[33m\"\u001b[39m\u001b[33mFound AIMessages with tool_calls that do not have a corresponding ToolMessage. \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 236\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHere are the first few of those tool calls: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtool_calls_without_results[:\u001b[32m3\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m\n\u001b[32m (...)\u001b[39m\u001b[32m 239\u001b[39m error_code=ErrorCode.INVALID_CHAT_HISTORY,\n\u001b[32m 240\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m241\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(error_message)\n",
"\u001b[31mValueError\u001b[39m: Found AIMessages with tool_calls that do not have a corresponding ToolMessage. Here are the first few of those tool calls: [{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_mI6KgowJUS0AJO8A4aVd0PFc', 'type': 'tool_call'}].\n\nEvery tool call (LLM requesting to call a tool) in the message history MUST have a corresponding ToolMessage (result of a tool invocation to return to the LLM) - this is required by most LLM providers.\nFor troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/INVALID_CHAT_HISTORY",
"During task with name 'agent' and id '4ae877ad-042d-c36a-2b53-00f45a824b08'",
"During task with name 'multiplication_expert' and id 'bfe917aa-ab04-9aab-ccf8-b27d6126b29b'"
]
}
],
"source": [
"for chunk in graph.stream(\n",
" {\"messages\": [(\"user\", \"what's (3 + 5) * 12\")]}, subgraphs=True\n",
"):\n",
" pretty_print_messages(chunk)"
]
},
{
"cell_type": "code",
"execution_count": 95,
"metadata": {},
"outputs": [],
"source": [
"from langgraph.prebuilt import create_react_agent\n",
"\n",
"model = ChatOpenAI(**{\n",
" \"model\": \"gpt-4o-mini\",\n",
"})\n",
"\n",
"addition_expert = create_react_agent(\n",
" model,\n",
" [add, make_handoff_tool(agent_name=\"multiplication_expert\")],\n",
" prompt=\"You are an addition expert, you can ask the multiplication expert for help with multiplication.\",\n",
")\n",
"\n",
"multiplication_expert = create_react_agent(\n",
" model,\n",
" [multiply, make_handoff_tool(agent_name=\"addition_expert\")],\n",
" prompt=\"You are a multiplication expert, you can ask an addition expert for help with addition.\",\n",
")\n",
"\n",
"builder = StateGraph(MessagesState)\n",
"builder.add_node(\"addition_expert\", addition_expert)\n",
"builder.add_node(\"multiplication_expert\", multiplication_expert)\n",
"builder.add_edge(START, \"addition_expert\")\n",
"graph = builder.compile()"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Before tranfer the messages is: [HumanMessage(content=\"what's (3 + 5) * 12\", additional_kwargs={}, response_metadata={}, id='c78bd4f5-75a5-4e24-a8eb-2ac2d6710517'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_XwMw5vqUpuGHPVoxGiA4YVKd', 'function': {'arguments': '{\"a\": 3, \"b\": 5}', 'name': 'add'}, 'type': 'function'}, {'id': 'call_3EdDOl6QsRYf16Qe2xXuCDS0', 'function': {'arguments': '{}', 'name': 'transfer_to_multiplication_expert'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 51, 'prompt_tokens': 91, 'total_tokens': 142, '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_ded0d14823', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-5ca9a7eb-3e65-499f-b7b2-2d4159e5b648-0', tool_calls=[{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_XwMw5vqUpuGHPVoxGiA4YVKd', 'type': 'tool_call'}, {'name': 'transfer_to_multiplication_expert', 'args': {}, 'id': 'call_3EdDOl6QsRYf16Qe2xXuCDS0', 'type': 'tool_call'}], usage_metadata={'input_tokens': 91, 'output_tokens': 51, 'total_tokens': 142, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})]\n",
"Update from subgraph addition_expert:\n",
"\n",
"\n",
"Update from node agent:\n",
"\n",
"\n",
"==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"Tool Calls:\n",
" add (call_XwMw5vqUpuGHPVoxGiA4YVKd)\n",
" Call ID: call_XwMw5vqUpuGHPVoxGiA4YVKd\n",
" Args:\n",
" a: 3\n",
" b: 5\n",
" transfer_to_multiplication_expert (call_3EdDOl6QsRYf16Qe2xXuCDS0)\n",
" Call ID: call_3EdDOl6QsRYf16Qe2xXuCDS0\n",
" Args:\n",
"\n",
"\n"
]
},
{
"ename": "ValueError",
"evalue": "Found AIMessages with tool_calls that do not have a corresponding ToolMessage. Here are the first few of those tool calls: [{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_XwMw5vqUpuGHPVoxGiA4YVKd', 'type': 'tool_call'}].\n\nEvery tool call (LLM requesting to call a tool) in the message history MUST have a corresponding ToolMessage (result of a tool invocation to return to the LLM) - this is required by most LLM providers.\nFor troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/INVALID_CHAT_HISTORY",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[96]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \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[43m.\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43muser\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mwhat\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[33;43ms (3 + 5) * 12\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msubgraphs\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\n\u001b[32m 3\u001b[39m \u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[43m \u001b[49m\u001b[43mpretty_print_messages\u001b[49m\u001b[43m(\u001b[49m\u001b[43mchunk\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:1993\u001b[39m, in \u001b[36mPregel.stream\u001b[39m\u001b[34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)\u001b[39m\n\u001b[32m 1987\u001b[39m \u001b[38;5;66;03m# Similarly to Bulk Synchronous Parallel / Pregel model\u001b[39;00m\n\u001b[32m 1988\u001b[39m \u001b[38;5;66;03m# computation proceeds in steps, while there are channel updates.\u001b[39;00m\n\u001b[32m 1989\u001b[39m \u001b[38;5;66;03m# Channel updates from step N are only visible in step N+1\u001b[39;00m\n\u001b[32m 1990\u001b[39m \u001b[38;5;66;03m# channels are guaranteed to be immutable for the duration of the step,\u001b[39;00m\n\u001b[32m 1991\u001b[39m \u001b[38;5;66;03m# with channel updates applied only at the transition between steps.\u001b[39;00m\n\u001b[32m 1992\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m loop.tick(input_keys=\u001b[38;5;28mself\u001b[39m.input_channels):\n\u001b[32m-> \u001b[39m\u001b[32m1993\u001b[39m \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[43m.\u001b[49m\u001b[43mtick\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1994\u001b[39m \u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtasks\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalues\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1995\u001b[39m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstep_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1996\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1997\u001b[39m \u001b[43m \u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m=\u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1998\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 1999\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# emit output\u001b[39;49;00m\n\u001b[32m 2000\u001b[39m \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[32m 2001\u001b[39m \u001b[38;5;66;03m# emit output\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:302\u001b[39m, in \u001b[36mPregelRunner.tick\u001b[39m\u001b[34m(self, tasks, reraise, timeout, retry_policy, get_waiter)\u001b[39m\n\u001b[32m 300\u001b[39m \u001b[38;5;28;01myield\u001b[39;00m\n\u001b[32m 301\u001b[39m \u001b[38;5;66;03m# panic on failure or timeout\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m302\u001b[39m \u001b[43m_panic_or_proceed\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 303\u001b[39m \u001b[43m \u001b[49m\u001b[43mfutures\u001b[49m\u001b[43m.\u001b[49m\u001b[43mdone\u001b[49m\u001b[43m.\u001b[49m\u001b[43munion\u001b[49m\u001b[43m(\u001b[49m\u001b[43mf\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mf\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mfutures\u001b[49m\u001b[43m.\u001b[49m\u001b[43mitems\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[43mt\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mis\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 304\u001b[39m \u001b[43m \u001b[49m\u001b[43mpanic\u001b[49m\u001b[43m=\u001b[49m\u001b[43mreraise\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 305\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:619\u001b[39m, in \u001b[36m_panic_or_proceed\u001b[39m\u001b[34m(futs, timeout_exc_cls, panic)\u001b[39m\n\u001b[32m 617\u001b[39m \u001b[38;5;66;03m# raise the exception\u001b[39;00m\n\u001b[32m 618\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m panic:\n\u001b[32m--> \u001b[39m\u001b[32m619\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m exc\n\u001b[32m 620\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m inflight:\n\u001b[32m 621\u001b[39m \u001b[38;5;66;03m# if we got here means we timed out\u001b[39;00m\n\u001b[32m 622\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m inflight:\n\u001b[32m 623\u001b[39m \u001b[38;5;66;03m# cancel all pending tasks\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/executor.py:83\u001b[39m, in \u001b[36mBackgroundExecutor.done\u001b[39m\u001b[34m(self, task)\u001b[39m\n\u001b[32m 81\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Remove the task from the tasks dict when it's done.\"\"\"\u001b[39;00m\n\u001b[32m 82\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m83\u001b[39m \u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43mresult\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 84\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m GraphBubbleUp:\n\u001b[32m 85\u001b[39m \u001b[38;5;66;03m# This exception is an interruption signal, not an error\u001b[39;00m\n\u001b[32m 86\u001b[39m \u001b[38;5;66;03m# so we don't want to re-raise it on exit\u001b[39;00m\n\u001b[32m 87\u001b[39m \u001b[38;5;28mself\u001b[39m.tasks.pop(task)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.11.0/lib/python3.11/concurrent/futures/_base.py:449\u001b[39m, in \u001b[36mFuture.result\u001b[39m\u001b[34m(self, timeout)\u001b[39m\n\u001b[32m 447\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n\u001b[32m 448\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._state == FINISHED:\n\u001b[32m--> \u001b[39m\u001b[32m449\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m__get_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 451\u001b[39m \u001b[38;5;28mself\u001b[39m._condition.wait(timeout)\n\u001b[32m 453\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.11.0/lib/python3.11/concurrent/futures/_base.py:401\u001b[39m, in \u001b[36mFuture.__get_result\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 399\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._exception:\n\u001b[32m 400\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m401\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m._exception\n\u001b[32m 402\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m 403\u001b[39m \u001b[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001b[39;00m\n\u001b[32m 404\u001b[39m \u001b[38;5;28mself\u001b[39m = \u001b[38;5;28;01mNone\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.11.0/lib/python3.11/concurrent/futures/thread.py:58\u001b[39m, in \u001b[36m_WorkItem.run\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 55\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[32m 57\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m58\u001b[39m result = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 59\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 60\u001b[39m \u001b[38;5;28mself\u001b[39m.future.set_exception(exc)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/retry.py:40\u001b[39m, in \u001b[36mrun_with_retry\u001b[39m\u001b[34m(task, retry_policy, configurable)\u001b[39m\n\u001b[32m 38\u001b[39m task.writes.clear()\n\u001b[32m 39\u001b[39m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m40\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43mproc\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43minput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 41\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 42\u001b[39m ns: \u001b[38;5;28mstr\u001b[39m = config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:546\u001b[39m, in \u001b[36mRunnableSeq.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 542\u001b[39m config = patch_config(\n\u001b[32m 543\u001b[39m config, callbacks=run_manager.get_child(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mseq:step:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;250m \u001b[39m+\u001b[38;5;250m \u001b[39m\u001b[32m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 544\u001b[39m )\n\u001b[32m 545\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m i == \u001b[32m0\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m546\u001b[39m \u001b[38;5;28minput\u001b[39m = \u001b[43mstep\u001b[49m\u001b[43m.\u001b[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[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 547\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 548\u001b[39m \u001b[38;5;28minput\u001b[39m = step.invoke(\u001b[38;5;28minput\u001b[39m, config)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:2336\u001b[39m, in \u001b[36mPregel.invoke\u001b[39m\u001b[34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, **kwargs)\u001b[39m\n\u001b[32m 2334\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 2335\u001b[39m chunks = []\n\u001b[32m-> \u001b[39m\u001b[32m2336\u001b[39m \u001b[43m\u001b[49m\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[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstream\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2337\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 2338\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2339\u001b[39m \u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m=\u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2340\u001b[39m \u001b[43m \u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[43m=\u001b[49m\u001b[43moutput_keys\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2341\u001b[39m \u001b[43m \u001b[49m\u001b[43minterrupt_before\u001b[49m\u001b[43m=\u001b[49m\u001b[43minterrupt_before\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2342\u001b[39m \u001b[43m \u001b[49m\u001b[43minterrupt_after\u001b[49m\u001b[43m=\u001b[49m\u001b[43minterrupt_after\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2343\u001b[39m \u001b[43m \u001b[49m\u001b[43mdebug\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdebug\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2344\u001b[39m \u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 2345\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 2346\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstream_mode\u001b[49m\u001b[43m \u001b[49m\u001b[43m==\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mvalues\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\n\u001b[32m 2347\u001b[39m \u001b[43m \u001b[49m\u001b[43mlatest\u001b[49m\u001b[43m \u001b[49m\u001b[43m=\u001b[49m\u001b[43m \u001b[49m\u001b[43mchunk\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/__init__.py:1993\u001b[39m, in \u001b[36mPregel.stream\u001b[39m\u001b[34m(self, input, config, stream_mode, output_keys, interrupt_before, interrupt_after, debug, subgraphs)\u001b[39m\n\u001b[32m 1987\u001b[39m \u001b[38;5;66;03m# Similarly to Bulk Synchronous Parallel / Pregel model\u001b[39;00m\n\u001b[32m 1988\u001b[39m \u001b[38;5;66;03m# computation proceeds in steps, while there are channel updates.\u001b[39;00m\n\u001b[32m 1989\u001b[39m \u001b[38;5;66;03m# Channel updates from step N are only visible in step N+1\u001b[39;00m\n\u001b[32m 1990\u001b[39m \u001b[38;5;66;03m# channels are guaranteed to be immutable for the duration of the step,\u001b[39;00m\n\u001b[32m 1991\u001b[39m \u001b[38;5;66;03m# with channel updates applied only at the transition between steps.\u001b[39;00m\n\u001b[32m 1992\u001b[39m \u001b[38;5;28;01mwhile\u001b[39;00m loop.tick(input_keys=\u001b[38;5;28mself\u001b[39m.input_channels):\n\u001b[32m-> \u001b[39m\u001b[32m1993\u001b[39m \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[43m.\u001b[49m\u001b[43mtick\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 1994\u001b[39m \u001b[43m \u001b[49m\u001b[43mloop\u001b[49m\u001b[43m.\u001b[49m\u001b[43mtasks\u001b[49m\u001b[43m.\u001b[49m\u001b[43mvalues\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1995\u001b[39m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mstep_timeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1996\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1997\u001b[39m \u001b[43m \u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m=\u001b[49m\u001b[43mget_waiter\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 1998\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[32m 1999\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# emit output\u001b[39;49;00m\n\u001b[32m 2000\u001b[39m \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[32m 2001\u001b[39m \u001b[38;5;66;03m# emit output\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/runner.py:230\u001b[39m, in \u001b[36mPregelRunner.tick\u001b[39m\u001b[34m(self, tasks, reraise, timeout, retry_policy, get_waiter)\u001b[39m\n\u001b[32m 228\u001b[39m t = tasks[\u001b[32m0\u001b[39m]\n\u001b[32m 229\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m230\u001b[39m \u001b[43mrun_with_retry\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 231\u001b[39m \u001b[43m \u001b[49m\u001b[43mt\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 232\u001b[39m \u001b[43m \u001b[49m\u001b[43mretry_policy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 233\u001b[39m \u001b[43m \u001b[49m\u001b[43mconfigurable\u001b[49m\u001b[43m=\u001b[49m\u001b[43m{\u001b[49m\n\u001b[32m 234\u001b[39m \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[32m 235\u001b[39m \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[32m 236\u001b[39m \u001b[43m \u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 237\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 238\u001b[39m \u001b[38;5;28mself\u001b[39m.commit(t, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[32m 239\u001b[39m \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",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/pregel/retry.py:40\u001b[39m, in \u001b[36mrun_with_retry\u001b[39m\u001b[34m(task, retry_policy, configurable)\u001b[39m\n\u001b[32m 38\u001b[39m task.writes.clear()\n\u001b[32m 39\u001b[39m \u001b[38;5;66;03m# run the task\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m40\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43mproc\u001b[49m\u001b[43m.\u001b[49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtask\u001b[49m\u001b[43m.\u001b[49m\u001b[43minput\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 41\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m ParentCommand \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 42\u001b[39m ns: \u001b[38;5;28mstr\u001b[39m = config[CONF][CONFIG_KEY_CHECKPOINT_NS]\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:546\u001b[39m, in \u001b[36mRunnableSeq.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 542\u001b[39m config = patch_config(\n\u001b[32m 543\u001b[39m config, callbacks=run_manager.get_child(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mseq:step:\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;250m \u001b[39m+\u001b[38;5;250m \u001b[39m\u001b[32m1\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m 544\u001b[39m )\n\u001b[32m 545\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m i == \u001b[32m0\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m546\u001b[39m \u001b[38;5;28minput\u001b[39m = \u001b[43mstep\u001b[49m\u001b[43m.\u001b[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[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 547\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 548\u001b[39m \u001b[38;5;28minput\u001b[39m = step.invoke(\u001b[38;5;28minput\u001b[39m, config)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/utils/runnable.py:302\u001b[39m, in \u001b[36mRunnableCallable.invoke\u001b[39m\u001b[34m(self, input, config, **kwargs)\u001b[39m\n\u001b[32m 300\u001b[39m context = copy_context()\n\u001b[32m 301\u001b[39m context.run(_set_config_context, child_config)\n\u001b[32m--> \u001b[39m\u001b[32m302\u001b[39m ret = \u001b[43mcontext\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 303\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[32m 304\u001b[39m run_manager.on_chain_error(e)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/prebuilt/chat_agent_executor.py:656\u001b[39m, in \u001b[36mcreate_react_agent.<locals>.call_model\u001b[39m\u001b[34m(state, config)\u001b[39m\n\u001b[32m 655\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mcall_model\u001b[39m(state: AgentState, config: RunnableConfig) -> AgentState:\n\u001b[32m--> \u001b[39m\u001b[32m656\u001b[39m \u001b[43m_validate_chat_history\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstate\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmessages\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 657\u001b[39m response = cast(AIMessage, model_runnable.invoke(state, config))\n\u001b[32m 658\u001b[39m \u001b[38;5;66;03m# add agent name to the AIMessage\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/Study/langchain-tutorials/.venv/lib/python3.11/site-packages/langgraph/prebuilt/chat_agent_executor.py:241\u001b[39m, in \u001b[36m_validate_chat_history\u001b[39m\u001b[34m(messages)\u001b[39m\n\u001b[32m 232\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[32m 234\u001b[39m error_message = create_error_message(\n\u001b[32m 235\u001b[39m message=\u001b[33m\"\u001b[39m\u001b[33mFound AIMessages with tool_calls that do not have a corresponding ToolMessage. \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 236\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHere are the first few of those tool calls: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtool_calls_without_results[:\u001b[32m3\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m\"\u001b[39m\n\u001b[32m (...)\u001b[39m\u001b[32m 239\u001b[39m error_code=ErrorCode.INVALID_CHAT_HISTORY,\n\u001b[32m 240\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m241\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(error_message)\n",
"\u001b[31mValueError\u001b[39m: Found AIMessages with tool_calls that do not have a corresponding ToolMessage. Here are the first few of those tool calls: [{'name': 'add', 'args': {'a': 3, 'b': 5}, 'id': 'call_XwMw5vqUpuGHPVoxGiA4YVKd', 'type': 'tool_call'}].\n\nEvery tool call (LLM requesting to call a tool) in the message history MUST have a corresponding ToolMessage (result of a tool invocation to return to the LLM) - this is required by most LLM providers.\nFor troubleshooting, visit: https://python.langchain.com/docs/troubleshooting/errors/INVALID_CHAT_HISTORY",
"During task with name 'agent' and id '9f9bff4d-87bf-a53f-5a88-95895e36d7f1'",
"During task with name 'multiplication_expert' and id '971f50d2-d00e-c80c-c36e-cf1739a6e192'"
]
}
],
"source": [
"for chunk in graph.stream(\n",
" {\"messages\": [(\"user\", \"what's (3 + 5) * 12\")]}, subgraphs=True\n",
"):\n",
" pretty_print_messages(chunk)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment