From 92be81e5661787e71963766b5c4b13ba8bafe1e2 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 28 Nov 2023 11:47:17 +0700 Subject: [PATCH] chore: rebase main chore: rebase main --- core/src/core.ts | 52 ++++---------- core/src/fs.ts | 13 ++-- core/src/index.ts | 22 ++---- core/src/plugins/conversational.ts | 2 + core/src/plugins/model.ts | 3 +- package.json | 2 +- plugins/conversational-json/src/index.ts | 21 ++++++ plugins/model-plugin/src/index.ts | 6 +- web/containers/CardSidebar/index.tsx | 11 ++- web/containers/DropdownListSidebar/index.tsx | 14 ++-- web/containers/ItemCardSidebar/index.tsx | 2 +- .../BottomBar/DownloadingState/index.tsx | 2 +- .../Layout/TopBar/CommandSearch/index.tsx | 3 +- web/containers/ModalCancelDownload/index.tsx | 4 +- web/containers/Providers/EventHandler.tsx | 17 ++--- web/helpers/atoms/ChatMessage.atom.ts | 2 +- web/hooks/useActiveModel.ts | 3 + web/hooks/useCreateNewThread.ts | 4 +- web/hooks/useDeleteConversation.ts | 34 +++++---- web/hooks/useDeleteModel.ts | 3 + web/hooks/useDownloadState.ts | 1 - web/hooks/useGetAllThreads.ts | 3 +- web/hooks/useGetAssistants.ts | 7 +- web/hooks/useGetDownloadedModels.ts | 11 ++- web/hooks/useSendChatMessage.ts | 14 +++- web/hooks/useSetActiveThread.ts | 9 ++- web/screens/Chat/ChatBody/index.tsx | 5 -- web/screens/Chat/ChatInstruction/index.tsx | 70 ------------------- web/screens/Chat/MessageToolbar/index.tsx | 66 ++++++++++------- web/screens/Chat/Sidebar/index.tsx | 23 +++--- web/screens/Chat/SimpleTextMessage/index.tsx | 4 +- web/screens/Chat/ThreadList/index.tsx | 14 ++-- web/screens/Chat/index.tsx | 17 ++--- 33 files changed, 203 insertions(+), 261 deletions(-) delete mode 100644 web/screens/Chat/ChatInstruction/index.tsx diff --git a/core/src/core.ts b/core/src/core.ts index a44a11e956..c26f867dd4 100644 --- a/core/src/core.ts +++ b/core/src/core.ts @@ -15,18 +15,6 @@ const executeOnMain: ( window.coreAPI?.invokePluginFunc(plugin, method, ...args) ?? window.electronAPI?.invokePluginFunc(plugin, method, ...args); -/** - * @deprecated This object is deprecated and should not be used. - * Use individual functions instead. - */ -const invokePluginFunc: ( - plugin: string, - method: string, - ...args: any[] -) => Promise = (plugin, method, ...args) => - window.coreAPI?.invokePluginFunc(plugin, method, ...args) ?? - window.electronAPI?.invokePluginFunc(plugin, method, ...args); - /** * Downloads a file from a URL and saves it to the local file system. * @param {string} url - The URL of the file to download. @@ -36,16 +24,7 @@ const invokePluginFunc: ( const downloadFile: (url: string, fileName: string) => Promise = ( url, fileName -) => - window.coreAPI?.downloadFile(url, fileName) ?? - window.electronAPI?.downloadFile(url, fileName); - -/** - * @deprecated This object is deprecated and should not be used. - * Use fs module instead. - */ -const deleteFile: (path: string) => Promise = (path) => - window.coreAPI?.deleteFile(path) ?? window.electronAPI?.deleteFile(path); +) => window.coreAPI?.downloadFile(url, fileName); /** * Aborts the download of a specific file. @@ -69,8 +48,16 @@ const appDataPath: () => Promise = () => window.coreAPI?.appDataPath(); const getUserSpace = (): Promise => window.coreAPI?.getUserSpace() ?? window.electronAPI?.getUserSpace(); -/** Register extension point function type definition - * +/** + * Opens the file explorer at a specific path. + * @param {string} path - The path to open in the file explorer. + * @returns {Promise} A promise that resolves when the file explorer is opened. + */ +const openFileExplorer: (path: string) => Promise = (path) => + window.coreAPI?.openFileExplorer(path); + +/** + * Register extension point function type definition */ export type RegisterExtensionPoint = ( extensionName: string, @@ -79,29 +66,14 @@ export type RegisterExtensionPoint = ( priority?: number ) => void; -/** - * @deprecated This object is deprecated and should not be used. - * Use individual functions instead. - */ -export const core = { - invokePluginFunc, - executeOnMain, - downloadFile, - abortDownload, - deleteFile, - appDataPath, - getUserSpace, -}; - /** * Functions exports */ export { - invokePluginFunc, executeOnMain, downloadFile, abortDownload, - deleteFile, appDataPath, getUserSpace, + openFileExplorer, }; diff --git a/core/src/fs.ts b/core/src/fs.ts index 460e6bf22e..b9671ce016 100644 --- a/core/src/fs.ts +++ b/core/src/fs.ts @@ -64,13 +64,13 @@ const appendFile: (path: string, data: string) => Promise = (path, data) => window.coreAPI?.appendFile(path, data) ?? window.electronAPI?.appendFile(path, data); +/** + * Reads a file line by line. + * @param {string} path - The path of the file to read. + * @returns {Promise} A promise that resolves to the lines of the file. + */ const readLineByLine: (path: string) => Promise = (path) => - window.coreAPI?.readLineByLine(path) ?? - window.electronAPI?.readLineByLine(path); - -const openFileExplorer: (path: string) => Promise = (path) => - window.coreAPI?.openFileExplorer(path) ?? - window.electronAPI?.openFileExplorer(path); + window.coreAPI?.readLineByLine(path); export const fs = { isDirectory, @@ -82,5 +82,4 @@ export const fs = { deleteFile, appendFile, readLineByLine, - openFileExplorer, }; diff --git a/core/src/index.ts b/core/src/index.ts index f7afccdb00..8d398f8b5f 100644 --- a/core/src/index.ts +++ b/core/src/index.ts @@ -1,40 +1,26 @@ -/** - * @deprecated This object is deprecated and should not be used. - * Use individual functions instead. - */ -export { core, deleteFile, invokePluginFunc } from "./core"; - /** * Core module exports. * @module */ -export { - downloadFile, - executeOnMain, - appDataPath, - getUserSpace, - abortDownload, -} from "./core"; +export * from "./core"; /** - * Events module exports. + * Events events exports. * @module */ -export { events } from "./events"; +export * from "./events"; /** * Events types exports. * @module */ -export * from "./events"; - export * from "./types/index"; /** * Filesystem module exports. * @module */ -export { fs } from "./fs"; +export * from "./fs"; /** * Plugin base module export. diff --git a/core/src/plugins/conversational.ts b/core/src/plugins/conversational.ts index dc0d68367c..63390181b9 100644 --- a/core/src/plugins/conversational.ts +++ b/core/src/plugins/conversational.ts @@ -32,5 +32,7 @@ export abstract class ConversationalPlugin extends JanPlugin { abstract addNewMessage(message: ThreadMessage): Promise; + abstract writeMessages(threadId: string, messages: ThreadMessage[]): Promise; + abstract getAllMessages(threadId: string): Promise; } diff --git a/core/src/plugins/model.ts b/core/src/plugins/model.ts index 9d263aaa1b..53d3d45651 100644 --- a/core/src/plugins/model.ts +++ b/core/src/plugins/model.ts @@ -18,11 +18,10 @@ export abstract class ModelPlugin extends JanPlugin { /** * Cancels the download of a specific model. - * @param {string} name - The name of the model to cancel the download for. * @param {string} modelId - The ID of the model to cancel the download for. * @returns {Promise} A promise that resolves when the download has been cancelled. */ - abstract cancelModelDownload(name: string, modelId: string): Promise; + abstract cancelModelDownload(modelId: string): Promise; /** * Deletes a model. diff --git a/package.json b/package.json index 036542c38b..cb670a096d 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "build:web": "yarn workspace jan-web build && cpx \"web/out/**\" \"electron/renderer/\"", "build:electron": "yarn workspace jan build", "build:electron:test": "yarn workspace jan build:test", - "build:plugins": "rimraf ./electron/core/pre-install/*.tgz && concurrently --kill-others-on-fail \"cd ./plugins/conversational-json && npm install && npm run build:publish\" \"cd ./plugins/inference-plugin && npm install && npm run build:publish\" \"cd ./plugins/model-plugin && npm install && npm run build:publish\" \"cd ./plugins/monitoring-plugin && npm install && npm run build:publish\"", + "build:plugins": "rimraf ./electron/core/pre-install/*.tgz && concurrently --kill-others-on-fail \"cd ./plugins/conversational-json && npm install && npm run build:publish\" \"cd ./plugins/inference-plugin && npm install && npm run build:publish\" \"cd ./plugins/model-plugin && npm install && npm run build:publish\" \"cd ./plugins/monitoring-plugin && npm install && npm run build:publish\" \"cd ./plugins/assistant-plugin && npm install && npm run build:publish\"", "build:test": "yarn build:web && yarn workspace jan build:test", "build": "yarn build:web && yarn workspace jan build", "build:publish": "yarn build:web && yarn workspace jan build:publish" diff --git a/plugins/conversational-json/src/index.ts b/plugins/conversational-json/src/index.ts index 5a3f05a1ac..500fcb5263 100644 --- a/plugins/conversational-json/src/index.ts +++ b/plugins/conversational-json/src/index.ts @@ -106,6 +106,27 @@ export default class JSONConversationalPlugin implements ConversationalPlugin { } } + async writeMessages( + threadId: string, + messages: ThreadMessage[] + ): Promise { + try { + const threadDirPath = join(JSONConversationalPlugin._homeDir, threadId) + const threadMessagePath = join( + threadDirPath, + JSONConversationalPlugin._threadMessagesFileName + ) + await fs.mkdir(threadDirPath) + await fs.writeFile( + threadMessagePath, + messages.map((msg) => JSON.stringify(msg)).join('\n') + ) + Promise.resolve() + } catch (err) { + Promise.reject(err) + } + } + /** * A promise builder for reading a thread from a file. * @param threadDirName the thread dir we are reading from. diff --git a/plugins/model-plugin/src/index.ts b/plugins/model-plugin/src/index.ts index c6a4687dc6..7ca63e7086 100644 --- a/plugins/model-plugin/src/index.ts +++ b/plugins/model-plugin/src/index.ts @@ -57,10 +57,10 @@ export default class JanModelPlugin implements ModelPlugin { * @param {string} modelId - The ID of the model whose download is to be cancelled. * @returns {Promise} A promise that resolves when the download has been cancelled. */ - async cancelModelDownload(name: string, modelId: string): Promise { - return abortDownload(join(JanModelPlugin._homeDir, name, modelId)).then( + async cancelModelDownload(modelId: string): Promise { + return abortDownload(join(JanModelPlugin._homeDir, modelId, modelId)).then( () => { - fs.deleteFile(join(JanModelPlugin._homeDir, name, modelId)) + fs.rmdir(join(JanModelPlugin._homeDir, modelId)) } ) } diff --git a/web/containers/CardSidebar/index.tsx b/web/containers/CardSidebar/index.tsx index 61f3c20e51..42f975aafd 100644 --- a/web/containers/CardSidebar/index.tsx +++ b/web/containers/CardSidebar/index.tsx @@ -1,10 +1,12 @@ import { ReactNode, useState } from 'react' import { Fragment } from 'react' + import { Menu, Transition } from '@headlessui/react' import { ChevronDownIcon, EllipsisVerticalIcon, } from '@heroicons/react/20/solid' +import { twMerge } from 'tailwind-merge' interface Props { children: ReactNode @@ -12,11 +14,6 @@ interface Props { onRevealInFinderClick: (type: string) => void onViewJsonClick: (type: string) => void } - -function classNames(...classes: any) { - return classes.filter(Boolean).join(' ') -} - export default function CardSidebar({ children, title, @@ -58,7 +55,7 @@ export default function CardSidebar({ {({ active }) => ( onRevealInFinderClick(title)} - className={classNames( + className={twMerge( active ? 'bg-gray-50' : '', 'block cursor-pointer px-3 py-1 text-xs leading-6 text-gray-900' )} @@ -71,7 +68,7 @@ export default function CardSidebar({ {({ active }) => ( onViewJsonClick(title)} - className={classNames( + className={twMerge( active ? 'bg-gray-50' : '', 'block cursor-pointer px-3 py-1 text-xs leading-6 text-gray-900' )} diff --git a/web/containers/DropdownListSidebar/index.tsx b/web/containers/DropdownListSidebar/index.tsx index 1cab4ce0df..91d194fbdb 100644 --- a/web/containers/DropdownListSidebar/index.tsx +++ b/web/containers/DropdownListSidebar/index.tsx @@ -1,13 +1,13 @@ import { Fragment, useEffect, useState } from 'react' + import { Listbox, Transition } from '@headlessui/react' import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid' -import { getDownloadedModels } from '@/hooks/useGetDownloadedModels' + import { Model } from '@janhq/core/lib/types' import { atom, useSetAtom } from 'jotai' +import { twMerge } from 'tailwind-merge' -function classNames(...classes: any) { - return classes.filter(Boolean).join(' ') -} +import { getDownloadedModels } from '@/hooks/useGetDownloadedModels' export const selectedModelAtom = atom(undefined) @@ -62,7 +62,7 @@ export default function DropdownListSidebar() { - classNames( + twMerge( active ? 'bg-indigo-600 text-white' : 'text-gray-900', 'relative cursor-default select-none py-2 pl-3 pr-9' ) @@ -72,7 +72,7 @@ export default function DropdownListSidebar() { {({ selected, active }) => ( <>
{title} -
+ (PluginType.Model) - ?.cancelModelDownload(model.name, item.fileName) + ?.cancelModelDownload(item.modelId) } }} > diff --git a/web/containers/Layout/TopBar/CommandSearch/index.tsx b/web/containers/Layout/TopBar/CommandSearch/index.tsx index 55ee5910cb..31896e81f4 100644 --- a/web/containers/Layout/TopBar/CommandSearch/index.tsx +++ b/web/containers/Layout/TopBar/CommandSearch/index.tsx @@ -11,6 +11,7 @@ import { CommandList, } from '@janhq/uikit' +import { useSetAtom } from 'jotai' import { MessageCircleIcon, SettingsIcon, @@ -26,7 +27,7 @@ import { FeatureToggleContext } from '@/context/FeatureToggle' import { MainViewState } from '@/constants/screens' import { useMainViewState } from '@/hooks/useMainViewState' -import { useSetAtom } from 'jotai' + import { showRightSideBarAtom } from '@/screens/Chat/Sidebar' export default function CommandSearch() { diff --git a/web/containers/ModalCancelDownload/index.tsx b/web/containers/ModalCancelDownload/index.tsx index 4ac2509377..07d62fa0ce 100644 --- a/web/containers/ModalCancelDownload/index.tsx +++ b/web/containers/ModalCancelDownload/index.tsx @@ -1,8 +1,8 @@ import { useMemo } from 'react' -import { Model } from '@janhq/core/lib/types' import { PluginType } from '@janhq/core' import { ModelPlugin } from '@janhq/core/lib/plugins' +import { Model } from '@janhq/core/lib/types' import { Modal, @@ -79,7 +79,7 @@ export default function ModalCancelDownload({ if (!model) return pluginManager .get(PluginType.Model) - ?.cancelModelDownload(model.name, downloadState.fileName) + ?.cancelModelDownload(downloadState.modelId) } }} > diff --git a/web/containers/Providers/EventHandler.tsx b/web/containers/Providers/EventHandler.tsx index 78a392b09b..de4a8c9a4b 100644 --- a/web/containers/Providers/EventHandler.tsx +++ b/web/containers/Providers/EventHandler.tsx @@ -16,7 +16,6 @@ import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import { addNewMessageAtom, - chatMessages, updateMessageAtom, } from '@/helpers/atoms/ChatMessage.atom' import { @@ -35,15 +34,12 @@ export default function EventHandler({ children }: { children: ReactNode }) { const updateConvWaiting = useSetAtom(updateConversationWaitingForResponseAtom) const models = useAtomValue(downloadingModelsAtom) - const messages = useAtomValue(chatMessages) - const conversations = useAtomValue(threadsAtom) - const messagesRef = useRef(messages) - const convoRef = useRef(conversations) + const threads = useAtomValue(threadsAtom) + const threadsRef = useRef(threads) useEffect(() => { - messagesRef.current = messages - convoRef.current = conversations - }, [messages, conversations]) + threadsRef.current = threads + }, [threads]) async function handleNewMessageResponse(message: ThreadMessage) { addNewMessage(message) @@ -59,7 +55,6 @@ export default function EventHandler({ children }: { children: ReactNode }) { } async function handleMessageResponseFinished(message: ThreadMessage) { - if (!convoRef.current) return updateConvWaiting(message.thread_id, false) if (message.id && message.content) { @@ -70,8 +65,7 @@ export default function EventHandler({ children }: { children: ReactNode }) { MessageStatus.Ready ) } - - const thread = convoRef.current.find((e) => e.id == message.thread_id) + const thread = threadsRef.current?.find((e) => e.id == message.thread_id) if (thread) { const messageContent = message.content[0]?.text.value ?? '' const metadata = { @@ -93,6 +87,7 @@ export default function EventHandler({ children }: { children: ReactNode }) { function handleDownloadUpdate(state: any) { if (!state) return + state.fileName = state.fileName.split('/').pop() ?? '' setDownloadState(state) } diff --git a/web/helpers/atoms/ChatMessage.atom.ts b/web/helpers/atoms/ChatMessage.atom.ts index 2cd0474c09..0aae397b86 100644 --- a/web/helpers/atoms/ChatMessage.atom.ts +++ b/web/helpers/atoms/ChatMessage.atom.ts @@ -92,7 +92,7 @@ export const cleanConversationMessages = atom(null, (get, set, id: string) => { set(chatMessages, newData) }) -export const deleteMessage = atom(null, (get, set, id: string) => { +export const deleteMessageAtom = atom(null, (get, set, id: string) => { const newData: Record = { ...get(chatMessages), } diff --git a/web/hooks/useActiveModel.ts b/web/hooks/useActiveModel.ts index 5cf73d7706..ca189a68a0 100644 --- a/web/hooks/useActiveModel.ts +++ b/web/hooks/useActiveModel.ts @@ -3,8 +3,11 @@ import { PluginType } from '@janhq/core' import { InferencePlugin } from '@janhq/core/lib/plugins' import { Model, ModelSettingParams } from '@janhq/core/lib/types' import { atom, useAtom } from 'jotai' + import { toaster } from '@/containers/Toast' + import { useGetDownloadedModels } from './useGetDownloadedModels' + import { pluginManager } from '@/plugin' const activeModelAtom = atom(undefined) diff --git a/web/hooks/useCreateNewThread.ts b/web/hooks/useCreateNewThread.ts index c397b329c5..6c01baf117 100644 --- a/web/hooks/useCreateNewThread.ts +++ b/web/hooks/useCreateNewThread.ts @@ -5,7 +5,9 @@ import { ThreadState, } from '@janhq/core/lib/types' import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai' + import { generateThreadId } from '@/utils/conversation' + import { threadsAtom, setActiveThreadIdAtom, @@ -65,7 +67,7 @@ export const useCreateNewThread = () => { const thread: Thread = { id: threadId, object: 'thread', - title: '', + title: 'New Thread', assistants: [assistantInfo], created: createdAt, updated: createdAt, diff --git a/web/hooks/useDeleteConversation.ts b/web/hooks/useDeleteConversation.ts index d793f6788d..ee38b139e2 100644 --- a/web/hooks/useDeleteConversation.ts +++ b/web/hooks/useDeleteConversation.ts @@ -1,4 +1,4 @@ -import { PluginType } from '@janhq/core' +import { ChatCompletionRole, PluginType } from '@janhq/core' import { ConversationalPlugin } from '@janhq/core/lib/plugins' import { useAtom, useAtomValue, useSetAtom } from 'jotai' @@ -13,6 +13,7 @@ import { useActiveModel } from './useActiveModel' import { cleanConversationMessages, deleteConversationMessage, + getCurrentChatMessagesAtom, } from '@/helpers/atoms/ChatMessage.atom' import { threadsAtom, @@ -22,27 +23,26 @@ import { export default function useDeleteThread() { const { activeModel } = useActiveModel() - const [userConversations, setUserConversations] = useAtom(threadsAtom) + const [threads, setThreads] = useAtom(threadsAtom) const setCurrentPrompt = useSetAtom(currentPromptAtom) const activeThreadId = useAtomValue(getActiveThreadIdAtom) + const messages = useAtomValue(getCurrentChatMessagesAtom) const setActiveConvoId = useSetAtom(setActiveThreadIdAtom) const deleteMessages = useSetAtom(deleteConversationMessage) const cleanMessages = useSetAtom(cleanConversationMessages) - const cleanConvo = async () => { + const cleanThread = async () => { if (activeThreadId) { - const currentConversation = userConversations.filter( - (c) => c.id === activeThreadId - )[0] + const thread = threads.filter((c) => c.id === activeThreadId)[0] cleanMessages(activeThreadId) - if (currentConversation) + if (thread) await pluginManager .get(PluginType.Conversational) - ?.saveThread({ - ...currentConversation, - id: activeThreadId, - }) + ?.writeMessages( + activeThreadId, + messages.filter((msg) => msg.role === ChatCompletionRole.System) + ) } } @@ -55,18 +55,16 @@ export default function useDeleteThread() { await pluginManager .get(PluginType.Conversational) ?.deleteThread(activeThreadId) - const currentConversations = userConversations.filter( - (c) => c.id !== activeThreadId - ) - setUserConversations(currentConversations) + const availableThreads = threads.filter((c) => c.id !== activeThreadId) + setThreads(availableThreads) deleteMessages(activeThreadId) setCurrentPrompt('') toaster({ title: 'Chat successfully deleted.', description: `Chat with ${activeModel?.name} has been successfully deleted.`, }) - if (currentConversations.length > 0) { - setActiveConvoId(currentConversations[0].id) + if (availableThreads.length > 0) { + setActiveConvoId(availableThreads[0].id) } else { setActiveConvoId(undefined) } @@ -76,7 +74,7 @@ export default function useDeleteThread() { } return { - cleanConvo, + cleanThread, deleteThread, } } diff --git a/web/hooks/useDeleteModel.ts b/web/hooks/useDeleteModel.ts index 6dada49be4..696943f4ac 100644 --- a/web/hooks/useDeleteModel.ts +++ b/web/hooks/useDeleteModel.ts @@ -1,8 +1,11 @@ import { PluginType } from '@janhq/core' import { ModelPlugin } from '@janhq/core/lib/plugins' import { Model } from '@janhq/core/lib/types' + import { toaster } from '@/containers/Toast' + import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' + import { pluginManager } from '@/plugin/PluginManager' export default function useDeleteModel() { diff --git a/web/hooks/useDownloadState.ts b/web/hooks/useDownloadState.ts index 3af336dd80..a4a4c9f9bf 100644 --- a/web/hooks/useDownloadState.ts +++ b/web/hooks/useDownloadState.ts @@ -10,7 +10,6 @@ const setDownloadStateAtom = atom(null, (get, set, state: DownloadState) => { console.debug( `current download state for ${state.fileName} is ${JSON.stringify(state)}` ) - state.fileName = state.fileName.replace('models/', '') currentState[state.fileName] = state set(modelDownloadStateAtom, currentState) }) diff --git a/web/hooks/useGetAllThreads.ts b/web/hooks/useGetAllThreads.ts index 8f9a1a39f3..e27b9a0391 100644 --- a/web/hooks/useGetAllThreads.ts +++ b/web/hooks/useGetAllThreads.ts @@ -1,6 +1,7 @@ import { PluginType, ThreadState } from '@janhq/core' import { ConversationalPlugin } from '@janhq/core/lib/plugins' import { useSetAtom } from 'jotai' + import { threadStatesAtom, threadsAtom, @@ -19,7 +20,7 @@ const useGetAllThreads = () => { const threadStates: Record = {} threads?.forEach((thread) => { if (thread.id != null) { - const lastMessage = thread.metadata?.lastMessage ?? '' + const lastMessage = (thread.metadata?.lastMessage as string) ?? '' threadStates[thread.id] = { hasMore: true, waitingForResponse: false, diff --git a/web/hooks/useGetAssistants.ts b/web/hooks/useGetAssistants.ts index c899a46b85..0d16add285 100644 --- a/web/hooks/useGetAssistants.ts +++ b/web/hooks/useGetAssistants.ts @@ -1,7 +1,10 @@ -import { pluginManager } from '@/plugin/PluginManager' +import { useEffect, useState } from 'react' + import { Assistant, PluginType } from '@janhq/core' + import { AssistantPlugin } from '@janhq/core/lib/plugins' -import { useEffect, useState } from 'react' + +import { pluginManager } from '@/plugin/PluginManager' const getAssistants = async (): Promise => { return ( diff --git a/web/hooks/useGetDownloadedModels.ts b/web/hooks/useGetDownloadedModels.ts index c51b62ea88..b83918d297 100644 --- a/web/hooks/useGetDownloadedModels.ts +++ b/web/hooks/useGetDownloadedModels.ts @@ -1,11 +1,17 @@ -import { useEffect, useState } from 'react' +import { useEffect } from 'react' + import { PluginType } from '@janhq/core' import { ModelPlugin } from '@janhq/core/lib/plugins' import { Model } from '@janhq/core/lib/types' + +import { atom, useAtom } from 'jotai' + import { pluginManager } from '@/plugin/PluginManager' +const downloadedModelsAtom = atom([]) + export function useGetDownloadedModels() { - const [downloadedModels, setDownloadedModels] = useState([]) + const [downloadedModels, setDownloadedModels] = useAtom(downloadedModelsAtom) useEffect(() => { getDownloadedModels().then((downloadedModels) => { @@ -20,5 +26,6 @@ export async function getDownloadedModels(): Promise { const models = await pluginManager .get(PluginType.Model) ?.getDownloadedModels() + return models ?? [] } diff --git a/web/hooks/useSendChatMessage.ts b/web/hooks/useSendChatMessage.ts index 071141eb86..487e3d8eda 100644 --- a/web/hooks/useSendChatMessage.ts +++ b/web/hooks/useSendChatMessage.ts @@ -15,8 +15,11 @@ import { useAtom, useAtomValue, useSetAtom } from 'jotai' import { ulid } from 'ulid' +import { selectedModelAtom } from '@/containers/DropdownListSidebar' import { currentPromptAtom } from '@/containers/Providers/Jotai' +import { toaster } from '@/containers/Toast' + import { useActiveModel } from './useActiveModel' import { @@ -29,7 +32,6 @@ import { updateConversationWaitingForResponseAtom, } from '@/helpers/atoms/Conversation.atom' import { pluginManager } from '@/plugin/PluginManager' -import { selectedModelAtom } from '@/containers/DropdownListSidebar' export default function useSendChatMessage() { const activeThread = useAtomValue(activeThreadAtom) @@ -41,6 +43,7 @@ export default function useSendChatMessage() { const currentMessages = useAtomValue(getCurrentChatMessagesAtom) const { activeModel } = useActiveModel() const selectedModel = useAtomValue(selectedModelAtom) + const { startModel } = useActiveModel() function updateThreadTitle(newMessage: MessageRequest) { if ( @@ -95,7 +98,7 @@ export default function useSendChatMessage() { if (!activeThread.isFinishInit) { if (!selectedModel) { - alert('Please select a model') + toaster({ title: 'Please select a model' }) return } const assistantId = activeThread.assistants[0].assistant_id ?? '' @@ -169,12 +172,17 @@ export default function useSendChatMessage() { } addNewMessage(threadMessage) + updateThreadTitle(messageRequest) + await pluginManager .get(PluginType.Conversational) ?.addNewMessage(threadMessage) + const modelId = selectedModel?.id ?? activeThread.assistants[0].model.id + if (activeModel?.id !== modelId) { + await startModel(modelId) + } events.emit(EventName.OnNewMessageRequest, messageRequest) - updateThreadTitle(messageRequest) } return { diff --git a/web/hooks/useSetActiveThread.ts b/web/hooks/useSetActiveThread.ts index 1137b6cbb9..36e69a14a1 100644 --- a/web/hooks/useSetActiveThread.ts +++ b/web/hooks/useSetActiveThread.ts @@ -1,12 +1,15 @@ +import { PluginType, Thread } from '@janhq/core' + +import { ConversationalPlugin } from '@janhq/core/lib/plugins' + +import { useAtomValue, useSetAtom } from 'jotai' + import { setConvoMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' import { getActiveThreadIdAtom, setActiveThreadIdAtom, } from '@/helpers/atoms/Conversation.atom' import { pluginManager } from '@/plugin' -import { PluginType, Thread } from '@janhq/core' -import { ConversationalPlugin } from '@janhq/core/lib/plugins' -import { useAtomValue, useSetAtom } from 'jotai' export default function useSetActiveThread() { const activeThreadId = useAtomValue(getActiveThreadIdAtom) diff --git a/web/screens/Chat/ChatBody/index.tsx b/web/screens/Chat/ChatBody/index.tsx index a477f75dc6..10d0086613 100644 --- a/web/screens/Chat/ChatBody/index.tsx +++ b/web/screens/Chat/ChatBody/index.tsx @@ -1,12 +1,8 @@ import { useAtomValue } from 'jotai' -import ChatInstruction from '../ChatInstruction' import ChatItem from '../ChatItem' import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' -import { useActiveModel } from '@/hooks/useActiveModel' -import { activeThreadAtom } from '@/helpers/atoms/Conversation.atom' -import { useEffect } from 'react' const ChatBody: React.FC = () => { const messages = useAtomValue(getCurrentChatMessagesAtom) @@ -15,7 +11,6 @@ const ChatBody: React.FC = () => { {messages.map((message) => ( ))} - {messages.length === 0 && } ) } diff --git a/web/screens/Chat/ChatInstruction/index.tsx b/web/screens/Chat/ChatInstruction/index.tsx deleted file mode 100644 index bff82101e9..0000000000 --- a/web/screens/Chat/ChatInstruction/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useState } from 'react' - -import { - ChatCompletionRole, - EventName, - MessageStatus, - ThreadMessage, - events, -} from '@janhq/core' - -import { Button, Textarea } from '@janhq/uikit' -import { useAtomValue } from 'jotai' - -import { getActiveConvoIdAtom } from '@/helpers/atoms/Conversation.atom' - -const ChatInstruction = () => { - const activeConvoId = useAtomValue(getActiveConvoIdAtom) - const [isSettingInstruction, setIsSettingInstruction] = useState(false) - const [instruction, setInstruction] = useState('') - const setSystemPrompt = (instruction: string) => { - const message: ThreadMessage = { - id: 'system-prompt', - content: instruction, - role: ChatCompletionRole.System, - status: MessageStatus.Ready, - createdAt: new Date().toISOString(), - threadId: activeConvoId, - } - events.emit(EventName.OnNewMessageResponse, message) - events.emit(EventName.OnMessageResponseFinished, message) - } - return ( -
-

- What does this Assistant do? How does it behave? What should it avoid - doing? -

- {!isSettingInstruction && activeConvoId && ( - <> - - - )} - {isSettingInstruction && ( -
-