Spaces:
Runtime error
Runtime error
"""Interface for tools.""" | |
from inspect import signature | |
from typing import Any, Awaitable, Callable, Optional, Union | |
from langchain.tools.base import BaseTool | |
class Tool(BaseTool): | |
"""Tool that takes in function or coroutine directly.""" | |
description: str = "" | |
func: Callable[[str], str] | |
coroutine: Optional[Callable[[str], Awaitable[str]]] = None | |
def _run(self, tool_input: str) -> str: | |
"""Use the tool.""" | |
return self.func(tool_input) | |
async def _arun(self, tool_input: str) -> str: | |
"""Use the tool asynchronously.""" | |
if self.coroutine: | |
return await self.coroutine(tool_input) | |
raise NotImplementedError("Tool does not support async") | |
# TODO: this is for backwards compatibility, remove in future | |
def __init__( | |
self, name: str, func: Callable[[str], str], description: str, **kwargs: Any | |
) -> None: | |
"""Initialize tool.""" | |
super(Tool, self).__init__( | |
name=name, func=func, description=description, **kwargs | |
) | |
class InvalidTool(BaseTool): | |
"""Tool that is run when invalid tool name is encountered by agent.""" | |
name = "invalid_tool" | |
description = "Called when tool name is invalid." | |
def _run(self, tool_name: str) -> str: | |
"""Use the tool.""" | |
return f"{tool_name} is not a valid tool, try another one." | |
async def _arun(self, tool_name: str) -> str: | |
"""Use the tool asynchronously.""" | |
return f"{tool_name} is not a valid tool, try another one." | |
def tool(*args: Union[str, Callable], return_direct: bool = False) -> Callable: | |
"""Make tools out of functions, can be used with or without arguments. | |
Requires: | |
- Function must be of type (str) -> str | |
- Function must have a docstring | |
Examples: | |
.. code-block:: python | |
@tool | |
def search_api(query: str) -> str: | |
# Searches the API for the query. | |
return | |
@tool("search", return_direct=True) | |
def search_api(query: str) -> str: | |
# Searches the API for the query. | |
return | |
""" | |
def _make_with_name(tool_name: str) -> Callable: | |
def _make_tool(func: Callable[[str], str]) -> Tool: | |
assert func.__doc__, "Function must have a docstring" | |
# Description example: | |
# search_api(query: str) - Searches the API for the query. | |
description = f"{tool_name}{signature(func)} - {func.__doc__.strip()}" | |
tool_ = Tool( | |
name=tool_name, | |
func=func, | |
description=description, | |
return_direct=return_direct, | |
) | |
return tool_ | |
return _make_tool | |
if len(args) == 1 and isinstance(args[0], str): | |
# if the argument is a string, then we use the string as the tool name | |
# Example usage: @tool("search", return_direct=True) | |
return _make_with_name(args[0]) | |
elif len(args) == 1 and callable(args[0]): | |
# if the argument is a function, then we use the function name as the tool name | |
# Example usage: @tool | |
return _make_with_name(args[0].__name__)(args[0]) | |
elif len(args) == 0: | |
# if there are no arguments, then we use the function name as the tool name | |
# Example usage: @tool(return_direct=True) | |
def _partial(func: Callable[[str], str]) -> BaseTool: | |
return _make_with_name(func.__name__)(func) | |
return _partial | |
else: | |
raise ValueError("Too many arguments for tool decorator") | |