Skip to content

Commit 19e32fa

Browse files
docs: add migration guide (#302)
1 parent 2319b01 commit 19e32fa

File tree

1 file changed

+250
-0
lines changed

1 file changed

+250
-0
lines changed

Diff for: MIGRATION.md

+250
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
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+
5+
This SDK includes breaking changes from the previous version to improve the ergonomics of constructing parameters and accessing responses.
6+
7+
# Request parameters
8+
9+
## Required primitives parameters serialize their zero values (`string`, `int64`, etc.)
10+
11+
> [!CAUTION] > **This change can cause new behavior in existing code, without compiler warnings.**
12+
13+
```diff
14+
type FooParams struct {
15+
- Age param.Field[int64] `json:"age,required"`
16+
- Name param.Field[string] `json:"name"`
17+
+ Age int64 `json:"age,required"`
18+
+ Name param.Opt[string] `json:"name,omitzero"`
19+
}
20+
```
21+
22+
<table>
23+
<tr>
24+
<th>Previous</th>
25+
<th>New</th>
26+
</tr>
27+
<tr>
28+
<td>
29+
30+
```go
31+
_ = FooParams{
32+
Name: openai.String("Jerry")
33+
}
34+
`{"name": "Jerry"}` // (after serialization)
35+
```
36+
37+
</td>
38+
<td>
39+
40+
```go
41+
_ = FooParams{
42+
Name: openai.String("Jerry")
43+
}
44+
`{"name": "Jerry", "age": 0}` // <== Notice the age field
45+
```
46+
47+
</td>
48+
</tr>
49+
</table>
50+
51+
The required field `"age"` is now present as `0`. Required primitive fields without the <code>\`json:"...,omitzero"\`</code> struct tag
52+
are always serialized, including their zero values.
53+
54+
## Transition from `param.Field[T]` to `omitzero`
55+
56+
The new 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].
57+
58+
`omitzero` is used for structs, slices, maps, string enums, and optional primitive types wrapped in `param.Opt[T]` (e.g. `param.Opt[string]`).
59+
60+
**Fields of a request struct:**
61+
62+
```diff
63+
type FooParams struct {
64+
- RequiredString param.Field[string] `json:"required_string,required"`
65+
+ RequiredString string `json:"required_string,required"`
66+
67+
- OptionalString param.Field[string] `json:"optional_string"`
68+
+ OptionalString param.Opt[string] `json:"optional_string,omitzero"`
69+
70+
- Array param.Field[[]BarParam] `json"array"`
71+
+ Array []BarParam `json"array,omitzero"`
72+
73+
- Map param.Field[map[string]BarParam] `json"map"`
74+
+ Map map[string]BarParam `json"map,omitzero"`
75+
76+
- RequiredObject param.Field[BarParam] `json:"required_object,required"`
77+
+ RequiredObject BarParam `json:"required_object,omitzero,required"`
78+
79+
- OptionalObject param.Field[BarParam] `json:"optional_object"`
80+
+ OptionalObject BarParam `json:"optional_object,omitzero"`
81+
82+
- StringEnum param.Field[BazEnum] `json:"string_enum"`
83+
+ StringEnum BazEnum `json:"string_enum,omitzero"`
84+
}
85+
```
86+
87+
**Previous vs New SDK: Constructing a request**
88+
89+
```diff
90+
foo = FooParams{
91+
- RequiredString: openai.String("hello"),
92+
+ RequiredString: "hello",
93+
94+
- OptionalString: openai.String("hi"),
95+
+ OptionalString: openai.String("hi"),
96+
97+
- Array: openai.F([]BarParam{
98+
- BarParam{Prop: ... }
99+
- }),
100+
+ Array: []BarParam{
101+
+ BarParam{Prop: ... }
102+
+ },
103+
104+
- RequiredObject: openai.F(BarParam{ ... }),
105+
+ RequiredObject: BarParam{ ... },
106+
107+
- OptionalObject: openai.F(BarParam{ ... }),
108+
+ OptionalObject: BarParam{ ... },
109+
110+
- StringEnum: openai.F[BazEnum]("baz-ok"),
111+
+ StringEnum: "baz-ok",
112+
}
113+
```
114+
115+
`param.Opt[string]` can be constructed with `openai.String(string)`. Similar functions exist for other primitive
116+
types like `openai.Int(int)`, `openai.Bool(bool)`, etc.
117+
118+
## Request Unions: Removing interfaces and moving to structs
119+
120+
For a type `AnimalUnionParam` which could be either a `string | CatParam | DogParam`.
121+
122+
<table>
123+
<tr><th>Previous</th> <th>New</th></tr>
124+
<tr>
125+
<td>
126+
127+
```go
128+
type AnimalParam interface {
129+
ImplAnimalParam()
130+
}
131+
132+
func (Dog) ImplAnimalParam() {}
133+
func (Cat) ImplAnimalParam() {}
134+
```
135+
136+
</td>
137+
<td>
138+
139+
```go
140+
type AnimalUnionParam struct {
141+
OfCat *Cat `json:",omitzero,inline`
142+
OfDog *Dog `json:",omitzero,inline`
143+
}
144+
```
145+
146+
</td>
147+
</tr>
148+
149+
<tr style="background:rgb(209, 217, 224)">
150+
<td>
151+
152+
```go
153+
var dog AnimalParam = DogParam{
154+
Name: "spot", ...
155+
}
156+
var cat AnimalParam = CatParam{
157+
Name: "whiskers", ...
158+
}
159+
```
160+
161+
</td>
162+
<td>
163+
164+
```go
165+
dog := AnimalUnionParam{
166+
OfDog: &DogParam{Name: "spot", ... },
167+
}
168+
cat := AnimalUnionParam{
169+
OfCat: &CatParam{Name: "whiskers", ... },
170+
}
171+
```
172+
173+
</td>
174+
</tr>
175+
176+
<tr>
177+
<td>
178+
179+
```go
180+
var name string
181+
switch v := animal.(type) {
182+
case Dog:
183+
name = v.Name
184+
case Cat:
185+
name = v.Name
186+
}
187+
```
188+
189+
</td>
190+
<td>
191+
192+
```go
193+
// Accessing fields
194+
var name *string = animal.GetName()
195+
```
196+
197+
</td>
198+
</tr>
199+
</table>
200+
201+
## Sending explicit `null` values
202+
203+
The old SDK had a function `param.Null[T]()` which could set `param.Field[T]` to `null`.
204+
205+
The new SDK uses `param.NullOpt[T]()` for to set a `param.Opt[T]` to `null`,
206+
and `param.NullObj[T]()` to set a param struct `T` to `null`.
207+
208+
```diff
209+
- var nullObj param.Field[BarParam] = param.Null[BarParam]()
210+
+ var nullObj BarParam = param.NullObj[BarParam]()
211+
212+
- var nullPrimitive param.Field[int64] = param.Null[int64]()
213+
+ var nullPrimitive param.Opt[int64] = param.NullOpt[int64]()
214+
```
215+
216+
## Sending custom values
217+
218+
The `openai.Raw[T](any)` function has been removed. All request structs now support a
219+
`.WithExtraField(map[string]any)` method to customize the fields.
220+
221+
```diff
222+
foo := FooParams{
223+
A: param.String("hello"),
224+
- B: param.Raw[string](12) // sending `12` instead of a string
225+
}
226+
+ foo.WithExtraFields(map[string]any{
227+
+ "B": 12,
228+
+ })
229+
```
230+
231+
# Response Properties
232+
233+
## Checking for presence of optional fields
234+
235+
The `.IsNull()` method has been changed to `.IsPresent()` to better reflect its behavior.
236+
237+
```diff
238+
- if !resp.Foo.JSON.Bar.IsNull() {
239+
+ if resp.Foo.JSON.Bar.IsPresent() {
240+
println("bar is present:", resp.Foo.Bar)
241+
}
242+
```
243+
244+
| Previous | New | Returns true for values |
245+
| -------------- | ------------------- | ----------------------- |
246+
| `.IsNull()` | `!.IsPresent()` | `null` or Omitted |
247+
| `.IsMissing()` | `.Raw() == ""` | Omitted |
248+
| `.Invalid()` | `.IsExplicitNull()` | `null` |
249+
250+
[^1]: The SDK doesn't require Go 1.24, despite supporting the `omitzero` feature

0 commit comments

Comments
 (0)