Skip to content

Commit

Permalink
fix: render performance while generating messages (#4328)
Browse files Browse the repository at this point in the history
  • Loading branch information
louis-jan authored Dec 23, 2024
1 parent e8e5c8c commit b28cac7
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 14 deletions.
37 changes: 30 additions & 7 deletions web/containers/Providers/ModelHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
extractInferenceParams,
ModelExtension,
} from '@janhq/core'
import { useAtomValue, useSetAtom } from 'jotai'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { ulid } from 'ulidx'

import { activeModelAtom, stateModelAtom } from '@/hooks/useActiveModel'
Expand All @@ -32,6 +32,7 @@ import {
updateMessageAtom,
tokenSpeedAtom,
deleteMessageAtom,
subscribedGeneratingMessageAtom,
} from '@/helpers/atoms/ChatMessage.atom'
import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom'
import {
Expand All @@ -40,6 +41,7 @@ import {
isGeneratingResponseAtom,
updateThreadAtom,
getActiveThreadModelParamsAtom,
activeThreadAtom,
} from '@/helpers/atoms/Thread.atom'

const maxWordForThreadTitle = 10
Expand All @@ -54,6 +56,10 @@ export default function ModelHandler() {
const activeModel = useAtomValue(activeModelAtom)
const setActiveModel = useSetAtom(activeModelAtom)
const setStateModel = useSetAtom(stateModelAtom)
const [subscribedGeneratingMessage, setSubscribedGeneratingMessage] = useAtom(

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-macos

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-ubuntu

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-windows (mcafee)

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-windows (default-windows-security)

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / test-on-windows (bit-defender)

'setSubscribedGeneratingMessage' is assigned a value but never used

Check warning on line 59 in web/containers/Providers/ModelHandler.tsx

View workflow job for this annotation

GitHub Actions / coverage-check

'setSubscribedGeneratingMessage' is assigned a value but never used
subscribedGeneratingMessageAtom
)
const activeThread = useAtomValue(activeThreadAtom)

const updateThreadWaiting = useSetAtom(updateThreadWaitingForResponseAtom)
const threads = useAtomValue(threadsAtom)
Expand All @@ -62,11 +68,17 @@ export default function ModelHandler() {
const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom)
const updateThread = useSetAtom(updateThreadAtom)
const messagesRef = useRef(messages)
const messageGenerationSubscriber = useRef(subscribedGeneratingMessage)
const activeModelRef = useRef(activeModel)
const activeThreadRef = useRef(activeThread)
const activeModelParams = useAtomValue(getActiveThreadModelParamsAtom)
const activeModelParamsRef = useRef(activeModelParams)
const setTokenSpeed = useSetAtom(tokenSpeedAtom)

useEffect(() => {
activeThreadRef.current = activeThread
}, [activeThread])

useEffect(() => {
threadsRef.current = threads
}, [threads])
Expand All @@ -87,6 +99,10 @@ export default function ModelHandler() {
activeModelParamsRef.current = activeModelParams
}, [activeModelParams])

useEffect(() => {
messageGenerationSubscriber.current = subscribedGeneratingMessage
}, [subscribedGeneratingMessage])

const onNewMessageResponse = useCallback(
async (message: ThreadMessage) => {
if (message.type === MessageRequestType.Thread) {
Expand Down Expand Up @@ -179,12 +195,19 @@ export default function ModelHandler() {

const updateThreadMessage = useCallback(
(message: ThreadMessage) => {
updateMessage(
message.id,
message.thread_id,
message.content,
message.status
)
if (
messageGenerationSubscriber.current &&
message.thread_id === activeThreadRef.current?.id &&
!messageGenerationSubscriber.current!.thread_id
) {
updateMessage(
message.id,
message.thread_id,
message.content,
message.status
)
}

if (message.status === MessageStatus.Pending) {
if (message.content.length) {
setIsGeneratingResponse(false)
Expand Down
7 changes: 7 additions & 0 deletions web/helpers/atoms/ChatMessage.atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export const chatMessages = atom(
}
)

/**
* Store subscribed generating message thread
*/
export const subscribedGeneratingMessageAtom = atom<{
thread_id?: string
}>({})

/**
* Stores the status of the messages load for each thread
*/
Expand Down
21 changes: 15 additions & 6 deletions web/hooks/useSetActiveThread.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import { ExtensionTypeEnum, Thread, ConversationalExtension } from '@janhq/core'

import { useSetAtom } from 'jotai'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'

import { extensionManager } from '@/extension'
import { activeAssistantAtom } from '@/helpers/atoms/Assistant.atom'
import { setConvoMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
import {
setConvoMessagesAtom,
subscribedGeneratingMessageAtom,
} from '@/helpers/atoms/ChatMessage.atom'
import {
getActiveThreadIdAtom,
setActiveThreadIdAtom,
setThreadModelParamsAtom,
} from '@/helpers/atoms/Thread.atom'
import { ModelParams } from '@/types/model'

export default function useSetActiveThread() {
const setActiveThreadId = useSetAtom(setActiveThreadIdAtom)
const setThreadMessage = useSetAtom(setConvoMessagesAtom)
const activeThreadId = useAtomValue(getActiveThreadIdAtom)
const setThreadMessages = useSetAtom(setConvoMessagesAtom)
const setThreadModelParams = useSetAtom(setThreadModelParamsAtom)
const setActiveAssistant = useSetAtom(activeAssistantAtom)
const [messageSubscriber, setMessageSubscriber] = useAtom(
subscribedGeneratingMessageAtom
)

const setActiveThread = async (thread: Thread) => {
if (!thread?.id) return
if (!thread?.id || activeThreadId === thread.id) return

setActiveThreadId(thread?.id)
setActiveThreadId(thread.id)

try {
const assistantInfo = await getThreadAssistant(thread.id)
Expand All @@ -32,7 +40,8 @@ export default function useSetActiveThread() {
...assistantInfo?.model?.settings,
}
setThreadModelParams(thread?.id, modelParams)
setThreadMessage(thread.id, messages)
setThreadMessages(thread.id, messages)
if (messageSubscriber.thread_id !== thread.id) setMessageSubscriber({})
} catch (e) {
console.error(e)
}
Expand Down
22 changes: 21 additions & 1 deletion web/screens/Thread/ThreadCenterPanel/ChatItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { forwardRef, useEffect, useState } from 'react'
import React, { forwardRef, useEffect, useRef, useState } from 'react'

import {
events,
Expand All @@ -8,10 +8,14 @@ import {
ThreadMessage,
} from '@janhq/core'

import { useAtom } from 'jotai'

import ErrorMessage from '@/containers/ErrorMessage'

import MessageContainer from '../TextMessage'

import { subscribedGeneratingMessageAtom } from '@/helpers/atoms/ChatMessage.atom'

type Ref = HTMLDivElement

type Props = {
Expand All @@ -22,19 +26,35 @@ type Props = {
const ChatItem = forwardRef<Ref, Props>((message, ref) => {
const [content, setContent] = useState<ThreadContent[]>(message.content)
const [status, setStatus] = useState<MessageStatus>(message.status)
const [subscribedGeneratingMessage, setSubscribedGeneratingMessage] = useAtom(
subscribedGeneratingMessageAtom
)
const [errorMessage, setErrorMessage] = useState<ThreadMessage | undefined>(
message.isCurrentMessage && !!message?.metadata?.error ? message : undefined
)
const subscribedGeneratingMessageRef = useRef(subscribedGeneratingMessage)

function onMessageUpdate(data: ThreadMessage) {
if (data.id === message.id) {
setContent(data.content)
if (data.status !== status) setStatus(data.status)
if (data.status === MessageStatus.Error && message.isCurrentMessage)
setErrorMessage(data)

// Update subscriber if the message is generating
if (
subscribedGeneratingMessageRef.current?.thread_id !== message.thread_id
)
setSubscribedGeneratingMessage({
thread_id: message.thread_id,
})
}
}

useEffect(() => {
subscribedGeneratingMessageRef.current = subscribedGeneratingMessage
}, [subscribedGeneratingMessage])

useEffect(() => {
if (!message.isCurrentMessage && errorMessage) setErrorMessage(undefined)
}, [message, errorMessage])
Expand Down

0 comments on commit b28cac7

Please sign in to comment.