-
-
Notifications
You must be signed in to change notification settings - Fork 350
/
.cursorrules
1885 lines (1520 loc) · 75.7 KB
/
.cursorrules
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
openai docs
pip install openai
Usage
The full API of this library can be found in api.md.
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
)
chat_completion = client.chat.completions.create(
messages=[
{
"role": "user",
"content": "Say this is a test",
}
],
model="gpt-4o",
)
While you can provide an api_key keyword argument, we recommend using python-dotenv to add OPENAI_API_KEY="My API Key" to your .env file so that your API Key is not stored in source control.
Vision
With a hosted image:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{
"type": "image_url",
"image_url": {"url": f"{img_url}"},
},
],
}
],
)
With the image as a base64 encoded string:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": prompt},
{
"type": "image_url",
"image_url": {"url": f"data:{img_type};base64,{img_b64_str}"},
},
],
}
],
)
Polling Helpers
When interacting with the API some actions such as starting a Run and adding files to vector stores are asynchronous and take time to complete. The SDK includes helper functions which will poll the status until it reaches a terminal state and then return the resulting object. If an API method results in an action that could benefit from polling there will be a corresponding version of the method ending in '_and_poll'.
For instance to create a Run and poll until it reaches a terminal state you can run:
run = client.beta.threads.runs.create_and_poll(
thread_id=thread.id,
assistant_id=assistant.id,
)
More information on the lifecycle of a Run can be found in the Run Lifecycle Documentation
Bulk Upload Helpers
When creating and interacting with vector stores, you can use polling helpers to monitor the status of operations. For convenience, we also provide a bulk upload helper to allow you to simultaneously upload several files at once.
sample_files = [Path("sample-paper.pdf"), ...]
batch = await client.vector_stores.file_batches.upload_and_poll(
store.id,
files=sample_files,
)
Streaming Helpers
The SDK also includes helpers to process streams and handle incoming events.
with client.beta.threads.runs.stream(
thread_id=thread.id,
assistant_id=assistant.id,
instructions="Please address the user as Jane Doe. The user has a premium account.",
) as stream:
for event in stream:
# Print the text from text delta events
if event.type == "thread.message.delta" and event.data.delta.content:
print(event.data.delta.content[0].text)
More information on streaming helpers can be found in the dedicated documentation: helpers.md
Async usage
Simply import AsyncOpenAI instead of OpenAI and use await with each API call:
import os
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI(
api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
)
async def main() -> None:
chat_completion = await client.chat.completions.create(
messages=[
{
"role": "user",
"content": "Say this is a test",
}
],
model="gpt-4o",
)
asyncio.run(main())
Functionality between the synchronous and asynchronous clients is otherwise identical.
Streaming responses
We provide support for streaming responses using Server Side Events (SSE).
from openai import OpenAI
client = OpenAI()
stream = client.chat.completions.create(
messages=[
{
"role": "user",
"content": "Say this is a test",
}
],
model="gpt-4o",
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")
The async client uses the exact same interface.
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI()
async def main():
stream = await client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Say this is a test"}],
stream=True,
)
async for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")
asyncio.run(main())
Module-level client
Important
We highly recommend instantiating client instances instead of relying on the global client.
We also expose a global client instance that is accessible in a similar fashion to versions prior to v1.
import openai
optional; defaults to os.environ['OPENAI_API_KEY']
openai.api_key = '...'
all client options can be configured just like the OpenAI instantiation counterpart
openai.base_url = "https://..."
openai.default_headers = {"x-foo": "true"}
completion = openai.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "user",
"content": "How do I output all files in a directory using Python?",
},
],
)
print(completion.choices[0].message.content)
Function calling , also called tool calls or tool calling
docs below
Function calling
Copy page
Connect models to external data and systems.
Function calling enables developers to connect language models to external data and systems. You can define a set of functions as tools that the model has access to, and it can use them when appropriate based on the conversation history. You can then execute those functions on the application side, and provide results back to the model.
Learn how to extend the capabilities of OpenAI models through function calling in this guide.
Experiment with function calling in the Playground by providing your own function definition or generate a ready-to-use definition.
Generate
Overview
Many applications require models to call custom functions to trigger actions within the application or interact with external systems.
Here is how you can define a function as a tool for the model to use:
from openai import OpenAI
client = OpenAI()
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
},
},
}
]
completion = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "What's the weather like in Paris today?"}],
tools=tools,
)
print(completion.choices[0].message.tool_calls)
This will return a function call that looks like this:
[
{
"id": "call_12345xyz",
"type": "function",
"function": { "name": "get_weather", "arguments": "{'location':'Paris'}" }
}
]
Functions are the only type of tools supported in the Chat Completions API, but the Assistants API also supports built-in tools.
Here are a few examples where function calling can be useful:
Fetching data: enable a conversational assistant to retrieve data from internal systems before responding to the user.
Taking action: allow an assistant to trigger actions based on the conversation, like scheduling meetings or initiating order returns.
Building rich workflows: allow assistants to execute multi-step workflows, like data extraction pipelines or content personalization.
Interacting with Application UIs: use function calls to update the user interface based on user input, like rendering a pin on a map or navigating a website.
You can find example use cases in the examples section below.
The lifecycle of a function call
When you use the OpenAI API with function calling, the model never actually executes functions itself - instead, it simply generates parameters that can be used to call your function. You are then responsible for handling how the function is executed in your code.
Read our integration guide below for more details on how to handle function calls.
Function Calling diagram
Function calling support
Function calling is supported in the Chat Completions API, Assistants API, Batch API and Realtime API.
This guide focuses on function calling using the Chat Completions API. We have separate guides for function calling using the Assistants API, and for function calling using the Realtime API.
Models supporting function calling
Function calling was introduced with the release of gpt-4-turbo on June 13, 2023. All gpt-* models released after this date support function calling.
Legacy models released before this date were not trained to support function calling.
Support for parallel function calling
You can find a complete list of models and their release date on our models page.
Integration guide
In this integration guide, we will walk through integrating function calling into an application, taking an order delivery assistant as an example. Rather than requiring users to interact with a form, we can let them ask the assistant for help in natural language.
We will cover how to define functions and instructions, then how to handle model responses and function execution results.
If you want to learn more about how to handle function calls in a streaming fashion, how to customize tool calling behavior or how to handle edge cases, refer to our advanced usage section.
Function definition
The starting point for function calling is choosing a function in your own codebase that you'd like to enable the model to generate arguments for.
For this example, let's imagine you want to allow the model to call the get_delivery_date function in your codebase which accepts an order_id and queries your database to determine the delivery date for a given package. Your function might look something like the following:
# This is the function that we want the model to be able to call
def get_delivery_date(order_id: str) -> datetime:
# Connect to the database
conn = sqlite3.connect('ecommerce.db')
cursor = conn.cursor()
# ...
Now that we know which function we wish to allow the model to call, we will create a “function definition” that describes the function to the model. This definition describes both what the function does (and potentially when it should be called) and what parameters are required to call the function.
The parameters section of your function definition should be described using JSON Schema. If and when the model generates a function call, it will use this information to generate arguments according to your provided schema.
If you want to ensure the model always adheres to your supplied schema, you can enable Structured Outputs with function calling.
In this example it may look like this:
{
"name": "get_delivery_date",
"description": "Get the delivery date for a customer's order. Call this whenever you need to know the delivery date, for example when a customer asks 'Where is my package'",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The customer's order ID."
}
},
"required": ["order_id"],
"additionalProperties": false
}
}
Next we need to provide our function definitions within an array of available “tools” when calling the Chat Completions API.
As always, we will provide an array of “messages”, which could for example contain your prompt or a back and forth conversation between the user and an assistant.
This example shows how you may call the Chat Completions API providing relevant tools and messages for an assistant that handles customer inquiries for a store.
tools = [
{
"type": "function",
"function": {
"name": "get_delivery_date",
"description": "Get the delivery date for a customer's order. Call this whenever you need to know the delivery date, for example when a customer asks 'Where is my package'",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The customer's order ID.",
},
},
"required": ["order_id"],
"additionalProperties": False,
},
}
}
]
messages = [
{
"role": "system",
"content": "You are a helpful customer support assistant. Use the supplied tools to assist the user."
},
{
"role": "user",
"content": "Hi, can you tell me the delivery date for my order?"
}
]
response = openai.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
)
Model instructions
While you should define in the function definitions how to call them, we recommend including instructions regarding when to call functions in the system prompt.
For example, you can tell the model when to use the function by saying something like: "Use the 'get_delivery_date' function when the user asks about their delivery date."
Handling model responses
The model only suggests function calls and generates arguments for the defined functions when appropriate. It is then up to you to decide how your application handles the execution of the functions based on these suggestions.
If the model determines that a function should be called, it will return a tool_calls field in the response, which you can use to determine if the model generated a function call and what the arguments were.
Unless you customize the tool calling behavior, the model will determine when to call functions based on the instructions and conversation.
Read the Tool calling behavior section below for more details on how you can force the model to call one or several tools.
If the model decides that no function should be called
If the model does not generate a function call, then the response will contain a direct reply to the user as a regular chat completion response.
For example, in this case chat_response.choices[0].message may contain:
chat.completionsMessage(
content='Hi there! I can help with that. Can you please provide your order ID?',
role='assistant',
function_call=None,
tool_calls=None
)
In an assistant use case you will typically want to show this response to the user and let them respond to it, in which case you will call the API again (with both the latest responses from the assistant and user appended to the messages).
Let's assume our user responded with their order id, and we sent the following request to the API.
tools = [
{
"type": "function",
"function": {
"name": "get_delivery_date",
"description": "Get the delivery date for a customer's order. Call this whenever you need to know the delivery date, for example when a customer asks 'Where is my package'",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The customer's order ID."
}
},
"required": ["order_id"],
"additionalProperties": False
}
}
}
]
messages = []
messages.append({"role": "system", "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user."})
messages.append({"role": "user", "content": "Hi, can you tell me the delivery date for my order?"})
messages.append({"role": "assistant", "content": "Hi there! I can help with that. Can you please provide your order ID?"})
messages.append({"role": "user", "content": "i think it is order_12345"})
response = client.chat.completions.create(
model='gpt-4o',
messages=messages,
tools=tools
)
If the model generated a function call
If the model generated a function call, it will generate the arguments for the call (based on the parameters definition you provided).
Here is an example response showing this:
Choice(
finish_reason='tool_calls',
index=0,
logprobs=None,
message=chat.completionsMessage(
content=None,
role='assistant',
function_call=None,
tool_calls=[
chat.completionsMessageToolCall(
id='call_62136354',
function=Function(
arguments='{"order_id":"order_12345"}',
name='get_delivery_date'),
type='function')
])
)
Handling the model response indicating that a function should be called
Assuming the response indicates that a function should be called, your code will now handle this:
# Extract the arguments for get_delivery_date
# Note this code assumes we have already determined that the model generated a function call. See below for a more production ready example that shows how to check if the model generated a function call
tool_call = response.choices[0].message.tool_calls[0]
arguments = json.loads(tool_call['function']['arguments'])
order_id = arguments.get('order_id')
# Call the get_delivery_date function with the extracted order_id
delivery_date = get_delivery_date(order_id)
Submitting function output
Once the function has been executed in the code, you need to submit the result of the function call back to the model.
This will trigger another model response, taking into account the function call result.
For example, this is how you can commit the result of the function call to a conversation history:
# Simulate the order_id and delivery_date
order_id = "order_12345"
delivery_date = datetime.now()
# Simulate the tool call response
response = {
"choices": [
{
"message": {
"role": "assistant",
"tool_calls": [
{
"id": "call_62136354",
"type": "function",
"function": {
"arguments": "{'order_id': 'order_12345'}",
"name": "get_delivery_date"
}
}
]
}
}
]
}
# Create a message containing the result of the function call
function_call_result_message = {
"role": "tool",
"content": json.dumps({
"order_id": order_id,
"delivery_date": delivery_date.strftime('%Y-%m-%d %H:%M:%S')
}),
"tool_call_id": response['choices'][0]['message']['tool_calls'][0]['id']
}
# Prepare the chat completion call payload
completion_payload = {
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user."},
{"role": "user", "content": "Hi, can you tell me the delivery date for my order?"},
{"role": "assistant", "content": "Hi there! I can help with that. Can you please provide your order ID?"},
{"role": "user", "content": "i think it is order_12345"},
response['choices'][0]['message'],
function_call_result_message
]
}
# Call the OpenAI API's chat completions endpoint to send the tool call result back to the model
response = openai.chat.completions.create(
model=completion_payload["model"],
messages=completion_payload["messages"]
)
# Print the response from the API. In this case it will typically contain a message such as "The delivery date for your order #12345 is xyz. Is there anything else I can help you with?"
print(response)
Note that an assistant message containing tool calls should always be followed by tool response messages (one per tool call). Making an API call with a messages array that does not follow this pattern will result in an error.
Structured Outputs
In August 2024, we launched Structured Outputs, which ensures that a model's output exactly matches a specified JSON schema.
By default, when using function calling, the API will offer best-effort matching for your parameters, which means that occasionally the model may miss parameters or get their types wrong when using complicated schemas.
You can enable Structured Outputs for function calling by setting the parameter strict: true in your function definition. You should also include the parameter additionalProperties: false and mark all arguments as required in your request. When this is enabled, the function arguments generated by the model will be constrained to match the JSON Schema provided in the function definition.
As an alternative to function calling you can instead constrain the model's regular output to match a JSON Schema of your choosing. Learn more about when to use function calling vs when to control the model's normal output by using response_format.
Parallel function calling and Structured Outputs
When the model outputs multiple function calls via parallel function calling, model outputs may not match strict schemas supplied in tools.
In order to ensure strict schema adherence, disable parallel function calls by supplying parallel_tool_calls: false. With this setting, the model will generate one function call at a time.
Why might I not want to turn on Structured Outputs?
The main reasons to not use Structured Outputs are:
If you need to use some feature of JSON Schema that is not yet supported (learn more), for example recursive schemas.
If each of your API requests includes a novel schema (i.e. your schemas are not fixed, but are generated on-demand and rarely repeat). The first request with a novel JSON schema will have increased latency as the schema is pre-processed and cached for future generations to constrain the output of the model.
What does Structured Outputs mean for Zero Data Retention?
When Structured Outputs is turned on, schemas provided are not eligible for zero data retention.
Supported schemas
Function calling with Structured Outputs supports a subset of the JSON Schema language.
For more information on supported schemas, see the Structured Outputs guide.
Structured Outputs
Copy page
Ensure responses adhere to a JSON schema.
Try it out
Try it out in the Playground or generate a ready-to-use schema definition to experiment with structured outputs.
Generate
Introduction
JSON is one of the most widely used formats in the world for applications to exchange data.
Structured Outputs is a feature that ensures the model will always generate responses that adhere to your supplied JSON Schema, so you don't need to worry about the model omitting a required key, or hallucinating an invalid enum value.
Some benefits of Structed Outputs include:
Reliable type-safety: No need to validate or retry incorrectly formatted responses
Explicit refusals: Safety-based model refusals are now programmatically detectable
Simpler prompting: No need for strongly worded prompts to achieve consistent formatting
In addition to supporting JSON Schema in the REST API, the OpenAI SDKs for Python and JavaScript also make it easy to define object schemas using Pydantic and Zod respectively. Below, you can see how to extract information from unstructured text that conforms to a schema defined in code.
Getting a structured response
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI()
class CalendarEvent(BaseModel):
name: str
date: str
participants: list[str]
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "Extract the event information."},
{"role": "user", "content": "Alice and Bob are going to a science fair on Friday."},
],
response_format=CalendarEvent,
)
event = completion.choices[0].message.parsed
Supported models
Structured Outputs are available in our latest large language models, starting with GPT-4o:
o1-2024-12-17 and later
gpt-4o-mini-2024-07-18 and later
gpt-4o-2024-08-06 and later
Older models like gpt-4-turbo and earlier may use JSON mode instead.
When to use Structured Outputs via function calling vs via response_format
Structured Outputs is available in two forms in the OpenAI API:
When using function calling
When using a json_schema response format
Function calling is useful when you are building an application that bridges the models and functionality of your application.
For example, you can give the model access to functions that query a database in order to build an AI assistant that can help users with their orders, or functions that can interact with the UI.
Conversely, Structured Outputs via response_format are more suitable when you want to indicate a structured schema for use when the model responds to the user, rather than when the model calls a tool.
For example, if you are building a math tutoring application, you might want the assistant to respond to your user using a specific JSON Schema so that you can generate a UI that displays different parts of the model's output in distinct ways.
Put simply:
If you are connecting the model to tools, functions, data, etc. in your system, then you should use function calling
If you want to structure the model's output when it responds to the user, then you should use a structured response_format
The remainder of this guide will focus on non-function calling use cases in the Chat Completions API. To learn more about how to use Structured Outputs with function calling, check out the Function Calling guide.
Structured Outputs vs JSON mode
Structured Outputs is the evolution of JSON mode. While both ensure valid JSON is produced, only Structured Outputs ensure schema adherance. Both Structured Outputs and JSON mode are supported in the Chat Completions API, Assistants API, Fine-tuning API and Batch API.
We recommend always using Structured Outputs instead of JSON mode when possible.
However, Structured Outputs with response_format: {type: "json_schema", ...} is only supported with the gpt-4o-mini, gpt-4o-mini-2024-07-18, and gpt-4o-2024-08-06 model snapshots and later.
Structured Outputs JSON Mode
Outputs valid JSON Yes Yes
Adheres to schema Yes (see supported schemas) No
Compatible models gpt-4o-mini, gpt-4o-2024-08-06, and later gpt-3.5-turbo, gpt-4-* and gpt-4o-* models
Enabling response_format: { type: "json_schema", json_schema: {"strict": true, "schema": ...} } response_format: { type: "json_object" }
Examples
Chain of thought
You can ask the model to output an answer in a structured, step-by-step way, to guide the user through the solution.
Structured Outputs for chain-of-thought math tutoring
from pydantic import BaseModel
from openai import OpenAI
client = OpenAI()
class Step(BaseModel):
explanation: str
output: str
class MathReasoning(BaseModel):
steps: list[Step]
final_answer: str
completion = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{"role": "system", "content": "You are a helpful math tutor. Guide the user through the solution step by step."},
{"role": "user", "content": "how can I solve 8x + 7 = -23"}
],
response_format=MathReasoning,
)
math_reasoning = completion.choices[0].message.parsed
Example
You can use zod in nodeJS and Pydantic in python when using the SDKs to pass your function definitions to the model.
from enum import Enum
from typing import Union
from pydantic import BaseModel
import openai
from openai import OpenAI
client = OpenAI()
class GetDeliveryDate(BaseModel):
order_id: str
tools = [openai.pydantic_function_tool(GetDeliveryDate)]
messages = []
messages.append({"role": "system", "content": "You are a helpful customer support assistant. Use the supplied tools to assist the user."})
messages.append({"role": "user", "content": "Hi, can you tell me the delivery date for my order #12345?"})
response = client.beta.chat.completions.create(
model='gpt-4o-2024-08-06',
messages=messages,
tools=tools
)
print(response.choices[0].message.tool_calls[0].function)
If you are not using the SDKs, add the strict: true parameter to your function definition. Additionally, all parameters must be included in the required array, and additionalProperties: false must be set.
{
"name": "get_delivery_date",
"description": "Get the delivery date for a customer's order. Call this whenever you need to know the delivery date, for example when a customer asks \\"Where is my package\\"",
"strict": true,
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string" }
},
"required": ["order_id"],
"additionalProperties": false,
}
}
Limitations
When you use Structured Outputs with function calling, the model will always follow your exact schema, except in a few circumstances:
When the model's response is cut off (either due to max_tokens, stop_tokens, or maximum context length)
When a model refusal happens
When there is a content_filter finish reason
Note that the first time you send a request with a new schema using Structured Outputs, there will be additional latency as the schema is processed, but subsequent requests should incur no overhead.
Advanced usage
Streaming tool calls
You can stream tool calls and process function arguments as they are being generated. This is especially useful if you want to display the function arguments in your UI, or if you don't need to wait for the whole function parameters to be generated before executing the function.
To enable streaming tool calls, you can set stream: true in your request. You can then process the streaming delta and check for any new tool calls delta.
You can find more information on streaming in the API reference.
Here is an example of how you can handle streaming tool calls with the node and python SDKs:
from openai import OpenAI
import json
client = OpenAI()
# Define functions
tools = [
{
"type": "function",
"function": {
"name": "generate_recipe",
"description": "Generate a recipe based on the user's input",
"parameters": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "Title of the recipe.",
},
"ingredients": {
"type": "array",
"items": {"type": "string"},
"description": "List of ingredients required for the recipe.",
},
"instructions": {
"type": "array",
"items": {"type": "string"},
"description": "Step-by-step instructions for the recipe.",
},
},
"required": ["title", "ingredients", "instructions"],
"additionalProperties": False,
},
},
}
]
response_stream = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": (
"You are an expert cook who can help turn any user input into a delicious recipe."
"As soon as the user tells you what they want, use the generate_recipe tool to create a detailed recipe for them."
),
},
{
"role": "user",
"content": "I want to make pancakes for 4.",
},
],
tools=tools,
stream=True,
)
function_arguments = ""
function_name = ""
is_collecting_function_args = False
for part in response_stream:
delta = part.choices[0].delta
finish_reason = part.choices[0].finish_reason
# Process assistant content
if 'content' in delta:
print("Assistant:", delta.content)
if delta.tool_calls:
is_collecting_function_args = True
tool_call = delta.tool_calls[0]
if tool_call.function.name:
function_name = tool_call.function.name
print(f"Function name: '{function_name}'")
# Process function arguments delta
if tool_call.function.arguments:
function_arguments += tool_call.function.arguments
print(f"Arguments: {function_arguments}")
# Process tool call with complete arguments
if finish_reason == "tool_calls" and is_collecting_function_args:
print(f"Function call '{function_name}' is complete.")
args = json.loads(function_arguments)
print("Complete function arguments:")
print(json.dumps(args, indent=2))
# Reset for the next potential function call
function_arguments = ""
function_name = ""
is_collecting_function_args = False
Tool calling behavior
The API supports advanced features such as parallel tool calling and the ability to force tool calls.
You can disable parallel tool calling by setting parallel_tool_calls: false.
Parallel tool calling
Any models released on or after Nov 6, 2023 may by default generate multiple tool calls in a single response, indicating that they should be called in parallel.
This is especially useful if executing the given functions takes a long time. For example, the model may call functions to get the weather in 3 different locations at the same time, which will result in a message with 3 function calls in the tool_calls array.
Example response:
response = Choice(
finish_reason='tool_calls',
index=0,
logprobs=None,
message=chat.completionsMessage(
content=None,
role='assistant',
function_call=None,
tool_calls=[
chat.completionsMessageToolCall(
id='call_62136355',
function=Function(
arguments='{"city":"New York"}',
name='check_weather'),
type='function'),
chat.completionsMessageToolCall(
id='call_62136356',
function=Function(
arguments='{"city":"London"}',
name='check_weather'),
type='function'),
chat.completionsMessageToolCall(
id='call_62136357',
function=Function(
arguments='{"city":"Tokyo"}',
name='check_weather'),
type='function')
])
)
# Iterate through tool calls to handle each weather check
for tool_call in response.message.tool_calls:
arguments = json.loads(tool_call.function.arguments)
city = arguments['city']
weather_info = check_weather(city)
print(f"Weather in {city}: {weather_info}")
Each function call in the array has a unique id.
Once you've executed these function calls in your application, you can provide the result back to the model by adding one new message to the conversation for each function call, each containing the result of one function call, with a tool_call_id referencing the id from tool_calls, for example:
# Assume we have fetched the weather data from somewhere
weather_data = {
"New York": {"temperature": "22°C", "condition": "Sunny"},
"London": {"temperature": "15°C", "condition": "Cloudy"},
"Tokyo": {"temperature": "25°C", "condition": "Rainy"}
}
# Prepare the chat completion call payload with inline function call result creation
completion_payload = {
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are a helpful assistant providing weather updates."},
{"role": "user", "content": "Can you tell me the weather in New York, London, and Tokyo?"},
# Append the original function calls to the conversation
response['message'],
# Include the result of the function calls
{
"role": "tool",
"content": json.dumps({
"city": "New York",
"weather": weather_data["New York"]
}),
# Here we specify the tool_call_id that this result corresponds to
"tool_call_id": response['message']['tool_calls'][0]['id']
},
{
"role": "tool",
"content": json.dumps({
"city": "London",
"weather": weather_data["London"]
}),
"tool_call_id": response['message']['tool_calls'][1]['id']
},
{
"role": "tool",
"content": json.dumps({
"city": "Tokyo",
"weather": weather_data["Tokyo"]
}),
"tool_call_id": response['message']['tool_calls'][2]['id']
}
]
}
# Call the OpenAI API's chat completions endpoint to send the tool call result back to the model
response = openai.chat.completions.create(
model=completion_payload["model"],
messages=completion_payload["messages"]
)
# Print the response from the API, which will return something like "In New York the weather is..."
print(response)
Forcing tool calls
By default, the model is configured to automatically select which tools to call, as determined by the tool_choice: "auto" setting.
We offer three ways to customize the default behavior:
To force the model to always call one or more tools, you can set tool_choice: "required". The model will then always select one or more tool(s) to call. This is useful for example if you want the model to pick between multiple actions to perform next
To force the model to call a specific function, you can set tool_choice: {"type": "function", "function": {"name": "my_function"}}
To disable function calling and force the model to only generate a user-facing message, you can either provide no tools, or set tool_choice: "none"
Note that if you do either 1 or 2 (i.e. force the model to call a function) then the subsequent finish_reason will be "stop" instead of being "tool_calls".
from openai import OpenAI
client = OpenAI()
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"},
"unit": {"type": "string", "enum": ["c", "f"]},
},
"required": ["location", "unit"],
"additionalProperties": False,
},
},
},
{
"type": "function",
"function": {
"name": "get_stock_price",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string"},
},
"required": ["symbol"],
"additionalProperties": False,
},
},
},
]
messages = [{"role": "user", "content": "What's the weather like in Boston today?"}]
completion = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="required"
)
print(completion)
To see a practical example of how to force tool calls, see our cookbook:
Customer service with tool required
Learn how to add an element of determinism to your customer service assistant
Edge cases
We recommend using the SDK to handle the edge cases described below. If for any reason you cannot use the SDK, you should handle these cases in your code.
When you receive a response from the API, if you're not using the SDK, there are a number of edge cases that production code should handle.
In general, the API will return a valid function call, but there are some edge cases when this won't happen: