diff --git a/plugins/inference-plugin/nitro/version.txt b/plugins/inference-plugin/nitro/version.txt index a34eaa5d0..44a7df273 100644 --- a/plugins/inference-plugin/nitro/version.txt +++ b/plugins/inference-plugin/nitro/version.txt @@ -1 +1 @@ -0.1.11 \ No newline at end of file +0.1.17 \ No newline at end of file diff --git a/plugins/inference-plugin/src/module.ts b/plugins/inference-plugin/src/module.ts index a1a1d4ea0..74cc9d89c 100644 --- a/plugins/inference-plugin/src/module.ts +++ b/plugins/inference-plugin/src/module.ts @@ -25,6 +25,7 @@ let currentModelFile = null; */ interface InitModelResponse { error?: any; + modelFile?: string; } /** @@ -51,7 +52,7 @@ function initModel(modelFile: string): Promise { .then(validateModelStatus) .catch((err) => { log.error("error: " + JSON.stringify(err)); - return { error: err }; + return { error: err, modelFile }; }) ); } diff --git a/web/containers/Layout/Ribbon/index.tsx b/web/containers/Layout/Ribbon/index.tsx index 09f29ced7..6babadb9d 100644 --- a/web/containers/Layout/Ribbon/index.tsx +++ b/web/containers/Layout/Ribbon/index.tsx @@ -20,15 +20,12 @@ import { twMerge } from 'tailwind-merge' import LogoMark from '@/containers/Brand/Logo/Mark' -import { FeatureToggleContext } from '@/context/FeatureToggle' - import { MainViewState } from '@/constants/screens' import { useMainViewState } from '@/hooks/useMainViewState' export default function RibbonNav() { const { mainViewState, setMainViewState } = useMainViewState() - const { experimentalFeatureEnabed } = useContext(FeatureToggleContext) const onMenuClick = (state: MainViewState) => { if (mainViewState === state) return @@ -49,8 +46,6 @@ export default function RibbonNav() { ] const secondaryMenus = [ - // Add menu if experimental feature - ...(experimentalFeatureEnabed ? [] : []), { name: 'Explore Models', icon: , diff --git a/web/containers/Layout/TopBar/CommandSearch/index.tsx b/web/containers/Layout/TopBar/CommandSearch/index.tsx index cd7b815d6..d5eeff3fd 100644 --- a/web/containers/Layout/TopBar/CommandSearch/index.tsx +++ b/web/containers/Layout/TopBar/CommandSearch/index.tsx @@ -28,7 +28,6 @@ import { MainViewState } from '@/constants/screens' import { useMainViewState } from '@/hooks/useMainViewState' export default function CommandSearch() { - const { experimentalFeatureEnabed } = useContext(FeatureToggleContext) const { setMainViewState } = useMainViewState() const menus = [ @@ -44,8 +43,6 @@ export default function CommandSearch() { ), state: MainViewState.Chat, }, - // Added experimental feature here - ...(experimentalFeatureEnabed ? [] : []), { name: 'Explore Models', icon: , diff --git a/web/hooks/useActiveModel.ts b/web/hooks/useActiveModel.ts index db70b0aeb..eb207a8fd 100644 --- a/web/hooks/useActiveModel.ts +++ b/web/hooks/useActiveModel.ts @@ -31,6 +31,8 @@ export function useActiveModel() { return } + setActiveModel(undefined) + setStateModel({ state: 'start', loading: true, model: modelId }) const model = downloadedModels.find((e) => e.id === modelId) @@ -52,7 +54,7 @@ export function useActiveModel() { console.debug('Init model: ', modelId) const path = join('models', model.name, modelId) const res = await initModel(path) - if (res?.error && (!activeModel?.id || modelId === activeModel?.id)) { + if (res && res.error && res.modelFile === stateModel.model) { const errorMessage = `${res.error}` alert(errorMessage) setStateModel(() => ({ @@ -60,7 +62,6 @@ export function useActiveModel() { loading: false, model: modelId, })) - setActiveModel(undefined) } else { console.debug( `Init model ${modelId} successfully!, take ${ diff --git a/web/hooks/useSendChatMessage.ts b/web/hooks/useSendChatMessage.ts index ea1111c15..42ccda9b2 100644 --- a/web/hooks/useSendChatMessage.ts +++ b/web/hooks/useSendChatMessage.ts @@ -59,6 +59,7 @@ export default function useSendChatMessage() { ...newMessage, messages: newMessage.messages?.slice(0, -1).concat([summaryMsg]), }) + .catch(console.error) if ( currentConvo && currentConvo.id === newMessage.threadId && diff --git a/web/screens/Chat/ChatBody/index.tsx b/web/screens/Chat/ChatBody/index.tsx index c0bd74f52..b34b4fae1 100644 --- a/web/screens/Chat/ChatBody/index.tsx +++ b/web/screens/Chat/ChatBody/index.tsx @@ -1,9 +1,5 @@ -import { useContext } from 'react' - import { useAtomValue } from 'jotai' -import { FeatureToggleContext } from '@/context/FeatureToggle' - import ChatInstruction from '../ChatInstruction' import ChatItem from '../ChatItem' @@ -11,15 +7,12 @@ import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' const ChatBody: React.FC = () => { const messages = useAtomValue(getCurrentChatMessagesAtom) - const { experimentalFeatureEnabed } = useContext(FeatureToggleContext) return (
{messages.map((message) => ( ))} - {experimentalFeatureEnabed && messages.length === 0 && ( - - )} + {messages.length === 0 && }
) } diff --git a/web/screens/Chat/ChatInstruction/index.tsx b/web/screens/Chat/ChatInstruction/index.tsx index 2931926c2..bff82101e 100644 --- a/web/screens/Chat/ChatInstruction/index.tsx +++ b/web/screens/Chat/ChatInstruction/index.tsx @@ -35,14 +35,16 @@ const ChatInstruction = () => { What does this Assistant do? How does it behave? What should it avoid doing?

- {!isSettingInstruction && ( - + {!isSettingInstruction && activeConvoId && ( + <> + + )} {isSettingInstruction && (
diff --git a/web/screens/Chat/ChatItem/index.tsx b/web/screens/Chat/ChatItem/index.tsx index a085c3dc8..5f192d436 100644 --- a/web/screens/Chat/ChatItem/index.tsx +++ b/web/screens/Chat/ChatItem/index.tsx @@ -7,7 +7,10 @@ import SimpleTextMessage from '../SimpleTextMessage' type Ref = HTMLDivElement const ChatItem = forwardRef((message, ref) => ( -
+
)) diff --git a/web/screens/Chat/HistoryList/index.tsx b/web/screens/Chat/HistoryList/index.tsx index 3d5f47a22..a6f4919dc 100644 --- a/web/screens/Chat/HistoryList/index.tsx +++ b/web/screens/Chat/HistoryList/index.tsx @@ -11,7 +11,6 @@ import { twMerge } from 'tailwind-merge' import { useActiveModel } from '@/hooks/useActiveModel' import { useCreateConversation } from '@/hooks/useCreateConversation' -import { useGetDownloadedModels } from '@/hooks/useGetDownloadedModels' import useGetUserConversations from '@/hooks/useGetUserConversations' import { displayDate } from '@/utils/datetime' @@ -27,11 +26,10 @@ export default function HistoryList() { const conversations = useAtomValue(userConversationsAtom) const threadStates = useAtomValue(conversationStatesAtom) const { getUserConversations } = useGetUserConversations() - const { activeModel, startModel } = useActiveModel() + const { activeModel } = useActiveModel() const { requestCreateConvo } = useCreateConversation() const activeConvoId = useAtomValue(getActiveConvoIdAtom) const setActiveConvoId = useSetAtom(setActiveConvoIdAtom) - const { downloadedModels } = useGetDownloadedModels() useEffect(() => { getUserConversations() @@ -48,14 +46,6 @@ export default function HistoryList() { console.debug('modelId is undefined') return } - const model = downloadedModels.find((e) => e.id === convo.modelId) - if (convo == null) { - console.debug('modelId is undefined') - return - } - if (model != null) { - startModel(model.id) - } if (activeConvoId !== convo.id) { setActiveConvoId(convo.id) } diff --git a/web/screens/Chat/MessageToolbar/index.tsx b/web/screens/Chat/MessageToolbar/index.tsx index aafe142c2..5b2a9ccf8 100644 --- a/web/screens/Chat/MessageToolbar/index.tsx +++ b/web/screens/Chat/MessageToolbar/index.tsx @@ -35,63 +35,69 @@ const MessageToolbar = ({ message }: { message: ThreadMessage }) => { } return (
- {message.status === MessageStatus.Pending && ( - stopInference()} - /> - )} - {message.status !== MessageStatus.Pending && - message.id === messages[0]?.id && ( - { - const messageRequest: MessageRequest = { - id: message.id ?? '', - messages: messages - .slice(1, messages.length) - .reverse() - .map((e) => { - return { - content: e.content, - role: e.role, - } as ChatCompletionMessage - }), - threadId: message.threadId ?? '', - } - if (message.role === ChatCompletionRole.Assistant) { - deleteAMessage(message.id ?? '') - } - events.emit(EventName.OnNewMessageRequest, messageRequest) - }} - /> +
+ {message.status === MessageStatus.Pending && ( +
stopInference()} + > + +
)} - { - navigator.clipboard.writeText(message.content ?? '') - toaster({ - title: 'Copied to clipboard', - }) - }} - /> - { - deleteAMessage(message.id ?? '') - if (thread) - await pluginManager - .get(PluginType.Conversational) - ?.saveConversation({ - ...thread, - messages: messages.filter((e) => e.id !== message.id), - }) - }} - /> + {message.status !== MessageStatus.Pending && + message.id === messages[0]?.id && ( +
{ + const messageRequest: MessageRequest = { + id: message.id ?? '', + messages: messages + .slice(1, messages.length) + .reverse() + .map((e) => { + return { + content: e.content, + role: e.role, + } as ChatCompletionMessage + }), + threadId: message.threadId ?? '', + } + if (message.role === ChatCompletionRole.Assistant) { + deleteAMessage(message.id ?? '') + } + events.emit(EventName.OnNewMessageRequest, messageRequest) + }} + > + +
+ )} +
{ + navigator.clipboard.writeText(message.content ?? '') + toaster({ + title: 'Copied to clipboard', + }) + }} + > + +
+
{ + deleteAMessage(message.id ?? '') + if (thread) + await pluginManager + .get(PluginType.Conversational) + ?.saveConversation({ + ...thread, + messages: messages.filter((e) => e.id !== message.id), + }) + }} + > + +
+
) } diff --git a/web/screens/Chat/SimpleTextMessage/index.tsx b/web/screens/Chat/SimpleTextMessage/index.tsx index c12ab5287..9bfaf9d1c 100644 --- a/web/screens/Chat/SimpleTextMessage/index.tsx +++ b/web/screens/Chat/SimpleTextMessage/index.tsx @@ -5,6 +5,7 @@ import { ChatCompletionRole, MessageStatus, ThreadMessage } from '@janhq/core' import hljs from 'highlight.js' +import { useAtomValue } from 'jotai' import { Marked } from 'marked' import { markedHighlight } from 'marked-highlight' @@ -21,6 +22,8 @@ import { displayDate } from '@/utils/datetime' import MessageToolbar from '../MessageToolbar' +import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom' + const marked = new Marked( markedHighlight({ langPrefix: 'hljs', @@ -47,7 +50,6 @@ const marked = new Marked( ) const SimpleTextMessage: React.FC = (props) => { - const { experimentalFeatureEnabed } = useContext(FeatureToggleContext) const parsedText = marked.parse(props.content ?? '') const isUser = props.role === ChatCompletionRole.User const isSystem = props.role === ChatCompletionRole.System @@ -55,9 +57,10 @@ const SimpleTextMessage: React.FC = (props) => { const [lastTimestamp, setLastTimestamp] = useState() const [tokenSpeed, setTokenSpeed] = useState(0) + const messages = useAtomValue(getCurrentChatMessagesAtom) useEffect(() => { - if (props.status === MessageStatus.Ready || !experimentalFeatureEnabed) { + if (props.status === MessageStatus.Ready) { return } const currentTimestamp = new Date().getTime() // Get current time in milliseconds @@ -73,25 +76,30 @@ const SimpleTextMessage: React.FC = (props) => { setTokenSpeed(averageTokenSpeed) setTokenCount(totalTokenCount) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.content]) return ( -
+
{!isUser && !isSystem && }
{props.role}

{displayDate(props.createdAt)}

- - {experimentalFeatureEnabed && ( -
- -
- )} +
+ +
@@ -111,12 +119,11 @@ const SimpleTextMessage: React.FC = (props) => { )}
- {experimentalFeatureEnabed && - (props.status === MessageStatus.Pending || tokenSpeed > 0) && ( -

- Token Speed: {Number(tokenSpeed).toFixed(2)}/s -

- )} + {(props.status === MessageStatus.Pending || tokenSpeed > 0) && ( +

+ Token Speed: {Number(tokenSpeed).toFixed(2)}/s +

+ )}
) } diff --git a/web/screens/Chat/index.tsx b/web/screens/Chat/index.tsx index 7b1f50195..492880e00 100644 --- a/web/screens/Chat/index.tsx +++ b/web/screens/Chat/index.tsx @@ -10,9 +10,12 @@ import { twMerge } from 'tailwind-merge' import { currentPromptAtom } from '@/containers/Providers/Jotai' -import { FeatureToggleContext } from '@/context/FeatureToggle' import ShortCut from '@/containers/Shortcut' +import { toaster } from '@/containers/Toast' + +import { FeatureToggleContext } from '@/context/FeatureToggle' + import { MainViewState } from '@/constants/screens' import { useActiveModel } from '@/hooks/useActiveModel' @@ -61,9 +64,13 @@ const ChatScreen = () => { const [isModelAvailable, setIsModelAvailable] = useState( downloadedModels.some((x) => x.id === currentConvo?.modelId) ) - const { experimentalFeatureEnabed } = useContext(FeatureToggleContext) - const textareaRef = useRef(null) + const { startModel } = useActiveModel() + const modelRef = useRef(activeModel) + + useEffect(() => { + modelRef.current = activeModel + }, [activeModel]) useEffect(() => { getUserConversations() @@ -81,6 +88,24 @@ const ChatScreen = () => { }, [currentConvo, downloadedModels]) const handleSendMessage = async () => { + if (!activeModel || activeModel.id !== currentConvo?.modelId) { + const model = downloadedModels.find((e) => e.id === currentConvo?.modelId) + + // Model is available to start + if (model != null) { + toaster({ + title: 'Message queued.', + description: 'It will be sent once the model is done loading.', + }) + startModel(model.id).then(() => { + setTimeout(() => { + if (modelRef?.current?.id === currentConvo?.modelId) + sendChatMessage() + }, 300) + }) + } + return + } if (activeConversationId) { sendChatMessage() } else { @@ -149,20 +174,16 @@ const ChatScreen = () => { Download Model )} - {experimentalFeatureEnabed && ( - cleanConvo()} - /> - )} - { - deleteConvo()} - /> - } + cleanConvo()} + /> + deleteConvo()} + />
@@ -206,11 +227,7 @@ const ChatScreen = () => { ref={textareaRef} onKeyDown={(e) => handleKeyDown(e)} placeholder="Type your message ..." - disabled={ - !activeModel || - stateModel.loading || - activeModel.id !== currentConvo?.modelId - } + disabled={stateModel.loading || !currentConvo} value={currentPrompt} onChange={(e) => { handleMessageChange(e) @@ -218,8 +235,8 @@ const ChatScreen = () => { />