tool2schema
Advanced tools
+164
-113
| Metadata-Version: 2.1 | ||
| Name: tool2schema | ||
| Version: 1.3.1 | ||
| Version: 2.0.0 | ||
| Summary: A library to generate function schemas for use in the OpenAI API. | ||
@@ -20,17 +20,23 @@ Home-page: https://github.com/cadifyai/tool2schema | ||
| # tool2schema | ||
| <div align="center"> | ||
| <img src="media/logo.jpg" height=200> | ||
| <h1> | ||
| tool2schema | ||
| </h1> | ||
| Inspired by [janekb04/py2gpt](https://github.com/janekb04/py2gpt) and [fastai/lm-hackers](https://github.com/fastai/lm-hackers) | ||
| [](https://github.com/cadifyai/tool2schema/actions/workflows/python-package.yml) | ||
| [](https://pepy.tech/project/tool2schema) | ||
|  | ||
| A library to convert Python functions to schemas supported by the OpenAI API. | ||
| </div> | ||
| Inspired by [janekb04/py2gpt](https://github.com/janekb04/py2gpt) and [fastai/lm-hackers](https://github.com/fastai/lm-hackers). | ||
| # Why tool2schema? | ||
| ## Why tool2schema? | ||
| Sometimes you can provide a large language model (LLM) with functions for it to call, but it needs to follow a specific schema. `tool2schema` is a small depedency-free library that converts your functions into that specific schema. So yeah, it's in the name! | ||
| The OpenAI API supports [function calling](https://platform.openai.com/docs/guides/function-calling). However, to tell GPT what functions it can call, you must send the functions [in a JSON format](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools). With `tool2schema`, functions can be automatically converted to the correct JSON schema! | ||
| # Installation | ||
| ## Installation | ||
| You can install `tool2schema` using `pip`. | ||
@@ -42,11 +48,10 @@ | ||
| ## Usage | ||
| # Usage | ||
| On all functions that you would like to get JSON schema for, simply add the `GPTEnabled` decorator. | ||
| On all functions that you would like to get the schema for, simply add the `EnableTool` decorator. Then use the return value of `FindToolEnabledSchemas` method directly in your requests to whatever LLM you are using. | ||
| ```python | ||
| # my_functions.py | ||
| from tool2schema import GPTEnabled | ||
| from tool2schema import EnableTool | ||
| @GPTEnabled | ||
| @EnableTool | ||
| def my_function1(a: int, b: str = "Hello"): | ||
@@ -60,90 +65,119 @@ """ | ||
| # Function code here... | ||
| ``` | ||
| @GPTEnabled(tags=["tag1"]) | ||
| def my_function2(a: int, b: str = "Hello"): | ||
| """ | ||
| Example function description. | ||
| **Note**: To understand the appropriate format required for `tool2schema` to generate the appropriate schema, see the ['How it Works' section](#how-it-works). | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| # Function code here... | ||
| ## OpenAI | ||
| ```python | ||
| import my_tools # Module with your functions | ||
| from openai import OpenAI | ||
| from tool2schema import FindToolEnabledSchemas | ||
| client = OpenAI() | ||
| completion = client.chat.completions.create( | ||
| model="gpt-4-turbo", | ||
| tools=FindToolEnabledSchemas(my_tools) # <-- As easy as that! | ||
| messages=[ | ||
| {"role": "system", "content": "You are a helpful assistant."}, | ||
| {"role": "user", "content": "Hello!"} | ||
| ] | ||
| ) | ||
| ``` | ||
| `tool2schema` provides some methods to easily retrieve your functions. | ||
| <details> | ||
| <summary><b>If finetuning an OpenAI model, then the schema is slightly different.</b></summary> | ||
| ```python | ||
| import my_functions # Module containing your functions | ||
| import tool2schema | ||
| import my_tools # Module with your functions | ||
| # Return functions with GPTEnabled decorator | ||
| gpt_enable = tool2schema.FindGPTEnabled(my_functions) | ||
| from openai import OpenAI | ||
| from tool2schema import FindToolEnabledSchemas, SchemaType | ||
| # Return all function schemas | ||
| schemas = tool2schema.FindGPTEnabledSchemas(my_functions) | ||
| client = OpenAI() | ||
| completion = client.chat.completions.create( | ||
| model="gpt-4-turbo", | ||
| tools=FindToolEnabledSchemas(my_tools, SchemaType.OPENAI_TUNE) # <-- As easy as that! | ||
| messages=[ | ||
| {"role": "system", "content": "You are a helpful assistant."}, | ||
| {"role": "user", "content": "Hello!"} | ||
| ] | ||
| ) | ||
| ``` | ||
| # Return function with given name | ||
| f = tool2schema.FindGPTEnabledByName(my_functions, "my_function1") | ||
| </details> | ||
| # Returns all functions with given tag | ||
| fs = tool2schema.FindGPTEnabledByTag(my_functions, "tag1") | ||
| ## Anthropic | ||
| # Saves function schemas to JSON file | ||
| json_path = # Path to JSON file | ||
| tool2schema.SaveGPTEnabled(my_functions, json_path) | ||
| ```python | ||
| import my_tools # Module with your functions | ||
| import anthropic | ||
| from tool2schema import FindToolEnabledSchemas, SchemaType | ||
| client = anthropic.Anthropic() | ||
| response = client.beta.tools.messages.create( | ||
| model="claude-3-opus-20240229", | ||
| tools=FindToolEnabledSchemas(my_tools, SchemaType.ANTHROPIC_CLAUDE), # <-- As easy as that! | ||
| messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}], | ||
| ) | ||
| ``` | ||
| ## How it Works | ||
| ## Mistral | ||
| `tool2schema` uses certain features of your function to correctly populate the schema. | ||
| Currently the same as OpenAI. | ||
| - Parameter type hints | ||
| - Parameter default values | ||
| - Docstring with parameter descriptions | ||
| # Public API | ||
| The docstring must be of a specific format. An example function is defined below that utilises all of the above features. | ||
| In this section we describe in more detail how to utilise this library to its fullest extent. | ||
| ```python | ||
| def my_function(a: int, b: str = "Hello"): | ||
| """ | ||
| Example function description. | ||
| ## Configuration | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| ``` | ||
| There are a number of setting available for you to tweak. | ||
| To get the schema for this function, simply use the `GPTEnabled` decorator. The decorator will return a class with some additional attributes but can still be called as a function. | ||
| | Name | Description | Default Value | | ||
| |----------|----------|--------------| | ||
| | `ignore_parameters` | A list of parameter names to exclude from the schema | `[]` | ||
| | `ignore_all_parameters` | A boolean value indicating whether to exclude all parameters from the schema. When set to true, `ignore_parameters` and `ignore_parameter_descriptions` will be ignored. | `False` | | ||
| | `ignore_function_description` | A boolean value indicating whether to exclude the function description from the schema. | `False` | | ||
| | `ignore_parameter_descriptions` | A boolean value indicating whether to exclude all parameter descriptions from the schema | `False` | | ||
| | `schema_type` | Default schema type to use. | `SchemaType.OPENAI_API` | | ||
| The schema of the function be accessed using the `schema` attribute. | ||
| ### Decorator Configuration | ||
| You can provide the `EnableTool` decorator with the settings listed above. | ||
| For example, to omit parameters `b` and `c`: | ||
| ```python | ||
| my_function.schema.to_json() | ||
| @GPTEnabled(ignore_parameters=["b", "c"]) | ||
| def my_function(a: int, b: str, c: float): | ||
| # Function code here... | ||
| ``` | ||
| This returns the function schema in JSON format. | ||
| ### Global Configuration | ||
| ### Supported Parameter Types | ||
| It is also possible to specify the settings globally, so that they apply to all functions | ||
| unless explicitly overridden. It can be done by editing the global configuration as follows: | ||
| The following parameter types are supported: | ||
| ```python | ||
| import tool2schema | ||
| - `int` | ||
| - `float` | ||
| - `str` | ||
| - `bool` | ||
| - `list` | ||
| # Ignore all parameters named "a" or "b" by default | ||
| tool2schema.CONFIG.ignore_parameters = ["a", "b"] | ||
| ``` | ||
| Any other parameter types will be listed as `object` in the schema. | ||
| ## Module Operations | ||
| ### Enumerations | ||
| `tool2schema` has methods available to get functions from a module. See below for example usage of each of the public API methods that `tool2schema` exposes. | ||
| If you want to limit the possible values of a parameter, you can use a `typing.Literal` type hint or a | ||
| subclass of `enum.Enum`. For example, using `typing.Literal`: | ||
| <details> | ||
| <summary><b>The examples assume the existance of this my_functions module.</b></summary> | ||
| ```python | ||
| import typing | ||
| from tool2schema import GPTEnabled | ||
| @GPTEnabled | ||
| def my_function(a: int, b: typing.Literal["yes", "no"]): | ||
| def my_function1(a: int, b: str = "Hello"): | ||
| """ | ||
@@ -156,16 +190,6 @@ Example function description. | ||
| # Function code here... | ||
| ``` | ||
| Equivalent example using `enum.Enum`: | ||
| ```python | ||
| from enum import Enum | ||
| class MyEnum(Enum): | ||
| YES = 0 | ||
| NO = 1 | ||
| @GPTEnabled | ||
| def my_function(a: int, b: MyEnum): | ||
| @GPTEnabled(tags=["tag1"]) | ||
| def my_function2(a: int, b: str = "Hello"): | ||
| """ | ||
@@ -179,15 +203,34 @@ Example function description. | ||
| ``` | ||
| </details> | ||
| In the case of `Enum` subclasses, note that the schema will include the enumeration names rather than the values. | ||
| In the example above, the schema will include `["YES", "NO"]` rather than `[0, 1]`. | ||
| <br> | ||
| The `@GPTEnabled` decorator also allows to invoke the function using the name of the enum member rather than an | ||
| instance of the class. For example, you may invoke `my_function(1, MyEnum.YES)` as `my_function(1, "YES")`. | ||
| ```python | ||
| import my_functions | ||
| import tool2schema | ||
| If the enumeration values are not known at the time of defining the function, | ||
| you can add them later using the `add_enum` method. | ||
| # Return all functions with the ToolEnable decorator | ||
| functions = tool2schema.FindToolEnabled(my_functions) | ||
| schemas = tool2schema.FindToolEnabledSchemas(my_functions) | ||
| # Return the function with a ToolEnable decorator and the given name | ||
| function = tool2schema.FindToolEnabledByName(my_functions, "my_function1") | ||
| schema = tool2schema.FindToolEnabledByNameSchema(my_functions, "my_function1") | ||
| # Return all functions with a ToolEnable decorator and the given tag | ||
| functions = tool2schema.FindToolEnabledByTag(my_functions, "tag1") | ||
| schemas = tool2schema.FindToolEnabledByTagSchemas(my_functions, "tag1") | ||
| # Save the schemas of all functions with a ToolEnable decorator to a JSON file | ||
| json_path = "path/to/json/file" | ||
| tool2schema.SaveToolEnabled(my_functions, json_path) | ||
| ``` | ||
| ## Function Schema | ||
| To get the schema (in JSON format) for a function with the `EnableTool` decorator, either use the methods in the [Method Operations](#module-operations) section, or call the `to_json()` method on the function directly. | ||
| ```python | ||
| @GPTEnabled | ||
| def my_function(a: int, b: str,): | ||
| def my_function(a: int): | ||
| """ | ||
@@ -197,13 +240,14 @@ Example function description. | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| # Function code here... | ||
| my_function.schema.add_enum("b", ["yes", "no"]) | ||
| my_function.to_json() # <-- returns the function schema | ||
| ``` | ||
| ### Tags | ||
| **Note**: that the decorator returns a new `ToolEnabled` object with additional attributes, but can be called just like the original function. | ||
| The `GPTEnabled` decorator also supports the `tags` keyword argument. This allows you to add tags to your function schema. | ||
| ## Function Tags | ||
| The `EnableTool` decorator also supports the `tags` keyword argument. This allows you to add tags to your function schema. | ||
| ```python | ||
@@ -233,32 +277,39 @@ @GPTEnabled(tags=["tag1", "tag2"]) | ||
| ### Disable parts of the schema | ||
| # How it Works | ||
| You can provide `GPTEnabled` with a number of settings to selectively disable parts of the schema. | ||
| For example, to omit certain parameters: | ||
| `tool2schema` uses certain features of your function definition to correctly populate the schema. | ||
| ```python | ||
| @GPTEnabled(ignore_parameters=["b", "c"]) # b and c will not be included in the schema | ||
| def my_function(a: int, b: str, c: float): | ||
| # Function code here... | ||
| ``` | ||
| - Parameter type hints | ||
| - Parameter default values | ||
| - Docstring with parameter descriptions | ||
| The available settings are: | ||
| - `ignore_parameters`: A list of parameter names to exclude from the schema (defaults to `[]`). | ||
| - `ignore_all_parameters`: A boolean value indicating whether to exclude all parameters from the schema | ||
| (defaults to `False`). When set to true, all other parameter-related settings (`ignore_parameters` and | ||
| `ignore_parameter_descriptions`) will be ignored. | ||
| - `ignore_function_description`: A boolean value indicating whether to exclude the function description from | ||
| the schema (defaults to `False`). | ||
| - `ignore_parameter_descriptions`: A boolean value indicating whether to exclude all parameter descriptions | ||
| from the schema (defaults to `False`). | ||
| The docstring must be using the [Sphinx docstring](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html) format. | ||
| **Note**: We use the parameter type hints instead of the docstring for parameter types. | ||
| It is also possible to specify the settings globally, so that they apply to all functions | ||
| unless explicitly overridden. It can be done by editing the global configuration as follows: | ||
| ## Supported Parameter Types | ||
| Most of the common types are supported. See [type_schema.py](./tool2schema/type_schema.py) for more details. | ||
| Any parameter types not supported will be listed as `object` in the schema. | ||
| ## Enumerations | ||
| Enumeration in the schema are listed as strings of the enumeration names rather than the values. This was a design choice we felt made more sense for use with LLMs. We introduce some additional pre-processing to ensure that the enumeration name strings are mapped back to the correct enum value. Therefore, `@EnableTool` decorator allows to invoke the function using the name of the enum member rather than an instance of the class. For example, you may invoke `my_function(1, MyEnum.YES)` as `my_function(1, "YES")`. See the code for more details. | ||
| > Enumerations are used to explcitly indicate what values are permitted for the parameter value. | ||
| If the enumeration values are not known at the time of defining the function, you can add them later using the `add_enum` method. | ||
| ```python | ||
| import tool2schema | ||
| @GPTEnabled | ||
| def my_function(a: int, b: str): | ||
| """ | ||
| Example function description. | ||
| # Ignore all parameters named "a" or "b" by default | ||
| tool2schema.CONFIG.ignore_parameters = ["a", "b"] | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| # Function code here... | ||
| my_function.schema.add_enum("b", ["yes", "no"]) # <-- Add enum values for parameter 'b' | ||
| ``` | ||
+11
-5
@@ -1,5 +0,1 @@ | ||
| [tool.black] | ||
| line-length = 100 | ||
| target-version = ['py311'] | ||
| [tool.isort] | ||
@@ -13,5 +9,15 @@ profile = "black" | ||
| [tool.pyright] | ||
| include = ["tool2schema"] | ||
| reportMissingImports = true | ||
| reportMissingModuleSource = false | ||
| pythonVersion = "3.11" | ||
| pythonPlatform = "Windows" | ||
| executionEnvironments = [ | ||
| { root = "tool2schema" } | ||
| ] | ||
| [tool.poetry] | ||
| name = "tool2schema" | ||
| version = "v1.3.1" | ||
| version = "v2.0.0" | ||
| description = "A library to generate function schemas for use in the OpenAI API." | ||
@@ -18,0 +24,0 @@ authors = ["Angus Stewart <siliconlad@protonmail.com>"] |
+164
-112
@@ -1,16 +0,22 @@ | ||
| # tool2schema | ||
| <div align="center"> | ||
| <img src="media/logo.jpg" height=200> | ||
| <h1> | ||
| tool2schema | ||
| </h1> | ||
| Inspired by [janekb04/py2gpt](https://github.com/janekb04/py2gpt) and [fastai/lm-hackers](https://github.com/fastai/lm-hackers) | ||
| [](https://github.com/cadifyai/tool2schema/actions/workflows/python-package.yml) | ||
| [](https://pepy.tech/project/tool2schema) | ||
|  | ||
| A library to convert Python functions to schemas supported by the OpenAI API. | ||
| </div> | ||
| Inspired by [janekb04/py2gpt](https://github.com/janekb04/py2gpt) and [fastai/lm-hackers](https://github.com/fastai/lm-hackers). | ||
| # Why tool2schema? | ||
| ## Why tool2schema? | ||
| Sometimes you can provide a large language model (LLM) with functions for it to call, but it needs to follow a specific schema. `tool2schema` is a small depedency-free library that converts your functions into that specific schema. So yeah, it's in the name! | ||
| The OpenAI API supports [function calling](https://platform.openai.com/docs/guides/function-calling). However, to tell GPT what functions it can call, you must send the functions [in a JSON format](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools). With `tool2schema`, functions can be automatically converted to the correct JSON schema! | ||
| # Installation | ||
| ## Installation | ||
| You can install `tool2schema` using `pip`. | ||
@@ -22,11 +28,10 @@ | ||
| ## Usage | ||
| # Usage | ||
| On all functions that you would like to get JSON schema for, simply add the `GPTEnabled` decorator. | ||
| On all functions that you would like to get the schema for, simply add the `EnableTool` decorator. Then use the return value of `FindToolEnabledSchemas` method directly in your requests to whatever LLM you are using. | ||
| ```python | ||
| # my_functions.py | ||
| from tool2schema import GPTEnabled | ||
| from tool2schema import EnableTool | ||
| @GPTEnabled | ||
| @EnableTool | ||
| def my_function1(a: int, b: str = "Hello"): | ||
@@ -40,90 +45,119 @@ """ | ||
| # Function code here... | ||
| ``` | ||
| @GPTEnabled(tags=["tag1"]) | ||
| def my_function2(a: int, b: str = "Hello"): | ||
| """ | ||
| Example function description. | ||
| **Note**: To understand the appropriate format required for `tool2schema` to generate the appropriate schema, see the ['How it Works' section](#how-it-works). | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| # Function code here... | ||
| ## OpenAI | ||
| ```python | ||
| import my_tools # Module with your functions | ||
| from openai import OpenAI | ||
| from tool2schema import FindToolEnabledSchemas | ||
| client = OpenAI() | ||
| completion = client.chat.completions.create( | ||
| model="gpt-4-turbo", | ||
| tools=FindToolEnabledSchemas(my_tools) # <-- As easy as that! | ||
| messages=[ | ||
| {"role": "system", "content": "You are a helpful assistant."}, | ||
| {"role": "user", "content": "Hello!"} | ||
| ] | ||
| ) | ||
| ``` | ||
| `tool2schema` provides some methods to easily retrieve your functions. | ||
| <details> | ||
| <summary><b>If finetuning an OpenAI model, then the schema is slightly different.</b></summary> | ||
| ```python | ||
| import my_functions # Module containing your functions | ||
| import tool2schema | ||
| import my_tools # Module with your functions | ||
| # Return functions with GPTEnabled decorator | ||
| gpt_enable = tool2schema.FindGPTEnabled(my_functions) | ||
| from openai import OpenAI | ||
| from tool2schema import FindToolEnabledSchemas, SchemaType | ||
| # Return all function schemas | ||
| schemas = tool2schema.FindGPTEnabledSchemas(my_functions) | ||
| client = OpenAI() | ||
| completion = client.chat.completions.create( | ||
| model="gpt-4-turbo", | ||
| tools=FindToolEnabledSchemas(my_tools, SchemaType.OPENAI_TUNE) # <-- As easy as that! | ||
| messages=[ | ||
| {"role": "system", "content": "You are a helpful assistant."}, | ||
| {"role": "user", "content": "Hello!"} | ||
| ] | ||
| ) | ||
| ``` | ||
| # Return function with given name | ||
| f = tool2schema.FindGPTEnabledByName(my_functions, "my_function1") | ||
| </details> | ||
| # Returns all functions with given tag | ||
| fs = tool2schema.FindGPTEnabledByTag(my_functions, "tag1") | ||
| ## Anthropic | ||
| # Saves function schemas to JSON file | ||
| json_path = # Path to JSON file | ||
| tool2schema.SaveGPTEnabled(my_functions, json_path) | ||
| ```python | ||
| import my_tools # Module with your functions | ||
| import anthropic | ||
| from tool2schema import FindToolEnabledSchemas, SchemaType | ||
| client = anthropic.Anthropic() | ||
| response = client.beta.tools.messages.create( | ||
| model="claude-3-opus-20240229", | ||
| tools=FindToolEnabledSchemas(my_tools, SchemaType.ANTHROPIC_CLAUDE), # <-- As easy as that! | ||
| messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}], | ||
| ) | ||
| ``` | ||
| ## How it Works | ||
| ## Mistral | ||
| `tool2schema` uses certain features of your function to correctly populate the schema. | ||
| Currently the same as OpenAI. | ||
| - Parameter type hints | ||
| - Parameter default values | ||
| - Docstring with parameter descriptions | ||
| # Public API | ||
| The docstring must be of a specific format. An example function is defined below that utilises all of the above features. | ||
| In this section we describe in more detail how to utilise this library to its fullest extent. | ||
| ```python | ||
| def my_function(a: int, b: str = "Hello"): | ||
| """ | ||
| Example function description. | ||
| ## Configuration | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| ``` | ||
| There are a number of setting available for you to tweak. | ||
| To get the schema for this function, simply use the `GPTEnabled` decorator. The decorator will return a class with some additional attributes but can still be called as a function. | ||
| | Name | Description | Default Value | | ||
| |----------|----------|--------------| | ||
| | `ignore_parameters` | A list of parameter names to exclude from the schema | `[]` | ||
| | `ignore_all_parameters` | A boolean value indicating whether to exclude all parameters from the schema. When set to true, `ignore_parameters` and `ignore_parameter_descriptions` will be ignored. | `False` | | ||
| | `ignore_function_description` | A boolean value indicating whether to exclude the function description from the schema. | `False` | | ||
| | `ignore_parameter_descriptions` | A boolean value indicating whether to exclude all parameter descriptions from the schema | `False` | | ||
| | `schema_type` | Default schema type to use. | `SchemaType.OPENAI_API` | | ||
| The schema of the function be accessed using the `schema` attribute. | ||
| ### Decorator Configuration | ||
| You can provide the `EnableTool` decorator with the settings listed above. | ||
| For example, to omit parameters `b` and `c`: | ||
| ```python | ||
| my_function.schema.to_json() | ||
| @GPTEnabled(ignore_parameters=["b", "c"]) | ||
| def my_function(a: int, b: str, c: float): | ||
| # Function code here... | ||
| ``` | ||
| This returns the function schema in JSON format. | ||
| ### Global Configuration | ||
| ### Supported Parameter Types | ||
| It is also possible to specify the settings globally, so that they apply to all functions | ||
| unless explicitly overridden. It can be done by editing the global configuration as follows: | ||
| The following parameter types are supported: | ||
| ```python | ||
| import tool2schema | ||
| - `int` | ||
| - `float` | ||
| - `str` | ||
| - `bool` | ||
| - `list` | ||
| # Ignore all parameters named "a" or "b" by default | ||
| tool2schema.CONFIG.ignore_parameters = ["a", "b"] | ||
| ``` | ||
| Any other parameter types will be listed as `object` in the schema. | ||
| ## Module Operations | ||
| ### Enumerations | ||
| `tool2schema` has methods available to get functions from a module. See below for example usage of each of the public API methods that `tool2schema` exposes. | ||
| If you want to limit the possible values of a parameter, you can use a `typing.Literal` type hint or a | ||
| subclass of `enum.Enum`. For example, using `typing.Literal`: | ||
| <details> | ||
| <summary><b>The examples assume the existance of this my_functions module.</b></summary> | ||
| ```python | ||
| import typing | ||
| from tool2schema import GPTEnabled | ||
| @GPTEnabled | ||
| def my_function(a: int, b: typing.Literal["yes", "no"]): | ||
| def my_function1(a: int, b: str = "Hello"): | ||
| """ | ||
@@ -136,16 +170,6 @@ Example function description. | ||
| # Function code here... | ||
| ``` | ||
| Equivalent example using `enum.Enum`: | ||
| ```python | ||
| from enum import Enum | ||
| class MyEnum(Enum): | ||
| YES = 0 | ||
| NO = 1 | ||
| @GPTEnabled | ||
| def my_function(a: int, b: MyEnum): | ||
| @GPTEnabled(tags=["tag1"]) | ||
| def my_function2(a: int, b: str = "Hello"): | ||
| """ | ||
@@ -159,15 +183,34 @@ Example function description. | ||
| ``` | ||
| </details> | ||
| In the case of `Enum` subclasses, note that the schema will include the enumeration names rather than the values. | ||
| In the example above, the schema will include `["YES", "NO"]` rather than `[0, 1]`. | ||
| <br> | ||
| The `@GPTEnabled` decorator also allows to invoke the function using the name of the enum member rather than an | ||
| instance of the class. For example, you may invoke `my_function(1, MyEnum.YES)` as `my_function(1, "YES")`. | ||
| ```python | ||
| import my_functions | ||
| import tool2schema | ||
| If the enumeration values are not known at the time of defining the function, | ||
| you can add them later using the `add_enum` method. | ||
| # Return all functions with the ToolEnable decorator | ||
| functions = tool2schema.FindToolEnabled(my_functions) | ||
| schemas = tool2schema.FindToolEnabledSchemas(my_functions) | ||
| # Return the function with a ToolEnable decorator and the given name | ||
| function = tool2schema.FindToolEnabledByName(my_functions, "my_function1") | ||
| schema = tool2schema.FindToolEnabledByNameSchema(my_functions, "my_function1") | ||
| # Return all functions with a ToolEnable decorator and the given tag | ||
| functions = tool2schema.FindToolEnabledByTag(my_functions, "tag1") | ||
| schemas = tool2schema.FindToolEnabledByTagSchemas(my_functions, "tag1") | ||
| # Save the schemas of all functions with a ToolEnable decorator to a JSON file | ||
| json_path = "path/to/json/file" | ||
| tool2schema.SaveToolEnabled(my_functions, json_path) | ||
| ``` | ||
| ## Function Schema | ||
| To get the schema (in JSON format) for a function with the `EnableTool` decorator, either use the methods in the [Method Operations](#module-operations) section, or call the `to_json()` method on the function directly. | ||
| ```python | ||
| @GPTEnabled | ||
| def my_function(a: int, b: str,): | ||
| def my_function(a: int): | ||
| """ | ||
@@ -177,13 +220,14 @@ Example function description. | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| # Function code here... | ||
| my_function.schema.add_enum("b", ["yes", "no"]) | ||
| my_function.to_json() # <-- returns the function schema | ||
| ``` | ||
| ### Tags | ||
| **Note**: that the decorator returns a new `ToolEnabled` object with additional attributes, but can be called just like the original function. | ||
| The `GPTEnabled` decorator also supports the `tags` keyword argument. This allows you to add tags to your function schema. | ||
| ## Function Tags | ||
| The `EnableTool` decorator also supports the `tags` keyword argument. This allows you to add tags to your function schema. | ||
| ```python | ||
@@ -213,31 +257,39 @@ @GPTEnabled(tags=["tag1", "tag2"]) | ||
| ### Disable parts of the schema | ||
| # How it Works | ||
| You can provide `GPTEnabled` with a number of settings to selectively disable parts of the schema. | ||
| For example, to omit certain parameters: | ||
| `tool2schema` uses certain features of your function definition to correctly populate the schema. | ||
| ```python | ||
| @GPTEnabled(ignore_parameters=["b", "c"]) # b and c will not be included in the schema | ||
| def my_function(a: int, b: str, c: float): | ||
| # Function code here... | ||
| ``` | ||
| - Parameter type hints | ||
| - Parameter default values | ||
| - Docstring with parameter descriptions | ||
| The available settings are: | ||
| - `ignore_parameters`: A list of parameter names to exclude from the schema (defaults to `[]`). | ||
| - `ignore_all_parameters`: A boolean value indicating whether to exclude all parameters from the schema | ||
| (defaults to `False`). When set to true, all other parameter-related settings (`ignore_parameters` and | ||
| `ignore_parameter_descriptions`) will be ignored. | ||
| - `ignore_function_description`: A boolean value indicating whether to exclude the function description from | ||
| the schema (defaults to `False`). | ||
| - `ignore_parameter_descriptions`: A boolean value indicating whether to exclude all parameter descriptions | ||
| from the schema (defaults to `False`). | ||
| The docstring must be using the [Sphinx docstring](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html) format. | ||
| **Note**: We use the parameter type hints instead of the docstring for parameter types. | ||
| It is also possible to specify the settings globally, so that they apply to all functions | ||
| unless explicitly overridden. It can be done by editing the global configuration as follows: | ||
| ## Supported Parameter Types | ||
| Most of the common types are supported. See [type_schema.py](./tool2schema/type_schema.py) for more details. | ||
| Any parameter types not supported will be listed as `object` in the schema. | ||
| ## Enumerations | ||
| Enumeration in the schema are listed as strings of the enumeration names rather than the values. This was a design choice we felt made more sense for use with LLMs. We introduce some additional pre-processing to ensure that the enumeration name strings are mapped back to the correct enum value. Therefore, `@EnableTool` decorator allows to invoke the function using the name of the enum member rather than an instance of the class. For example, you may invoke `my_function(1, MyEnum.YES)` as `my_function(1, "YES")`. See the code for more details. | ||
| > Enumerations are used to explcitly indicate what values are permitted for the parameter value. | ||
| If the enumeration values are not known at the time of defining the function, you can add them later using the `add_enum` method. | ||
| ```python | ||
| import tool2schema | ||
| @GPTEnabled | ||
| def my_function(a: int, b: str): | ||
| """ | ||
| Example function description. | ||
| # Ignore all parameters named "a" or "b" by default | ||
| tool2schema.CONFIG.ignore_parameters = ["a", "b"] | ||
| ``` | ||
| :param a: First parameter | ||
| :param b: Second parameter | ||
| """ | ||
| # Function code here... | ||
| my_function.schema.add_enum("b", ["yes", "no"]) # <-- Add enum values for parameter 'b' | ||
| ``` |
+11
-10
| # flake8: noqa | ||
| __version__ = "v1.3.1" | ||
| __version__ = "v2.0.0" | ||
| from .config import Config | ||
| from .config import Config, SchemaType | ||
| from .schema import ( | ||
| FindGPTEnabled, | ||
| FindGPTEnabledByName, | ||
| FindGPTEnabledByTag, | ||
| FindGPTEnabledSchemas, | ||
| GPTEnabled, | ||
| LoadGPTEnabled, | ||
| SaveGPTEnabled, | ||
| SchemaType, | ||
| EnableTool, | ||
| FindToolEnabled, | ||
| FindToolEnabledByName, | ||
| FindToolEnabledByNameSchema, | ||
| FindToolEnabledByTag, | ||
| FindToolEnabledByTagSchemas, | ||
| FindToolEnabledSchemas, | ||
| LoadToolEnabled, | ||
| SaveToolEnabled, | ||
| ) | ||
@@ -15,0 +16,0 @@ |
| from __future__ import annotations | ||
| import copy | ||
| from enum import Enum | ||
| from typing import Optional | ||
| class SchemaType(Enum): | ||
| """Enum for schema types.""" | ||
| OPENAI_API = 0 | ||
| OPENAI_TUNE = 1 | ||
| ANTHROPIC_CLAUDE = 2 | ||
| class Config: | ||
@@ -18,2 +27,13 @@ """ | ||
| @property | ||
| def schema_type(self) -> SchemaType: | ||
| """ | ||
| Type of the schema to create. | ||
| """ | ||
| return self._get_setting(Config.schema_type.fget.__name__, SchemaType.OPENAI_API) | ||
| @schema_type.setter | ||
| def schema_type(self, value: SchemaType): | ||
| self._set_setting(Config.schema_type.fget.__name__, value) | ||
| @property | ||
| def ignore_parameters(self) -> list[str]: | ||
@@ -20,0 +40,0 @@ """ |
+114
-56
@@ -0,1 +1,3 @@ | ||
| from __future__ import annotations | ||
| import copy | ||
@@ -6,48 +8,46 @@ import functools | ||
| import re | ||
| from enum import Enum | ||
| import sys | ||
| from inspect import Parameter | ||
| from types import ModuleType | ||
| from typing import Any, Callable, Optional | ||
| from typing import Any, Callable, Generic, Optional, TypeVar, overload | ||
| import tool2schema | ||
| from tool2schema.config import Config | ||
| from tool2schema.config import Config, SchemaType | ||
| from tool2schema.parameter_schema import ParameterSchema | ||
| if sys.version_info < (3, 10): | ||
| from typing_extensions import ParamSpec | ||
| else: | ||
| from typing import ParamSpec | ||
| class SchemaType(Enum): | ||
| """Enum for schema types.""" | ||
| API = 0 | ||
| TUNE = 1 | ||
| def FindGPTEnabled(module: ModuleType) -> list[Callable]: | ||
| def FindToolEnabled(module: ModuleType) -> list[ToolEnabled]: | ||
| """ | ||
| Find all functions with the GPTEnabled decorator. | ||
| Find all functions with the EnableTool decorator. | ||
| :param module: Module to search for GPTEnabled functions | ||
| :param module: Module to search for ToolEnabled functions | ||
| """ | ||
| return [x for x in module.__dict__.values() if hasattr(x, "gpt_enabled")] | ||
| return [x for x in module.__dict__.values() if hasattr(x, "tool_enabled")] | ||
| def FindGPTEnabledSchemas( | ||
| module: ModuleType, schema_type: SchemaType = SchemaType.API | ||
| def FindToolEnabledSchemas( | ||
| module: ModuleType, schema_type: Optional[SchemaType] = None | ||
| ) -> list[dict]: | ||
| """ | ||
| Find all function schemas with the GPTEnabled decorator. | ||
| Find all function schemas with the EnableTool decorator. | ||
| :param module: Module to search for GPTEnabled functions | ||
| :param schema_type: Type of schema to return | ||
| :param module: Module to search for ToolEnabled functions | ||
| :param schema_type: Type of schema to return (None indicates default) | ||
| """ | ||
| return [x.schema.to_json(schema_type) for x in FindGPTEnabled(module)] | ||
| return [x.to_json(schema_type) for x in FindToolEnabled(module)] | ||
| def FindGPTEnabledByName(module: ModuleType, name: str) -> Optional[Callable]: | ||
| def FindToolEnabledByName(module: ModuleType, name: str) -> Optional[ToolEnabled]: | ||
| """ | ||
| Find a function with the GPTEnabled decorator by name. | ||
| Find a function with the EnableTool decorator by name. | ||
| :param module: Module to search for GPTEnabled functions | ||
| :param module: Module to search for ToolEnabled functions | ||
| :param name: Name of the function to find | ||
| """ | ||
| for func in FindGPTEnabled(module): | ||
| for func in FindToolEnabled(module): | ||
| if func.__name__ == name: | ||
@@ -58,21 +58,49 @@ return func | ||
| def FindGPTEnabledByTag(module: ModuleType, tag: str) -> list[Callable]: | ||
| def FindToolEnabledByNameSchema( | ||
| module: ModuleType, name: str, schema_type: Optional[SchemaType] = None | ||
| ) -> Optional[dict]: | ||
| """ | ||
| Find all functions with the GPTEnabled decorator by tag. | ||
| Find a function schema with the EnableTool decorator by name. | ||
| :param module: Module to search for GPTEnabled functions | ||
| :param module: Module to search for ToolEnabled functions | ||
| :param name: Name of the function to find | ||
| :param schema_type: Type of schema to return (None indicates default) | ||
| """ | ||
| if (func := FindToolEnabledByName(module, name)) is None: | ||
| return None | ||
| return func.to_json(schema_type) | ||
| def FindToolEnabledByTag(module: ModuleType, tag: str) -> list[ToolEnabled]: | ||
| """ | ||
| Find all functions with the EnableTool decorator by tag. | ||
| :param module: Module to search for ToolEnabled functions | ||
| :param tag: Tag to search for | ||
| """ | ||
| return [x for x in FindGPTEnabled(module) if x.has(tag)] | ||
| return [x for x in FindToolEnabled(module) if x.has(tag)] | ||
| def SaveGPTEnabled(module: ModuleType, path: str, schema_type: SchemaType = SchemaType.API): | ||
| def FindToolEnabledByTagSchemas( | ||
| module: ModuleType, tag: str, schema_type: Optional[SchemaType] = None | ||
| ) -> list[dict]: | ||
| """ | ||
| Save all function schemas with the GPTEnabled decorator to a file. | ||
| Find all function schemas with the EnableTool decorator by tag. | ||
| :param module: Module to search for GPTEnabled functions | ||
| :param module: Module to search for ToolEnabled functions | ||
| :param tag: Tag to search for | ||
| :param schema_type: Type of schema to return (None indicates default) | ||
| """ | ||
| return [x.to_json(schema_type) for x in FindToolEnabledByTag(module, tag)] | ||
| def SaveToolEnabled(module: ModuleType, path: str, schema_type: Optional[SchemaType] = None): | ||
| """ | ||
| Save all function schemas with the EnableTool decorator to a file. | ||
| :param module: Module to search for ToolEnabled functions | ||
| :param path: Path to save the schemas to | ||
| :param schema_type: Type of schema to return | ||
| :param schema_type: Type of schema to return (None indicates default) | ||
| """ | ||
| schemas = FindGPTEnabledSchemas(module, schema_type) | ||
| schemas = FindToolEnabledSchemas(module, schema_type) | ||
| json.dump(schemas, open(path, "w")) | ||
@@ -83,7 +111,6 @@ | ||
| """Exception for schema parsing errors.""" | ||
| pass | ||
| def LoadGPTEnabled( | ||
| def LoadToolEnabled( | ||
| module: ModuleType, | ||
@@ -96,3 +123,3 @@ function: dict, | ||
| Given a function dictionary containing the name of a function and the arguments to pass to it, | ||
| retrieve the corresponding function among those with the `GPTEnabled` decorator defined in | ||
| retrieve the corresponding function among those with the `EnableTool` decorator defined in | ||
| `module`. When `validate` is true, validate the arguments and raise `ParseException` if the | ||
@@ -110,3 +137,3 @@ arguments are not valid (see more information below). | ||
| :raises ParseException: Thrown when any of the following conditions is met: | ||
| - Function isn't defined in the given module, or is not decorated with `GPTEnabled` | ||
| - Function isn't defined in the given module, or is not decorated with `EnableTool` | ||
| - The arguments are given as string and the string is not valid, meaning it is: | ||
@@ -146,3 +173,3 @@ - Not parsable as JSON, or; | ||
| f = FindGPTEnabledByName(module, name) | ||
| f = FindToolEnabledByName(module, name) | ||
@@ -153,3 +180,3 @@ if not f: | ||
| f"Function with name '{name}' is not defined in given module " | ||
| f"'{module.__name__}' or is missing 'GPTEnabled' decorator" | ||
| f"'{module.__name__}' or is missing 'EnableTool' decorator" | ||
| ) | ||
@@ -163,3 +190,3 @@ | ||
| def _validate_arguments(f: Callable, arguments: dict, ignore_hallucinations: bool) -> dict: | ||
| def _validate_arguments(f: ToolEnabled, arguments: dict, ignore_hallucinations: bool) -> dict: | ||
| """ | ||
@@ -170,3 +197,3 @@ Verify that all required arguments are present, and the arguments are of the expected type. | ||
| :param f: A GPTEnabled-decorated function | ||
| :param f: A EnableTool-decorated function | ||
| :param arguments: Arguments to validate | ||
@@ -199,4 +226,8 @@ :param ignore_hallucinations: Whether to ignore hallucinated arguments or throw an exception | ||
| class _GPTEnabled: | ||
| def __init__(self, func, **kwargs) -> None: | ||
| P = ParamSpec("P") # User-provided function parameters type | ||
| T = TypeVar("T") # User-provided function return type | ||
| class ToolEnabled(Generic[P, T]): | ||
| def __init__(self, func: Callable[P, T], **kwargs) -> None: | ||
| self.func = func | ||
@@ -208,3 +239,3 @@ self.tags = kwargs.pop("tags", []) | ||
| def __call__(self, *args, **kwargs): | ||
| def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: | ||
@@ -226,5 +257,14 @@ args = list(args) # Tuple is immutable, thus convert to list | ||
| def gpt_enabled(self) -> bool: | ||
| def tool_enabled(self) -> bool: | ||
| return True | ||
| def to_json(self, schema_type: Optional[SchemaType] = None) -> dict: | ||
| """ | ||
| Return JSON schema for the function. | ||
| :param schema_type: None indicates default schema type | ||
| :return: JSON schema | ||
| """ | ||
| return self.schema.to_json(schema_type) | ||
| def has(self, tag: str) -> bool: | ||
@@ -234,10 +274,18 @@ return tag in self.tags | ||
| def GPTEnabled(func=None, **kwargs): | ||
| @overload | ||
| def EnableTool(func: Callable[P, T], **kwargs) -> ToolEnabled[P, T]: ... | ||
| @overload | ||
| def EnableTool(**kwargs) -> Callable[[Callable[P, T]], ToolEnabled[P, T]]: ... | ||
| def EnableTool(func=None, **kwargs): | ||
| """Decorator to generate a function schema for OpenAI.""" | ||
| if func: | ||
| return _GPTEnabled(func, **kwargs) | ||
| if func is not None: | ||
| return ToolEnabled(func, **kwargs) | ||
| else: | ||
| def wrapper(function): | ||
| return _GPTEnabled(function, **kwargs) | ||
| def wrapper(function: Callable[P, T]) -> ToolEnabled[P, T]: | ||
| return ToolEnabled(function, **kwargs) | ||
@@ -261,3 +309,3 @@ return wrapper | ||
| def to_json(self, schema_type: SchemaType = SchemaType.API) -> dict: | ||
| def to_json(self, schema_type: Optional[SchemaType] = None) -> dict: | ||
| """ | ||
@@ -267,8 +315,11 @@ Convert schema to JSON. | ||
| """ | ||
| if schema_type == SchemaType.TUNE: | ||
| schema_type = schema_type or self.config.schema_type | ||
| if schema_type == SchemaType.OPENAI_TUNE: | ||
| return self._get_function_schema(schema_type) | ||
| elif schema_type == SchemaType.ANTHROPIC_CLAUDE: | ||
| return self._get_function_schema(schema_type) | ||
| return self._get_schema() | ||
| def add_enum(self, n: str, enum: list) -> "FunctionSchema": | ||
| def add_enum(self, n: str, enum: list) -> FunctionSchema: | ||
| """ | ||
@@ -279,2 +330,3 @@ Add enum property to a particular function parameter. | ||
| :param enum: The list of values for the enum parameter | ||
| :return: This function schema | ||
| """ | ||
@@ -289,3 +341,3 @@ self._all_parameter_schemas[n].add_enum(enum) | ||
| # This dictionary is only used with the API schema type | ||
| return {"type": "function", "function": self._get_function_schema(SchemaType.API)} | ||
| return {"type": "function", "function": self._get_function_schema(SchemaType.OPENAI_API)} | ||
@@ -296,7 +348,13 @@ def _get_function_schema(self, schema_type: SchemaType) -> dict: | ||
| """ | ||
| schema = {"name": self.f.__name__} | ||
| schema: dict[str, Any] = {"name": self.f.__name__} | ||
| if self.parameter_schemas or schema_type == SchemaType.TUNE: | ||
| need_empty_param = schema_type in [ | ||
| SchemaType.OPENAI_TUNE, | ||
| SchemaType.ANTHROPIC_CLAUDE] | ||
| if self.parameter_schemas or need_empty_param: | ||
| # If the schema type is tune, add the dictionary even if there are no parameters | ||
| schema["parameters"] = self._get_parameters_schema() | ||
| if schema_type == SchemaType.ANTHROPIC_CLAUDE: | ||
| schema["input_schema"] = self._get_parameters_schema() | ||
| else: | ||
| schema["parameters"] = self._get_parameters_schema() | ||
@@ -303,0 +361,0 @@ if (description := self._get_description()) is not None: |
@@ -13,3 +13,3 @@ from __future__ import annotations | ||
| def GPTTypeSchema(cls: Type[TypeSchema]): | ||
| def ToolTypeSchema(cls: Type[TypeSchema]): | ||
| """ | ||
@@ -124,3 +124,3 @@ Decorator to register a type schema class. | ||
| @GPTTypeSchema | ||
| @ToolTypeSchema | ||
| class ValueTypeSchema(TypeSchema): | ||
@@ -144,2 +144,5 @@ """ | ||
| def validate(self, value) -> bool: | ||
| # Allow implicit conversion from int to float | ||
| if self.type is float and type(value) is int: | ||
| return True | ||
| return self.type == type(value) | ||
@@ -172,3 +175,3 @@ | ||
| @GPTTypeSchema | ||
| @ToolTypeSchema | ||
| class ListTypeSchema(GenericTypeSchema): | ||
@@ -214,3 +217,3 @@ """ | ||
| @GPTTypeSchema | ||
| @ToolTypeSchema | ||
| class UnionTypeSchema(GenericTypeSchema): | ||
@@ -274,3 +277,3 @@ """ | ||
| @GPTTypeSchema | ||
| @ToolTypeSchema | ||
| class EnumClassTypeSchema(EnumTypeSchema): | ||
@@ -310,3 +313,3 @@ """ | ||
| @GPTTypeSchema | ||
| @ToolTypeSchema | ||
| class LiteralTypeSchema(EnumTypeSchema): | ||
@@ -313,0 +316,0 @@ """ |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
63678
14.22%763
9.31%