diff --git a/assistant/package.json b/assistant/package.json index cb3dd987..b3a2ff48 100644 --- a/assistant/package.json +++ b/assistant/package.json @@ -1,6 +1,6 @@ { "name": "@petercatai/assistant", - "version": "2.0.5", + "version": "2.0.6", "description": "PeterCat Assistant Application", "repository": "https://github.com/petercat-ai/petercat.git", "license": "MIT", diff --git a/assistant/src/Chat/index.tsx b/assistant/src/Chat/index.tsx index b5a402b8..9187434d 100644 --- a/assistant/src/Chat/index.tsx +++ b/assistant/src/Chat/index.tsx @@ -1,13 +1,14 @@ import { Bubble, useXAgent, useXChat, XStream } from '@ant-design/x'; import { MessageInfo } from '@ant-design/x/es/useXChat'; import { Flex, GetProp, theme } from 'antd'; -import { isEmpty } from 'lodash'; +import { isEmpty, isEqual } from 'lodash'; import React, { memo, useEffect, useRef, useState, type FC } from 'react'; import useSWR from 'swr'; import SignatureIcon from '../icons/SignatureIcon'; import { IContentMessage, ImageURLContentBlock, + ITool, MessageContent, MessageTypeEnum, Role, @@ -25,6 +26,7 @@ import UserContent from './components/UserContent'; import './index.css'; import { UITemplateRender } from './template'; +const CANCEL_REASON = 'petercat user cancel'; export interface MetaData { /** * 角色头像 @@ -102,13 +104,34 @@ const Chat: FC = memo( }) => { const { token: designToken } = theme.useToken(); const tokenRef = useRef(token); + const requestParamsRef = useRef({ apiDomain, apiUrl, prompt, editBotId }); + useEffect(() => { + requestParamsRef.current = { + apiDomain, + apiUrl, + prompt, + editBotId, + }; + }, [tokenRef?.current, apiDomain, apiUrl, prompt, editBotId]); + useEffect(() => { tokenRef.current = token; }, [token]); const messageMinWidth = drawerWidth ? `calc(${drawerWidth}px - 90px)` : '400px'; - const [currentBotInfo, setCurrentBotInfo] = useState(); + + const [currentBotInfo, setCurrentBotInfo] = useState({ + assistantMeta, + helloMessage, + starters, + }); + const currentBotInfoRef = useRef({ + assistantMeta, + helloMessage, + starters, + }); + const { data: botDetail, isValidating } = useSWR( tokenRef?.current ? [ @@ -130,8 +153,8 @@ const Chat: FC = memo( setAbortController(newAbortController); return newAbortController; }; - // ============================ Agent ============================= + const [agent] = useXAgent({ baseURL: apiDomain, request: async ({ messages = [] }, { onUpdate, onSuccess }) => { @@ -155,6 +178,8 @@ const Chat: FC = memo( }); let res: IContentMessage = { role: Role.assistant, content: [] }; try { + const { apiDomain, prompt, apiUrl, editBotId } = + requestParamsRef.current; const response = await streamChat( newMessages, apiDomain, @@ -175,6 +200,13 @@ const Chat: FC = memo( role: Role.assistant, content: resContent, }; + // @ts-ignore + const toolContent: ITool[] = resContent.filter( + (i: MessageContent) => i.type === 'tool', + ); + if (toolContent.length > 0 && toolContent[0]?.extra) { + getToolsResult?.(toolContent[0]?.extra); + } onUpdate(res); } } else { @@ -189,10 +221,8 @@ const Chat: FC = memo( }; } } catch (e: any) { - console.error('Error:', e); - if (e.name === 'AbortError') { + if (e.name === 'AbortError' || e === CANCEL_REASON) { // ignore abort error - // onError(e); } else { res = { role: Role.assistant, @@ -200,6 +230,7 @@ const Chat: FC = memo( }; } } + onSuccess(res); }, }); @@ -213,6 +244,7 @@ const Chat: FC = memo( }); const resetChat = () => { + resetController(); const initMessages: MessageInfo[] = [ { id: 'init', @@ -243,15 +275,11 @@ const Chat: FC = memo( }, }); } - setMessages(initMessages); - }; - - useEffect(() => { - resetController(); + // resetController may touch abort error and set Error Message setTimeout(() => { - resetChat(); + setMessages(initMessages); }, 0); - }, [currentBotInfo]); + }; // ============================ Event ============================ const handleSendMessage = (message: IContentMessage) => { @@ -261,6 +289,43 @@ const Chat: FC = memo( onRequest(message); }; + useEffect(() => { + return () => { + resetController(); + }; + }, []); + + useEffect(() => { + if (isEmpty(botDetail)) { + return; + } + try { + // @ts-ignore + const info = botDetail?.[0] as any; + setCurrentBotInfo({ + assistantMeta: { + avatar: info.avatar, + title: info.name, + }, + helloMessage: info.hello_message, + starters: info.starters || [], + }); + } catch (e) { + console.error('botDetail effect', e); + } + }, [botDetail]); + + useEffect(() => { + if (isEqual(currentBotInfo, currentBotInfoRef.current)) { + return; + } + if (currentBotInfo?.assistantMeta?.title) { + document.title = currentBotInfo.assistantMeta.title; + } + resetChat(); + currentBotInfoRef.current = currentBotInfo; + }, [currentBotInfo]); + useEffect(() => { setCurrentBotInfo({ assistantMeta: { @@ -273,22 +338,6 @@ const Chat: FC = memo( }); }, [assistantMeta, helloMessage, starters]); - useEffect(() => { - if (isEmpty(botDetail)) { - return; - } - // @ts-ignore - const info = botDetail?.[0] as any; - setCurrentBotInfo({ - assistantMeta: { - avatar: info.avatar, - title: info.name, - }, - helloMessage: info.hello_message, - starters: info.starters, - }); - }, [botDetail]); - // ============================ Roles ============================= const roles: GetProp = React.useMemo(() => { const { @@ -347,8 +396,8 @@ const Chat: FC = memo( }, [Role.assistant]: { classNames: { - avatar: 'petercat-avatar', header: 'petercat-header', + avatar: 'petercat-avatar', }, placement: 'start', avatar: , @@ -360,7 +409,7 @@ const Chat: FC = memo( (i: MessageContent) => i.type === 'tool', ); const extra = toolContent?.extra; - getToolsResult?.(extra); + // getToolsResult?.(extra); const textContent = message.content.find( (i: MessageContent) => i.type === MessageTypeEnum.TEXT, ); @@ -369,41 +418,38 @@ const Chat: FC = memo( ); return ( <> - {/* @ts-ignore */} - <> - {extra && ( -
- -
- )} - {textContent && ( -
- -
- )} - {errorContent && ( -
- ops..., {errorContent.text} -
- )} - {extra?.template_id && message.status === 'success' && ( -
- {UITemplateRender({ - templateId: extra.template_id, - cardData: extra.data, - apiDomain: apiDomain, - token: tokenRef?.current ?? '', - })} -
- )} - + {extra && ( +
+ +
+ )} + {textContent && ( +
+ +
+ )} + {errorContent && ( +
+ ops... {errorContent.text} +
+ )} + {extra?.template_id && message.status === 'success' && ( +
+ {UITemplateRender({ + templateId: extra.template_id, + cardData: extra.data, + apiDomain: apiDomain, + token: tokenRef?.current ?? '', + })} +
+ )} ); } catch (e) { @@ -435,6 +481,7 @@ const Chat: FC = memo( return ; } catch (e) { console.error('user items', e); + return null; } }, }, @@ -511,7 +558,7 @@ const Chat: FC = memo( resetChat(); }} onStop={() => { - abortController?.abort('user cancel'); + abortController?.abort(CANCEL_REASON); }} /> diff --git a/client/app/factory/edit/page.tsx b/client/app/factory/edit/page.tsx index 9a6c6f4f..b813fafb 100644 --- a/client/app/factory/edit/page.tsx +++ b/client/app/factory/edit/page.tsx @@ -174,7 +174,7 @@ export default function Edit() { }); } } catch (e) { - console.error(JSON.stringify(e)); + console.error('updateConfigFromChatResult error,', JSON.stringify(e)); } }, []); @@ -201,7 +201,6 @@ export default function Edit() { ); useEffect(() => { - console.log('config', config); if (!isEmpty(config)) { setBotProfile((draft) => { draft.id = config.id; @@ -321,9 +320,8 @@ export default function Edit() { apiDomain={API_HOST} helloMessage={I18N.edit.page.chuCiJianMianXian} starters={[I18N.edit.page.bangWoPeiZhiYi]} - getToolsResult={(result) => { - const data = result?.data; - updateConfigFromChatResult(data); + getToolsResult={(result: any) => { + updateConfigFromChatResult(result?.data); }} /> )} diff --git a/client/yarn.lock b/client/yarn.lock index 8efbf301..bc330f05 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -34,6 +34,15 @@ dependencies: "@ctrl/tinycolor" "^3.6.1" +"@ant-design/cssinjs-utils@^1.1.0": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz#5dd79126057920a6992d57b38dd84e2c0b707977" + integrity sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg== + dependencies: + "@ant-design/cssinjs" "^1.21.0" + "@babel/runtime" "^7.23.2" + rc-util "^5.38.0" + "@ant-design/cssinjs-utils@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.1.tgz#57abb43671023f937348bd33442862c60ac8e8b2" @@ -79,6 +88,17 @@ classnames "^2.2.6" rc-util "^5.31.1" +"@ant-design/icons@^5.4.0": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.5.2.tgz#c4567943cc2b7c6dbe9cae68c06ffa35f755dc0d" + integrity sha512-xc53rjVBl9v2BqFxUjZGti/RfdDeA8/6KYglmInM2PNqSXc/WfuGDTifJI/ZsokJK0aeKvOIbXc9y2g8ILAhEA== + dependencies: + "@ant-design/colors" "^7.0.0" + "@ant-design/icons-svg" "^4.4.0" + "@babel/runtime" "^7.24.8" + classnames "^2.2.6" + rc-util "^5.31.1" + "@ant-design/pro-chat@^1.9.0": version "1.15.3" resolved "https://registry.yarnpkg.com/@ant-design/pro-chat/-/pro-chat-1.15.3.tgz#6fe9bfc919efaed6cefda5d94074726dc2244727" @@ -173,6 +193,21 @@ resize-observer-polyfill "^1.5.1" throttle-debounce "^5.0.0" +"@ant-design/x@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@ant-design/x/-/x-1.0.3.tgz#b4c7dbdab00d8357307a3b6a35446528f8ed6288" + integrity sha512-CSZWkXjyfXGKaYSA8x9ry699NyJl9UBuUXRJCQtIGLMqjP1pK41Dfrr4MTfV8UidwZ2xBXAmuQ+J/DePoMYg3g== + dependencies: + "@ant-design/colors" "^7.1.0" + "@ant-design/cssinjs" "^1.21.1" + "@ant-design/cssinjs-utils" "^1.1.0" + "@ant-design/fast-color" "^2.0.6" + "@ant-design/icons" "^5.4.0" + "@babel/runtime" "^7.25.6" + classnames "^2.5.1" + rc-motion "^2.9.2" + rc-util "^5.43.0" + "@anthropic-ai/sdk@^0.6.2": version "0.6.8" resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.6.8.tgz#670ecb3275e5f63f19f6decdd2ca94f4f8ac5f05" @@ -1053,7 +1088,7 @@ "@babel/plugin-transform-modules-commonjs" "^7.25.9" "@babel/plugin-transform-typescript" "^7.25.9" -"@babel/runtime@^7", "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.1", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.8.4": +"@babel/runtime@^7", "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.1", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6", "@babel/runtime@^7.25.7", "@babel/runtime@^7.8.4": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== @@ -2842,14 +2877,15 @@ resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.2.1.tgz#cb0d111ef700136f4580349ff0226bf25c853f23" integrity sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw== -"@petercatai/assistant@1.0.22": - version "1.0.22" - resolved "https://registry.yarnpkg.com/@petercatai/assistant/-/assistant-1.0.22.tgz#a4113bf4eae9dc66ad0f0e2b33b1f579ca1252a2" - integrity sha512-E8uMZRK3bdD9Oh2mQhK6Zd2A+KV6dt/H2F/fnv/cBT6KOdywwDQIx94K/2fTcpZJXPsUCTMcOhl2877FNaJkxQ== +"@petercatai/assistant@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@petercatai/assistant/-/assistant-2.0.5.tgz#3f66d3a955a00055ab03a732237eb8171fe21162" + integrity sha512-IsWAADjhNqTpaOd5bDnk6hdpvpe0hGRtVuHhUruGjX3sA+fKQOvWzS3KFtfNp2cc4Xf0VslRBkkqENs0gvYVig== dependencies: "@ant-design/icons" "^5.3.5" "@ant-design/pro-chat" "^1.9.0" "@ant-design/pro-editor" "^1.1.1" + "@ant-design/x" "^1.0.0" "@babel/runtime" "^7.18.0" antd "^5.15.3" antd-style "^3.6.1" @@ -10395,6 +10431,15 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motio classnames "^2.2.1" rc-util "^5.43.0" +rc-motion@^2.9.2: + version "2.9.5" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" + integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.44.0" + rc-notification@~5.6.2: version "5.6.2" resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.2.tgz#8525b32d49dd96ec974acae61d1d1eabde61463a" @@ -10598,6 +10643,14 @@ rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2. "@babel/runtime" "^7.18.3" react-is "^18.2.0" +rc-util@^5.44.0: + version "5.44.2" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.2.tgz#6bc5db0e96ebdb515eb5977a7371887e5413a6f8" + integrity sha512-uGSk3hpPBLa3/0QAcKhCjgl4SFnhQCJDLvvpoLdbR6KgDuXrujG+dQaUeUvBJr2ZWak1O/9n+cYbJiWmmk95EQ== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: version "3.15.0" resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.15.0.tgz#45c5b1ef1363287214e0a3c17af29ee0c3764764"