fix: download now change state immediately (#475)
* fix: download now change state immediately Signed-off-by: James <james@jan.ai> * fix: add back last message Signed-off-by: James <james@jan.ai> * update send button status Signed-off-by: James <james@jan.ai> --------- Signed-off-by: James <james@jan.ai> Co-authored-by: James <james@jan.ai>
This commit is contained in:
parent
7b7d2db131
commit
0bd52e58ec
@ -6,6 +6,7 @@ export enum EventName {
|
|||||||
OnNewMessageRequest = "onNewMessageRequest",
|
OnNewMessageRequest = "onNewMessageRequest",
|
||||||
OnNewMessageResponse = "onNewMessageResponse",
|
OnNewMessageResponse = "onNewMessageResponse",
|
||||||
OnMessageResponseUpdate = "onMessageResponseUpdate",
|
OnMessageResponseUpdate = "onMessageResponseUpdate",
|
||||||
|
OnMessageResponseFinished = "OnMessageResponseFinished",
|
||||||
OnDownloadUpdate = "onDownloadUpdate",
|
OnDownloadUpdate = "onDownloadUpdate",
|
||||||
OnDownloadSuccess = "onDownloadSuccess",
|
OnDownloadSuccess = "onDownloadSuccess",
|
||||||
OnDownloadError = "onDownloadError",
|
OnDownloadError = "onDownloadError",
|
||||||
|
|||||||
@ -140,6 +140,8 @@ async function handleMessageRequest(data: NewMessageRequest) {
|
|||||||
message.message = message.message.trim();
|
message.message = message.message.trim();
|
||||||
// TODO: Common collections should be able to access via core functions instead of store
|
// TODO: Common collections should be able to access via core functions instead of store
|
||||||
await store.updateOne("messages", message._id, message);
|
await store.updateOne("messages", message._id, message);
|
||||||
|
events.emit("OnMessageResponseFinished", message);
|
||||||
|
// events.emit(EventName.OnMessageResponseFinished, message);
|
||||||
},
|
},
|
||||||
error: async (err) => {
|
error: async (err) => {
|
||||||
message.message =
|
message.message =
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import SimpleTag from '../SimpleTag'
|
import SimpleTag from '../SimpleTag'
|
||||||
import PrimaryButton from '../PrimaryButton'
|
|
||||||
import { formatDownloadPercentage, toGigabytes } from '@utils/converter'
|
import { formatDownloadPercentage, toGigabytes } from '@utils/converter'
|
||||||
import SecondaryButton from '../SecondaryButton'
|
|
||||||
import { useCallback, useEffect, useMemo } from 'react'
|
import { useCallback, useEffect, useMemo } from 'react'
|
||||||
import useGetPerformanceTag from '@hooks/useGetPerformanceTag'
|
import useGetPerformanceTag from '@hooks/useGetPerformanceTag'
|
||||||
import useDownloadModel from '@hooks/useDownloadModel'
|
import useDownloadModel from '@hooks/useDownloadModel'
|
||||||
@ -58,7 +56,6 @@ const ExploreModelItemHeader: React.FC<Props> = ({
|
|||||||
if (isDownloaded) {
|
if (isDownloaded) {
|
||||||
downloadButton = (
|
downloadButton = (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
|
||||||
themes="accent"
|
themes="accent"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMainViewState(MainViewState.MyModel)
|
setMainViewState(MainViewState.MyModel)
|
||||||
@ -72,17 +69,20 @@ const ExploreModelItemHeader: React.FC<Props> = ({
|
|||||||
if (downloadState != null) {
|
if (downloadState != null) {
|
||||||
// downloading
|
// downloading
|
||||||
downloadButton = (
|
downloadButton = (
|
||||||
<SecondaryButton
|
<Button
|
||||||
disabled
|
disabled
|
||||||
title={`Downloading (${formatDownloadPercentage(
|
themes="accent"
|
||||||
downloadState.percent
|
onClick={() => {
|
||||||
)})`}
|
setMainViewState(MainViewState.MyModel)
|
||||||
/>
|
}}
|
||||||
|
>
|
||||||
|
Downloading {formatDownloadPercentage(downloadState.percent)}
|
||||||
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-border bg-background/50 flex items-center justify-between rounded-t-md border-b px-4 py-2">
|
<div className="flex items-center justify-between rounded-t-md border-b border-border bg-background/50 px-4 py-2">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span>{exploreModel.name}</span>
|
<span>{exploreModel.name}</span>
|
||||||
{performanceTag && (
|
{performanceTag && (
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { useAtomValue, useSetAtom } from 'jotai'
|
|||||||
import {
|
import {
|
||||||
getActiveConvoIdAtom,
|
getActiveConvoIdAtom,
|
||||||
setActiveConvoIdAtom,
|
setActiveConvoIdAtom,
|
||||||
updateConversationWaitingForResponseAtom,
|
|
||||||
} from '@helpers/atoms/Conversation.atom'
|
} from '@helpers/atoms/Conversation.atom'
|
||||||
import {
|
import {
|
||||||
setMainViewStateAtom,
|
setMainViewStateAtom,
|
||||||
@ -12,7 +11,6 @@ import {
|
|||||||
import { displayDate } from '@utils/datetime'
|
import { displayDate } from '@utils/datetime'
|
||||||
import { twMerge } from 'tailwind-merge'
|
import { twMerge } from 'tailwind-merge'
|
||||||
import { activeAssistantModelAtom } from '@helpers/atoms/Model.atom'
|
import { activeAssistantModelAtom } from '@helpers/atoms/Model.atom'
|
||||||
import { switchingModelConfirmationModalPropsAtom } from '@helpers/atoms/Modal.atom'
|
|
||||||
import useStartStopModel from '@hooks/useStartStopModel'
|
import useStartStopModel from '@hooks/useStartStopModel'
|
||||||
import useGetModelById from '@hooks/useGetModelById'
|
import useGetModelById from '@hooks/useGetModelById'
|
||||||
|
|
||||||
@ -37,10 +35,6 @@ const HistoryItem: React.FC<Props> = ({
|
|||||||
|
|
||||||
const setMainViewState = useSetAtom(setMainViewStateAtom)
|
const setMainViewState = useSetAtom(setMainViewStateAtom)
|
||||||
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom)
|
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom)
|
||||||
const updateConvWaiting = useSetAtom(updateConversationWaitingForResponseAtom)
|
|
||||||
const setConfirmationModalProps = useSetAtom(
|
|
||||||
switchingModelConfirmationModalPropsAtom
|
|
||||||
)
|
|
||||||
|
|
||||||
const onClick = async () => {
|
const onClick = async () => {
|
||||||
if (conversation.modelId == null) {
|
if (conversation.modelId == null) {
|
||||||
@ -55,14 +49,13 @@ const HistoryItem: React.FC<Props> = ({
|
|||||||
startModel(model._id)
|
startModel(model._id)
|
||||||
} else if (activeModel._id !== model._id) {
|
} else if (activeModel._id !== model._id) {
|
||||||
// display confirmation modal
|
// display confirmation modal
|
||||||
setConfirmationModalProps({
|
// TODO: temporarily disabled
|
||||||
replacingModel: model,
|
// setConfirmationModalProps({
|
||||||
})
|
// replacingModel: model,
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversation._id) updateConvWaiting(conversation._id, true)
|
|
||||||
|
|
||||||
if (activeConvoId !== conversation._id) {
|
if (activeConvoId !== conversation._id) {
|
||||||
setMainViewState(MainViewState.Conversation)
|
setMainViewState(MainViewState.Conversation)
|
||||||
setActiveConvoId(conversation._id)
|
setActiveConvoId(conversation._id)
|
||||||
|
|||||||
@ -82,11 +82,11 @@ const SwitchingModelConfirmationModal: React.FC = () => {
|
|||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
Selected conversation is using model{' '}
|
Selected conversation is using model{' '}
|
||||||
<span className="font-semibold text-black">
|
<span className="font-semibold text-black">
|
||||||
{props?.replacingModel._id}
|
{props?.replacingModel.name}
|
||||||
</span>
|
</span>
|
||||||
, but the active model is using{' '}
|
, but the active model is using{' '}
|
||||||
<span className="font-semibold text-black">
|
<span className="font-semibold text-black">
|
||||||
{activeModel?._id}
|
{activeModel?.name}
|
||||||
</span>
|
</span>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
@ -95,7 +95,7 @@ const SwitchingModelConfirmationModal: React.FC = () => {
|
|||||||
Switch to
|
Switch to
|
||||||
<span className="font-semibold text-black">
|
<span className="font-semibold text-black">
|
||||||
{' '}
|
{' '}
|
||||||
{props?.replacingModel._id}?
|
{props?.replacingModel.name}?
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,17 +1,35 @@
|
|||||||
import { addNewMessageAtom, updateMessageAtom } from './atoms/ChatMessage.atom'
|
import { addNewMessageAtom, updateMessageAtom } from './atoms/ChatMessage.atom'
|
||||||
import { toChatMessage } from '@models/ChatMessage'
|
import { toChatMessage } from '@models/ChatMessage'
|
||||||
import { events, EventName, NewMessageResponse } from '@janhq/core'
|
import { events, EventName, NewMessageResponse, DataService } from '@janhq/core'
|
||||||
import { useSetAtom } from 'jotai'
|
import { useSetAtom } from 'jotai'
|
||||||
import { ReactNode, useEffect } from 'react'
|
import { ReactNode, useEffect } from 'react'
|
||||||
import useGetBots from '@hooks/useGetBots'
|
import useGetBots from '@hooks/useGetBots'
|
||||||
import useGetUserConversations from '@hooks/useGetUserConversations'
|
import useGetUserConversations from '@hooks/useGetUserConversations'
|
||||||
|
import {
|
||||||
|
updateConversationAtom,
|
||||||
|
updateConversationWaitingForResponseAtom,
|
||||||
|
} from './atoms/Conversation.atom'
|
||||||
|
import { executeSerial } from '../../electron/core/plugin-manager/execution/extension-manager'
|
||||||
|
import { debounce } from 'lodash'
|
||||||
|
|
||||||
|
let currentConversation: Conversation | undefined = undefined
|
||||||
|
|
||||||
|
const debouncedUpdateConversation = debounce(
|
||||||
|
async (updatedConv: Conversation) => {
|
||||||
|
await executeSerial(DataService.UpdateConversation, updatedConv)
|
||||||
|
},
|
||||||
|
1000
|
||||||
|
)
|
||||||
|
|
||||||
export default function EventHandler({ children }: { children: ReactNode }) {
|
export default function EventHandler({ children }: { children: ReactNode }) {
|
||||||
const addNewMessage = useSetAtom(addNewMessageAtom)
|
const addNewMessage = useSetAtom(addNewMessageAtom)
|
||||||
const updateMessage = useSetAtom(updateMessageAtom)
|
const updateMessage = useSetAtom(updateMessageAtom)
|
||||||
|
const updateConversation = useSetAtom(updateConversationAtom)
|
||||||
const { getBotById } = useGetBots()
|
const { getBotById } = useGetBots()
|
||||||
const { getConversationById } = useGetUserConversations()
|
const { getConversationById } = useGetUserConversations()
|
||||||
|
|
||||||
|
const updateConvWaiting = useSetAtom(updateConversationWaitingForResponseAtom)
|
||||||
|
|
||||||
async function handleNewMessageResponse(message: NewMessageResponse) {
|
async function handleNewMessageResponse(message: NewMessageResponse) {
|
||||||
if (message.conversationId) {
|
if (message.conversationId) {
|
||||||
const convo = await getConversationById(message.conversationId)
|
const convo = await getConversationById(message.conversationId)
|
||||||
@ -34,7 +52,7 @@ export default function EventHandler({ children }: { children: ReactNode }) {
|
|||||||
messageResponse.conversationId &&
|
messageResponse.conversationId &&
|
||||||
messageResponse._id &&
|
messageResponse._id &&
|
||||||
messageResponse.message
|
messageResponse.message
|
||||||
)
|
) {
|
||||||
updateMessage(
|
updateMessage(
|
||||||
messageResponse._id,
|
messageResponse._id,
|
||||||
messageResponse.conversationId,
|
messageResponse.conversationId,
|
||||||
@ -42,10 +60,43 @@ export default function EventHandler({ children }: { children: ReactNode }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (messageResponse.conversationId) {
|
||||||
|
if (
|
||||||
|
!currentConversation ||
|
||||||
|
currentConversation._id !== messageResponse.conversationId
|
||||||
|
) {
|
||||||
|
currentConversation = await getConversationById(
|
||||||
|
messageResponse.conversationId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedConv: Conversation = {
|
||||||
|
...currentConversation,
|
||||||
|
lastMessage: messageResponse.message,
|
||||||
|
}
|
||||||
|
|
||||||
|
updateConversation(updatedConv)
|
||||||
|
debouncedUpdateConversation(updatedConv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleMessageResponseFinished(
|
||||||
|
messageResponse: NewMessageResponse
|
||||||
|
) {
|
||||||
|
if (!messageResponse.conversationId) return
|
||||||
|
console.debug('handleMessageResponseFinished', messageResponse)
|
||||||
|
updateConvWaiting(messageResponse.conversationId, false)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window.corePlugin.events) {
|
if (window.corePlugin.events) {
|
||||||
events.on(EventName.OnNewMessageResponse, handleNewMessageResponse)
|
events.on(EventName.OnNewMessageResponse, handleNewMessageResponse)
|
||||||
events.on(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate)
|
events.on(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate)
|
||||||
|
events.on(
|
||||||
|
"OnMessageResponseFinished",
|
||||||
|
// EventName.OnMessageResponseFinished,
|
||||||
|
handleMessageResponseFinished
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@ -53,6 +104,11 @@ export default function EventHandler({ children }: { children: ReactNode }) {
|
|||||||
return () => {
|
return () => {
|
||||||
events.off(EventName.OnNewMessageResponse, handleNewMessageResponse)
|
events.off(EventName.OnNewMessageResponse, handleNewMessageResponse)
|
||||||
events.off(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate)
|
events.off(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate)
|
||||||
|
events.off(
|
||||||
|
"OnMessageResponseFinished",
|
||||||
|
// EventName.OnMessageResponseFinished,
|
||||||
|
handleMessageResponseFinished
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
return <>{children}</>
|
return <>{children}</>
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import { executeSerial } from '@services/pluginService'
|
import { executeSerial } from '@services/pluginService'
|
||||||
import { DataService, ModelManagementService } from '@janhq/core'
|
import { ModelManagementService } from '@janhq/core'
|
||||||
|
import { useSetAtom } from 'jotai'
|
||||||
|
import { setDownloadStateAtom } from '@helpers/atoms/DownloadState.atom'
|
||||||
|
|
||||||
export default function useDownloadModel() {
|
export default function useDownloadModel() {
|
||||||
|
const setDownloadState = useSetAtom(setDownloadStateAtom)
|
||||||
|
|
||||||
const assistanModel = (
|
const assistanModel = (
|
||||||
model: Product,
|
model: Product,
|
||||||
modelVersion: ModelVersion
|
modelVersion: ModelVersion
|
||||||
@ -37,6 +41,22 @@ export default function useDownloadModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const downloadModel = async (model: Product, modelVersion: ModelVersion) => {
|
const downloadModel = async (model: Product, modelVersion: ModelVersion) => {
|
||||||
|
// set an initial download state
|
||||||
|
setDownloadState({
|
||||||
|
modelId: modelVersion._id,
|
||||||
|
time: {
|
||||||
|
elapsed: 0,
|
||||||
|
remaining: 0,
|
||||||
|
},
|
||||||
|
speed: 0,
|
||||||
|
percent: 0,
|
||||||
|
size: {
|
||||||
|
total: 0,
|
||||||
|
transferred: 0,
|
||||||
|
},
|
||||||
|
fileName: modelVersion._id,
|
||||||
|
})
|
||||||
|
|
||||||
modelVersion.startDownloadAt = Date.now()
|
modelVersion.startDownloadAt = Date.now()
|
||||||
const assistantModel = assistanModel(model, modelVersion)
|
const assistantModel = assistanModel(model, modelVersion)
|
||||||
await executeSerial(ModelManagementService.StoreModel, assistantModel)
|
await executeSerial(ModelManagementService.StoreModel, assistantModel)
|
||||||
|
|||||||
@ -13,13 +13,14 @@ import { addNewMessageAtom } from '@helpers/atoms/ChatMessage.atom'
|
|||||||
import {
|
import {
|
||||||
currentConversationAtom,
|
currentConversationAtom,
|
||||||
updateConversationAtom,
|
updateConversationAtom,
|
||||||
|
updateConversationWaitingForResponseAtom,
|
||||||
} from '@helpers/atoms/Conversation.atom'
|
} from '@helpers/atoms/Conversation.atom'
|
||||||
|
|
||||||
export default function useSendChatMessage() {
|
export default function useSendChatMessage() {
|
||||||
const currentConvo = useAtomValue(currentConversationAtom)
|
const currentConvo = useAtomValue(currentConversationAtom)
|
||||||
const addNewMessage = useSetAtom(addNewMessageAtom)
|
const addNewMessage = useSetAtom(addNewMessageAtom)
|
||||||
const updateConversation = useSetAtom(updateConversationAtom)
|
const updateConversation = useSetAtom(updateConversationAtom)
|
||||||
|
const updateConvWaiting = useSetAtom(updateConversationWaitingForResponseAtom)
|
||||||
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom)
|
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom)
|
||||||
|
|
||||||
let timeout: any | undefined = undefined
|
let timeout: any | undefined = undefined
|
||||||
@ -60,10 +61,15 @@ export default function useSendChatMessage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sendChatMessage = async () => {
|
const sendChatMessage = async () => {
|
||||||
|
const convoId = currentConvo?._id
|
||||||
|
|
||||||
|
if (!convoId) return
|
||||||
setCurrentPrompt('')
|
setCurrentPrompt('')
|
||||||
|
updateConvWaiting(convoId, true)
|
||||||
|
|
||||||
const prompt = currentPrompt.trim()
|
const prompt = currentPrompt.trim()
|
||||||
const newMessage: RawMessage = {
|
const newMessage: RawMessage = {
|
||||||
conversationId: currentConvo?._id,
|
conversationId: convoId,
|
||||||
message: prompt,
|
message: prompt,
|
||||||
user: 'user',
|
user: 'user',
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
@ -77,10 +83,20 @@ export default function useSendChatMessage() {
|
|||||||
events.emit(EventName.OnNewMessageRequest, newMessage)
|
events.emit(EventName.OnNewMessageRequest, newMessage)
|
||||||
|
|
||||||
if (!currentConvo?.summary && currentConvo) {
|
if (!currentConvo?.summary && currentConvo) {
|
||||||
const updatedConv = {
|
const updatedConv: Conversation = {
|
||||||
...currentConvo,
|
...currentConvo,
|
||||||
|
lastMessage: prompt,
|
||||||
summary: `Prompt: ${prompt}`,
|
summary: `Prompt: ${prompt}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateConversation(updatedConv)
|
||||||
|
await executeSerial(DataService.UpdateConversation, updatedConv)
|
||||||
|
} else {
|
||||||
|
const updatedConv: Conversation = {
|
||||||
|
...currentConvo,
|
||||||
|
lastMessage: prompt,
|
||||||
|
}
|
||||||
|
|
||||||
updateConversation(updatedConv)
|
updateConversation(updatedConv)
|
||||||
await executeSerial(DataService.UpdateConversation, updatedConv)
|
await executeSerial(DataService.UpdateConversation, updatedConv)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,10 +28,10 @@
|
|||||||
"eslint-config-next": "13.4.10",
|
"eslint-config-next": "13.4.10",
|
||||||
"framer-motion": "^10.16.4",
|
"framer-motion": "^10.16.4",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"react-intersection-observer": "^9.5.2",
|
|
||||||
"jotai": "^2.4.0",
|
"jotai": "^2.4.0",
|
||||||
"jotai-optics": "^0.3.1",
|
"jotai-optics": "^0.3.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.288.0",
|
"lucide-react": "^0.288.0",
|
||||||
"marked": "^9.1.2",
|
"marked": "^9.1.2",
|
||||||
"marked-highlight": "^2.0.6",
|
"marked-highlight": "^2.0.6",
|
||||||
@ -42,6 +42,7 @@
|
|||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.45.4",
|
"react-hook-form": "^7.45.4",
|
||||||
|
"react-intersection-observer": "^9.5.2",
|
||||||
"sass": "^1.69.4",
|
"sass": "^1.69.4",
|
||||||
"tailwind-merge": "^1.14.0",
|
"tailwind-merge": "^1.14.0",
|
||||||
"tailwindcss": "3.3.3",
|
"tailwindcss": "3.3.3",
|
||||||
@ -50,6 +51,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/forms": "^0.5.4",
|
"@tailwindcss/forms": "^0.5.4",
|
||||||
|
"@types/lodash": "^4.14.200",
|
||||||
"@types/node": "20.6.5",
|
"@types/node": "20.6.5",
|
||||||
"@types/uuid": "^9.0.6",
|
"@types/uuid": "^9.0.6",
|
||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user