From 02e2db549b68928048475438fa4438342f8ac19c Mon Sep 17 00:00:00 2001 From: Chester Curme Date: Thu, 11 Jul 2024 14:23:10 -0400 Subject: [PATCH] update core --- .../langchain_core/utils/function_calling.py | 29 ++++++++++++------- .../unit_tests/utils/test_function_calling.py | 7 +++-- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/libs/core/langchain_core/utils/function_calling.py b/libs/core/langchain_core/utils/function_calling.py index f4bcba6e701..17c495c9673 100644 --- a/libs/core/langchain_core/utils/function_calling.py +++ b/libs/core/langchain_core/utils/function_calling.py @@ -27,6 +27,7 @@ from langchain_core.messages import ( ToolMessage, ) from langchain_core.pydantic_v1 import BaseModel +from langchain_core.runnables import Runnable from langchain_core.utils.json_schema import dereference_refs if TYPE_CHECKING: @@ -189,15 +190,16 @@ def format_tool_to_openai_tool(tool: BaseTool) -> ToolDescription: def convert_to_openai_function( - function: Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool], + function: Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool, Runnable], + **kwargs: Any, ) -> Dict[str, Any]: """Convert a raw function/class to an OpenAI function. Args: - function: Either a dictionary, a pydantic.BaseModel class, or a Python function. - If a dictionary is passed in, it is assumed to already be a valid OpenAI - function or a JSON schema with top-level 'title' and 'description' keys - specified. + function: Either a dictionary, a pydantic.BaseModel class, a Python function, + or a Runnable. If a dictionary is passed in, it is assumed to already be + a valid OpenAI function or a JSON schema with top-level 'title' and + 'description' keys specified. Returns: A dict version of the passed in function which is compatible with the @@ -224,6 +226,13 @@ def convert_to_openai_function( return cast(Dict, convert_pydantic_to_openai_function(function)) elif isinstance(function, BaseTool): return cast(Dict, format_tool_to_openai_function(function)) + elif isinstance(function, Runnable): + as_tool_kwargs = { + k: v for k, v in kwargs.items() if k in ("name", "description", "arg_types") + } + return cast( + Dict, format_tool_to_openai_function(function.as_tool(**as_tool_kwargs)) + ) elif callable(function): return cast(Dict, convert_python_function_to_openai_function(function)) else: @@ -236,15 +245,15 @@ def convert_to_openai_function( def convert_to_openai_tool( - tool: Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool], + tool: Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool, Runnable], ) -> Dict[str, Any]: """Convert a raw function/class to an OpenAI tool. Args: - tool: Either a dictionary, a pydantic.BaseModel class, Python function, or - BaseTool. If a dictionary is passed in, it is assumed to already be a valid - OpenAI tool, OpenAI function, or a JSON schema with top-level 'title' and - 'description' keys specified. + tool: Either a dictionary, a pydantic.BaseModel class, Python function, + BaseTool, or Runnable. If a dictionary is passed in, it is assumed + to already be a valid OpenAI tool, OpenAI function, or a JSON schema + with top-level 'title' and 'description' keys specified. Returns: A dict version of the passed in tool which is compatible with the diff --git a/libs/core/tests/unit_tests/utils/test_function_calling.py b/libs/core/tests/unit_tests/utils/test_function_calling.py index 32c6349a5fb..aaa3367ec79 100644 --- a/libs/core/tests/unit_tests/utils/test_function_calling.py +++ b/libs/core/tests/unit_tests/utils/test_function_calling.py @@ -188,7 +188,6 @@ def test_convert_to_openai_function( assert actual == expected # Test runnables - actual = convert_to_openai_function(runnable.as_tool(description="dummy function")) parameters = { "type": "object", "properties": { @@ -202,7 +201,11 @@ def test_convert_to_openai_function( } runnable_expected = expected.copy() runnable_expected["parameters"] = parameters - assert actual == runnable_expected + for actual in [ + convert_to_openai_function(runnable.as_tool(description="dummy function")), + convert_to_openai_function(runnable, description="dummy function"), + ]: + assert actual == runnable_expected def test_convert_to_openai_function_nested() -> None: