mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-10 03:37:51 +02:00
langgraph[patch]: ToolNode support for tools outputting msg (#977)
ToolNode passes ToolCall to tools directly and outputs the generated ToolMessages directly
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any, Callable, Dict, Literal, Optional, Sequence, Union
|
||||
|
||||
from langchain_core.messages import AIMessage, AnyMessage, ToolCall, ToolMessage
|
||||
@@ -11,20 +10,11 @@ from langchain_core.tools import tool as create_tool
|
||||
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 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 node that runs the tools called 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.
|
||||
|
||||
The `ToolNode` is roughly analogous to:
|
||||
@@ -79,14 +69,13 @@ class ToolNode(RunnableCallable):
|
||||
|
||||
def run_one(call: ToolCall):
|
||||
try:
|
||||
output = self.tools_by_name[call["name"]].invoke(call["args"], config)
|
||||
input = {**call, **{"type": "tool_call"}}
|
||||
return self.tools_by_name[call["name"]].invoke(input, config)
|
||||
except Exception as e:
|
||||
if not self.handle_tool_errors:
|
||||
raise e
|
||||
output = f"Error: {repr(e)}\n Please fix your mistakes."
|
||||
return ToolMessage(
|
||||
content=str_output(output), name=call["name"], tool_call_id=call["id"]
|
||||
)
|
||||
content = f"Error: {repr(e)}\n Please fix your mistakes."
|
||||
return ToolMessage(content, name=call["name"], tool_call_id=call["id"])
|
||||
|
||||
with get_executor_for_config(config) as executor:
|
||||
outputs = [*executor.map(run_one, message.tool_calls)]
|
||||
@@ -112,16 +101,13 @@ class ToolNode(RunnableCallable):
|
||||
|
||||
async def run_one(call: ToolCall):
|
||||
try:
|
||||
output = await self.tools_by_name[call["name"]].ainvoke(
|
||||
call["args"], config
|
||||
)
|
||||
input = {**call, **{"type": "tool_call"}}
|
||||
return await self.tools_by_name[call["name"]].ainvoke(input, config)
|
||||
except Exception as e:
|
||||
if not self.handle_tool_errors:
|
||||
raise e
|
||||
output = f"Error: {repr(e)}\n Please fix your mistakes."
|
||||
return ToolMessage(
|
||||
content=str_output(output), name=call["name"], tool_call_id=call["id"]
|
||||
)
|
||||
content = f"Error: {repr(e)}\n Please fix your mistakes."
|
||||
return ToolMessage(content, 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":
|
||||
|
||||
Generated
+4
-4
@@ -1746,13 +1746,13 @@ langchain-core = ">=0.2.2rc1,<0.3"
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.2.16"
|
||||
version = "0.2.19"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.2.16-py3-none-any.whl", hash = "sha256:68dd6fbdd8d704e124ee14c39d0b0dcf38a47bb32234328b63ad2e98385b1c80"},
|
||||
{file = "langchain_core-0.2.16.tar.gz", hash = "sha256:cc03083e059bff86ca3e6ba60097f91d7765a929edd04eb226bb20c208447caf"},
|
||||
{file = "langchain_core-0.2.19-py3-none-any.whl", hash = "sha256:5b3cd34395be274c89e822c84f0e03c4da14168c177a83921c5b9414ac7a0651"},
|
||||
{file = "langchain_core-0.2.19.tar.gz", hash = "sha256:13043a83e5c9ab58b9f5ce2a56896e7e88b752e8891b2958960a98e71801471e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -4130,4 +4130,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools",
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9.0,<4.0"
|
||||
content-hash = "19250230952cb11ee6b5c820ae0a2b589ce520a59e0723204dcee0c46c3b739e"
|
||||
content-hash = "170eaa0e542a02d5f2fb0d42d1f04c5d010bd3735a44b927b28e6b742c689eb0"
|
||||
|
||||
@@ -9,7 +9,7 @@ repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9.0,<4.0"
|
||||
langchain-core = ">=0.2.15,<0.3"
|
||||
langchain-core = ">=0.2.19,<0.3"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
@@ -61,7 +61,7 @@ omit = ["tests/*"]
|
||||
[tool.pytest-watcher]
|
||||
now = true
|
||||
delay = 0.1
|
||||
runner_args = ["-x", "--ff", "-vv", "--snapshot-update"]
|
||||
runner_args = ["--ff", "-vv", "--snapshot-update"]
|
||||
patterns = ["*.py"]
|
||||
|
||||
[build-system]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3143,6 +3143,7 @@ Some examples of past conversations:
|
||||
"name": "search_api",
|
||||
"args": {"query": "query"},
|
||||
"id": "tool_call123",
|
||||
"type": "tool_call",
|
||||
}
|
||||
],
|
||||
),
|
||||
@@ -3154,7 +3155,7 @@ Some examples of past conversations:
|
||||
),
|
||||
AIMessage(content="answer", id=AnyStr()),
|
||||
]
|
||||
assert app.invoke(
|
||||
actual = app.invoke(
|
||||
{"messages": "what is weather in sf"},
|
||||
{
|
||||
"configurable": {
|
||||
@@ -3162,7 +3163,9 @@ Some examples of past conversations:
|
||||
"expected_examples": [],
|
||||
},
|
||||
},
|
||||
) == {"messages": first_messages}
|
||||
)
|
||||
expected = {"messages": first_messages}
|
||||
assert actual == expected
|
||||
|
||||
# get first checkpoint
|
||||
chkpnt_tuple_1 = saver.get_tuple({"configurable": {"thread_id": "1"}})
|
||||
@@ -3193,6 +3196,7 @@ Some examples of past conversations:
|
||||
"name": "search_api",
|
||||
"args": {"query": "query"},
|
||||
"id": "tool_call123",
|
||||
"type": "tool_call",
|
||||
}
|
||||
],
|
||||
),
|
||||
@@ -3252,6 +3256,7 @@ Some examples of past conversations:
|
||||
"name": "search_api",
|
||||
"args": {"query": "query"},
|
||||
"id": "tool_call123",
|
||||
"type": "tool_call",
|
||||
}
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user