UNPKG

@alauda/doom

Version:

Doctor Doom making docs.

149 lines (148 loc) 6.56 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { consumeSmartDocDisplayStream } from '@alauda/doc-stream-sdk'; import { clsx } from 'clsx'; import { useEffect, useRef, useState } from 'react'; import { Tooltip } from 'react-tooltip'; import { ApiMethod, interceptors, ResponseError, xfetch, } from 'x-fetch'; import { getCloudAuth, useCloudAuth } from "../../../login/store.js"; import { getCloudOrigin, isLoggedIn } from "../../../login/utils.js"; import { Chat } from "./Chat/index.js"; import { Preamble } from "./Preamble/index.js"; import { ResizableUserInput } from "./ResizableUserInput/index.js"; import { Thinking } from "./Thinking.js"; import CloseIcon from '@alauda/doom/assets/close.svg?react'; import LogoutIcon from '@alauda/doom/assets/logout.svg?react'; import NewChatIcon from '@alauda/doom/assets/new-chat.svg?react'; import { useMemoizedFn, useTranslation } from '@alauda/doom/runtime'; import classes from '@alauda/doom/styles/ai-assistant.module.scss'; export const AIAssistant = ({ open, onOpenChange }) => { const t = useTranslation(); const { authInfo, setAuthBasic } = useCloudAuth(); const loggedIn = isLoggedIn(authInfo); const sessionIdRef = useRef(null); const [messages, setMessages] = useState([]); const chatRef = useRef(null); const onNewChat = useMemoizedFn(() => { sessionIdRef.current = null; setMessages([]); }); const onLogout = useMemoizedFn(() => { setAuthBasic(); onNewChat(); }); useEffect(() => { const interceptor = async (req, next) => { if (!req.url.startsWith('/smart/')) { return next(req); } if (!req.headers.has('Authorization')) { req.headers.set('Authorization', `Bearer ${authInfo.token}`); } if (!req.headers.has('CLOUD_AUTH_ORIGIN')) { req.headers.set('CLOUD_AUTH_ORIGIN', getCloudOrigin()); } try { return await next(req); } catch (err) { if (err instanceof ResponseError && // type-coverage:ignore-next-line -- no idea err.response.status === 401) { onLogout(); } throw err; } }; interceptors.use(interceptor); return () => { interceptors.eject(interceptor); }; }, [authInfo, onLogout]); const onClose = useMemoizedFn(() => { onOpenChange(false); }); const flushMessages = useMemoizedFn((setMessagesAction) => { setMessages(setMessagesAction); setTimeout(() => { const chatEl = chatRef.current; if (!chatEl) { return; } chatEl.scrollTop = chatEl.scrollHeight; }, 200); }); const assistantMessageIndexRef = useRef(-1); const onSend_ = async (content) => { const assistantMessage = { id: Date.now(), role: 'assistant', content: _jsx(Thinking, {}), }; const index = (assistantMessageIndexRef.current = messages.length + 1); flushMessages((messages) => [ ...messages, { id: Date.now(), role: 'user', content }, assistantMessage, ]); if (!sessionIdRef.current) { const { session_id } = await xfetch('/smart/api/new_session', { method: ApiMethod.POST, }); sessionIdRef.current = session_id; } const sessionId = sessionIdRef.current; const res = await xfetch('/smart/api/smart_answer_with_search', { type: null, method: ApiMethod.POST, body: { input_text: content, session_id: sessionId, }, }); if (!res.body) { return; } await consumeSmartDocDisplayStream(res.body, { ignoreDocsBlocks: false, onDisplayMessage(nextMessage) { if (sessionId !== sessionIdRef.current) { return; } flushMessages((messages) => [ ...messages.slice(0, index), { ...messages[index], content: nextMessage.content, thoughtProcess: nextMessage.thoughtProcess || undefined, refDocs: nextMessage.refDocs, }, ...messages.slice(index + 1), ]); }, }); }; const [loading, setLoading] = useState(false); const onSend = useMemoizedFn(async (content) => { setLoading(true); try { await onSend_(content); } catch { if (!isLoggedIn(getCloudAuth())) { return; } flushMessages((messages) => { const index = assistantMessageIndexRef.current; return [ ...messages.slice(0, index), { ...messages[index], content: t('NetworkError') }, ...messages.slice(index + 1), ]; }); } finally { setLoading(false); } }); return (_jsxs("div", { className: clsx(classes.container, 'rp-doc', open && classes.open), children: [_jsxs("div", { className: classes.header, children: [_jsxs("div", { className: classes.title, children: [t('ai_assistant'), loggedIn && (_jsxs(_Fragment, { children: [_jsxs("span", { className: classes.username, children: ["(", authInfo.detail.user.name, ")"] }), _jsx(LogoutIcon, { className: "ai-assistant-logout", onClick: onLogout }), _jsx(Tooltip, { anchorSelect: ".ai-assistant-logout", children: t('logout') })] }))] }), _jsxs("div", { className: classes.icons, children: [messages.length ? (_jsxs(_Fragment, { children: [_jsx(NewChatIcon, { className: "ai-assistant-new-chat", onClick: onNewChat }), _jsx(Tooltip, { anchorSelect: ".ai-assistant-new-chat", children: t('new_chat') })] })) : null, _jsx(CloseIcon, { className: "ai-assistant-close", onClick: onClose }), _jsx(Tooltip, { anchorSelect: ".ai-assistant-close", children: t('close') })] })] }), messages.length ? (_jsx(Chat, { ref: chatRef, messages: messages })) : (_jsx(Preamble, { loggedIn: loggedIn })), loggedIn && _jsx(ResizableUserInput, { loading: loading, onSend: onSend })] })); };