How to properly type hint function calls: Function vs FunctionDefinition

tl;dr
How to properly type hint function calls?

The problem
There is a plethora of different classes that can be found under openai.types and it’s not always clear how they relate to one another or what the differences are. More specifically, I would like to properly type hint my function calls. I can find the following relevant types:

Both of these have the exact same attributes: name, description, and parameters. But it is not clear to me when to use one over the other.

An example
The following function is correctly defined. Pyright (my type checker of choice) sees no problem with this, and I can pass it as an argument to the chat completions API without issues.

from openai.types.chat.completion_create_params import Function

function_definition_extract_number: Function = {
    "name": "extract_number",
    "description": "Extract the first number from the user message.",
    "parameters": {
        "type": "object",
        "properties": {
            "first_number": {"type": "array", "description": "The first number that appears in the user input."},
        },
        "required": ["first_number"],
    },
}

However, when I change the type hint to FunctionDefinition, my type checker complains about incorrect types:

from openai.types.shared.function_definition import FunctionDefinition

function_definition_extract_number: FunctionDefinition = {
...
}  # <-- type checker complains

On the other hand, when I want to use a FunctionTool – necessary to do function calling with the assistant API – The type checker tells me that I cannot use a Function where I need a FunctionDefinition

Any clarification on how/when to use these is appreciated!

I found the solution: apparently there are even more related type hints, one of which is also called FunctionDefinition. Note that even though this thing is called literally the same thing, it is actually a different class than the one I used in my example above. (One is imported from openai.types.shared, the other from openai.types.shared_params.)

This works:

from openai.types.shared_params.function_definition import FunctionDefinition

function_definition_extract_number: FunctionDefinition = {
...
}

This thing is accepted by the assistant API:

  1. the tools argument in openai.beta.assistants.create accepts Iterable[AssistantToolParam]
  2. A FunctionToolParam is a valid AssistantToolParam
  3. We can create a FunctionToolParam by combining a FunctionDefinition with the literal string "function" in a python dict. Note: this only works with FunctionDefinition imported from openai.types.shared_params, not the one imported from openai.shared.params.
  4. We can create a FunctionDefinition in the usual way, i.e. a python dict with three keys: name, description, and parameters – see above for an example.

Miraculously it is also accepted by the chat completions API, even though the documentation does not show that!!!

  1. The functions arguments of client.chat.completions.create accepts an object of type Iterable[completion_create_params.Function].
  2. Somehow, miraculously, it also accepts Iterable[FunctionDefinition]. (Or at least Pyright doesn’t complain about it.)

So yeah… It’s a mess, which leads to pretty interesting type checking errors :stuck_out_tongue: :

Any simplifications on this front would be appreciated!