README.md

  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).