Add empty model conversation (#254)
* add empty conversation model selection Signed-off-by: James <james@jan.ai> * chore: using secondary button instead of sidebar button 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
29d8c30c3e
commit
00c944c0b5
@ -1,7 +1,7 @@
|
|||||||
import { currentProductAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import ModelTable from "../ModelTable";
|
import ModelTable from "../ModelTable";
|
||||||
|
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
const ActiveModelTable: React.FC = () => {
|
const ActiveModelTable: React.FC = () => {
|
||||||
const activeModel = useAtomValue(currentProductAtom);
|
const activeModel = useAtomValue(currentProductAtom);
|
||||||
|
|||||||
@ -2,9 +2,8 @@ import { Product } from "@/_models/Product";
|
|||||||
import DownloadModelContent from "../DownloadModelContent";
|
import DownloadModelContent from "../DownloadModelContent";
|
||||||
import ModelDownloadButton from "../ModelDownloadButton";
|
import ModelDownloadButton from "../ModelDownloadButton";
|
||||||
import ModelDownloadingButton from "../ModelDownloadingButton";
|
import ModelDownloadingButton from "../ModelDownloadingButton";
|
||||||
import ViewModelDetailButton from "../ViewModelDetailButton";
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { modelDownloadStateAtom } from "@/_helpers/JotaiWrapper";
|
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
product: Product;
|
product: Product;
|
||||||
@ -36,8 +35,6 @@ const AvailableModelCard: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleViewDetails = () => {};
|
|
||||||
|
|
||||||
const downloadButton = isDownloading ? (
|
const downloadButton = isDownloading ? (
|
||||||
<div className="w-1/5 flex items-start justify-end">
|
<div className="w-1/5 flex items-start justify-end">
|
||||||
<ModelDownloadingButton total={total} value={transferred} />
|
<ModelDownloadingButton total={total} value={transferred} />
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { showingAdvancedPromptAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import SecondaryButton from "../SecondaryButton";
|
import SecondaryButton from "../SecondaryButton";
|
||||||
import SendButton from "../SendButton";
|
import SendButton from "../SendButton";
|
||||||
|
import { showingAdvancedPromptAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
const BasicPromptAccessories: React.FC = () => {
|
const BasicPromptAccessories: React.FC = () => {
|
||||||
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom);
|
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
|
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
|
||||||
import { showingAdvancedPromptAtom } from "@/_helpers/JotaiWrapper";
|
import { showingAdvancedPromptAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
const BasicPromptButton: React.FC = () => {
|
const BasicPromptButton: React.FC = () => {
|
||||||
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom);
|
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom);
|
||||||
|
|||||||
@ -1,22 +1,45 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
|
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
|
||||||
|
import { getActiveConvoIdAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
import { selectedModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
import useCreateConversation from "@/_hooks/useCreateConversation";
|
||||||
|
import useInitModel from "@/_hooks/useInitModel";
|
||||||
import useSendChatMessage from "@/_hooks/useSendChatMessage";
|
import useSendChatMessage from "@/_hooks/useSendChatMessage";
|
||||||
import { useAtom } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
|
import { ChangeEvent } from "react";
|
||||||
|
|
||||||
const BasicPromptInput: React.FC = () => {
|
const BasicPromptInput: React.FC = () => {
|
||||||
|
const activeConversationId = useAtomValue(getActiveConvoIdAtom);
|
||||||
|
const selectedModel = useAtomValue(selectedModelAtom);
|
||||||
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom);
|
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom);
|
||||||
const { sendChatMessage } = useSendChatMessage();
|
const { sendChatMessage } = useSendChatMessage();
|
||||||
|
const { requestCreateConvo } = useCreateConversation();
|
||||||
|
|
||||||
const handleMessageChange = (event: any) => {
|
const { initModel } = useInitModel();
|
||||||
|
|
||||||
|
const handleMessageChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
setCurrentPrompt(event.target.value);
|
setCurrentPrompt(event.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = (event: any) => {
|
const handleKeyDown = async (
|
||||||
|
event: React.KeyboardEvent<HTMLTextAreaElement>
|
||||||
|
) => {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
if (!event.shiftKey) {
|
if (!event.shiftKey) {
|
||||||
|
if (activeConversationId) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
sendChatMessage();
|
sendChatMessage();
|
||||||
|
} else {
|
||||||
|
if (!selectedModel) {
|
||||||
|
console.log("No model selected");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await requestCreateConvo(selectedModel);
|
||||||
|
await initModel(selectedModel);
|
||||||
|
sendChatMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,14 +4,12 @@ import React, { useCallback, useRef, useState } from "react";
|
|||||||
import ChatItem from "../ChatItem";
|
import ChatItem from "../ChatItem";
|
||||||
import { ChatMessage } from "@/_models/ChatMessage";
|
import { ChatMessage } from "@/_models/ChatMessage";
|
||||||
import useChatMessages from "@/_hooks/useChatMessages";
|
import useChatMessages from "@/_hooks/useChatMessages";
|
||||||
import {
|
import { showingTyping } from "@/_helpers/JotaiWrapper";
|
||||||
chatMessages,
|
|
||||||
getActiveConvoIdAtom,
|
|
||||||
showingTyping,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { selectAtom } from "jotai/utils";
|
import { selectAtom } from "jotai/utils";
|
||||||
import LoadingIndicator from "../LoadingIndicator";
|
import LoadingIndicator from "../LoadingIndicator";
|
||||||
|
import { getActiveConvoIdAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
import { chatMessages } from "@/_helpers/atoms/ChatMessage.atom";
|
||||||
|
|
||||||
const ChatBody: React.FC = () => {
|
const ChatBody: React.FC = () => {
|
||||||
const activeConversationId = useAtomValue(getActiveConvoIdAtom) ?? "";
|
const activeConversationId = useAtomValue(getActiveConvoIdAtom) ?? "";
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import SimpleTextMessage from "../SimpleTextMessage";
|
|||||||
import { ChatMessage, MessageType } from "@/_models/ChatMessage";
|
import { ChatMessage, MessageType } from "@/_models/ChatMessage";
|
||||||
import StreamTextMessage from "../StreamTextMessage";
|
import StreamTextMessage from "../StreamTextMessage";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { currentStreamingMessageAtom } from "@/_helpers/JotaiWrapper";
|
import { currentStreamingMessageAtom } from "@/_helpers/atoms/ChatMessage.atom";
|
||||||
|
|
||||||
export default function renderChatMessage({
|
export default function renderChatMessage({
|
||||||
id,
|
id,
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { MainViewState, getMainViewStateAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import Welcome from "../WelcomeContainer";
|
import Welcome from "../WelcomeContainer";
|
||||||
import { Preferences } from "../Preferences";
|
import { Preferences } from "../Preferences";
|
||||||
import MyModelContainer from "../MyModelContainer";
|
import MyModelContainer from "../MyModelContainer";
|
||||||
import ExploreModelContainer from "../ExploreModelContainer";
|
import ExploreModelContainer from "../ExploreModelContainer";
|
||||||
|
import {
|
||||||
|
MainViewState,
|
||||||
|
getMainViewStateAtom,
|
||||||
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
|
import EmptyChatContainer from "../EmptyChatContainer";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@ -16,6 +20,8 @@ export default function ChatContainer({ children }: Props) {
|
|||||||
const viewState = useAtomValue(getMainViewStateAtom);
|
const viewState = useAtomValue(getMainViewStateAtom);
|
||||||
|
|
||||||
switch (viewState) {
|
switch (viewState) {
|
||||||
|
case MainViewState.ConversationEmptyModel:
|
||||||
|
return <EmptyChatContainer />
|
||||||
case MainViewState.ExploreModel:
|
case MainViewState.ExploreModel:
|
||||||
return <ExploreModelContainer />;
|
return <ExploreModelContainer />;
|
||||||
case MainViewState.Setting:
|
case MainViewState.Setting:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import JanImage from "../JanImage";
|
import JanImage from "../JanImage";
|
||||||
import { setActiveConvoIdAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
|
import { setActiveConvoIdAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
|
||||||
const CompactLogo: React.FC = () => {
|
const CompactLogo: React.FC = () => {
|
||||||
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom);
|
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { showConfirmDeleteConversationModalAtom } from "@/_helpers/JotaiWrapper";
|
import { showConfirmDeleteConversationModalAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
import useDeleteConversation from "@/_hooks/useDeleteConversation";
|
import useDeleteConversation from "@/_hooks/useDeleteConversation";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline";
|
import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline";
|
||||||
import { showConfirmDeleteModalAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
|
import { showConfirmDeleteModalAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
const ConfirmDeleteModelModal: React.FC = () => {
|
const ConfirmDeleteModelModal: React.FC = () => {
|
||||||
const [show, setShow] = useAtom(showConfirmDeleteModalAtom);
|
const [show, setShow] = useAtom(showConfirmDeleteModalAtom);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
import { Dialog, Transition } from "@headlessui/react";
|
||||||
import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline";
|
import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline";
|
||||||
import { showConfirmSignOutModalAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
import useSignOut from "@/_hooks/useSignOut";
|
import useSignOut from "@/_hooks/useSignOut";
|
||||||
|
import { showConfirmSignOutModalAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
const ConfirmSignOutModal: React.FC = () => {
|
const ConfirmSignOutModal: React.FC = () => {
|
||||||
const [show, setShow] = useAtom(showConfirmSignOutModalAtom);
|
const [show, setShow] = useAtom(showConfirmSignOutModalAtom);
|
||||||
|
|||||||
14
web/app/_components/EmptyChatContainer/index.tsx
Normal file
14
web/app/_components/EmptyChatContainer/index.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from "react";
|
||||||
|
import SelectModels from "../ModelSelector";
|
||||||
|
import InputToolbar from "../InputToolbar";
|
||||||
|
|
||||||
|
const EmptyChatContainer: React.FC = () => (
|
||||||
|
<div className="flex flex-col flex-1">
|
||||||
|
<div className="flex flex-1 items-center justify-center">
|
||||||
|
<SelectModels />
|
||||||
|
</div>
|
||||||
|
<InputToolbar />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default EmptyChatContainer;
|
||||||
@ -7,8 +7,8 @@ import { Product } from "@/_models/Product";
|
|||||||
import SimpleTag, { TagType } from "../SimpleTag";
|
import SimpleTag, { TagType } from "../SimpleTag";
|
||||||
import { displayDate } from "@/_utils/datetime";
|
import { displayDate } from "@/_utils/datetime";
|
||||||
import useDownloadModel from "@/_hooks/useDownloadModel";
|
import useDownloadModel from "@/_hooks/useDownloadModel";
|
||||||
import { modelDownloadStateAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { atom, useAtomValue } from "jotai";
|
import { atom, useAtomValue } from "jotai";
|
||||||
|
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
model: Product;
|
model: Product;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { showingMobilePaneAtom } from "@/_helpers/JotaiWrapper";
|
import { showingMobilePaneAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
import { Bars3Icon } from "@heroicons/react/24/outline";
|
import { Bars3Icon } from "@heroicons/react/24/outline";
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import JanImage from "../JanImage";
|
import JanImage from "../JanImage";
|
||||||
import {
|
|
||||||
MainViewState,
|
|
||||||
conversationStatesAtom,
|
|
||||||
currentProductAtom,
|
|
||||||
getActiveConvoIdAtom,
|
|
||||||
setActiveConvoIdAtom,
|
|
||||||
setMainViewStateAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtomValue, useSetAtom } from "jotai";
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Conversation } from "@/_models/Conversation";
|
import { Conversation } from "@/_models/Conversation";
|
||||||
import { DataService, InfereceService } from "../../../shared/coreService";
|
import { DataService } from "../../../shared/coreService";
|
||||||
import { executeSerial } from "../../../../electron/core/plugin-manager/execution/extension-manager";
|
import { executeSerial } from "../../../../electron/core/plugin-manager/execution/extension-manager";
|
||||||
|
import {
|
||||||
|
conversationStatesAtom,
|
||||||
|
getActiveConvoIdAtom,
|
||||||
|
setActiveConvoIdAtom,
|
||||||
|
} from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
import {
|
||||||
|
setMainViewStateAtom,
|
||||||
|
MainViewState,
|
||||||
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
|
import useInitModel from "@/_hooks/useInitModel";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
conversation: Conversation;
|
conversation: Conversation;
|
||||||
@ -33,7 +35,7 @@ const HistoryItem: React.FC<Props> = ({
|
|||||||
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom);
|
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom);
|
||||||
const isSelected = activeConvoId === conversation.id;
|
const isSelected = activeConvoId === conversation.id;
|
||||||
|
|
||||||
const setActiveProduct = useSetAtom(currentProductAtom);
|
const { initModel } = useInitModel();
|
||||||
|
|
||||||
const onClick = async () => {
|
const onClick = async () => {
|
||||||
const model = await executeSerial(
|
const model = await executeSerial(
|
||||||
@ -45,10 +47,7 @@ const HistoryItem: React.FC<Props> = ({
|
|||||||
`Model ${conversation.model_id} not found! Please re-download the model first.`
|
`Model ${conversation.model_id} not found! Please re-download the model first.`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setActiveProduct(model);
|
initModel(model);
|
||||||
executeSerial(InfereceService.INIT_MODEL, model)
|
|
||||||
.then(() => console.info(`Init model success`))
|
|
||||||
.catch((err) => console.log(`Init model error ${err}`));
|
|
||||||
}
|
}
|
||||||
if (activeConvoId !== conversation.id) {
|
if (activeConvoId !== conversation.id) {
|
||||||
setMainViewState(MainViewState.Conversation);
|
setMainViewState(MainViewState.Conversation);
|
||||||
|
|||||||
@ -2,9 +2,10 @@ import HistoryItem from "../HistoryItem";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ExpandableHeader from "../ExpandableHeader";
|
import ExpandableHeader from "../ExpandableHeader";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { searchAtom, userConversationsAtom } from "@/_helpers/JotaiWrapper";
|
import { searchAtom } from "@/_helpers/JotaiWrapper";
|
||||||
import useGetUserConversations from "@/_hooks/useGetUserConversations";
|
import useGetUserConversations from "@/_hooks/useGetUserConversations";
|
||||||
import SidebarEmptyHistory from "../SidebarEmptyHistory";
|
import SidebarEmptyHistory from "../SidebarEmptyHistory";
|
||||||
|
import { userConversationsAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
|
||||||
const HistoryList: React.FC = () => {
|
const HistoryList: React.FC = () => {
|
||||||
const conversations = useAtomValue(userConversationsAtom);
|
const conversations = useAtomValue(userConversationsAtom);
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import BasicPromptInput from "../BasicPromptInput";
|
import BasicPromptInput from "../BasicPromptInput";
|
||||||
import BasicPromptAccessories from "../BasicPromptAccessories";
|
import BasicPromptAccessories from "../BasicPromptAccessories";
|
||||||
import { showingAdvancedPromptAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
|
import { showingAdvancedPromptAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
const InputToolbar: React.FC = () => {
|
const InputToolbar: React.FC = () => {
|
||||||
const showingAdvancedPrompt = useAtomValue(showingAdvancedPromptAtom);
|
const showingAdvancedPrompt = useAtomValue(showingAdvancedPromptAtom);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { setActiveConvoIdAtom } from "@/_helpers/JotaiWrapper";
|
import { setActiveConvoIdAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|||||||
@ -3,14 +3,12 @@ import SidebarFooter from "../SidebarFooter";
|
|||||||
import SidebarHeader from "../SidebarHeader";
|
import SidebarHeader from "../SidebarHeader";
|
||||||
import SidebarMenu from "../SidebarMenu";
|
import SidebarMenu from "../SidebarMenu";
|
||||||
import HistoryList from "../HistoryList";
|
import HistoryList from "../HistoryList";
|
||||||
import SecondaryButton from "../SecondaryButton";
|
import NewChatButton from "../NewChatButton";
|
||||||
|
|
||||||
const LeftContainer: React.FC = () => (
|
const LeftContainer: React.FC = () => (
|
||||||
<div className="w-[323px] flex-shrink-0 p-3 h-screen border-r border-gray-200 flex flex-col">
|
<div className="w-[323px] flex-shrink-0 p-3 h-screen border-r border-gray-200 flex flex-col">
|
||||||
<SidebarHeader />
|
<SidebarHeader />
|
||||||
<div className="h-5" />
|
<NewChatButton />
|
||||||
<SecondaryButton title={"New Chat"} onClick={() => {}} />
|
|
||||||
<div className="h-6" />
|
|
||||||
<HistoryList />
|
<HistoryList />
|
||||||
<SidebarMenu />
|
<SidebarMenu />
|
||||||
<SidebarFooter />
|
<SidebarFooter />
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Popover, Transition } from "@headlessui/react";
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
// import useGetCurrentUser from "@/_hooks/useGetCurrentUser";
|
// import useGetCurrentUser from "@/_hooks/useGetCurrentUser";
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import { showConfirmSignOutModalAtom } from "@/_helpers/JotaiWrapper";
|
import { showConfirmSignOutModalAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
export const MenuHeader: React.FC = () => {
|
export const MenuHeader: React.FC = () => {
|
||||||
const setShowConfirmSignOutModal = useSetAtom(showConfirmSignOutModalAtom);
|
const setShowConfirmSignOutModal = useSetAtom(showConfirmSignOutModalAtom);
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import React, { useRef } from "react";
|
|||||||
import { Dialog } from "@headlessui/react";
|
import { Dialog } from "@headlessui/react";
|
||||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { showingMobilePaneAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtom } from "jotai";
|
import { useAtom } from "jotai";
|
||||||
|
import { showingMobilePaneAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
const MobileMenuPane: React.FC = () => {
|
const MobileMenuPane: React.FC = () => {
|
||||||
const [show, setShow] = useAtom(showingMobilePaneAtom);
|
const [show, setShow] = useAtom(showingMobilePaneAtom);
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useAtomValue, useSetAtom } from "jotai";
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
import {
|
|
||||||
currentProductAtom,
|
|
||||||
showConfirmDeleteConversationModalAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import { PlusIcon, TrashIcon } from "@heroicons/react/24/outline";
|
import { PlusIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||||
import useCreateConversation from "@/_hooks/useCreateConversation";
|
import useCreateConversation from "@/_hooks/useCreateConversation";
|
||||||
|
import { showConfirmDeleteConversationModalAtom } from "@/_helpers/atoms/Modal.atom";
|
||||||
|
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
const ModelMenu: React.FC = () => {
|
const ModelMenu: React.FC = () => {
|
||||||
const currentProduct = useAtomValue(currentProductAtom);
|
const currentProduct = useAtomValue(currentProductAtom);
|
||||||
|
|||||||
@ -4,10 +4,10 @@ import Image from "next/image";
|
|||||||
import { ModelStatus, ModelStatusComponent } from "../ModelStatusComponent";
|
import { ModelStatus, ModelStatusComponent } from "../ModelStatusComponent";
|
||||||
import ModelActionMenu from "../ModelActionMenu";
|
import ModelActionMenu from "../ModelActionMenu";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { currentProductAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import ModelActionButton, { ModelActionType } from "../ModelActionButton";
|
import ModelActionButton, { ModelActionType } from "../ModelActionButton";
|
||||||
import useStartStopModel from "@/_hooks/useStartStopModel";
|
import useStartStopModel from "@/_hooks/useStartStopModel";
|
||||||
import useDeleteModel from "@/_hooks/useDeleteModel";
|
import useDeleteModel from "@/_hooks/useDeleteModel";
|
||||||
|
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
model: Product;
|
model: Product;
|
||||||
|
|||||||
118
web/app/_components/ModelSelector/index.tsx
Normal file
118
web/app/_components/ModelSelector/index.tsx
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import { Fragment, useEffect } from "react";
|
||||||
|
import { Listbox, Transition } from "@headlessui/react";
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
|
||||||
|
import { useGetDownloadedModels } from "@/_hooks/useGetDownloadedModels";
|
||||||
|
import { Product } from "@/_models/Product";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { selectedModelAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
|
function classNames(...classes: any) {
|
||||||
|
return classes.filter(Boolean).join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
const SelectModels: React.FC = () => {
|
||||||
|
const { downloadedModels } = useGetDownloadedModels();
|
||||||
|
const [selectedModel, setSelectedModel] = useAtom(selectedModelAtom);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (downloadedModels && downloadedModels.length > 0) {
|
||||||
|
onModelSelected(downloadedModels[0]);
|
||||||
|
}
|
||||||
|
}, [downloadedModels]);
|
||||||
|
|
||||||
|
const onModelSelected = (model: Product) => {
|
||||||
|
setSelectedModel(model);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!selectedModel) {
|
||||||
|
return <div>You have not downloaded any model!</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Listbox value={selectedModel} onChange={onModelSelected}>
|
||||||
|
{({ open }) => (
|
||||||
|
<div className="w-[461px]">
|
||||||
|
<Listbox.Label className="block text-sm font-medium leading-6 text-gray-900">
|
||||||
|
Select a Model:
|
||||||
|
</Listbox.Label>
|
||||||
|
<div className="relative mt-[19px]">
|
||||||
|
<Listbox.Button className="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 sm:text-sm sm:leading-6">
|
||||||
|
<span className="flex items-center">
|
||||||
|
<img
|
||||||
|
src={selectedModel.avatarUrl}
|
||||||
|
alt=""
|
||||||
|
className="h-5 w-5 flex-shrink-0 rounded-full"
|
||||||
|
/>
|
||||||
|
<span className="ml-3 block truncate">
|
||||||
|
{selectedModel.name}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
|
||||||
|
<ChevronUpDownIcon
|
||||||
|
className="h-5 w-5 text-gray-400"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</Listbox.Button>
|
||||||
|
|
||||||
|
<Transition
|
||||||
|
show={open}
|
||||||
|
as={Fragment}
|
||||||
|
leave="transition ease-in duration-100"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<Listbox.Options className="absolute z-10 mt-1 max-h-[188px] w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||||
|
{downloadedModels.map((model) => (
|
||||||
|
<Listbox.Option
|
||||||
|
key={model.id}
|
||||||
|
className={({ active }) =>
|
||||||
|
classNames(
|
||||||
|
active ? "bg-indigo-600 text-white" : "text-gray-900",
|
||||||
|
"relative cursor-default select-none py-2 pl-3 pr-9"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
value={model}
|
||||||
|
>
|
||||||
|
{({ selected, active }) => (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<img
|
||||||
|
src={model.avatarUrl}
|
||||||
|
alt=""
|
||||||
|
className="h-5 w-5 flex-shrink-0 rounded-full"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={classNames(
|
||||||
|
selected ? "font-semibold" : "font-normal",
|
||||||
|
"ml-3 block truncate"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{model.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selected ? (
|
||||||
|
<span
|
||||||
|
className={classNames(
|
||||||
|
active ? "text-white" : "text-indigo-600",
|
||||||
|
"absolute inset-y-0 right-0 flex items-center pr-4"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Listbox.Option>
|
||||||
|
))}
|
||||||
|
</Listbox.Options>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Listbox>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectModels;
|
||||||
@ -1,14 +1,12 @@
|
|||||||
import ProgressBar from "../ProgressBar";
|
import ProgressBar from "../ProgressBar";
|
||||||
import SystemItem from "../SystemItem";
|
import SystemItem from "../SystemItem";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import {
|
import { appDownloadProgress } from "@/_helpers/JotaiWrapper";
|
||||||
appDownloadProgress,
|
|
||||||
currentProductAtom,
|
|
||||||
getSystemBarVisibilityAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { executeSerial } from "../../../../electron/core/plugin-manager/execution/extension-manager";
|
import { executeSerial } from "../../../../electron/core/plugin-manager/execution/extension-manager";
|
||||||
import { SystemMonitoringService } from "../../../shared/coreService";
|
import { SystemMonitoringService } from "../../../shared/coreService";
|
||||||
|
import { getSystemBarVisibilityAtom } from "@/_helpers/atoms/SystemBar.atom";
|
||||||
|
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
const MonitorBar: React.FC = () => {
|
const MonitorBar: React.FC = () => {
|
||||||
const show = useAtomValue(getSystemBarVisibilityAtom);
|
const show = useAtomValue(getSystemBarVisibilityAtom);
|
||||||
|
|||||||
39
web/app/_components/NewChatButton/index.tsx
Normal file
39
web/app/_components/NewChatButton/index.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import SecondaryButton from "../SecondaryButton";
|
||||||
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
|
import {
|
||||||
|
MainViewState,
|
||||||
|
setMainViewStateAtom,
|
||||||
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
|
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
import useCreateConversation from "@/_hooks/useCreateConversation";
|
||||||
|
import useInitModel from "@/_hooks/useInitModel";
|
||||||
|
import { Product } from "@/_models/Product";
|
||||||
|
|
||||||
|
const NewChatButton: React.FC = () => {
|
||||||
|
const activeModel = useAtomValue(currentProductAtom);
|
||||||
|
const setMainView = useSetAtom(setMainViewStateAtom);
|
||||||
|
const { requestCreateConvo } = useCreateConversation();
|
||||||
|
const { initModel } = useInitModel();
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (!activeModel) {
|
||||||
|
setMainView(MainViewState.ConversationEmptyModel);
|
||||||
|
} else {
|
||||||
|
createConversationAndInitModel(activeModel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createConversationAndInitModel = async (model: Product) => {
|
||||||
|
await requestCreateConvo(model);
|
||||||
|
await initModel(model);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SecondaryButton title={"New Chat"} onClick={onClick} className="my-5" />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewChatButton;
|
||||||
@ -2,14 +2,20 @@ type Props = {
|
|||||||
title: string;
|
title: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SecondaryButton: React.FC<Props> = ({ title, onClick, disabled }) => (
|
const SecondaryButton: React.FC<Props> = ({
|
||||||
|
title,
|
||||||
|
onClick,
|
||||||
|
disabled,
|
||||||
|
className,
|
||||||
|
}) => (
|
||||||
<button
|
<button
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
|
className={`rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 ${className}`}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import {
|
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
|
||||||
currentConvoStateAtom,
|
import { currentConvoStateAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
currentPromptAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import useSendChatMessage from "@/_hooks/useSendChatMessage";
|
import useSendChatMessage from "@/_hooks/useSendChatMessage";
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
import { useAtom, useAtomValue } from "jotai";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
callback?: () => void;
|
|
||||||
className?: string;
|
|
||||||
icon: string;
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SidebarButton: React.FC<Props> = ({
|
|
||||||
callback,
|
|
||||||
height,
|
|
||||||
icon,
|
|
||||||
className,
|
|
||||||
width,
|
|
||||||
title,
|
|
||||||
}) => (
|
|
||||||
<button onClick={callback} className={className}>
|
|
||||||
<Image src={icon} width={width} height={height} alt="" />
|
|
||||||
<span>{title}</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
@ -1,28 +1,56 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { SidebarButton } from "../SidebarButton";
|
|
||||||
import { executeSerial } from "../../../../electron/core/plugin-manager/execution/extension-manager";
|
|
||||||
import { DataService } from "../../../shared/coreService";
|
|
||||||
import useCreateConversation from "@/_hooks/useCreateConversation";
|
import useCreateConversation from "@/_hooks/useCreateConversation";
|
||||||
|
import PrimaryButton from "../PrimaryButton";
|
||||||
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
|
import { useGetDownloadedModels } from "@/_hooks/useGetDownloadedModels";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
MainViewState,
|
||||||
|
setMainViewStateAtom,
|
||||||
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
|
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
import useInitModel from "@/_hooks/useInitModel";
|
||||||
|
import { Product } from "@/_models/Product";
|
||||||
|
|
||||||
|
enum ActionButton {
|
||||||
|
DownloadModel = "Download a Model",
|
||||||
|
StartChat = "Start a Conversation",
|
||||||
|
}
|
||||||
|
|
||||||
const SidebarEmptyHistory: React.FC = () => {
|
const SidebarEmptyHistory: React.FC = () => {
|
||||||
|
const { downloadedModels } = useGetDownloadedModels();
|
||||||
|
const activeModel = useAtomValue(currentProductAtom);
|
||||||
|
const setMainView = useSetAtom(setMainViewStateAtom);
|
||||||
const { requestCreateConvo } = useCreateConversation();
|
const { requestCreateConvo } = useCreateConversation();
|
||||||
const startChat = async () => {
|
const [action, setAction] = useState(ActionButton.DownloadModel);
|
||||||
// Host
|
|
||||||
if (window && !window.electronAPI) {
|
const { initModel } = useInitModel();
|
||||||
// requestCreateConvo(); // TODO: get model id from somewhere
|
|
||||||
}
|
useEffect(() => {
|
||||||
// Electron
|
if (downloadedModels.length > 0) {
|
||||||
const downloadedModels = await executeSerial(
|
setAction(ActionButton.StartChat);
|
||||||
DataService.GET_FINISHED_DOWNLOAD_MODELS
|
|
||||||
);
|
|
||||||
if (!downloadedModels || downloadedModels?.length === 0) {
|
|
||||||
alert(
|
|
||||||
"Seems like there is no model downloaded yet. Please download a model first."
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
requestCreateConvo(downloadedModels[0]);
|
setAction(ActionButton.DownloadModel);
|
||||||
|
}
|
||||||
|
}, [downloadedModels]);
|
||||||
|
|
||||||
|
const onClick = () => {
|
||||||
|
if (action === ActionButton.DownloadModel) {
|
||||||
|
setMainView(MainViewState.ExploreModel);
|
||||||
|
} else {
|
||||||
|
if (!activeModel) {
|
||||||
|
setMainView(MainViewState.ConversationEmptyModel);
|
||||||
|
} else {
|
||||||
|
createConversationAndInitModel(activeModel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createConversationAndInitModel = async (model: Product) => {
|
||||||
|
await requestCreateConvo(model);
|
||||||
|
await initModel(model);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center py-10 gap-3">
|
<div className="flex flex-col items-center py-10 gap-3">
|
||||||
<Image
|
<Image
|
||||||
@ -32,22 +60,11 @@ const SidebarEmptyHistory: React.FC = () => {
|
|||||||
alt=""
|
alt=""
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col items-center gap-6">
|
<div className="flex flex-col items-center gap-6">
|
||||||
<div>
|
<div className="text-center text-gray-900 text-sm">No Chat History</div>
|
||||||
<div className="text-center text-gray-900 text-sm">
|
|
||||||
No Chat History
|
|
||||||
</div>
|
|
||||||
<div className="text-center text-gray-500 text-sm">
|
<div className="text-center text-gray-500 text-sm">
|
||||||
Get started by creating a new chat.
|
Get started by creating a new chat.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<PrimaryButton title={action} onClick={onClick} />
|
||||||
<SidebarButton
|
|
||||||
callback={startChat}
|
|
||||||
className="flex items-center border bg-blue-600 rounded-lg py-[9px] pl-[15px] pr-[17px] gap-2 text-white font-medium text-sm"
|
|
||||||
height={14}
|
|
||||||
icon="icons/Icon_plus.svg"
|
|
||||||
title="New chat"
|
|
||||||
width={14}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,27 +1,21 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { SidebarButton } from "../SidebarButton";
|
import SecondaryButton from "../SecondaryButton";
|
||||||
|
|
||||||
const SidebarFooter: React.FC = () => (
|
const SidebarFooter: React.FC = () => (
|
||||||
<div className="flex justify-between items-center gap-2">
|
<div className="flex justify-between items-center gap-2">
|
||||||
<SidebarButton
|
<SecondaryButton
|
||||||
className="flex items-center border border-gray-200 rounded-lg p-2 gap-3 flex-1 justify-center text-gray-900 font-medium text-sm"
|
title={"Discord"}
|
||||||
height={24}
|
onClick={() =>
|
||||||
icon="icons/discord.svg"
|
window.electronAPI?.openExternalUrl("https://discord.gg/AsJ8krTT3N")
|
||||||
title="Discord"
|
}
|
||||||
width={24}
|
className="flex-1"
|
||||||
callback={() => {
|
|
||||||
window.electronAPI?.openExternalUrl("https://discord.gg/AsJ8krTT3N");
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<SidebarButton
|
<SecondaryButton
|
||||||
className="flex items-center border border-gray-200 rounded-lg p-2 gap-3 flex-1 justify-center text-gray-900 font-medium text-sm"
|
title={"Discord"}
|
||||||
height={24}
|
onClick={() =>
|
||||||
icon="icons/unicorn_twitter.svg"
|
window.electronAPI?.openExternalUrl("https://twitter.com/jan_dotai")
|
||||||
title="Twitter"
|
}
|
||||||
width={24}
|
className="flex-1"
|
||||||
callback={() => {
|
|
||||||
window.electronAPI?.openExternalUrl("https://twitter.com/jan_dotai");
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { MainViewState } from "@/_helpers/JotaiWrapper";
|
|
||||||
import SidebarMenuItem from "../SidebarMenuItem";
|
import SidebarMenuItem from "../SidebarMenuItem";
|
||||||
|
import { MainViewState } from "@/_helpers/atoms/MainView.atom";
|
||||||
|
|
||||||
const menu = [
|
const menu = [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
|
import Image from "next/image";
|
||||||
import {
|
import {
|
||||||
MainViewState,
|
MainViewState,
|
||||||
getMainViewStateAtom,
|
getMainViewStateAtom,
|
||||||
setMainViewStateAtom,
|
setMainViewStateAtom,
|
||||||
} from "@/_helpers/JotaiWrapper";
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
import { useAtomValue, useSetAtom } from "jotai";
|
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { TextCode } from "../TextCode";
|
|||||||
import { getMessageCode } from "@/_utils/message";
|
import { getMessageCode } from "@/_utils/message";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { currentStreamingMessageAtom } from "@/_helpers/JotaiWrapper";
|
import { currentStreamingMessageAtom } from "@/_helpers/atoms/ChatMessage.atom";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { currentConversationAtom } from "@/_helpers/JotaiWrapper";
|
import { currentConversationAtom } from "@/_helpers/atoms/Conversation.atom";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { SidebarButton } from "../SidebarButton";
|
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import { MainViewState, setMainViewStateAtom } from "@/_helpers/JotaiWrapper";
|
import {
|
||||||
|
setMainViewStateAtom,
|
||||||
|
MainViewState,
|
||||||
|
} from "@/_helpers/atoms/MainView.atom";
|
||||||
|
import SecondaryButton from "../SecondaryButton";
|
||||||
|
|
||||||
const Welcome: React.FC = () => {
|
const Welcome: React.FC = () => {
|
||||||
const setMainViewState = useSetAtom(setMainViewStateAtom);
|
const setMainViewState = useSetAtom(setMainViewStateAtom);
|
||||||
@ -15,13 +18,9 @@ const Welcome: React.FC = () => {
|
|||||||
<br />
|
<br />
|
||||||
let’s download your first model
|
let’s download your first model
|
||||||
</span>
|
</span>
|
||||||
<SidebarButton
|
<SecondaryButton
|
||||||
callback={() => setMainViewState(MainViewState.ExploreModel)}
|
title={"Explore models"}
|
||||||
className="flex flex-row-reverse items-center rounded-lg gap-2 px-3 py-2 text-xs font-medium border border-gray-200"
|
onClick={() => setMainViewState(MainViewState.ExploreModel)}
|
||||||
icon={"icons/app_icon.svg"}
|
|
||||||
title="Explore models"
|
|
||||||
height={16}
|
|
||||||
width={16}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useAtom, useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
import { ReactNode, useEffect } from "react";
|
import { ReactNode, useEffect } from "react";
|
||||||
import {
|
import { appDownloadProgress } from "./JotaiWrapper";
|
||||||
appDownloadProgress,
|
|
||||||
setDownloadStateAtom,
|
|
||||||
setDownloadStateSuccessAtom,
|
|
||||||
} from "./JotaiWrapper";
|
|
||||||
import { DownloadState } from "@/_models/DownloadState";
|
import { DownloadState } from "@/_models/DownloadState";
|
||||||
import { execute } from "../../../electron/core/plugin-manager/execution/extension-manager";
|
import { execute } from "../../../electron/core/plugin-manager/execution/extension-manager";
|
||||||
import { DataService } from "../../shared/coreService";
|
import { DataService } from "../../shared/coreService";
|
||||||
|
import {
|
||||||
|
setDownloadStateAtom,
|
||||||
|
setDownloadStateSuccessAtom,
|
||||||
|
} from "./atoms/DownloadState.atom";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ChatMessage, MessageStatus } from "@/_models/ChatMessage";
|
|
||||||
import { Conversation, ConversationState } from "@/_models/Conversation";
|
|
||||||
import { DownloadState } from "@/_models/DownloadState";
|
|
||||||
import { Product } from "@/_models/Product";
|
|
||||||
import { Provider, atom } from "jotai";
|
import { Provider, atom } from "jotai";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
@ -15,286 +11,11 @@ export default function JotaiWrapper({ children }: Props) {
|
|||||||
return <Provider>{children}</Provider>;
|
return <Provider>{children}</Provider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeConversationIdAtom = atom<string | undefined>(undefined);
|
|
||||||
export const getActiveConvoIdAtom = atom((get) =>
|
|
||||||
get(activeConversationIdAtom)
|
|
||||||
);
|
|
||||||
export const setActiveConvoIdAtom = atom(
|
|
||||||
null,
|
|
||||||
(_get, set, convoId: string | undefined) => {
|
|
||||||
if (convoId) {
|
|
||||||
console.log(`set active convo id to ${convoId}`);
|
|
||||||
set(setMainViewStateAtom, MainViewState.Conversation);
|
|
||||||
}
|
|
||||||
set(activeConversationIdAtom, convoId);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const currentPromptAtom = atom<string>("");
|
export const currentPromptAtom = atom<string>("");
|
||||||
|
|
||||||
export const showingAdvancedPromptAtom = atom<boolean>(false);
|
|
||||||
export const showingProductDetailAtom = atom<boolean>(false);
|
|
||||||
export const showingMobilePaneAtom = atom<boolean>(false);
|
|
||||||
export const showingTyping = atom<boolean>(false);
|
export const showingTyping = atom<boolean>(false);
|
||||||
|
|
||||||
export const appDownloadProgress = atom<number>(-1);
|
export const appDownloadProgress = atom<number>(-1);
|
||||||
export const searchingModelText = atom<string>("");
|
export const searchingModelText = atom<string>("");
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores all conversations for the current user
|
|
||||||
*/
|
|
||||||
export const userConversationsAtom = atom<Conversation[]>([]);
|
|
||||||
export const currentConversationAtom = atom<Conversation | undefined>((get) =>
|
|
||||||
get(userConversationsAtom).find((c) => c.id === get(activeConversationIdAtom))
|
|
||||||
);
|
|
||||||
export const setConvoUpdatedAtAtom = atom(null, (get, set, convoId: string) => {
|
|
||||||
const convo = get(userConversationsAtom).find((c) => c.id === convoId);
|
|
||||||
if (!convo) return;
|
|
||||||
const newConvo: Conversation = {
|
|
||||||
...convo,
|
|
||||||
updated_at: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
const newConversations: Conversation[] = get(userConversationsAtom).map((c) =>
|
|
||||||
c.id === convoId ? newConvo : c
|
|
||||||
);
|
|
||||||
|
|
||||||
set(userConversationsAtom, newConversations);
|
|
||||||
});
|
|
||||||
|
|
||||||
export const currentStreamingMessageAtom = atom<ChatMessage | undefined>(
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setConvoLastImageAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, convoId: string, lastImageUrl: string) => {
|
|
||||||
const convo = get(userConversationsAtom).find((c) => c.id === convoId);
|
|
||||||
if (!convo) return;
|
|
||||||
const newConvo: Conversation = { ...convo };
|
|
||||||
const newConversations: Conversation[] = get(userConversationsAtom).map(
|
|
||||||
(c) => (c.id === convoId ? newConvo : c)
|
|
||||||
);
|
|
||||||
|
|
||||||
set(userConversationsAtom, newConversations);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores all conversation states for the current user
|
|
||||||
*/
|
|
||||||
export const conversationStatesAtom = atom<Record<string, ConversationState>>(
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
export const currentConvoStateAtom = atom<ConversationState | undefined>(
|
|
||||||
(get) => {
|
|
||||||
const activeConvoId = get(activeConversationIdAtom);
|
|
||||||
if (!activeConvoId) {
|
|
||||||
console.log("active convo id is undefined");
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return get(conversationStatesAtom)[activeConvoId];
|
|
||||||
}
|
|
||||||
);
|
|
||||||
export const addNewConversationStateAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, conversationId: string, state: ConversationState) => {
|
|
||||||
const currentState = { ...get(conversationStatesAtom) };
|
|
||||||
currentState[conversationId] = state;
|
|
||||||
set(conversationStatesAtom, currentState);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
export const updateConversationWaitingForResponseAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, conversationId: string, waitingForResponse: boolean) => {
|
|
||||||
const currentState = { ...get(conversationStatesAtom) };
|
|
||||||
currentState[conversationId] = {
|
|
||||||
...currentState[conversationId],
|
|
||||||
waitingForResponse,
|
|
||||||
};
|
|
||||||
set(conversationStatesAtom, currentState);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
export const updateConversationHasMoreAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, conversationId: string, hasMore: boolean) => {
|
|
||||||
const currentState = { ...get(conversationStatesAtom) };
|
|
||||||
currentState[conversationId] = { ...currentState[conversationId], hasMore };
|
|
||||||
set(conversationStatesAtom, currentState);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores all chat messages for all conversations
|
|
||||||
*/
|
|
||||||
export const chatMessages = atom<Record<string, ChatMessage[]>>({});
|
|
||||||
export const currentChatMessagesAtom = atom<ChatMessage[]>((get) => {
|
|
||||||
const activeConversationId = get(activeConversationIdAtom);
|
|
||||||
if (!activeConversationId) return [];
|
|
||||||
return get(chatMessages)[activeConversationId] ?? [];
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addOldMessagesAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, newMessages: ChatMessage[]) => {
|
|
||||||
const currentConvoId = get(activeConversationIdAtom);
|
|
||||||
if (!currentConvoId) return;
|
|
||||||
|
|
||||||
const currentMessages = get(chatMessages)[currentConvoId] ?? [];
|
|
||||||
const updatedMessages = [...currentMessages, ...newMessages];
|
|
||||||
|
|
||||||
const newData: Record<string, ChatMessage[]> = {
|
|
||||||
...get(chatMessages),
|
|
||||||
};
|
|
||||||
newData[currentConvoId] = updatedMessages;
|
|
||||||
set(chatMessages, newData);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
export const addNewMessageAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, newMessage: ChatMessage) => {
|
|
||||||
const currentConvoId = get(activeConversationIdAtom);
|
|
||||||
if (!currentConvoId) return;
|
|
||||||
|
|
||||||
const currentMessages = get(chatMessages)[currentConvoId] ?? [];
|
|
||||||
const updatedMessages = [newMessage, ...currentMessages];
|
|
||||||
|
|
||||||
const newData: Record<string, ChatMessage[]> = {
|
|
||||||
...get(chatMessages),
|
|
||||||
};
|
|
||||||
newData[currentConvoId] = updatedMessages;
|
|
||||||
set(chatMessages, newData);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const deleteConversationMessage = atom(null, (get, set, id: string) => {
|
|
||||||
const newData: Record<string, ChatMessage[]> = {
|
|
||||||
...get(chatMessages),
|
|
||||||
};
|
|
||||||
newData[id] = [];
|
|
||||||
set(chatMessages, newData);
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateMessageAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, id: string, conversationId: string, text: string) => {
|
|
||||||
const messages = get(chatMessages)[conversationId] ?? [];
|
|
||||||
const message = messages.find((e) => e.id === id);
|
|
||||||
if (message) {
|
|
||||||
message.text = text;
|
|
||||||
const updatedMessages = [...messages];
|
|
||||||
|
|
||||||
const newData: Record<string, ChatMessage[]> = {
|
|
||||||
...get(chatMessages),
|
|
||||||
};
|
|
||||||
newData[conversationId] = updatedMessages;
|
|
||||||
set(chatMessages, newData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* For updating the status of the last AI message that is pending
|
|
||||||
*/
|
|
||||||
export const updateLastMessageAsReadyAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, id, text: string) => {
|
|
||||||
const currentConvoId = get(activeConversationIdAtom);
|
|
||||||
if (!currentConvoId) return;
|
|
||||||
|
|
||||||
const currentMessages = get(chatMessages)[currentConvoId] ?? [];
|
|
||||||
const messageToUpdate = currentMessages.find((e) => e.id === id);
|
|
||||||
|
|
||||||
// if message is not found, do nothing
|
|
||||||
if (!messageToUpdate) return;
|
|
||||||
|
|
||||||
const index = currentMessages.indexOf(messageToUpdate);
|
|
||||||
const updatedMsg: ChatMessage = {
|
|
||||||
...messageToUpdate,
|
|
||||||
status: MessageStatus.Ready,
|
|
||||||
text: text,
|
|
||||||
};
|
|
||||||
|
|
||||||
currentMessages[index] = updatedMsg;
|
|
||||||
const newData: Record<string, ChatMessage[]> = {
|
|
||||||
...get(chatMessages),
|
|
||||||
};
|
|
||||||
newData[currentConvoId] = currentMessages;
|
|
||||||
set(chatMessages, newData);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const currentProductAtom = atom<Product | undefined>(undefined);
|
|
||||||
|
|
||||||
export const searchAtom = atom<string>("");
|
export const searchAtom = atom<string>("");
|
||||||
|
|
||||||
// modal atoms
|
|
||||||
export const showConfirmDeleteConversationModalAtom = atom(false);
|
|
||||||
export const showConfirmSignOutModalAtom = atom(false);
|
|
||||||
export const showConfirmDeleteModalAtom = atom(false);
|
|
||||||
|
|
||||||
export type FileDownloadStates = {
|
|
||||||
[key: string]: DownloadState;
|
|
||||||
};
|
|
||||||
|
|
||||||
// main view state
|
|
||||||
export enum MainViewState {
|
|
||||||
Welcome,
|
|
||||||
ExploreModel,
|
|
||||||
MyModel,
|
|
||||||
ResourceMonitor,
|
|
||||||
Setting,
|
|
||||||
Conversation,
|
|
||||||
}
|
|
||||||
|
|
||||||
const systemBarVisibilityAtom = atom<boolean>(true);
|
|
||||||
export const getSystemBarVisibilityAtom = atom((get) =>
|
|
||||||
get(systemBarVisibilityAtom)
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentMainViewStateAtom = atom<MainViewState>(MainViewState.Welcome);
|
|
||||||
export const getMainViewStateAtom = atom((get) =>
|
|
||||||
get(currentMainViewStateAtom)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setMainViewStateAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, state: MainViewState) => {
|
|
||||||
if (get(getMainViewStateAtom) === state) return;
|
|
||||||
if (state !== MainViewState.Conversation) {
|
|
||||||
set(activeConversationIdAtom, undefined);
|
|
||||||
}
|
|
||||||
const showSystemBar = state !== MainViewState.Conversation;
|
|
||||||
set(systemBarVisibilityAtom, showSystemBar);
|
|
||||||
set(currentMainViewStateAtom, state);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// download states
|
|
||||||
export const modelDownloadStateAtom = atom<FileDownloadStates>({});
|
|
||||||
|
|
||||||
export const setDownloadStateAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, state: DownloadState) => {
|
|
||||||
const currentState = { ...get(modelDownloadStateAtom) };
|
|
||||||
console.debug(
|
|
||||||
`current download state for ${state.fileName} is ${JSON.stringify(state)}`
|
|
||||||
);
|
|
||||||
currentState[state.fileName] = state;
|
|
||||||
set(modelDownloadStateAtom, currentState);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const setDownloadStateSuccessAtom = atom(
|
|
||||||
null,
|
|
||||||
(get, set, fileName: string) => {
|
|
||||||
const currentState = { ...get(modelDownloadStateAtom) };
|
|
||||||
const state = currentState[fileName];
|
|
||||||
if (!state) {
|
|
||||||
console.error(`Cannot find download state for ${fileName}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete currentState[fileName];
|
|
||||||
set(modelDownloadStateAtom, currentState);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|||||||
109
web/app/_helpers/atoms/ChatMessage.atom.ts
Normal file
109
web/app/_helpers/atoms/ChatMessage.atom.ts
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import { ChatMessage, MessageStatus } from "@/_models/ChatMessage";
|
||||||
|
import { atom } from "jotai";
|
||||||
|
import { getActiveConvoIdAtom } from "./Conversation.atom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores all chat messages for all conversations
|
||||||
|
*/
|
||||||
|
export const chatMessages = atom<Record<string, ChatMessage[]>>({});
|
||||||
|
|
||||||
|
export const currentChatMessagesAtom = atom<ChatMessage[]>((get) => {
|
||||||
|
const activeConversationId = get(getActiveConvoIdAtom);
|
||||||
|
if (!activeConversationId) return [];
|
||||||
|
return get(chatMessages)[activeConversationId] ?? [];
|
||||||
|
});
|
||||||
|
|
||||||
|
export const addOldMessagesAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, newMessages: ChatMessage[]) => {
|
||||||
|
const currentConvoId = get(getActiveConvoIdAtom);
|
||||||
|
if (!currentConvoId) return;
|
||||||
|
|
||||||
|
const currentMessages = get(chatMessages)[currentConvoId] ?? [];
|
||||||
|
const updatedMessages = [...currentMessages, ...newMessages];
|
||||||
|
|
||||||
|
const newData: Record<string, ChatMessage[]> = {
|
||||||
|
...get(chatMessages),
|
||||||
|
};
|
||||||
|
newData[currentConvoId] = updatedMessages;
|
||||||
|
set(chatMessages, newData);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const addNewMessageAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, newMessage: ChatMessage) => {
|
||||||
|
const currentConvoId = get(getActiveConvoIdAtom);
|
||||||
|
if (!currentConvoId) return;
|
||||||
|
|
||||||
|
const currentMessages = get(chatMessages)[currentConvoId] ?? [];
|
||||||
|
const updatedMessages = [newMessage, ...currentMessages];
|
||||||
|
|
||||||
|
const newData: Record<string, ChatMessage[]> = {
|
||||||
|
...get(chatMessages),
|
||||||
|
};
|
||||||
|
newData[currentConvoId] = updatedMessages;
|
||||||
|
set(chatMessages, newData);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const deleteConversationMessage = atom(null, (get, set, id: string) => {
|
||||||
|
const newData: Record<string, ChatMessage[]> = {
|
||||||
|
...get(chatMessages),
|
||||||
|
};
|
||||||
|
newData[id] = [];
|
||||||
|
set(chatMessages, newData);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateMessageAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, id: string, conversationId: string, text: string) => {
|
||||||
|
const messages = get(chatMessages)[conversationId] ?? [];
|
||||||
|
const message = messages.find((e) => e.id === id);
|
||||||
|
if (message) {
|
||||||
|
message.text = text;
|
||||||
|
const updatedMessages = [...messages];
|
||||||
|
|
||||||
|
const newData: Record<string, ChatMessage[]> = {
|
||||||
|
...get(chatMessages),
|
||||||
|
};
|
||||||
|
newData[conversationId] = updatedMessages;
|
||||||
|
set(chatMessages, newData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For updating the status of the last AI message that is pending
|
||||||
|
*/
|
||||||
|
export const updateLastMessageAsReadyAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, id, text: string) => {
|
||||||
|
const currentConvoId = get(getActiveConvoIdAtom);
|
||||||
|
if (!currentConvoId) return;
|
||||||
|
|
||||||
|
const currentMessages = get(chatMessages)[currentConvoId] ?? [];
|
||||||
|
const messageToUpdate = currentMessages.find((e) => e.id === id);
|
||||||
|
|
||||||
|
// if message is not found, do nothing
|
||||||
|
if (!messageToUpdate) return;
|
||||||
|
|
||||||
|
const index = currentMessages.indexOf(messageToUpdate);
|
||||||
|
const updatedMsg: ChatMessage = {
|
||||||
|
...messageToUpdate,
|
||||||
|
status: MessageStatus.Ready,
|
||||||
|
text: text,
|
||||||
|
};
|
||||||
|
|
||||||
|
currentMessages[index] = updatedMsg;
|
||||||
|
const newData: Record<string, ChatMessage[]> = {
|
||||||
|
...get(chatMessages),
|
||||||
|
};
|
||||||
|
newData[currentConvoId] = currentMessages;
|
||||||
|
set(chatMessages, newData);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const currentStreamingMessageAtom = atom<ChatMessage | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
104
web/app/_helpers/atoms/Conversation.atom.ts
Normal file
104
web/app/_helpers/atoms/Conversation.atom.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
import { MainViewState, setMainViewStateAtom } from "./MainView.atom";
|
||||||
|
import { Conversation, ConversationState } from "@/_models/Conversation";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the current active conversation id.
|
||||||
|
*/
|
||||||
|
const activeConversationIdAtom = atom<string | undefined>(undefined);
|
||||||
|
|
||||||
|
export const getActiveConvoIdAtom = atom((get) =>
|
||||||
|
get(activeConversationIdAtom)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const setActiveConvoIdAtom = atom(
|
||||||
|
null,
|
||||||
|
(_get, set, convoId: string | undefined) => {
|
||||||
|
if (convoId) {
|
||||||
|
console.debug(`Set active conversation id: ${convoId}`);
|
||||||
|
set(setMainViewStateAtom, MainViewState.Conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(activeConversationIdAtom, convoId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores all conversation states for the current user
|
||||||
|
*/
|
||||||
|
export const conversationStatesAtom = atom<Record<string, ConversationState>>(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
export const currentConvoStateAtom = atom<ConversationState | undefined>(
|
||||||
|
(get) => {
|
||||||
|
const activeConvoId = get(activeConversationIdAtom);
|
||||||
|
if (!activeConvoId) {
|
||||||
|
console.log("active convo id is undefined");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get(conversationStatesAtom)[activeConvoId];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export const addNewConversationStateAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, conversationId: string, state: ConversationState) => {
|
||||||
|
const currentState = { ...get(conversationStatesAtom) };
|
||||||
|
currentState[conversationId] = state;
|
||||||
|
set(conversationStatesAtom, currentState);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export const updateConversationWaitingForResponseAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, conversationId: string, waitingForResponse: boolean) => {
|
||||||
|
const currentState = { ...get(conversationStatesAtom) };
|
||||||
|
currentState[conversationId] = {
|
||||||
|
...currentState[conversationId],
|
||||||
|
waitingForResponse,
|
||||||
|
};
|
||||||
|
set(conversationStatesAtom, currentState);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export const updateConversationHasMoreAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, conversationId: string, hasMore: boolean) => {
|
||||||
|
const currentState = { ...get(conversationStatesAtom) };
|
||||||
|
currentState[conversationId] = { ...currentState[conversationId], hasMore };
|
||||||
|
set(conversationStatesAtom, currentState);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores all conversations for the current user
|
||||||
|
*/
|
||||||
|
export const userConversationsAtom = atom<Conversation[]>([]);
|
||||||
|
export const currentConversationAtom = atom<Conversation | undefined>((get) =>
|
||||||
|
get(userConversationsAtom).find((c) => c.id === get(getActiveConvoIdAtom))
|
||||||
|
);
|
||||||
|
export const setConvoUpdatedAtAtom = atom(null, (get, set, convoId: string) => {
|
||||||
|
const convo = get(userConversationsAtom).find((c) => c.id === convoId);
|
||||||
|
if (!convo) return;
|
||||||
|
const newConvo: Conversation = {
|
||||||
|
...convo,
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
const newConversations: Conversation[] = get(userConversationsAtom).map((c) =>
|
||||||
|
c.id === convoId ? newConvo : c
|
||||||
|
);
|
||||||
|
|
||||||
|
set(userConversationsAtom, newConversations);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setConvoLastImageAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, convoId: string, lastImageUrl: string) => {
|
||||||
|
const convo = get(userConversationsAtom).find((c) => c.id === convoId);
|
||||||
|
if (!convo) return;
|
||||||
|
const newConvo: Conversation = { ...convo };
|
||||||
|
const newConversations: Conversation[] = get(userConversationsAtom).map(
|
||||||
|
(c) => (c.id === convoId ? newConvo : c)
|
||||||
|
);
|
||||||
|
|
||||||
|
set(userConversationsAtom, newConversations);
|
||||||
|
}
|
||||||
|
);
|
||||||
32
web/app/_helpers/atoms/DownloadState.atom.ts
Normal file
32
web/app/_helpers/atoms/DownloadState.atom.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { DownloadState } from "@/_models/DownloadState";
|
||||||
|
import { atom } from "jotai";
|
||||||
|
|
||||||
|
// download states
|
||||||
|
export const modelDownloadStateAtom = atom<Record<string, DownloadState>>({});
|
||||||
|
|
||||||
|
export const setDownloadStateAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, state: DownloadState) => {
|
||||||
|
const currentState = { ...get(modelDownloadStateAtom) };
|
||||||
|
console.debug(
|
||||||
|
`current download state for ${state.fileName} is ${JSON.stringify(state)}`
|
||||||
|
);
|
||||||
|
currentState[state.fileName] = state;
|
||||||
|
set(modelDownloadStateAtom, currentState);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const setDownloadStateSuccessAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, fileName: string) => {
|
||||||
|
const currentState = { ...get(modelDownloadStateAtom) };
|
||||||
|
const state = currentState[fileName];
|
||||||
|
if (!state) {
|
||||||
|
console.error(`Cannot find download state for ${fileName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete currentState[fileName];
|
||||||
|
set(modelDownloadStateAtom, currentState);
|
||||||
|
}
|
||||||
|
);
|
||||||
54
web/app/_helpers/atoms/MainView.atom.ts
Normal file
54
web/app/_helpers/atoms/MainView.atom.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
import { setActiveConvoIdAtom } from "./Conversation.atom";
|
||||||
|
import { systemBarVisibilityAtom } from "./SystemBar.atom";
|
||||||
|
|
||||||
|
export enum MainViewState {
|
||||||
|
Welcome,
|
||||||
|
ExploreModel,
|
||||||
|
MyModel,
|
||||||
|
ResourceMonitor,
|
||||||
|
Setting,
|
||||||
|
Conversation,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When user wants to create new conversation but haven't selected a model yet.
|
||||||
|
*/
|
||||||
|
ConversationEmptyModel,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the current main view state. Default is Welcome.
|
||||||
|
*/
|
||||||
|
const currentMainViewStateAtom = atom<MainViewState>(MainViewState.Welcome);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter for current main view state.
|
||||||
|
*/
|
||||||
|
export const getMainViewStateAtom = atom((get) =>
|
||||||
|
get(currentMainViewStateAtom)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter for current main view state.
|
||||||
|
*/
|
||||||
|
export const setMainViewStateAtom = atom(
|
||||||
|
null,
|
||||||
|
(get, set, state: MainViewState) => {
|
||||||
|
// return if the state is already set
|
||||||
|
if (get(getMainViewStateAtom) === state) return;
|
||||||
|
|
||||||
|
if (state !== MainViewState.Conversation) {
|
||||||
|
// clear active conversation id if main view state is not Conversation
|
||||||
|
set(setActiveConvoIdAtom, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
const showSystemBar =
|
||||||
|
state !== MainViewState.Conversation &&
|
||||||
|
state !== MainViewState.ConversationEmptyModel;
|
||||||
|
|
||||||
|
// show system bar if state is not Conversation nor ConversationEmptyModel
|
||||||
|
set(systemBarVisibilityAtom, showSystemBar);
|
||||||
|
|
||||||
|
set(currentMainViewStateAtom, state);
|
||||||
|
}
|
||||||
|
);
|
||||||
8
web/app/_helpers/atoms/Modal.atom.ts
Normal file
8
web/app/_helpers/atoms/Modal.atom.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
|
||||||
|
export const showConfirmDeleteConversationModalAtom = atom(false);
|
||||||
|
export const showConfirmSignOutModalAtom = atom(false);
|
||||||
|
export const showConfirmDeleteModalAtom = atom(false);
|
||||||
|
export const showingAdvancedPromptAtom = atom<boolean>(false);
|
||||||
|
export const showingProductDetailAtom = atom<boolean>(false);
|
||||||
|
export const showingMobilePaneAtom = atom<boolean>(false);
|
||||||
6
web/app/_helpers/atoms/Model.atom.ts
Normal file
6
web/app/_helpers/atoms/Model.atom.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Product } from "@/_models/Product";
|
||||||
|
import { atom } from "jotai";
|
||||||
|
|
||||||
|
export const currentProductAtom = atom<Product | undefined>(undefined);
|
||||||
|
|
||||||
|
export const selectedModelAtom = atom<Product | undefined>(undefined);
|
||||||
7
web/app/_helpers/atoms/SystemBar.atom.ts
Normal file
7
web/app/_helpers/atoms/SystemBar.atom.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
|
||||||
|
export const systemBarVisibilityAtom = atom<boolean>(true);
|
||||||
|
|
||||||
|
export const getSystemBarVisibilityAtom = atom((get) =>
|
||||||
|
get(systemBarVisibilityAtom)
|
||||||
|
);
|
||||||
@ -1,14 +1,14 @@
|
|||||||
import {
|
|
||||||
addOldMessagesAtom,
|
|
||||||
conversationStatesAtom,
|
|
||||||
currentConversationAtom,
|
|
||||||
updateConversationHasMoreAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import { ChatMessage, RawMessage, toChatMessage } from "@/_models/ChatMessage";
|
import { ChatMessage, RawMessage, toChatMessage } from "@/_models/ChatMessage";
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
import { useAtomValue, useSetAtom } from "jotai";
|
import { useAtomValue, useSetAtom } from "jotai";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { DataService } from "../../shared/coreService";
|
import { DataService } from "../../shared/coreService";
|
||||||
|
import { addOldMessagesAtom } from "@/_helpers/atoms/ChatMessage.atom";
|
||||||
|
import {
|
||||||
|
currentConversationAtom,
|
||||||
|
conversationStatesAtom,
|
||||||
|
updateConversationHasMoreAtom,
|
||||||
|
} from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hooks to get chat messages for current(active) conversation
|
* Custom hooks to get chat messages for current(active) conversation
|
||||||
|
|||||||
@ -1,23 +1,22 @@
|
|||||||
// import useGetCurrentUser from "./useGetCurrentUser";
|
|
||||||
import { useAtom, useSetAtom } from "jotai";
|
import { useAtom, useSetAtom } from "jotai";
|
||||||
import {
|
|
||||||
addNewConversationStateAtom,
|
|
||||||
currentProductAtom,
|
|
||||||
setActiveConvoIdAtom,
|
|
||||||
userConversationsAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import { Conversation } from "@/_models/Conversation";
|
import { Conversation } from "@/_models/Conversation";
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
import { DataService, InfereceService } from "../../shared/coreService";
|
import { DataService } from "../../shared/coreService";
|
||||||
import { Product } from "@/_models/Product";
|
import { Product } from "@/_models/Product";
|
||||||
|
import {
|
||||||
|
userConversationsAtom,
|
||||||
|
setActiveConvoIdAtom,
|
||||||
|
addNewConversationStateAtom,
|
||||||
|
} from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
import useInitModel from "./useInitModel";
|
||||||
|
|
||||||
const useCreateConversation = () => {
|
const useCreateConversation = () => {
|
||||||
|
const { initModel } = useInitModel();
|
||||||
const [userConversations, setUserConversations] = useAtom(
|
const [userConversations, setUserConversations] = useAtom(
|
||||||
userConversationsAtom
|
userConversationsAtom
|
||||||
);
|
);
|
||||||
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom);
|
const setActiveConvoId = useSetAtom(setActiveConvoIdAtom);
|
||||||
const addNewConvoState = useSetAtom(addNewConversationStateAtom);
|
const addNewConvoState = useSetAtom(addNewConversationStateAtom);
|
||||||
const setActiveProduct = useSetAtom(currentProductAtom);
|
|
||||||
|
|
||||||
const requestCreateConvo = async (model: Product) => {
|
const requestCreateConvo = async (model: Product) => {
|
||||||
const conv: Conversation = {
|
const conv: Conversation = {
|
||||||
@ -28,8 +27,7 @@ const useCreateConversation = () => {
|
|||||||
name: "Conversation",
|
name: "Conversation",
|
||||||
};
|
};
|
||||||
const id = await executeSerial(DataService.CREATE_CONVERSATION, conv);
|
const id = await executeSerial(DataService.CREATE_CONVERSATION, conv);
|
||||||
await executeSerial(InfereceService.INIT_MODEL, model);
|
await initModel(model);
|
||||||
setActiveProduct(model);
|
|
||||||
|
|
||||||
const mappedConvo: Conversation = {
|
const mappedConvo: Conversation = {
|
||||||
id,
|
id,
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
import {
|
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
|
||||||
currentPromptAtom,
|
|
||||||
deleteConversationMessage,
|
|
||||||
getActiveConvoIdAtom,
|
|
||||||
setActiveConvoIdAtom,
|
|
||||||
showingAdvancedPromptAtom,
|
|
||||||
showingProductDetailAtom,
|
|
||||||
userConversationsAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
import { execute } from "@/_services/pluginService";
|
import { execute } from "@/_services/pluginService";
|
||||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import { DataService } from "../../shared/coreService";
|
import { DataService } from "../../shared/coreService";
|
||||||
|
import { deleteConversationMessage } from "@/_helpers/atoms/ChatMessage.atom";
|
||||||
|
import {
|
||||||
|
userConversationsAtom,
|
||||||
|
getActiveConvoIdAtom,
|
||||||
|
setActiveConvoIdAtom,
|
||||||
|
} from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
import {
|
||||||
|
showingProductDetailAtom,
|
||||||
|
showingAdvancedPromptAtom,
|
||||||
|
} from "@/_helpers/atoms/Modal.atom";
|
||||||
|
|
||||||
export default function useDeleteConversation() {
|
export default function useDeleteConversation() {
|
||||||
const [userConversations, setUserConversations] = useAtom(
|
const [userConversations, setUserConversations] = useAtom(
|
||||||
|
|||||||
@ -3,8 +3,8 @@ import { executeSerial } from "@/_services/pluginService";
|
|||||||
import { ModelManagementService } from "../../shared/coreService";
|
import { ModelManagementService } from "../../shared/coreService";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { getModelFiles } from "./useGetDownloadedModels";
|
import { getModelFiles } from "./useGetDownloadedModels";
|
||||||
import { modelDownloadStateAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
|
import { modelDownloadStateAtom } from "@/_helpers/atoms/DownloadState.atom";
|
||||||
|
|
||||||
export default function useGetAvailableModels() {
|
export default function useGetAvailableModels() {
|
||||||
const downloadState = useAtomValue(modelDownloadStateAtom);
|
const downloadState = useAtomValue(modelDownloadStateAtom);
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { Conversation, ConversationState } from "@/_models/Conversation";
|
import { Conversation, ConversationState } from "@/_models/Conversation";
|
||||||
import { useSetAtom } from "jotai";
|
import { useSetAtom } from "jotai";
|
||||||
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
|
import { DataService } from "../../shared/coreService";
|
||||||
import {
|
import {
|
||||||
conversationStatesAtom,
|
conversationStatesAtom,
|
||||||
userConversationsAtom,
|
userConversationsAtom,
|
||||||
} from "@/_helpers/JotaiWrapper";
|
} from "@/_helpers/atoms/Conversation.atom";
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
|
||||||
import { DataService } from "../../shared/coreService";
|
|
||||||
|
|
||||||
const useGetUserConversations = () => {
|
const useGetUserConversations = () => {
|
||||||
const setConversationStates = useSetAtom(conversationStatesAtom);
|
const setConversationStates = useSetAtom(conversationStatesAtom);
|
||||||
|
|||||||
25
web/app/_hooks/useInitModel.ts
Normal file
25
web/app/_hooks/useInitModel.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Product } from "@/_models/Product";
|
||||||
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
|
import { InfereceService } from "../../shared/coreService";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { currentProductAtom } from "@/_helpers/atoms/Model.atom";
|
||||||
|
|
||||||
|
export default function useInitModel() {
|
||||||
|
const [activeModel, setActiveModel] = useAtom(currentProductAtom);
|
||||||
|
|
||||||
|
const initModel = async (model: Product) => {
|
||||||
|
if (activeModel && activeModel.id === model.id) {
|
||||||
|
console.debug(`Model ${model.id} is already init. Ignore..`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await executeSerial(InfereceService.INIT_MODEL, model);
|
||||||
|
console.debug(`Init model ${model.name} successfully!`);
|
||||||
|
setActiveModel(model);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Init model ${model.name} failed: ${err}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { initModel };
|
||||||
|
}
|
||||||
@ -1,14 +1,4 @@
|
|||||||
import {
|
import { currentPromptAtom, showingTyping } from "@/_helpers/JotaiWrapper";
|
||||||
addNewMessageAtom,
|
|
||||||
chatMessages,
|
|
||||||
currentConversationAtom,
|
|
||||||
currentPromptAtom,
|
|
||||||
currentStreamingMessageAtom,
|
|
||||||
getActiveConvoIdAtom,
|
|
||||||
showingTyping,
|
|
||||||
updateMessageAtom,
|
|
||||||
} from "@/_helpers/JotaiWrapper";
|
|
||||||
|
|
||||||
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
||||||
import { selectAtom } from "jotai/utils";
|
import { selectAtom } from "jotai/utils";
|
||||||
import { DataService, InfereceService } from "../../shared/coreService";
|
import { DataService, InfereceService } from "../../shared/coreService";
|
||||||
@ -19,6 +9,16 @@ import {
|
|||||||
} from "@/_models/ChatMessage";
|
} from "@/_models/ChatMessage";
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
|
import {
|
||||||
|
addNewMessageAtom,
|
||||||
|
updateMessageAtom,
|
||||||
|
chatMessages,
|
||||||
|
currentStreamingMessageAtom,
|
||||||
|
} from "@/_helpers/atoms/ChatMessage.atom";
|
||||||
|
import {
|
||||||
|
currentConversationAtom,
|
||||||
|
getActiveConvoIdAtom,
|
||||||
|
} from "@/_helpers/atoms/Conversation.atom";
|
||||||
|
|
||||||
export default function useSendChatMessage() {
|
export default function useSendChatMessage() {
|
||||||
const currentConvo = useAtomValue(currentConversationAtom);
|
const currentConvo = useAtomValue(currentConversationAtom);
|
||||||
@ -50,9 +50,9 @@ export default function useSendChatMessage() {
|
|||||||
|
|
||||||
const newChatMessage = await toChatMessage(newMessage);
|
const newChatMessage = await toChatMessage(newMessage);
|
||||||
addNewMessage(newChatMessage);
|
addNewMessage(newChatMessage);
|
||||||
|
const messageHistory = chatMessagesHistory ?? [];
|
||||||
const recentMessages = [
|
const recentMessages = [
|
||||||
...chatMessagesHistory.sort((a, b) => parseInt(a.id) - parseInt(b.id)),
|
...messageHistory.sort((a, b) => parseInt(a.id) - parseInt(b.id)),
|
||||||
newChatMessage,
|
newChatMessage,
|
||||||
]
|
]
|
||||||
.slice(-10)
|
.slice(-10)
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
import { currentProductAtom } from "@/_helpers/JotaiWrapper";
|
|
||||||
import { executeSerial } from "@/_services/pluginService";
|
import { executeSerial } from "@/_services/pluginService";
|
||||||
import { DataService, InfereceService } from "../../shared/coreService";
|
import { DataService } from "../../shared/coreService";
|
||||||
import { useSetAtom } from "jotai";
|
import useInitModel from "./useInitModel";
|
||||||
|
|
||||||
export default function useStartStopModel() {
|
export default function useStartStopModel() {
|
||||||
const setActiveModel = useSetAtom(currentProductAtom);
|
const { initModel } = useInitModel();
|
||||||
|
|
||||||
const startModel = async (modelId: string) => {
|
const startModel = async (modelId: string) => {
|
||||||
const model = await executeSerial(DataService.GET_MODEL_BY_ID, modelId);
|
const model = await executeSerial(DataService.GET_MODEL_BY_ID, modelId);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
alert(`Model ${modelId} not found! Please re-download the model first.`);
|
alert(`Model ${modelId} not found! Please re-download the model first.`);
|
||||||
} else {
|
} else {
|
||||||
setActiveModel(model);
|
await initModel(model);
|
||||||
executeSerial(InfereceService.INIT_MODEL, model)
|
|
||||||
.then(() => console.info(`Init model success`))
|
|
||||||
.catch((err) => console.log(`Init model error ${err}`));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user