@@ -18,7 +18,6 @@ import {
1818 type StreamMessage ,
1919} from "./validators.js" ;
2020
21-
2221export function blankUIMessage < METADATA = unknown > (
2322 streamMessage : StreamMessage & { metadata ?: METADATA } ,
2423 threadId : string ,
@@ -538,3 +537,65 @@ function mergeProviderMetadata(
538537 }
539538 return merged ;
540539}
540+
541+ export function combineUIMessages ( messages : UIMessage [ ] ) : UIMessage [ ] {
542+ const combined = messages . reduce ( ( acc , message ) => {
543+ if ( ! acc . length ) {
544+ return [ message ] ;
545+ }
546+ const previous = acc . at ( - 1 ) ! ;
547+ if ( previous . role !== message . role ) {
548+ acc . push ( message ) ;
549+ return acc ;
550+ }
551+ // We will replace it with a combined message
552+ acc . pop ( ) ;
553+ const newParts = [ ...previous . parts ] ;
554+ for ( const part of message . parts ) {
555+ const toolCallId = getToolCallId ( part ) ;
556+ if ( ! toolCallId ) {
557+ newParts . push ( part ) ;
558+ continue ;
559+ }
560+ const previousPartIndex = newParts . findIndex (
561+ ( p ) => getToolCallId ( p ) === toolCallId ,
562+ ) ;
563+ const previousPart = newParts . splice ( previousPartIndex , 1 ) [ 0 ] ;
564+ if ( ! previousPart ) {
565+ newParts . push ( part ) ;
566+ continue ;
567+ }
568+ newParts . push ( mergeParts ( previousPart , part ) ) ;
569+ }
570+ acc . push ( {
571+ ...previous ,
572+ ...pick ( message , [ "status" , "metadata" , "agentName" ] ) ,
573+ parts : newParts ,
574+ text : newParts
575+ . filter ( ( p ) => p . type === "text" )
576+ . map ( ( p ) => p . text )
577+ . join ( "" ) ,
578+ } ) ;
579+ return acc ;
580+ } , [ ] as UIMessage [ ] ) ;
581+ return combined ;
582+ }
583+
584+ function getToolCallId (
585+ part : UIMessage [ "parts" ] [ number ] & { toolCallId ?: string } ,
586+ ) {
587+ return part . toolCallId ;
588+ }
589+
590+ function mergeParts (
591+ previousPart : UIMessage [ "parts" ] [ number ] ,
592+ part : UIMessage [ "parts" ] [ number ] ,
593+ ) : UIMessage [ "parts" ] [ number ] {
594+ const merged : Record < string , unknown > = { ...previousPart } ;
595+ for ( const [ key , value ] of Object . entries ( part ) ) {
596+ if ( value !== undefined ) {
597+ merged [ key ] = value ;
598+ }
599+ }
600+ return merged as ToolUIPart | DynamicToolUIPart ;
601+ }
0 commit comments