@lobehub/chat 0.44.4 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +67 -0
- package/package.json +3 -3
- package/src/features/AgentSetting/AgentPrompt/index.tsx +11 -6
- package/src/locales/default/common.ts +5 -0
- package/src/pages/chat/features/Conversation/ChatList/index.tsx +6 -26
- package/src/pages/chat/features/Conversation/Input/index.tsx +11 -5
- package/src/pages/chat/features/Header/index.tsx +2 -2
- package/src/pages/chat/features/Sidebar/Header.tsx +10 -0
- package/src/pages/chat/features/Sidebar/SystemRole.tsx +10 -6
- package/src/pages/chat/features/Sidebar/Topic/index.tsx +3 -3
- package/src/plugins/type.ts +42 -0
- package/src/store/session/hooks/index.ts +1 -0
- package/src/store/session/hooks/useSessionChatInit.ts +12 -0
- package/src/store/session/slices/chat/actions/message.ts +48 -18
- package/src/store/session/slices/chat/initialState.ts +1 -0
- package/src/store/session/slices/chat/selectors/chat.ts +36 -1
- package/src/store/session/slices/chat/selectors/index.ts +2 -1
- package/vitest.config.ts +4 -0
package/.eslintrc.js
CHANGED
|
@@ -9,5 +9,6 @@ config.rules['unicorn/no-null'] = 0;
|
|
|
9
9
|
config.rules['unicorn/no-typeof-undefined'] = 0;
|
|
10
10
|
config.rules['unicorn/explicit-length-check'] = 0;
|
|
11
11
|
config.rules['unicorn/prefer-code-point'] = 0;
|
|
12
|
+
config.rules['no-extra-boolean-cast'] = 0;
|
|
12
13
|
|
|
13
14
|
module.exports = config;
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,73 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.46.0](https://github.com/lobehub/lobe-chat/compare/v0.45.0...v0.46.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-08-14**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Update styling and functionality of AgentPrompt and EditableMessage components, 支持停止生成消息.
|
|
12
|
+
|
|
13
|
+
#### 🐛 Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **misc**: Remove input highlight.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### What's improved
|
|
23
|
+
|
|
24
|
+
- **misc**: Update styling and functionality of AgentPrompt and EditableMessage components ([80b521c](https://github.com/lobehub/lobe-chat/commit/80b521c))
|
|
25
|
+
- **misc**: 支持停止生成消息, closes [#78](https://github.com/lobehub/lobe-chat/issues/78) ([9eeca80](https://github.com/lobehub/lobe-chat/commit/9eeca80))
|
|
26
|
+
|
|
27
|
+
#### What's fixed
|
|
28
|
+
|
|
29
|
+
- **misc**: Remove input highlight ([ad2001a](https://github.com/lobehub/lobe-chat/commit/ad2001a))
|
|
30
|
+
|
|
31
|
+
</details>
|
|
32
|
+
|
|
33
|
+
<div align="right">
|
|
34
|
+
|
|
35
|
+
[](#readme-top)
|
|
36
|
+
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
## [Version 0.45.0](https://github.com/lobehub/lobe-chat/compare/v0.44.4...v0.45.0)
|
|
40
|
+
|
|
41
|
+
<sup>Released on **2023-08-14**</sup>
|
|
42
|
+
|
|
43
|
+
#### ✨ Features
|
|
44
|
+
|
|
45
|
+
- **misc**: 优化每个角色的初始引导.
|
|
46
|
+
|
|
47
|
+
#### 💄 Styles
|
|
48
|
+
|
|
49
|
+
- **misc**: 优化初始化加载状态,等到会话加载完毕再显示内容.
|
|
50
|
+
|
|
51
|
+
<br/>
|
|
52
|
+
|
|
53
|
+
<details>
|
|
54
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
55
|
+
|
|
56
|
+
#### What's improved
|
|
57
|
+
|
|
58
|
+
- **misc**: 优化每个角色的初始引导, closes [#76](https://github.com/lobehub/lobe-chat/issues/76) ([8d78dc5](https://github.com/lobehub/lobe-chat/commit/8d78dc5))
|
|
59
|
+
|
|
60
|
+
#### Styles
|
|
61
|
+
|
|
62
|
+
- **misc**: 优化初始化加载状态,等到会话加载完毕再显示内容 ([cf603cb](https://github.com/lobehub/lobe-chat/commit/cf603cb))
|
|
63
|
+
|
|
64
|
+
</details>
|
|
65
|
+
|
|
66
|
+
<div align="right">
|
|
67
|
+
|
|
68
|
+
[](#readme-top)
|
|
69
|
+
|
|
70
|
+
</div>
|
|
71
|
+
|
|
5
72
|
### [Version 0.44.4](https://github.com/lobehub/lobe-chat/compare/v0.44.3...v0.44.4)
|
|
6
73
|
|
|
7
74
|
<sup>Released on **2023-08-13**</sup>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"react-dom": "^18",
|
|
92
92
|
"react-hotkeys-hook": "^4",
|
|
93
93
|
"react-i18next": "^13",
|
|
94
|
-
"react-layout-kit": "^1",
|
|
94
|
+
"react-layout-kit": "^1.7.1",
|
|
95
95
|
"serpapi": "^2",
|
|
96
96
|
"swr": "^2",
|
|
97
97
|
"ts-md5": "^1",
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"@lobehub/i18n-cli": "latest",
|
|
106
106
|
"@lobehub/lint": "latest",
|
|
107
107
|
"@next/eslint-plugin-next": "^13",
|
|
108
|
-
"@testing-library/jest-dom": "^
|
|
108
|
+
"@testing-library/jest-dom": "^6",
|
|
109
109
|
"@testing-library/react": "^14",
|
|
110
110
|
"@types/chroma-js": "^2",
|
|
111
111
|
"@types/lodash-es": "^4",
|
|
@@ -14,14 +14,9 @@ import { LobeAgentConfig } from '@/types/session';
|
|
|
14
14
|
|
|
15
15
|
export const useStyles = createStyles(({ css, token }) => ({
|
|
16
16
|
input: css`
|
|
17
|
-
|
|
18
|
-
background: ${token.colorFillTertiary};
|
|
19
|
-
border: 1px solid ${token.colorPrimaryBorder};
|
|
20
|
-
border-radius: 8px;
|
|
17
|
+
border: 1px solid ${token.colorBorderSecondary};
|
|
21
18
|
`,
|
|
22
19
|
markdown: css`
|
|
23
|
-
padding: 12px;
|
|
24
|
-
background: ${token.colorFillTertiary};
|
|
25
20
|
border: 1px solid transparent;
|
|
26
21
|
`,
|
|
27
22
|
}));
|
|
@@ -45,6 +40,7 @@ const AgentPrompt = memo<AgentPromptProps>(({ config, updateConfig }) => {
|
|
|
45
40
|
<TokenTag
|
|
46
41
|
displayMode={'used'}
|
|
47
42
|
maxValue={ModelTokens[model]}
|
|
43
|
+
shape={'square'}
|
|
48
44
|
text={{
|
|
49
45
|
overload: t('tokenTag.overload', { ns: 'common' }),
|
|
50
46
|
remained: t('tokenTag.remained', { ns: 'common' }),
|
|
@@ -61,12 +57,20 @@ const AgentPrompt = memo<AgentPromptProps>(({ config, updateConfig }) => {
|
|
|
61
57
|
<Flexbox gap={16} paddingBlock={16}>
|
|
62
58
|
<EditableMessage
|
|
63
59
|
classNames={styles}
|
|
60
|
+
editButtonSize={'small'}
|
|
64
61
|
editing={editing}
|
|
62
|
+
height={'auto'}
|
|
63
|
+
inputType={'block'}
|
|
65
64
|
onChange={(e) => {
|
|
66
65
|
updateConfig({ systemRole: e });
|
|
67
66
|
}}
|
|
68
67
|
onEditingChange={setEditing}
|
|
68
|
+
placeholder={t('settingAgent.prompt.placeholder')}
|
|
69
69
|
showEditWhenEmpty
|
|
70
|
+
text={{
|
|
71
|
+
cancel: t('cancel', { ns: 'common' }),
|
|
72
|
+
confirm: t('ok', { ns: 'common' }),
|
|
73
|
+
}}
|
|
70
74
|
value={systemRole}
|
|
71
75
|
/>
|
|
72
76
|
{!editing && !!systemRole && (
|
|
@@ -76,6 +80,7 @@ const AgentPrompt = memo<AgentPromptProps>(({ config, updateConfig }) => {
|
|
|
76
80
|
e.stopPropagation();
|
|
77
81
|
setEditing(true);
|
|
78
82
|
}}
|
|
83
|
+
size={'small'}
|
|
79
84
|
type={'primary'}
|
|
80
85
|
>
|
|
81
86
|
{t('edit', { ns: 'common' })}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
about: '关于',
|
|
3
3
|
advanceSettings: '高级设置',
|
|
4
|
+
agentDefaultMessage:
|
|
5
|
+
'你好,我是你的自定义助手,你可以立即与我开始对话,也可以前往 [助手设置](/chat/{{id}}/setting) 完善我的信息。',
|
|
6
|
+
agentDefaultMessageWithSystemRole:
|
|
7
|
+
'你好,我是你的自定义助手,我的角色设定如下: \n\n --- \n\n {{systemRole}} \n\n --- \n\n 立即与我开始对话吧~',
|
|
4
8
|
agentMaxToken: '会话最大长度',
|
|
5
9
|
agentModel: '模型',
|
|
6
10
|
agentProfile: '助手信息',
|
|
@@ -31,6 +35,7 @@ export default {
|
|
|
31
35
|
},
|
|
32
36
|
feedback: '反馈与建议',
|
|
33
37
|
import: '导入配置',
|
|
38
|
+
|
|
34
39
|
inbox: {
|
|
35
40
|
defaultMessage:
|
|
36
41
|
'你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击「+」创建自定义助手',
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { ChatList, RenderErrorMessage, RenderMessage } from '@lobehub/ui';
|
|
2
2
|
import isEqual from 'fast-deep-equal';
|
|
3
|
-
import { memo, useCallback
|
|
3
|
+
import { memo, useCallback } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { Flexbox } from 'react-layout-kit';
|
|
6
6
|
|
|
7
|
-
import { DEFAULT_INBOX_AVATAR } from '@/const/meta';
|
|
8
|
-
import { INBOX_SESSION_ID } from '@/const/session';
|
|
9
7
|
import {
|
|
10
8
|
agentSelectors,
|
|
11
9
|
chatSelectors,
|
|
12
|
-
|
|
10
|
+
useSessionChatInit,
|
|
13
11
|
useSessionStore,
|
|
14
12
|
} from '@/store/session';
|
|
15
13
|
import { ChatMessage } from '@/types/chatMessage';
|
|
@@ -35,14 +33,13 @@ const renderErrorMessage: RenderErrorMessage = (error, message) => {
|
|
|
35
33
|
};
|
|
36
34
|
|
|
37
35
|
const List = () => {
|
|
38
|
-
const init =
|
|
36
|
+
const init = useSessionChatInit();
|
|
39
37
|
const { t } = useTranslation('common');
|
|
40
38
|
|
|
41
|
-
const data = useSessionStore(chatSelectors.
|
|
39
|
+
const data = useSessionStore(chatSelectors.currentChatsWithGuideMessage, isEqual);
|
|
42
40
|
|
|
43
|
-
const [
|
|
41
|
+
const [displayMode, chatLoadingId, deleteMessage, resendMessage, dispatchMessage] =
|
|
44
42
|
useSessionStore((s) => [
|
|
45
|
-
s.activeId === INBOX_SESSION_ID,
|
|
46
43
|
agentSelectors.currentAgentConfig(s).displayMode,
|
|
47
44
|
s.chatLoadingId,
|
|
48
45
|
s.deleteMessage,
|
|
@@ -81,28 +78,11 @@ const List = () => {
|
|
|
81
78
|
[chatLoadingId],
|
|
82
79
|
);
|
|
83
80
|
|
|
84
|
-
// 针对 inbox 添加初始化时的自定义消息
|
|
85
|
-
const displayDataSource = useMemo(() => {
|
|
86
|
-
const emptyGuideMessage = {
|
|
87
|
-
content: t('inbox.defaultMessage'),
|
|
88
|
-
createAt: Date.now(),
|
|
89
|
-
extra: {},
|
|
90
|
-
id: 'default',
|
|
91
|
-
meta: {
|
|
92
|
-
avatar: DEFAULT_INBOX_AVATAR,
|
|
93
|
-
},
|
|
94
|
-
role: 'assistant',
|
|
95
|
-
updateAt: Date.now(),
|
|
96
|
-
} as ChatMessage;
|
|
97
|
-
|
|
98
|
-
return isInbox && data.length === 0 ? [emptyGuideMessage] : data;
|
|
99
|
-
}, [data]);
|
|
100
|
-
|
|
101
81
|
return !init ? (
|
|
102
82
|
<SkeletonList />
|
|
103
83
|
) : (
|
|
104
84
|
<ChatList
|
|
105
|
-
data={
|
|
85
|
+
data={data}
|
|
106
86
|
loadingId={chatLoadingId}
|
|
107
87
|
onActionClick={(key, id) => {
|
|
108
88
|
switch (key) {
|
|
@@ -21,11 +21,15 @@ const ChatInput = () => {
|
|
|
21
21
|
s.preference.inputHeight,
|
|
22
22
|
s.updatePreference,
|
|
23
23
|
]);
|
|
24
|
-
const [
|
|
25
|
-
s
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
const [isLoading, hasTopic, sendMessage, saveToTopic, stopGenerateMessage] = useSessionStore(
|
|
25
|
+
(s) => [
|
|
26
|
+
!!s.chatLoadingId,
|
|
27
|
+
!!s.activeTopicId,
|
|
28
|
+
s.sendMessage,
|
|
29
|
+
s.saveToTopic,
|
|
30
|
+
s.stopGenerateMessage,
|
|
31
|
+
],
|
|
32
|
+
);
|
|
29
33
|
|
|
30
34
|
const footer = hasTopic ? null : (
|
|
31
35
|
<Tooltip title={t('topic.saveCurrentMessages')}>
|
|
@@ -60,10 +64,12 @@ const ChatInput = () => {
|
|
|
60
64
|
actionsRight={<ActionsRight />}
|
|
61
65
|
expand={expand}
|
|
62
66
|
footer={footer}
|
|
67
|
+
loading={isLoading}
|
|
63
68
|
minHeight={CHAT_TEXTAREA_HEIGHT}
|
|
64
69
|
onExpandChange={setExpand}
|
|
65
70
|
onInputChange={setText}
|
|
66
71
|
onSend={sendMessage}
|
|
72
|
+
onStop={stopGenerateMessage}
|
|
67
73
|
placeholder={t('sendPlaceholder')}
|
|
68
74
|
text={{
|
|
69
75
|
send: t('send'),
|
|
@@ -11,12 +11,12 @@ import HeaderTitle from '@/components/HeaderTitle';
|
|
|
11
11
|
import Tag from '@/components/Tag';
|
|
12
12
|
import { INBOX_SESSION_ID } from '@/const/session';
|
|
13
13
|
import { useGlobalStore } from '@/store/global';
|
|
14
|
-
import { agentSelectors,
|
|
14
|
+
import { agentSelectors, useSessionChatInit, useSessionStore } from '@/store/session';
|
|
15
15
|
|
|
16
16
|
import PluginTag from './PluginTag';
|
|
17
17
|
|
|
18
18
|
const Header = memo<{ settings?: boolean }>(({ settings = true }) => {
|
|
19
|
-
const init =
|
|
19
|
+
const init = useSessionChatInit();
|
|
20
20
|
|
|
21
21
|
const { t } = useTranslation('common');
|
|
22
22
|
|
|
@@ -1,15 +1,25 @@
|
|
|
1
|
+
import { createStyles } from 'antd-style';
|
|
1
2
|
import { type ReactNode, memo } from 'react';
|
|
2
3
|
import { Flexbox } from 'react-layout-kit';
|
|
3
4
|
|
|
5
|
+
const useStyles = createStyles(
|
|
6
|
+
({ css, token }) => css`
|
|
7
|
+
z-index: 10;
|
|
8
|
+
box-shadow: 0 2px 8px ${token.colorBgLayout};
|
|
9
|
+
`,
|
|
10
|
+
);
|
|
11
|
+
|
|
4
12
|
interface HeaderProps {
|
|
5
13
|
actions?: ReactNode;
|
|
6
14
|
title: string;
|
|
7
15
|
}
|
|
8
16
|
|
|
9
17
|
const Header = memo<HeaderProps>(({ title, actions }) => {
|
|
18
|
+
const { styles } = useStyles();
|
|
10
19
|
return (
|
|
11
20
|
<Flexbox
|
|
12
21
|
align={'center'}
|
|
22
|
+
className={styles}
|
|
13
23
|
distribution={'space-between'}
|
|
14
24
|
horizontal
|
|
15
25
|
padding={12}
|
|
@@ -7,13 +7,17 @@ import { useTranslation } from 'react-i18next';
|
|
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
|
8
8
|
|
|
9
9
|
import Header from '@/pages/chat/features/Sidebar/Header';
|
|
10
|
-
import { agentSelectors,
|
|
10
|
+
import { agentSelectors, useSessionChatInit, useSessionStore } from '@/store/session';
|
|
11
11
|
|
|
12
12
|
const useStyles = createStyles(({ css, token }) => ({
|
|
13
13
|
prompt: css`
|
|
14
14
|
overflow-x: hidden;
|
|
15
15
|
overflow-y: auto;
|
|
16
|
+
|
|
17
|
+
padding: 0 16px 16px;
|
|
18
|
+
|
|
16
19
|
opacity: 0.75;
|
|
20
|
+
|
|
17
21
|
transition: opacity 200ms ${token.motionEaseOut};
|
|
18
22
|
|
|
19
23
|
&:hover {
|
|
@@ -49,7 +53,7 @@ const SystemRole = memo(() => {
|
|
|
49
53
|
s.updateAgentConfig,
|
|
50
54
|
]);
|
|
51
55
|
|
|
52
|
-
const
|
|
56
|
+
const init = useSessionChatInit();
|
|
53
57
|
const { t } = useTranslation('common');
|
|
54
58
|
return (
|
|
55
59
|
<Flexbox height={'fit-content'}>
|
|
@@ -64,8 +68,10 @@ const SystemRole = memo(() => {
|
|
|
64
68
|
}
|
|
65
69
|
title={t('settingAgent.prompt.title', { ns: 'setting' })}
|
|
66
70
|
/>
|
|
67
|
-
<Flexbox className={styles.promptBox} height={200}
|
|
68
|
-
{
|
|
71
|
+
<Flexbox className={styles.promptBox} height={200}>
|
|
72
|
+
{!init ? (
|
|
73
|
+
<Skeleton active avatar={false} round style={{ marginTop: 12 }} title={false} />
|
|
74
|
+
) : (
|
|
69
75
|
<>
|
|
70
76
|
<EditableMessage
|
|
71
77
|
classNames={{ markdown: styles.prompt }}
|
|
@@ -86,8 +92,6 @@ const SystemRole = memo(() => {
|
|
|
86
92
|
/>
|
|
87
93
|
<div className={styles.promptMask} />
|
|
88
94
|
</>
|
|
89
|
-
) : (
|
|
90
|
-
<Skeleton active avatar={false} style={{ marginTop: 12 }} title={false} />
|
|
91
95
|
)}
|
|
92
96
|
</Flexbox>
|
|
93
97
|
</Flexbox>
|
|
@@ -5,13 +5,13 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
5
5
|
|
|
6
6
|
import Empty from '@/components/Empty';
|
|
7
7
|
import { useGlobalStore } from '@/store/global';
|
|
8
|
-
import { topicSelectors,
|
|
8
|
+
import { topicSelectors, useSessionChatInit, useSessionStore } from '@/store/session';
|
|
9
9
|
|
|
10
10
|
import SkeletonList from './SkeletonList';
|
|
11
11
|
import TopicItem from './TopicItem';
|
|
12
12
|
|
|
13
13
|
export const Topic = () => {
|
|
14
|
-
const
|
|
14
|
+
const init = useSessionChatInit();
|
|
15
15
|
const topics = useSessionStore(topicSelectors.currentTopics, isEqual);
|
|
16
16
|
const { isDarkMode } = useThemeMode();
|
|
17
17
|
const [activeTopicId] = useSessionStore((s) => [s.activeTopicId]);
|
|
@@ -22,7 +22,7 @@ export const Topic = () => {
|
|
|
22
22
|
s.updateGuideState,
|
|
23
23
|
]);
|
|
24
24
|
|
|
25
|
-
return !
|
|
25
|
+
return !init ? (
|
|
26
26
|
<SkeletonList />
|
|
27
27
|
) : (
|
|
28
28
|
<Flexbox gap={2} style={{ marginBottom: 12 }}>
|
package/src/plugins/type.ts
CHANGED
|
@@ -1,17 +1,59 @@
|
|
|
1
1
|
import { ChatCompletionFunctions } from 'openai-edge/types/api';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 插件项
|
|
6
|
+
* @template Result - 结果类型,默认为 any
|
|
7
|
+
* @template RunnerParams - 运行参数类型,默认为 any
|
|
8
|
+
*/
|
|
4
9
|
export interface PluginItem<Result = any, RunnerParams = any> {
|
|
10
|
+
/**
|
|
11
|
+
* 头像
|
|
12
|
+
*/
|
|
5
13
|
avatar: string;
|
|
14
|
+
/**
|
|
15
|
+
* 名称
|
|
16
|
+
*/
|
|
6
17
|
name: string;
|
|
18
|
+
/**
|
|
19
|
+
* 运行器
|
|
20
|
+
* @param params - 运行参数
|
|
21
|
+
* @returns 运行结果的 Promise
|
|
22
|
+
*/
|
|
7
23
|
runner: PluginRunner<RunnerParams, Result>;
|
|
24
|
+
/**
|
|
25
|
+
* 聊天完成函数的模式
|
|
26
|
+
*/
|
|
8
27
|
schema: ChatCompletionFunctions;
|
|
9
28
|
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 插件渲染函数
|
|
32
|
+
* @param props - 插件渲染属性
|
|
33
|
+
* @returns React 节点
|
|
34
|
+
*/
|
|
10
35
|
export type PluginRender = (props: PluginRenderProps) => ReactNode;
|
|
11
36
|
|
|
37
|
+
/**
|
|
38
|
+
* 插件运行器
|
|
39
|
+
* @template Params - 参数类型,默认为 object
|
|
40
|
+
* @template Result - 结果类型,默认为 any
|
|
41
|
+
* @param params - 运行参数
|
|
42
|
+
* @returns 运行结果的 Promise
|
|
43
|
+
*/
|
|
12
44
|
export type PluginRunner<Params = object, Result = any> = (params: Params) => Promise<Result>;
|
|
13
45
|
|
|
46
|
+
/**
|
|
47
|
+
* 插件渲染属性
|
|
48
|
+
* @template Result - 结果类型,默认为 any
|
|
49
|
+
*/
|
|
14
50
|
export interface PluginRenderProps<Result = any> {
|
|
51
|
+
/**
|
|
52
|
+
* 内容
|
|
53
|
+
*/
|
|
15
54
|
content: Result;
|
|
55
|
+
/**
|
|
56
|
+
* 名称
|
|
57
|
+
*/
|
|
16
58
|
name: string;
|
|
17
59
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useSessionStore } from '../store';
|
|
2
|
+
import { useSessionHydrated } from './useSessionHydrated';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 用于判断某个会话是否完全被激活
|
|
6
|
+
*/
|
|
7
|
+
export const useSessionChatInit = () => {
|
|
8
|
+
const sessionHydrated = useSessionHydrated();
|
|
9
|
+
const [hasActive] = useSessionStore((s) => [!!s.activeId]);
|
|
10
|
+
|
|
11
|
+
return sessionHydrated && hasActive;
|
|
12
|
+
};
|
|
@@ -46,7 +46,6 @@ export interface ChatMessageAction {
|
|
|
46
46
|
messages: ChatMessage[],
|
|
47
47
|
assistantMessageId: string,
|
|
48
48
|
) => Promise<{ isFunctionCall: boolean }>;
|
|
49
|
-
|
|
50
49
|
/**
|
|
51
50
|
* 实际获取 AI 响应
|
|
52
51
|
*
|
|
@@ -54,17 +53,24 @@ export interface ChatMessageAction {
|
|
|
54
53
|
* @param parentId - 父消息 ID,可选
|
|
55
54
|
*/
|
|
56
55
|
realFetchAIResponse: (messages: ChatMessage[], parentId: string) => Promise<void>;
|
|
56
|
+
|
|
57
57
|
/**
|
|
58
58
|
* 重新发送消息
|
|
59
59
|
* @param id - 消息 ID
|
|
60
60
|
*/
|
|
61
61
|
resendMessage: (id: string) => Promise<void>;
|
|
62
|
-
|
|
63
62
|
/**
|
|
64
63
|
* 发送消息
|
|
65
64
|
* @param text - 消息文本
|
|
66
65
|
*/
|
|
67
66
|
sendMessage: (text: string) => Promise<void>;
|
|
67
|
+
|
|
68
|
+
stopGenerateMessage: () => void;
|
|
69
|
+
toggleChatLoading: (
|
|
70
|
+
loading: boolean,
|
|
71
|
+
id?: string,
|
|
72
|
+
action?: string,
|
|
73
|
+
) => AbortController | undefined;
|
|
68
74
|
triggerFunctionCall: (id: string) => Promise<void>;
|
|
69
75
|
}
|
|
70
76
|
|
|
@@ -97,14 +103,15 @@ export const chatMessage: StateCreator<
|
|
|
97
103
|
|
|
98
104
|
get().dispatchSession({ chats, id: activeId, type: 'updateSessionChat' });
|
|
99
105
|
},
|
|
100
|
-
|
|
101
106
|
generateMessage: async (messages, assistantId) => {
|
|
102
|
-
const { dispatchMessage } = get();
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
const { dispatchMessage, toggleChatLoading } = get();
|
|
108
|
+
|
|
109
|
+
const abortController = toggleChatLoading(
|
|
110
|
+
true,
|
|
111
|
+
assistantId,
|
|
112
|
+
t('generateMessage(start)', { assistantId, messages }) as string,
|
|
107
113
|
);
|
|
114
|
+
|
|
108
115
|
const config = agentSelectors.currentAgentConfig(get());
|
|
109
116
|
|
|
110
117
|
const compiler = template(config.inputTemplate, { interpolate: /{{([\S\s]+?)}}/g });
|
|
@@ -138,12 +145,15 @@ export const chatMessage: StateCreator<
|
|
|
138
145
|
}
|
|
139
146
|
|
|
140
147
|
const fetcher = () =>
|
|
141
|
-
fetchChatModel(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
fetchChatModel(
|
|
149
|
+
{
|
|
150
|
+
messages: postMessages,
|
|
151
|
+
model: config.model,
|
|
152
|
+
...config.params,
|
|
153
|
+
plugins: config.plugins,
|
|
154
|
+
},
|
|
155
|
+
{ signal: abortController?.signal },
|
|
156
|
+
);
|
|
147
157
|
|
|
148
158
|
let output = '';
|
|
149
159
|
let isFunctionCall = false;
|
|
@@ -175,7 +185,7 @@ export const chatMessage: StateCreator<
|
|
|
175
185
|
},
|
|
176
186
|
});
|
|
177
187
|
|
|
178
|
-
|
|
188
|
+
toggleChatLoading(false, undefined, t('generateMessage(end)') as string);
|
|
179
189
|
|
|
180
190
|
return { isFunctionCall };
|
|
181
191
|
},
|
|
@@ -281,8 +291,27 @@ export const chatMessage: StateCreator<
|
|
|
281
291
|
}
|
|
282
292
|
},
|
|
283
293
|
|
|
294
|
+
stopGenerateMessage: () => {
|
|
295
|
+
const { abortController, toggleChatLoading } = get();
|
|
296
|
+
if (!abortController) return;
|
|
297
|
+
|
|
298
|
+
abortController.abort();
|
|
299
|
+
|
|
300
|
+
toggleChatLoading(false);
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
toggleChatLoading: (loading, id, action) => {
|
|
304
|
+
if (loading) {
|
|
305
|
+
const abortController = new AbortController();
|
|
306
|
+
set({ abortController, chatLoadingId: id }, false, action);
|
|
307
|
+
return abortController;
|
|
308
|
+
} else {
|
|
309
|
+
set({ abortController: undefined, chatLoadingId: undefined }, false, action);
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
|
|
284
313
|
triggerFunctionCall: async (id) => {
|
|
285
|
-
const { dispatchMessage, realFetchAIResponse } = get();
|
|
314
|
+
const { dispatchMessage, realFetchAIResponse, toggleChatLoading } = get();
|
|
286
315
|
const session = sessionSelectors.currentSession(get());
|
|
287
316
|
|
|
288
317
|
if (!session) return;
|
|
@@ -317,7 +346,9 @@ export const chatMessage: StateCreator<
|
|
|
317
346
|
// type: 'addMessage',
|
|
318
347
|
// });
|
|
319
348
|
|
|
320
|
-
const
|
|
349
|
+
const abortController = toggleChatLoading(true, id);
|
|
350
|
+
const data = await fetchPlugin(payload, { signal: abortController?.signal });
|
|
351
|
+
toggleChatLoading(false);
|
|
321
352
|
|
|
322
353
|
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: data });
|
|
323
354
|
|
|
@@ -325,7 +356,6 @@ export const chatMessage: StateCreator<
|
|
|
325
356
|
|
|
326
357
|
await realFetchAIResponse(chats, message.id);
|
|
327
358
|
},
|
|
328
|
-
|
|
329
359
|
// genShareUrl: () => {
|
|
330
360
|
// const session = sessionSelectors.currentSession(get());
|
|
331
361
|
// if (!session) return '';
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t } from 'i18next';
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_INBOX_AVATAR, DEFAULT_USER_AVATAR } from '@/const/meta';
|
|
4
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
|
2
5
|
import { useGlobalStore } from '@/store/global';
|
|
3
6
|
import { ChatMessage } from '@/types/chatMessage';
|
|
4
7
|
|
|
@@ -6,6 +9,7 @@ import type { SessionStore } from '../../../store';
|
|
|
6
9
|
import { agentSelectors } from '../../agentConfig';
|
|
7
10
|
import { sessionSelectors } from '../../session/selectors';
|
|
8
11
|
import { getSlicedMessagesWithConfig } from '../utils';
|
|
12
|
+
import { currentTopics } from './topic';
|
|
9
13
|
import { organizeChats } from './utils';
|
|
10
14
|
|
|
11
15
|
export const getChatsById =
|
|
@@ -36,6 +40,37 @@ export const currentChats = (s: SessionStore): ChatMessage[] => {
|
|
|
36
40
|
return getChatsById(s.activeId)(s);
|
|
37
41
|
};
|
|
38
42
|
|
|
43
|
+
// 针对新助手添加初始化时的自定义消息
|
|
44
|
+
export const currentChatsWithGuideMessage = (s: SessionStore): ChatMessage[] => {
|
|
45
|
+
const data = currentChats(s);
|
|
46
|
+
const noTopic = currentTopics(s);
|
|
47
|
+
|
|
48
|
+
const isBrandNewChat = data.length === 0 && noTopic;
|
|
49
|
+
|
|
50
|
+
if (!isBrandNewChat) return data;
|
|
51
|
+
|
|
52
|
+
const [activeId, isInbox] = [s.activeId, s.activeId === INBOX_SESSION_ID];
|
|
53
|
+
const systemRole = agentSelectors.currentAgentSystemRole(s);
|
|
54
|
+
|
|
55
|
+
const emptyInboxGuideMessage = {
|
|
56
|
+
content: isInbox
|
|
57
|
+
? t('inbox.defaultMessage')
|
|
58
|
+
: !!systemRole
|
|
59
|
+
? t('agentDefaultMessageWithSystemRole', { systemRole })
|
|
60
|
+
: t('agentDefaultMessage', { id: activeId }),
|
|
61
|
+
createAt: Date.now(),
|
|
62
|
+
extra: {},
|
|
63
|
+
id: 'default',
|
|
64
|
+
meta: {
|
|
65
|
+
avatar: DEFAULT_INBOX_AVATAR,
|
|
66
|
+
},
|
|
67
|
+
role: 'assistant',
|
|
68
|
+
updateAt: Date.now(),
|
|
69
|
+
} as ChatMessage;
|
|
70
|
+
|
|
71
|
+
return [emptyInboxGuideMessage];
|
|
72
|
+
};
|
|
73
|
+
|
|
39
74
|
export const currentChatsWithHistoryConfig = (s: SessionStore): ChatMessage[] => {
|
|
40
75
|
const chats = currentChats(s);
|
|
41
76
|
const config = agentSelectors.currentAgentConfig(s);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { currentChats, getChatsById } from './chat';
|
|
1
|
+
import { currentChats, currentChatsWithGuideMessage, getChatsById } from './chat';
|
|
2
2
|
import { chatsTokenCount, systemRoleTokenCount, totalTokenCount } from './token';
|
|
3
3
|
import { currentTopics, getTopicMessages } from './topic';
|
|
4
4
|
|
|
5
5
|
export const chatSelectors = {
|
|
6
6
|
chatsTokenCount,
|
|
7
7
|
currentChats,
|
|
8
|
+
currentChatsWithGuideMessage,
|
|
8
9
|
getChatsById,
|
|
9
10
|
systemRoleTokenCount,
|
|
10
11
|
totalTokenCount,
|