diff --git a/docs/how-to-guides/develop-a-tool/add-a-tool-icon.md b/docs/how-to-guides/develop-a-tool/add-a-tool-icon.md index 4b0ffcfd7e4..e193b295897 100644 --- a/docs/how-to-guides/develop-a-tool/add-a-tool-icon.md +++ b/docs/how-to-guides/develop-a-tool/add-a-tool-icon.md @@ -1,4 +1,4 @@ -# Adding a Tool Icon +# Adding a tool icon A tool icon serves as a graphical representation of your tool in the user interface (UI). Follow this guidance to add a custom tool icon when developing your own tool package. Adding a custom tool icon is optional. If you do not provide one, the system uses a default icon. @@ -6,6 +6,7 @@ Adding a custom tool icon is optional. If you do not provide one, the system use ## Prerequisites - Please ensure that your [Prompt flow for VS Code](https://marketplace.visualstudio.com/items?itemName=prompt-flow.prompt-flow) is updated to version 1.4.2 or later. +- Please install promptflow package and ensure that its version is 1.1.0 or later. - Create a tool package as described in [Create and Use Tool Package](create-and-use-tool-package.md). - Prepare custom icon image that meets these requirements: @@ -14,39 +15,50 @@ Adding a custom tool icon is optional. If you do not provide one, the system use - Avoid complex images with lots of detail or contrast, as they may not resize well. See [this example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/icons/custom-tool-icon.png) as a reference. -- Install dependencies to generate icon data URI: - ``` - pip install pillow - ``` +## Add tool icon with _icon_ parameter -## Add tool icon with _icon_ parameter -Run the command below in your tool project directory to automatically generate your tool YAML, use _-i_ or _--icon_ parameter to add a custom tool icon: +### Initialize a package tool with icon +You can use [pf tool init](../../reference/pf-command-reference.md#pf-tool-init) to initialize a package tool with icon: +```bash +pf tool init --package --tool --set icon= ``` -python \scripts\tool\generate_package_tool_meta.py -m -o -i -``` -Here we use [an existing tool project](https://github.com/microsoft/promptflow/tree/main/examples/tools/tool-package-quickstart) as an example. -``` -cd D:\proj\github\promptflow\examples\tools\tool-package-quickstart -python D:\proj\github\promptflow\scripts\tool\generate_package_tool_meta.py -m my_tool_package.tools.my_tool_1 -o my_tool_package\yamls\my_tool_1.yaml -i my_tool_package\icons\custom-tool-icon.png +PF CLI will copy the icon file to the folder `/icons/` and generate a tool script in the package. The tool icon will be configured in the tool script. Here we use [an existing tool](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_1.py) as an example, the code is as follows: +```python +from pathlib import Path + +from promptflow import tool +from promptflow.connections import CustomConnection + + +@tool( + name="My First Tool", + description="This is my first tool", + icon=Path(__file__).parent.parent / "icons" / "custom-tool-icon.png" +) +def my_tool(connection: CustomConnection, input_text: str) -> str: + # Replace with your tool code. + # Usually connection contains configs to connect to an API. + # Use CustomConnection is a dict. You can use it like: connection.api_key, connection.api_base + # Not all tools need a connection. You can remove it if you don't need it. + return "Hello " + input_text ``` -In the auto-generated tool YAML file, the custom tool icon data URI is added in the `icon` field: -```yaml -my_tool_package.tools.my_tool_1.my_tool: - function: my_tool - icon: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACR0lEQVR4nKWS3UuTcRTHP79nm9ujM+fccqFGI5viRRpjJgkJ3hiCENVN/QMWdBHUVRdBNwX9ARHd2FVEWFLRjaS9XPmSC/EFTNOWc3Pi48y9PHNzz68L7UXTCvreHM65+PA953uElFLyHzLvHMwsJrnzfJqFeAan3cKV9mr8XseeAOXX5vqjSS53jdF+tIz1nIFAMDCzwpvJ5b87+LSYYHw+gcWkEAwluXnOR2Q1R+9YjJ7BKJG4zoXmqr0ddL3+QnV5EeUOK821LsJammcjEeZiafJScrd3bm8H6zkDd4mVztZKAK49/Mj8is4Z/35GPq9R5VJ5GYztDtB1HT1vovGQSiqVAqDugI3I6jpP3i9x9VQVfu8+1N/OvbWCqqqoBSa6h1fQNA1N0xiYTWJSBCZF8HgwSjQapbRQ2RUg5NYj3O6ZochmYkFL03S4mImIzjFvCf2xS5gtCRYXWvBUvKXjyEVeTN/DXuDgxsnuzSMK4HTAw1Q0hZba4NXEKp0tbpq9VkxCwTAETrsVwxBIBIYhMPI7YqyrtONQzSznJXrO4H5/GJ9LUGg0YFYydJxoYnwpj1s9SEN5KzZz4fYYAW6dr+VsowdFgamlPE/Hs8SzQZYzg0S+zjIc6iOWDDEc6uND+N12B9/VVu+mrd79o38wFCCdTeBSK6hxBii1eahxBlAtRbsDdmoiHGRNj1NZ7GM0NISvzM9oaIhiqwOO/wMgl4FsRpLf2KxGXpLNSLLInzH+CWBIA6RECIGUEiEUpDRACBSh8A3pXfGWdXfMgAAAAABJRU5ErkJggg== - inputs: - connection: - type: - - CustomConnection - input_text: - type: - - string - module: my_tool_package.tools.my_tool_1 - name: my_tool - type: python +The folder structure of the generated tool package is as follows: +``` + +│ MANIFEST.in +│ README.md +│ setup.py +│ +├───icons +│ +│ +└─── + .py + utils.py + __init__.py ``` ## Verify the tool icon in VS Code extension @@ -72,37 +84,42 @@ The content of `output.html` looks like the following, open it in a web browser ``` -### Can I add a tool icon to an existing tool package? -Yes, you can refer to the [preview icon](add-a-tool-icon.md#can-i-preview-the-tool-icon-image-before-adding-it-to-a-tool) section to generate the data URI and manually add the data URI to the tool's YAML file. +### Can I add a tool icon to an existing tool package + +You can follow these steps to add an icon to an existing package tool: +1. Copy the icon image to the package folder. +2. Configure the icon for the tool. + + In the tool script, add the `icon` parameter to the decorator method `@tool`, and the parameter value is the `icon path`. The code is as follows: + ```python + from promptflow import tool + + @tool(name="tool_name", icon=) + def tool_func(input_text: str) -> str: + # Tool logic + pass + ``` +3. Update `MANIFEST.in` in the package folder. + + This file is used to determine which files to include in the distribution of the project. You need to add the icon path relative to the package folder to this file. + ``` + include + ``` ### Can I add tool icons for dark and light mode separately? -Yes, you can add the tool icon data URIs manually or run the command below in your tool project directory to automatically generate your tool YAML, use _--icon-light_ to add a custom tool icon for the light mode and use _--icon-dark_ to add a custom tool icon for the dark mode: -``` -python \scripts\tool\generate_package_tool_meta.py -m -o --icon-light --icon-dark -``` -Here we use [an existing tool project](https://github.com/microsoft/promptflow/tree/main/examples/tools/tool-package-quickstart) as an example. -``` -cd D:\proj\github\promptflow\examples\tools\tool-package-quickstart +Yes, you can add the tool icon data to the tool code as follows: +```python +from promptflow import tool -python D:\proj\github\promptflow\scripts\tool\generate_package_tool_meta.py -m my_tool_package.tools.my_tool_1 -o my_tool_package\yamls\my_tool_1.yaml --icon-light my_tool_package\icons\custom-tool-icon-light.png --icon-dark my_tool_package\icons\custom-tool-icon-dark.png +@tool(name="tool_name", icon_dark=, icon_light=) +def tool_func(input_text: str) -> str: + # Tool logic + pass ``` -In the auto-generated tool YAML file, the light and dark tool icon data URIs are added in the `icon` field: -```yaml -my_tool_package.tools.my_tool_1.my_tool: - function: my_tool - icon: - dark: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAB00lEQVR4nI1SO2iTURT+7iNNb16a+Cg6iJWqRKwVRIrWV6GVUkrFdqiVShBaxIIi4iY4iouDoy4ODkKn4uQkDs5FfEzFYjEtJYQo5P/z35j/3uNw7Z80iHqHC/ec8z3OuQeMMcYYAHenU8n84YMAABw7mo93dEQpAIyBAyAiF1Kq8/Wrl5fHR1x6tjC9uPBcSrlZD4BxIgIgBCei+bnC6cGxSuWHEEIIUa58H7l0dWZqwlqSUjhq7oDWEoAL584Y6ymljDHGmM543BhvaPAsAKLfEjIyB6BeryPw796+EWidUInr16b5z6rWAYCmKXeEEADGRy+SLgXlFfLWbbWoyytULZ4f6Hee2yDgnAG4OVsoff20try08eX92vLSzJVJAJw3q7dISSnDMFx48UypeCa97cPHz7fu3Y/FYo1Go8nbCiAiIUStVus/eaKvN691IAQnsltI24wZY9Kp1Ju373K5bDKZNMa6gf5ZIWrG9/0g0K3W/wYIw3Dvnq6dO7KNMPwvgOf5x3uPHOrp9n3/HwBrLYCu3bv6Tg0PjU0d2L8PAEWfDKCtac6YIVrfKN2Zn8tkUqvfigBaR88Ya66uezMgl93+9Mmjxw8fJBIqWv7NAvwCHeuq7gEPU/QAAAAASUVORK5CYII= - light: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAB2UlEQVR4nH1SO2hUQRQ9c18K33u72cXs7jOL8UeQCCJoJaIgKAiCWKilaGNlYREFDRGNjayVWKiFFmITECFKJKIokQRRsDFENoooUchHU5qdWZ2512KymxcNOcUwc5nDuefeA2FhZpGFU0S0Mf5S0zpdF2FhISgopUREKfXj59yhoycmPn4GAKDncuXa9VtKKWYGACgowHOdc9a6g0eOA7mx8apzzlp76vRZoGXw6XMRsdb6nwSAmYnoQ3Xi5fBIdk2SiSMiCoKgNZslteruvX4ASikvSwAEAGDqdYhAXO+VypevkwODQ4+HnlGcq2mDNLwtZq5pvWP3AYRJ0Lq2uG5rWNgYFjaBVt+8c19E/jRaWvQgImPj1e279ufaN8elzly5K1/u6r7QZ51zrjmoBqHJ+TU/39ax5cy5i53bdnb39KXtLpr28OMLgiCfz78YHpmemi0W2piZWdIWaMmDCIDWet/ePUlS0toQUWM8yxG8jrVuw/qOTBw19rUiQUQoCGZm50z9txf8By3/K0Rh+PDRk8lv3+MoWklBBACmpmdKxcKn96O3b1SqC6FSyxOUgohk4pjZ9T8YeDX6ptye+PoSpNIrfkGv3747fOzk+UtXjTE+BM14M8tfl7BQR9VzUXEAAAAASUVORK5CYII= - inputs: - connection: - type: - - CustomConnection - input_text: - type: - - string - module: my_tool_package.tools.my_tool_1 - name: my_tool - type: python +Or run the command below in your tool project directory to automatically generate tool code, use _--icon_light_ to add a custom tool icon for the light mode and use _--icon_dark_ to add a custom tool icon for the dark mode: +```python +pf tool init --tool --set icon_dark= icon_light= ``` -Note: Both light and dark icons are optional. If you set either a light or dark icon, it will be used in its respective mode, and the system default icon will be used in the other mode. \ No newline at end of file + +Note: Both light and dark icons are optional. If you set either a light or dark icon, it will be used in its respective mode, and the system default icon will be used in the other mode. diff --git a/docs/how-to-guides/develop-a-tool/add-category-and-tags-for-tool.md b/docs/how-to-guides/develop-a-tool/add-category-and-tags-for-tool.md index 193f2853415..c7b9a0bb06b 100644 --- a/docs/how-to-guides/develop-a-tool/add-category-and-tags-for-tool.md +++ b/docs/how-to-guides/develop-a-tool/add-category-and-tags-for-tool.md @@ -1,4 +1,4 @@ -# Adding Category and Tags for Tool +# Adding category and tags for tool This document is dedicated to guiding you through the process of categorizing and tagging your tools for optimal organization and efficiency. Categories help you organize your tools into specific folders, making it much easier to find what you need. Tags, on the other hand, work like labels that offer more detailed descriptions. They enable you to quickly search and filter tools based on specific characteristics or functions. By using categories and tags, you'll not only tailor your tool library to your preferences but also save time by effortlessly finding the right tool for any task. @@ -13,49 +13,62 @@ This document is dedicated to guiding you through the process of categorizing an ## Prerequisites - Please ensure that your [Prompt flow for VS Code](https://marketplace.visualstudio.com/items?itemName=prompt-flow.prompt-flow) is updated to version 1.1.0 or later. +- Please install promptflow package and ensure that its version is 1.1.0 or later. ## How to add category and tags for a tool -Run the command below in your tool project directory to automatically generate your tool YAML, use _-c_ or _--category_ to add category, and use _--tags_ to add tags for your tool: -``` -python \scripts\tool\generate_package_tool_meta.py -m -o --category --tags -``` +You can use [pf tool init](../../reference/pf-command-reference.md#pf-tool-init) to initialize a package tool with category and tags: +```python +pf tool init --package --tool --set category= tags= -Here, we use [an existing tool](https://github.com/microsoft/promptflow/tree/main/examples/tools/tool-package-quickstart/my_tool_package/yamls/my_tool_1.yaml) as an example. If you wish to create your own tool, please refer to the [create and use tool package](create-and-use-tool-package.md#create-custom-tool-package) guide. ``` -cd D:\proj\github\promptflow\examples\tools\tool-package-quickstart -python D:\proj\github\promptflow\scripts\tool\generate_package_tool_meta.py -m my_tool_package.tools.my_tool_1 -o my_tool_package\yamls\my_tool_1.yaml --category "test_tool" --tags "{'tag1':'value1','tag2':'value2'}" +Here, we use an example to show the categories and tags of the tool after initialization. Assume that the user executes this command: +```python +pf tool init --tool my_tool --set name="My First Tool" description="This is my first tool" category="test_tool" tags="{'tag1':'value1','tag2':'value2'}" ``` -In the auto-generated tool YAML file, the category and tags are shown as below: -```yaml -my_tool_package.tools.my_tool_1.my_tool: - function: my_tool - inputs: - connection: - type: - - CustomConnection - input_text: - type: - - string - module: my_tool_package.tools.my_tool_1 - name: My First Tool - description: This is my first tool - type: python - # Category and tags are shown as below. - category: test_tool - tags: - tag1: value1 - tag2: value2 +The generated tool script is as follows, where category and tags have been configured on the tool: +```python +from promptflow import tool +from promptflow.connections import CustomConnection + + +@tool( + name="My First Tool", + description="This is my first tool", + category="test_tool", + tags={"tag1": "value1", "tag2": "value2"}, +) +def my_tool(self, input_text: str) -> str: + # Replace with your tool code. + # Usually connection contains configs to connect to an API. + # Use CustomConnection is a dict. You can use it like: connection.api_key, connection.api_base + # Not all tools need a connection. You can remove it if you don't need it. + return "Hello " + input_text ``` ## Tool with category and tags experience in VS Code extension -Follow the [steps](create-and-use-tool-package.md#use-your-tool-from-vscode-extension) to use your tool via the VS Code extension. -- Experience in the tool tree -![category_and_tags_in_tool_tree](../../media/how-to-guides/develop-a-tool/category_and_tags_in_tool_tree.png) - -- Experience in the tool list -By clicking `More` in the visual editor, you can view your tools along with their category and tags: -![category_and_tags_in_tool_list](../../media/how-to-guides/develop-a-tool/category_and_tags_in_tool_list.png) -Furthermore, you have the option to search or filter tools based on tags: -![filter_tools_by_tag](../../media/how-to-guides/develop-a-tool/filter_tools_by_tag.png) \ No newline at end of file +Follow the [steps](create-and-use-tool-package.md#use-your-tool-from-vscode-extension) to use your tool via the VS Code extension. +- Experience in the tool tree +![category_and_tags_in_tool_tree](../../media/how-to-guides/develop-a-tool/category_and_tags_in_tool_tree.png) + +- Experience in the tool list +By clicking `More` in the visual editor, you can view your tools along with their category and tags: +![category_and_tags_in_tool_list](../../media/how-to-guides/develop-a-tool/category_and_tags_in_tool_list.png) +Furthermore, you have the option to search or filter tools based on tags: +![filter_tools_by_tag](../../media/how-to-guides/develop-a-tool/filter_tools_by_tag.png) + +## FAQ +### How to configure category and tags on an existing package tool +Customer can configure category and tags directly on the tool script, as shown in the following code: +```python +@tool( + name="tool_name", + description="This is tool_name tool", + category=, + tags=, +) +def tool_name(input_text: str) -> str: + # tool logic + pass +``` \ No newline at end of file diff --git a/docs/how-to-guides/develop-a-tool/create-and-use-tool-package.md b/docs/how-to-guides/develop-a-tool/create-and-use-tool-package.md index 42269a3d792..9f8c21a5511 100644 --- a/docs/how-to-guides/develop-a-tool/create-and-use-tool-package.md +++ b/docs/how-to-guides/develop-a-tool/create-and-use-tool-package.md @@ -1,4 +1,4 @@ -# Create and Use Tool Package +# Create and use tool package In this document, we will guide you through the process of developing your own tool package, offering detailed steps and advice on how to utilize your creation. The custom tool is the prompt flow tool developed by yourself. If you find it useful, you can follow this guidance to make it a tool package. This will enable you to conveniently reuse it, share it with your team, or distribute it to anyone in the world. @@ -14,75 +14,45 @@ Create a new conda environment using python 3.9 or 3.10. Run below command to in ``` pip install promptflow ``` -Install Pytest packages for running tests: -``` -pip install pytest pytest-mock -``` -Clone the PromptFlow repository from GitHub using the following command: -``` -git clone https://github.com/microsoft/promptflow.git -``` ### Create custom tool package -Run below command under the root folder to create your tool project quickly: -``` -python \scripts\tool\generate_tool_package_template.py --destination --package-name --tool-name --function-name +You can use [pf tool init](../../reference/pf-command-reference.md#pf-tool-init) to initialize a package tool in current folder: + +```bash +pf tool init --package --tool + ``` For example: -``` -python D:\proj\github\promptflow\scripts\tool\generate_tool_package_template.py --destination hello-world-proj --package-name hello-world --tool-name hello_world_tool --function-name get_greeting_message +```bash +pf tool init --package hello_world --tool hello_world_tool ``` This auto-generated script will create one tool for you. The parameters _destination_ and _package-name_ are mandatory. The parameters _tool-name_ and _function-name_ are optional. If left unfilled, the _tool-name_ will default to _hello_world_tool_, and the _function-name_ will default to _tool-name_. The command will generate the tool project as follows with one tool `hello_world_tool.py` in it: ``` -hello-world-proj/ -│ -├── hello_world/ -│ ├── tools/ -│ │ ├── __init__.py -│ │ ├── hello_world_tool.py -│ │ └── utils.py -│ ├── yamls/ -│ │ └── hello_world_tool.yaml -│ └── __init__.py -│ -├── tests/ -│ ├── __init__.py -│ └── test_hello_world_tool.py -│ -├── MANIFEST.in -│ -└── setup.py +hello_world/ +│ MANIFEST.in +│ README.md +│ setup.py +│ +└───hello_world/ + hello_world_tool.py + utils.py + __init__.py ``` ```The points outlined below explain the purpose of each folder/file in the package. If your aim is to develop multiple tools within your package, please make sure to closely examine point 2 and 5.``` -1. **hello-world-proj**: This is the source directory. All of your project's source code should be placed in this directory. -2. **hello-world/tools**: This directory contains the individual tools for your project. Your tool package can contain either one tool or many tools. When adding a new tool, you should create another *_tool.py under the `tools` folder. -3. **hello-world/tools/hello_world_tool.py**: Develop your tool within the def function. Use the `@tool` decorator to identify the function as a tool. +1. **hello_world**: This is the source directory. All of your project's source code should be placed in this directory. +2. **hello_world/hello_world**: This directory contains the individual tools for your project. Your tool package can contain either one tool or many tools. When adding a new tool, you should create another *.py under this folder. +3. **hello-world/hello_world/hello_world_tool.py**: Develop your tool within the def function. Use the `@tool` decorator to identify the function as a tool. > [!Note] There are two ways to write a tool. The default and recommended way is the function implemented way. You can also use the class implementation way, referring to [my_tool_2.py](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_2.py) as an example. -4. **hello-world/tools/utils.py**: This file implements the tool list method, which collects all the tools defined. It is required to have this tool list method, as it allows the User Interface (UI) to retrieve your tools and display them within the UI. +4. **hello-world/hello_world/utils.py**: This file implements the tool list method, which collects all the tools defined. It is required to have this tool list method, as it allows the User Interface (UI) to retrieve your tools and display them within the UI. > [!Note] There's no need to create your own list method if you maintain the existing folder structure. You can simply use the auto-generated list method provided in the `utils.py` file. -5. **hello_world/yamls/hello_world_tool.yaml**: Tool YAMLs defines the metadata of the tool. The tool list method, as outlined in the `utils.py`, fetches these tool YAMLs. - - > [!Note] If you create a new tool, don't forget to also create the corresponding tool YAML. You can run below command under your tool project to auto generate your tool YAML. You may want to specify `-n` for `name` and `-d` for `description`, which would be displayed as the tool name and tooltip in prompt flow UI. - ``` - python \scripts\tool\generate_package_tool_meta.py -m -o -n -d - ``` - For example: - ``` - python D:\proj\github\promptflow\scripts\tool\generate_package_tool_meta.py -m hello_world.tools.hello_world_tool -o hello_world\yamls\hello_world_tool.yaml -n "Hello World Tool" -d "This is my hello world tool." - ``` - To populate your tool module, adhere to the pattern \.tools.\, which represents the folder path to your tool within the package. -6. **tests**: This directory contains all your tests, though they are not required for creating your custom tool package. When adding a new tool, you can also create corresponding tests and place them in this directory. Run below command under your tool project: - ``` - pytest tests - ``` -7. **MANIFEST.in**: This file is used to determine which files to include in the distribution of the project. Tool YAML files should be included in MANIFEST.in so that your tool YAMLs would be packaged and your tools can show in the UI. +7. **MANIFEST.in**: This file is used to determine which files to include in the distribution of the project. > [!Note] There's no need to update this file if you maintain the existing folder structure. -8. **setup.py**: This file contains metadata about your project like the name, version, author, and more. Additionally, the entry point is automatically configured for you in the `generate_tool_package_template.py` script. In Python, configuring the entry point in `setup.py` helps establish the primary execution point for a package, streamlining its integration with other software. +8. **setup.py**: This file contains metadata about your project like the name, version, author, and more. Additionally, the entry point is automatically configured for you by `pf tool init`. In Python, configuring the entry point in `setup.py` helps establish the primary execution point for a package, streamlining its integration with other software. The `package_tools` entry point together with the tool list method are used to retrieve all the tools and display them in the UI. ```python @@ -120,8 +90,7 @@ hello-world-proj/ ## FAQs ### Why is my custom tool not showing up in the UI? -Confirm that the tool YAML files are included in your custom tool package. You can add the YAML files to [MANIFEST.in](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/MANIFEST.in) and include the package data in [setup.py](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/setup.py). -Alternatively, you can test your tool package using the script below to ensure that you've packaged your tool YAML files and configured the package tool entry point correctly. +Confirm that configured the package tool correctly. Alternatively, you can test your tool package using the script below to ensure that you've configured the package tool entry point correctly. 1. Make sure to install the tool package in your conda environment before executing this script. 2. Create a python file anywhere and copy the content below into it. @@ -145,6 +114,7 @@ Alternatively, you can test your tool package using the script below to ensure t {'module': 'module_name', 'package': 'package_name', 'package_version': 'package_version', ...} """ entry_points = importlib.metadata.entry_points() + PACKAGE_TOOLS_ENTRY = "package_tools" if isinstance(entry_points, list): entry_points = entry_points.select(group=PACKAGE_TOOLS_ENTRY) else: diff --git a/docs/how-to-guides/develop-a-tool/create-cascading-tool-inputs.md b/docs/how-to-guides/develop-a-tool/create-cascading-tool-inputs.md index c639b8d800f..d4c1e296f0c 100644 --- a/docs/how-to-guides/develop-a-tool/create-cascading-tool-inputs.md +++ b/docs/how-to-guides/develop-a-tool/create-cascading-tool-inputs.md @@ -1,22 +1,30 @@ -# Creating Cascading Tool Inputs +# Creating cascading tool inputs Cascading input settings are useful when the value of one input field determines which subsequent inputs are shown. This makes the input process more streamlined, user-friendly, and error-free. This guide will walk through how to create cascading inputs for your tools. ## Prerequisites -Please make sure you have the latest version of [Prompt flow for VS Code](https://marketplace.visualstudio.com/items?itemName=prompt-flow.prompt-flow) installed (v1.2.0+). - +- Please make sure you have the latest version of [Prompt flow for VS Code](https://marketplace.visualstudio.com/items?itemName=prompt-flow.prompt-flow) installed (v1.2.0+). +- Please install promptflow package and ensure that its version is 1.0.0 or later. + ``` + pip install promptflow>=1.0.0 + ``` ## Create a tool with cascading inputs -We'll build out an example tool to show how cascading inputs work. The `student_id` and `teacher_id` inputs will be controlled by the value selected for the `user_type` input. Here's how to configure this in the tool code and YAML. +We'll build out an example tool to show how cascading inputs work. The `student_id` and `teacher_id` inputs will be controlled by the value selected for the `user_type` input. Here's how to configure this in the tool code. -1. Develop the tool function, following the [cascading inputs example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_cascading_inputs.py). Key points: - * Use the `@tool` decorator to mark the function as a tool. - * Define `UserType` as an Enum class, as it accepts only a specific set of fixed values in this example. - * Conditionally use inputs in the tool logic based on `user_type`. +Develop the tool function, following the [cascading inputs example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_cascading_inputs.py). Key points: + * Use the `@tool` decorator to mark the function as a tool. + * Define `UserType` as an Enum class, as it accepts only a specific set of fixed values in this example. + * Conditionally use inputs in the tool logic based on `user_type`. + * Add `enabled_by` and `enabled_by_value` to control visibility of dependent inputs. + * The `enabled_by` attribute specifies the input field, which must be an enum type, that controls the visibility of the dependent input field. + * The `enabled_by_value` attribute defines the accepted enum values from the `enabled_by` field that will make this dependent input field visible. + > Note: `enabled_by_value` takes a list, allowing multiple values to enable an input. ```python from enum import Enum +from promptflow.entities import InputSetting from promptflow import tool @@ -25,10 +33,16 @@ class UserType(str, Enum): TEACHER = "teacher" -@tool -def my_tool(user_type: Enum, student_id: str = "", teacher_id: str = "") -> str: - """This is a dummy function to support cascading inputs. - +@tool( + name="My Tool with Enabled By Value", + description="This is my tool with enabled by value", + input_settings={ + "teacher_id": InputSetting(enabled_by="user_type", enabled_by_value=[UserType.TEACHER]), + "student_id": InputSetting(enabled_by="user_type", enabled_by_value=[UserType.STUDENT]), + } +) +def my_tool(user_type: UserType, student_id: str = "", teacher_id: str = "") -> str: + """This is a dummy function to support enabled by feature. :param user_type: user type, student or teacher. :param student_id: student id. :param teacher_id: teacher id. @@ -44,42 +58,6 @@ def my_tool(user_type: Enum, student_id: str = "", teacher_id: str = "") -> str: raise Exception("Invalid user.") ``` -2. Generate a starting YAML for your tool per the [tool package guide](create-and-use-tool-package.md), then update it to enable cascading: - - Add `enabled_by` and `enabled_by_value` to control visibility of dependent inputs. See the [example YAML](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_cascading_inputs.yaml) for reference. - - * The `enabled_by` attribute specifies the input field, which must be an enum type, that controls the visibility of the dependent input field. - - * The `enabled_by_value` attribute defines the accepted enum values from the `enabled_by` field that will make this dependent input field visible. - > Note: `enabled_by_value` takes a list, allowing multiple values to enable an input. - -```yaml -my_tool_package.tools.tool_with_cascading_inputs.my_tool: - function: my_tool - inputs: - user_type: - type: - - string - enum: - - student - - teacher - student_id: - type: - - string - # This input is enabled by the input "user_type". - enabled_by: user_type - # This input is enabled when "user_type" is "student". - enabled_by_value: [student] - teacher_id: - type: - - string - enabled_by: user_type - enabled_by_value: [teacher] - module: my_tool_package.tools.tool_with_cascading_inputs - name: My Tool with Cascading Inputs - description: This is my tool with cascading inputs - type: python -``` ## Use the tool in VS Code Once you package and share your tool, you can use it in VS Code per the [tool package guide](create-and-use-tool-package.md). We have a [demo flow](https://github.com/microsoft/promptflow/tree/main/examples/tools/use-cases/cascading-inputs-tool-showcase) you can try. @@ -94,43 +72,34 @@ Before selecting a `user_type`, the `student_id` and `teacher_id` inputs are hid ## FAQs ### How do I create multi-layer cascading inputs? If you are dealing with multiple levels of cascading inputs, you can effectively manage the dependencies between them by using the `enabled_by` and `enabled_by_value` attributes. For example: -```yaml -my_tool_package.tools.tool_with_multi_layer_cascading_inputs.my_tool: - function: my_tool - inputs: - event_type: - type: - - string - enum: - - corporate - - private - corporate_theme: - type: - - string - # This input is enabled by the input "event_type". - enabled_by: event_type - # This input is enabled when "event_type" is "corporate". - enabled_by_value: [corporate] - enum: - - seminar - - team_building - seminar_location: - type: - - string - # This input is enabled by the input "corporate_theme". - enabled_by: corporate_theme - # This input is enabled when "corporate_theme" is "seminar". - enabled_by_value: [seminar] - private_theme: - type: - - string - # This input is enabled by the input "event_type". - enabled_by: event_type - # This input is enabled when "event_type" is "private". - enabled_by_value: [private] - module: my_tool_package.tools.tool_with_multi_layer_cascading_inputs - name: My Tool with Multi-Layer Cascading Inputs - description: This is my tool with multi-layer cascading inputs - type: python +```python +from enum import Enum + +from promptflow.entities import InputSetting +from promptflow import tool + + +class EventType(str, Enum): + CORPORATE = "corporate" + PRIVATE = "private" + + +class CorporateTheme(str, Enum): + SEMINAR = "seminar" + TEAM_BUILDING = "team_building" + + +@tool( + name="My Tool with Multi-Layer Cascading Inputs", + description="This is my tool with multi-layer cascading inputs", + input_settings={ + "corporate_theme": InputSetting(enabled_by="event_type", enabled_by_value=[EventType.CORPORATE]), + "seminar_location": InputSetting(enabled_by="corporate_theme", enabled_by_value=[CorporateTheme.SEMINAR]), + "private_theme": InputSetting(enabled_by="event_type", enabled_by_value=[CorporateTheme.PRIVATE]), + } +) +def my_tool(event_type: EventType, corporate_theme: CorporateTheme, seminar_location: str, private_theme: str) -> str: + """This is a dummy function to support enabled by feature.""" + pass ``` Inputs will be enabled in a cascading way based on selections. \ No newline at end of file diff --git a/docs/how-to-guides/develop-a-tool/create-dynamic-list-tool-input.md b/docs/how-to-guides/develop-a-tool/create-dynamic-list-tool-input.md index 475236ff813..44bb1042dae 100644 --- a/docs/how-to-guides/develop-a-tool/create-dynamic-list-tool-input.md +++ b/docs/how-to-guides/develop-a-tool/create-dynamic-list-tool-input.md @@ -1,4 +1,4 @@ -# Creating a Dynamic List Tool Input +# Creating a dynamic list tool input Tool input options can be generated on the fly using a dynamic list. Instead of having predefined static options, the tool author defines a request function that queries backends like APIs to retrieve real-time options. This enables flexible integration with various data sources to populate dynamic options. For instance, the function could call a storage API to list current files. Rather than a hardcoded list, the user sees up-to-date options when running the tool. @@ -60,53 +60,39 @@ def my_list_func(prefix: str = "", size: int = 10, **kwargs) -> List[Dict[str, U ### Configure a tool input with the list function -In `inputs` section of tool YAML, add following properties to the input that you want to make dynamic: +In `input_settings` section of tool, add following properties to the input that you want to make dynamic: -- `dynamic_list`: - - `func_path`: Path to the list function (module_name.function_name). - - `func_kwargs`: Parameters to pass to the function, can reference other input values. +- `DynamicList`: + - `function`: Path to the list function (module_name.function_name). + - `input_mapping`: Parameters to pass to the function, can reference other input values. - `allow_manual_entry`: Allow user to enter input value manually. Default to false. - `is_multi_select`: Allow user to select multiple values. Default to false. -See [tool_with_dynamic_list_input.yaml](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_dynamic_list_input.yaml) as an example. - -```yaml -my_tool_package.tools.tool_with_dynamic_list_input.my_tool: - function: my_tool - inputs: - input_text: - type: - - list - dynamic_list: - func_path: my_tool_package.tools.tool_with_dynamic_list_input.my_list_func - func_kwargs: - - name: prefix # argument name to be passed to the function - type: - - string - # if optional is not specified, default to false. - # this is for UX pre-validaton. If optional is false, but no input. UX can throw error in advanced. - optional: true - reference: ${inputs.input_prefix} # dynamic reference to another input parameter - - name: size # another argument name to be passed to the function - type: - - int - optional: true - default: 10 - # enum and dynamic list may need below setting. - # allow user to enter input value manually, default false. - allow_manual_entry: true - # allow user to select multiple values, default false. - is_multi_select: true - # used to filter - input_prefix: - type: - - string - module: my_tool_package.tools.tool_with_dynamic_list_input - name: My Tool with Dynamic List Input - description: This is my tool with dynamic list input - type: python -``` +See [tool_with_dynamic_list_input.py](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_dynamic_list_input.py) as an example. +```python +from promptflow._core.tool import tool +from promptflow.entities import InputSetting, DynamicList + + +dynamic_list_setting = DynamicList(function=my_list_func, input_mapping={"prefix": "input_prefix"}) +input_settings = { + "input_text": InputSetting( + dynamic_list=dynamic_list_setting, + allow_manual_entry=True, + is_multi_select=True + ) +} + + +@tool( + name="My Tool with Dynamic List Input", + description="This is my tool with dynamic list input", + input_settings=input_settings +) +def my_tool(input_text: list, input_prefix: str) -> str: + return f"Hello {input_prefix} {','.join(input_text)}" +``` ## Use the tool in VS Code Once you package and share your tool, you can use it in VS Code per the [tool package guide](create-and-use-tool-package.md#use-your-tool-from-vscode-extension). You could try `my-tools-package` for a quick test. diff --git a/docs/how-to-guides/develop-a-tool/create-your-own-custom-strong-type-connection.md b/docs/how-to-guides/develop-a-tool/create-your-own-custom-strong-type-connection.md index 44b8ece0b70..ae12dbc182c 100644 --- a/docs/how-to-guides/develop-a-tool/create-your-own-custom-strong-type-connection.md +++ b/docs/how-to-guides/develop-a-tool/create-your-own-custom-strong-type-connection.md @@ -1,4 +1,4 @@ -# Create and Use Your Own Custom Strong Type Connection +# Create and use your own custom strong type connection Connections provide a secure method for managing credentials for external APIs and data sources in prompt flow. This guide explains how to create and use a custom strong type connection. ## What is a Custom Strong Type Connection? @@ -12,9 +12,9 @@ For other connections types, please refer to [Connections](https://microsoft.git ## Prerequisites - Please ensure that your [Prompt flow for VS Code](https://marketplace.visualstudio.com/items?itemName=prompt-flow.prompt-flow) is updated to at least version 1.2.1. -- Please install promptflow package and ensure that its version is 0.1.0b8 or later. +- Please install promptflow package and ensure that its version is 1.0.0 or later. ``` - pip install promptflow>=0.1.0b8 + pip install promptflow>=1.0.0 ``` ## Create a custom strong type connection diff --git a/docs/how-to-guides/develop-a-tool/customize_an_llm_tool.md b/docs/how-to-guides/develop-a-tool/customize_an_llm_tool.md index d21ef07d0d6..1a102a78324 100644 --- a/docs/how-to-guides/develop-a-tool/customize_an_llm_tool.md +++ b/docs/how-to-guides/develop-a-tool/customize_an_llm_tool.md @@ -1,4 +1,4 @@ -# Customizing an LLM Tool +# Customizing an LLM tool In this document, we will guide you through the process of customizing an LLM tool, allowing users to seamlessly connect to a large language model with prompt tuning experience using a `PromptTemplate`. ## Prerequisites @@ -7,7 +7,7 @@ In this document, we will guide you through the process of customizing an LLM to ## How to customize an LLM tool Here we use [an existing tool package](https://github.com/microsoft/promptflow/tree/main/examples/tools/tool-package-quickstart/my_tool_package) as an example. If you want to create your own tool, please refer to [create and use tool package](create-and-use-tool-package.md). -1. Develop the tool code as in [this example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_llm_type.py). +Develop the tool code as in [this example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_llm_type.py). - Add a `CustomConnection` input to the tool, which is used to authenticate and establish a connection to the large language model. - Add a `PromptTemplate` input to the tool, which serves as an argument to be passed into the large language model. @@ -25,33 +25,6 @@ Here we use [an existing tool package](https://github.com/microsoft/promptflow/t return rendered_prompt ``` -2. Generate the custom LLM tool YAML. - Run the command below in your tool project directory to automatically generate your tool YAML, use _-t "custom_llm"_ or _--tool-type "custom_llm"_ to indicate this is a custom LLM tool: - ``` - python \scripts\tool\generate_package_tool_meta.py -m -o -t "custom_llm" - ``` - Here we use [an existing tool](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_custom_llm_type.yaml) as an example. - ``` - cd D:\proj\github\promptflow\examples\tools\tool-package-quickstart - - python D:\proj\github\promptflow\scripts\tool\generate_package_tool_meta.py -m my_tool_package.tools.tool_with_custom_llm_type -o my_tool_package\yamls\tool_with_custom_llm_type.yaml -n "My Custom LLM Tool" -d "This is a tool to demonstrate how to customize an LLM tool with a PromptTemplate." -t "custom_llm" - ``` - This command will generate a YAML file as follows: - - ```yaml - my_tool_package.tools.tool_with_custom_llm_type.my_tool: - name: My Custom LLM Tool - description: This is a tool to demonstrate how to customize an LLM tool with a PromptTemplate. - # The type is custom_llm. - type: custom_llm - module: my_tool_package.tools.tool_with_custom_llm_type - function: my_tool - inputs: - connection: - type: - - CustomConnection - ``` - ## Use the tool in VS Code Follow the steps to [build and install your tool package](create-and-use-tool-package.md#build-and-share-the-tool-package) and [use your tool from VS Code extension](create-and-use-tool-package.md#use-your-tool-from-vscode-extension). diff --git a/docs/how-to-guides/develop-a-tool/use-file-path-as-tool-input.md b/docs/how-to-guides/develop-a-tool/use-file-path-as-tool-input.md index d54fadfa048..06e3cc2ff10 100644 --- a/docs/how-to-guides/develop-a-tool/use-file-path-as-tool-input.md +++ b/docs/how-to-guides/develop-a-tool/use-file-path-as-tool-input.md @@ -1,4 +1,4 @@ -# Using File Path as Tool Input +# Using file path as tool input Users sometimes need to reference local files within a tool to implement specific logic. To simplify this, we've introduced the `FilePath` input type. This input type enables users to either select an existing file or create a new one, then pass it to a tool, allowing the tool to access the file's content. @@ -6,9 +6,9 @@ In this guide, we will provide a detailed walkthrough on how to use `FilePath` a ## Prerequisites -- Please install promptflow package and ensure that its version is 0.1.0b8 or later. +- Please install promptflow package and ensure that its version is 1.0.0 or later. ``` - pip install promptflow>=0.1.0b8 + pip install promptflow>=1.0.0 ``` - Please ensure that your [Prompt flow for VS Code](https://marketplace.visualstudio.com/items?itemName=prompt-flow.prompt-flow) is updated to version 1.1.0 or later. @@ -18,44 +18,25 @@ In this guide, we will provide a detailed walkthrough on how to use `FilePath` a Here we use [an existing tool package](https://github.com/microsoft/promptflow/tree/main/examples/tools/tool-package-quickstart/my_tool_package) as an example. If you want to create your own tool, please refer to [create and use tool package](create-and-use-tool-package.md#create-custom-tool-package). -1. Add a `FilePath` input for your tool, like in [this example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_file_path_input.py). +Add a `FilePath` input for your tool, like in [this example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_file_path_input.py). - ```python - import importlib - from pathlib import Path - from promptflow import tool - # 1. import the FilePath type - from promptflow.contracts.types import FilePath +```python +import importlib +from pathlib import Path +from promptflow import tool +# 1. import the FilePath type +from promptflow.contracts.types import FilePath - # 2. add a FilePath input for your tool method - @tool - def my_tool(input_file: FilePath, input_text: str) -> str: - # 3. customise your own code to handle and use the input_file here - new_module = importlib.import_module(Path(input_file).stem) - - return new_module.hello(input_text) - ``` +# 2. add a FilePath input for your tool method +@tool() +def my_tool(input_file: FilePath, input_text: str) -> str: + # 3. customise your own code to handle and use the input_file here + new_module = importlib.import_module(Path(input_file).stem) -2. `FilePath` input format in a tool YAML, like in [this example](https://github.com/microsoft/promptflow/blob/main/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_file_path_input.yaml). - - ```yaml - my_tool_package.tools.tool_with_file_path_input.my_tool: - function: my_tool - inputs: - # yaml format for FilePath input - input_file: - type: - - file_path - input_text: - type: - - string - module: my_tool_package.tools.tool_with_file_path_input - name: Tool with FilePath Input - description: This is a tool to demonstrate the usage of FilePath input - type: python - ``` + return new_module.hello(input_text) +``` - > [!Note] tool yaml file can be generated using a python script. For further details, please refer to [create custom tool package](create-and-use-tool-package.md#create-custom-tool-package). +> [!Note] tool yaml file can be generated using a python script. For further details, please refer to [create custom tool package](create-and-use-tool-package.md#create-custom-tool-package). ### Use tool with a file path input in VS Code extension diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_1.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_1.py index aab5630f3e3..3101fe53467 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_1.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_1.py @@ -1,8 +1,16 @@ +from pathlib import Path + from promptflow import tool from promptflow.connections import CustomConnection -@tool +@tool( + name="My First Tool", + description="This is my first tool", + icon=Path(__file__).parent.parent / "icons" / "custom-tool-icon.png", + category="test_tool", + tags={"tag1": "value1", "tag2": "value2"}, +) def my_tool(connection: CustomConnection, input_text: str) -> str: # Replace with your tool code. # Usually connection contains configs to connect to an API. diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_2.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_2.py index 7921e62f4dc..edb4d2d545f 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_2.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/my_tool_2.py @@ -11,7 +11,7 @@ def __init__(self, connection: CustomConnection): super().__init__() self.connection = connection - @tool + @tool(name="My Second Tool", description="This is my second tool") def my_tool(self, input_text: str) -> str: # Replace with your tool code. # Usually connection contains configs to connect to an API. diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_cascading_inputs.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_cascading_inputs.py index 0b706f52884..c74f242446d 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_cascading_inputs.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_cascading_inputs.py @@ -1,5 +1,6 @@ from enum import Enum +from promptflow.entities import InputSetting from promptflow import tool @@ -8,9 +9,16 @@ class UserType(str, Enum): TEACHER = "teacher" -@tool -def my_tool(user_type: Enum, student_id: str = "", teacher_id: str = "") -> str: - """This is a dummy function to support cascading inputs. +@tool( + name="My Tool with Enabled By Value", + description="This is my tool with enabled by value", + input_settings={ + "teacher_id": InputSetting(enabled_by="user_type", enabled_by_value=[UserType.TEACHER]), + "student_id": InputSetting(enabled_by="user_type", enabled_by_value=[UserType.STUDENT]), + } +) +def my_tool(user_type: UserType, student_id: str = "", teacher_id: str = "") -> str: + """This is a dummy function to support enabled by feature. :param user_type: user type, student or teacher. :param student_id: student id. diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_llm_type.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_llm_type.py index b313595622e..520b2ea507c 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_llm_type.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_llm_type.py @@ -4,7 +4,10 @@ from promptflow.contracts.types import PromptTemplate -@tool +@tool( + name="My Custom LLM Tool", + description="This is a tool to demonstrate how to customize an LLM tool with a PromptTemplate." +) def my_tool(connection: CustomConnection, prompt: PromptTemplate, **kwargs) -> str: # Replace with your tool code, customise your own code to handle and use the prompt here. # Usually connection contains configs to connect to an API. diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_strong_type_connection.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_strong_type_connection.py index 4450d49b198..e57e624a4a8 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_strong_type_connection.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_custom_strong_type_connection.py @@ -15,7 +15,10 @@ class MyCustomConnection(CustomStrongTypeConnection): api_base: str = "This is a fake api base." -@tool +@tool( + name="Tool With Custom Strong Type Connection", + description="This is my tool with custom strong type connection.", +) def my_tool(connection: MyCustomConnection, input_text: str) -> str: # Replace with your tool code. # Use custom strong type connection like: connection.api_key, connection.api_base diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_dynamic_list_input.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_dynamic_list_input.py index e5950b452d1..8708bc6ce11 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_dynamic_list_input.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_dynamic_list_input.py @@ -1,4 +1,5 @@ from promptflow import tool +from promptflow.entities import InputSetting, DynamicList from typing import List, Union, Dict @@ -68,6 +69,26 @@ def list_endpoint_names(subscription_id, resource_group_name, workspace_name, pr return result -@tool +input_text_dynamic_list_setting = DynamicList(function=my_list_func, input_mapping={"prefix": "input_prefix"}) +endpoint_name_dynamic_list_setting = DynamicList(function=list_endpoint_names, input_mapping={"prefix": "input_prefix"}) +input_settings = { + "input_text": InputSetting( + dynamic_list=input_text_dynamic_list_setting, + allow_manual_entry=True, + is_multi_select=True + ), + "endpoint_name": InputSetting( + dynamic_list=endpoint_name_dynamic_list_setting, + allow_manual_entry=False, + is_multi_select=False + ) +} + + +@tool( + name="My Tool with Dynamic List Input", + description="This is my tool with dynamic list input", + input_settings=input_settings +) def my_tool(input_prefix: str, input_text: list, endpoint_name: str) -> str: return f"Hello {input_prefix} {','.join(input_text)} {endpoint_name}" diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_file_path_input.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_file_path_input.py index 83af836751e..ba8a75b27a7 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_file_path_input.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_file_path_input.py @@ -4,7 +4,10 @@ from promptflow.contracts.types import FilePath -@tool +@tool( + name="Tool with FilePath Input", + description="This is a tool to demonstrate the usage of FilePath input", +) def my_tool(input_file: FilePath, input_text: str) -> str: # customise your own code to handle and use the input_file here new_module = importlib.import_module(Path(input_file).stem) diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_generated_by_input.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_generated_by_input.py index 898eff128af..3483ac46b43 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_generated_by_input.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/tool_with_generated_by_input.py @@ -1,6 +1,7 @@ from typing import Union from promptflow import tool +from promptflow._core.tool import InputSetting, DynamicList, GeneratedBy from typing import Dict, List from promptflow.connections import AzureOpenAIConnection, OpenAIConnection, CognitiveSearchConnection @@ -120,6 +121,68 @@ def list_embedding_deployment(embedding_connection: str) -> List[str]: return [{"value": "text-embedding-ada-002"}, {"value": "ada-1k-tpm"}] -@tool +generated_by_settings = GeneratedBy( + function=generate_index_json, + reverse_function=reverse_generate_index_json, + input_settings={ + "index_type": InputSetting( + dynamic_list=DynamicList(function=list_index_types) + ), + "index": InputSetting( + enabled_by="index_type", + enabled_by_value=["Workspace MLIndex"], + dynamic_list=DynamicList(function=list_indexes) + ), + "index_connection": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + ), + "index_name": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + ), + "content_field": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + dynamic_list=DynamicList(function=list_fields), + ), + "embedding_field": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + dynamic_list=DynamicList(function=list_fields), + ), + "metadata_field": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + dynamic_list=DynamicList(function=list_fields), + ), + "semantic_configuration": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + dynamic_list=DynamicList(function=list_semantic_configuration), + ), + "embedding_connection": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + ), + "embedding_deployment": InputSetting( + enabled_by="index_type", + enabled_by_value=["Azure Cognitive Search"], + dynamic_list=DynamicList( + function=list_embedding_deployment, + input_mapping={"embedding_connection": "embedding_connection"} + ), + ), + }, +) + + +@tool( + name="Tool with Generated By Input", + description="This is a tool with generated by input", + input_settings={ + "index_json": InputSetting(generated_by=generated_by_settings) + }, +) def my_tool(index_json: str, queries: str, top_k: int) -> str: return f"Hello {index_json}" diff --git a/examples/tools/tool-package-quickstart/my_tool_package/tools/utils.py b/examples/tools/tool-package-quickstart/my_tool_package/tools/utils.py index c5c89ec4cb6..d3f9aed0b99 100644 --- a/examples/tools/tool-package-quickstart/my_tool_package/tools/utils.py +++ b/examples/tools/tool-package-quickstart/my_tool_package/tools/utils.py @@ -1,18 +1,29 @@ -import yaml from pathlib import Path +import importlib.util +from promptflow import PFClient -def collect_tools_from_directory(base_dir) -> dict: - tools = {} - for f in Path(base_dir).glob("**/*.yaml"): - with open(f, "r") as f: - tools_in_file = yaml.safe_load(f) - for identifier, tool in tools_in_file.items(): - tools[identifier] = tool - return tools +package_name = "my_tools_package" def list_package_tools(): - """List package tools""" - yaml_dir = Path(__file__).parents[1] / "yamls" - return collect_tools_from_directory(yaml_dir) + """ + List the meta of all tools in the package. + The key of meta dict is the module name of tools and value is the meta data of the tool. + """ + # This function is auto generated by pf CLI, please do not modify manually. + tools = {} + pf_client = PFClient() + script_files = Path(__file__).parent.glob("**/*.py") + for file in script_files: + if not str(file).endswith("__init__.py"): + module_name = f'{package_name}.{Path(file).stem}' + + # Load the module from the file path + spec = importlib.util.spec_from_file_location(module_name, file) + module = importlib.util.module_from_spec(spec) + + # Load the module's code + spec.loader.exec_module(module) + tools.update(pf_client._tools.generate_tool_meta(module)) + return tools diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/my_tool_1.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/my_tool_1.yaml deleted file mode 100644 index 5aacdf98932..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/my_tool_1.yaml +++ /dev/null @@ -1,13 +0,0 @@ -my_tool_package.tools.my_tool_1.my_tool: - function: my_tool - inputs: - connection: - type: - - CustomConnection - input_text: - type: - - string - module: my_tool_package.tools.my_tool_1 - name: My First Tool - description: This is my first tool - type: python diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/my_tool_2.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/my_tool_2.yaml deleted file mode 100644 index a91954e1b34..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/my_tool_2.yaml +++ /dev/null @@ -1,14 +0,0 @@ -my_tool_package.tools.my_tool_2.MyTool.my_tool: - class_name: MyTool - function: my_tool - inputs: - connection: - type: - - CustomConnection - input_text: - type: - - string - module: my_tool_package.tools.my_tool_2 - name: My Second Tool - description: This is my second tool - type: python diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_cascading_inputs.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_cascading_inputs.yaml deleted file mode 100644 index 4103dad9c1a..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_cascading_inputs.yaml +++ /dev/null @@ -1,23 +0,0 @@ -my_tool_package.tools.tool_with_cascading_inputs.my_tool: - function: my_tool - inputs: - user_type: - type: - - string - enum: - - student - - teacher - student_id: - type: - - string - enabled_by: user_type - enabled_by_value: [student] - teacher_id: - type: - - string - enabled_by: user_type - enabled_by_value: [teacher] - module: my_tool_package.tools.tool_with_cascading_inputs - name: My Tool with Cascading Inputs - description: This is my tool with cascading inputs - type: python \ No newline at end of file diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_custom_llm_type.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_custom_llm_type.yaml deleted file mode 100644 index 2f0f0474a71..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_custom_llm_type.yaml +++ /dev/null @@ -1,10 +0,0 @@ -my_tool_package.tools.tool_with_custom_llm_type.my_tool: - name: My Custom LLM Tool - description: This is a tool to demonstrate how to customize an LLM tool with a PromptTemplate. - type: custom_llm - module: my_tool_package.tools.tool_with_custom_llm_type - function: my_tool - inputs: - connection: - type: - - CustomConnection diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_custom_strong_type_connection.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_custom_strong_type_connection.yaml deleted file mode 100644 index 888a5dbe6e1..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_custom_strong_type_connection.yaml +++ /dev/null @@ -1,15 +0,0 @@ -my_tool_package.tools.tool_with_custom_strong_type_connection.my_tool: - description: This is my tool with custom strong type connection. - function: my_tool - inputs: - connection: - custom_type: - - MyCustomConnection - type: - - CustomConnection - input_text: - type: - - string - module: my_tool_package.tools.tool_with_custom_strong_type_connection - name: Tool With Custom Strong Type Connection - type: python diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_dynamic_list_input.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_dynamic_list_input.yaml deleted file mode 100644 index a2fec97f44c..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_dynamic_list_input.yaml +++ /dev/null @@ -1,46 +0,0 @@ -my_tool_package.tools.tool_with_dynamic_list_input.my_tool: - function: my_tool - inputs: - input_prefix: - type: - - string - input_text: - type: - - list - dynamic_list: - func_path: my_tool_package.tools.tool_with_dynamic_list_input.my_list_func - func_kwargs: - - name: prefix # argument name to be passed to the function - type: - - string - # if optional is not specified, default to false. - # this is for UX pre-validaton. If optional is false, but no input. UX can throw error in advanced. - optional: true - reference: ${inputs.input_prefix} # dynamic reference to another input parameter - - name: size # another argument name to be passed to the function - type: - - int - optional: true - default: 10 - # enum and dynamic list may need below setting. - # allow user to enter input value manually, default false. - allow_manual_entry: true - # allow user to select multiple values, default false. - is_multi_select: true - endpoint_name: - type: - - string - dynamic_list: - func_path: my_tool_package.tools.tool_with_dynamic_list_input.list_endpoint_names - func_kwargs: - - name: prefix - type: - - string - optional: true - reference: ${inputs.input_prefix} - allow_manual_entry: false - is_multi_select: false - module: my_tool_package.tools.tool_with_dynamic_list_input - name: My Tool with Dynamic List Input - description: This is my tool with dynamic list input - type: python diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_file_path_input.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_file_path_input.yaml deleted file mode 100644 index c11b72be3c0..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_file_path_input.yaml +++ /dev/null @@ -1,13 +0,0 @@ -my_tool_package.tools.tool_with_file_path_input.my_tool: - function: my_tool - inputs: - input_file: - type: - - file_path - input_text: - type: - - string - module: my_tool_package.tools.tool_with_file_path_input - name: Tool with FilePath Input - description: This is a tool to demonstrate the usage of FilePath input - type: python diff --git a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_generated_by_input.yaml b/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_generated_by_input.yaml deleted file mode 100644 index 73575212730..00000000000 --- a/examples/tools/tool-package-quickstart/my_tool_package/yamls/tool_with_generated_by_input.yaml +++ /dev/null @@ -1,142 +0,0 @@ -my_tool_package.tools.tool_with_generated_by_input.my_tool: - function: my_tool - inputs: - index_json: - type: - - string - generated_by: - func_path: my_tool_package.tools.tool_with_generated_by_input.generate_index_json - func_kwargs: - - name: index_type - type: - - string - reference: ${inputs.index_type} - - name: index - type: - - string - optional: true - reference: ${inputs.index} - - name: index_connection - type: [CognitiveSearchConnection] - optional: true - reference: ${inputs.index_connection} - - name: index_name - type: - - string - optional: true - reference: ${inputs.index_name} - - name: content_field - type: - - string - optional: true - reference: ${inputs.content_field} - - name: embedding_field - type: - - string - optional: true - reference: ${inputs.embedding_field} - - name: metadata_field - type: - - string - optional: true - reference: ${inputs.metadata_field} - - name: semantic_configuration - type: - - string - optional: true - reference: ${inputs.semantic_configuration} - - name: embedding_connection - type: [AzureOpenAIConnection, OpenAIConnection] - optional: true - reference: ${inputs.embedding_connection} - - name: embedding_deployment - type: - - string - optional: true - reference: ${inputs.embedding_deployment} - reverse_func_path: my_tool_package.tools.tool_with_generated_by_input.reverse_generate_index_json - queries: - type: - - string - top_k: - type: - - int - index_type: - type: - - string - dynamic_list: - func_path: my_tool_package.tools.tool_with_generated_by_input.list_index_types - input_type: uionly_hidden - index: - type: - - string - enabled_by: index_type - enabled_by_value: ["Workspace MLIndex"] - dynamic_list: - func_path: my_tool_package.tools.tool_with_generated_by_input.list_indexes - input_type: uionly_hidden - index_connection: - type: [CognitiveSearchConnection] - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - input_type: uionly_hidden - index_name: - type: - - string - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - input_type: uionly_hidden - content_field: - type: - - string - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - dynamic_list: - func_path: my_tool_package.tools.tool_with_generated_by_input.list_fields - input_type: uionly_hidden - embedding_field: - type: - - string - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - dynamic_list: - func_path: my_tool_package.tools.tool_with_generated_by_input.list_fields - input_type: uionly_hidden - metadata_field: - type: - - string - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - dynamic_list: - func_path: my_tool_package.tools.tool_with_generated_by_input.list_fields - input_type: uionly_hidden - semantic_configuration: - type: - - string - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - dynamic_list: - func_path: my_tool_package.tools.tool_with_generated_by_input.list_semantic_configuration - input_type: uionly_hidden - embedding_connection: - type: [AzureOpenAIConnection, OpenAIConnection] - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - input_type: uionly_hidden - embedding_deployment: - type: - - string - enabled_by: index_type - enabled_by_value: ["Azure Cognitive Search"] - dynamic_list: - func_path: my_tool_package.tools.tool_with_generated_by_input.list_embedding_deployment - func_kwargs: - - name: embedding_connection - type: - - string - reference: ${inputs.embedding_connection} - input_type: uionly_hidden - module: my_tool_package.tools.tool_with_generated_by_input - name: Tool with Generated By Input - description: This is a tool with generated by input - type: python diff --git a/src/promptflow/promptflow/_cli/_pf/_tool.py b/src/promptflow/promptflow/_cli/_pf/_tool.py index 7960c0c0bbd..2a6cab132a6 100644 --- a/src/promptflow/promptflow/_cli/_pf/_tool.py +++ b/src/promptflow/promptflow/_cli/_pf/_tool.py @@ -156,10 +156,10 @@ def init_tool(args): script_code_path = package_path / package_name script_code_path.mkdir(parents=True, exist_ok=True) if icon_path: - package_icon_path = package_path / "icon" + package_icon_path = package_path / "icons" package_icon_path.mkdir(exist_ok=True) dst = shutil.copy2(icon_path, package_icon_path) - icon_path = f'Path(__file__).parent.parent / "icon" / "{Path(dst).name}"' + icon_path = f'Path(__file__).parent.parent / "icons" / "{Path(dst).name}"' # Generate package setup.py SetupGenerator(package_name=package_name, tool_name=args.tool).generate_to_file(package_path / "setup.py") # Generate manifest file