@@ -12,6 +12,10 @@ import (
1212 "github.com/tidwall/sjson"
1313)
1414
15+ type oaiToResponsesStateReasoning struct {
16+ ReasoningID string
17+ ReasoningData string
18+ }
1519type oaiToResponsesState struct {
1620 Seq int
1721 ResponseID string
@@ -23,6 +27,7 @@ type oaiToResponsesState struct {
2327 // Per-output message text buffers by index
2428 MsgTextBuf map [int ]* strings.Builder
2529 ReasoningBuf strings.Builder
30+ Reasonings []oaiToResponsesStateReasoning
2631 FuncArgsBuf map [int ]* strings.Builder // index -> args
2732 FuncNames map [int ]string // index -> name
2833 FuncCallIDs map [int ]string // index -> call_id
@@ -63,6 +68,7 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context,
6368 MsgItemDone : make (map [int ]bool ),
6469 FuncArgsDone : make (map [int ]bool ),
6570 FuncItemDone : make (map [int ]bool ),
71+ Reasonings : make ([]oaiToResponsesStateReasoning , 0 ),
6672 }
6773 }
6874 st := (* param ).(* oaiToResponsesState )
@@ -157,6 +163,31 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context,
157163 st .Started = true
158164 }
159165
166+ stopReasoning := func (text string ) {
167+ // Emit reasoning done events
168+ textDone := `{"type":"response.reasoning_summary_text.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"text":""}`
169+ textDone , _ = sjson .Set (textDone , "sequence_number" , nextSeq ())
170+ textDone , _ = sjson .Set (textDone , "item_id" , st .ReasoningID )
171+ textDone , _ = sjson .Set (textDone , "output_index" , st .ReasoningIndex )
172+ textDone , _ = sjson .Set (textDone , "text" , text )
173+ out = append (out , emitRespEvent ("response.reasoning_summary_text.done" , textDone ))
174+ partDone := `{"type":"response.reasoning_summary_part.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}`
175+ partDone , _ = sjson .Set (partDone , "sequence_number" , nextSeq ())
176+ partDone , _ = sjson .Set (partDone , "item_id" , st .ReasoningID )
177+ partDone , _ = sjson .Set (partDone , "output_index" , st .ReasoningIndex )
178+ partDone , _ = sjson .Set (partDone , "part.text" , text )
179+ out = append (out , emitRespEvent ("response.reasoning_summary_part.done" , partDone ))
180+ outputItemDone := `{"type":"response.output_item.done","item":{"id":"","type":"reasoning","encrypted_content":"","summary":[{"type":"summary_text","text":""}]},"output_index":0,"sequence_number":0}`
181+ outputItemDone , _ = sjson .Set (outputItemDone , "sequence_number" , nextSeq ())
182+ outputItemDone , _ = sjson .Set (outputItemDone , "item.id" , st .ReasoningID )
183+ outputItemDone , _ = sjson .Set (outputItemDone , "output_index" , st .ReasoningIndex )
184+ outputItemDone , _ = sjson .Set (outputItemDone , "item.summary.text" , text )
185+ out = append (out , emitRespEvent ("response.output_item.done" , outputItemDone ))
186+
187+ st .Reasonings = append (st .Reasonings , oaiToResponsesStateReasoning {ReasoningID : st .ReasoningID , ReasoningData : text })
188+ st .ReasoningID = ""
189+ }
190+
160191 // choices[].delta content / tool_calls / reasoning_content
161192 if choices := root .Get ("choices" ); choices .Exists () && choices .IsArray () {
162193 choices .ForEach (func (_ , choice gjson.Result ) bool {
@@ -165,6 +196,10 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context,
165196 if delta .Exists () {
166197 if c := delta .Get ("content" ); c .Exists () && c .String () != "" {
167198 // Ensure the message item and its first content part are announced before any text deltas
199+ if st .ReasoningID != "" {
200+ stopReasoning (st .ReasoningBuf .String ())
201+ st .ReasoningBuf .Reset ()
202+ }
168203 if ! st .MsgItemAdded [idx ] {
169204 item := `{"type":"response.output_item.added","sequence_number":0,"output_index":0,"item":{"id":"","type":"message","status":"in_progress","content":[],"role":"assistant"}}`
170205 item , _ = sjson .Set (item , "sequence_number" , nextSeq ())
@@ -226,6 +261,10 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context,
226261
227262 // tool calls
228263 if tcs := delta .Get ("tool_calls" ); tcs .Exists () && tcs .IsArray () {
264+ if st .ReasoningID != "" {
265+ stopReasoning (st .ReasoningBuf .String ())
266+ st .ReasoningBuf .Reset ()
267+ }
229268 // Before emitting any function events, if a message is open for this index,
230269 // close its text/content to match Codex expected ordering.
231270 if st .MsgItemAdded [idx ] && ! st .MsgItemDone [idx ] {
@@ -361,17 +400,8 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context,
361400 }
362401
363402 if st .ReasoningID != "" {
364- // Emit reasoning done events
365- textDone := `{"type":"response.reasoning_summary_text.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"text":""}`
366- textDone , _ = sjson .Set (textDone , "sequence_number" , nextSeq ())
367- textDone , _ = sjson .Set (textDone , "item_id" , st .ReasoningID )
368- textDone , _ = sjson .Set (textDone , "output_index" , st .ReasoningIndex )
369- out = append (out , emitRespEvent ("response.reasoning_summary_text.done" , textDone ))
370- partDone := `{"type":"response.reasoning_summary_part.done","sequence_number":0,"item_id":"","output_index":0,"summary_index":0,"part":{"type":"summary_text","text":""}}`
371- partDone , _ = sjson .Set (partDone , "sequence_number" , nextSeq ())
372- partDone , _ = sjson .Set (partDone , "item_id" , st .ReasoningID )
373- partDone , _ = sjson .Set (partDone , "output_index" , st .ReasoningIndex )
374- out = append (out , emitRespEvent ("response.reasoning_summary_part.done" , partDone ))
403+ stopReasoning (st .ReasoningBuf .String ())
404+ st .ReasoningBuf .Reset ()
375405 }
376406
377407 // Emit function call done events for any active function calls
@@ -485,11 +515,13 @@ func ConvertOpenAIChatCompletionsResponseToOpenAIResponses(ctx context.Context,
485515 }
486516 // Build response.output using aggregated buffers
487517 outputsWrapper := `{"arr":[]}`
488- if st .ReasoningBuf .Len () > 0 {
489- item := `{"id":"","type":"reasoning","summary":[{"type":"summary_text","text":""}]}`
490- item , _ = sjson .Set (item , "id" , st .ReasoningID )
491- item , _ = sjson .Set (item , "summary.0.text" , st .ReasoningBuf .String ())
492- outputsWrapper , _ = sjson .SetRaw (outputsWrapper , "arr.-1" , item )
518+ if len (st .Reasonings ) > 0 {
519+ for _ , r := range st .Reasonings {
520+ item := `{"id":"","type":"reasoning","summary":[{"type":"summary_text","text":""}]}`
521+ item , _ = sjson .Set (item , "id" , r .ReasoningID )
522+ item , _ = sjson .Set (item , "summary.0.text" , r .ReasoningData )
523+ outputsWrapper , _ = sjson .SetRaw (outputsWrapper , "arr.-1" , item )
524+ }
493525 }
494526 // Append message items in ascending index order
495527 if len (st .MsgItemAdded ) > 0 {
0 commit comments