1# OpenAI Go Migration Guide
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
5This SDK includes breaking changes to improve the ergonomics of constructing parameters and accessing responses.
6
7To reduce verbosity, the `openai.F(...)` and `param.Field[T]` have been removed.
8All calls to `openai.F(...)` can be deleted.
9
10The SDK now uses the <code>\`json:"...,omitzero"\`</code> struct tag to omit fields. Nested structs, arrays and maps
11can be declared like normal.
12
13The old SDK used interfaces for unions in requests, which required
14a type assertion to access variants and fields. The new design uses
15structs with a field for each variant, wherein only one field can be set.
16These struct unions also expose 'Get' methods to access and mutate subfields
17which may be shared by multiple variants.
18
19# Request parameters
20
21## Required primitives parameters serialize their zero values (`string`, `int64`, etc.)
22
23> [!CAUTION]
24>
25> **This change can cause new behavior in existing code, without compiler warnings.**
26
27While migrating, ensure that all required fields are explicitly set. A required primitive
28field `Age` will use the <code>\`json:"age,required"\`</code> struct tag without `omitzero`.
29
30If a required primitive field is not set, the zero value will be serialized.
31This was not the case in with `param.Field[T]`.
32
33```diff
34type FooParams struct {
35- Age param.Field[int64] `json:"age,required"`
36- Name param.Field[string] `json:"name"`
37+ Age int64 `json:"age,required"` // <== Notice no omitzero
38+ Name param.Opt[string] `json:"name,omitzero"`
39}
40```
41
42<table>
43<tr>
44<th>Previous</th>
45<th>New</th>
46</tr>
47<tr>
48<td>
49
50```go
51_ = FooParams{
52 Name: openai.String("Jerry")
53}
54`{"name": "Jerry"}` // (after serialization)
55```
56
57</td>
58<td>
59
60```go
61_ = FooParams{
62 Name: openai.String("Jerry")
63}
64`{"name": "Jerry", "age": 0}` // <== Notice the age field
65```
66
67</td>
68</tr>
69</table>
70
71The required field `"age"` is now present as `0`. Fields without the <code>\`json:"...,omitzero"\`</code> struct tag
72are always serialized, including their zero values.
73
74## Transition from `param.Field[T]` to `omitzero`
75
76The `openai.F(...)` function and `param.Field[T]` type are no longer present in the new SDK.
77
78To represent omitted fields, the SDK uses <a href="https://pkg.go.dev/encoding/json#Marshal"><code>\`json:"...,omitzero"\`</code> semantics</a> from Go 1.24+ for JSON encoding[^1]. `omitzero` always omits fields
79with zero values.
80
81In all cases other than optional primitives, `openai.F()` can simply be removed.
82For optional primitive types, such as `param.Opt[string]`, you can use `openai.String(string)` to construct the value.
83Similar functions exist for other primitive types like `openai.Int(int)`, `openai.Bool(bool)`, etc.
84
85`omitzero` is used for fields whose type is either a struct, slice, map, string enum,
86or wrapped optional primitive (e.g. `param.Opt[T]`). Required primitive fields don't use `omitzero`.
87
88**Example User Code: Constructing a request**
89
90```diff
91foo = FooParams{
92- RequiredString: openai.String("hello"),
93+ RequiredString: "hello",
94
95- OptionalString: openai.String("hi"),
96+ OptionalString: openai.String("hi"),
97
98- Array: openai.F([]BarParam{
99- BarParam{Prop: ... }
100- }),
101+ Array: []BarParam{
102+ BarParam{Prop: ... }
103+ },
104
105- RequiredObject: openai.F(BarParam{ ... }),
106+ RequiredObject: BarParam{ ... },
107
108- OptionalObject: openai.F(BarParam{ ... }),
109+ OptionalObject: BarParam{ ... },
110
111- StringEnum: openai.F[BazEnum]("baz-ok"),
112+ StringEnum: "baz-ok",
113}
114```
115
116**Internal SDK Code: Fields of a request struct:**
117
118```diff
119type FooParams struct {
120- RequiredString param.Field[string] `json:"required_string,required"`
121+ RequiredString string `json:"required_string,required"`
122
123- OptionalString param.Field[string] `json:"optional_string"`
124+ OptionalString param.Opt[string] `json:"optional_string,omitzero"`
125
126- Array param.Field[[]BarParam] `json"array"`
127+ Array []BarParam `json"array,omitzero"`
128
129- Map param.Field[map[string]BarParam] `json"map"`
130+ Map map[string]BarParam `json"map,omitzero"`
131
132- RequiredObject param.Field[BarParam] `json:"required_object,required"`
133+ RequiredObject BarParam `json:"required_object,omitzero,required"`
134
135- OptionalObject param.Field[BarParam] `json:"optional_object"`
136+ OptionalObject BarParam `json:"optional_object,omitzero"`
137
138- StringEnum param.Field[BazEnum] `json:"string_enum"`
139+ StringEnum BazEnum `json:"string_enum,omitzero"`
140}
141```
142
143## Request Unions: Removing interfaces and moving to structs
144
145For a type `AnimalUnionParam` which could be either a `CatParam | DogParam`.
146
147<table>
148<tr><th>Previous</th> <th>New</th></tr>
149<tr>
150<td>
151
152```go
153type AnimalParam interface {
154 ImplAnimalParam()
155}
156
157func (Dog) ImplAnimalParam() {}
158func (Cat) ImplAnimalParam() {}
159```
160
161</td>
162<td>
163
164```go
165type AnimalUnionParam struct {
166 OfCat *Cat `json:",omitzero,inline`
167 OfDog *Dog `json:",omitzero,inline`
168}
169```
170
171</td>
172</tr>
173
174<tr style="background:rgb(209, 217, 224)">
175<td>
176
177```go
178var dog AnimalParam = DogParam{
179 Name: "spot", ...
180}
181var cat AnimalParam = CatParam{
182 Name: "whiskers", ...
183}
184```
185
186</td>
187<td>
188
189```go
190dog := AnimalUnionParam{
191 OfDog: &DogParam{Name: "spot", ... },
192}
193cat := AnimalUnionParam{
194 OfCat: &CatParam{Name: "whiskers", ... },
195}
196```
197
198</td>
199</tr>
200
201<tr>
202<td>
203
204```go
205var name string
206switch v := animal.(type) {
207case Dog:
208 name = v.Name
209case Cat:
210 name = v.Name
211}
212```
213
214</td>
215<td>
216
217```go
218// Accessing fields
219var name *string = animal.GetName()
220```
221
222</td>
223</tr>
224</table>
225
226## Sending explicit `null` values
227
228The old SDK had a function `param.Null[T]()` which could set `param.Field[T]` to `null`.
229
230The new SDK uses `param.Null[T]()` for to set a `param.Opt[T]` to `null`,
231but `param.NullStruct[T]()` to set a param struct `T` to `null`.
232
233```diff
234- var nullPrimitive param.Field[int64] = param.Null[int64]()
235+ var nullPrimitive param.Opt[int64] = param.Null[int64]()
236
237- var nullStruct param.Field[BarParam] = param.Null[BarParam]()
238+ var nullStruct BarParam = param.NullStruct[BarParam]()
239```
240
241## Sending custom values
242
243The `openai.Raw[T](any)` function has been removed. All request structs now support a
244`.WithExtraField(map[string]any)` method to customize the fields.
245
246```diff
247foo := FooParams{
248 A: param.String("hello"),
249- B: param.Raw[string](12) // sending `12` instead of a string
250}
251+ foo.SetExtraFields(map[string]any{
252+ "B": 12,
253+ })
254```
255
256# Response Properties
257
258## Checking for presence of optional fields
259
260The `.IsNull()` method has been changed to `.Valid()` to better reflect its behavior.
261
262```diff
263- if !resp.Foo.JSON.Bar.IsNull() {
264+ if resp.Foo.JSON.Bar.Valid() {
265 println("bar is present:", resp.Foo.Bar)
266}
267```
268
269| Previous | New | Returns true for values |
270| -------------- | ------------------------ | ----------------------- |
271| `.IsNull()` | `!.Valid()` | `null` or Omitted |
272| `.IsMissing()` | `.Raw() == resp.Omitted` | Omitted |
273| | `.Raw() == resp.Null` |
274
275## Checking Raw JSON of a response
276
277The `.RawJSON()` method has moved to the parent of the `.JSON` property.
278
279```diff
280- resp.Foo.JSON.RawJSON()
281+ resp.Foo.RawJSON()
282```
283
284[^1]: The SDK doesn't require Go 1.24, despite supporting the `omitzero` feature