|
|
|
@ -4,24 +4,21 @@ import { toast } from "react-hot-toast";
|
|
|
|
|
import * as api from "../helpers/api";
|
|
|
|
|
import useLoading from "../hooks/useLoading";
|
|
|
|
|
import { marked } from "../labs/marked";
|
|
|
|
|
import { useMessageStore } from "../store/zustand/message";
|
|
|
|
|
import Icon from "./Icon";
|
|
|
|
|
import { generateDialog } from "./Dialog";
|
|
|
|
|
import showSettingDialog from "./SettingDialog";
|
|
|
|
|
|
|
|
|
|
type Props = DialogProps;
|
|
|
|
|
|
|
|
|
|
interface History {
|
|
|
|
|
question: string;
|
|
|
|
|
answer: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AskAIDialog: React.FC<Props> = (props: Props) => {
|
|
|
|
|
const { destroy, hide } = props;
|
|
|
|
|
const fetchingState = useLoading(false);
|
|
|
|
|
const [historyList, setHistoryList] = useState<History[]>([]);
|
|
|
|
|
const messageStore = useMessageStore();
|
|
|
|
|
const [isEnabled, setIsEnabled] = useState<boolean>(true);
|
|
|
|
|
const [isInIME, setIsInIME] = useState(false);
|
|
|
|
|
const [question, setQuestion] = useState<string>("");
|
|
|
|
|
const messageList = messageStore.messageList;
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
api.checkOpenAIEnabled().then(({ data }) => {
|
|
|
|
@ -47,10 +44,18 @@ const AskAIDialog: React.FC<Props> = (props: Props) => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSendQuestionButtonClick = async () => {
|
|
|
|
|
if (!question) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fetchingState.setLoading();
|
|
|
|
|
setQuestion("");
|
|
|
|
|
messageStore.addMessage({
|
|
|
|
|
role: "user",
|
|
|
|
|
content: question,
|
|
|
|
|
});
|
|
|
|
|
try {
|
|
|
|
|
await askQuestion(question);
|
|
|
|
|
await fetchChatCompletion();
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
toast.error(error.response.data.error);
|
|
|
|
@ -58,21 +63,15 @@ const AskAIDialog: React.FC<Props> = (props: Props) => {
|
|
|
|
|
fetchingState.setFinish();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const askQuestion = async (question: string) => {
|
|
|
|
|
if (question === "") {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fetchChatCompletion = async () => {
|
|
|
|
|
const messageList = messageStore.getState().messageList;
|
|
|
|
|
const {
|
|
|
|
|
data: { data: answer },
|
|
|
|
|
} = await api.postChatCompletion(question);
|
|
|
|
|
setHistoryList([
|
|
|
|
|
{
|
|
|
|
|
question,
|
|
|
|
|
answer: answer.replace(/^\n\n/, ""),
|
|
|
|
|
},
|
|
|
|
|
...historyList,
|
|
|
|
|
]);
|
|
|
|
|
} = await api.postChatCompletion(messageList);
|
|
|
|
|
messageStore.addMessage({
|
|
|
|
|
role: "assistant",
|
|
|
|
|
content: answer.replace(/^\n\n/, ""),
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
@ -87,7 +86,36 @@ const AskAIDialog: React.FC<Props> = (props: Props) => {
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="dialog-content-container !w-112 max-w-full">
|
|
|
|
|
<div className="w-full relative">
|
|
|
|
|
{messageList.map((message, index) => (
|
|
|
|
|
<div key={index} className="w-full flex flex-col justify-start items-start mt-4 space-y-2">
|
|
|
|
|
{message.role === "user" ? (
|
|
|
|
|
<div className="w-full flex flex-row justify-end items-start pl-6">
|
|
|
|
|
<span className="word-break shadow rounded-lg rounded-tr-none px-3 py-2 opacity-80 bg-gray-100 dark:bg-zinc-700">
|
|
|
|
|
{message.content}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="w-full flex flex-row justify-start items-start pr-8 space-x-2">
|
|
|
|
|
<Icon.Bot className="mt-2 flex-shrink-0 mr-1 w-6 h-auto opacity-80" />
|
|
|
|
|
<div className="memo-content-wrapper !w-auto flex flex-col justify-start items-start shadow rounded-lg rounded-tl-none px-3 py-2 bg-gray-100 dark:bg-zinc-700">
|
|
|
|
|
<div className="memo-content-text">{marked(message.content)}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
{fetchingState.isLoading && (
|
|
|
|
|
<p className="w-full py-2 mt-4 flex flex-row justify-center items-center">
|
|
|
|
|
<Icon.Loader className="w-5 h-auto animate-spin" />
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
{!isEnabled && (
|
|
|
|
|
<div className="w-full flex flex-col justify-center items-center mt-4 space-y-2">
|
|
|
|
|
<p>You have not set up your OpenAI API key.</p>
|
|
|
|
|
<Button onClick={() => handleGotoSystemSetting()}>Go to settings</Button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
<div className="w-full relative mt-4">
|
|
|
|
|
<Textarea
|
|
|
|
|
className="w-full"
|
|
|
|
|
placeholder="Ask anything…"
|
|
|
|
@ -104,32 +132,6 @@ const AskAIDialog: React.FC<Props> = (props: Props) => {
|
|
|
|
|
onClick={handleSendQuestionButtonClick}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{fetchingState.isLoading && (
|
|
|
|
|
<p className="w-full py-2 mt-4 flex flex-row justify-center items-center">
|
|
|
|
|
<Icon.Loader className="w-5 h-auto animate-spin" />
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
{historyList.map((history, index) => (
|
|
|
|
|
<div key={index} className="w-full flex flex-col justify-start items-start mt-4 space-y-2">
|
|
|
|
|
<div className="w-full flex flex-row justify-start items-start pr-6">
|
|
|
|
|
<span className="word-break rounded-lg rounded-tl-none px-3 py-2 opacity-80 bg-gray-100 dark:bg-zinc-700">
|
|
|
|
|
{history.question}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="w-full flex flex-row justify-end items-start pl-8 space-x-2">
|
|
|
|
|
<div className="memo-content-wrapper !w-auto flex flex-col justify-start items-start rounded-lg rounded-tr-none px-3 py-2 bg-gray-100 dark:bg-zinc-700">
|
|
|
|
|
<div className="memo-content-text">{marked(history.answer)}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<Icon.Bot className="mt-2 flex-shrink-0 mr-1 w-6 h-auto opacity-80" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
{!isEnabled && (
|
|
|
|
|
<div className="w-full flex flex-col justify-center items-center mt-4 space-y-2">
|
|
|
|
|
<p>You have not set up your OpenAI API key.</p>
|
|
|
|
|
<Button onClick={() => handleGotoSystemSetting()}>Go to settings</Button>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|