Skip to content

Commit

Permalink
core[patch]: remove deprecated functions from tool binding hotpath (#…
Browse files Browse the repository at this point in the history
…29015)

(Inspired by #26918)

We rely on some deprecated public functions in the hot path for tool
binding (`convert_pydantic_to_openai_function`,
`convert_python_function_to_openai_function`, and
`format_tool_to_openai_function`). My understanding is that what is
deprecated is not the functionality they implement, but use of them in
the public API -- we expect to continue to rely on them.

Here we update these functions to be private and not deprecated. We keep
the public, deprecated functions as simple wrappers that can be safely
deleted.

The `@deprecated` wrapper adds considerable latency due to its use of
the `inspect` module. This update speeds up `bind_tools` by a factor of
~100x:

Before:

![Screenshot 2025-01-03 at 11 22
55 AM](https://github.com/user-attachments/assets/94b1c433-ce12-406f-b64c-ca7103badfe0)

After:

![Screenshot 2025-01-03 at 11 23
41 AM](https://github.com/user-attachments/assets/02d0deab-82e4-45ca-8cc7-a20b91a5b5db)

---------

Co-authored-by: Erick Friis <[email protected]>
  • Loading branch information
ccurme and efriis authored Jan 3, 2025
1 parent a86904e commit 4bb391f
Showing 1 changed file with 32 additions and 26 deletions.
58 changes: 32 additions & 26 deletions libs/core/langchain_core/utils/function_calling.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,7 @@ def _rm_titles(kv: dict, prev_key: str = "") -> dict:
return new_kv


@deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_function()",
removal="1.0",
)
def convert_pydantic_to_openai_function(
def _convert_pydantic_to_openai_function(
model: type,
*,
name: Optional[str] = None,
Expand Down Expand Up @@ -121,6 +116,13 @@ def convert_pydantic_to_openai_function(
}


convert_pydantic_to_openai_function = deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_function()",
removal="1.0",
)(_convert_pydantic_to_openai_function)


@deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_tool()",
Expand All @@ -144,7 +146,7 @@ def convert_pydantic_to_openai_tool(
Returns:
The tool description.
"""
function = convert_pydantic_to_openai_function(
function = _convert_pydantic_to_openai_function(
model, name=name, description=description
)
return {"type": "function", "function": function}
Expand All @@ -155,12 +157,7 @@ def _get_python_function_name(function: Callable) -> str:
return function.__name__


@deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_function()",
removal="1.0",
)
def convert_python_function_to_openai_function(
def _convert_python_function_to_openai_function(
function: Callable,
) -> FunctionDescription:
"""Convert a Python function to an OpenAI function-calling API compatible dict.
Expand All @@ -186,13 +183,20 @@ def convert_python_function_to_openai_function(
error_on_invalid_docstring=False,
include_injected=False,
)
return convert_pydantic_to_openai_function(
return _convert_pydantic_to_openai_function(
model,
name=func_name,
description=model.__doc__,
)


convert_python_function_to_openai_function = deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_function()",
removal="1.0",
)(_convert_python_function_to_openai_function)


def _convert_typed_dict_to_openai_function(typed_dict: type) -> FunctionDescription:
visited: dict = {}
from pydantic.v1 import BaseModel
Expand All @@ -201,7 +205,7 @@ def _convert_typed_dict_to_openai_function(typed_dict: type) -> FunctionDescript
type[BaseModel],
_convert_any_typed_dicts_to_pydantic(typed_dict, visited=visited),
)
return convert_pydantic_to_openai_function(model) # type: ignore
return _convert_pydantic_to_openai_function(model) # type: ignore


_MAX_TYPED_DICT_RECURSION = 25
Expand Down Expand Up @@ -272,12 +276,7 @@ def _convert_any_typed_dicts_to_pydantic(
return type_


@deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_function()",
removal="1.0",
)
def format_tool_to_openai_function(tool: BaseTool) -> FunctionDescription:
def _format_tool_to_openai_function(tool: BaseTool) -> FunctionDescription:
"""Format tool into the OpenAI function API.
Args:
Expand All @@ -290,7 +289,7 @@ def format_tool_to_openai_function(tool: BaseTool) -> FunctionDescription:

is_simple_oai_tool = isinstance(tool, simple.Tool) and not tool.args_schema
if tool.tool_call_schema and not is_simple_oai_tool:
return convert_pydantic_to_openai_function(
return _convert_pydantic_to_openai_function(
tool.tool_call_schema, name=tool.name, description=tool.description
)
else:
Expand All @@ -312,6 +311,13 @@ def format_tool_to_openai_function(tool: BaseTool) -> FunctionDescription:
}


format_tool_to_openai_function = deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_function()",
removal="1.0",
)(_format_tool_to_openai_function)


@deprecated(
"0.1.16",
alternative="langchain_core.utils.function_calling.convert_to_openai_tool()",
Expand All @@ -326,7 +332,7 @@ def format_tool_to_openai_tool(tool: BaseTool) -> ToolDescription:
Returns:
The tool description.
"""
function = format_tool_to_openai_function(tool)
function = _format_tool_to_openai_function(tool)
return {"type": "function", "function": function}


Expand Down Expand Up @@ -408,15 +414,15 @@ def convert_to_openai_function(
if function_copy and "properties" in function_copy:
oai_function["parameters"] = function_copy
elif isinstance(function, type) and is_basemodel_subclass(function):
oai_function = cast(dict, convert_pydantic_to_openai_function(function))
oai_function = cast(dict, _convert_pydantic_to_openai_function(function))
elif is_typeddict(function):
oai_function = cast(
dict, _convert_typed_dict_to_openai_function(cast(type, function))
)
elif isinstance(function, BaseTool):
oai_function = cast(dict, format_tool_to_openai_function(function))
oai_function = cast(dict, _format_tool_to_openai_function(function))
elif callable(function):
oai_function = cast(dict, convert_python_function_to_openai_function(function))
oai_function = cast(dict, _convert_python_function_to_openai_function(function))
else:
msg = (
f"Unsupported function\n\n{function}\n\nFunctions must be passed in"
Expand Down

0 comments on commit 4bb391f

Please sign in to comment.