From cc4cca90fdad622bccf0dfd2e5b4c3e688c25023 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Thu, 26 Mar 2026 18:35:12 +0200 Subject: [PATCH] perf(site): memo-wrap SmoothedResponse and ReasoningDisclosure to skip completed blocks during streaming (#23674) During streaming, StreamingOutput's compiler cache guard misses every chunk because streamState.blocks and streamTools are new references. This causes renderBlockList to recreate all child JSX elements, and React calls every child function even for blocks that finished streaming. Wrapping SmoothedResponse and ReasoningDisclosure in React.memo lets React skip the function call entirely when props are stable. For N completed response blocks and M completed thinking blocks, this reduces per-chunk function calls from N+M+1 to 1. The compiler still compiles both inner functions cleanly (6 and 12 cache slots respectively, zero diagnostics). --- .../components/AgentDetail/ConversationTimeline.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.tsx b/site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.tsx index b560f5433b..4291b171cd 100644 --- a/site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.tsx +++ b/site/src/pages/AgentsPage/components/AgentDetail/ConversationTimeline.tsx @@ -48,12 +48,12 @@ import type { StreamState, } from "./types"; -const ReasoningDisclosure: FC<{ +const ReasoningDisclosure = memo<{ id: string; text: string; isStreaming?: boolean; urlTransform?: UrlTransform; -}> = ({ id, text, isStreaming = false, urlTransform }) => { +}>(({ id, text, isStreaming = false, urlTransform }) => { const { visibleText } = useSmoothStreamingText({ fullText: text, isStreaming, @@ -86,7 +86,7 @@ const ReasoningDisclosure: FC<{ ); -}; +}); // Shared block renderer used by both ChatMessageItem (historical // messages) and StreamingOutput (live stream). Encapsulates the @@ -107,11 +107,11 @@ type RenderBlockListParams = { // Wrapper that runs the smooth-streaming jitter buffer on a single // response block. Only used during live streaming — historical // messages render through directly. -const SmoothedResponse: FC<{ +const SmoothedResponse = memo<{ text: string; streamKey: string; urlTransform?: UrlTransform; -}> = ({ text, streamKey, urlTransform }) => { +}>(({ text, streamKey, urlTransform }) => { const { visibleText } = useSmoothStreamingText({ fullText: text, isStreaming: true, @@ -123,7 +123,7 @@ const SmoothedResponse: FC<{ {visibleText} ); -}; +}); const InlineTextAttachmentButton: FC<{ content: string;