Decorator to generate schemas for tools #120

Closed
opened 2026-02-15 16:28:06 -05:00 by yindo · 10 comments
Owner

Originally created by @animaldomestico on GitHub (Jul 26, 2024).

I would like to know if there is any intention to add some sort of basic decorator to automatically generate schemas for functions we provide to the model. Examples? Maybe something like langchain has.

As you see there in the doc we can easily generate the description in JSON with that feature:

@tool(parse_docstring=True)
def foo(bar: str, baz: int) -> str:
    """The foo.

    Args:
        bar: The bar.
        baz: The baz.
    """
    return bar

foo.args_schema.schema()

{
    "title": "fooSchema",
    "description": "The foo.",
    "type": "object",
    "properties": {
        "bar": {
            "title": "Bar",
            "description": "The bar.",
            "type": "string"
        },
        "baz": {
            "title": "Baz",
            "description": "The baz.",
            "type": "integer"
        }
    },
    "required": [
        "bar",
        "baz"
    ]
}

I think this is a basic feature to ask. It is very practical and useful, think about that.

Thank you.

Originally created by @animaldomestico on GitHub (Jul 26, 2024). I would like to know if there is any intention to add some sort of basic decorator to automatically generate schemas for functions we provide to the model. Examples? Maybe something like [langchain has](https://api.python.langchain.com/en/latest/tools/langchain_core.tools.tool.html). As you see there in the doc we can easily generate the description in JSON with that feature: ```python @tool(parse_docstring=True) def foo(bar: str, baz: int) -> str: """The foo. Args: bar: The bar. baz: The baz. """ return bar ``` foo.args_schema.schema() ```json { "title": "fooSchema", "description": "The foo.", "type": "object", "properties": { "bar": { "title": "Bar", "description": "The bar.", "type": "string" }, "baz": { "title": "Baz", "description": "The baz.", "type": "integer" } }, "required": [ "bar", "baz" ] } ``` I think this is a basic feature to ask. It is very practical and useful, think about that. Thank you.
yindo closed this issue 2026-02-15 16:28:06 -05:00
Author
Owner

@antoninoLorenzo commented on GitHub (Jul 26, 2024):

Man before you opened that issue I didn't knew I could use tools 💀

Maybe I am the one skipping docs, however I would like to know where/how did you find out that.

Having an actual documentation would be really good 😭

@antoninoLorenzo commented on GitHub (Jul 26, 2024): Man before you opened that issue I didn't knew I could use tools 💀 Maybe I am the one skipping docs, however I would like to know where/how did you find out that. Having an actual documentation would be really good 😭
Author
Owner

@animaldomestico commented on GitHub (Jul 26, 2024):

Maybe I am the one skipping docs, however I would like to know where/how did you find out that.

Yesterday ollama maintainers announced a new release that provided support for tools. They gave one example available in this repository. That is why I came here to check if there are some new features related to it. The ollama release was the reason I asked for auto generated schemas for tools, so I don't have to manually write things like:

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_flight_times",
            "description": "Get the flight times between two cities",
            "parameters": {
                "type": "object",
                "properties": {
                    "departure": {
                        "type": "string",
                        "description": "The departure city (airport code)",
                    },
                    "arrival": {
                        "type": "string",
                        "description": "The arrival city (airport code)",
                    },
                },
                "required": ["departure", "arrival"],
            },
        },
    },
]

With the feature I asked you would just:

@tool 
def get_flight_times(departure: str, arrival: str) -> str:
"""
write basic docstring here
...
"""

And you are good to go...

