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
6API](https://platform.openai.com/docs) from applications written in Go. The full API of this library can be found in [api.md](api.md).
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@v0.1.0-beta.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]`. Use the provided constructors set `param.Opt[T]` fields such as `openai.String(string)`, `openai.Int(int64)`, etc.
302
303Optional primitives, maps, slices and structs and string enums (represented as `string`) always feature the
304tag <code>\`json:"...,omitzero"\`</code>. Their zero values are considered omitted.
305
306Any non-nil slice of length zero will serialize as an empty JSON array, `"[]"`. Similarly, any non-nil map with length zero with serialize as an empty JSON object, `"{}"`.
307
308To send `null` instead of an `param.Opt[T]`, use `param.NullOpt[T]()`.
309To send `null` instead of a struct, use `param.NullObj[T]()`, where `T` is a struct.
310To send a custom value instead of a struct, use `param.OverrideObj[T](value)`.
311
312To override request structs contain a `.WithExtraFields(map[string]any)` method which can be used to
313send non-conforming fields in the request body. Extra fields take higher precedence than normal
314fields.
315
316```go
317params := FooParams{
318 ID: "id_xxx", // required property
319 Name: openai.String("hello"), // optional property
320 Description: param.NullOpt[string](), // explicit null property
321
322 Point: openai.Point{
323 X: 0, // required field will serialize as 0
324 Y: openai.Int(1), // optional field will serialize as 1
325 // ... omitted non-required fields will not be serialized
326 }),
327
328 Origin: openai.Origin{}, // the zero value of [Origin] is considered omitted
329}
330
331// In cases where the API specifies a given type,
332// but you want to send something else, use [WithExtraFields]:
333params.WithExtraFields(map[string]any{
334 "x": 0.01, // send "x" as a float instead of int
335})
336
337// Send a number instead of an object
338custom := param.OverrideObj[openai.FooParams](12)
339```
340
341When available, use the `.IsPresent()` method to check if an optional parameter is not omitted or `null`.
342Otherwise, the `param.IsOmitted(any)` function can confirm the presence of any `omitzero` field.
343
344### Request unions
345
346Unions are represented as a struct with fields prefixed by "Of" for each of it's variants,
347only one field can be non-zero. The non-zero field will be serialized.
348
349Sub-properties of the union can be accessed via methods on the union struct.
350These methods return a mutable pointer to the underlying data, if present.
351
352```go
353// Only one field can be non-zero, use param.IsOmitted() to check if a field is set
354type AnimalUnionParam struct {
355 OfCat *Cat `json:",omitzero,inline`
356 OfDog *Dog `json:",omitzero,inline`
357}
358
359animal := AnimalUnionParam{
360 OfCat: &Cat{
361 Name: "Whiskers",
362 Owner: PersonParam{
363 Address: AddressParam{Street: "3333 Coyote Hill Rd", Zip: 0},
364 },
365 },
366}
367
368// Mutating a field
369if address := animal.GetOwner().GetAddress(); address != nil {
370 address.ZipCode = 94304
371}
372```
373
374### Response objects
375
376All fields in response structs are value types (not pointers or wrappers).
377
378If a given field is `null`, not present, or invalid, the corresponding field
379will simply be its zero value.
380
381All response structs also include a special `JSON` field, containing more detailed
382information about each property, which you can use like so:
383
384```go
385if res.Name == "" {
386 // true if `"name"` was unmarshalled successfully
387 res.JSON.Name.IsPresent()
388
389 res.JSON.Name.IsExplicitNull() // true if `"name"` is explicitly null
390 res.JSON.Name.Raw() == "" // true if `"name"` field does not exist
391
392 // When the API returns data that cannot be coerced to the expected type:
393 if !res.JSON.Name.IsPresent() && res.JSON.Name.Raw() != "" {
394 raw := res.JSON.Name.Raw()
395
396 legacyName := struct{
397 First string `json:"first"`
398 Last string `json:"last"`
399 }{}
400 json.Unmarshal([]byte(raw), &legacyName)
401 name = legacyName.First + " " + legacyName.Last
402 }
403}
404```
405
406These `.JSON` structs also include an `ExtraFields` map containing
407any properties in the json response that were not specified
408in the struct. This can be useful for API features not yet
409present in the SDK.
410
411```go
412body := res.JSON.ExtraFields["my_unexpected_field"].Raw()
413```
414
415### Response Unions
416
417In responses, unions are represented by a flattened struct containing all possible fields from each of the
418object variants.
419To convert it to a variant use the `.AsFooVariant()` method or the `.AsAny()` method if present.
420
421If a response value union contains primitive values, primitive fields will be alongside
422the properties but prefixed with `Of` and feature the tag `json:"...,inline"`.
423
424```go
425type AnimalUnion struct {
426 OfString string `json:",inline"`
427 Name string `json:"name"`
428 Owner Person `json:"owner"`
429 // ...
430 JSON struct {
431 OfString resp.Field
432 Name resp.Field
433 Owner resp.Field
434 // ...
435 }
436}
437
438// If animal variant
439if animal.Owner.Address.JSON.ZipCode == "" {
440 panic("missing zip code")
441}
442
443// If string variant
444if !animal.OfString == "" {
445 panic("expected a name")
446}
447
448// Switch on the variant
449switch variant := animalOrName.AsAny().(type) {
450case string:
451case Dog:
452case Cat:
453default:
454 panic("unexpected type")
455}
456```
457
458### RequestOptions
459
460This library uses the functional options pattern. Functions defined in the
461`option` package return a `RequestOption`, which is a closure that mutates a
462`RequestConfig`. These options can be supplied to the client or at individual
463requests. For example:
464
465```go
466client := openai.NewClient(
467 // Adds a header to every request made by the client
468 option.WithHeader("X-Some-Header", "custom_header_info"),
469)
470
471client.Chat.Completions.New(context.TODO(), ...,
472 // Override the header
473 option.WithHeader("X-Some-Header", "some_other_custom_header_info"),
474 // Add an undocumented field to the request body, using sjson syntax
475 option.WithJSONSet("some.json.path", map[string]string{"my": "object"}),
476)
477```
478
479See the [full list of request options](https://pkg.go.dev/github.com/openai/openai-go/option).
480
481### Pagination
482
483This library provides some conveniences for working with paginated list endpoints.
484
485You can use `.ListAutoPaging()` methods to iterate through items across all pages:
486
487```go
488iter := client.FineTuning.Jobs.ListAutoPaging(context.TODO(), openai.FineTuningJobListParams{
489 Limit: openai.Int(20),
490})
491// Automatically fetches more pages as needed.
492for iter.Next() {
493 fineTuningJob := iter.Current()
494 fmt.Printf("%+v\n", fineTuningJob)
495}
496if err := iter.Err(); err != nil {
497 panic(err.Error())
498}
499```
500
501Or you can use simple `.List()` methods to fetch a single page and receive a standard response object
502with additional helper methods like `.GetNextPage()`, e.g.:
503
504```go
505page, err := client.FineTuning.Jobs.List(context.TODO(), openai.FineTuningJobListParams{
506 Limit: openai.Int(20),
507})
508for page != nil {
509 for _, job := range page.Data {
510 fmt.Printf("%+v\n", job)
511 }
512 page, err = page.GetNextPage()
513}
514if err != nil {
515 panic(err.Error())
516}
517```
518
519### Errors
520
521When the API returns a non-success status code, we return an error with type
522`*openai.Error`. This contains the `StatusCode`, `*http.Request`, and
523`*http.Response` values of the request, as well as the JSON of the error body
524(much like other response objects in the SDK).
525
526To handle errors, we recommend that you use the `errors.As` pattern:
527
528```go
529_, err := client.FineTuning.Jobs.New(context.TODO(), openai.FineTuningJobNewParams{
530 Model: "babbage-002",
531 TrainingFile: "file-abc123",
532})
533if err != nil {
534 var apierr *openai.Error
535 if errors.As(err, &apierr) {
536 println(string(apierr.DumpRequest(true))) // Prints the serialized HTTP request
537 println(string(apierr.DumpResponse(true))) // Prints the serialized HTTP response
538 }
539 panic(err.Error()) // GET "/fine_tuning/jobs": 400 Bad Request { ... }
540}
541```
542
543When other errors occur, they are returned unwrapped; for example,
544if HTTP transport fails, you might receive `*url.Error` wrapping `*net.OpError`.
545
546### Timeouts
547
548Requests do not time out by default; use context to configure a timeout for a request lifecycle.
549
550Note that if a request is [retried](#retries), the context timeout does not start over.
551To set a per-retry timeout, use `option.WithRequestTimeout()`.
552
553```go
554// This sets the timeout for the request, including all the retries.
555ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
556defer cancel()
557client.Chat.Completions.New(
558 ctx,
559 openai.ChatCompletionNewParams{
560 Messages: []openai.ChatCompletionMessageParamUnion{{
561 OfUser: &openai.ChatCompletionUserMessageParam{
562 Content: openai.ChatCompletionUserMessageParamContentUnion{
563 OfString: openai.String("How can I list all files in a directory using Python?"),
564 },
565 },
566 }},
567 Model: shared.ChatModelO3Mini,
568 },
569 // This sets the per-retry timeout
570 option.WithRequestTimeout(20*time.Second),
571)
572```
573
574### File uploads
575
576Request parameters that correspond to file uploads in multipart requests are typed as
577`io.Reader`. The contents of the `io.Reader` will by default be sent as a multipart form
578part with the file name of "anonymous_file" and content-type of "application/octet-stream".
579
580The file name and content-type can be customized by implementing `Name() string` or `ContentType()
581string` on the run-time type of `io.Reader`. Note that `os.File` implements `Name() string`, so a
582file returned by `os.Open` will be sent with the file name on disk.
583
584We also provide a helper `openai.FileParam(reader io.Reader, filename string, contentType string)`
585which can be used to wrap any `io.Reader` with the appropriate file name and content type.
586
587```go
588// A file from the file system
589file, err := os.Open("input.jsonl")
590openai.FileNewParams{
591 File: openai.F[io.Reader](file),
592 Purpose: openai.FilePurposeFineTune,
593}
594
595// A file from a string
596openai.FileNewParams{
597 File: openai.F[io.Reader](strings.NewReader("my file contents")),
598 Purpose: openai.FilePurposeFineTune,
599}
600
601// With a custom filename and contentType
602openai.FileNewParams{
603 File: openai.FileParam(strings.NewReader(`{"hello": "foo"}`), "file.go", "application/json"),
604 Purpose: openai.FilePurposeFineTune,
605}
606```
607
608### Retries
609
610Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
611We retry by default all connection errors, 408 Request Timeout, 409 Conflict, 429 Rate Limit,
612and >=500 Internal errors.
613
614You can use the `WithMaxRetries` option to configure or disable this:
615
616```go
617// Configure the default for all requests:
618client := openai.NewClient(
619 option.WithMaxRetries(0), // default is 2
620)
621
622// Override per-request:
623client.Chat.Completions.New(
624 context.TODO(),
625 openai.ChatCompletionNewParams{
626 Messages: []openai.ChatCompletionMessageParamUnion{{
627 OfUser: &openai.ChatCompletionUserMessageParam{
628 Content: openai.ChatCompletionUserMessageParamContentUnion{
629 OfString: openai.String("How can I get the name of the current day in JavaScript?"),
630 },
631 },
632 }},
633 Model: shared.ChatModelO3Mini,
634 },
635 option.WithMaxRetries(5),
636)
637```
638
639### Accessing raw response data (e.g. response headers)
640
641You can access the raw HTTP response data by using the `option.WithResponseInto()` request option. This is useful when
642you need to examine response headers, status codes, or other details.
643
644```go
645// Create a variable to store the HTTP response
646var response *http.Response
647chatCompletion, err := client.Chat.Completions.New(
648 context.TODO(),
649 openai.ChatCompletionNewParams{
650 Messages: []openai.ChatCompletionMessageParamUnion{{
651 OfUser: &openai.ChatCompletionUserMessageParam{
652 Content: openai.ChatCompletionUserMessageParamContentUnion{
653 OfString: openai.String("Say this is a test"),
654 },
655 },
656 }},
657 Model: shared.ChatModelO3Mini,
658 },
659 option.WithResponseInto(&response),
660)
661if err != nil {
662 // handle error
663}
664fmt.Printf("%+v\n", chatCompletion)
665
666fmt.Printf("Status Code: %d\n", response.StatusCode)
667fmt.Printf("Headers: %+#v\n", response.Header)
668```
669
670### Making custom/undocumented requests
671
672This library is typed for convenient access to the documented API. If you need to access undocumented
673endpoints, params, or response properties, the library can still be used.
674
675#### Undocumented endpoints
676
677To make requests to undocumented endpoints, you can use `client.Get`, `client.Post`, and other HTTP verbs.
678`RequestOptions` on the client, such as retries, will be respected when making these requests.
679
680```go
681var (
682 // params can be an io.Reader, a []byte, an encoding/json serializable object,
683 // or a "β¦Params" struct defined in this library.
684 params map[string]interface{}
685
686 // result can be an []byte, *http.Response, a encoding/json deserializable object,
687 // or a model defined in this library.
688 result *http.Response
689)
690err := client.Post(context.Background(), "/unspecified", params, &result)
691if err != nil {
692 β¦
693}
694```
695
696#### Undocumented request params
697
698To make requests using undocumented parameters, you may use either the `option.WithQuerySet()`
699or the `option.WithJSONSet()` methods.
700
701```go
702params := FooNewParams{
703 ID: "id_xxxx",
704 Data: FooNewParamsData{
705 FirstName: openai.String("John"),
706 },
707}
708client.Foo.New(context.Background(), params, option.WithJSONSet("data.last_name", "Doe"))
709```
710
711#### Undocumented response properties
712
713To access undocumented response properties, you may either access the raw JSON of the response as a string
714with `result.JSON.RawJSON()`, or get the raw JSON of a particular field on the result with
715`result.JSON.Foo.Raw()`.
716
717Any 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`.
718
719### Middleware
720
721We provide `option.WithMiddleware` which applies the given
722middleware to requests.
723
724```go
725func Logger(req *http.Request, next option.MiddlewareNext) (res *http.Response, err error) {
726 // Before the request
727 start := time.Now()
728 LogReq(req)
729
730 // Forward the request to the next handler
731 res, err = next(req)
732
733 // Handle stuff after the request
734 end := time.Now()
735 LogRes(res, err, start - end)
736
737 return res, err
738}
739
740client := openai.NewClient(
741 option.WithMiddleware(Logger),
742)
743```
744
745When multiple middlewares are provided as variadic arguments, the middlewares
746are applied left to right. If `option.WithMiddleware` is given
747multiple times, for example first in the client then the method, the
748middleware in the client will run first and the middleware given in the method
749will run next.
750
751You may also replace the default `http.Client` with
752`option.WithHTTPClient(client)`. Only one http client is
753accepted (this overwrites any previous client) and receives requests after any
754middleware has been applied.
755
756## Microsoft Azure OpenAI
757
758To use this library with [Azure OpenAI]https://learn.microsoft.com/azure/ai-services/openai/overview),
759use the option.RequestOption functions in the `azure` package.
760
761```go
762package main
763
764import (
765 "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
766 "github.com/openai/openai-go"
767 "github.com/openai/openai-go/azure"
768)
769
770func main() {
771 const azureOpenAIEndpoint = "https://<azure-openai-resource>.openai.azure.com"
772
773 // The latest API versions, including previews, can be found here:
774 // ttps://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versionng
775 const azureOpenAIAPIVersion = "2024-06-01"
776
777 tokenCredential, err := azidentity.NewDefaultAzureCredential(nil)
778
779 if err != nil {
780 fmt.Printf("Failed to create the DefaultAzureCredential: %s", err)
781 os.Exit(1)
782 }
783
784 client := openai.NewClient(
785 azure.WithEndpoint(azureOpenAIEndpoint, azureOpenAIAPIVersion),
786
787 // Choose between authenticating using a TokenCredential or an API Key
788 azure.WithTokenCredential(tokenCredential),
789 // or azure.WithAPIKey(azureOpenAIAPIKey),
790 )
791}
792```
793
794
795## Semantic versioning
796
797This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
798
7991. 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.)_
8002. Changes that we do not expect to impact the vast majority of users in practice.
801
802We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
803
804We are keen for your feedback; please open an [issue](https://www.github.com/openai/openai-go/issues) with questions, bugs, or suggestions.
805
806## Contributing
807
808See [the contributing documentation](./CONTRIBUTING.md).