diff --git a/docs/cassettes/pass-run-time-values-to-tools_2e3fd1e2-cc19-4023-8ffa-b0fc13da9e09.msgpack.zlib b/docs/cassettes/pass-run-time-values-to-tools_2e3fd1e2-cc19-4023-8ffa-b0fc13da9e09.msgpack.zlib
new file mode 100644
index 000000000..9f2ff8fbb
--- /dev/null
+++ b/docs/cassettes/pass-run-time-values-to-tools_2e3fd1e2-cc19-4023-8ffa-b0fc13da9e09.msgpack.zlib
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/cassettes/pass-run-time-values-to-tools_4a2128ed-e23f-4f25-a026-0c6590f01a1c.msgpack.zlib b/docs/cassettes/pass-run-time-values-to-tools_4a2128ed-e23f-4f25-a026-0c6590f01a1c.msgpack.zlib
deleted file mode 100644
index 1a8a1b8dc..000000000
--- a/docs/cassettes/pass-run-time-values-to-tools_4a2128ed-e23f-4f25-a026-0c6590f01a1c.msgpack.zlib
+++ /dev/null
@@ -1 +0,0 @@
-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
\ No newline at end of file
diff --git a/docs/cassettes/pass-run-time-values-to-tools_8edb04b9-40b6-46f1-a7a8-4b2d8aba7752.msgpack.zlib b/docs/cassettes/pass-run-time-values-to-tools_8edb04b9-40b6-46f1-a7a8-4b2d8aba7752.msgpack.zlib
deleted file mode 100644
index afaf191c3..000000000
--- a/docs/cassettes/pass-run-time-values-to-tools_8edb04b9-40b6-46f1-a7a8-4b2d8aba7752.msgpack.zlib
+++ /dev/null
@@ -1 +0,0 @@
-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
\ No newline at end of file
diff --git a/docs/cassettes/pass-run-time-values-to-tools_c0858273-10f2-45c4-a922-b11321ac3fae.msgpack.zlib b/docs/cassettes/pass-run-time-values-to-tools_c0858273-10f2-45c4-a922-b11321ac3fae.msgpack.zlib
new file mode 100644
index 000000000..5cf7c3901
--- /dev/null
+++ b/docs/cassettes/pass-run-time-values-to-tools_c0858273-10f2-45c4-a922-b11321ac3fae.msgpack.zlib
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/cassettes/pass-run-time-values-to-tools_d683986f-faf4-4724-a13f-bac39ea9bafe.msgpack.zlib b/docs/cassettes/pass-run-time-values-to-tools_d683986f-faf4-4724-a13f-bac39ea9bafe.msgpack.zlib
new file mode 100644
index 000000000..339d0b1ad
--- /dev/null
+++ b/docs/cassettes/pass-run-time-values-to-tools_d683986f-faf4-4724-a13f-bac39ea9bafe.msgpack.zlib
@@ -0,0 +1 @@
+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
\ No newline at end of file
diff --git a/docs/docs/how-tos/index.md b/docs/docs/how-tos/index.md
index 7ee97325e..2ecf2d853 100644
--- a/docs/docs/how-tos/index.md
+++ b/docs/docs/how-tos/index.md
@@ -67,7 +67,7 @@ These guides show how to use different streaming modes.
- [How to call tools using ToolNode](tool-calling.ipynb)
- [How to handle tool calling errors](tool-calling-errors.ipynb)
-- [How to pass graph state to tools](pass-run-time-values-to-tools.ipynb)
+- [How to pass runtime values to tools](pass-run-time-values-to-tools.ipynb)
- [How to pass config to tools](pass-config-to-tools.ipynb)
- [How to handle large numbers of tools](many-tools.ipynb)
diff --git a/docs/docs/how-tos/pass-run-time-values-to-tools.ipynb b/docs/docs/how-tos/pass-run-time-values-to-tools.ipynb
index 206c9c5fd..df1d803a2 100644
--- a/docs/docs/how-tos/pass-run-time-values-to-tools.ipynb
+++ b/docs/docs/how-tos/pass-run-time-values-to-tools.ipynb
@@ -5,15 +5,93 @@
"id": "51466c8d-8ce4-4b3d-be4e-18fdbeda5f53",
"metadata": {},
"source": [
- "# How to pass graph state to tools\n",
+ "# How to pass runtime values to tools\n",
"\n",
- "Sometimes we need to pass in agent state to our tools. This type of stateful tools is useful when a tool's output is affected by past agent steps (e.g. if you're using a sub-agent as a tool, and want to pass the message history in to the sub-agent), or when a tool's input needs to be validated given context from past agent steps. \n",
+ "Sometimes, you want to let a tool-calling LLM populate a *subset* of the tool functions' arguments and provide the other values for the other arguments at runtime. If you're using LangChain-style [tools](https://python.langchain.com/docs/concepts/#tools), an easy way to handle this is by annotating function parameters with [InjectedArg](https://python.langchain.com/docs/how_to/tool_runtime/). This annotation excludes that parameter from being shown to the LLM.\n",
"\n",
- "In this guide we'll demonstrate how to create tools that take agent state as input.\n",
+ "In LangGraph applications you might want to pass the graph state or [shared memory](https://langchain-ai.github.io/langgraph/how-tos/cross-thread-persistence/) (store) to the tools at runtime. This type of stateful tools is useful when a tool's output is affected by past agent steps (e.g. if you're using a sub-agent as a tool, and want to pass the message history in to the sub-agent), or when a tool's input needs to be validated given context from past agent steps.\n",
"\n",
- "This is a special case of [passing runtime arguments to tools](https://python.langchain.com/docs/how_to/tool_runtime/), which you can learn about in the LangChain docs."
+ "In this guide we'll demonstrate how to do so using LangGraph's prebuilt [ToolNode](https://langchain-ai.github.io/langgraph/how-tos/tool-calling/).\n",
+ "\n",
+ "
\n",
+ "
Prerequisites
\n",
+ "
\n",
+ " This guide targets **LangChain tool calling** assumes familiarity with the following:\n",
+ "
\n",
+ " - \n",
+ " \n",
+ " Tools\n",
+ " \n",
+ "
\n",
+ " - \n",
+ " \n",
+ " State\n",
+ " \n",
+ "
\n",
+ " - \n",
+ " \n",
+ " Tool-calling\n",
+ " \n",
+ "
\n",
+ "
\n",
+ " You can still use tool calling in LangGraph using your provider SDK without losing any of LangGraph's core features.\n",
+ " \n",
+ "
\n",
+ "\n",
+ "The core technique the examples below is to **annotate** a parameter as \"injected\", meaning it will be injected by your program and should not be seen or populated by the LLM. Let the following codesnippet serve as a tl;dr:\n",
+ "\n",
+ "```python\n",
+ "from typing import Annotated\n",
+ "\n",
+ "from langchain_core.runnables import RunnableConfig\n",
+ "from langchain_core.tools import InjectedToolArg\n",
+ "from langgraph.store.base import BaseStore\n",
+ "\n",
+ "from langgraph.prebuilt import InjectedState, InjectedStore\n",
+ "\n",
+ "\n",
+ "# Can be sync or async; @tool decorator not required\n",
+ "async def my_tool(\n",
+ " # These arguments are populated by the LLM\n",
+ " some_arg: str,\n",
+ " another_arg: float,\n",
+ " # The config: RunnableConfig is always available in LangChain calls\n",
+ " # This is not exposed to the LLM\n",
+ " config: RunnableConfig,\n",
+ " # The following three are specific to the prebuilt ToolNode\n",
+ " # (and `create_react_agent` by extension). If you are invoking the\n",
+ " # tool on its own (in your own node), then you would need to provide these yourself.\n",
+ " store: Annotated[BaseStore, InjectedStore],\n",
+ " # This passes in the full state.\n",
+ " state: Annotated[State, InjectedState],\n",
+ " # You can also inject single fields from your state if you\n",
+ " messages: Annotated[list, InjectedState(\"messages\")]\n",
+ " # The following is not compatible with create_react_agent or ToolNode\n",
+ " # You can also exclude other arguments from being shown to the model.\n",
+ " # These must be provided manually and are useful if you call the tools/functions in your own node\n",
+ " # some_other_arg=Annotated[\"MyPrivateClass\", InjectedToolArg],\n",
+ "):\n",
+ " \"\"\"Call my_tool to have an impact on the real world.\n",
+ "\n",
+ " Args:\n",
+ " some_arg: a very important argument\n",
+ " another_arg: another argument the LLM will provide\n",
+ " \"\"\" # The docstring becomes the description for your tool and is passed to the model\n",
+ " print(some_arg, another_arg, config, store, state, messages)\n",
+ " # Config, some_other_rag, store, and state are all \"hidden\" from\n",
+ " # LangChain models when passed to bind_tools or with_structured_output\n",
+ " return \"... some response\"\n",
+ "```"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5c205242",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
{
"cell_type": "markdown",
"id": "7cbd446a-808f-4394-be92-d45ab818953c",
@@ -26,13 +104,13 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 1,
"id": "af4ce0ba-7596-4e5f-8bf8-0b0bd6e62833",
"metadata": {},
"outputs": [],
"source": [
"%%capture --no-stderr\n",
- "%pip install --quiet -U langgraph langchain langchain-openai"
+ "%pip install --quiet -U langgraph langchain-openai"
]
},
{
@@ -45,10 +123,18 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 2,
"id": "c903a1cf-2977-4e2d-ad7d-8b3946821d89",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "OPENAI_API_KEY: ········\n"
+ ]
+ }
+ ],
"source": [
"import getpass\n",
"import os\n",
@@ -76,11 +162,40 @@
]
},
{
+ "attachments": {},
"cell_type": "markdown",
- "id": "21ac643b-cb06-4724-a80c-2862ba4773f1",
+ "id": "1a5908ab-7c1f-4832-85ae-511536b89b9f",
"metadata": {},
"source": [
- "## Defining the tools\n",
+ "## Pass graph state to tools\n",
+ "\n",
+ "Let's first take a look at how to give our tools access to the graph state. We'll need to define our graph state:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "a2ded72f-1450-4295-a879-fd213ecfd4b5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from typing import List\n",
+ "\n",
+ "# this is the state schema used by the prebuilt create_react_agent we'll be using below\n",
+ "from langgraph.prebuilt.chat_agent_executor import AgentState\n",
+ "from langchain_core.documents import Document\n",
+ "\n",
+ "\n",
+ "class State(AgentState):\n",
+ " docs: List[str]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "75d2d28c-0da3-46c6-9c67-979a71b5f517",
+ "metadata": {},
+ "source": [
+ "### Define the tools\n",
"\n",
"We'll want our tool to take graph state as an input, but we don't want the model to try to generate this input when calling the tool. We can use the `InjectedState` annotation to mark arguments as required graph state (or some field of graph state. These arguments will not be generated by the model. When using `ToolNode`, graph state will automatically be passed in to the relevant tools and arguments.\n",
"\n",
@@ -102,7 +217,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 4,
"id": "1d36e782-80f4-4334-b7d7-ee4c79864480",
"metadata": {},
"outputs": [],
@@ -110,69 +225,15 @@
"from typing import List, Tuple\n",
"from typing_extensions import Annotated\n",
"\n",
- "from langchain_core.documents import Document\n",
"from langchain_core.messages import ToolMessage\n",
"from langchain_core.tools import tool\n",
"from langgraph.prebuilt import InjectedState\n",
"\n",
- "from pydantic import BaseModel\n",
"\n",
- "\n",
- "@tool(parse_docstring=True, response_format=\"content_and_artifact\")\n",
- "def get_context(question: List[str]) -> Tuple[str, List[Document]]:\n",
- " \"\"\"Get context on the question.\n",
- "\n",
- " Args:\n",
- " question: The user question\n",
- " \"\"\"\n",
- " # return constant dummy output\n",
- " docs = [\n",
- " Document(\n",
- " \"FooBar company just raised 1 Billion dollars!\",\n",
- " metadata={\"source\": \"twitter\"},\n",
- " ),\n",
- " Document(\n",
- " \"FooBar company is now only hiring AI's\", metadata={\"source\": \"twitter\"}\n",
- " ),\n",
- " Document(\n",
- " \"FooBar company was founded in 2019\", metadata={\"source\": \"wikipedia\"}\n",
- " ),\n",
- " Document(\n",
- " \"FooBar company makes friendly robots\", metadata={\"source\": \"wikipedia\"}\n",
- " ),\n",
- " ]\n",
- " return \"\\n\\n\".join(doc.page_content for doc in docs), docs\n",
- "\n",
- "\n",
- "@tool(parse_docstring=True, response_format=\"content_and_artifact\")\n",
- "def cite_context_sources(\n",
- " claim: str, state: Annotated[dict, InjectedState]\n",
- ") -> Tuple[str, List[Document]]:\n",
- " \"\"\"Cite which source a claim was based on.\n",
- "\n",
- " Args:\n",
- " claim: The claim that was made.\n",
- " \"\"\"\n",
- " docs = []\n",
- " # We get the potentially cited docs from past ToolMessages in our state.\n",
- " for msg in state[\"messages\"]:\n",
- " if isinstance(msg, ToolMessage) and msg.name == \"get_context\":\n",
- " docs.extend(msg.artifact)\n",
- "\n",
- " class Cite(BaseModel):\n",
- " \"\"\"Return the index(es) of the documents that justify the claim\"\"\"\n",
- "\n",
- " indexes: List[int]\n",
- "\n",
- " structured_model = model.with_structured_output(Cite)\n",
- " system = f\"Which of the following documents best justifies the claim:\\n\\n{claim}\"\n",
- " context = \"\\n\\n\".join(\n",
- " f\"Document {i}:\\n\" + doc.page_content for i, doc in enumerate(docs)\n",
- " )\n",
- " citation = structured_model.invoke([(\"system\", system), (\"human\", context)])\n",
- " cited_docs = [docs[i] for i in citation.indexes]\n",
- " sources = \", \".join(doc.metadata[\"source\"] for doc in cited_docs)\n",
- " return sources, cited_docs"
+ "@tool\n",
+ "def get_context(question: str, state: Annotated[dict, InjectedState]):\n",
+ " \"\"\"Get relevant context for answering the question.\"\"\"\n",
+ " return \"\\n\\n\".join(doc for doc in state[\"docs\"])"
]
},
{
@@ -185,30 +246,28 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": 5,
"id": "1092929b-c939-4b2a-9f9c-e725b0e34af2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "{'description': 'Cite which source a claim was based on.',\n",
- " 'properties': {'claim': {'description': 'The claim that was made.',\n",
- " 'title': 'Claim',\n",
- " 'type': 'string'},\n",
+ "{'description': 'Get relevant context for answering the question.',\n",
+ " 'properties': {'question': {'title': 'Question', 'type': 'string'},\n",
" 'state': {'title': 'State', 'type': 'object'}},\n",
- " 'required': ['claim', 'state'],\n",
- " 'title': 'cite_context_sources',\n",
+ " 'required': ['question', 'state'],\n",
+ " 'title': 'get_context',\n",
" 'type': 'object'}"
]
},
- "execution_count": 3,
+ "execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
- "cite_context_sources.get_input_schema().schema()"
+ "get_context.get_input_schema().schema()"
]
},
{
@@ -221,20 +280,217 @@
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 6,
"id": "3912bb51-3107-4335-a659-021c5d89fb37",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "{'title': 'cite_context_sources',\n",
- " 'description': 'Cite which source a claim was based on.',\n",
- " 'type': 'object',\n",
- " 'properties': {'claim': {'title': 'Claim',\n",
- " 'description': 'The claim that was made.',\n",
- " 'type': 'string'}},\n",
- " 'required': ['claim']}"
+ "{'description': 'Get relevant context for answering the question.',\n",
+ " 'properties': {'question': {'title': 'Question', 'type': 'string'}},\n",
+ " 'required': ['question'],\n",
+ " 'title': 'get_context',\n",
+ " 'type': 'object'}"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "get_context.tool_call_schema.schema()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8e8b9211-93d0-4ad5-aa7a-9c09099c53ff",
+ "metadata": {},
+ "source": [
+ "### Define the graph\n",
+ "\n",
+ "In this example we will be using a [prebuilt ReAct agent](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/). We'll first need to define our model and a tool-calling node ([ToolNode](https://langchain-ai.github.io/langgraph/reference/prebuilt/#langgraph.prebuilt.tool_node.ToolNode)):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "be341be4-bdc3-4e78-9bc1-7486da27fd7c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from langchain_openai import ChatOpenAI\n",
+ "from langgraph.prebuilt import ToolNode, create_react_agent\n",
+ "from langgraph.checkpoint.memory import MemorySaver\n",
+ "\n",
+ "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
+ "tools = [get_context]\n",
+ "\n",
+ "# ToolNode will automatically take care of injecting state into tools\n",
+ "tool_node = ToolNode(tools)\n",
+ "\n",
+ "checkpointer = MemorySaver()\n",
+ "graph = create_react_agent(model, tools, state_schema=State, checkpointer=checkpointer)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "547c3931-3dae-4281-ad4e-4b51305594d4",
+ "metadata": {},
+ "source": [
+ "### Use it!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "c0858273-10f2-45c4-a922-b11321ac3fae",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "================================\u001b[1m Human Message \u001b[0m=================================\n",
+ "\n",
+ "what's the latest news about FooBar\n",
+ "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
+ "Tool Calls:\n",
+ " get_context (call_UkqfR7z2cLJQjhatUpDeEa5H)\n",
+ " Call ID: call_UkqfR7z2cLJQjhatUpDeEa5H\n",
+ " Args:\n",
+ " question: latest news about FooBar\n",
+ "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
+ "Name: get_context\n",
+ "\n",
+ "FooBar company just raised 1 Billion dollars!\n",
+ "\n",
+ "FooBar company was founded in 2019\n",
+ "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
+ "\n",
+ "The latest news about FooBar is that the company has just raised 1 billion dollars.\n"
+ ]
+ }
+ ],
+ "source": [
+ "docs = [\n",
+ " \"FooBar company just raised 1 Billion dollars!\",\n",
+ " \"FooBar company was founded in 2019\",\n",
+ "]\n",
+ "\n",
+ "inputs = {\n",
+ " \"messages\": [{\"type\": \"user\", \"content\": \"what's the latest news about FooBar\"}],\n",
+ " \"docs\": docs,\n",
+ "}\n",
+ "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
+ "for chunk in graph.stream(inputs, config, stream_mode=\"values\"):\n",
+ " chunk[\"messages\"][-1].pretty_print()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "09dd6d7d-d8a8-48f9-a30d-fba79fb7cb1e",
+ "metadata": {},
+ "source": [
+ "## Pass shared memory (store) to the graph\n",
+ "\n",
+ "You might also want to give tools access to memory that is shared across multiple conversations or users. We can do it by passing LangGraph [Store](https://langchain-ai.github.io/langgraph/how-tos/cross-thread-persistence/) to the tools using a different annotation -- `InjectedStore`.\n",
+ "\n",
+ "Let's modify our example to save the documents in an in-memory store and retrieve them using `get_context` tool. We'll also make the documents accessible based on a user ID, so that some documents are only visible to certain users. The tool will then use the `user_id` provided in the [config](https://langchain-ai.github.io/langgraph/how-tos/pass-config-to-tools/) to retrieve a correct set of documents."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "41644e7e-bda2-4c0d-95da-99572554c02d",
+ "metadata": {},
+ "source": [
+ "\n",
+ "
Note
\n",
+ "
\n",
+ " \n",
+ " Support for Store API and InjectedStore used in this notebook was added in LangGraph v0.2.34.\n",
+ " \n",
+ " \n",
+ " InjectedStore annotation requires langchain-core >= 0.3.8\n",
+ " \n",
+ " \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "759186ed-5506-4e9a-80c4-0a2405ff58de",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from langgraph.store.memory import InMemoryStore\n",
+ "\n",
+ "doc_store = InMemoryStore()\n",
+ "\n",
+ "namespace = (\"documents\", \"1\") # user ID\n",
+ "doc_store.put(\n",
+ " namespace, \"doc_0\", {\"doc\": \"FooBar company just raised 1 Billion dollars!\"}\n",
+ ")\n",
+ "namespace = (\"documents\", \"2\") # user ID\n",
+ "doc_store.put(namespace, \"doc_1\", {\"doc\": \"FooBar company was founded in 2019\"})"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c1a6e4e4-3256-4d42-aa5f-de337bfa6a97",
+ "metadata": {},
+ "source": [
+ "### Define the tools"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "91ab7e2b-7df4-41ea-8b20-e2ba950aacb2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from langgraph.store.base import BaseStore\n",
+ "from langchain_core.runnables import RunnableConfig\n",
+ "from langgraph.prebuilt import InjectedStore\n",
+ "\n",
+ "\n",
+ "@tool\n",
+ "def get_context(\n",
+ " question: str,\n",
+ " config: RunnableConfig,\n",
+ " store: Annotated[BaseStore, InjectedStore()],\n",
+ ") -> Tuple[str, List[Document]]:\n",
+ " \"\"\"Get relevant context for answering the question.\"\"\"\n",
+ " user_id = config.get(\"configurable\", {}).get(\"user_id\")\n",
+ " docs = [item.value[\"doc\"] for item in store.search((\"documents\", user_id))]\n",
+ " return \"\\n\\n\".join(doc for doc in docs)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fcd29b33-b647-4e11-8942-2d47f129095b",
+ "metadata": {},
+ "source": [
+ "We can also verify that the tool-calling model will ignore `store` arg of `get_context` tool:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "07bdc124-06bb-4c25-a18e-700ab7aa6521",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'description': 'Get relevant context for answering the question.',\n",
+ " 'properties': {'question': {'title': 'Question', 'type': 'string'}},\n",
+ " 'required': ['question'],\n",
+ " 'title': 'get_context',\n",
+ " 'type': 'object'}"
]
},
"execution_count": 11,
@@ -243,297 +499,138 @@
}
],
"source": [
- "cite_context_sources.tool_call_schema.schema()"
+ "get_context.tool_call_schema.schema()"
]
},
{
"cell_type": "markdown",
- "id": "8e8b9211-93d0-4ad5-aa7a-9c09099c53ff",
+ "id": "10622c58-db67-476b-81a3-1b964f5b471f",
"metadata": {},
"source": [
- "## Define the agent state\n",
+ "### Define the graph\n",
"\n",
- "The main type of graph in `langgraph` is the `StateGraph`.\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"
+ "Let's update our ReAct agent:"
]
},
{
"cell_type": "code",
- "execution_count": 4,
- "id": "ea793afa-2eab-4901-910d-6eed90cd6564",
+ "execution_count": 12,
+ "id": "10c70125-a105-4103-89d2-2e93b401080c",
"metadata": {},
"outputs": [],
"source": [
- "import operator\n",
- "from typing import Annotated, Sequence\n",
- "from typing_extensions import TypedDict\n",
+ "tools = [get_context]\n",
"\n",
- "from langchain_core.messages import BaseMessage\n",
+ "# ToolNode will automatically take care of injecting Store into tools\n",
+ "tool_node = ToolNode(tools)\n",
"\n",
- "\n",
- "class AgentState(TypedDict):\n",
- " messages: Annotated[Sequence[BaseMessage], operator.add]"
+ "checkpointer = MemorySaver()\n",
+ "# NOTE: we need to pass our store to `create_react_agent` to make sure our graph is aware of it\n",
+ "graph = create_react_agent(model, tools, checkpointer=checkpointer, store=doc_store)"
]
},
{
"cell_type": "markdown",
- "id": "e03c5094-9297-4d19-a04e-3eedc75cefb4",
+ "id": "35ba8adc-c706-48a6-992f-24c03c4cee46",
"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/concepts/#langchain-expression-language-lcel).\n",
- "There are two main nodes we need for this:\n",
- "\n",
- "1. The agent: responsible for deciding what (if any) actions to take.\n",
- "2. A function to invoke tools: if the agent decides to take an action, this node will then execute that action.\n",
- "\n",
- "We will also need to define some edges.\n",
- "Some of these edges may be conditional.\n",
- "The reason they are conditional is that based on the output of a node, one of several paths may be taken.\n",
- "The path that is taken is not known until that node is run (the LLM decides).\n",
- "\n",
- "1. Conditional Edge: after the agent is called, we should either:\n",
- " a. If the agent said to take an action, then the function to invoke tools should be called\n",
- " b. If the agent said that it was finished, then it should finish\n",
- "2. Normal Edge: after the tools are invoked, it should always go back to the agent to decide what to do next\n",
- "\n",
- "Let's define the nodes, as well as a function to decide how what conditional edge to take."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "id": "3b541bb9-900c-40d0-964d-7b5dfee30667",
- "metadata": {},
- "outputs": [],
- "source": [
- "from langchain_openai import ChatOpenAI\n",
- "from langgraph.prebuilt import ToolNode\n",
- "\n",
- "model = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n",
- "\n",
- "\n",
- "# Define the function that determines whether to continue or not\n",
- "def should_continue(state, config):\n",
- " messages = state[\"messages\"]\n",
- " last_message = messages[-1]\n",
- " # If there is no function call, then we finish\n",
- " if not last_message.tool_calls:\n",
- " return \"end\"\n",
- " # Otherwise if there is, we continue\n",
- " else:\n",
- " return \"continue\"\n",
- "\n",
- "\n",
- "tools = [get_context, cite_context_sources]\n",
- "\n",
- "\n",
- "# Define the function that calls the model\n",
- "def call_model(state, config):\n",
- " messages = state[\"messages\"]\n",
- " model_with_tools = model.bind_tools(tools)\n",
- " response = model_with_tools.invoke(messages)\n",
- " # We return a list, because this will get added to the existing list\n",
- " return {\"messages\": [response]}\n",
- "\n",
- "\n",
- "# ToolNode will automatically take care of injecting state into tools\n",
- "tool_node = ToolNode(tools)"
+ "### Use it!"
]
},
{
"cell_type": "markdown",
- "id": "ffd6e892-946c-4899-8cc0-7c9291c1f73b",
+ "id": "c155ef73-91fe-459d-a3af-7ed254c19e23",
"metadata": {},
"source": [
- "## Define the graph\n",
- "\n",
- "We can now put it all together and define the graph!"
+ "Let's try running our graph with a `\"user_id\"` in the config."
]
},
{
"cell_type": "code",
- "execution_count": 6,
- "id": "813ae66c-3b58-4283-a02a-36da72a2ab90",
- "metadata": {},
- "outputs": [],
- "source": [
- "from langgraph.graph import END, START, StateGraph\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.add_edge(START, \"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": "code",
- "execution_count": 7,
- "id": "a8afd6ef",
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/jpeg": "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",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "from IPython.display import Image, display\n",
- "\n",
- "try:\n",
- " display(Image(app.get_graph(xray=True).draw_mermaid_png()))\n",
- "except Exception:\n",
- " # This requires some extra dependencies and is optional\n",
- " pass"
- ]
- },
- {
- "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/concepts/#langchain-expression-language-lcel) as all other LangChain runnables."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "id": "8edb04b9-40b6-46f1-a7a8-4b2d8aba7752",
+ "execution_count": 13,
+ "id": "d683986f-faf4-4724-a13f-bac39ea9bafe",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Output from node 'agent':\n",
- "---\n",
- "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_BidVTw5NiW2wp8Ez7m8dDoHI', 'function': {'arguments': '{\"question\":[\"latest news about FooBar\"]}', 'name': 'get_context'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 87, 'total_tokens': 106}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_c4e5b6fa31', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-fcac1b73-563e-4f4c-b1b0-626f55d377be-0', tool_calls=[{'name': 'get_context', 'args': {'question': ['latest news about FooBar']}, 'id': 'call_BidVTw5NiW2wp8Ez7m8dDoHI', 'type': 'tool_call'}], usage_metadata={'input_tokens': 87, 'output_tokens': 19, 'total_tokens': 106})]}\n",
+ "================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
- "---\n",
+ "what's the latest news about FooBar\n",
+ "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
+ "Tool Calls:\n",
+ " get_context (call_ocyHBpGgF3LPFOgRKURBfkGG)\n",
+ " Call ID: call_ocyHBpGgF3LPFOgRKURBfkGG\n",
+ " Args:\n",
+ " question: latest news about FooBar\n",
+ "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
+ "Name: get_context\n",
"\n",
- "Output from node 'action':\n",
- "---\n",
- "{'messages': [ToolMessage(content=\"FooBar company just raised 1 Billion dollars!\\n\\nFooBar company is now only hiring AI's\\n\\nFooBar company was founded in 2019\\n\\nFooBar company makes friendly robots\", name='get_context', tool_call_id='call_BidVTw5NiW2wp8Ez7m8dDoHI', artifact=[Document(metadata={'source': 'twitter'}, page_content='FooBar company just raised 1 Billion dollars!'), Document(metadata={'source': 'twitter'}, page_content=\"FooBar company is now only hiring AI's\"), Document(metadata={'source': 'wikipedia'}, page_content='FooBar company was founded in 2019'), Document(metadata={'source': 'wikipedia'}, page_content='FooBar company makes friendly robots')])]}\n",
+ "FooBar company just raised 1 Billion dollars!\n",
+ "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
- "---\n",
- "\n",
- "Output from node 'agent':\n",
- "---\n",
- "{'messages': [AIMessage(content='The latest news about FooBar is that the company has just raised 1 billion dollars!', response_metadata={'token_usage': {'completion_tokens': 19, 'prompt_tokens': 150, 'total_tokens': 169}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_c4e5b6fa31', 'finish_reason': 'stop', 'logprobs': None}, id='run-a8407471-7715-4c16-bd46-c29e5751e882-0', usage_metadata={'input_tokens': 150, 'output_tokens': 19, 'total_tokens': 169})]}\n",
- "\n",
- "---\n",
- "\n"
+ "The latest news about FooBar is that the company has just raised 1 billion dollars.\n"
]
}
],
"source": [
- "from langchain_core.messages import HumanMessage\n",
- "\n",
- "messages = [HumanMessage(\"what's the latest news about FooBar\")]\n",
- "for output in app.stream({\"messages\": messages}):\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",
- " messages.extend(value[\"messages\"])\n",
- " print(\"\\n---\\n\")"
+ "messages = [{\"type\": \"user\", \"content\": \"what's the latest news about FooBar\"}]\n",
+ "config = {\"configurable\": {\"thread_id\": \"1\", \"user_id\": \"1\"}}\n",
+ "for chunk in graph.stream({\"messages\": messages}, config, stream_mode=\"values\"):\n",
+ " chunk[\"messages\"][-1].pretty_print()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4fac8ead-a4fa-488f-835d-21872c67ec72",
+ "metadata": {},
+ "source": [
+ "We can see that the tool only retrieved the correct document for user \"1\" when looking up the information in the store. Let's now try it again for a different user:"
]
},
{
"cell_type": "code",
- "execution_count": 22,
- "id": "4a2128ed-e23f-4f25-a026-0c6590f01a1c",
+ "execution_count": 14,
+ "id": "2e3fd1e2-cc19-4023-8ffa-b0fc13da9e09",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Output from node 'agent':\n",
- "---\n",
- "{'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_EB0zaQypXMqEUzaqwflUr0zH', 'function': {'arguments': '{\"claim\":\"FooBar company just raised 1 Billion dollars!\"}', 'name': 'cite_context_sources'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 183, 'total_tokens': 208}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_c4e5b6fa31', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-b4952777-e2b3-4448-be87-200e6e80981b-0', tool_calls=[{'name': 'cite_context_sources', 'args': {'claim': 'FooBar company just raised 1 Billion dollars!'}, 'id': 'call_EB0zaQypXMqEUzaqwflUr0zH', 'type': 'tool_call'}], usage_metadata={'input_tokens': 183, 'output_tokens': 25, 'total_tokens': 208})]}\n",
+ "================================\u001b[1m Human Message \u001b[0m=================================\n",
"\n",
- "---\n",
+ "what's the latest news about FooBar\n",
+ "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
+ "Tool Calls:\n",
+ " get_context (call_zxO9KVlL8UxFQUMb8ETeHNvs)\n",
+ " Call ID: call_zxO9KVlL8UxFQUMb8ETeHNvs\n",
+ " Args:\n",
+ " question: latest news about FooBar\n",
+ "=================================\u001b[1m Tool Message \u001b[0m=================================\n",
+ "Name: get_context\n",
"\n",
- "Output from node 'action':\n",
- "---\n",
- "{'messages': [ToolMessage(content='twitter', name='cite_context_sources', tool_call_id='call_EB0zaQypXMqEUzaqwflUr0zH', artifact=[Document(metadata={'source': 'twitter'}, page_content='FooBar company just raised 1 Billion dollars!')])]}\n",
+ "FooBar company was founded in 2019\n",
+ "==================================\u001b[1m Ai Message \u001b[0m==================================\n",
"\n",
- "---\n",
- "\n",
- "Output from node 'agent':\n",
- "---\n",
- "{'messages': [AIMessage(content='The information that FooBar company just raised 1 billion dollars comes from Twitter.', response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 218, 'total_tokens': 235}, 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_400f27fa1f', 'finish_reason': 'stop', 'logprobs': None}, id='run-a0dede05-dadd-46f6-8654-746520d4cef8-0', usage_metadata={'input_tokens': 218, 'output_tokens': 17, 'total_tokens': 235})]}\n",
- "\n",
- "---\n",
- "\n"
+ "FooBar company was founded in 2019. If you need more specific or recent news, please let me know!\n"
]
}
],
"source": [
- "messages.append(HumanMessage(\"where did you get this information?\"))\n",
- "for output in app.stream({\"messages\": messages}):\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\")"
+ "messages = [{\"type\": \"user\", \"content\": \"what's the latest news about FooBar\"}]\n",
+ "config = {\"configurable\": {\"thread_id\": \"2\", \"user_id\": \"2\"}}\n",
+ "for chunk in graph.stream({\"messages\": messages}, config, stream_mode=\"values\"):\n",
+ " chunk[\"messages\"][-1].pretty_print()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "196fd8ec-38e7-4300-88f6-9f8774bba313",
+ "metadata": {},
+ "source": [
+ "We can see that the tool pulled in a different document this time."
]
}
],
diff --git a/docs/docs/how-tos/persistence.ipynb b/docs/docs/how-tos/persistence.ipynb
index caae5b363..9978471a4 100644
--- a/docs/docs/how-tos/persistence.ipynb
+++ b/docs/docs/how-tos/persistence.ipynb
@@ -47,7 +47,7 @@
"\n",
"
Note
\n",
"
\n",
- " If you need memory that is shared across multiple conversations or users (cross-thread persistence), check out this how-to guide).\n",
+ " If you need memory that is shared across multiple conversations or users (cross-thread persistence), check out this how-to guide).\n",
"
\n",
"
"
]
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 8fd5f6c68..dced365e1 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -167,7 +167,7 @@ nav:
- Tool calling:
- Call tools using ToolNode: how-tos/tool-calling.ipynb
- Handle tool calling errors: how-tos/tool-calling-errors.ipynb
- - Pass graph state to tools: how-tos/pass-run-time-values-to-tools.ipynb
+ - Pass runtime values to tools: how-tos/pass-run-time-values-to-tools.ipynb
- Pass config to tools: how-tos/pass-config-to-tools.ipynb
- Handle many tools: how-tos/many-tools.ipynb
- Subgraphs:
diff --git a/poetry.lock b/poetry.lock
index 05b3a8a43..b0f04b373 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
@@ -2477,13 +2477,13 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
[[package]]
name = "langchain-core"
-version = "0.3.6"
+version = "0.3.8"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0,>=3.9"
files = [
- {file = "langchain_core-0.3.6-py3-none-any.whl", hash = "sha256:7bb3df0117bdc628b18b6c8748de72c6f537d745d47566053ce6650d5712281c"},
- {file = "langchain_core-0.3.6.tar.gz", hash = "sha256:eb190494a5483f1965f693bb2085edb523370b20fc52dc294d3bd425773cd076"},
+ {file = "langchain_core-0.3.8-py3-none-any.whl", hash = "sha256:07015f7b1d9f52eefe05130e8cafe4dcbdbbf72a8411c9edafe38422e4d11b5c"},
+ {file = "langchain_core-0.3.8.tar.gz", hash = "sha256:7485904f7082f1df880d5ae470a488161616132f30d99f556a1877901fffd1cb"},
]
[package.dependencies]
@@ -2579,7 +2579,7 @@ langchain-core = ">=0.3.0,<0.4.0"
[[package]]
name = "langgraph"
-version = "0.2.28"
+version = "0.2.34"
description = "Building stateful, multi-actor applications with LLMs"
optional = false
python-versions = ">=3.9.0,<4.0"
@@ -2588,7 +2588,7 @@ develop = true
[package.dependencies]
langchain-core = ">=0.2.39,<0.4"
-langgraph-checkpoint = "^1.0.2"
+langgraph-checkpoint = "^2.0.0"
[package.source]
type = "directory"
@@ -2596,7 +2596,7 @@ url = "libs/langgraph"
[[package]]
name = "langgraph-checkpoint"
-version = "1.0.11"
+version = "2.0.0"
description = "Library with base interfaces for LangGraph checkpoint savers."
optional = false
python-versions = "^3.9.0,<4.0"
@@ -2613,7 +2613,7 @@ url = "libs/checkpoint"
[[package]]
name = "langgraph-checkpoint-postgres"
-version = "1.0.8"
+version = "2.0.0"
description = "Library with a Postgres implementation of LangGraph checkpoint saver."
optional = false
python-versions = "^3.9.0,<4.0"
@@ -2621,7 +2621,7 @@ files = []
develop = true
[package.dependencies]
-langgraph-checkpoint = "^1.0.11"
+langgraph-checkpoint = "^2.0.0"
orjson = ">=3.10.1"
psycopg = "^3.0.0"
psycopg-pool = "^3.0.0"
@@ -2632,7 +2632,7 @@ url = "libs/checkpoint-postgres"
[[package]]
name = "langgraph-checkpoint-sqlite"
-version = "1.0.4"
+version = "2.0.0"
description = "Library with a SQLite implementation of LangGraph checkpoint saver."
optional = false
python-versions = "^3.9.0"
@@ -2641,7 +2641,7 @@ develop = true
[package.dependencies]
aiosqlite = "^0.20.0"
-langgraph-checkpoint = "^1.0.11"
+langgraph-checkpoint = "^2.0.0"
[package.source]
type = "directory"
@@ -2649,7 +2649,7 @@ url = "libs/checkpoint-sqlite"
[[package]]
name = "langgraph-sdk"
-version = "0.1.31"
+version = "0.1.32"
description = "SDK for interacting with LangGraph API"
optional = false
python-versions = "^3.9.0,<4.0"
@@ -4625,7 +4625,6 @@ description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs
optional = false
python-versions = ">=3.8"
files = [
- {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
{file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
]
@@ -4636,7 +4635,6 @@ description = "A collection of ASN.1-based protocols modules"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"},
{file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"},
]
@@ -6355,6 +6353,7 @@ description = "Automatically mock your HTTP interactions to simplify and speed u
optional = false
python-versions = ">=3.8"
files = [
+ {file = "vcrpy-6.0.1-py2.py3-none-any.whl", hash = "sha256:621c3fb2d6bd8aa9f87532c688e4575bcbbde0c0afeb5ebdb7e14cac409edfdd"},
{file = "vcrpy-6.0.1.tar.gz", hash = "sha256:9e023fee7f892baa0bbda2f7da7c8ac51165c1c6e38ff8688683a12a4bde9278"},
]