Tool
The general meaning of a tool is "something that gets controlled by someone to accomplish a particular task". There are three parts of this definition that are related to Embedia:
- The tool itself
- The "someone" that controls the tool
- The task to accomplish
In a real world example,
- The tool is a hammer
- The "someone" controlling the tool is you
- The task is "driving a nail into the wall"
In the context of Embedia,
- The tool is any python function abstracted through the
Tool
class - The "someone" controlling the tool is an Agent (Learn more about: Agents)
- The task is whatever the Agent has to accomplish
Tool
is an abstract class. Inherit from this class and define the _run
method. The _run
method can take any number of arguments, runs the internal
logic of the tool and returns an output. Although most of the time, an Agent
will be calling the Tool
instance, you can also use the Tool
instance
directly by calling it like a function with the expected arguments that you've
defined.
Any python function in the entire world can become a tool if you put its
contents inside the _run
method.
Attributes
docs
(ToolDocumentation
): The documentation of the tool's usage and its arguments. (Learn more about: ToolDocumentation)
Methods
_run
(abstract): Implement this method to run the internal logic of the tool. Do not call this method directly. Instead, use the__call__
method.
- Input:
*args, **kwargs
- Output:
ToolReturn
(Learn more about: ToolReturn)
__call__
: Internally calls the_run
method. Use this method by calling the class instance like a function with the expected arguments.
- Input:
*args, **kwargs
- Output:
ToolReturn
- Publishes a
ToolStart
event before calling_run
and aToolEnd
event after_run
returns.
human_confirmation
: Use this method to ask for human confirmation at any point in the_run
function.
- Input:
dict
: The details to be displayed while asking for confirmation. - Output:
None
- Asks for an input from the user. If the user inputs
y
, the code moves forward. If the user inputs anything else, it raisesUserDeniedError
.
Usage
Basic Usage
A Tool
is created so that it can be used by an Agent. For an Agent to
understand how to use a Tool
, it needs to know two things about the tool:
- What does this tool do?
- What are the input parameters of this tool?
To give a structure to these two questions, Embedia provides the
ToolDocumentation
model. (Learn more about:
ToolDocumentation). The first question is
solved by the name
and desc
attributes of the model. The second question is
solved by the params
attribute of the model.
To understand whether the tool ran successfully or not, we provide the
ToolReturn
model. (Learn more about:
ToolReturn). This is useful when we are
defining different logical paths based on the fact that the tool ran
successfully or not.
Let's create a simple print tool to understand how to use the Tool
class.
import asyncio
from embedia import Tool, ToolDocumentation, ParamDocumentation, ToolReturn
class PrintTool(Tool):
def __init__(self):
super().__init__(docs=ToolDocumentation(
name="Print Tool",
desc="Prints whatever you want",
params=[ParamDocumentation(name="text", desc="The text to be printed. Type: String")]
))
async def _run(self, text: str):
print(text)
return ToolReturn(output='done', exit_code=0)
if __name__ == '__main__':
printer = PrintTool()
resp = asyncio.run(printer('Hello World!!!!!!'))
Running this code gives the following output:
[time: 2023-10-01T02:43:20.350426+00:00] [id: 140149931670096] [event: Tool Start]
Tool: PrintTool
Args: ('Hello World!!!!!!',)
Kwargs: {}
Hello World!!!!!!
[time: 2023-10-01T02:43:20.350558+00:00] [id: 140149931670096] [event: Tool End]
Tool: PrintTool
ExitCode: 0
Output:
done
To make it easier to code, Embedia can convert python dictionaries to pydantic
models (like ToolDocumentation
, ParamDocumentation
and ToolReturn
)
internally. So the above code can be simplified as follows:
import asyncio
from embedia import Tool
class PrintTool(Tool):
def __init__(self):
super().__init__(docs={
"name": "Print Tool",
"desc": "Prints whatever you want",
"params": [{"name": "text", "desc": "The text to be printed. Type: String"}]
})
async def _run(self, text: str):
print(text)
return {'output': 'done'}
if __name__ == '__main__':
printer = PrintTool()
resp = asyncio.run(printer('Hello World!!!!!!'))
It results in the same output as before:
[time: 2023-10-01T02:43:20.350426+00:00] [id: 140149931670096] [event: Tool Start]
Tool: PrintTool
Args: ('Hello World!!!!!!',)
Kwargs: {}
Hello World!!!!!!
[time: 2023-10-01T02:43:20.350558+00:00] [id: 140149931670096] [event: Tool End]
Tool: PrintTool
ExitCode: 0
Output:
done
Adding human confirmation inside _run function
You can ask for human confirmation at any point in your _run
function by
calling the self.human_confirmation
method. It takes in a details
parameter
(type: dict) that will be displayed to the end user while asking for
confirmation.
This is useful when the tool is about to do something that can't be undone (eg: deleting a file). Adding human confirmation to the PrintTool would look something like this:
import asyncio
from embedia import Tool
class PrintTool(Tool):
def __init__(self):
super().__init__(docs={
"name": "Print Tool",
"desc": "Prints whatever you want",
"params": [{"name": "text", "desc": "The text to be printed. Type: String"}]
})
async def _run(self, text: str):
await self.human_confirmation(details={"text": text})
print(text)
return {'output': 'done'}
if __name__ == '__main__':
printer = PrintTool()
resp = asyncio.run(printer('Hello World!!!!!!'))
Running this code gives the following output:
[time: 2023-10-01T11:14:14.151069+00:00] [id: 140182104013264] [event: Tool Start]
Tool: PrintTool
Args: ('Hello World!!!!!!',)
Kwargs: {}
Tool: PrintTool
Details: {'text': 'Hello World!!!!!!'} Confirm (y/n):
If the user inputs y
, the code successfully moves forward like so:
Hello World!!!!!!
[time: 2023-10-01T11:14:18.693709+00:00] [id: 140182104013264] [event: Tool End]
Tool: PrintTool
ExitCode: 0
Output:
done
If the user inputs anything else, it raises UserDeniedError
:
embedia.utils.exceptions.UserDeniedError: Tool: PrintTool Details: {'text': 'Hello World!!!!!!'}