Created
April 2, 2025 08:58
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "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