Skip to content

Commit 761c226

Browse files
committed
feat(chat): add reasoning field fallback to reasoning_content
Add support for falling back to the reasoning field when reasoning_content is empty in chat completion messages and stream deltas. This provides backward compatibility for older API versions that use the reasoning field.
1 parent 03cefca commit 761c226

3 files changed

Lines changed: 92 additions & 7 deletions

File tree

chat.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ func (m *ChatCompletionMessage) UnmarshalJSON(bs []byte) error {
231231
MultiContent []ChatMessagePart
232232
Name string `json:"name,omitempty"`
233233
ReasoningContent string `json:"reasoning_content,omitempty"`
234+
Reasoning string `json:"reasoning,omitempty"`
234235
FunctionCall *FunctionCall `json:"function_call,omitempty"`
235236
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
236237
ToolCallID string `json:"tool_call_id,omitempty"`
@@ -248,6 +249,9 @@ func (m *ChatCompletionMessage) UnmarshalJSON(bs []byte) error {
248249
}
249250
m.Name = msg.Name
250251
m.ReasoningContent = msg.ReasoningContent
252+
if m.ReasoningContent == "" && msg.Reasoning != "" {
253+
m.ReasoningContent = msg.Reasoning
254+
}
251255
m.FunctionCall = msg.FunctionCall
252256
m.ToolCalls = msg.ToolCalls
253257
m.ToolCallID = msg.ToolCallID
@@ -262,6 +266,7 @@ func (m *ChatCompletionMessage) UnmarshalJSON(bs []byte) error {
262266
MultiContent []ChatMessagePart `json:"content"`
263267
Name string `json:"name,omitempty"`
264268
ReasoningContent string `json:"reasoning_content,omitempty"`
269+
Reasoning string `json:"reasoning,omitempty"`
265270
FunctionCall *FunctionCall `json:"function_call,omitempty"`
266271
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
267272
ToolCallID string `json:"tool_call_id,omitempty"`
@@ -280,6 +285,9 @@ func (m *ChatCompletionMessage) UnmarshalJSON(bs []byte) error {
280285
}
281286
m.Name = multiMsg.Name
282287
m.ReasoningContent = multiMsg.ReasoningContent
288+
if m.ReasoningContent == "" && multiMsg.Reasoning != "" {
289+
m.ReasoningContent = multiMsg.Reasoning
290+
}
283291
m.FunctionCall = multiMsg.FunctionCall
284292
m.ToolCalls = multiMsg.ToolCalls
285293
m.ToolCallID = multiMsg.ToolCallID

chat_stream.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func (d *ChatCompletionStreamChoiceDelta) UnmarshalJSON(bs []byte) error {
3232
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
3333
Refusal string `json:"refusal,omitempty"`
3434
ReasoningContent string `json:"reasoning_content,omitempty"`
35+
Reasoning string `json:"reasoning,omitempty"`
3536
Audio *ChatMessageAudio `json:"audio,omitempty"`
3637
ExtraContent map[string]any `json:"extra_content,omitempty"`
3738
}
@@ -43,12 +44,16 @@ func (d *ChatCompletionStreamChoiceDelta) UnmarshalJSON(bs []byte) error {
4344

4445
// No content — simply copy scalar fields.
4546
if len(p.Content) == 0 {
47+
reasoningContent := p.ReasoningContent
48+
if reasoningContent == "" && p.Reasoning != "" {
49+
reasoningContent = p.Reasoning
50+
}
4651
*d = ChatCompletionStreamChoiceDelta{
4752
Role: p.Role,
4853
FunctionCall: p.FunctionCall,
4954
ToolCalls: p.ToolCalls,
5055
Refusal: p.Refusal,
51-
ReasoningContent: p.ReasoningContent,
56+
ReasoningContent: reasoningContent,
5257
Audio: p.Audio,
5358
ExtraContent: p.ExtraContent,
5459
}
@@ -62,21 +67,26 @@ func (d *ChatCompletionStreamChoiceDelta) UnmarshalJSON(bs []byte) error {
6267
Role string `json:"role,omitempty"`
6368
FunctionCall *FunctionCall `json:"function_call,omitempty"`
6469
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
65-
Refusal string `json:"refusal,omitempty"`
66-
ReasoningContent string `json:"reasoning_content,omitempty"`
67-
Audio *ChatMessageAudio `json:"audio,omitempty"`
68-
ExtraContent map[string]any `json:"extra_content,omitempty"`
70+
Refusal string `json:"refusal,omitempty"`
71+
ReasoningContent string `json:"reasoning_content,omitempty"`
72+
Reasoning string `json:"reasoning,omitempty"`
73+
Audio *ChatMessageAudio `json:"audio,omitempty"`
74+
ExtraContent map[string]any `json:"extra_content,omitempty"`
6975
}
7076
if err := json.Unmarshal(bs, &r); err != nil {
7177
return err
7278
}
79+
reasoningContent := r.ReasoningContent
80+
if reasoningContent == "" && r.Reasoning != "" {
81+
reasoningContent = r.Reasoning
82+
}
7383
*d = ChatCompletionStreamChoiceDelta{
7484
Content: r.Content,
7585
Role: r.Role,
7686
FunctionCall: r.FunctionCall,
7787
ToolCalls: r.ToolCalls,
7888
Refusal: r.Refusal,
79-
ReasoningContent: r.ReasoningContent,
89+
ReasoningContent: reasoningContent,
8090
Audio: r.Audio,
8191
ExtraContent: r.ExtraContent,
8292
}
@@ -91,20 +101,25 @@ func (d *ChatCompletionStreamChoiceDelta) UnmarshalJSON(bs []byte) error {
91101
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
92102
Refusal string `json:"refusal,omitempty"`
93103
ReasoningContent string `json:"reasoning_content,omitempty"`
104+
Reasoning string `json:"reasoning,omitempty"`
94105
Audio *ChatMessageAudio `json:"audio,omitempty"`
95106
ExtraContent map[string]any `json:"extra_content,omitempty"`
96107
}
97108
if err := json.Unmarshal(bs, &a); err != nil {
98109
return err
99110
}
111+
reasoningContent := a.ReasoningContent
112+
if reasoningContent == "" && a.Reasoning != "" {
113+
reasoningContent = a.Reasoning
114+
}
100115
*d = ChatCompletionStreamChoiceDelta{
101116
Content: concatTextParts(a.MultiContent),
102117
MultiContent: a.MultiContent,
103118
Role: a.Role,
104119
FunctionCall: a.FunctionCall,
105120
ToolCalls: a.ToolCalls,
106121
Refusal: a.Refusal,
107-
ReasoningContent: a.ReasoningContent,
122+
ReasoningContent: reasoningContent,
108123
Audio: a.Audio,
109124
ExtraContent: a.ExtraContent,
110125
}

chat_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,37 @@ func TestChatCompletionStreamChoiceDeltaAudio(t *testing.T) {
12901290
}
12911291
}
12921292

1293+
func TestChatCompletionStreamChoiceDeltaReasoningFallback(t *testing.T) {
1294+
tests := []struct {
1295+
name string
1296+
raw string
1297+
reasoningContent string
1298+
}{
1299+
{
1300+
name: "reasoning_content wins",
1301+
raw: `{"reasoning_content":"rc","reasoning":"r"}`,
1302+
reasoningContent: "rc",
1303+
},
1304+
{
1305+
name: "reasoning fallback",
1306+
raw: `{"reasoning":"r"}`,
1307+
reasoningContent: "r",
1308+
},
1309+
}
1310+
1311+
for _, tt := range tests {
1312+
t.Run(tt.name, func(t *testing.T) {
1313+
var d openai.ChatCompletionStreamChoiceDelta
1314+
if err := json.Unmarshal([]byte(tt.raw), &d); err != nil {
1315+
t.Fatalf("unmarshal failed: %v", err)
1316+
}
1317+
if d.ReasoningContent != tt.reasoningContent {
1318+
t.Fatalf("expected reasoning content %q, got %q", tt.reasoningContent, d.ReasoningContent)
1319+
}
1320+
})
1321+
}
1322+
}
1323+
12931324
func TestChatCompletionMessageFlattenContent(t *testing.T) {
12941325
raw := []byte(`{
12951326
"role":"assistant",
@@ -1310,6 +1341,37 @@ func TestChatCompletionMessageFlattenContent(t *testing.T) {
13101341
}
13111342
}
13121343

1344+
func TestChatCompletionMessageReasoningFallback(t *testing.T) {
1345+
tests := []struct {
1346+
name string
1347+
raw string
1348+
reasoningContent string
1349+
}{
1350+
{
1351+
name: "reasoning_content wins",
1352+
raw: `{"role":"assistant","reasoning_content":"rc","reasoning":"r"}`,
1353+
reasoningContent: "rc",
1354+
},
1355+
{
1356+
name: "reasoning fallback",
1357+
raw: `{"role":"assistant","reasoning":"r"}`,
1358+
reasoningContent: "r",
1359+
},
1360+
}
1361+
1362+
for _, tt := range tests {
1363+
t.Run(tt.name, func(t *testing.T) {
1364+
var msg openai.ChatCompletionMessage
1365+
if err := json.Unmarshal([]byte(tt.raw), &msg); err != nil {
1366+
t.Fatalf("unmarshal failed: %v", err)
1367+
}
1368+
if msg.ReasoningContent != tt.reasoningContent {
1369+
t.Fatalf("expected reasoning content %q, got %q", tt.reasoningContent, msg.ReasoningContent)
1370+
}
1371+
})
1372+
}
1373+
}
1374+
13131375
func TestChatCompletionMessageInputAudioSerialization(t *testing.T) {
13141376
msg := openai.ChatCompletionMessage{
13151377
Role: openai.ChatMessageRoleUser,

0 commit comments

Comments
 (0)