11'use client' ;
22
33import { useStreamableText } from '@/hooks/use-streamable-text' ;
4+ import { type StreamableValue } from '@ai-sdk/rsc' ;
45import { cn } from '@comp/ui/cn' ;
5- import type { Message as TMessage } from 'ai' ;
6- import type { StreamableValue } from 'ai/rsc' ;
6+ import type { UIMessage } from 'ai' ;
7+
78import equal from 'fast-deep-equal' ;
89import { ChevronDownIcon , ChevronUpIcon } from 'lucide-react' ;
910import { AnimatePresence , motion } from 'motion/react' ;
@@ -26,8 +27,8 @@ interface ExtendedToolInvocation extends ToolInvocation {
2627
2728interface ReasoningPart {
2829 type : 'reasoning' ;
29- reasoning : string ;
30- details : Array < { type : 'text' ; text : string } > ;
30+ text : string ;
31+ details ? : Array < { type : 'text' ; text : string } > ;
3132}
3233
3334interface ReasoningMessagePartProps {
@@ -117,13 +118,13 @@ export function ReasoningMessagePart({ part, isReasoning }: ReasoningMessagePart
117118 variants = { variants }
118119 transition = { { duration : 0.2 , ease : 'easeInOut' } }
119120 >
120- { part . details . map ( ( detail ) =>
121+ { part . details ? .map ( ( detail ) =>
121122 detail . type === 'text' ? (
122123 < StreamableMarkdown key = { detail . text } text = { detail . text } />
123124 ) : (
124125 '<redacted>'
125126 ) ,
126- ) }
127+ ) || < StreamableMarkdown text = { part . text } /> }
127128 </ motion . div >
128129 ) }
129130 </ AnimatePresence >
@@ -159,7 +160,7 @@ const PurePreviewMessage = ({
159160 isLatestMessage,
160161 status,
161162} : {
162- message : TMessage ;
163+ message : UIMessage ;
163164 isLoading : boolean ;
164165 status : 'error' | 'submitted' | 'streaming' | 'ready' ;
165166 isLatestMessage : boolean ;
@@ -180,7 +181,12 @@ const PurePreviewMessage = ({
180181 ) }
181182 >
182183 < div className = "flex w-full flex-col space-y-4" >
183- { message . parts ?. map ( ( part , i ) => {
184+ { message . parts ?. map ( ( part : any , i : number ) => {
185+ // Skip invalid parts
186+ if ( ! part || ! part . type ) {
187+ return null ;
188+ }
189+
184190 switch ( part . type ) {
185191 case 'text' :
186192 return message . role === 'user' ? (
@@ -196,7 +202,7 @@ const PurePreviewMessage = ({
196202 message . role === 'user' ,
197203 } ) }
198204 >
199- < StreamableMarkdown text = { part . text } />
205+ < StreamableMarkdown text = { part . text || '' } />
200206 </ div >
201207 </ motion . div >
202208 ) : (
@@ -207,7 +213,7 @@ const PurePreviewMessage = ({
207213 className = "flex w-full flex-row items-start gap-2 pb-2"
208214 >
209215 < BotCard key = { `message-${ message . id } -part-${ i } ` } >
210- < StreamableMarkdown text = { part . text } />
216+ < StreamableMarkdown text = { part . text || '' } />
211217 </ BotCard >
212218 </ motion . div >
213219 ) ;
@@ -216,8 +222,7 @@ const PurePreviewMessage = ({
216222 return (
217223 < ReasoningMessagePart
218224 key = { `message-${ message . id } -${ i } ` }
219- // @ts -expect-error part
220- part = { part }
225+ part = { part as ReasoningPart }
221226 isReasoning = {
222227 ( message . parts &&
223228 status === 'streaming' &&
@@ -227,7 +232,9 @@ const PurePreviewMessage = ({
227232 />
228233 ) ;
229234 }
235+
230236 default :
237+ // Skip tool parts - only show final text response
231238 return null ;
232239 }
233240 } ) }
@@ -303,8 +310,6 @@ export function UserMessage({ content }: { content: string }) {
303310export const Message = memo ( PurePreviewMessage , ( prevProps , nextProps ) => {
304311 if ( prevProps . status !== nextProps . status ) return false ;
305312
306- if ( prevProps . message . annotations !== nextProps . message . annotations ) return false ;
307-
308313 if ( ! equal ( prevProps . message . parts , nextProps . message . parts ) ) return false ;
309314
310315 return true ;
0 commit comments