UNPKG

@antv/dumi-theme-antv

Version:
407 lines (399 loc) 18.5 kB
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } import { BranchesOutlined, CheckOutlined, CopyOutlined, DeleteOutlined, PlusSquareOutlined, SyncOutlined, ToolOutlined } from '@ant-design/icons'; import { Bubble } from '@ant-design/x'; import { Button, Flex, Space, Tooltip } from 'antd'; import { useChat } from '@ai-sdk/react'; import { TextStreamChatTransport } from 'ai'; import { useIntl, useSiteData } from 'dumi'; import { findLast } from 'lodash-es'; import React, { useEffect, useState, useMemo, useRef } from 'react'; import { useCopyToClipboard } from 'react-use'; import { useSnapshot } from 'valtio'; import { PromptTextarea } from "../../../../components/AI/HomeDialog/PromptTextarea"; import { AIChatStore, branchMessage, clearEmptySession, createPureNewSession, deleteMessage, derivedState } from "../../../../model/AIChat"; import { getCodeFromMarkdown, isPreviewable } from "../../../../utils/code"; import { MarkdownComponent } from "../MarkdownComponent"; import styles from "./index.module.less"; import { useAutoScroll } from "./useAutoScroll"; import { getBaseURL } from "../../../../utils/env"; import { AIMode } from "../../../../components/AI/constant"; import { trackEvent } from "../../../../utils/analytics"; var avatar = { icon: /*#__PURE__*/React.createElement("img", { draggable: false, src: 'https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*7svFR6wkPMoAAAAAAAAAAAAADmJ7AQ/original', alt: "AntV" }), style: { borderRadius: 0, backgroundColor: 'transparent' } }; var chatScrollIntoView = function chatScrollIntoView() { setTimeout(function () { document.getElementById('msgBoxAnchor').scrollIntoView({ block: 'end' }); }, 100); }; // 辅助函数:将旧的 Message 格式转换为新的 UIMessage 格式 var convertToUIMessages = function convertToUIMessages(messages) { return messages.map(function (msg) { return { id: msg.id, role: msg.role, parts: [{ type: 'text', text: msg.content }] }; }); }; // 辅助函数:从 UIMessage 提取文本内容 var getTextContent = function getTextContent(message) { return message.parts.filter(function (part) { return part.type === 'text'; }).map(function (part) { return part.text; }).join(''); }; function MsgBox(props) { var _derivedSnap$activeSe, _derivedSnap$activeSe2, _derivedSnap$activeSe4, _derivedSnap$activeSe7, _derivedSnap$activeSe8, _messages; var _props$messages = props.messages, initialMessages = _props$messages === void 0 ? [] : _props$messages, _props$simple = props.simple, simple = _props$simple === void 0 ? false : _props$simple, onCodegen = props.onCodegen, title = props.title; var _useIntl = useIntl(), formatMessage = _useIntl.formatMessage; var _useState = useState(''), _useState2 = _slicedToArray(_useState, 2), promptText = _useState2[0], setPromptText = _useState2[1]; var _useState3 = useState(''), _useState4 = _slicedToArray(_useState3, 2), fileSummary = _useState4[0], setFileSummary = _useState4[1]; var snap = useSnapshot(AIChatStore); var derivedSnap = useSnapshot(derivedState); var _useCopyToClipboard = useCopyToClipboard(), _useCopyToClipboard2 = _slicedToArray(_useCopyToClipboard, 2), copyState = _useCopyToClipboard2[0], copyToClipboard = _useCopyToClipboard2[1]; var latestUserMessage = findLast((_derivedSnap$activeSe = derivedSnap.activeSession) === null || _derivedSnap$activeSe === void 0 ? void 0 : _derivedSnap$activeSe.messages, function (msg) { return msg.role === 'user'; }); // 使用 ref 存储动态值,避免重新创建 transport var anonymousUserIdRef = useRef(snap.anonymousUserId); var activeSessionIdRef = useRef((_derivedSnap$activeSe2 = derivedSnap.activeSession) === null || _derivedSnap$activeSe2 === void 0 ? void 0 : _derivedSnap$activeSe2.id); var _useSiteData = useSiteData(), themeConfig = _useSiteData.themeConfig; useEffect(function () { var _derivedSnap$activeSe3; anonymousUserIdRef.current = snap.anonymousUserId; activeSessionIdRef.current = (_derivedSnap$activeSe3 = derivedSnap.activeSession) === null || _derivedSnap$activeSe3 === void 0 ? void 0 : _derivedSnap$activeSe3.id; }, [snap.anonymousUserId, (_derivedSnap$activeSe4 = derivedSnap.activeSession) === null || _derivedSnap$activeSe4 === void 0 ? void 0 : _derivedSnap$activeSe4.id]); // 转换初始消息为新格式 var convertedInitialMessages = useMemo(function () { return convertToUIMessages(initialMessages); }, [initialMessages]); // 核心:使用 useChat hook,配置 DefaultChatTransport var _useChat = useChat({ transport: new TextStreamChatTransport({ api: getBaseURL() + '/api/modules/antv/ai/chat', credentials: 'include', headers: { // xxx }, // body 可以是函数,用于获取最新的动态值 body: function body() { return { gptConversationId: activeSessionIdRef.current, anonymousUserId: anonymousUserIdRef.current, mountId: 'container', antvContext: (latestUserMessage === null || latestUserMessage === void 0 ? void 0 : latestUserMessage.context) || props.context, library: AIChatStore.lib, mode: AIChatStore.mode }; } }), messages: convertedInitialMessages, // 当AI响应结束时触发 onFinish: function onFinish(_ref) { var message = _ref.message, isAbort = _ref.isAbort, isDisconnect = _ref.isDisconnect, isError = _ref.isError; if (!isAbort && !isDisconnect && !isError) { var _derivedState$activeS; var messageContent = getTextContent(message); if (isPreviewable(messageContent)) { var codeBlock = getCodeFromMarkdown(messageContent).code; AIChatStore.codeBlock = codeBlock; onCodegen === null || onCodegen === void 0 || onCodegen(codeBlock); } else { AIChatStore.codeBlock = ''; } (_derivedState$activeS = derivedState.activeSession) === null || _derivedState$activeS === void 0 || (_derivedState$activeS = _derivedState$activeS.messages) === null || _derivedState$activeS === void 0 || _derivedState$activeS.push({ id: crypto.randomUUID(), role: 'assistant', content: messageContent, createdAt: Date.now() }); } }, onError: function onError(error) { var _derivedState$activeS2; console.error('回答失败', error); // 同步错误消息到 valtio (_derivedState$activeS2 = derivedState.activeSession) === null || _derivedState$activeS2 === void 0 || (_derivedState$activeS2 = _derivedState$activeS2.messages) === null || _derivedState$activeS2 === void 0 || _derivedState$activeS2.push({ id: crypto.randomUUID(), role: 'assistant', content: formatMessage({ id: 'ai.msgbox.error.response' }), createdAt: Date.now() // mode, // lib, }); }, experimental_throttle: 500 }), messages = _useChat.messages, setMessages = _useChat.setMessages, sendMessage = _useChat.sendMessage, regenerate = _useChat.regenerate, status = _useChat.status, stop = _useChat.stop; // 处理用户提交 var handleSubmit = function handleSubmit() { var _derivedSnap$activeSe5, _derivedState$activeS3; var trimmedPrompt = promptText.trim(); if (!trimmedPrompt || status === 'streaming' || status === 'submitted') return; if (derivedSnap.activeSession && ((_derivedSnap$activeSe5 = derivedSnap.activeSession.messages) === null || _derivedSnap$activeSe5 === void 0 ? void 0 : _derivedSnap$activeSe5.length) === 0) { derivedState.activeSession.title = trimmedPrompt; } // 使用 sendMessage 函数发送新消息 // 第二个参数传递每次请求特定的额外数据 sendMessage({ text: promptText }, { body: { // context: fileSummary, // lib: snap.lib, // mode: snap.mode, } }); setPromptText(''); (_derivedState$activeS3 = derivedState.activeSession) === null || _derivedState$activeS3 === void 0 || (_derivedState$activeS3 = _derivedState$activeS3.messages) === null || _derivedState$activeS3 === void 0 || _derivedState$activeS3.push({ id: crypto.randomUUID(), role: 'user', content: promptText, createdAt: Date.now(), context: fileSummary, lib: snap.lib }); chatScrollIntoView(); // 埋点 if ((typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object') { trackEvent('start_ai_chat', { entry_point: simple ? 'Drawer' : 'MsgBox', mode: AIChatStore.mode, lib: AIChatStore.lib, page_title: document.title, location: location.href }); } }; // 同步 Valtio store -> useChat state useEffect(function () { if (simple) { return; } setTimeout(function () { var _derivedSnap$activeSe6; var sessionMessages = (_derivedSnap$activeSe6 = derivedSnap.activeSession) === null || _derivedSnap$activeSe6 === void 0 ? void 0 : _derivedSnap$activeSe6.messages; if ((sessionMessages === null || sessionMessages === void 0 ? void 0 : sessionMessages.length) > 0) { var converted = convertToUIMessages(sessionMessages); // 避免无限循环,仅当消息数量或内容不同时更新 if (messages.length !== converted.length || JSON.stringify(messages) !== JSON.stringify(converted)) { setMessages(converted); } } else { setMessages([]); } }); }, [(_derivedSnap$activeSe7 = derivedSnap.activeSession) === null || _derivedSnap$activeSe7 === void 0 ? void 0 : _derivedSnap$activeSe7.id, (_derivedSnap$activeSe8 = derivedSnap.activeSession) === null || _derivedSnap$activeSe8 === void 0 || (_derivedSnap$activeSe8 = _derivedSnap$activeSe8.messages) === null || _derivedSnap$activeSe8 === void 0 ? void 0 : _derivedSnap$activeSe8.length]); // 处理从外部(如demo页)发起的对话 useEffect(function () { if (snap.tempMessage) { var _derivedState$activeS4, _derivedState$activeS5; sendMessage({ text: snap.tempMessage.content }); (_derivedState$activeS4 = derivedState.activeSession) === null || _derivedState$activeS4 === void 0 || (_derivedState$activeS4 = _derivedState$activeS4.messages) === null || _derivedState$activeS4 === void 0 || (_derivedState$activeS5 = _derivedState$activeS4.push) === null || _derivedState$activeS5 === void 0 || _derivedState$activeS5.call(_derivedState$activeS4, snap.tempMessage); AIChatStore.tempMessage = null; } }, [snap.tempMessage]); useEffect(function () { chatScrollIntoView(); if (simple) { createPureNewSession(title); AIChatStore.mode = AIMode.implement; if (!themeConfig.isAntVSite && themeConfig.title) { AIChatStore.lib = themeConfig.title; } } return function () { if (simple) { clearEmptySession(); } }; }, []); useEffect(function () { chatScrollIntoView(); stop(); AIChatStore.errorMsg = null; }, [snap.activeSessionId]); // 将 messages 数组作为依赖项。当它变化时,Hook 会运行。 var _useAutoScroll = useAutoScroll(messages), containerRef = _useAutoScroll.containerRef, anchorRef = _useAutoScroll.anchorRef; var autofix = function autofix() { var _derivedState$activeS6; var autoFixPromptText = "".concat(formatMessage({ id: 'ai.msgbox.auto.fix.prompt' }), " [").concat(snap.errorMsg, "]"); sendMessage({ text: autoFixPromptText }, { body: { antvContext: snap.codeBlock } }); AIChatStore.errorMsg = null; (_derivedState$activeS6 = derivedState.activeSession) === null || _derivedState$activeS6 === void 0 || (_derivedState$activeS6 = _derivedState$activeS6.messages) === null || _derivedState$activeS6 === void 0 || _derivedState$activeS6.push({ id: crypto.randomUUID(), role: 'user', content: autoFixPromptText, createdAt: Date.now(), context: snap.codeBlock, lib: snap.lib }); chatScrollIntoView(); }; return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Flex, { gap: "middle", vertical: true, className: styles.chatContainer, ref: containerRef }, messages.map(function (msg, index) { var textContent = getTextContent(msg); return /*#__PURE__*/React.createElement(Bubble, { key: msg.id || index, content: /*#__PURE__*/React.createElement(MarkdownComponent, { content: textContent, showRunButton: !props.simple }), avatar: msg.role === 'assistant' ? avatar : null, footer: status === 'ready' && /*#__PURE__*/React.createElement(Space, { size: "small" }, msg.role === 'assistant' && index === messages.length - 1 && /*#__PURE__*/React.createElement(Tooltip, { title: formatMessage({ id: 'ai.msgbox.retry' }) }, /*#__PURE__*/React.createElement(Button, { onClick: function onClick() { regenerate(); derivedState.activeSession.messages.pop(); }, color: "default", variant: "text", size: "small", icon: /*#__PURE__*/React.createElement(SyncOutlined, null) })), /*#__PURE__*/React.createElement(Tooltip, { title: formatMessage({ id: 'ai.msgbox.copy' }) }, /*#__PURE__*/React.createElement(Button, { color: "default", variant: "text", size: "small", onClick: function onClick() { return copyToClipboard(textContent); }, icon: copyState.value === textContent ? /*#__PURE__*/React.createElement(CheckOutlined, null) : /*#__PURE__*/React.createElement(CopyOutlined, null) })), /*#__PURE__*/React.createElement(Tooltip, { title: formatMessage({ id: 'ai.msgbox.continue.from.here' }) }, /*#__PURE__*/React.createElement(Button, { color: "default", variant: "text", size: "small", onClick: function onClick() { return branchMessage(index); }, icon: /*#__PURE__*/React.createElement(BranchesOutlined, null) })), msg.role === 'assistant' && /*#__PURE__*/React.createElement(Tooltip, { title: formatMessage({ id: 'ai.msgbox.delete' }) }, /*#__PURE__*/React.createElement(Button, { color: "default", variant: "text", size: "small", onClick: function onClick() { return deleteMessage(msg.id); }, icon: /*#__PURE__*/React.createElement(DeleteOutlined, null) }))), placement: msg.role === 'user' ? 'end' : 'start' }); }), (status === 'streaming' || status === 'submitted') && ((_messages = messages[messages.length - 1]) === null || _messages === void 0 ? void 0 : _messages.role) === 'user' && /*#__PURE__*/React.createElement(Bubble, { placement: "start", avatar: avatar, loading: true }), /*#__PURE__*/React.createElement("div", { ref: anchorRef, id: "msgBoxAnchor" })), /*#__PURE__*/React.createElement("div", null, !props.simple && /*#__PURE__*/React.createElement("div", { className: styles.newButtonContainer }, /*#__PURE__*/React.createElement(Space, null, /*#__PURE__*/React.createElement("button", { type: "button", onClick: function onClick() { return createPureNewSession(); }, className: styles.newButton }, /*#__PURE__*/React.createElement(Space, null, /*#__PURE__*/React.createElement(PlusSquareOutlined, null), formatMessage({ id: 'ai.msgbox.start.new.chat' }))), snap.errorMsg && status === 'ready' && /*#__PURE__*/React.createElement(Button, { onClick: autofix, color: "danger", variant: "filled", icon: /*#__PURE__*/React.createElement(ToolOutlined, null) }, formatMessage({ id: 'ai.msgbox.auto.fix' })))), /*#__PURE__*/React.createElement(PromptTextarea, { size: "compact", value: promptText, onChange: setPromptText, loading: status === 'streaming' || status === 'submitted', onCancel: stop, showAction: !props.simple, style: { marginBottom: 0 }, onConfirm: handleSubmit, onDataSummaryChange: setFileSummary, showModeSelector: true }))); } export default MsgBox;