1# OpenAI Go API Library
2
3<a href="https://pkg.go.dev/github.com/openai/openai-go"><img src="https://pkg.go.dev/badge/github.com/openai/openai-go.svg" alt="Go Reference"></a>
4
5The OpenAI Go library provides convenient access to the [OpenAI REST API](https://platform.openai.com/docs)
6from applications written in Go.
7
8> [!WARNING]
9> The latest version of this package uses a new design with significant breaking changes.
10> Please refer to the [migration guide](./MIGRATION.md) for more information on how to update your code.
11
12## Installation
13
14<!-- x-release-please-start-version -->
15
16```go
17import (
18 "github.com/openai/openai-go" // imported as openai
19)
20```
21
22<!-- x-release-please-end -->
23
24Or to pin the version:
25
26<!-- x-release-please-start-version -->
27
28```sh
29go get -u 'github.com/openai/openai-go@v1.8.2'
30```
31
32<!-- x-release-please-end -->
33
34## Requirements
35
36This library requires Go 1.18+.
37
38## Usage
39
40The full API of this library can be found in [api.md](api.md).
41
42```go
43package main
44
45import (
46 "context"
47 "fmt"
48
49 "github.com/openai/openai-go"
50 "github.com/openai/openai-go/option"
51 "github.com/openai/openai-go/shared"
52)
53
54func main() {
55 client := openai.NewClient(
56 option.WithAPIKey("My API Key"), // defaults to os.LookupEnv("OPENAI_API_KEY")
57 )
58 chatCompletion, err := client.Chat.Completions.New(context.TODO(), openai.ChatCompletionNewParams{
59 Messages: []openai.ChatCompletionMessageParamUnion{
60 openai.UserMessage("Say this is a test"),
61 },
62 Model: openai.ChatModelGPT4o,
63 })
64 if err != nil {
65 panic(err.Error())
66 }
67 println(chatCompletion.Choices[0].Message.Content)
68}
69
70```
71
72<details>
73<summary>Conversations</summary>
74
75```go
76param := openai.ChatCompletionNewParams{
77 Messages: []openai.ChatCompletionMessageParamUnion{
78 openai.UserMessage("What kind of houseplant is easy to take care of?"),
79 },
80 Seed: openai.Int(1),
81 Model: openai.ChatModelGPT4o,
82}
83
84completion, err := client.Chat.Completions.New(ctx, param)
85
86param.Messages = append(param.Messages, completion.Choices[0].Message.ToParam())
87param.Messages = append(param.Messages, openai.UserMessage("How big are those?"))
88
89// continue the conversation
90completion, err = client.Chat.Completions.New(ctx, param)
91```
92
93</details>
94
95<details>
96<summary>Streaming responses</summary>
97
98```go
99question := "Write an epic"
100
101stream := client.Chat.Completions.NewStreaming(ctx, openai.ChatCompletionNewParams{
102 Messages: []openai.ChatCompletionMessageParamUnion{
103 openai.UserMessage(question),
104 },
105 Seed: openai.Int(0),
106 Model: openai.ChatModelGPT4o,
107})
108
109// optionally, an accumulator helper can be used
110acc := openai.ChatCompletionAccumulator{}
111
112for stream.Next() {
113 chunk := stream.Current()
114 acc.AddChunk(chunk)
115
116 if content, ok := acc.JustFinishedContent(); ok {
117 println("Content stream finished:", content)
118 }
119
120 // if using tool calls
121 if tool, ok := acc.JustFinishedToolCall(); ok {
122 println("Tool call stream finished:", tool.Index, tool.Name, tool.Arguments)
123 }
124
125 if refusal, ok := acc.JustFinishedRefusal(); ok {
126 println("Refusal stream finished:", refusal)
127 }
128
129 // it's best to use chunks after handling JustFinished events
130 if len(chunk.Choices) > 0 {
131 println(chunk.Choices[0].Delta.Content)
132 }
133}
134
135if stream.Err() != nil {
136 panic(stream.Err())
137}
138
139// After the stream is finished, acc can be used like a ChatCompletion
140_ = acc.Choices[0].Message.Content
141```
142
143> See the [full streaming and accumulation example](./examples/chat-completion-accumulating/main.go)
144
145</details>
146
147<details>
148<summary>Tool calling</summary>
149
150```go
151import (
152 "encoding/json"
153 // ...
154)
155
156// ...
157
158question := "What is the weather in New York City?"
159
160params := openai.ChatCompletionNewParams{
161 Messages: []openai.ChatCompletionMessageParamUnion{
162 openai.UserMessage(question),
163 },
164 Tools: []openai.ChatCompletionToolParam{
165 {
166 Function: openai.FunctionDefinitionParam{
167 Name: "get_weather",
168 Description: openai.String("Get weather at the given location"),
169 Parameters: openai.FunctionParameters{
170 "type": "object",
171 "properties": map[string]interface{}{
172 "location": map[string]string{
173 "type": "string",
174 },
175 },
176 "required": []string{"location"},
177 },
178 },
179 },
180 },
181 Model: openai.ChatModelGPT4o,
182}
183
184// If there is a was a function call, continue the conversation
185params.Messages = append(params.Messages, completion.Choices[0].Message.ToParam())
186for _, toolCall := range toolCalls {
187 if toolCall.Function.Name == "get_weather" {
188 // Extract the location from the function call arguments
189 var args map[string]interface{}
190 err := json.Unmarshal([]byte(toolCall.Function.Arguments), &args)
191 if err != nil {
192 panic(err)
193 }
194 location := args["location"].(string)
195
196 // Simulate getting weather data
197 weatherData := getWeather(location)
198
199 // Print the weather data
200 fmt.Printf("Weather in %s: %s\n", location, weatherData)
201
202 params.Messages = append(params.Messages, openai.ToolMessage(weatherData, toolCall.ID))
203 }
204}
205
206// ... continue the conversation with the information provided by the tool
207```
208
209> See the [full tool calling example](./examples/chat-completion-tool-calling/main.go)
210
211</details>
212
213<details>
214<summary>Structured outputs</summary>
215
216```go
217import (
218 "encoding/json"
219 "github.com/invopop/jsonschema"
220 // ...
221)
222
223// A struct that will be converted to a Structured Outputs response schema
224type HistoricalComputer struct {
225 Origin Origin `json:"origin" jsonschema_description:"The origin of the computer"`
226 Name string `json:"full_name" jsonschema_description:"The name of the device model"`
227 Legacy string `json:"legacy" jsonschema:"enum=positive,enum=neutral,enum=negative" jsonschema_description:"Its influence on the field of computing"`
228 NotableFacts []string `json:"notable_facts" jsonschema_description:"A few key facts about the computer"`
229}
230
231type Origin struct {
232 YearBuilt int64 `json:"year_of_construction" jsonschema_description:"The year it was made"`
233 Organization string `json:"organization" jsonschema_description:"The organization that was in charge of its development"`
234}
235
236func GenerateSchema[T any]() interface{} {
237 // Structured Outputs uses a subset of JSON schema
238 // These flags are necessary to comply with the subset
239 reflector := jsonschema.Reflector{
240 AllowAdditionalProperties: false,
241 DoNotReference: true,
242 }
243 var v T
244 schema := reflector.Reflect(v)
245 return schema
246}
247
248// Generate the JSON schema at initialization time
249var HistoricalComputerResponseSchema = GenerateSchema[HistoricalComputer]()
250
251func main() {
252
253 // ...
254
255 question := "What computer ran the first neural network?"
256
257 schemaParam := openai.ResponseFormatJSONSchemaJSONSchemaParam{
258 Name: "historical_computer",
259 Description: openai.String("Notable information about a computer"),
260 Schema: HistoricalComputerResponseSchema,
261 Strict: openai.Bool(true),
262 }
263
264 chat, _ := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
265 // ...
266 ResponseFormat: openai.ChatCompletionNewParamsResponseFormatUnion{
267 OfJSONSchema: &openai.ResponseFormatJSONSchemaParam{
268 JSONSchema: schemaParam,
269 },
270 },
271 // only certain models can perform structured outputs
272 Model: openai.ChatModelGPT4o2024_08_06,
273 })
274
275 // extract into a well-typed struct
276 var historicalComputer HistoricalComputer
277 _ = json.Unmarshal([]byte(chat.Choices[0].Message.Content), &historicalComputer)
278
279 historicalComputer.Name
280 historicalComputer.Origin.YearBuilt
281 historicalComputer.Origin.Organization
282 for i, fact := range historicalComputer.NotableFacts {
283 // ...
284 }
285}
286```
287
288> See the [full structured outputs example](./examples/structured-outputs/main.go)
289
290</details>
291
292
293### Request fields
294
295The openai library uses the [`omitzero`](https://tip.golang.org/doc/go1.24#encodingjsonpkgencodingjson)
296semantics from the Go 1.24+ `encoding/json` release for request fields.
297
298Required primitive fields (`int64`, `string`, etc.) feature the tag <code>\`json:"...,required"\`</code>. These
299fields are always serialized, even their zero values.
300
301Optional primitive types are wrapped in a `param.Opt[T]`. These fields can be set with the provided constructors, `openai.String(string)`, `openai.Int(int64)`, etc.
302
303Any `param.Opt[T]`, map, slice, struct or string enum uses the
304tag <code>\`json:"...,omitzero"\`</code>. Its zero value is considered omitted.
305
306The `param.IsOmitted(any)` function can confirm the presence of any `omitzero` field.
307
308```go
309p := openai.ExampleParams{
310 ID: "id_xxx", // required property
311 Name: openai.String("..."), // optional property
312
313 Point: openai.Point{
314 X: 0, // required field will serialize as 0
315 Y: openai.Int(1), // optional field will serialize as 1
316 // ... omitted non-required fields will not be serialized
317 },
318
319 Origin: openai.Origin{}, // the zero value of [Origin] is considered omitted
320}
321```
322
323To send `null` instead of a `param.Opt[T]`, use `param.Null[T]()`.
324To send `null` instead of a struct `T`, use `param.NullStruct[T]()`.
325
326```go
327p.Name = param.Null[string]() // 'null' instead of string
328p.Point = param.NullStruct[Point]() // 'null' instead of struct
329
330param.IsNull(p.Name) // true
331param.IsNull(p.Point) // true
332```
333
334Request structs contain a `.SetExtraFields(map[string]any)` method which can send non-conforming
335fields in the request body. Extra fields overwrite any struct fields with a matching
336key. For security reasons, only use `SetExtraFields` with trusted data.
337
338To send a custom value instead of a struct, use `param.Override[T](value)`.
339
340```go
341// In cases where the API specifies a given type,
342// but you want to send something else, use [SetExtraFields]:
343p.SetExtraFields(map[string]any{
344 "x": 0.01, // send "x" as a float instead of int
345})
346
347// Send a number instead of an object
348custom := param.Override[openai.FooParams](12)
349```
350
351### Request unions
352
353Unions are represented as a struct with fields prefixed by "Of" for each of it's variants,
354only one field can be non-zero. The non-zero field will be serialized.
355
356Sub-properties of the union can be accessed via methods on the union struct.
357These methods return a mutable pointer to the underlying data, if present.
358
359```go
360// Only one field can be non-zero, use param.IsOmitted() to check if a field is set
361type AnimalUnionParam struct {
362 OfCat *Cat `json:",omitzero,inline`
363 OfDog *Dog `json:",omitzero,inline`
364}
365
366animal := AnimalUnionParam{
367 OfCat: &Cat{
368 Name: "Whiskers",
369 Owner: PersonParam{
370 Address: AddressParam{Street: "3333 Coyote Hill Rd", Zip: 0},
371 },
372 },
373}
374
375// Mutating a field
376if address := animal.GetOwner().GetAddress(); address != nil {
377 address.ZipCode = 94304
378}
379```
380
381### Response objects
382
383All fields in response structs are ordinary value types (not pointers or wrappers).
384Response structs also include a special `JSON` field containing metadata about
385each property.
386
387```go
388type Animal struct {
389 Name string `json:"name,nullable"`
390 Owners int `json:"owners"`
391 Age int `json:"age"`
392 JSON struct {
393 Name respjson.Field
394 Owner respjson.Field
395 Age respjson.Field
396 ExtraFields map[string]respjson.Field
397 } `json:"-"`
398}
399```
400
401To handle optional data, use the `.Valid()` method on the JSON field.
402`.Valid()` returns true if a field is not `null`, not present, or couldn't be marshaled.
403
404If `.Valid()` is false, the corresponding field will simply be its zero value.
405
406```go
407raw := `{"owners": 1, "name": null}`
408
409var res Animal
410json.Unmarshal([]byte(raw), &res)
411
412// Accessing regular fields
413
414res.Owners // 1
415res.Name // ""
416res.Age // 0
417
418// Optional field checks
419
420res.JSON.Owners.Valid() // true
421res.JSON.Name.Valid() // false
422res.JSON.Age.Valid() // false
423
424// Raw JSON values
425
426res.JSON.Owners.Raw() // "1"
427res.JSON.Name.Raw() == "null" // true
428res.JSON.Name.Raw() == respjson.Null // true
429res.JSON.Age.Raw() == "" // true
430res.JSON.Age.Raw() == respjson.Omitted // true
431```
432
433These `.JSON` structs also include an `ExtraFields` map containing
434any properties in the json response that were not specified
435in the struct. This can be useful for API features not yet
436present in the SDK.
437
438```go
439body := res.JSON.ExtraFields["my_unexpected_field"].Raw()
440```
441
442### Response Unions
443
444In responses, unions are represented by a flattened struct containing all possible fields from each of the
445object variants.
446To convert it to a variant use the `.AsFooVariant()` method or the `.AsAny()` method if present.
447
448If a response value union contains primitive values, primitive fields will be alongside
449the properties but prefixed with `Of` and feature the tag `json:"...,inline"`.
450
451```go
452type AnimalUnion struct {
453 // From variants [Dog], [Cat]
454 Owner Person `json:"owner"`
455 // From variant [Dog]
456 DogBreed string `json:"dog_breed"`
457 // From variant [Cat]
458 CatBreed string `json:"cat_breed"`
459 // ...
460
461 JSON struct {
462 Owner respjson.Field
463 // ...
464 } `json:"-"`
465}
466
467// If animal variant
468if animal.Owner.Address.ZipCode == "" {
469 panic("missing zip code")
470}
471
472// Switch on the variant
473switch variant := animal.AsAny().(type) {
474case Dog:
475case Cat:
476default:
477 panic("unexpected type")
478}
479```
480
481### RequestOptions
482
483This library uses the functional options pattern. Functions defined in the
484`option` package return a `RequestOption`, which is a closure that mutates a
485`RequestConfig`. These options can be supplied to the client or at individual
486requests. For example:
487
488```go
489client := openai.NewClient(
490 // Adds a header to every request made by the client
491 option.WithHeader("X-Some-Header", "custom_header_info"),
492)
493
494client.Chat.Completions.New(context.TODO(), ...,
495 // Override the header
496 option.WithHeader("X-Some-Header", "some_other_custom_header_info"),
497 // Add an undocumented field to the request body, using sjson syntax
498 option.WithJSONSet("some.json.path", map[string]string{"my": "object"}),
499)
500```
501
502The request option `option.WithDebugLog(nil)` may be helpful while debugging.
503
504See the [full list of request options](https://pkg.go.dev/github.com/openai/openai-go/option).
505
506### Pagination
507
508This library provides some conveniences for working with paginated list endpoints.
509
510You can use `.ListAutoPaging()` methods to iterate through items across all pages:
511
512```go
513iter := client.FineTuning.Jobs.ListAutoPaging(context.TODO(), openai.FineTuningJobListParams{
514 Limit: openai.Int(20),
515})
516// Automatically fetches more pages as needed.
517for iter.Next() {
518 fineTuningJob := iter.Current()
519 fmt.Printf("%+v\n", fineTuningJob)
520}
521if err := iter.Err(); err != nil {
522 panic(err.Error())
523}
524```
525
526Or you can use simple `.List()` methods to fetch a single page and receive a standard response object
527with additional helper methods like `.GetNextPage()`, e.g.:
528
529```go
530page, err := client.FineTuning.Jobs.List(context.TODO(), openai.FineTuningJobListParams{
531 Limit: openai.Int(20),
532})
533for page != nil {
534 for _, job := range page.Data {
535 fmt.Printf("%+v\n", job)
536 }
537 page, err = page.GetNextPage()
538}
539if err != nil {
540 panic(err.Error())
541}
542```
543
544### Errors
545
546When the API returns a non-success status code, we return an error with type
547`*openai.Error`. This contains the `StatusCode`, `*http.Request`, and
548`*http.Response` values of the request, as well as the JSON of the error body
549(much like other response objects in the SDK).
550
551To handle errors, we recommend that you use the `errors.As` pattern:
552
553```go
554_, err := client.FineTuning.Jobs.New(context.TODO(), openai.FineTuningJobNewParams{
555 Model: openai.FineTuningJobNewParamsModelBabbage002,
556 TrainingFile: "file-abc123",
557})
558if err != nil {
559 var apierr *openai.Error
560 if errors.As(err, &apierr) {
561 println(string(apierr.DumpRequest(true))) // Prints the serialized HTTP request
562 println(string(apierr.DumpResponse(true))) // Prints the serialized HTTP response
563 }
564 panic(err.Error()) // GET "/fine_tuning/jobs": 400 Bad Request { ... }
565}
566```
567
568When other errors occur, they are returned unwrapped; for example,
569if HTTP transport fails, you might receive `*url.Error` wrapping `*net.OpError`.
570
571### Timeouts
572
573Requests do not time out by default; use context to configure a timeout for a request lifecycle.
574
575Note that if a request is [retried](#retries), the context timeout does not start over.
576To set a per-retry timeout, use `option.WithRequestTimeout()`.
577
578```go
579// This sets the timeout for the request, including all the retries.
580ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
581defer cancel()
582client.Chat.Completions.New(
583 ctx,
584 openai.ChatCompletionNewParams{
585 Messages: []openai.ChatCompletionMessageParamUnion{{
586 OfUser: &openai.ChatCompletionUserMessageParam{
587 Content: openai.ChatCompletionUserMessageParamContentUnion{
588 OfString: openai.String("How can I list all files in a directory using Python?"),
589 },
590 },
591 }},
592 Model: shared.ChatModelGPT4_1,
593 },
594 // This sets the per-retry timeout
595 option.WithRequestTimeout(20*time.Second),
596)
597```
598
599### File uploads
600
601Request parameters that correspond to file uploads in multipart requests are typed as
602`io.Reader`. The contents of the `io.Reader` will by default be sent as a multipart form
603part with the file name of "anonymous_file" and content-type of "application/octet-stream".
604
605The file name and content-type can be customized by implementing `Name() string` or `ContentType()
606string` on the run-time type of `io.Reader`. Note that `os.File` implements `Name() string`, so a
607file returned by `os.Open` will be sent with the file name on disk.
608
609We also provide a helper `openai.File(reader io.Reader, filename string, contentType string)`
610which can be used to wrap any `io.Reader` with the appropriate file name and content type.
611
612```go
613// A file from the file system
614file, err := os.Open("input.jsonl")
615openai.FileNewParams{
616 File: file,
617 Purpose: openai.FilePurposeFineTune,
618}
619
620// A file from a string
621openai.FileNewParams{
622 File: strings.NewReader("my file contents"),
623 Purpose: openai.FilePurposeFineTune,
624}
625
626// With a custom filename and contentType
627openai.FileNewParams{
628 File: openai.File(strings.NewReader(`{"hello": "foo"}`), "file.go", "application/json"),
629 Purpose: openai.FilePurposeFineTune,
630}
631```
632
633## Webhook Verification
634
635Verifying webhook signatures is _optional but encouraged_.
636
637For more information about webhooks, see [the API docs](https://platform.openai.com/docs/guides/webhooks).
638
639### Parsing webhook payloads
640
641For most use cases, you will likely want to verify the webhook and parse the payload at the same time. To achieve this, we provide the method `client.Webhooks.Unwrap()`, which parses a webhook request and verifies that it was sent by OpenAI. This method will return an error if the signature is invalid.
642
643Note that the `body` parameter should be the raw JSON bytes sent from the server (do not parse it first). The `Unwrap()` method will parse this JSON for you into an event object after verifying the webhook was sent from OpenAI.
644
645```go
646package main
647
648import (
649 "io"
650 "log"
651 "net/http"
652 "os"
653
654 "github.com/gin-gonic/gin"
655 "github.com/openai/openai-go"
656 "github.com/openai/openai-go/option"
657 "github.com/openai/openai-go/webhooks"
658)
659
660func main() {
661 client := openai.NewClient(
662 option.WithWebhookSecret(os.Getenv("OPENAI_WEBHOOK_SECRET")), // env var used by default; explicit here.
663 )
664
665 r := gin.Default()
666
667 r.POST("/webhook", func(c *gin.Context) {
668 body, err := io.ReadAll(c.Request.Body)
669 if err != nil {
670 c.JSON(http.StatusInternalServerError, gin.H{"error": "Error reading request body"})
671 return
672 }
673 defer c.Request.Body.Close()
674
675 webhookEvent, err := client.Webhooks.Unwrap(body, c.Request.Header)
676 if err != nil {
677 log.Printf("Invalid webhook signature: %v", err)
678 c.JSON(http.StatusBadRequest, gin.H{"error": "invalid signature"})
679 return
680 }
681
682 switch event := webhookEvent.AsAny().(type) {
683 case webhooks.ResponseCompletedWebhookEvent:
684 log.Printf("Response completed: %+v", event.Data)
685 case webhooks.ResponseFailedWebhookEvent:
686 log.Printf("Response failed: %+v", event.Data)
687 default:
688 log.Printf("Unhandled event type: %T", event)
689 }
690
691 c.JSON(http.StatusOK, gin.H{"message": "ok"})
692 })
693
694 r.Run(":8000")
695}
696```
697
698### Verifying webhook payloads directly
699
700In some cases, you may want to verify the webhook separately from parsing the payload. If you prefer to handle these steps separately, we provide the method `client.Webhooks.VerifySignature()` to _only verify_ the signature of a webhook request. Like `Unwrap()`, this method will return an error if the signature is invalid.
701
702Note that the `body` parameter should be the raw JSON bytes sent from the server (do not parse it first). You will then need to parse the body after verifying the signature.
703
704```go
705package main
706
707import (
708 "encoding/json"
709 "io"
710 "log"
711 "net/http"
712 "os"
713
714 "github.com/gin-gonic/gin"
715 "github.com/openai/openai-go"
716 "github.com/openai/openai-go/option"
717)
718
719func main() {
720 client := openai.NewClient(
721 option.WithWebhookSecret(os.Getenv("OPENAI_WEBHOOK_SECRET")), // env var used by default; explicit here.
722 )
723
724 r := gin.Default()
725
726 r.POST("/webhook", func(c *gin.Context) {
727 body, err := io.ReadAll(c.Request.Body)
728 if err != nil {
729 c.JSON(http.StatusInternalServerError, gin.H{"error": "Error reading request body"})
730 return
731 }
732 defer c.Request.Body.Close()
733
734 err = client.Webhooks.VerifySignature(body, c.Request.Header)
735 if err != nil {
736 log.Printf("Invalid webhook signature: %v", err)
737 c.JSON(http.StatusBadRequest, gin.H{"error": "invalid signature"})
738 return
739 }
740
741 c.JSON(http.StatusOK, gin.H{"message": "ok"})
742 })
743
744 r.Run(":8000")
745}
746```
747
748### Retries
749
750Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
751We retry by default all connection errors, 408 Request Timeout, 409 Conflict, 429 Rate Limit,
752and >=500 Internal errors.
753
754You can use the `WithMaxRetries` option to configure or disable this:
755
756```go
757// Configure the default for all requests:
758client := openai.NewClient(
759 option.WithMaxRetries(0), // default is 2
760)
761
762// Override per-request:
763client.Chat.Completions.New(
764 context.TODO(),
765 openai.ChatCompletionNewParams{
766 Messages: []openai.ChatCompletionMessageParamUnion{{
767 OfUser: &openai.ChatCompletionUserMessageParam{
768 Content: openai.ChatCompletionUserMessageParamContentUnion{
769 OfString: openai.String("How can I get the name of the current day in JavaScript?"),
770 },
771 },
772 }},
773 Model: shared.ChatModelGPT4_1,
774 },
775 option.WithMaxRetries(5),
776)
777```
778
779### Accessing raw response data (e.g. response headers)
780
781You can access the raw HTTP response data by using the `option.WithResponseInto()` request option. This is useful when
782you need to examine response headers, status codes, or other details.
783
784```go
785// Create a variable to store the HTTP response
786var response *http.Response
787chatCompletion, err := client.Chat.Completions.New(
788 context.TODO(),
789 openai.ChatCompletionNewParams{
790 Messages: []openai.ChatCompletionMessageParamUnion{{
791 OfUser: &openai.ChatCompletionUserMessageParam{
792 Content: openai.ChatCompletionUserMessageParamContentUnion{
793 OfString: openai.String("Say this is a test"),
794 },
795 },
796 }},
797 Model: shared.ChatModelGPT4_1,
798 },
799 option.WithResponseInto(&response),
800)
801if err != nil {
802 // handle error
803}
804fmt.Printf("%+v\n", chatCompletion)
805
806fmt.Printf("Status Code: %d\n", response.StatusCode)
807fmt.Printf("Headers: %+#v\n", response.Header)
808```
809
810### Making custom/undocumented requests
811
812This library is typed for convenient access to the documented API. If you need to access undocumented
813endpoints, params, or response properties, the library can still be used.
814
815#### Undocumented endpoints
816
817To make requests to undocumented endpoints, you can use `client.Get`, `client.Post`, and other HTTP verbs.
818`RequestOptions` on the client, such as retries, will be respected when making these requests.
819
820```go
821var (
822 // params can be an io.Reader, a []byte, an encoding/json serializable object,
823 // or a "β¦Params" struct defined in this library.
824 params map[string]any
825
826 // result can be an []byte, *http.Response, a encoding/json deserializable object,
827 // or a model defined in this library.
828 result *http.Response
829)
830err := client.Post(context.Background(), "/unspecified", params, &result)
831if err != nil {
832 β¦
833}
834```
835
836#### Undocumented request params
837
838To make requests using undocumented parameters, you may use either the `option.WithQuerySet()`
839or the `option.WithJSONSet()` methods.
840
841```go
842params := FooNewParams{
843 ID: "id_xxxx",
844 Data: FooNewParamsData{
845 FirstName: openai.String("John"),
846 },
847}
848client.Foo.New(context.Background(), params, option.WithJSONSet("data.last_name", "Doe"))
849```
850
851#### Undocumented response properties
852
853To access undocumented response properties, you may either access the raw JSON of the response as a string
854with `result.JSON.RawJSON()`, or get the raw JSON of a particular field on the result with
855`result.JSON.Foo.Raw()`.
856
857Any fields that are not present on the response struct will be saved and can be accessed by `result.JSON.ExtraFields()` which returns the extra fields as a `map[string]Field`.
858
859### Middleware
860
861We provide `option.WithMiddleware` which applies the given
862middleware to requests.
863
864```go
865func Logger(req *http.Request, next option.MiddlewareNext) (res *http.Response, err error) {
866 // Before the request
867 start := time.Now()
868 LogReq(req)
869
870 // Forward the request to the next handler
871 res, err = next(req)
872
873 // Handle stuff after the request
874 end := time.Now()
875 LogRes(res, err, start - end)
876
877 return res, err
878}
879
880client := openai.NewClient(
881 option.WithMiddleware(Logger),
882)
883```
884
885When multiple middlewares are provided as variadic arguments, the middlewares
886are applied left to right. If `option.WithMiddleware` is given
887multiple times, for example first in the client then the method, the
888middleware in the client will run first and the middleware given in the method
889will run next.
890
891You may also replace the default `http.Client` with
892`option.WithHTTPClient(client)`. Only one http client is
893accepted (this overwrites any previous client) and receives requests after any
894middleware has been applied.
895
896## Microsoft Azure OpenAI
897
898To use this library with [Azure OpenAI]https://learn.microsoft.com/azure/ai-services/openai/overview),
899use the option.RequestOption functions in the `azure` package.
900
901```go
902package main
903
904import (
905 "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
906 "github.com/openai/openai-go"
907 "github.com/openai/openai-go/azure"
908)
909
910func main() {
911 const azureOpenAIEndpoint = "https://<azure-openai-resource>.openai.azure.com"
912
913 // The latest API versions, including previews, can be found here:
914 // ttps://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versionng
915 const azureOpenAIAPIVersion = "2024-06-01"
916
917 tokenCredential, err := azidentity.NewDefaultAzureCredential(nil)
918
919 if err != nil {
920 fmt.Printf("Failed to create the DefaultAzureCredential: %s", err)
921 os.Exit(1)
922 }
923
924 client := openai.NewClient(
925 azure.WithEndpoint(azureOpenAIEndpoint, azureOpenAIAPIVersion),
926
927 // Choose between authenticating using a TokenCredential or an API Key
928 azure.WithTokenCredential(tokenCredential),
929 // or azure.WithAPIKey(azureOpenAIAPIKey),
930 )
931}
932```
933
934
935## Semantic versioning
936
937This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
938
9391. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
9402. Changes that we do not expect to impact the vast majority of users in practice.
941
942We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
943
944We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-go/issues) with questions, bugs, or suggestions.
945
946## Contributing
947
948See [the contributing documentation](./CONTRIBUTING.md).