mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-22 17:45:09 +02:00
langgraph: bring back tool content stringify (#1114)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import json
|
||||
from copy import copy
|
||||
from typing import (
|
||||
Any,
|
||||
@@ -28,6 +29,16 @@ INVALID_TOOL_NAME_ERROR_TEMPLATE = (
|
||||
TOOL_CALL_ERROR_TEMPLATE = "Error: {error}\n Please fix your mistakes."
|
||||
|
||||
|
||||
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 called in the last AIMessage.
|
||||
|
||||
@@ -94,7 +105,12 @@ class ToolNode(RunnableCallable):
|
||||
|
||||
try:
|
||||
input = {**call, **{"type": "tool_call"}}
|
||||
return self.tools_by_name[call["name"]].invoke(input, config)
|
||||
tool_message: ToolMessage = self.tools_by_name[call["name"]].invoke(
|
||||
input, config
|
||||
)
|
||||
# TODO: handle this properly in core
|
||||
tool_message.content = str_output(tool_message.content)
|
||||
return tool_message
|
||||
except Exception as e:
|
||||
if not self.handle_tool_errors:
|
||||
raise e
|
||||
@@ -106,7 +122,12 @@ class ToolNode(RunnableCallable):
|
||||
return invalid_tool_message
|
||||
try:
|
||||
input = {**call, **{"type": "tool_call"}}
|
||||
return await self.tools_by_name[call["name"]].ainvoke(input, config)
|
||||
tool_message: ToolMessage = await self.tools_by_name[call["name"]].ainvoke(
|
||||
input, config
|
||||
)
|
||||
# TODO: handle this properly in core
|
||||
tool_message.content = str_output(tool_message.content)
|
||||
return tool_message
|
||||
except Exception as e:
|
||||
if not self.handle_tool_errors:
|
||||
raise e
|
||||
|
||||
@@ -267,6 +267,13 @@ async def test_tool_node():
|
||||
raise ValueError("Test error")
|
||||
return f"tool2: {some_val} - {some_other_val}"
|
||||
|
||||
async def tool3(some_val: int, some_other_val: str) -> str:
|
||||
"""Tool 3 docstring."""
|
||||
return [
|
||||
{"key_1": some_val, "key_2": "foo"},
|
||||
{"key_1": some_other_val, "key_2": "baz"},
|
||||
]
|
||||
|
||||
result = ToolNode([tool1]).invoke(
|
||||
{
|
||||
"messages": [
|
||||
@@ -377,6 +384,31 @@ async def test_tool_node():
|
||||
)
|
||||
assert tool_message.tool_call_id == "some 0"
|
||||
|
||||
# list of dicts tool content
|
||||
result3 = await ToolNode([tool3]).ainvoke(
|
||||
{
|
||||
"messages": [
|
||||
AIMessage(
|
||||
"hi?",
|
||||
tool_calls=[
|
||||
{
|
||||
"name": "tool3",
|
||||
"args": {"some_val": 2, "some_other_val": "bar"},
|
||||
"id": "some 0",
|
||||
}
|
||||
],
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
tool_message: ToolMessage = result3["messages"][-1]
|
||||
assert tool_message.type == "tool"
|
||||
assert (
|
||||
tool_message.content
|
||||
== '[{"key_1": 2, "key_2": "foo"}, {"key_1": "bar", "key_2": "baz"}]'
|
||||
)
|
||||
assert tool_message.tool_call_id == "some 0"
|
||||
|
||||
|
||||
def my_function(some_val: int, some_other_val: str) -> str:
|
||||
return f"{some_val} - {some_other_val}"
|
||||
|
||||
Reference in New Issue
Block a user