Before starting with this advanced guide, please make sure you have a basic understanding of the tool integration process in Dify. Check out Quick Integration for a quick run through.
Tool Interface
We have defined a series of helper methods in the Tool class to help developers quickly build more complex tools.
Message Return
Dify supports various message types such as text, link, image, and file BLOB. You can return different types of messages to the LLM and users through the following interfaces.
Please note, some parameters in the following interfaces will be introduced in later sections.
Image URL
You only need to pass the URL of the image, and Dify will automatically download the image and return it to the user.
defcreate_image_message(self,image:str,save_as:str='') -> ToolInvokeMessage:""" create an image message :param image: the url of the image :return: the image message """
Link
If you need to return a link, you can use the following interface.
defcreate_link_message(self,link:str,save_as:str='') -> ToolInvokeMessage:""" create a link message :param link: the url of the link :return: the link message """
Text
If you need to return a text message, you can use the following interface.
defcreate_text_message(self,text:str,save_as:str='') -> ToolInvokeMessage:""" create a text message :param text: the text of the message :return: the text message """
File BLOB
If you need to return the raw data of a file, such as images, audio, video, PPT, Word, Excel, etc., you can use the following interface.
blob The raw data of the file, of bytes type
meta The metadata of the file, if you know the type of the file, it is best to pass a mime_type, otherwise Dify will use octet/stream as the default type
defcreate_blob_message(self,blob:bytes,meta:dict=None,save_as:str='') -> ToolInvokeMessage:""" create a blob message :param blob: the blob :return: the blob message """
Shortcut Tools
In large model applications, we have two common needs:
First, summarize a long text in advance, and then pass the summary content to the LLM to prevent the original text from being too long for the LLM to handle
The content obtained by the tool is a link, and the web page information needs to be crawled before it can be returned to the LLM
To help developers quickly implement these two needs, we provide the following two shortcut tools.
Text Summary Tool
This tool takes in an user_id and the text to be summarized, and returns the summarized text. Dify will use the default model of the current workspace to summarize the long text.
defsummary(self,user_id:str,content:str) ->str:""" summary the content :param user_id: the user id :param content: the content :return: the summary """
Web Page Crawling Tool
This tool takes in web page link to be crawled and a user_agent (which can be empty), and returns a string containing the information of the web page. The user_agent is an optional parameter that can be used to identify the tool. If not passed, Dify will use the default user_agent.
defget_url(self,url:str,user_agent:str=None) ->str:""" get url """ the crawled result
Variable Pool
We have introduced a variable pool in Tool to store variables, files, etc. generated during the tool's operation. These variables can be used by other tools during the tool's operation.
Next, we will use DallE3 and Vectorizer.AI as examples to introduce how to use the variable pool.
DallE3 is an image generation tool that can generate images based on text. Here, we will let DallE3 generate a logo for a coffee shop
Vectorizer.AI is a vector image conversion tool that can convert images into vector images, so that the images can be infinitely enlarged without distortion. Here, we will convert the PNG icon generated by DallE3 into a vector image, so that it can be truly used by designers.
DallE3
First, we use DallE3. After creating the image, we save the image to the variable pool. The code is as follows:
from typing import Any, Dict, List, Unionfrom core.tools.entities.tool_entities import ToolInvokeMessagefrom core.tools.tool.builtin_tool import BuiltinToolfrom base64 import b64decodefrom openai import OpenAIclassDallE3Tool(BuiltinTool):def_invoke(self,user_id:str,tool_Parameters: Dict[str, Any], ) -> Union[ToolInvokeMessage, List[ToolInvokeMessage]]:""" invoke tools """ client =OpenAI( api_key=self.runtime.credentials['openai_api_key'], )# prompt prompt = tool_Parameters.get('prompt', '')ifnot prompt:return self.create_text_message('Please input prompt')# call openapi dalle3 response = client.images.generate( prompt=prompt, model='dall-e-3', size='1024x1024', n=1, style='vivid', quality='standard', response_format='b64_json' ) result = []for image in response.data: # Save all images to the variable pool through the save_as parameter. The variable name is self.VARIABLE_KEY.IMAGE.value. If new images are generated later, they will overwrite the previous images.
result.append(self.create_blob_message(blob=b64decode(image.b64_json), meta={ 'mime_type': 'image/png' }, save_as=self.VARIABLE_KEY.IMAGE.value))return result
Note that we used self.VARIABLE_KEY.IMAGE.value as the variable name of the image. In order for developers' tools to cooperate with each other, we defined this KEY. You can use it freely, or you can choose not to use this KEY. Passing a custom KEY is also acceptable.
Vectorizer.AI
Next, we use Vectorizer.AI to convert the PNG icon generated by DallE3 into a vector image. Let's go through the functions we defined here. The code is as follows:
from core.tools.tool.builtin_tool import BuiltinToolfrom core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameterfrom core.tools.errors import ToolProviderCredentialValidationErrorfrom typing import Any, Dict, List, Unionfrom httpx import postfrom base64 import b64decodeclassVectorizerTool(BuiltinTool):def_invoke(self,user_id:str,tool_Parameters: Dict[str, Any]) \-> Union[ToolInvokeMessage, List[ToolInvokeMessage]]:""" Tool invocation, the image variable name needs to be passed in from here, so that we can get the image from the variable pool
"""defget_runtime_parameters(self) -> List[ToolParameter]:""" Override the tool parameter list, we can dynamically generate the parameter list based on the actual situation in the current variable pool, so that the LLM can generate the form based on the parameter list
"""defis_tool_available(self) ->bool:""" Whether the current tool is available, if there is no image in the current variable pool, then we don't need to display this tool, just return False here
"""
Next, let's implement these three functions
from core.tools.tool.builtin_tool import BuiltinToolfrom core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameterfrom core.tools.errors import ToolProviderCredentialValidationErrorfrom typing import Any, Dict, List, Unionfrom httpx import postfrom base64 import b64decodeclassVectorizerTool(BuiltinTool):def_invoke(self,user_id:str,tool_Parameters: Dict[str, Any]) \-> Union[ToolInvokeMessage, List[ToolInvokeMessage]]:""" invoke tools """ api_key_name = self.runtime.credentials.get('api_key_name', None) api_key_value = self.runtime.credentials.get('api_key_value', None)ifnot api_key_name ornot api_key_value:raiseToolProviderCredentialValidationError('Please input api key name and value')# Get image_id, the definition of image_id can be found in get_runtime_parameters image_id = tool_Parameters.get('image_id', '')ifnot image_id:return self.create_text_message('Please input image id')# Get the image generated by DallE from the variable pool image_binary = self.get_variable_file(self.VARIABLE_KEY.IMAGE)ifnot image_binary:return self.create_text_message('Image not found, please request user to generate image firstly.')# Generate vector image response =post('https://vectorizer.ai/api/v1/vectorize', files={ 'image': image_binary }, data={ 'mode': 'test' }, auth=(api_key_name, api_key_value), timeout=30 )if response.status_code !=200:raiseException(response.text)return [ self.create_text_message('the vectorized svg is saved as an image.'), self.create_blob_message(blob=response.content, meta={'mime_type': 'image/svg+xml'}) ]defget_runtime_parameters(self) -> List[ToolParameter]:""" override the runtime parameters """ # Here, we override the tool parameter list, define the image_id, and set its option list to all images in the current variable pool. The configuration here is consistent with the configuration in yaml.
return [ ToolParameter.get_simple_instance( name='image_id', llm_description=f'the image id that you want to vectorize, \ and the image id should be specified in \{[i.name for i in self.list_default_image_variables()]}', type=ToolParameter.ToolParameterType.SELECT, required=True, options=[i.name for i in self.list_default_image_variables()] ) ]defis_tool_available(self) ->bool:# Only when there are images in the variable pool, the LLM needs to use this toolreturnlen(self.list_default_image_variables())>0
It's worth noting that we didn't actually use image_id here. We assumed that there must be an image in the default variable pool when calling this tool, so we directly used image_binary = self.get_variable_file(self.VARIABLE_KEY.IMAGE) to get the image. In cases where the model's capabilities are weak, we recommend developers to do the same, which can effectively improve fault tolerance and avoid the model passing incorrect parameters.