Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function calling vllm #8

Merged
merged 32 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8b60241
add function calling with example
maxDavid40 Jul 15, 2024
b1932fc
FIX : change request before router via FastApi.Depends
maxDavid40 Jul 16, 2024
ec522e0
FIX : top_logprobs == 0 equivalent to None else bug vllm
maxDavid40 Jul 16, 2024
3efd44c
clean codes
maxDavid40 Jul 18, 2024
ad8cb80
docstring and clean code
maxDavid40 Jul 18, 2024
dde9521
add getter function + Unit tests
maxDavid40 Jul 18, 2024
400d612
finish Unit tests
maxDavid40 Jul 18, 2024
4139701
clean code
maxDavid40 Jul 18, 2024
80f8260
fix conftest.py
maxDavid40 Jul 18, 2024
22ec060
fix conftest.py
maxDavid40 Jul 18, 2024
ed9f7ef
Fix : import TestClient from FastApi useless
maxDavid40 Jul 23, 2024
7ac3801
test on functionnal function depends
maxDavid40 Jul 26, 2024
4da8fc5
CODE : migrate all code about tools in example directory
maxDavid40 Jul 26, 2024
595dfc8
CODE : remove from happy_vllm/src all code about tools
maxDavid40 Jul 26, 2024
96f5e5e
update README.md
maxDavid40 Jul 26, 2024
241024f
get main last update
maxDavid40 Jul 26, 2024
60dbdb3
FIX : miundo lete routers.schemas.functiona
maxDavid40 Jul 26, 2024
1a41188
FIX : move test_schema_functional in example/function_tools
maxDavid40 Jul 26, 2024
b22fdda
Update README.md for typo
maxDavid40 Jul 29, 2024
31a492a
Fix : import package useless in some test files
maxDavid40 Jul 29, 2024
c4ae437
Reorganizing function tools python files
maxDavid40 Jul 29, 2024
f58668b
Fix code with new reorganization
maxDavid40 Jul 29, 2024
bd24e5f
Fix code about PR reviews
maxDavid40 Jul 29, 2024
0992305
update readme.md
maxDavid40 Jul 29, 2024
8572716
Fix file mistake and add docstring
maxDavid40 Jul 29, 2024
a300ca7
create folder for test files
maxDavid40 Jul 29, 2024
919478a
create folder for test files
maxDavid40 Jul 29, 2024
68d19ce
FIX: README.md typo
maxDavid40 Jul 29, 2024
585ae1e
FIX : remove global variables and centralize all in routers.shcema.fu…
maxDavid40 Jul 29, 2024
68b35b8
FIX : README.md typo
maxDavid40 Jul 29, 2024
bb575c3
README Typo
maxDavid40 Jul 29, 2024
dbb0c2e
FIX : mistake code data_dict = data.dict()
maxDavid40 Jul 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions examples/function_tools/README.md
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The readme should indicate where the subclass created should be put in order to be able to use it (in TOOLS and TOOLS_DICT)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid repeatedly specifying all attributes related to tools and tool_choice when using functions calling, you can create a classe inheriting from functions.ToolFunctions.

You need to instantiate 4 attributes:

  • description (string)
  • parameters (dict)
  • name (string)
  • tool_type (string)

Each attributes correpondings to Openai api

After the class is created, you have to declare in TOOLS_DICT and TOOLS global variables in functions_tools.function_tools_util.py (add weather function tool for example).

TOOLS_DICT = {
    'weather': Weather(),
}
TOOLS = ['weather']

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The readme should also indicate what one should do with functional.py ? Replace the original one (I prefer this option), copy/paste the relevant part ....
In any case, it should also indicate what is different between the two file (if I am not mistaken, it is only the new route)

Copy link
Collaborator Author

@maxDavid40 maxDavid40 Jul 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"To use this implementation, you must replace routers.function.py and routers.schema.function.py with the respective files in the folder example.function_tools."

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Functions calling

## Deploy a new function
To avoid repeatedly specifying all attributes related to tools and tool_choice when functions calling, you can create a classe inherit ```functions.ToolFunctions```.
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved

You need to instantiate 4 attributes:
- description (string)
- parameters (dict)
- name (string)
- tool_type (string)

