@@ -1084,6 +1084,109 @@ describe("convertToOpenAiMessages", () => {
10841084 expect ( assistantMessage . reasoning_details [ 2 ] . data ) . toBe ( "encrypted_data" )
10851085 } )
10861086 } )
1087+
1088+ describe ( "reasoning_content round-trip for DeepSeek / Z.ai thinking mode" , ( ) => {
1089+ it ( "should pass through top-level reasoning_content on assistant messages" , ( ) => {
1090+ const anthropicMessages = [
1091+ {
1092+ role : "assistant" as const ,
1093+ content : "Here is my answer." ,
1094+ reasoning_content : "Let me think about this carefully..." ,
1095+ } ,
1096+ ] as any as Anthropic . Messages . MessageParam [ ]
1097+
1098+ const result = convertToOpenAiMessages ( anthropicMessages )
1099+
1100+ expect ( result ) . toHaveLength ( 1 )
1101+ expect ( ( result [ 0 ] as any ) . reasoning_content ) . toBe ( "Let me think about this carefully..." )
1102+ } )
1103+
1104+ it ( "should extract reasoning_content from reasoning content block" , ( ) => {
1105+ // buildCleanConversationHistory stores reasoning as a content block when preserveReasoning=true
1106+ const anthropicMessages = [
1107+ {
1108+ role : "assistant" as const ,
1109+ content : [
1110+ { type : "reasoning" , text : "Let me think..." , summary : [ ] } ,
1111+ { type : "text" , text : "My answer." } ,
1112+ ] ,
1113+ } ,
1114+ ] as any as Anthropic . Messages . MessageParam [ ]
1115+
1116+ const result = convertToOpenAiMessages ( anthropicMessages )
1117+
1118+ expect ( result ) . toHaveLength ( 1 )
1119+ const msg = result [ 0 ] as any
1120+ expect ( msg . reasoning_content ) . toBe ( "Let me think..." )
1121+ expect ( msg . content ) . toBe ( "My answer." )
1122+ } )
1123+
1124+ it ( "should extract reasoning_content from reasoning block alongside tool calls" , ( ) => {
1125+ // The critical case: DeepSeek thinking + tool call in the same turn.
1126+ // Without reasoning_content on the second request, DeepSeek returns 400:
1127+ // "The reasoning_content in the thinking mode must be passed back to the API."
1128+ const anthropicMessages = [
1129+ {
1130+ role : "assistant" as const ,
1131+ content : [
1132+ { type : "reasoning" , text : "I need to read a file." , summary : [ ] } ,
1133+ {
1134+ type : "tool_use" ,
1135+ id : "call_abc" ,
1136+ name : "read_file" ,
1137+ input : { path : "foo.txt" } ,
1138+ } ,
1139+ ] ,
1140+ } ,
1141+ ] as any as Anthropic . Messages . MessageParam [ ]
1142+
1143+ const result = convertToOpenAiMessages ( anthropicMessages )
1144+
1145+ expect ( result ) . toHaveLength ( 1 )
1146+ const msg = result [ 0 ] as any
1147+ expect ( msg . reasoning_content ) . toBe ( "I need to read a file." )
1148+ expect ( msg . tool_calls ) . toHaveLength ( 1 )
1149+ expect ( msg . tool_calls [ 0 ] . id ) . toBe ( "call_abc" )
1150+ } )
1151+
1152+ it ( "should prefer top-level reasoning_content over content block" , ( ) => {
1153+ const anthropicMessages = [
1154+ {
1155+ role : "assistant" as const ,
1156+ content : [
1157+ { type : "reasoning" , text : "block reasoning" , summary : [ ] } ,
1158+ { type : "text" , text : "answer" } ,
1159+ ] ,
1160+ reasoning_content : "top-level reasoning" ,
1161+ } ,
1162+ ] as any as Anthropic . Messages . MessageParam [ ]
1163+
1164+ const result = convertToOpenAiMessages ( anthropicMessages )
1165+
1166+ expect ( ( result [ 0 ] as any ) . reasoning_content ) . toBe ( "top-level reasoning" )
1167+ } )
1168+
1169+ it ( "should not set reasoning_content when there is none" , ( ) => {
1170+ const anthropicMessages : Anthropic . Messages . MessageParam [ ] = [
1171+ {
1172+ role : "assistant" ,
1173+ content : [
1174+ {
1175+ type : "tool_use" ,
1176+ id : "call_abc" ,
1177+ name : "read_file" ,
1178+ input : { path : "foo.txt" } ,
1179+ } ,
1180+ ] ,
1181+ } ,
1182+ ]
1183+
1184+ const result = convertToOpenAiMessages ( anthropicMessages )
1185+
1186+ expect ( result ) . toHaveLength ( 1 )
1187+ expect ( ( result [ 0 ] as any ) . reasoning_content ) . toBeUndefined ( )
1188+ } )
1189+ } )
10871190} )
10881191
10891192describe ( "consolidateReasoningDetails" , ( ) => {
0 commit comments