Skip to content

Commit

Permalink
core[patch]: support tool calls with non-pickleable args in tools (#2…
Browse files Browse the repository at this point in the history
…4741)

Deepcopy raises with non-pickleable args.
  • Loading branch information
ccurme authored Jul 29, 2024
1 parent df78608 commit 9998e55
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 5 deletions.
6 changes: 2 additions & 4 deletions libs/core/langchain_core/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from __future__ import annotations

import asyncio
import copy
import functools
import inspect
import json
Expand Down Expand Up @@ -1491,9 +1490,8 @@ def _prep_run_args(
) -> Tuple[Union[str, Dict], Dict]:
config = ensure_config(config)
if _is_tool_call(input):
input_copy = copy.deepcopy(input)
tool_call_id: Optional[str] = cast(ToolCall, input_copy)["id"]
tool_input: Union[str, dict] = cast(ToolCall, input_copy)["args"]
tool_call_id: Optional[str] = cast(ToolCall, input)["id"]
tool_input: Union[str, dict] = cast(ToolCall, input)["args"].copy()
else:
tool_call_id = None
tool_input = cast(Union[str, dict], input)
Expand Down
22 changes: 21 additions & 1 deletion libs/core/tests/unit_tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import json
import sys
import textwrap
import threading
from datetime import datetime
from enum import Enum
from functools import partial
Expand Down Expand Up @@ -977,7 +978,7 @@ async def _arun(self, bar: Any, bar_config: RunnableConfig, **kwargs: Any) -> An
def test_tool_pass_config(tool: BaseTool) -> None:
assert tool.invoke({"bar": "baz"}, {"configurable": {"foo": "not-bar"}}) == "baz"

# Test tool calls
# Test we don't mutate tool calls
tool_call = {
"name": tool.name,
"args": {"bar": "baz"},
Expand All @@ -988,6 +989,25 @@ def test_tool_pass_config(tool: BaseTool) -> None:
assert tool_call["args"] == {"bar": "baz"}


class FooBaseNonPickleable(FooBase):
def _run(self, bar: Any, bar_config: RunnableConfig, **kwargs: Any) -> Any:
return True


def test_tool_pass_config_non_pickleable() -> None:
tool = FooBaseNonPickleable()

args = {"bar": threading.Lock()}
tool_call = {
"name": tool.name,
"args": args,
"id": "abc123",
"type": "tool_call",
}
_ = tool.invoke(tool_call, {"configurable": {"foo": "not-bar"}})
assert tool_call["args"] == args


@pytest.mark.parametrize(
"tool", [foo, afoo, simple_foo, asimple_foo, FooBase(), AFooBase()]
)
Expand Down

0 comments on commit 9998e55

Please sign in to comment.