mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
perf(web): optimize message conversion with useExternalMessageConverter
Use the cached message converter from @assistant-ui/react to prevent re-converting all messages on every render. The previous implementation called convertMessage for every ChatBlock on each render cycle, creating new objects (Date, arrays, metadata) that triggered unnecessary state updates and re-render storms. The new implementation: - Uses useExternalMessageConverter which internally caches via WeakMap - Memoizes callbacks with useCallback for stable references - Memoizes the adapter object to prevent subscription churn
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react'
|
||||
import type { AppendMessage, ExternalStoreAdapter, ThreadMessageLike } from '@assistant-ui/react'
|
||||
import { useExternalStoreRuntime } from '@assistant-ui/react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import type { AppendMessage, ThreadMessageLike } from '@assistant-ui/react'
|
||||
import { useExternalMessageConverter, useExternalStoreRuntime } from '@assistant-ui/react'
|
||||
import { renderEventLabel } from '@/chat/presentation'
|
||||
import type { ChatBlock } from '@/chat/types'
|
||||
import type { AgentEvent, ToolCallBlock } from '@/chat/types'
|
||||
@@ -113,23 +113,34 @@ export function useHappyRuntime(props: {
|
||||
onSendMessage: (text: string) => void
|
||||
onAbort: () => Promise<void>
|
||||
}) {
|
||||
const adapter = useMemo(() => {
|
||||
return {
|
||||
isDisabled: !props.session.active || props.isSending,
|
||||
isRunning: props.session.thinking,
|
||||
messages: props.blocks,
|
||||
onNew: async (message: AppendMessage) => {
|
||||
const text = getTextFromAppendMessage(message)
|
||||
if (!text) return
|
||||
props.onSendMessage(text)
|
||||
},
|
||||
onCancel: async () => {
|
||||
await props.onAbort()
|
||||
},
|
||||
convertMessage: (block: ChatBlock) => toThreadMessageLike(block),
|
||||
unstable_capabilities: { copy: true }
|
||||
} satisfies ExternalStoreAdapter<ChatBlock>
|
||||
}, [props.session.active, props.session.thinking, props.isSending, props.blocks, props.onSendMessage, props.onAbort])
|
||||
// Use cached message converter for performance optimization
|
||||
// This prevents re-converting all messages on every render
|
||||
const convertedMessages = useExternalMessageConverter<ChatBlock>({
|
||||
callback: toThreadMessageLike,
|
||||
messages: props.blocks as ChatBlock[],
|
||||
isRunning: props.session.thinking,
|
||||
})
|
||||
|
||||
const onNew = useCallback(async (message: AppendMessage) => {
|
||||
const text = getTextFromAppendMessage(message)
|
||||
if (!text) return
|
||||
props.onSendMessage(text)
|
||||
}, [props.onSendMessage])
|
||||
|
||||
const onCancel = useCallback(async () => {
|
||||
await props.onAbort()
|
||||
}, [props.onAbort])
|
||||
|
||||
// Memoize the adapter to avoid recreating on every render
|
||||
// useExternalStoreRuntime may use adapter identity for subscriptions
|
||||
const adapter = useMemo(() => ({
|
||||
isDisabled: !props.session.active || props.isSending,
|
||||
isRunning: props.session.thinking,
|
||||
messages: convertedMessages,
|
||||
onNew,
|
||||
onCancel,
|
||||
unstable_capabilities: { copy: true }
|
||||
}), [props.session.active, props.isSending, props.session.thinking, convertedMessages, onNew, onCancel])
|
||||
|
||||
return useExternalStoreRuntime(adapter)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user