@animaldomestico commented on GitHub (Jul 26, 2024): > Maybe I am the one skipping docs, however I would like to know where/how did you find out that. Yesterday ollama maintainers announced a [new release](https://github.com/ollama/ollama/releases/tag/v0.3.0) that provided support for tools. They gave one example [available in this repository](https://github.com/ollama/ollama-python/blob/main/examples/tools/main.py). That is why I came here to check if there are some new features related to it. The ollama release was the reason I asked for auto generated schemas for tools, so I don't have to manually write things like: ```python tools = [ { "type": "function", "function": { "name": "get_flight_times", "description": "Get the flight times between two cities", "parameters": { "type": "object", "properties": { "departure": { "type": "string", "description": "The departure city (airport code)", }, "arrival": { "type": "string", "description": "The arrival city (airport code)", }, }, "required": ["departure", "arrival"], }, }, }, ] ``` With the feature I asked you would just: ```python @tool def get_flight_times(departure: str, arrival: str) -> str: """ write basic docstring here ... """ ``` And you are good to go...
Author
Owner

@antoninoLorenzo commented on GitHub (Jul 26, 2024):

Ok, however I wrote some code to see how it could be done, there are still some edge cases to handle and I wrote it kind of in a rush, here it is:

import json
import inspect


def foo(bar: int, par: int = 2):
    """
    Computes par * bar
    Basic stuff
    :param bar: bar description
    :param par: par description
    """
    return par * bar


# --- Get Parameters Information
sig = inspect.signature(foo)

required = []
properties = {}
return_type = sig.return_annotation.__name__
for name, param in sig.parameters.items():
    properties[name] = {'type': param.annotation.__name__}

    if param.default == inspect.Parameter.empty:
        required.append(name)

# get parameter description
param_lines = [line.strip() for line in foo.__doc__.split('\n') if line.strip().startswith(':param')]
param_lines = [line[line[1:].find(':')+2:].strip() for line in param_lines]
for prop, desc in zip(properties.keys(), param_lines):
    properties[prop]['description'] = desc

# get docs
doc_lines = [line.strip() for line in foo.__doc__.split('\n') if not line.strip().startswith(':param')]

# --- Create docs
tool_doc = {
    'type': type(foo).__name__,
    'function': {
        'name': foo.__name__,
        'description': '\n'.join(doc_lines),
        'parameters': {
            'type': return_type,
            'properties': properties,
            'required': required
        }
    }
}

json_str = json.dumps(tool_doc, indent=4)
print(json_str)


Output:

{
    "type": "function",
    "function": {
        "name": "foo",
        "description": "\nComputes par * bar\nBasic stuff\n",
        "parameters": {
            "type": "_empty",
            "properties": {
                "bar": {
                    "type": "int",
                    "description": "bar description"
                },
                "par": {
                    "type": "int",
                    "description": "par description"
                }
            },
            "required": [
                "bar"
            ]
        }
    }
}

For some reason type is _empty

@antoninoLorenzo commented on GitHub (Jul 26, 2024): Ok, however I wrote some code to see how it could be done, there are still some edge cases to handle and I wrote it kind of in a rush, here it is: ```python import json import inspect def foo(bar: int, par: int = 2): """ Computes par * bar Basic stuff :param bar: bar description :param par: par description """ return par * bar # --- Get Parameters Information sig = inspect.signature(foo) required = [] properties = {} return_type = sig.return_annotation.__name__ for name, param in sig.parameters.items(): properties[name] = {'type': param.annotation.__name__} if param.default == inspect.Parameter.empty: required.append(name) # get parameter description param_lines = [line.strip() for line in foo.__doc__.split('\n') if line.strip().startswith(':param')] param_lines = [line[line[1:].find(':')+2:].strip() for line in param_lines] for prop, desc in zip(properties.keys(), param_lines): properties[prop]['description'] = desc # get docs doc_lines = [line.strip() for line in foo.__doc__.split('\n') if not line.strip().startswith(':param')] # --- Create docs tool_doc = { 'type': type(foo).__name__, 'function': { 'name': foo.__name__, 'description': '\n'.join(doc_lines), 'parameters': { 'type': return_type, 'properties': properties, 'required': required } } } json_str = json.dumps(tool_doc, indent=4) print(json_str) ``` Output: ```json { "type": "function", "function": { "name": "foo", "description": "\nComputes par * bar\nBasic stuff\n", "parameters": { "type": "_empty", "properties": { "bar": { "type": "int", "description": "bar description" }, "par": { "type": "int", "description": "par description" } }, "required": [ "bar" ] } } } ``` > For some reason type is `_empty`
Author
Owner

@animaldomestico commented on GitHub (Jul 26, 2024):

For some reason type is _empty

In open webui tools.py file they just write "object":

"description": function_doc.get("description", function_name),
                "parameters": {
                    "type": "object",
                    "properties": {

Maybe it is for OpenAI API compatibility.

@animaldomestico commented on GitHub (Jul 26, 2024): > For some reason type is _empty In [open webui tools.py file](https://github.com/open-webui/open-webui/blob/main/backend/utils/tools.py#L40) they just write "object": ```python "description": function_doc.get("description", function_name), "parameters": { "type": "object", "properties": { ``` Maybe it is for [OpenAI API compatibility](https://platform.openai.com/docs/guides/function-calling).
Author
Owner

@antoninoLorenzo commented on GitHub (Jul 26, 2024):

In open webui tools.py file they just write "object":

I misinterpreted its meaning.

As shown it is simple to implement, I would enjoy working on it myself, however there are no contribution guidelines; so I think that's on the maintainers now.

@antoninoLorenzo commented on GitHub (Jul 26, 2024): > In [open webui tools.py file](https://github.com/open-webui/open-webui/blob/main/backend/utils/tools.py#L40) they just write "object": I misinterpreted its meaning. As shown it is simple to implement, I would enjoy working on it myself, however there are no contribution guidelines; so I think that's on the maintainers now.
Author
Owner

@animaldomestico commented on GitHub (Jul 26, 2024):

In open webui tools.py file they just write "object":

I misinterpreted its meaning.

As shown it is simple to implement, I would enjoy working on it myself, however there are no contribution guidelines; so I think that's on the maintainers now.

Yes, this is very simple to implement, and there are some implementations out there that the maintainer can look into if he has any doubts, like also lanchain implementation.

Lets see what those maintainers say about that feature, if it is good or not to include something like this in this library. For me, as they are already integrating some "tools" support in this library, would be good to step ahead and add something useful like that. In my opinion download a whole framework for something so basic like that (langchain) is not interesting.

@animaldomestico commented on GitHub (Jul 26, 2024): > > In [open webui tools.py file](https://github.com/open-webui/open-webui/blob/main/backend/utils/tools.py#L40) they just write "object": > > I misinterpreted its meaning. > > As shown it is simple to implement, I would enjoy working on it myself, however there are no contribution guidelines; so I think that's on the maintainers now. Yes, this is very simple to implement, and there are some implementations out there that the maintainer can look into if he has any doubts, like also [lanchain implementation](https://api.python.langchain.com/en/latest/_modules/langchain_core/tools.html#tool). Lets see what those maintainers say about that feature, if it is good or not to include something like this in this library. For me, as they are already integrating some "tools" support in this library, would be good to step ahead and add something useful like that. In my opinion download a whole framework for something so basic like that (langchain) is not interesting.
Author
Owner

@antoninoLorenzo commented on GitHub (Jul 26, 2024):

In my opinion download a whole framework for something so basic like that (langchain) is not interesting.

I agree, I rarely use langchain and I do not feel comfortable using It; however It objectively provides some nice to have features (under and absurd/buggy level of abstractions)

@antoninoLorenzo commented on GitHub (Jul 26, 2024): > In my opinion download a whole framework for something so basic like that (langchain) is not interesting. I agree, I rarely use langchain and I do not feel comfortable using It; however It objectively provides some nice to have features (under and absurd/buggy level of abstractions)
Author
Owner

@animaldomestico commented on GitHub (Jul 27, 2024):

Seems like someone was also interested in something related to it and provided a PR here. But one user thinks that the best approach is a third-party library.

I think I will close this issue. Thanks for your contributions @antoninoLorenzo, @synacktraa.

@animaldomestico commented on GitHub (Jul 27, 2024): Seems like someone was also interested in something related to it and provided a [PR here](https://github.com/ollama/ollama-python/pull/234#issue-2432302103). But one user [thinks](https://github.com/ollama/ollama-python/pull/234#issuecomment-2253048664) that the best approach is a third-party library. I think I will close this issue. Thanks for your contributions @antoninoLorenzo, @synacktraa.
Author
Owner

@synacktraa commented on GitHub (Jul 28, 2024):

Seems like someone was also interested in something related to it and provided a PR here. But one user thinks that the best approach is a third-party library.

I think I will close this issue. Thanks for your contributions @antoninoLorenzo, @synacktraa.

I've nearly completed converting the PR into a third-party library. The library will support the registration of functions—both synchronous and asynchronous—as well as pydantic models, TypedDicts, namedtuples, and dataclasses as tools. Additionally, there will be an infer method that leverages litellm to interact with LLM APIs. A key advantage is that pydantic and litellm won't be bundled with the package, keeping it lightweight. Users can choose to install those dependencies separately if they need those features.

@synacktraa commented on GitHub (Jul 28, 2024): > Seems like someone was also interested in something related to it and provided a [PR here](https://github.com/ollama/ollama-python/pull/234#issue-2432302103). But one user [thinks](https://github.com/ollama/ollama-python/pull/234#issuecomment-2253048664) that the best approach is a third-party library. > > > > I think I will close this issue. Thanks for your contributions @antoninoLorenzo, @synacktraa. I've nearly completed converting the PR into a third-party library. The library will support the registration of functions—both synchronous and asynchronous—as well as pydantic models, TypedDicts, namedtuples, and dataclasses as tools. Additionally, there will be an `infer` method that leverages litellm to interact with LLM APIs. A key advantage is that pydantic and litellm won't be bundled with the package, keeping it lightweight. Users can choose to install those dependencies separately if they need those features.
Author
Owner

@synacktraa commented on GitHub (Jul 28, 2024):

In open webui tools.py file they just write "object":

I misinterpreted its meaning.

As shown it is simple to implement, I would enjoy working on it myself, however there are no contribution guidelines; so I think that's on the maintainers now.

Yes, this is very simple to implement, and there are some implementations out there that the maintainer can look into if he has any doubts, like also lanchain implementation.

Lets see what those maintainers say about that feature, if it is good or not to include something like this in this library. For me, as they are already integrating some "tools" support in this library, would be good to step ahead and add something useful like that. In my opinion download a whole framework for something so basic like that (langchain) is not interesting.

That's correct; many people find this feature unnecessary because frameworks like Langchain already exist. However, the issue is that if someone only wants to use the tool-calling feature, these frameworks are too heavy for such a simple task.

@synacktraa commented on GitHub (Jul 28, 2024): > > > In [open webui tools.py file](https://github.com/open-webui/open-webui/blob/main/backend/utils/tools.py#L40) they just write "object": > > > > > > I misinterpreted its meaning. > > > > > > As shown it is simple to implement, I would enjoy working on it myself, however there are no contribution guidelines; so I think that's on the maintainers now. > > > > Yes, this is very simple to implement, and there are some implementations out there that the maintainer can look into if he has any doubts, like also [lanchain implementation](https://api.python.langchain.com/en/latest/_modules/langchain_core/tools.html#tool). > > > > Lets see what those maintainers say about that feature, if it is good or not to include something like this in this library. For me, as they are already integrating some "tools" support in this library, would be good to step ahead and add something useful like that. In my opinion download a whole framework for something so basic like that (langchain) is not interesting. That's correct; many people find this feature unnecessary because frameworks like Langchain already exist. However, the issue is that if someone only wants to use the tool-calling feature, these frameworks are too heavy for such a simple task.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ollama/ollama-python#120