Each attributes correpondings as [Openai api](https://platform.openai.com/docs/api-reference/chat/create#chat-create-tools)
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved

## Called functions
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we specify that this endpoints has to be called, only if happy_vllm server is deployed with pre defined tools?

After deploy you REST API, you can call it with the following rutes ```/v1/chat/completions_tools```
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved

## To know

With vllm 0.5.0 to 0.5.3.post1, tool_choice's option ```auto``` and ```required``` are not yet implemented. You can only use one function by deployement :
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it one function for each deployement or one function for each call ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for each deployement


```
{
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
"type": "function",
"function": {"name": "get_current_weather"}
}
```

Empty file.
135 changes: 135 additions & 0 deletions examples/function_tools/functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import logging
from copy import copy
from typing import Union
from argparse import Namespace


class ToolFunctions:
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
"""
Represents a tool function with specific attributes.

Attributes:
description (str): Description of the tool function.
parameters (dict): Parameters required for the tool function.
name (str): Name of the tool function.
tool_type (str): Type of the tool function.

Methods:
__init__(description: Union[str, None], parameters: Union[dict, None], name: Union[str, None], tool_type: Union[str, None]):
Initializes a ToolFunctions instance with the provided attributes. Raises NotImplementedError if any attribute is None.

_check_attributes():
Checks if the required attributes (description, parameters, name, tool_type) are not None.
Raises NotImplementedError if any attribute is None.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be a value error. In my opinion, the NotImplementedError is only used for missing methods, not missing attributes. I may be mistaken

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading python doc, should be AttributeError.
To use NotImplementdError, we have to be OOP with _get / _set methods 😒


generate_dict() -> dict:
Generates and returns a dictionary representation of the tool function, including its type, name, description, and parameters.
"""
def __init__(self, description:Union[str, None], parameters:Union[dict, None], name:Union[str, None], tool_type:Union[str, None]):
self.description:str = description
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
self.parameters:dict = parameters
self.name:str = name
self.tool_type:str = tool_type
self._check_attributes()

def _check_attributes(self):
if not self.description:
raise NotImplementedError("This attributes must be different to None")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValueError ? (see comment above)

if not self.parameters:
raise NotImplementedError("This attributes must be different to None")
if not self.name:
raise NotImplementedError("This attributes must be different to None")
if not self.tool_type:
raise NotImplementedError("This attributes must be different to None")

def generate_dict(self):
return {
"type": self.tool_type,
"function": {
"name": self.name,
"description": self.description,
"parameters": self.parameters,

}
}


class Weather(ToolFunctions):
"""
Represents a example tool function about the weather.
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
"""
def __init__(self):
tool_type = "function"
name = "get_current_weather"
description = "Get current weather"
parameters = {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the users location.",
},
},
"required": ["location", "format"]
}
super().__init__(description=description, parameters=parameters, name=name, tool_type=tool_type)


TOOLS_DICT = {
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
'weather': Weather(),
}
TOOLS = ['weather']


def get_tools():
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
return TOOLS_DICT, TOOLS


def reset_tools_dict_and_tools():
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
"""
Resets the global variables TOOLS_DICT and TOOLS with new default values.

Returns:
tuple: A tuple containing the updated values of TOOLS_DICT and TOOLS.
TOOLS_DICT is a dictionary with keys for different tools and corresponding values,
and TOOLS is an empty list.
"""
global TOOLS_DICT
global TOOLS
TOOLS_DICT = {
'weather': Weather()
}
TOOLS = ['weather']


def get_tools_prompt() -> dict:
maxDavid40 marked this conversation as resolved.
Show resolved Hide resolved
"""
Returns a dictionary containing information about selected tools.

Returns:
dict or None: A dictionary containing information about selected tools, structured as follows:
- "tools": A list of dictionaries, each representing a tool's generated dictionary.
- "tool_choice": A dictionary containing type and function details of the first tool in the list,
or None if TOOLS is empty.
Returns None if TOOLS is empty.
"""
tools_dict = copy(TOOLS_DICT)
tools = copy(TOOLS)
if tools:
return {
"tools": [tools_dict[t].generate_dict() for t in tools],
"tool_choice": [
{
"type": tools_dict[t].tool_type,
"function": {"name":tools_dict[t].name}
}
for t in tools
][0]
}
else:
return None
Loading
Loading