From 7dafc09a5bfc1b87efb97d28e9c65a6dbba7e805 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Wed, 10 Apr 2024 18:15:19 -0700 Subject: [PATCH 1/4] Add prebuilt ToolNode --- .../prebuilt-tool-node.ipynb | 622 ++++++++++++++++++ langgraph/prebuilt/__init__.py | 2 + langgraph/prebuilt/chat_agent_executor.py | 85 +-- langgraph/prebuilt/tool_node.py | 96 +++ poetry.lock | 24 +- pyproject.toml | 4 +- tests/__snapshots__/test_pregel.ambr | 38 +- tests/test_pregel.py | 617 +++++++++-------- tests/test_pregel_async.py | 211 +++--- 9 files changed, 1201 insertions(+), 498 deletions(-) create mode 100644 examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb create mode 100644 langgraph/prebuilt/tool_node.py diff --git a/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb new file mode 100644 index 000000000..00eafa2ef --- /dev/null +++ b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb @@ -0,0 +1,622 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53", + "metadata": {}, + "source": [ + "# Chat Agent Executor using prebuilt Tool Node\n", + "\n", + "\n", + "In this example we will build a chat executor that uses tool calling and the prebuilt ToolNode." + ] + }, + { + "cell_type": "markdown", + "id": "7cbd446a-808f-4394-be92-d45ab818953c", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First we need to install the packages required" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --quiet -U langchain langchain_openai tavily-python" + ] + }, + { + "cell_type": "markdown", + "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d", + "metadata": {}, + "source": [ + "Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import getpass\n", + "\n", + "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", + "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" + ] + }, + { + "cell_type": "markdown", + "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c", + "metadata": {}, + "source": [ + "Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3", + "metadata": {}, + "outputs": [], + "source": [ + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass(\"LangSmith API Key:\")" + ] + }, + { + "cell_type": "markdown", + "id": "21ac643b-cb06-4724-a80c-2862ba4773f1", + "metadata": {}, + "source": [ + "## Set up the tools\n", + "\n", + "We will first define the tools we want to use.\n", + "For this simple example, we will use a built-in search tool via Tavily.\n", + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n", + "\n", + "**MODIFICATION**\n", + "\n", + "We don't need a ToolExecutor when using ToolNode.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d7ef57dd-5d6e-4ad3-9377-a92201c1310e", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "\n", + "tools = [TavilySearchResults(max_results=1)]" + ] + }, + { + "cell_type": "markdown", + "id": "5497ed70-fce3-47f1-9cad-46f912bad6a5", + "metadata": {}, + "source": [ + "## Set up the model\n", + "\n", + "Now we need to load the chat model we want to use.\n", + "Importantly, this should satisfy two criteria:\n", + "\n", + "1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.\n", + "2. It should work with tool calling. This means it should be a model that implements `.bind_tools()`.\n", + "\n", + "Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "892b54b9-75f0-4804-9ed0-88b5e5532989", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_openai import ChatOpenAI\n", + "\n", + "model = ChatOpenAI(temperature=0)" + ] + }, + { + "cell_type": "markdown", + "id": "a77995c0-bae2-4cee-a036-8688a90f05b9", + "metadata": {}, + "source": [ + "\n", + "After we've done this, we should make sure the model knows that it has these tools available to call.\n", + "We can do this by converting the LangChain tools into the format for OpenAI function calling, and then bind them to the model class.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cd3cbae5-d92c-4559-a4aa-44721b80d107", + "metadata": {}, + "outputs": [], + "source": [ + "model = model.bind_tools(tools)" + ] + }, + { + "cell_type": "markdown", + "id": "8e8b9211-93d0-4ad5-aa7a-9c09099c53ff", + "metadata": {}, + "source": [ + "## Define the agent state\n", + "\n", + "The main type of graph in `langgraph` is the `StatefulGraph`.\n", + "This graph is parameterized by a state object that it passes around to each node.\n", + "Each node then returns operations to update that state.\n", + "These operations can either SET specific attributes on the state (e.g. overwrite the existing values) or ADD to the existing attribute.\n", + "Whether to set or add is denoted by annotating the state object you construct the graph with.\n", + "\n", + "For this example, the state we will track will just be a list of messages.\n", + "We want each node to just add messages to that list.\n", + "Therefore, we will use a `TypedDict` with one key (`messages`) and annotate it so that the `messages` attribute is always added to.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ea793afa-2eab-4901-910d-6eed90cd6564", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import TypedDict, Annotated, Sequence\n", + "import operator\n", + "from langchain_core.messages import BaseMessage\n", + "\n", + "\n", + "class AgentState(TypedDict):\n", + " messages: Annotated[Sequence[BaseMessage], operator.add]" + ] + }, + { + "cell_type": "markdown", + "id": "e03c5094-9297-4d19-a04e-3eedc75cefb4", + "metadata": {}, + "source": [ + "## Define the nodes\n", + "\n", + "We now need to define a few different nodes in our graph.\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "There are two main nodes we need for this:\n", + "\n", + "1. The agent: responsible for deciding what (if any) actions to take.\n", + "2. **MODIFICATION** The prebuilt ToolNode, given the list of tools. This will take tool calls from the most recent AIMessage, execute them, and return the result as ToolMessages.\n", + "\n", + "We will also need to define some edges.\n", + "Some of these edges may be conditional.\n", + "The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n", + "The path that is taken is not known until that node is run (the LLM decides).\n", + "\n", + "1. Conditional Edge: after the agent is called, we should either:\n", + " a. If the agent said to take an action, then the function to invoke tools should be called\n", + " b. If the agent said that it was finished, then it should finish\n", + "2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n", + "\n", + "Let's define the nodes, as well as a function to decide how what conditional edge to take.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3b541bb9-900c-40d0-964d-7b5dfee30667", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.prebuilt import ToolNode\n", + "import json\n", + "from langchain_core.messages import FunctionMessage\n", + "\n", + "\n", + "# Define the function that determines whether to continue or not\n", + "def should_continue(state):\n", + " messages = state[\"messages\"]\n", + " last_message = messages[-1]\n", + " # If there are no tool calls, then we finish\n", + " if not last_message.tool_calls:\n", + " return \"end\"\n", + " # Otherwise if there is, we continue\n", + " else:\n", + " return \"continue\"\n", + "\n", + "\n", + "# Define the function that calls the model\n", + "def call_model(state):\n", + " messages = state[\"messages\"]\n", + " response = model.invoke(messages)\n", + " # We return a list, because this will get added to the existing list\n", + " return {\"messages\": [response]}\n", + "\n", + "\n", + "# Define the function to execute tools\n", + "tool_node = ToolNode(tools)" + ] + }, + { + "cell_type": "markdown", + "id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b", + "metadata": {}, + "source": [ + "## Define the graph\n", + "\n", + "We can now put it all together and define the graph!" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "813ae66c-3b58-4283-a02a-36da72a2ab90", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import StateGraph, END\n", + "\n", + "# Define a new graph\n", + "workflow = StateGraph(AgentState)\n", + "\n", + "# Define the two nodes we will cycle between\n", + "workflow.add_node(\"agent\", call_model)\n", + "workflow.add_node(\"action\", tool_node)\n", + "\n", + "# Set the entrypoint as `agent`\n", + "# This means that this node is the first one called\n", + "workflow.set_entry_point(\"agent\")\n", + "\n", + "# We now add a conditional edge\n", + "workflow.add_conditional_edges(\n", + " # First, we define the start node. We use `agent`.\n", + " # This means these are the edges taken after the `agent` node is called.\n", + " \"agent\",\n", + " # Next, we pass in the function that will determine which node is called next.\n", + " should_continue,\n", + " # Finally we pass in a mapping.\n", + " # The keys are strings, and the values are other nodes.\n", + " # END is a special node marking that the graph should finish.\n", + " # What will happen is we will call `should_continue`, and then the output of that\n", + " # will be matched against the keys in this mapping.\n", + " # Based on which one it matches, that node will then be called.\n", + " {\n", + " # If `tools`, then we call the tool node.\n", + " \"continue\": \"action\",\n", + " # Otherwise we finish.\n", + " \"end\": END,\n", + " },\n", + ")\n", + "\n", + "# We now add a normal edge from `tools` to `agent`.\n", + "# This means that after `tools` is called, `agent` node is called next.\n", + "workflow.add_edge(\"action\", \"agent\")\n", + "\n", + "# Finally, we compile it!\n", + "# This compiles it into a LangChain Runnable,\n", + "# meaning you can use it as you would any other runnable\n", + "app = workflow.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "547c3931-3dae-4281-ad4e-4b51305594d4", + "metadata": {}, + "source": [ + "## Use it!\n", + "\n", + "We can now use it!\n", + "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "8edb04b9-40b6-46f1-a7a8-4b2d8aba7752", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages/langchain_core/messages/ai.py:52: UserWarning: New langchain packages are available that more efficiently handle tool calling. Please upgrade your packages to versions that set message tool calls. e.g., `pip install --upgrade langchain-anthropic`, pip install--upgrade langchain-openai`, etc.\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "text/plain": [ + "{'messages': [HumanMessage(content='what is the weather in sf'),\n", + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Q7l0TopyJxaly7xM9Vq2aGxO', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 87, 'total_tokens': 108}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-d39a73db-e37a-46ce-9476-73fe9eb84b2e-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_Q7l0TopyJxaly7xM9Vq2aGxO'}]),\n", + " ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712797953, \\'localtime\\': \\'2024-04-10 18:12\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712797200, \\'last_updated\\': \\'2024-04-10 18:00\\', \\'temp_c\\': 21.1, \\'temp_f\\': 70.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 10.5, \\'wind_kph\\': 16.9, \\'wind_degree\\': 300, \\'wind_dir\\': \\'WNW\\', \\'pressure_mb\\': 1017.0, \\'pressure_in\\': 30.04, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 59, \\'cloud\\': 75, \\'feelslike_c\\': 21.1, \\'feelslike_f\\': 70.0, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 5.0, \\'gust_mph\\': 16.0, \\'gust_kph\\': 25.8}}\"}]', name='tavily_search_results_json', tool_call_id='call_Q7l0TopyJxaly7xM9Vq2aGxO'),\n", + " AIMessage(content='The current weather in San Francisco is partly cloudy with a temperature of 21.1°C (70.0°F). The wind speed is 10.5 mph (16.9 kph) coming from the west-northwest direction. The humidity is at 59% with a visibility of 16.0 km (9.0 miles).', response_metadata={'token_usage': {'completion_tokens': 72, 'prompt_tokens': 466, 'total_tokens': 538}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-091dcb4c-9424-40fc-aab6-58968d9929a2-0')]}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.messages import HumanMessage\n", + "\n", + "inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n", + "app.invoke(inputs)" + ] + }, + { + "cell_type": "markdown", + "id": "5a9e8155-70c5-4973-912c-dc55104b2acf", + "metadata": {}, + "source": [ + "This may take a little bit - it's making a few calls behind the scenes.\n", + "In order to start seeing some intermediate results as they happen, we can use streaming - see below for more information on that.\n", + "\n", + "## Streaming\n", + "\n", + "LangGraph has support for several different types of streaming.\n", + "\n", + "### Streaming Node Output\n", + "\n", + "One of the benefits of using LangGraph is that it is easy to stream output as it's produced by each node.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f544977e-31f7-41f0-88c4-ec9c27b8cecb", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages/langchain_core/messages/ai.py:52: UserWarning: New langchain packages are available that more efficiently handle tool calling. Please upgrade your packages to versions that set message tool calls. e.g., `pip install --upgrade langchain-anthropic`, pip install--upgrade langchain-openai`, etc.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Output from node 'agent':\n", + "---\n", + "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_VgzsFE5Cf3sdlrIudeijqVsp', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 87, 'total_tokens': 108}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b3acca13-ccbf-4761-9d0c-1410d5f4f0a9-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_VgzsFE5Cf3sdlrIudeijqVsp'}])]}\n", + "\n", + "---\n", + "\n", + "Output from node 'action':\n", + "---\n", + "{'messages': [ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712797953, \\'localtime\\': \\'2024-04-10 18:12\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712797200, \\'last_updated\\': \\'2024-04-10 18:00\\', \\'temp_c\\': 21.1, \\'temp_f\\': 70.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 10.5, \\'wind_kph\\': 16.9, \\'wind_degree\\': 300, \\'wind_dir\\': \\'WNW\\', \\'pressure_mb\\': 1017.0, \\'pressure_in\\': 30.04, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 59, \\'cloud\\': 75, \\'feelslike_c\\': 21.1, \\'feelslike_f\\': 70.0, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 5.0, \\'gust_mph\\': 16.0, \\'gust_kph\\': 25.8}}\"}]', name='tavily_search_results_json', tool_call_id='call_VgzsFE5Cf3sdlrIudeijqVsp')]}\n", + "\n", + "---\n", + "\n", + "Output from node 'agent':\n", + "---\n", + "{'messages': [AIMessage(content='The current weather in San Francisco is partly cloudy with a temperature of 70°F (21.1°C). The wind speed is 10.5 mph (16.9 kph) coming from the west-northwest direction. The humidity is at 59%, and the visibility is 9.0 miles.', response_metadata={'token_usage': {'completion_tokens': 65, 'prompt_tokens': 466, 'total_tokens': 531}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-fa95b6d0-34bb-41e7-8cfd-87b6af69064f-0')]}\n", + "\n", + "---\n", + "\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n", + "for output in app.stream(inputs):\n", + " # stream() yields dictionaries with output keyed by node name\n", + " for key, value in output.items():\n", + " print(f\"Output from node '{key}':\")\n", + " print(\"---\")\n", + " print(value)\n", + " print(\"\\n---\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "2a1b56c5-bd61-4192-8bdb-458a1e9f0159", + "metadata": {}, + "source": [ + "### Streaming LLM Tokens\n", + "\n", + "You can also access the LLM tokens as they are produced by each node. \n", + "In this case only the \"agent\" node produces LLM tokens.\n", + "In order for this to work properly, you must be using an LLM that supports streaming as well as have set it when constructing the LLM (e.g. `ChatOpenAI(model=\"gpt-3.5-turbo-1106\", streaming=True)`)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "cfd140f0-a5a6-4697-8115-322242f197b5", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages/langchain_core/messages/ai.py:52: UserWarning: New langchain packages are available that more efficiently handle tool calling. Please upgrade your packages to versions that set message tool calls. e.g., `pip install --upgrade langchain-anthropic`, pip install--upgrade langchain-openai`, etc.\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_TyyKAi2L0Uymjr3YW2d3ZnIm', 'function': {'arguments': '', 'name': 'tavily_search_results_json'}, 'type': 'function'}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': 'tavily_search_results_json', 'args': '', 'id': 'call_TyyKAi2L0Uymjr3YW2d3ZnIm', 'error': 'Malformed args.'}] tool_call_chunks=[{'name': 'tavily_search_results_json', 'args': '', 'id': 'call_TyyKAi2L0Uymjr3YW2d3ZnIm', 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '{\"', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' tool_calls=[{'name': '', 'args': {}, 'id': None}] tool_call_chunks=[{'name': None, 'args': '{\"', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': 'query', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': 'query', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': 'query', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '\":\"', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': '\":\"', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': '\":\"', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': 'weather', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': 'weather', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': 'weather', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' in', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': ' in', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' in', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' San', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': ' San', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' San', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' Francisco', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': ' Francisco', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' Francisco', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '\"}', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': '\"}', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': '\"}', 'id': None, 'index': 0}]\n", + "content='' response_metadata={'finish_reason': 'tool_calls'} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1'\n", + "content='' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='The' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' current' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' weather' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' in' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' San' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Francisco' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' is' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' as' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' follows' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Temperature' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='21' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='1' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='°C' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' (' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='70' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='°F' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=')\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Condition' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Part' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='ly' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' cloudy' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Wind' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='10' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='5' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' mph' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' from' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' W' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='NW' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Pressure' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='101' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='7' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' mb' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Hum' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='idity' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='59' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='%\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Cloud' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Cover' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='75' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='%\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Visibility' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='16' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' km' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' (' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='9' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' miles' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=')\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' UV' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' Index' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='5' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='\\n\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='For' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' more' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' details' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=',' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' you' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' can' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' visit' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' [' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='Weather' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=' API' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='](' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='https' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='://' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='www' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.weather' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='api' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='.com' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='/' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content=').' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", + "content='' response_metadata={'finish_reason': 'stop'} id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf?\")]}\n", + "\n", + "async for output in app.astream_log(inputs, include_types=[\"llm\"]):\n", + " # astream_log() yields the requested logs (here LLMs) in JSONPatch format\n", + " for op in output.ops:\n", + " if op[\"path\"] == \"/streamed_output/-\":\n", + " # this is the output from .stream()\n", + " ...\n", + " elif op[\"path\"].startswith(\"/logs/\") and op[\"path\"].endswith(\n", + " \"/streamed_output/-\"\n", + " ):\n", + " # because we chose to only include LLMs, these are LLM tokens\n", + " print(op[\"value\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08ae8246-11d5-40e1-8567-361e5bef8917", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/langgraph/prebuilt/__init__.py b/langgraph/prebuilt/__init__.py index db31f778b..4ffffd2cd 100644 --- a/langgraph/prebuilt/__init__.py +++ b/langgraph/prebuilt/__init__.py @@ -1,10 +1,12 @@ from langgraph.prebuilt import chat_agent_executor from langgraph.prebuilt.agent_executor import create_agent_executor from langgraph.prebuilt.tool_executor import ToolExecutor, ToolInvocation +from langgraph.prebuilt.tool_node import ToolNode __all__ = [ "create_agent_executor", "chat_agent_executor", "ToolExecutor", "ToolInvocation", + "ToolNode", ] diff --git a/langgraph/prebuilt/chat_agent_executor.py b/langgraph/prebuilt/chat_agent_executor.py index fcd4d6806..473d46cad 100644 --- a/langgraph/prebuilt/chat_agent_executor.py +++ b/langgraph/prebuilt/chat_agent_executor.py @@ -1,18 +1,24 @@ import json -import operator from typing import Annotated, Sequence, TypedDict, Union from langchain_core.language_models import LanguageModelLike -from langchain_core.messages import BaseMessage, FunctionMessage, ToolMessage +from langchain_core.messages import BaseMessage, FunctionMessage from langchain_core.runnables import RunnableLambda from langchain_core.tools import BaseTool -from langchain_core.utils.function_calling import ( - convert_to_openai_function, - convert_to_openai_tool, -) +from langchain_core.utils.function_calling import convert_to_openai_function from langgraph.graph import END, StateGraph +from langgraph.graph.message import add_messages from langgraph.prebuilt.tool_executor import ToolExecutor, ToolInvocation +from langgraph.prebuilt.tool_node import ToolNode + + +# We create the AgentState that we will pass around +# This simply involves a list of messages +# We want steps to return messages to append to the list +# So we annotate the messages attribute with operator.add +class AgentState(TypedDict): + messages: Annotated[Sequence[BaseMessage], add_messages] def create_function_calling_executor( @@ -26,13 +32,6 @@ def create_function_calling_executor( tool_classes = tools model = model.bind(functions=[convert_to_openai_function(t) for t in tool_classes]) - # We create the AgentState that we will pass around - # This simply involves a list of messages - # We want steps to return messages to append to the list - # So we annotate the messages attribute with operator.add - class AgentState(TypedDict): - messages: Annotated[Sequence[BaseMessage], operator.add] - # Define the function that determines whether to continue or not def should_continue(state: AgentState): messages = state["messages"] @@ -135,26 +134,17 @@ def create_tool_calling_executor( model: LanguageModelLike, tools: Union[ToolExecutor, Sequence[BaseTool]] ): if isinstance(tools, ToolExecutor): - tool_executor = tools tool_classes = tools.tools else: - tool_executor = ToolExecutor(tools) tool_classes = tools - model = model.bind(tools=[convert_to_openai_tool(t) for t in tool_classes]) - - # We create the AgentState that we will pass around - # This simply involves a list of messages - # We want steps to return messages to append to the list - # So we annotate the messages attribute with operator.add - class AgentState(TypedDict): - messages: Annotated[Sequence[BaseMessage], operator.add] + model = model.bind_tools(tool_classes) # Define the function that determines whether to continue or not def should_continue(state: AgentState): messages = state["messages"] last_message = messages[-1] # If there is no function call, then we finish - if "tool_calls" not in last_message.additional_kwargs: + if not last_message.tool_calls: return "end" # Otherwise if there is, we continue else: @@ -173,57 +163,12 @@ def create_tool_calling_executor( # We return a list, because this will get added to the existing list return {"messages": [response]} - # Define the function to execute tools - def _get_actions(state: AgentState): - messages = state["messages"] - # Based on the continue condition - # we know the last message involves a tool call - last_message = messages[-1] - # We construct an AgentAction from each of the tool_calls - return ( - [ - ToolInvocation( - tool=tool_call["function"]["name"], - tool_input=json.loads(tool_call["function"]["arguments"]), - ) - for tool_call in last_message.additional_kwargs["tool_calls"] - ], - [ - tool_call["id"] - for tool_call in last_message.additional_kwargs["tool_calls"] - ], - ) - - def call_tool(state: AgentState): - actions, ids = _get_actions(state) - # We call the tool_executor and get back a response - responses = tool_executor.batch(actions) - # We use the response to create a FunctionMessage - tool_messages = [ - ToolMessage(content=str(response), tool_call_id=id) - for response, id in zip(responses, ids) - ] - # We return a list, because this will get added to the existing list - return {"messages": tool_messages} - - async def acall_tool(state: AgentState): - actions, ids = _get_actions(state) - # We call the tool_executor and get back a response - responses = await tool_executor.abatch(actions) - # We use the response to create a FunctionMessage - tool_messages = [ - ToolMessage(content=str(response), tool_call_id=id) - for response, id in zip(responses, ids) - ] - # We return a list, because this will get added to the existing list - return {"messages": tool_messages} - # Define a new graph workflow = StateGraph(AgentState) # Define the two nodes we will cycle between workflow.add_node("agent", RunnableLambda(call_model, acall_model)) - workflow.add_node("action", RunnableLambda(call_tool, acall_tool)) + workflow.add_node("action", ToolNode(tools)) # Set the entrypoint as `agent` # This means that this node is the first one called diff --git a/langgraph/prebuilt/tool_node.py b/langgraph/prebuilt/tool_node.py new file mode 100644 index 000000000..b0d2da8be --- /dev/null +++ b/langgraph/prebuilt/tool_node.py @@ -0,0 +1,96 @@ +import asyncio +import json +from typing import Any, Sequence, Union + +from langchain_core.messages import AIMessage, AnyMessage, ToolCall, ToolMessage +from langchain_core.runnables import RunnableConfig +from langchain_core.runnables.config import get_executor_for_config +from langchain_core.tools import BaseTool + +from langgraph.utils import RunnableCallable + + +def str_output(output: Any) -> str: + if isinstance(output, str): + return output + else: + try: + return json.dumps(output) + except Exception: + return str(output) + + +class ToolNode(RunnableCallable): + """ + A node that runs the tols requested in the last AIMessage. It can be used + either in StateGraph with a "messages" key or in MessageGraph. If multiple + tool calls are requested, they will be run in parallel. The output will be + a list of ToolMessages, one for each tool call. + """ + + def __init__( + self, + tools: Sequence[BaseTool], + *, + name: str = "tools", + tags: list[str] | None = None, + ) -> None: + super().__init__(self._func, self._afunc, name=name, tags=tags, trace=False) + self.tools_by_name = {tool.name: tool for tool in tools} + + def _func( + self, input: Union[list[AnyMessage], dict[str, Any]], config: RunnableConfig + ) -> Any: + if isinstance(input, list): + output_type = "list" + message: AnyMessage = input[-1] + elif messages := input.get("messages", []): + output_type = "dict" + message = messages[-1] + else: + raise ValueError("No message found in input") + + if not isinstance(message, AIMessage): + raise ValueError("Last message is not an AIMessage") + + def run_one(call: ToolCall): + output = self.tools_by_name[call["name"]].invoke(call["args"], config) + return ToolMessage( + content=str_output(output), name=call["name"], tool_call_id=call["id"] + ) + + with get_executor_for_config(config) as executor: + outputs = [*executor.map(run_one, message.tool_calls)] + if output_type == "list": + return outputs + else: + return {"messages": outputs} + + async def _afunc( + self, input: Union[list[AnyMessage], dict[str, Any]], config: RunnableConfig + ) -> Any: + if isinstance(input, list): + output_type = "list" + message: AnyMessage = input[-1] + elif messages := input.get("messages", []): + output_type = "dict" + message = messages[-1] + else: + raise ValueError("No message found in input") + + if not isinstance(message, AIMessage): + raise ValueError("Last message is not an AIMessage") + + async def run_one(call: ToolCall): + output = await self.tools_by_name[call["name"]].ainvoke( + call["args"], config + ) + return ToolMessage( + content=str_output(output), name=call["name"], tool_call_id=call["id"] + ) + + outputs = await asyncio.gather(*(run_one(call) for call in message.tool_calls)) + if output_type == "list": + return outputs + else: + return {"messages": outputs} diff --git a/poetry.lock b/poetry.lock index 7e209a01b..d7bc341f6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1587,13 +1587,13 @@ extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15. [[package]] name = "langchain-core" -version = "0.1.38" +version = "0.1.42rc1" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.1.38-py3-none-any.whl", hash = "sha256:d881b2754254cb4bdb0d5bb56e5c138d032b6e75e5cb21f151b01224b322e02b"}, - {file = "langchain_core-0.1.38.tar.gz", hash = "sha256:ee8da6d061c06cce7dc22fec224b6ecbc3a8de106d6dd9f409c7fe448ea41861"}, + {file = "langchain_core-0.1.42rc1-py3-none-any.whl", hash = "sha256:2b216652f61b915ae274d1228ad45e7fc99af1d5f41bb6900aafd0636e66def5"}, + {file = "langchain_core-0.1.42rc1.tar.gz", hash = "sha256:af75525f31251d8d2889671b6051a0e2afffd355b5efefb1c59fc91545805ab8"}, ] [package.dependencies] @@ -1602,7 +1602,6 @@ langsmith = ">=0.1.0,<0.2.0" packaging = ">=23.2,<24.0" pydantic = ">=1,<3" PyYAML = ">=5.3" -requests = ">=2,<3" tenacity = ">=8.1.0,<9.0.0" [package.extras] @@ -1610,20 +1609,19 @@ extended-testing = ["jinja2 (>=3,<4)"] [[package]] name = "langchain-openai" -version = "0.0.2.post1" +version = "0.1.2" description = "An integration package connecting OpenAI and LangChain" optional = false -python-versions = ">=3.8.1,<4.0" +python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_openai-0.0.2.post1-py3-none-any.whl", hash = "sha256:ba468b94c23da9d8ccefe5d5a3c1c65b4b9702292523e53acc689a9110022e26"}, - {file = "langchain_openai-0.0.2.post1.tar.gz", hash = "sha256:f8e78db4a663feeac71d9f036b9422406c199ea3ef4c97d99ff392c93530e073"}, + {file = "langchain_openai-0.1.2-py3-none-any.whl", hash = "sha256:45fab91803df22c6d5fce7c010df404569898372df5ae8cd03af50bef774d2ec"}, + {file = "langchain_openai-0.1.2.tar.gz", hash = "sha256:cd391e61bd93ab72ae24d8e1f250257d6acff6d9e455e623363b8c171533050a"}, ] [package.dependencies] -langchain-core = ">=0.1.7,<0.2" -numpy = ">=1,<2" -openai = ">=1.6.1,<2.0.0" -tiktoken = ">=0.5.2,<0.6.0" +langchain-core = ">=0.1.41,<0.2.0" +openai = ">=1.10.0,<2.0.0" +tiktoken = ">=0.5.2,<1" [[package]] name = "langchain-text-splitters" @@ -3859,4 +3857,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9.0,<4.0" -content-hash = "3f31fdccb53a66dc294a53d63bcef0233f38c9909170113c44b8dc215fc77d7c" +content-hash = "a3e232b85db7332e70c88b6715a0b5716bcb424fe7e8ac7cb65057a8b93b39a4" diff --git a/pyproject.toml b/pyproject.toml index d30d581b6..54e5d1245 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ repository = "https://www.github.com/langchain-ai/langgraph" [tool.poetry.dependencies] python = ">=3.9.0,<4.0" -langchain-core = "^0.1.38" +langchain-core = "0.1.42rc1" [tool.poetry.group.test.dependencies] @@ -41,7 +41,7 @@ optional = true jupyter = "^1.0.0" langchain = "^0.1.0" langchainhub = "^0.1.14" -langchain-openai = "^0.0.2" +langchain-openai = "^0.1.2" [tool.ruff] select = [ "E", "F", "I" ] diff --git a/tests/__snapshots__/test_pregel.ambr b/tests/__snapshots__/test_pregel.ambr index aec89a615..79d9337f2 100644 --- a/tests/__snapshots__/test_pregel.ambr +++ b/tests/__snapshots__/test_pregel.ambr @@ -1146,10 +1146,10 @@ ''' # --- # name: test_message_graph[end_of_run] - '{"title": "LangGraphInput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' + '{"title": "LangGraphInput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' # --- # name: test_message_graph[end_of_run].1 - '{"title": "LangGraphOutput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' + '{"title": "LangGraphOutput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' # --- # name: test_message_graph[end_of_run].2 ''' @@ -1182,12 +1182,12 @@ "type": "runnable", "data": { "id": [ - "langchain_core", - "runnables", - "base", - "RunnableLambda" + "langgraph", + "prebuilt", + "tool_node", + "ToolNode" ], - "name": "call_tool" + "name": "tools" } }, { @@ -1257,10 +1257,10 @@ ''' # --- # name: test_message_graph[end_of_step] - '{"title": "LangGraphInput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' + '{"title": "LangGraphInput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' # --- # name: test_message_graph[end_of_step].1 - '{"title": "LangGraphOutput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' + '{"title": "LangGraphOutput", "type": "array", "items": {"anyOf": [{"$ref": "#/definitions/AIMessage"}, {"$ref": "#/definitions/HumanMessage"}, {"$ref": "#/definitions/ChatMessage"}, {"$ref": "#/definitions/SystemMessage"}, {"$ref": "#/definitions/FunctionMessage"}, {"$ref": "#/definitions/ToolMessage"}]}, "definitions": {"ToolCall": {"title": "ToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "object"}, "id": {"title": "Id", "type": "string"}}, "required": ["name", "args", "id"]}, "InvalidToolCall": {"title": "InvalidToolCall", "type": "object", "properties": {"name": {"title": "Name", "type": "string"}, "args": {"title": "Args", "type": "string"}, "id": {"title": "Id", "type": "string"}, "error": {"title": "Error", "type": "string"}}, "required": ["name", "args", "id", "error"]}, "AIMessage": {"title": "AIMessage", "description": "Message from an AI.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "ai", "enum": ["ai"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}, "tool_calls": {"title": "Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/ToolCall"}}, "invalid_tool_calls": {"title": "Invalid Tool Calls", "default": [], "type": "array", "items": {"$ref": "#/definitions/InvalidToolCall"}}}, "required": ["content"]}, "HumanMessage": {"title": "HumanMessage", "description": "Message from a human.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "human", "enum": ["human"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "example": {"title": "Example", "default": false, "type": "boolean"}}, "required": ["content"]}, "ChatMessage": {"title": "ChatMessage", "description": "Message that can be assigned an arbitrary speaker (i.e. role).", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "chat", "enum": ["chat"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "role": {"title": "Role", "type": "string"}}, "required": ["content", "role"]}, "SystemMessage": {"title": "SystemMessage", "description": "Message for priming AI behavior, usually passed in as the first of a sequence\\nof input messages.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "system", "enum": ["system"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content"]}, "FunctionMessage": {"title": "FunctionMessage", "description": "Message for passing the result of executing a function back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "function", "enum": ["function"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}}, "required": ["content", "name"]}, "ToolMessage": {"title": "ToolMessage", "description": "Message for passing the result of executing a tool back to a model.", "type": "object", "properties": {"content": {"title": "Content", "anyOf": [{"type": "string"}, {"type": "array", "items": {"anyOf": [{"type": "string"}, {"type": "object"}]}}]}, "additional_kwargs": {"title": "Additional Kwargs", "type": "object"}, "response_metadata": {"title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "default": "tool", "enum": ["tool"], "type": "string"}, "name": {"title": "Name", "type": "string"}, "id": {"title": "Id", "type": "string"}, "tool_call_id": {"title": "Tool Call Id", "type": "string"}}, "required": ["content", "tool_call_id"]}}}' # --- # name: test_message_graph[end_of_step].2 ''' @@ -1293,12 +1293,12 @@ "type": "runnable", "data": { "id": [ - "langchain_core", - "runnables", - "base", - "RunnableLambda" + "langgraph", + "prebuilt", + "tool_node", + "ToolNode" ], - "name": "call_tool" + "name": "tools" } }, { @@ -1517,12 +1517,12 @@ "type": "runnable", "data": { "id": [ - "langchain_core", - "runnables", - "base", - "RunnableLambda" + "langgraph", + "prebuilt", + "tool_node", + "ToolNode" ], - "name": "call_tool" + "name": "tools" } }, { diff --git a/tests/test_pregel.py b/tests/test_pregel.py index 6be844479..16288f9d2 100644 --- a/tests/test_pregel.py +++ b/tests/test_pregel.py @@ -25,7 +25,7 @@ from langgraph.prebuilt.chat_agent_executor import ( create_function_calling_executor, create_tool_calling_executor, ) -from langgraph.prebuilt.tool_executor import ToolExecutor +from langgraph.prebuilt.tool_node import ToolNode from langgraph.pregel import Channel, GraphRecursionError, Pregel, StateSnapshot from tests.any_str import AnyStr from tests.memory_assert import MemorySaverAssertImmutable @@ -1825,7 +1825,7 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: from langchain_core.messages import AIMessage, HumanMessage, ToolMessage class FakeFuntionChatModel(FakeMessagesListChatModel): - def bind_functions(self, functions: list): + def bind_tools(self, functions: list): return self @tool() @@ -1840,41 +1840,28 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: responses=[ AIMessage( content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call123", - "type": "function", - "function": { - "name": "search_api", - "arguments": json.dumps("query"), - }, - } - ] - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + }, + ], ), AIMessage( content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call234", - "type": "function", - "function": { - "name": "search_api", - "arguments": json.dumps("another"), - }, - }, - { - "id": "tool_call567", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"a third one"', - }, - }, - ] - }, + tool_calls=[ + { + "id": "tool_call234", + "name": "search_api", + "args": {"query": "another"}, + }, + { + "id": "tool_call567", + "name": "search_api", + "args": {"query": "a third one"}, + }, + ], ), AIMessage(content="answer"), ] @@ -1891,50 +1878,52 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: {"messages": [HumanMessage(content="what is weather in sf")]} ) == { "messages": [ - HumanMessage(content="what is weather in sf"), + HumanMessage(content="what is weather in sf", id=AnyStr()), AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call123", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"query"', - }, - } - ] - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + }, + ], + ), + ToolMessage( + content="result for query", + name="search_api", + tool_call_id="tool_call123", + id=AnyStr(), ), - ToolMessage(content="result for query", tool_call_id="tool_call123"), AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call234", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"another"', - }, - }, - { - "id": "tool_call567", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"a third one"', - }, - }, - ] - }, + tool_calls=[ + { + "id": "tool_call234", + "name": "search_api", + "args": {"query": "another"}, + }, + { + "id": "tool_call567", + "name": "search_api", + "args": {"query": "a third one"}, + }, + ], + ), + ToolMessage( + content="result for another", + name="search_api", + tool_call_id="tool_call234", + id=AnyStr(), + ), + ToolMessage( + content="result for a third one", + name="search_api", + tool_call_id="tool_call567", + id=AnyStr(), ), - ToolMessage(content="result for another", tool_call_id="tool_call234"), - ToolMessage(content="result for a third one", tool_call_id="tool_call567"), AIMessage(content="answer", id=AnyStr()), ] } @@ -1948,18 +1937,13 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: "messages": [ AIMessage( content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call123", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"query"', - }, - } - ] - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + }, + ], id=AnyStr(), ) ] @@ -1968,7 +1952,12 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: { "action": { "messages": [ - ToolMessage(content="result for query", tool_call_id="tool_call123") + ToolMessage( + content="result for query", + name="search_api", + tool_call_id="tool_call123", + id=AnyStr(), + ) ] } }, @@ -1977,26 +1966,18 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: "messages": [ AIMessage( content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call234", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"another"', - }, - }, - { - "id": "tool_call567", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"a third one"', - }, - }, - ] - }, + tool_calls=[ + { + "id": "tool_call234", + "name": "search_api", + "args": {"query": "another"}, + }, + { + "id": "tool_call567", + "name": "search_api", + "args": {"query": "a third one"}, + }, + ], id=AnyStr(), ) ] @@ -2006,10 +1987,16 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: "action": { "messages": [ ToolMessage( - content="result for another", tool_call_id="tool_call234" + content="result for another", + name="search_api", + tool_call_id="tool_call234", + id=AnyStr(), ), ToolMessage( - content="result for a third one", tool_call_id="tool_call567" + content="result for a third one", + name="search_api", + tool_call_id="tool_call567", + id=AnyStr(), ), ] } @@ -2035,18 +2022,13 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call123", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"query"', - }, - } - ] - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + }, + ], ) ] } @@ -2054,7 +2036,12 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: { "action": { "messages": [ - ToolMessage(content="result for query", tool_call_id="tool_call123") + ToolMessage( + content="result for query", + name="search_api", + tool_call_id="tool_call123", + id=AnyStr(), + ) ] } }, @@ -2064,26 +2051,18 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call234", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"another"', - }, - }, - { - "id": "tool_call567", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"a third one"', - }, - }, - ] - }, + tool_calls=[ + { + "id": "tool_call234", + "name": "search_api", + "args": {"query": "another"}, + }, + { + "id": "tool_call567", + "name": "search_api", + "args": {"query": "a third one"}, + }, + ], ) ] } @@ -2092,10 +2071,16 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None: "action": { "messages": [ ToolMessage( - content="result for another", tool_call_id="tool_call234" + content="result for another", + name="search_api", + tool_call_id="tool_call234", + id=AnyStr(), ), ToolMessage( - content="result for a third one", tool_call_id="tool_call567" + content="result for a third one", + name="search_api", + tool_call_id="tool_call567", + id=AnyStr(), ), ] } @@ -2156,7 +2141,7 @@ def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None: {"messages": [HumanMessage(content="what is weather in sf")]} ) == { "messages": [ - HumanMessage(content="what is weather in sf"), + HumanMessage(content="what is weather in sf", id=AnyStr()), AIMessage( id=AnyStr(), content="", @@ -2164,7 +2149,7 @@ def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None: "function_call": {"name": "search_api", "arguments": '"query"'} }, ), - FunctionMessage(content="result for query", name="search_api"), + FunctionMessage(content="result for query", name="search_api", id=AnyStr()), AIMessage( id=AnyStr(), content="", @@ -2172,7 +2157,9 @@ def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None: "function_call": {"name": "search_api", "arguments": '"another"'} }, ), - FunctionMessage(content="result for another", name="search_api"), + FunctionMessage( + content="result for another", name="search_api", id=AnyStr() + ), AIMessage(content="answer", id=AnyStr()), ] } @@ -2199,7 +2186,9 @@ def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None: { "action": { "messages": [ - FunctionMessage(content="result for query", name="search_api") + FunctionMessage( + content="result for query", name="search_api", id=AnyStr() + ) ] } }, @@ -2222,7 +2211,9 @@ def test_prebuilt_chat(snapshot: SnapshotAssertion) -> None: { "action": { "messages": [ - FunctionMessage(content="result for another", name="search_api") + FunctionMessage( + content="result for another", name="search_api", id=AnyStr() + ) ] } }, @@ -2242,13 +2233,12 @@ def test_message_graph( from langchain.chat_models.fake import FakeMessagesListChatModel from langchain_community.tools import tool - from langchain_core.agents import AgentAction from langchain_core.callbacks import CallbackManagerForLLMRun from langchain_core.messages import ( AIMessage, BaseMessage, - FunctionMessage, HumanMessage, + ToolMessage, ) from langchain_core.outputs import ChatGeneration, ChatResult @@ -2282,63 +2272,46 @@ def test_message_graph( responses=[ AIMessage( content="", - additional_kwargs={ - "function_call": { + tool_calls=[ + { + "id": "tool_call123", "name": "search_api", - "arguments": json.dumps("query"), + "args": {"query": "query"}, } - }, + ], id="ai1", ), AIMessage( content="", - additional_kwargs={ - "function_call": { + tool_calls=[ + { + "id": "tool_call456", "name": "search_api", - "arguments": json.dumps("another"), + "args": {"query": "another"}, } - }, + ], id="ai2", ), AIMessage(content="answer", id="ai3"), ] ) - tool_executor = ToolExecutor(tools) - # Define the function that determines whether to continue or not def should_continue(messages): last_message = messages[-1] # If there is no function call, then we finish - if "function_call" not in last_message.additional_kwargs: + if not last_message.tool_calls: return "end" # Otherwise if there is, we continue else: return "continue" - def call_tool(messages): - # Based on the continue condition - # we know the last message involves a function call - last_message = messages[-1] - # We construct an AgentAction from the function_call - action = AgentAction( - tool=last_message.additional_kwargs["function_call"]["name"], - tool_input=json.loads( - last_message.additional_kwargs["function_call"]["arguments"] - ), - log="", - ) - # We call the tool_executor and get back a response - response = tool_executor.invoke(action) - # We use the response to create a FunctionMessage - return FunctionMessage(content=str(response), name=action.tool) - # Define a new graph workflow = MessageGraph() # Define the two nodes we will cycle between workflow.add_node("agent", model) - workflow.add_node("action", call_tool) + workflow.add_node("action", ToolNode(tools)) # Set the entrypoint as `agent` # This means that this node is the first one called @@ -2386,27 +2359,37 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"query"'} - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + } + ], id="ai1", # respects ids passed in ), - FunctionMessage( + ToolMessage( content="result for query", name="search_api", - id="00000000-0000-4000-8000-000000000012", + tool_call_id="tool_call123", + id="00000000-0000-4000-8000-000000000011", ), AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"another"'} - }, + tool_calls=[ + { + "id": "tool_call456", + "name": "search_api", + "args": {"query": "another"}, + } + ], id="ai2", ), - FunctionMessage( + ToolMessage( content="result for another", name="search_api", - id="00000000-0000-4000-8000-000000000022", + tool_call_id="tool_call456", + id="00000000-0000-4000-8000-000000000020", ), AIMessage(content="answer", id="ai3"), ] @@ -2415,34 +2398,48 @@ def test_message_graph( { "agent": AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"query"'} - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + } + ], id="ai1", ) }, { - "action": FunctionMessage( - content="result for query", - name="search_api", - id="00000000-0000-4000-8000-000000000039", - ) + "action": [ + ToolMessage( + content="result for query", + name="search_api", + tool_call_id="tool_call123", + id="00000000-0000-4000-8000-000000000036", + ) + ] }, { "agent": AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"another"'} - }, + tool_calls=[ + { + "id": "tool_call456", + "name": "search_api", + "args": {"query": "another"}, + } + ], id="ai2", ) }, { - "action": FunctionMessage( - content="result for another", - name="search_api", - id="00000000-0000-4000-8000-000000000049", - ) + "action": [ + ToolMessage( + content="result for another", + name="search_api", + tool_call_id="tool_call456", + id="00000000-0000-4000-8000-000000000045", + ) + ] }, {"agent": AIMessage(content="answer", id="ai3")}, ] @@ -2459,9 +2456,13 @@ def test_message_graph( { "agent": AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"query"'} - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + } + ], id="ai1", ) }, @@ -2472,9 +2473,13 @@ def test_message_graph( HumanMessage(content="what is weather in sf", id=AnyStr()), AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"query"'} - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + } + ], id="ai1", ), ], @@ -2484,7 +2489,7 @@ def test_message_graph( # modify ai message last_message = app_w_interrupt.get_state(config).values[-1] - last_message.additional_kwargs["function_call"]["arguments"] = '"a different query"' + last_message.tool_calls[0]["args"] = {"query": "a different query"} next_config = app_w_interrupt.update_state(config, last_message) # message was replaced instead of appended @@ -2493,13 +2498,14 @@ def test_message_graph( HumanMessage(content="what is weather in sf", id=AnyStr()), AIMessage( content="", - additional_kwargs={ - "function_call": { - "name": "search_api", - "arguments": '"a different query"', - } - }, id="ai1", + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "a different query"}, + } + ], ), ], next=("action",), @@ -2508,18 +2514,25 @@ def test_message_graph( assert [c for c in app_w_interrupt.stream(None, config)] == [ { - "action": FunctionMessage( - content="result for a different query", - name="search_api", - id=AnyStr(), - ) + "action": [ + ToolMessage( + content="result for a different query", + name="search_api", + tool_call_id="tool_call123", + id=AnyStr(), + ) + ] }, { "agent": AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"another"'} - }, + tool_calls=[ + { + "id": "tool_call456", + "name": "search_api", + "args": {"query": "another"}, + } + ], id="ai2", ) }, @@ -2533,24 +2546,30 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": { - "name": "search_api", - "arguments": '"a different query"', - } - }, id="ai1", + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "a different query"}, + } + ], ), - FunctionMessage( + ToolMessage( content="result for a different query", name="search_api", + tool_call_id="tool_call123", id=AnyStr(), ), AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"another"'} - }, + tool_calls=[ + { + "id": "tool_call456", + "name": "search_api", + "args": {"query": "another"}, + } + ], id="ai2", ), ], @@ -2572,17 +2591,19 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": { - "name": "search_api", - "arguments": '"a different query"', - } - }, id="ai1", + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "a different query"}, + } + ], ), - FunctionMessage( + ToolMessage( content="result for a different query", name="search_api", + tool_call_id="tool_call123", id=AnyStr(), ), AIMessage(content="answer", id="ai2"), @@ -2602,9 +2623,13 @@ def test_message_graph( { "agent": AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"query"'} - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + } + ], id="ai1", ) }, @@ -2618,9 +2643,13 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"query"'} - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + } + ], id="ai1", ), ], @@ -2630,7 +2659,7 @@ def test_message_graph( # modify ai message last_message = app_w_interrupt.get_state(config).values[-1] - last_message.additional_kwargs["function_call"]["arguments"] = '"a different query"' + last_message.tool_calls[0]["args"] = {"query": "a different query"} app_w_interrupt.update_state(config, last_message) # message was replaced instead of appended @@ -2642,13 +2671,14 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": { - "name": "search_api", - "arguments": '"a different query"', - } - }, id="ai1", + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "a different query"}, + } + ], ), ], next=("action",), @@ -2657,18 +2687,25 @@ def test_message_graph( assert [c for c in app_w_interrupt.stream(None, config)] == [ { - "action": FunctionMessage( - content="result for a different query", - name="search_api", - id=AnyStr(), - ) + "action": [ + ToolMessage( + content="result for a different query", + name="search_api", + tool_call_id="tool_call123", + id=AnyStr(), + ) + ] }, { "agent": AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"another"'} - }, + tool_calls=[ + { + "id": "tool_call456", + "name": "search_api", + "args": {"query": "another"}, + } + ], id="ai2", ) }, @@ -2682,24 +2719,30 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": { - "name": "search_api", - "arguments": '"a different query"', - } - }, id="ai1", + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "a different query"}, + } + ], ), - FunctionMessage( + ToolMessage( content="result for a different query", name="search_api", + tool_call_id="tool_call123", id=AnyStr(), ), AIMessage( content="", - additional_kwargs={ - "function_call": {"name": "search_api", "arguments": '"another"'} - }, + tool_calls=[ + { + "id": "tool_call456", + "name": "search_api", + "args": {"query": "another"}, + } + ], id="ai2", ), ], @@ -2721,17 +2764,19 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": { - "name": "search_api", - "arguments": '"a different query"', - } - }, id="ai1", + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "a different query"}, + } + ], ), - FunctionMessage( + ToolMessage( content="result for a different query", name="search_api", + tool_call_id="tool_call123", id=AnyStr(), ), AIMessage(content="answer", id="ai2"), @@ -2753,17 +2798,19 @@ def test_message_graph( ), AIMessage( content="", - additional_kwargs={ - "function_call": { - "name": "search_api", - "arguments": '"a different query"', - } - }, id="ai1", + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "a different query"}, + } + ], ), - FunctionMessage( + ToolMessage( content="result for a different query", name="search_api", + tool_call_id="tool_call123", id=AnyStr(), ), AIMessage(content="answer", id="ai2"), diff --git a/tests/test_pregel_async.py b/tests/test_pregel_async.py index 9d8e0e971..ecec6c625 100644 --- a/tests/test_pregel_async.py +++ b/tests/test_pregel_async.py @@ -1956,7 +1956,7 @@ async def test_prebuilt_tool_chat() -> None: from langchain_core.messages import AIMessage, HumanMessage, ToolMessage class FakeFuntionChatModel(FakeMessagesListChatModel): - def bind_functions(self, functions: list): + def bind_tools(self, functions: list): return self @tool() @@ -1971,41 +1971,28 @@ async def test_prebuilt_tool_chat() -> None: responses=[ AIMessage( content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call123", - "type": "function", - "function": { - "name": "search_api", - "arguments": json.dumps("query"), - }, - } - ] - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + }, + ], ), AIMessage( content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call234", - "type": "function", - "function": { - "name": "search_api", - "arguments": json.dumps("another"), - }, - }, - { - "id": "tool_call567", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"a third one"', - }, - }, - ] - }, + tool_calls=[ + { + "id": "tool_call234", + "name": "search_api", + "args": {"query": "another"}, + }, + { + "id": "tool_call567", + "name": "search_api", + "args": {"query": "a third one"}, + }, + ], ), AIMessage(content="answer"), ] @@ -2017,50 +2004,52 @@ async def test_prebuilt_tool_chat() -> None: {"messages": [HumanMessage(content="what is weather in sf")]} ) == { "messages": [ - HumanMessage(content="what is weather in sf"), + HumanMessage(content="what is weather in sf", id=AnyStr()), AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call123", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"query"', - }, - } - ] - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + }, + ], + ), + ToolMessage( + content="result for query", + name="search_api", + tool_call_id="tool_call123", + id=AnyStr(), ), - ToolMessage(content="result for query", tool_call_id="tool_call123"), AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call234", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"another"', - }, - }, - { - "id": "tool_call567", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"a third one"', - }, - }, - ] - }, + tool_calls=[ + { + "id": "tool_call234", + "name": "search_api", + "args": {"query": "another"}, + }, + { + "id": "tool_call567", + "name": "search_api", + "args": {"query": "a third one"}, + }, + ], + ), + ToolMessage( + content="result for another", + name="search_api", + tool_call_id="tool_call234", + id=AnyStr(), + ), + ToolMessage( + content="result for a third one", + name="search_api", + tool_call_id="tool_call567", + id=AnyStr(), ), - ToolMessage(content="result for another", tool_call_id="tool_call234"), - ToolMessage(content="result for a third one", tool_call_id="tool_call567"), AIMessage(content="answer", id=AnyStr()), ] } @@ -2077,18 +2066,13 @@ async def test_prebuilt_tool_chat() -> None: AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call123", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"query"', - }, - } - ] - }, + tool_calls=[ + { + "id": "tool_call123", + "name": "search_api", + "args": {"query": "query"}, + }, + ], ) ] } @@ -2096,7 +2080,12 @@ async def test_prebuilt_tool_chat() -> None: { "action": { "messages": [ - ToolMessage(content="result for query", tool_call_id="tool_call123") + ToolMessage( + content="result for query", + name="search_api", + tool_call_id="tool_call123", + id=AnyStr(), + ) ] } }, @@ -2106,26 +2095,18 @@ async def test_prebuilt_tool_chat() -> None: AIMessage( id=AnyStr(), content="", - additional_kwargs={ - "tool_calls": [ - { - "id": "tool_call234", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"another"', - }, - }, - { - "id": "tool_call567", - "type": "function", - "function": { - "name": "search_api", - "arguments": '"a third one"', - }, - }, - ] - }, + tool_calls=[ + { + "id": "tool_call234", + "name": "search_api", + "args": {"query": "another"}, + }, + { + "id": "tool_call567", + "name": "search_api", + "args": {"query": "a third one"}, + }, + ], ) ] } @@ -2134,10 +2115,16 @@ async def test_prebuilt_tool_chat() -> None: "action": { "messages": [ ToolMessage( - content="result for another", tool_call_id="tool_call234" + content="result for another", + tool_call_id="tool_call234", + name="search_api", + id=AnyStr(), ), ToolMessage( - content="result for a third one", tool_call_id="tool_call567" + content="result for a third one", + tool_call_id="tool_call567", + name="search_api", + id=AnyStr(), ), ] } @@ -2193,7 +2180,7 @@ async def test_prebuilt_chat() -> None: {"messages": [HumanMessage(content="what is weather in sf")]} ) == { "messages": [ - HumanMessage(content="what is weather in sf"), + HumanMessage(content="what is weather in sf", id=AnyStr()), AIMessage( id=AnyStr(), content="", @@ -2201,7 +2188,7 @@ async def test_prebuilt_chat() -> None: "function_call": {"name": "search_api", "arguments": '"query"'} }, ), - FunctionMessage(content="result for query", name="search_api"), + FunctionMessage(content="result for query", name="search_api", id=AnyStr()), AIMessage( id=AnyStr(), content="", @@ -2209,7 +2196,9 @@ async def test_prebuilt_chat() -> None: "function_call": {"name": "search_api", "arguments": '"another"'} }, ), - FunctionMessage(content="result for another", name="search_api"), + FunctionMessage( + content="result for another", name="search_api", id=AnyStr() + ), AIMessage(content="answer", id=AnyStr()), ] } @@ -2239,7 +2228,9 @@ async def test_prebuilt_chat() -> None: { "action": { "messages": [ - FunctionMessage(content="result for query", name="search_api") + FunctionMessage( + content="result for query", name="search_api", id=AnyStr() + ) ] } }, @@ -2262,7 +2253,9 @@ async def test_prebuilt_chat() -> None: { "action": { "messages": [ - FunctionMessage(content="result for another", name="search_api") + FunctionMessage( + content="result for another", name="search_api", id=AnyStr() + ) ] } }, From 89a14160b0bb7756f4b359c019fb30b695c96d92 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Wed, 10 Apr 2024 18:41:12 -0700 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: William FH <13333726+hinthornw@users.noreply.github.com> --- langgraph/prebuilt/tool_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langgraph/prebuilt/tool_node.py b/langgraph/prebuilt/tool_node.py index b0d2da8be..cc2ba8b2c 100644 --- a/langgraph/prebuilt/tool_node.py +++ b/langgraph/prebuilt/tool_node.py @@ -22,7 +22,7 @@ def str_output(output: Any) -> str: class ToolNode(RunnableCallable): """ - A node that runs the tols requested in the last AIMessage. It can be used + A node that runs the tools requested in the last AIMessage. It can be used either in StateGraph with a "messages" key or in MessageGraph. If multiple tool calls are requested, they will be run in parallel. The output will be a list of ToolMessages, one for each tool call. From c8d3f73d0463cebb74580762cbdacf39426066c6 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Wed, 10 Apr 2024 18:55:07 -0700 Subject: [PATCH 3/4] Lint --- langgraph/prebuilt/tool_node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/langgraph/prebuilt/tool_node.py b/langgraph/prebuilt/tool_node.py index cc2ba8b2c..6fc302c3c 100644 --- a/langgraph/prebuilt/tool_node.py +++ b/langgraph/prebuilt/tool_node.py @@ -1,6 +1,6 @@ import asyncio import json -from typing import Any, Sequence, Union +from typing import Any, Optional, Sequence, Union from langchain_core.messages import AIMessage, AnyMessage, ToolCall, ToolMessage from langchain_core.runnables import RunnableConfig @@ -33,7 +33,7 @@ class ToolNode(RunnableCallable): tools: Sequence[BaseTool], *, name: str = "tools", - tags: list[str] | None = None, + tags: Optional[list[str]] = None, ) -> None: super().__init__(self._func, self._afunc, name=name, tags=tags, trace=False) self.tools_by_name = {tool.name: tool for tool in tools} From 92deae4196e5e00fe73c40a88446e3667c5151f3 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Thu, 11 Apr 2024 10:49:46 -0700 Subject: [PATCH 4/4] Add anthropic notebook --- .../anthropic.ipynb | 417 ++++++++++++++++++ .../prebuilt-tool-node.ipynb | 239 ++++------ poetry.lock | 260 ++++++++++- pyproject.toml | 3 +- 4 files changed, 755 insertions(+), 164 deletions(-) create mode 100644 examples/chat_agent_executor_with_function_calling/anthropic.ipynb diff --git a/examples/chat_agent_executor_with_function_calling/anthropic.ipynb b/examples/chat_agent_executor_with_function_calling/anthropic.ipynb new file mode 100644 index 000000000..77492ac72 --- /dev/null +++ b/examples/chat_agent_executor_with_function_calling/anthropic.ipynb @@ -0,0 +1,417 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53", + "metadata": {}, + "source": [ + "# Chat Agent Executor with Anthropic\n", + "\n", + "\n", + "In this example we will build a chat executor that uses tool calling and the prebuilt ToolNode with Anthropic." + ] + }, + { + "cell_type": "markdown", + "id": "7cbd446a-808f-4394-be92-d45ab818953c", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "First we need to install the packages required" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --quiet -U langchain langchain_anthropic tavily-python" + ] + }, + { + "cell_type": "markdown", + "id": "0abe11f4-62ed-4dc4-8875-3db21e260d1d", + "metadata": {}, + "source": [ + "Next, we need to set API keys for OpenAI (the LLM we will use) and Tavily (the search tool we will use)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import getpass\n", + "\n", + "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n", + "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API Key:\")" + ] + }, + { + "cell_type": "markdown", + "id": "f0ed46a8-effe-4596-b0e1-a6a29ee16f5c", + "metadata": {}, + "source": [ + "Optionally, we can set API key for [LangSmith tracing](https://smith.langchain.com/), which will give us best-in-class observability." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95e25aec-7c9f-4a63-b143-225d0e9a79c3", + "metadata": {}, + "outputs": [], + "source": [ + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass(\"LangSmith API Key:\")" + ] + }, + { + "cell_type": "markdown", + "id": "21ac643b-cb06-4724-a80c-2862ba4773f1", + "metadata": {}, + "source": [ + "## Set up the tools\n", + "\n", + "We will first define the tools we want to use.\n", + "For this simple example, we will use a built-in search tool via Tavily.\n", + "However, it is really easy to create your own tools - see documentation [here](https://python.langchain.com/docs/modules/agents/tools/custom_tools) on how to do that.\n", + "\n", + "**MODIFICATION**\n", + "\n", + "We don't need a ToolExecutor when using ToolNode.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d7ef57dd-5d6e-4ad3-9377-a92201c1310e", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_community.tools.tavily_search import TavilySearchResults\n", + "\n", + "tools = [TavilySearchResults(max_results=1)]" + ] + }, + { + "cell_type": "markdown", + "id": "5497ed70-fce3-47f1-9cad-46f912bad6a5", + "metadata": {}, + "source": [ + "## Set up the model\n", + "\n", + "Now we need to load the chat model we want to use.\n", + "Importantly, this should satisfy two criteria:\n", + "\n", + "1. It should work with messages. We will represent all agent state in the form of messages, so it needs to be able to work well with them.\n", + "2. It should work with tool calling. This means it should be a model that implements `.bind_tools()`.\n", + "\n", + "Note: these model requirements are not requirements for using LangGraph - they are just requirements for this one example.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "892b54b9-75f0-4804-9ed0-88b5e5532989", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_anthropic import ChatAnthropic\n", + "\n", + "model = ChatAnthropic(temperature=0, model_name=\"claude-3-opus-20240229\")" + ] + }, + { + "cell_type": "markdown", + "id": "a77995c0-bae2-4cee-a036-8688a90f05b9", + "metadata": {}, + "source": [ + "\n", + "After we've done this, we should make sure the model knows that it has these tools available to call.\n", + "We can do this by converting the LangChain tools into the format for OpenAI function calling, and then bind them to the model class.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "cd3cbae5-d92c-4559-a4aa-44721b80d107", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages/langchain_core/_api/beta_decorator.py:87: LangChainBetaWarning: The function `bind_tools` is in beta. It is actively being worked on, so the API may change.\n", + " warn_beta(\n" + ] + } + ], + "source": [ + "model = model.bind_tools(tools)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ea793afa-2eab-4901-910d-6eed90cd6564", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import TypedDict, Annotated, Sequence\n", + "import operator\n", + "from langchain_core.messages import BaseMessage\n", + "\n", + "\n", + "class AgentState(TypedDict):\n", + " messages: Annotated[Sequence[BaseMessage], operator.add]" + ] + }, + { + "cell_type": "markdown", + "id": "e03c5094-9297-4d19-a04e-3eedc75cefb4", + "metadata": {}, + "source": [ + "## Define the nodes\n", + "\n", + "We now need to define a few different nodes in our graph.\n", + "In `langgraph`, a node can be either a function or a [runnable](https://python.langchain.com/docs/expression_language/).\n", + "There are two main nodes we need for this:\n", + "\n", + "1. The agent: responsible for deciding what (if any) actions to take.\n", + "2. **MODIFICATION** The prebuilt ToolNode, given the list of tools. This will take tool calls from the most recent AIMessage, execute them, and return the result as ToolMessages.\n", + "\n", + "We will also need to define some edges.\n", + "Some of these edges may be conditional.\n", + "The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n", + "The path that is taken is not known until that node is run (the LLM decides).\n", + "\n", + "1. Conditional Edge: after the agent is called, we should either:\n", + " a. If the agent said to take an action, then the function to invoke tools should be called\n", + " b. If the agent said that it was finished, then it should finish\n", + "2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n", + "\n", + "Let's define the nodes, as well as a function to decide how what conditional edge to take.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "3b541bb9-900c-40d0-964d-7b5dfee30667", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.prebuilt import ToolNode\n", + "\n", + "\n", + "# Define the function that determines whether to continue or not\n", + "def should_continue(state):\n", + " messages = state[\"messages\"]\n", + " last_message = messages[-1]\n", + " # If there are no tool calls, then we finish\n", + " if not last_message.tool_calls:\n", + " return \"end\"\n", + " # Otherwise if there is, we continue\n", + " else:\n", + " return \"continue\"\n", + "\n", + "\n", + "# Define the function that calls the model\n", + "def call_model(state):\n", + " messages = state[\"messages\"]\n", + " response = model.invoke(messages)\n", + " # We return a list, because this will get added to the existing list\n", + " return {\"messages\": [response]}\n", + "\n", + "\n", + "# Define the function to execute tools\n", + "tool_node = ToolNode(tools)" + ] + }, + { + "cell_type": "markdown", + "id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b", + "metadata": {}, + "source": [ + "## Define the graph\n", + "\n", + "We can now put it all together and define the graph!" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "813ae66c-3b58-4283-a02a-36da72a2ab90", + "metadata": {}, + "outputs": [], + "source": [ + "from langgraph.graph import StateGraph, END\n", + "\n", + "# Define a new graph\n", + "workflow = StateGraph(AgentState)\n", + "\n", + "# Define the two nodes we will cycle between\n", + "workflow.add_node(\"agent\", call_model)\n", + "workflow.add_node(\"action\", tool_node)\n", + "\n", + "# Set the entrypoint as `agent`\n", + "# This means that this node is the first one called\n", + "workflow.set_entry_point(\"agent\")\n", + "\n", + "# We now add a conditional edge\n", + "workflow.add_conditional_edges(\n", + " # First, we define the start node. We use `agent`.\n", + " # This means these are the edges taken after the `agent` node is called.\n", + " \"agent\",\n", + " # Next, we pass in the function that will determine which node is called next.\n", + " should_continue,\n", + " # Finally we pass in a mapping.\n", + " # The keys are strings, and the values are other nodes.\n", + " # END is a special node marking that the graph should finish.\n", + " # What will happen is we will call `should_continue`, and then the output of that\n", + " # will be matched against the keys in this mapping.\n", + " # Based on which one it matches, that node will then be called.\n", + " {\n", + " # If `tools`, then we call the tool node.\n", + " \"continue\": \"action\",\n", + " # Otherwise we finish.\n", + " \"end\": END,\n", + " },\n", + ")\n", + "\n", + "# We now add a normal edge from `tools` to `agent`.\n", + "# This means that after `tools` is called, `agent` node is called next.\n", + "workflow.add_edge(\"action\", \"agent\")\n", + "\n", + "# Finally, we compile it!\n", + "# This compiles it into a LangChain Runnable,\n", + "# meaning you can use it as you would any other runnable\n", + "app = workflow.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "547c3931-3dae-4281-ad4e-4b51305594d4", + "metadata": {}, + "source": [ + "## Use it!\n", + "\n", + "We can now use it!\n", + "This now exposes the [same interface](https://python.langchain.com/docs/expression_language/) as all other LangChain runnables." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "8edb04b9-40b6-46f1-a7a8-4b2d8aba7752", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'messages': [HumanMessage(content='what is the weather in sf'),\n", + " AIMessage(content=[{'text': '\\nThe relevant tool to answer this question is tavily_search_results_json, which can provide comprehensive results about current events like weather.\\n\\nTo call tavily_search_results_json, I need to provide a value for the required \"query\" parameter. The user\\'s request directly specifies the query to search for - \"weather in sf\". \"sf\" here likely refers to San Francisco.\\n\\nI have the required parameter value to make the API call, so I will proceed with the search.\\n', 'type': 'text'}, {'id': 'toolu_01AnGNJEYsAvrJpJYh6MuV16', 'input': {'query': 'weather in San Francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], response_metadata={'id': 'msg_01BujbLhL23TFWN8zrarY4So', 'model': 'claude-3-opus-20240229', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 507, 'output_tokens': 170}}, id='run-3c5a870b-48b4-4b94-8c70-f5dc322da35a-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'toolu_01AnGNJEYsAvrJpJYh6MuV16'}]),\n", + " ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712857380, \\'localtime\\': \\'2024-04-11 10:43\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712856600, \\'last_updated\\': \\'2024-04-11 10:30\\', \\'temp_c\\': 15.6, \\'temp_f\\': 60.1, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 4.3, \\'wind_kph\\': 6.8, \\'wind_degree\\': 50, \\'wind_dir\\': \\'NE\\', \\'pressure_mb\\': 1015.0, \\'pressure_in\\': 29.96, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 78, \\'cloud\\': 25, \\'feelslike_c\\': 15.6, \\'feelslike_f\\': 60.1, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 5.0, \\'gust_mph\\': 5.1, \\'gust_kph\\': 8.3}}\"}]', name='tavily_search_results_json', tool_call_id='toolu_01AnGNJEYsAvrJpJYh6MuV16'),\n", + " AIMessage(content=\"\\nThe search results provide a comprehensive weather report for San Francisco, including the current temperature, conditions, wind, humidity, and other relevant details. This should be sufficient to fully answer the question of what the current weather is like in San Francisco.\\n\\n5\\n\\n\\nAccording to the weather report, the current weather in San Francisco is:\\n\\nTemperature: 60.1°F (15.6°C)\\nConditions: Partly cloudy \\nWind: 4.3 mph (6.8 km/h) from the northeast\\nHumidity: 78%\\n\\nIt feels like 60.1°F (15.6°C). Visibility is 9 miles (16 km). The UV index is 5 out of 10.\\n\\nSo in summary, it's a mild spring day in San Francisco with some cloud cover, light winds, and comfortable temperatures in the low 60s Fahrenheit. A light jacket or sweater should suffice for being outdoors.\\n\", response_metadata={'id': 'msg_01KhNRhAaoyT8v7kk8QJUyYj', 'model': 'claude-3-opus-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 1102, 'output_tokens': 250}}, id='run-5694fbc4-8041-4577-a007-227e09ef5bd8-0')]}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.messages import HumanMessage\n", + "\n", + "inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n", + "app.invoke(inputs)" + ] + }, + { + "cell_type": "markdown", + "id": "5a9e8155-70c5-4973-912c-dc55104b2acf", + "metadata": {}, + "source": [ + "This may take a little bit - it's making a few calls behind the scenes.\n", + "In order to start seeing some intermediate results as they happen, we can use streaming - see below for more information on that.\n", + "\n", + "## Streaming\n", + "\n", + "LangGraph has support for several different types of streaming.\n", + "\n", + "### Streaming Node Output\n", + "\n", + "One of the benefits of using LangGraph is that it is easy to stream output as it's produced by each node.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "f544977e-31f7-41f0-88c4-ec9c27b8cecb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Output from node 'agent':\n", + "---\n", + "{'messages': [AIMessage(content=[{'text': '\\nThe relevant tool to answer this question is tavily_search_results_json, which can provide comprehensive results about current events like weather.\\n\\nTo call this function, I need to provide a value for the required \"query\" parameter. The user\\'s request directly specifies the query to search for: \"weather in sf\". \"sf\" here likely refers to San Francisco.\\n\\nSince I have a value for the required parameter, I can proceed with the function call.\\n', 'type': 'text'}, {'id': 'toolu_01XgUtdMt17UaBS8BUN2ZRyn', 'input': {'query': 'weather in San Francisco'}, 'name': 'tavily_search_results_json', 'type': 'tool_use'}], response_metadata={'id': 'msg_01SyKFjD9dxUNxwTQ5FiT3Yr', 'model': 'claude-3-opus-20240229', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 507, 'output_tokens': 162}}, id='run-42b25509-f322-4c4b-9817-f9ae154b8293-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'toolu_01XgUtdMt17UaBS8BUN2ZRyn'}])]}\n", + "\n", + "---\n", + "\n", + "Output from node 'action':\n", + "---\n", + "{'messages': [ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712857380, \\'localtime\\': \\'2024-04-11 10:43\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712856600, \\'last_updated\\': \\'2024-04-11 10:30\\', \\'temp_c\\': 15.6, \\'temp_f\\': 60.1, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 4.3, \\'wind_kph\\': 6.8, \\'wind_degree\\': 50, \\'wind_dir\\': \\'NE\\', \\'pressure_mb\\': 1015.0, \\'pressure_in\\': 29.96, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 78, \\'cloud\\': 25, \\'feelslike_c\\': 15.6, \\'feelslike_f\\': 60.1, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 5.0, \\'gust_mph\\': 5.1, \\'gust_kph\\': 8.3}}\"}]', name='tavily_search_results_json', tool_call_id='toolu_01XgUtdMt17UaBS8BUN2ZRyn')]}\n", + "\n", + "---\n", + "\n", + "Output from node 'agent':\n", + "---\n", + "{'messages': [AIMessage(content='\\nThe search results provide a comprehensive and up-to-date weather report for San Francisco, including key details like temperature, conditions, wind, humidity, and more. This should be sufficient to fully answer the question of what the current weather is like in San Francisco.\\n\\n5\\n\\n\\nAccording to the latest weather report, the current weather in San Francisco is:\\n\\nTemperature: 60.1°F (15.6°C)\\nConditions: Partly cloudy \\nWind: 4.3 mph (6.8 km/h) from the NE\\nHumidity: 78%\\nPrecipitation: 0 inches\\nVisibility: 9 miles\\nUV Index: 5.0\\n\\nIt feels like 60.1°F (15.6°C). The report indicates it is a partly cloudy day with no rain expected. Winds are light out of the northeast.\\n', response_metadata={'id': 'msg_01X8S82ECeXU8px2TpMPfkce', 'model': 'claude-3-opus-20240229', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'input_tokens': 1094, 'output_tokens': 232}}, id='run-772e7225-dc58-4b63-a0d7-6d7d39e3b059-0')]}\n", + "\n", + "---\n", + "\n" + ] + } + ], + "source": [ + "inputs = {\"messages\": [HumanMessage(content=\"what is the weather in sf\")]}\n", + "for output in app.stream(inputs):\n", + " # stream() yields dictionaries with output keyed by node name\n", + " for key, value in output.items():\n", + " print(f\"Output from node '{key}':\")\n", + " print(\"---\")\n", + " print(value)\n", + " print(\"\\n---\\n\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb index 00eafa2ef..f85dcc1c4 100644 --- a/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb +++ b/examples/chat_agent_executor_with_function_calling/prebuilt-tool-node.ipynb @@ -211,14 +211,12 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "id": "3b541bb9-900c-40d0-964d-7b5dfee30667", "metadata": {}, "outputs": [], "source": [ "from langgraph.prebuilt import ToolNode\n", - "import json\n", - "from langchain_core.messages import FunctionMessage\n", "\n", "\n", "# Define the function that determines whether to continue or not\n", @@ -257,7 +255,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 6, "id": "813ae66c-3b58-4283-a02a-36da72a2ab90", "metadata": {}, "outputs": [], @@ -319,28 +317,20 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 7, "id": "8edb04b9-40b6-46f1-a7a8-4b2d8aba7752", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages/langchain_core/messages/ai.py:52: UserWarning: New langchain packages are available that more efficiently handle tool calling. Please upgrade your packages to versions that set message tool calls. e.g., `pip install --upgrade langchain-anthropic`, pip install--upgrade langchain-openai`, etc.\n", - " warnings.warn(\n" - ] - }, { "data": { "text/plain": [ "{'messages': [HumanMessage(content='what is the weather in sf'),\n", - " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Q7l0TopyJxaly7xM9Vq2aGxO', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 87, 'total_tokens': 108}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-d39a73db-e37a-46ce-9476-73fe9eb84b2e-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_Q7l0TopyJxaly7xM9Vq2aGxO'}]),\n", - " ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712797953, \\'localtime\\': \\'2024-04-10 18:12\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712797200, \\'last_updated\\': \\'2024-04-10 18:00\\', \\'temp_c\\': 21.1, \\'temp_f\\': 70.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 10.5, \\'wind_kph\\': 16.9, \\'wind_degree\\': 300, \\'wind_dir\\': \\'WNW\\', \\'pressure_mb\\': 1017.0, \\'pressure_in\\': 30.04, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 59, \\'cloud\\': 75, \\'feelslike_c\\': 21.1, \\'feelslike_f\\': 70.0, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 5.0, \\'gust_mph\\': 16.0, \\'gust_kph\\': 25.8}}\"}]', name='tavily_search_results_json', tool_call_id='call_Q7l0TopyJxaly7xM9Vq2aGxO'),\n", - " AIMessage(content='The current weather in San Francisco is partly cloudy with a temperature of 21.1°C (70.0°F). The wind speed is 10.5 mph (16.9 kph) coming from the west-northwest direction. The humidity is at 59% with a visibility of 16.0 km (9.0 miles).', response_metadata={'token_usage': {'completion_tokens': 72, 'prompt_tokens': 466, 'total_tokens': 538}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-091dcb4c-9424-40fc-aab6-58968d9929a2-0')]}" + " AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_HGOi2cCxKKVWnz8WMuOCWnZx', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 87, 'total_tokens': 108}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-df061477-a815-432b-a69f-9951d4c6edfa-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_HGOi2cCxKKVWnz8WMuOCWnZx'}]),\n", + " ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712852407, \\'localtime\\': \\'2024-04-11 9:20\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712852100, \\'last_updated\\': \\'2024-04-11 09:15\\', \\'temp_c\\': 15.0, \\'temp_f\\': 59.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 3.8, \\'wind_kph\\': 6.1, \\'wind_degree\\': 350, \\'wind_dir\\': \\'N\\', \\'pressure_mb\\': 1015.0, \\'pressure_in\\': 29.97, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 78, \\'cloud\\': 25, \\'feelslike_c\\': 15.8, \\'feelslike_f\\': 60.4, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 4.0, \\'gust_mph\\': 8.3, \\'gust_kph\\': 13.3}}\"}]', name='tavily_search_results_json', tool_call_id='call_HGOi2cCxKKVWnz8WMuOCWnZx'),\n", + " AIMessage(content='The current weather in San Francisco is as follows:\\n- Temperature: 15.0°C (59.0°F)\\n- Condition: Partly cloudy\\n- Wind: 3.8 mph from the North\\n- Humidity: 78%\\n- Visibility: 16.0 km (9.0 miles)\\n- UV Index: 4.0\\n\\nFor more details, you can visit [Weather API](https://www.weatherapi.com/).', response_metadata={'token_usage': {'completion_tokens': 93, 'prompt_tokens': 465, 'total_tokens': 558}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-923bcbd2-3c79-4696-8f9e-5142b50b20cf-0')]}" ] }, - "execution_count": 14, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -371,37 +361,29 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 8, "id": "f544977e-31f7-41f0-88c4-ec9c27b8cecb", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages/langchain_core/messages/ai.py:52: UserWarning: New langchain packages are available that more efficiently handle tool calling. Please upgrade your packages to versions that set message tool calls. e.g., `pip install --upgrade langchain-anthropic`, pip install--upgrade langchain-openai`, etc.\n", - " warnings.warn(\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ "Output from node 'agent':\n", "---\n", - "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_VgzsFE5Cf3sdlrIudeijqVsp', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 87, 'total_tokens': 108}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b3acca13-ccbf-4761-9d0c-1410d5f4f0a9-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_VgzsFE5Cf3sdlrIudeijqVsp'}])]}\n", + "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_3QXwm9UTKcfN2BuFhTDlLgIN', 'function': {'arguments': '{\"query\":\"weather in San Francisco\"}', 'name': 'tavily_search_results_json'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 87, 'total_tokens': 108}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-9a2d6e22-873a-4afc-8ae2-0adf8176b1b2-0', tool_calls=[{'name': 'tavily_search_results_json', 'args': {'query': 'weather in San Francisco'}, 'id': 'call_3QXwm9UTKcfN2BuFhTDlLgIN'}])]}\n", "\n", "---\n", "\n", "Output from node 'action':\n", "---\n", - "{'messages': [ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712797953, \\'localtime\\': \\'2024-04-10 18:12\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712797200, \\'last_updated\\': \\'2024-04-10 18:00\\', \\'temp_c\\': 21.1, \\'temp_f\\': 70.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 10.5, \\'wind_kph\\': 16.9, \\'wind_degree\\': 300, \\'wind_dir\\': \\'WNW\\', \\'pressure_mb\\': 1017.0, \\'pressure_in\\': 30.04, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 59, \\'cloud\\': 75, \\'feelslike_c\\': 21.1, \\'feelslike_f\\': 70.0, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 5.0, \\'gust_mph\\': 16.0, \\'gust_kph\\': 25.8}}\"}]', name='tavily_search_results_json', tool_call_id='call_VgzsFE5Cf3sdlrIudeijqVsp')]}\n", + "{'messages': [ToolMessage(content='[{\"url\": \"https://www.weatherapi.com/\", \"content\": \"{\\'location\\': {\\'name\\': \\'San Francisco\\', \\'region\\': \\'California\\', \\'country\\': \\'United States of America\\', \\'lat\\': 37.78, \\'lon\\': -122.42, \\'tz_id\\': \\'America/Los_Angeles\\', \\'localtime_epoch\\': 1712852407, \\'localtime\\': \\'2024-04-11 9:20\\'}, \\'current\\': {\\'last_updated_epoch\\': 1712852100, \\'last_updated\\': \\'2024-04-11 09:15\\', \\'temp_c\\': 15.0, \\'temp_f\\': 59.0, \\'is_day\\': 1, \\'condition\\': {\\'text\\': \\'Partly cloudy\\', \\'icon\\': \\'//cdn.weatherapi.com/weather/64x64/day/116.png\\', \\'code\\': 1003}, \\'wind_mph\\': 3.8, \\'wind_kph\\': 6.1, \\'wind_degree\\': 350, \\'wind_dir\\': \\'N\\', \\'pressure_mb\\': 1015.0, \\'pressure_in\\': 29.97, \\'precip_mm\\': 0.0, \\'precip_in\\': 0.0, \\'humidity\\': 78, \\'cloud\\': 25, \\'feelslike_c\\': 15.8, \\'feelslike_f\\': 60.4, \\'vis_km\\': 16.0, \\'vis_miles\\': 9.0, \\'uv\\': 4.0, \\'gust_mph\\': 8.3, \\'gust_kph\\': 13.3}}\"}]', name='tavily_search_results_json', tool_call_id='call_3QXwm9UTKcfN2BuFhTDlLgIN')]}\n", "\n", "---\n", "\n", "Output from node 'agent':\n", "---\n", - "{'messages': [AIMessage(content='The current weather in San Francisco is partly cloudy with a temperature of 70°F (21.1°C). The wind speed is 10.5 mph (16.9 kph) coming from the west-northwest direction. The humidity is at 59%, and the visibility is 9.0 miles.', response_metadata={'token_usage': {'completion_tokens': 65, 'prompt_tokens': 466, 'total_tokens': 531}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-fa95b6d0-34bb-41e7-8cfd-87b6af69064f-0')]}\n", + "{'messages': [AIMessage(content='The current weather in San Francisco is partly cloudy with a temperature of 59°F (15°C). The wind speed is 6.1 km/h coming from the north. The humidity is at 78%, and the visibility is 16.0 km.', response_metadata={'token_usage': {'completion_tokens': 53, 'prompt_tokens': 465, 'total_tokens': 518}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': 'fp_b28b39ffa8', 'finish_reason': 'stop', 'logprobs': None}, id='run-8875456d-e31e-42b0-b2af-bdc1a9cfccfe-0')]}\n", "\n", "---\n", "\n" @@ -433,143 +415,82 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "id": "cfd140f0-a5a6-4697-8115-322242f197b5", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/nuno/dev/langgraph/.venv/lib/python3.11/site-packages/langchain_core/messages/ai.py:52: UserWarning: New langchain packages are available that more efficiently handle tool calling. Please upgrade your packages to versions that set message tool calls. e.g., `pip install --upgrade langchain-anthropic`, pip install--upgrade langchain-openai`, etc.\n", - " warnings.warn(\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_TyyKAi2L0Uymjr3YW2d3ZnIm', 'function': {'arguments': '', 'name': 'tavily_search_results_json'}, 'type': 'function'}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': 'tavily_search_results_json', 'args': '', 'id': 'call_TyyKAi2L0Uymjr3YW2d3ZnIm', 'error': 'Malformed args.'}] tool_call_chunks=[{'name': 'tavily_search_results_json', 'args': '', 'id': 'call_TyyKAi2L0Uymjr3YW2d3ZnIm', 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '{\"', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' tool_calls=[{'name': '', 'args': {}, 'id': None}] tool_call_chunks=[{'name': None, 'args': '{\"', 'id': None, 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': 'query', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': 'query', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': 'query', 'id': None, 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '\":\"', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': '\":\"', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': '\":\"', 'id': None, 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': 'weather', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': 'weather', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': 'weather', 'id': None, 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' in', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': ' in', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' in', 'id': None, 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' San', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': ' San', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' San', 'id': None, 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' Francisco', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': ' Francisco', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' Francisco', 'id': None, 'index': 0}]\n", - "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '\"}', 'name': None}, 'type': None}]} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1' invalid_tool_calls=[{'name': None, 'args': '\"}', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': '\"}', 'id': None, 'index': 0}]\n", - "content='' response_metadata={'finish_reason': 'tool_calls'} id='run-7106791e-464e-4aa1-aaee-5d4674fe49e1'\n", - "content='' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='The' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' current' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' weather' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' in' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' San' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Francisco' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' is' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' as' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' follows' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Temperature' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='21' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='1' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='°C' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' (' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='70' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='°F' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=')\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Condition' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Part' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='ly' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' cloudy' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Wind' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='10' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='5' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' mph' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' from' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' W' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='NW' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Pressure' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='101' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='7' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' mb' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Hum' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='idity' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='59' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='%\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Cloud' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Cover' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='75' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='%\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Visibility' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='16' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' km' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' (' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='9' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' miles' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=')\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='-' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' UV' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' Index' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=':' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' ' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='5' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='0' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='\\n\\n' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='For' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' more' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' details' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=',' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' you' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' can' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' visit' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' [' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='Weather' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=' API' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='](' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='https' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='://' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='www' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.weather' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='api' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='.com' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='/' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content=').' id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n", - "content='' response_metadata={'finish_reason': 'stop'} id='run-9000bee8-10fe-4712-852b-e043aecb42ec'\n" + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_EdTLEVxQKMLRNv82Yqdcugdy', 'function': {'arguments': '', 'name': 'tavily_search_results_json'}, 'type': 'function'}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': 'tavily_search_results_json', 'args': '', 'id': 'call_EdTLEVxQKMLRNv82Yqdcugdy', 'error': 'Malformed args.'}] tool_call_chunks=[{'name': 'tavily_search_results_json', 'args': '', 'id': 'call_EdTLEVxQKMLRNv82Yqdcugdy', 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '{\"', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' tool_calls=[{'name': '', 'args': {}, 'id': None}] tool_call_chunks=[{'name': None, 'args': '{\"', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': 'query', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': None, 'args': 'query', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': 'query', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '\":\"', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': None, 'args': '\":\"', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': '\":\"', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': 'weather', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': None, 'args': 'weather', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': 'weather', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' in', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': None, 'args': ' in', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' in', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' San', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': None, 'args': ' San', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' San', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': ' Francisco', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': None, 'args': ' Francisco', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': ' Francisco', 'id': None, 'index': 0}]\n", + "content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': None, 'function': {'arguments': '\"}', 'name': None}, 'type': None}]} id='run-acf76f4b-c5d0-46a1-a114-75021091719b' invalid_tool_calls=[{'name': None, 'args': '\"}', 'id': None, 'error': 'Malformed args.'}] tool_call_chunks=[{'name': None, 'args': '\"}', 'id': None, 'index': 0}]\n", + "content='' response_metadata={'finish_reason': 'tool_calls'} id='run-acf76f4b-c5d0-46a1-a114-75021091719b'\n", + "content='' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='The' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' current' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' weather' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' in' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' San' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' Francisco' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' is' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' partly' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' cloudy' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' with' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' a' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' temperature' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' of' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' ' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='59' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='°F' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' (' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='15' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='°C' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=').' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' The' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' wind' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' speed' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' is' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' ' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='3' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='.' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='8' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' mph' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' (' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='6' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='.' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='1' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' k' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='ph' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=')' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' coming' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' from' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' the' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' north' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='.' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' The' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' humidity' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' is' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' at' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' ' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='78' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='%' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' with' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' a' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' visibility' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' of' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' ' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='9' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content=' miles' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='.' id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n", + "content='' response_metadata={'finish_reason': 'stop'} id='run-bd561aa4-2af3-4d44-a110-b7991ec0d930'\n" ] } ], diff --git a/poetry.lock b/poetry.lock index d7bc341f6..1d51de2c3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -136,6 +136,30 @@ files = [ {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, ] +[[package]] +name = "anthropic" +version = "0.25.1" +description = "The official Python library for the anthropic API" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anthropic-0.25.1-py3-none-any.whl", hash = "sha256:95d0cedc2a4b5beae3a78f9030aea4001caea5f46c6d263cce377c891c594e71"}, + {file = "anthropic-0.25.1.tar.gz", hash = "sha256:0c01b30b77d041a8d07c532737bae69da58086031217150008e4541f52a64bd9"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tokenizers = ">=0.13.0" +typing-extensions = ">=4.7,<5" + +[package.extras] +bedrock = ["boto3 (>=1.28.57)", "botocore (>=1.31.57)"] +vertex = ["google-auth (>=2,<3)"] + [[package]] name = "anyio" version = "4.3.0" @@ -750,6 +774,22 @@ files = [ [package.extras] devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] +[[package]] +name = "filelock" +version = "3.13.4" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, + {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + [[package]] name = "fqdn" version = "1.5.1" @@ -847,6 +887,41 @@ files = [ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, ] +[[package]] +name = "fsspec" +version = "2024.3.1" +description = "File-system specification" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fsspec-2024.3.1-py3-none-any.whl", hash = "sha256:918d18d41bf73f0e2b261824baeb1b124bcf771767e3a26425cd7dec3332f512"}, + {file = "fsspec-2024.3.1.tar.gz", hash = "sha256:f39780e282d7d117ffb42bb96992f8a90795e4d0fb0f661a70ca39fe9c43ded9"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +devel = ["pytest", "pytest-cov"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +tqdm = ["tqdm"] + [[package]] name = "grandalf" version = "0.8" @@ -991,6 +1066,40 @@ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +[[package]] +name = "huggingface-hub" +version = "0.22.2" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "huggingface_hub-0.22.2-py3-none-any.whl", hash = "sha256:3429e25f38ccb834d310804a3b711e7e4953db5a9e420cc147a5e194ca90fd17"}, + {file = "huggingface_hub-0.22.2.tar.gz", hash = "sha256:32e9a9a6843c92f253ff9ca16b9985def4d80a93fb357af5353f770ef74a81be"}, +] + +[package.dependencies] +filelock = "*" +fsspec = ">=2023.5.0" +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +inference = ["aiohttp", "minijinja (>=1.0)"] +quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + [[package]] name = "idna" version = "3.6" @@ -1559,6 +1668,22 @@ openai = ["openai (<2)", "tiktoken (>=0.3.2,<0.6.0)"] qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"] text-helpers = ["chardet (>=5.1.0,<6.0.0)"] +[[package]] +name = "langchain-anthropic" +version = "0.1.8" +description = "An integration package connecting AnthropicMessages and LangChain" +optional = false +python-versions = "<4.0,>=3.8.1" +files = [ + {file = "langchain_anthropic-0.1.8-py3-none-any.whl", hash = "sha256:634eda00a1b2f4dc9bc59f35b6593483dd845c898af7ae491f91fb9ed871dc2b"}, + {file = "langchain_anthropic-0.1.8.tar.gz", hash = "sha256:e3e03dcc25338797a867705b296faba910243559c37a517992586d866b363bb3"}, +] + +[package.dependencies] +anthropic = ">=0.23.0,<1" +defusedxml = ">=0.7.1,<0.8.0" +langchain-core = ">=0.1.42,<0.2.0" + [[package]] name = "langchain-community" version = "0.0.27" @@ -1587,13 +1712,13 @@ extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15. [[package]] name = "langchain-core" -version = "0.1.42rc1" +version = "0.1.42" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.1.42rc1-py3-none-any.whl", hash = "sha256:2b216652f61b915ae274d1228ad45e7fc99af1d5f41bb6900aafd0636e66def5"}, - {file = "langchain_core-0.1.42rc1.tar.gz", hash = "sha256:af75525f31251d8d2889671b6051a0e2afffd355b5efefb1c59fc91545805ab8"}, + {file = "langchain_core-0.1.42-py3-none-any.whl", hash = "sha256:c5653ffa08a44f740295c157a24c0def4a753333f6a2c41f76bf431cd00be8b5"}, + {file = "langchain_core-0.1.42.tar.gz", hash = "sha256:40751bf60ea5d8e2b2efe65290db434717ee3834870c002e40e2811f09d814e6"}, ] [package.dependencies] @@ -3483,6 +3608,133 @@ webencodings = ">=0.4" doc = ["sphinx", "sphinx_rtd_theme"] test = ["flake8", "isort", "pytest"] +[[package]] +name = "tokenizers" +version = "0.15.2" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tokenizers-0.15.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:52f6130c9cbf70544287575a985bf44ae1bda2da7e8c24e97716080593638012"}, + {file = "tokenizers-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:054c1cc9c6d68f7ffa4e810b3d5131e0ba511b6e4be34157aa08ee54c2f8d9ee"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9b9b070fdad06e347563b88c278995735292ded1132f8657084989a4c84a6d5"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea621a7eef4b70e1f7a4e84dd989ae3f0eeb50fc8690254eacc08acb623e82f1"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7fd9a5141634fa3aa8d6b7be362e6ae1b4cda60da81388fa533e0b552c98fd"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44f2a832cd0825295f7179eaf173381dc45230f9227ec4b44378322d900447c9"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b9ec69247a23747669ec4b0ca10f8e3dfb3545d550258129bd62291aabe8605"}, + {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b6a4c78da863ff26dbd5ad9a8ecc33d8a8d97b535172601cf00aee9d7ce9ce"}, + {file = "tokenizers-0.15.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5ab2a4d21dcf76af60e05af8063138849eb1d6553a0d059f6534357bce8ba364"}, + {file = "tokenizers-0.15.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a47acfac7e511f6bbfcf2d3fb8c26979c780a91e06fb5b9a43831b2c0153d024"}, + {file = "tokenizers-0.15.2-cp310-none-win32.whl", hash = "sha256:064ff87bb6acdbd693666de9a4b692add41308a2c0ec0770d6385737117215f2"}, + {file = "tokenizers-0.15.2-cp310-none-win_amd64.whl", hash = "sha256:3b919afe4df7eb6ac7cafd2bd14fb507d3f408db7a68c43117f579c984a73843"}, + {file = "tokenizers-0.15.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:89cd1cb93e4b12ff39bb2d626ad77e35209de9309a71e4d3d4672667b4b256e7"}, + {file = "tokenizers-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cfed5c64e5be23d7ee0f0e98081a25c2a46b0b77ce99a4f0605b1ec43dd481fa"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a907d76dcfda37023ba203ab4ceeb21bc5683436ebefbd895a0841fd52f6f6f2"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20ea60479de6fc7b8ae756b4b097572372d7e4032e2521c1bbf3d90c90a99ff0"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48e2b9335be2bc0171df9281385c2ed06a15f5cf121c44094338306ab7b33f2c"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:112a1dd436d2cc06e6ffdc0b06d55ac019a35a63afd26475205cb4b1bf0bfbff"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4620cca5c2817177ee8706f860364cc3a8845bc1e291aaf661fb899e5d1c45b0"}, + {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccd73a82751c523b3fc31ff8194702e4af4db21dc20e55b30ecc2079c5d43cb7"}, + {file = "tokenizers-0.15.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:107089f135b4ae7817affe6264f8c7a5c5b4fd9a90f9439ed495f54fcea56fb4"}, + {file = "tokenizers-0.15.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0ff110ecc57b7aa4a594396525a3451ad70988e517237fe91c540997c4e50e29"}, + {file = "tokenizers-0.15.2-cp311-none-win32.whl", hash = "sha256:6d76f00f5c32da36c61f41c58346a4fa7f0a61be02f4301fd30ad59834977cc3"}, + {file = "tokenizers-0.15.2-cp311-none-win_amd64.whl", hash = "sha256:cc90102ed17271cf0a1262babe5939e0134b3890345d11a19c3145184b706055"}, + {file = "tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670"}, + {file = "tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c"}, + {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456"}, + {file = "tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834"}, + {file = "tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d"}, + {file = "tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b"}, + {file = "tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221"}, + {file = "tokenizers-0.15.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:38bfb0204ff3246ca4d5e726e8cc8403bfc931090151e6eede54d0e0cf162ef0"}, + {file = "tokenizers-0.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c861d35e8286a53e06e9e28d030b5a05bcbf5ac9d7229e561e53c352a85b1fc"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:936bf3842db5b2048eaa53dade907b1160f318e7c90c74bfab86f1e47720bdd6"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:620beacc3373277700d0e27718aa8b25f7b383eb8001fba94ee00aeea1459d89"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2735ecbbf37e52db4ea970e539fd2d450d213517b77745114f92867f3fc246eb"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:473c83c5e2359bb81b0b6fde870b41b2764fcdd36d997485e07e72cc3a62264a"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968fa1fb3c27398b28a4eca1cbd1e19355c4d3a6007f7398d48826bbe3a0f728"}, + {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:865c60ae6eaebdde7da66191ee9b7db52e542ed8ee9d2c653b6d190a9351b980"}, + {file = "tokenizers-0.15.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7c0d8b52664ab2d4a8d6686eb5effc68b78608a9008f086a122a7b2996befbab"}, + {file = "tokenizers-0.15.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f33dfbdec3784093a9aebb3680d1f91336c56d86cc70ddf88708251da1fe9064"}, + {file = "tokenizers-0.15.2-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d44ba80988ff9424e33e0a49445072ac7029d8c0e1601ad25a0ca5f41ed0c1d6"}, + {file = "tokenizers-0.15.2-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:dce74266919b892f82b1b86025a613956ea0ea62a4843d4c4237be2c5498ed3a"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0ef06b9707baeb98b316577acb04f4852239d856b93e9ec3a299622f6084e4be"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c73e2e74bbb07910da0d37c326869f34113137b23eadad3fc00856e6b3d9930c"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eeb12daf02a59e29f578a865f55d87cd103ce62bd8a3a5874f8fdeaa82e336b"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ba9f6895af58487ca4f54e8a664a322f16c26bbb442effd01087eba391a719e"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccec77aa7150e38eec6878a493bf8c263ff1fa8a62404e16c6203c64c1f16a26"}, + {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f40604f5042ff210ba82743dda2b6aa3e55aa12df4e9f2378ee01a17e2855e"}, + {file = "tokenizers-0.15.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5645938a42d78c4885086767c70923abad047163d809c16da75d6b290cb30bbe"}, + {file = "tokenizers-0.15.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:05a77cbfebe28a61ab5c3891f9939cc24798b63fa236d84e5f29f3a85a200c00"}, + {file = "tokenizers-0.15.2-cp37-none-win32.whl", hash = "sha256:361abdc068e8afe9c5b818769a48624687fb6aaed49636ee39bec4e95e1a215b"}, + {file = "tokenizers-0.15.2-cp37-none-win_amd64.whl", hash = "sha256:7ef789f83eb0f9baeb4d09a86cd639c0a5518528f9992f38b28e819df397eb06"}, + {file = "tokenizers-0.15.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4fe1f74a902bee74a3b25aff180fbfbf4f8b444ab37c4d496af7afd13a784ed2"}, + {file = "tokenizers-0.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c4b89038a684f40a6b15d6b09f49650ac64d951ad0f2a3ea9169687bbf2a8ba"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d05a1b06f986d41aed5f2de464c003004b2df8aaf66f2b7628254bcbfb72a438"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:508711a108684111ec8af89d3a9e9e08755247eda27d0ba5e3c50e9da1600f6d"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:daa348f02d15160cb35439098ac96e3a53bacf35885072611cd9e5be7d333daa"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:494fdbe5932d3416de2a85fc2470b797e6f3226c12845cadf054dd906afd0442"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2d60f5246f4da9373f75ff18d64c69cbf60c3bca597290cea01059c336d2470"}, + {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93268e788825f52de4c7bdcb6ebc1fcd4a5442c02e730faa9b6b08f23ead0e24"}, + {file = "tokenizers-0.15.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6fc7083ab404019fc9acafe78662c192673c1e696bd598d16dc005bd663a5cf9"}, + {file = "tokenizers-0.15.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41e39b41e5531d6b2122a77532dbea60e171ef87a3820b5a3888daa847df4153"}, + {file = "tokenizers-0.15.2-cp38-none-win32.whl", hash = "sha256:06cd0487b1cbfabefb2cc52fbd6b1f8d4c37799bd6c6e1641281adaa6b2504a7"}, + {file = "tokenizers-0.15.2-cp38-none-win_amd64.whl", hash = "sha256:5179c271aa5de9c71712e31cb5a79e436ecd0d7532a408fa42a8dbfa4bc23fd9"}, + {file = "tokenizers-0.15.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82f8652a74cc107052328b87ea8b34291c0f55b96d8fb261b3880216a9f9e48e"}, + {file = "tokenizers-0.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:02458bee6f5f3139f1ebbb6d042b283af712c0981f5bc50edf771d6b762d5e4f"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c9a09cd26cca2e1c349f91aa665309ddb48d71636370749414fbf67bc83c5343"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:158be8ea8554e5ed69acc1ce3fbb23a06060bd4bbb09029431ad6b9a466a7121"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ddba9a2b0c8c81633eca0bb2e1aa5b3a15362b1277f1ae64176d0f6eba78ab1"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ef5dd1d39797044642dbe53eb2bc56435308432e9c7907728da74c69ee2adca"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:454c203164e07a860dbeb3b1f4a733be52b0edbb4dd2e5bd75023ffa8b49403a"}, + {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cf6b7f1d4dc59af960e6ffdc4faffe6460bbfa8dce27a58bf75755ffdb2526d"}, + {file = "tokenizers-0.15.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2ef09bbc16519f6c25d0c7fc0c6a33a6f62923e263c9d7cca4e58b8c61572afb"}, + {file = "tokenizers-0.15.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c9a2ebdd2ad4ec7a68e7615086e633857c85e2f18025bd05d2a4399e6c5f7169"}, + {file = "tokenizers-0.15.2-cp39-none-win32.whl", hash = "sha256:918fbb0eab96fe08e72a8c2b5461e9cce95585d82a58688e7f01c2bd546c79d0"}, + {file = "tokenizers-0.15.2-cp39-none-win_amd64.whl", hash = "sha256:524e60da0135e106b254bd71f0659be9f89d83f006ea9093ce4d1fab498c6d0d"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9b648a58281c4672212fab04e60648fde574877d0139cd4b4f93fe28ca8944"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7c7d18b733be6bbca8a55084027f7be428c947ddf871c500ee603e375013ffba"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:13ca3611de8d9ddfbc4dc39ef54ab1d2d4aaa114ac8727dfdc6a6ec4be017378"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:237d1bf3361cf2e6463e6c140628e6406766e8b27274f5fcc62c747ae3c6f094"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67a0fe1e49e60c664915e9fb6b0cb19bac082ab1f309188230e4b2920230edb3"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4e022fe65e99230b8fd89ebdfea138c24421f91c1a4f4781a8f5016fd5cdfb4d"}, + {file = "tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d857be2df69763362ac699f8b251a8cd3fac9d21893de129bc788f8baaef2693"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:708bb3e4283177236309e698da5fcd0879ce8fd37457d7c266d16b550bcbbd18"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:64c35e09e9899b72a76e762f9854e8750213f67567787d45f37ce06daf57ca78"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1257f4394be0d3b00de8c9e840ca5601d0a4a8438361ce9c2b05c7d25f6057b"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02272fe48280e0293a04245ca5d919b2c94a48b408b55e858feae9618138aeda"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dc3ad9ebc76eabe8b1d7c04d38be884b8f9d60c0cdc09b0aa4e3bcf746de0388"}, + {file = "tokenizers-0.15.2-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:32e16bdeffa7c4f46bf2152172ca511808b952701d13e7c18833c0b73cb5c23f"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fb16ba563d59003028b678d2361a27f7e4ae0ab29c7a80690efa20d829c81fdb"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:2277c36d2d6cdb7876c274547921a42425b6810d38354327dd65a8009acf870c"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cf75d32e8d250781940d07f7eece253f2fe9ecdb1dc7ba6e3833fa17b82fcbc"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1b3b31884dc8e9b21508bb76da80ebf7308fdb947a17affce815665d5c4d028"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10122d8d8e30afb43bb1fe21a3619f62c3e2574bff2699cf8af8b0b6c5dc4a3"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d88b96ff0fe8e91f6ef01ba50b0d71db5017fa4e3b1d99681cec89a85faf7bf7"}, + {file = "tokenizers-0.15.2-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:37aaec5a52e959892870a7c47cef80c53797c0db9149d458460f4f31e2fb250e"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e2ea752f2b0fe96eb6e2f3adbbf4d72aaa1272079b0dfa1145507bd6a5d537e6"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b19a808d8799fda23504a5cd31d2f58e6f52f140380082b352f877017d6342b"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:64c86e5e068ac8b19204419ed8ca90f9d25db20578f5881e337d203b314f4104"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de19c4dc503c612847edf833c82e9f73cd79926a384af9d801dcf93f110cea4e"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea09acd2fe3324174063d61ad620dec3bcf042b495515f27f638270a7d466e8b"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cf27fd43472e07b57cf420eee1e814549203d56de00b5af8659cb99885472f1f"}, + {file = "tokenizers-0.15.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7ca22bd897537a0080521445d91a58886c8c04084a6a19e6c78c586e0cfa92a5"}, + {file = "tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91"}, +] + +[package.dependencies] +huggingface_hub = ">=0.16.4,<1.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools_rust", "sphinx", "sphinx_rtd_theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] + [[package]] name = "tomli" version = "2.0.1" @@ -3857,4 +4109,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.9.0,<4.0" -content-hash = "a3e232b85db7332e70c88b6715a0b5716bcb424fe7e8ac7cb65057a8b93b39a4" +content-hash = "5d0e5b014e355f65a1731548531a53313cff61d729312d64f1f3b339673e2f5c" diff --git a/pyproject.toml b/pyproject.toml index 54e5d1245..49e38f9ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ repository = "https://www.github.com/langchain-ai/langgraph" [tool.poetry.dependencies] python = ">=3.9.0,<4.0" -langchain-core = "0.1.42rc1" +langchain-core = "^0.1.42" [tool.poetry.group.test.dependencies] @@ -42,6 +42,7 @@ jupyter = "^1.0.0" langchain = "^0.1.0" langchainhub = "^0.1.14" langchain-openai = "^0.1.2" +langchain-anthropic = "^0.1.8" [tool.ruff] select = [ "E", "F", "I" ]