I am wondering whether to use functions or tools in the ChatCompletions API with the newest gpt-3.5-turbo-1106 to do function calls as they all seem to have the same functionality. Then I checked the OpenAI cookbook and they use the tools parameter to insert the functions instead of the functions parameter. What is the difference and do I need to format the functions differently?
+1
Iâm seeing discrepancies abound:
Their python SDK cookbook was changed 9 hours ago from "role": "tool"
to "role": "function"
However their node readme.md code (which weâre using) leverages "role": "tool"
Meanwhile their docs also use "role: "tool",
so I guess the cookbook update is throwing me off:
Finally their completions.ts ChatCompletionToolMessageParam lacks a ânameâ attribute, which is present in the examples in their node repo
Wooo innovation
I just found the answer here: https://platform.openai.com/docs/api-reference/chat/create
functions
have been deprecated, and tools
are the way forward.
Tools:
A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.
In the response object there is also a new finish_reason âtool_callsâ, which also mentions the deprecated function_call.
It seems that most cookbooks have been updated, but the guides havenât yet.
Thank you so much! Itâs kind of a random update as it doesnât seem like they behave differently.
Youâre welcome! My guess is that theyâre preparing the way for OpenAI hosted âtoolsâ, such as GPTs or Assistants, that your agent can then call. Creating a multi-agent system basically.
I agree with @dane.jordan.
I ran into the same issue with the name field. I had to remove the name field for Typescript to work.
I think the confusion on OpenAIâs side is that they are defining ChatCompletionMessageParam as a Discrimating Union type, but they are consuming it as if it is an Intersection type.
Discrimating Union as OpenAI defines:
export type ChatCompletionMessageParam =
| ChatCompletionSystemMessageParam
| ChatCompletionUserMessageParam
| ChatCompletionAssistantMessageParam
| ChatCompletionToolMessageParam
| ChatCompletionFunctionMessageParam;
They are using it as if it was defined like this:
export type ChatCompletionMessageParam =
ChatCompletionSystemMessageParam
& ChatCompletionUserMessageParam
& ChatCompletionAssistantMessageParam
& ChatCompletionToolMessageParam
& ChatCompletionFunctionMessageParam;
Hi, has any of you good folks figured out a way to stream the response while using Function Calls? Iâm able to stream without function calls and display the entire result at once (without streaming) while using function calls. I read on another post that one can get streaming to work by dumping the function response as an assistant message into messages. In that case, are you running it through a loop? If anyone has got this to work, please do share your script. Thanks in advance!
Certainly. Do you expect ânobody knows how to make it workâ?
Here is an example request that takes each stream chunk as they are received, and displays any content and adds any tool_call parts to a list:
c = client.chat.completions.with_raw_response.create(**params)
reply=""
tools=[]
for chunk in c.parse():
# This gets content, which is clear text meant for a user
if chunk.choices[0].delta.content:
reply += chunk.choices[0].delta.content # gather for chat history
print(chunk.choices[0].delta.content, end="") # your output method
# This gets whole tool chunk objects, that need later assembly
if chunk.choices[0].delta.tool_calls:
tools += chunk.choices[0].delta.tool_calls # gather ChoiceDeltaToolCall list chunks
What we have gathered delta parts in the âtoolsâ list when they had that element in the json of the chunk.
At this point, weâve now got a python list list of objects, (the libraryâs ChoiceDeltaToolCall
). They look like this if we dump to dictionary:
for tool in tools:
print(tool.model_dump())
collected from the streamed chunks:
{'index': 0, 'id': 'call_P0WmAn1FTpLTlJtz18a53z', 'function': {'arguments': '', 'name': 'get_random_float'}, 'type': 'function'}
{'index': 0, 'id': None, 'function': {'arguments': '{"', 'name': None}, 'type': None}
{'index': 0, 'id': None, 'function': {'arguments': 'range', 'name': None}, 'type': None}
{'index': 0, 'id': None, 'function': {'arguments': '_start', 'name': None}, 'type': None}
{'index': 0, 'id': None, 'function': {'arguments': '":', 'name': None}, 'type': None}
...
You can see then that we need to do a bit of parsing to put these back in the same type of standalone tool call object weâd get with non-streaming. Letâs use a function:
tools_obj = tool_list_to_tool_obj(tools)
Which I have written out beforeâŚand you can avoid the search by clicking.