22
33import { useStreamableText } from '@/hooks/use-streamable-text' ;
44import { cn } from '@comp/ui/cn' ;
5- import type { Message as TMessage } from 'ai' ;
5+ import type { UIMessage } from 'ai' ;
66import type { StreamableValue } from 'ai/rsc' ;
77import equal from 'fast-deep-equal' ;
88import { ChevronDownIcon , ChevronUpIcon } from 'lucide-react' ;
@@ -26,8 +26,8 @@ interface ExtendedToolInvocation extends ToolInvocation {
2626
2727interface ReasoningPart {
2828 type : 'reasoning' ;
29- reasoning : string ;
30- details : Array < { type : 'text' ; text : string } > ;
29+ text : string ;
30+ details ? : Array < { type : 'text' ; text : string } > ;
3131}
3232
3333interface ReasoningMessagePartProps {
@@ -117,13 +117,13 @@ export function ReasoningMessagePart({ part, isReasoning }: ReasoningMessagePart
117117 variants = { variants }
118118 transition = { { duration : 0.2 , ease : 'easeInOut' } }
119119 >
120- { part . details . map ( ( detail ) =>
120+ { part . details ? .map ( ( detail ) =>
121121 detail . type === 'text' ? (
122122 < StreamableMarkdown key = { detail . text } text = { detail . text } />
123123 ) : (
124124 '<redacted>'
125125 ) ,
126- ) }
126+ ) || < StreamableMarkdown text = { part . text } /> }
127127 </ motion . div >
128128 ) }
129129 </ AnimatePresence >
@@ -159,7 +159,7 @@ const PurePreviewMessage = ({
159159 isLatestMessage,
160160 status,
161161} : {
162- message : TMessage ;
162+ message : UIMessage ;
163163 isLoading : boolean ;
164164 status : 'error' | 'submitted' | 'streaming' | 'ready' ;
165165 isLatestMessage : boolean ;
@@ -180,7 +180,12 @@ const PurePreviewMessage = ({
180180 ) }
181181 >
182182 < div className = "flex w-full flex-col space-y-4" >
183- { message . parts ?. map ( ( part , i ) => {
183+ { message . parts ?. map ( ( part : any , i : number ) => {
184+ // Skip invalid parts
185+ if ( ! part || ! part . type ) {
186+ return null ;
187+ }
188+
184189 switch ( part . type ) {
185190 case 'text' :
186191 return message . role === 'user' ? (
@@ -196,7 +201,7 @@ const PurePreviewMessage = ({
196201 message . role === 'user' ,
197202 } ) }
198203 >
199- < StreamableMarkdown text = { part . text } />
204+ < StreamableMarkdown text = { part . text || '' } />
200205 </ div >
201206 </ motion . div >
202207 ) : (
@@ -207,7 +212,7 @@ const PurePreviewMessage = ({
207212 className = "flex w-full flex-row items-start gap-2 pb-2"
208213 >
209214 < BotCard key = { `message-${ message . id } -part-${ i } ` } >
210- < StreamableMarkdown text = { part . text } />
215+ < StreamableMarkdown text = { part . text || '' } />
211216 </ BotCard >
212217 </ motion . div >
213218 ) ;
@@ -216,8 +221,7 @@ const PurePreviewMessage = ({
216221 return (
217222 < ReasoningMessagePart
218223 key = { `message-${ message . id } -${ i } ` }
219- // @ts -expect-error part
220- part = { part }
224+ part = { part as ReasoningPart }
221225 isReasoning = {
222226 ( message . parts &&
223227 status === 'streaming' &&
@@ -227,7 +231,9 @@ const PurePreviewMessage = ({
227231 />
228232 ) ;
229233 }
234+
230235 default :
236+ // Skip tool parts - only show final text response
231237 return null ;
232238 }
233239 } ) }
@@ -303,8 +309,6 @@ export function UserMessage({ content }: { content: string }) {
303309export const Message = memo ( PurePreviewMessage , ( prevProps , nextProps ) => {
304310 if ( prevProps . status !== nextProps . status ) return false ;
305311
306- if ( prevProps . message . annotations !== nextProps . message . annotations ) return false ;
307-
308312 if ( ! equal ( prevProps . message . parts , nextProps . message . parts ) ) return false ;
309313
310314 return true ;
0 commit comments