@@ -444,6 +444,124 @@ describe('chats2GPTMessages', () => {
444444 // Plan should be skipped when reserveTool is false
445445 expect ( result ) . toHaveLength ( 2 ) ;
446446 } ) ;
447+
448+ it ( 'should convert AI message with reasoning only' , ( ) => {
449+ const messages : ChatItemMiniType [ ] = [
450+ {
451+ obj : ChatRoleEnum . AI ,
452+ value : [ { reasoning : { content : 'Let me think...' } } ]
453+ }
454+ ] ;
455+
456+ const result = chats2GPTMessages ( { messages, reserveId : false } ) ;
457+
458+ expect ( result ) . toHaveLength ( 1 ) ;
459+ expect ( result [ 0 ] . role ) . toBe ( ChatCompletionRequestMessageRoleEnum . Assistant ) ;
460+ expect ( ( result [ 0 ] as any ) . reasoning_content ) . toBe ( 'Let me think...' ) ;
461+ expect ( ( result [ 0 ] as any ) . content ) . toBeUndefined ( ) ;
462+ } ) ;
463+
464+ it ( 'should merge reasoning + text into a single assistant message' , ( ) => {
465+ const messages : ChatItemMiniType [ ] = [
466+ {
467+ obj : ChatRoleEnum . AI ,
468+ value : [
469+ { reasoning : { content : 'Let me think...' } } ,
470+ { text : { content : 'Final answer' } }
471+ ]
472+ }
473+ ] ;
474+
475+ const result = chats2GPTMessages ( { messages, reserveId : false } ) ;
476+
477+ expect ( result ) . toHaveLength ( 1 ) ;
478+ expect ( result [ 0 ] . role ) . toBe ( ChatCompletionRequestMessageRoleEnum . Assistant ) ;
479+ expect ( ( result [ 0 ] as any ) . reasoning_content ) . toBe ( 'Let me think...' ) ;
480+ expect ( ( result [ 0 ] as any ) . content ) . toBe ( 'Final answer' ) ;
481+ } ) ;
482+
483+ it ( 'should merge reasoning + tool_calls into a single assistant message' , ( ) => {
484+ const messages : ChatItemMiniType [ ] = [
485+ {
486+ obj : ChatRoleEnum . AI ,
487+ value : [
488+ { reasoning : { content : 'Need to call a tool' } } ,
489+ {
490+ tool : {
491+ id : 'tool-1' ,
492+ toolName : 'Search' ,
493+ toolAvatar : '' ,
494+ functionName : 'search_web' ,
495+ params : '{"q":"x"}' ,
496+ response : '{}'
497+ }
498+ }
499+ ]
500+ }
501+ ] ;
502+
503+ const result = chats2GPTMessages ( { messages, reserveId : false , reserveTool : true } ) ;
504+
505+ // 1 merged assistant (reasoning + tool_calls) + 1 tool response
506+ expect ( result ) . toHaveLength ( 2 ) ;
507+ expect ( result [ 0 ] . role ) . toBe ( ChatCompletionRequestMessageRoleEnum . Assistant ) ;
508+ expect ( ( result [ 0 ] as any ) . reasoning_content ) . toBe ( 'Need to call a tool' ) ;
509+ expect ( ( result [ 0 ] as any ) . tool_calls ) . toHaveLength ( 1 ) ;
510+ expect ( ( result [ 0 ] as any ) . tool_calls [ 0 ] . function . name ) . toBe ( 'search_web' ) ;
511+ expect ( result [ 1 ] . role ) . toBe ( ChatCompletionRequestMessageRoleEnum . Tool ) ;
512+ } ) ;
513+
514+ it ( 'should keep tool_calls separate when no reasoning precedes them' , ( ) => {
515+ const messages : ChatItemMiniType [ ] = [
516+ {
517+ obj : ChatRoleEnum . AI ,
518+ value : [
519+ { text : { content : 'Calling tool' } } ,
520+ {
521+ tool : {
522+ id : 'tool-1' ,
523+ toolName : 'Search' ,
524+ toolAvatar : '' ,
525+ functionName : 'search_web' ,
526+ params : '{}' ,
527+ response : '{}'
528+ }
529+ }
530+ ]
531+ }
532+ ] ;
533+
534+ const result = chats2GPTMessages ( { messages, reserveId : false , reserveTool : true } ) ;
535+
536+ // text assistant + tool_calls assistant + tool response
537+ expect ( result ) . toHaveLength ( 3 ) ;
538+ expect ( ( result [ 0 ] as any ) . content ) . toBe ( 'Calling tool' ) ;
539+ expect ( ( result [ 0 ] as any ) . tool_calls ) . toBeUndefined ( ) ;
540+ expect ( ( result [ 1 ] as any ) . tool_calls ) . toHaveLength ( 1 ) ;
541+ expect ( result [ 2 ] . role ) . toBe ( ChatCompletionRequestMessageRoleEnum . Tool ) ;
542+ } ) ;
543+
544+ it ( 'should handle multiple reasoning values producing separate assistant entries' , ( ) => {
545+ const messages : ChatItemMiniType [ ] = [
546+ {
547+ obj : ChatRoleEnum . AI ,
548+ value : [
549+ { reasoning : { content : 'Step 1 thinking' } } ,
550+ { text : { content : 'Intermediate answer' } } ,
551+ { reasoning : { content : 'Step 2 thinking' } } ,
552+ { text : { content : 'Final answer' } }
553+ ]
554+ }
555+ ] ;
556+
557+ const result = chats2GPTMessages ( { messages, reserveId : false } ) ;
558+
559+ expect ( result ) . toHaveLength ( 2 ) ;
560+ expect ( ( result [ 0 ] as any ) . reasoning_content ) . toBe ( 'Step 1 thinking' ) ;
561+ expect ( ( result [ 0 ] as any ) . content ) . toBe ( 'Intermediate answer' ) ;
562+ expect ( ( result [ 1 ] as any ) . reasoning_content ) . toBe ( 'Step 2 thinking' ) ;
563+ expect ( ( result [ 1 ] as any ) . content ) . toBe ( 'Final answer' ) ;
564+ } ) ;
447565} ) ;
448566
449567describe ( 'GPTMessages2Chats' , ( ) => {
@@ -530,6 +648,40 @@ describe('GPTMessages2Chats', () => {
530648 expect ( result [ 0 ] . value [ 1 ] . text ?. content ) . toBe ( 'Final answer' ) ;
531649 } ) ;
532650
651+ it ( 'should drop reasoning when reserveReason is false' , ( ) => {
652+ const messages : ChatCompletionMessageParam [ ] = [
653+ {
654+ role : ChatCompletionRequestMessageRoleEnum . Assistant ,
655+ content : 'Final answer' ,
656+ reasoning_content : 'Let me think about this...'
657+ }
658+ ] ;
659+
660+ const result = GPTMessages2Chats ( { messages, reserveReason : false } ) ;
661+
662+ expect ( result ) . toHaveLength ( 1 ) ;
663+ expect ( result [ 0 ] . value ) . toHaveLength ( 1 ) ;
664+ expect ( result [ 0 ] . value [ 0 ] . text ?. content ) . toBe ( 'Final answer' ) ;
665+ expect ( ( result [ 0 ] . value [ 0 ] as any ) . reasoning ) . toBeUndefined ( ) ;
666+ } ) ;
667+
668+ it ( 'should keep only reasoning when assistant message has no content' , ( ) => {
669+ const messages : ChatCompletionMessageParam [ ] = [
670+ {
671+ role : ChatCompletionRequestMessageRoleEnum . Assistant ,
672+ content : '' ,
673+ reasoning_content : 'Thinking only'
674+ }
675+ ] ;
676+
677+ const result = GPTMessages2Chats ( { messages } ) ;
678+
679+ expect ( result ) . toHaveLength ( 1 ) ;
680+ expect ( result [ 0 ] . value ) . toHaveLength ( 1 ) ;
681+ const value = result [ 0 ] . value [ 0 ] as { reasoning ?: { content : string } } ;
682+ expect ( value . reasoning ?. content ) . toBe ( 'Thinking only' ) ;
683+ } ) ;
684+
533685 it ( 'should merge messages with same dataId' , ( ) => {
534686 const messages : ChatCompletionMessageParam [ ] = [
535687 {
0 commit comments