@lobehub/chat 0.19.0 → 0.21.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/CHANGELOG.md +58 -0
- package/package.json +1 -1
- package/src/{features → components}/SliderWithInput/index.tsx +1 -0
- package/src/locales/default/common.ts +2 -2
- package/src/locales/default/setting.ts +1 -0
- package/src/pages/chat/[id]/Config/SideBar.tsx +9 -5
- package/src/pages/chat/[id]/Config/Topic.tsx +27 -0
- package/src/pages/chat/[id]/Config/TopicItem.tsx +80 -0
- package/src/pages/chat/[id]/Conversation/Input/Action.tsx +66 -7
- package/src/pages/chat/[id]/Conversation/Input/index.tsx +5 -2
- package/src/pages/chat/[id]/Header.tsx +14 -10
- package/src/pages/chat/[id]/edit/AgentConfig/index.tsx +1 -1
- package/src/pages/chat/layout.tsx +10 -4
- package/src/pages/setting/SettingForm.tsx +1 -1
- package/src/prompts/chat.ts +19 -0
- package/src/store/session/selectors.ts +1 -1
- package/src/store/session/slices/agentConfig/selectors.ts +4 -0
- package/src/store/session/slices/chat/actions/index.ts +21 -0
- package/src/store/session/slices/chat/{action.ts → actions/message.ts} +33 -23
- package/src/store/session/slices/chat/actions/topic.ts +98 -0
- package/src/store/session/slices/chat/index.ts +1 -1
- package/src/store/session/slices/chat/initialState.ts +2 -5
- package/src/store/session/slices/chat/reducers/message.test.ts +92 -0
- package/src/store/session/slices/chat/reducers/message.ts +16 -1
- package/src/store/session/slices/chat/reducers/topic.test.ts +179 -0
- package/src/store/session/slices/chat/reducers/topic.ts +59 -0
- package/src/store/session/slices/chat/selectors/chat.ts +8 -4
- package/src/store/session/slices/chat/selectors/index.ts +5 -0
- package/src/store/session/slices/chat/selectors/topic.ts +16 -0
- package/src/store/session/slices/chat/selectors/utils.test.ts +81 -1
- package/src/store/session/slices/chat/selectors/utils.ts +8 -2
- package/src/store/session/slices/session/reducers/session.ts +25 -1
- package/src/types/topic.ts +0 -1
- package/src/pages/chat/[id]/Config/ConfigCell.tsx +0 -68
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.21.0](https://github.com/lobehub/lobe-chat/compare/v0.20.0...v0.21.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-07-25**</sup>
|
|
8
|
+
|
|
9
|
+
#### ♻ Code Refactoring
|
|
10
|
+
|
|
11
|
+
- **misc**: Move component folder.
|
|
12
|
+
|
|
13
|
+
#### ✨ Features
|
|
14
|
+
|
|
15
|
+
- **misc**: 支持快捷配置模型、温度.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### Code refactoring
|
|
23
|
+
|
|
24
|
+
- **misc**: Move component folder ([fb85d16](https://github.com/lobehub/lobe-chat/commit/fb85d16))
|
|
25
|
+
|
|
26
|
+
#### What's improved
|
|
27
|
+
|
|
28
|
+
- **misc**: 支持快捷配置模型、温度, closes [#19](https://github.com/lobehub/lobe-chat/issues/19) ([31daee1](https://github.com/lobehub/lobe-chat/commit/31daee1))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
## [Version 0.20.0](https://github.com/lobehub/lobe-chat/compare/v0.19.0...v0.20.0)
|
|
39
|
+
|
|
40
|
+
<sup>Released on **2023-07-25**</sup>
|
|
41
|
+
|
|
42
|
+
#### ✨ Features
|
|
43
|
+
|
|
44
|
+
- **misc**: 实现话题模块.
|
|
45
|
+
|
|
46
|
+
<br/>
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
50
|
+
|
|
51
|
+
#### What's improved
|
|
52
|
+
|
|
53
|
+
- **misc**: 实现话题模块, closes [#16](https://github.com/lobehub/lobe-chat/issues/16) ([64fd6ee](https://github.com/lobehub/lobe-chat/commit/64fd6ee))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
## [Version 0.19.0](https://github.com/lobehub/lobe-chat/compare/v0.18.2...v0.19.0)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2023-07-24**</sup>
|
package/package.json
CHANGED
|
@@ -27,8 +27,8 @@ export default {
|
|
|
27
27
|
share: '分享',
|
|
28
28
|
tokenDetail: '系统设定: {{systemRoleToken}} 历史消息: {{chatsToken}}',
|
|
29
29
|
topic: {
|
|
30
|
-
saveCurrentMessages: '
|
|
31
|
-
searchPlaceholder: '
|
|
30
|
+
saveCurrentMessages: '将当前会话保存为话题',
|
|
31
|
+
searchPlaceholder: '搜索话题...',
|
|
32
32
|
},
|
|
33
33
|
updateAgent: '更新助理信息',
|
|
34
34
|
updatePrompt: '更新提示词',
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ActionIcon, DraggablePanelBody, EditableMessage, SearchBar } from '@lobehub/ui';
|
|
2
2
|
import { createStyles } from 'antd-style';
|
|
3
|
-
import {
|
|
3
|
+
import { Maximize2Icon } from 'lucide-react';
|
|
4
4
|
import { memo, useState } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
import { shallow } from 'zustand/shallow';
|
|
8
8
|
|
|
9
|
+
import { Topic } from '@/pages/chat/[id]/Config/Topic';
|
|
9
10
|
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
10
11
|
|
|
11
12
|
import Header from './Header';
|
|
@@ -42,8 +43,10 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
42
43
|
const SideBar = memo(() => {
|
|
43
44
|
const [openModal, setOpenModal] = useState(false);
|
|
44
45
|
const { styles } = useStyles();
|
|
45
|
-
const [updateAgentConfig] = useSessionStore(
|
|
46
|
-
|
|
46
|
+
const [systemRole, updateAgentConfig] = useSessionStore(
|
|
47
|
+
(s) => [agentSelectors.currentAgentSystemRole(s), s.updateAgentConfig],
|
|
48
|
+
shallow,
|
|
49
|
+
);
|
|
47
50
|
|
|
48
51
|
const { t } = useTranslation('common');
|
|
49
52
|
return (
|
|
@@ -51,7 +54,7 @@ const SideBar = memo(() => {
|
|
|
51
54
|
<Header
|
|
52
55
|
actions={
|
|
53
56
|
<ActionIcon
|
|
54
|
-
icon={
|
|
57
|
+
icon={Maximize2Icon}
|
|
55
58
|
onClick={() => setOpenModal(true)}
|
|
56
59
|
size="small"
|
|
57
60
|
title={t('edit')}
|
|
@@ -76,8 +79,9 @@ const SideBar = memo(() => {
|
|
|
76
79
|
}}
|
|
77
80
|
value={systemRole}
|
|
78
81
|
/>
|
|
79
|
-
<Flexbox style={{ padding: 16 }}>
|
|
82
|
+
<Flexbox gap={12} style={{ padding: 16 }}>
|
|
80
83
|
<SearchBar placeholder={t('topic.searchPlaceholder')} type={'block'} />
|
|
84
|
+
<Topic />
|
|
81
85
|
</Flexbox>
|
|
82
86
|
</DraggablePanelBody>
|
|
83
87
|
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Flexbox } from 'react-layout-kit';
|
|
2
|
+
import { shallow } from 'zustand/shallow';
|
|
3
|
+
|
|
4
|
+
import { topicSelectors, useSessionStore } from '@/store/session';
|
|
5
|
+
|
|
6
|
+
import TopicItem from './TopicItem';
|
|
7
|
+
|
|
8
|
+
export const Topic = () => {
|
|
9
|
+
const topics = useSessionStore(topicSelectors.currentTopics);
|
|
10
|
+
const [activeTopicId] = useSessionStore((s) => [s.activeTopicId], shallow);
|
|
11
|
+
return (
|
|
12
|
+
<Flexbox gap={8}>
|
|
13
|
+
<TopicItem active={!activeTopicId} fav={false} title={'默认话题'} />
|
|
14
|
+
|
|
15
|
+
{topics.map(({ id, favorite, title }) => (
|
|
16
|
+
<TopicItem
|
|
17
|
+
active={activeTopicId === id}
|
|
18
|
+
fav={favorite}
|
|
19
|
+
id={id}
|
|
20
|
+
key={id}
|
|
21
|
+
showFav
|
|
22
|
+
title={title}
|
|
23
|
+
/>
|
|
24
|
+
))}
|
|
25
|
+
</Flexbox>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { StarFilled, StarOutlined } from '@ant-design/icons';
|
|
2
|
+
import { ActionIcon } from '@lobehub/ui';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import { LucideIcon } from 'lucide-react';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
import { shallow } from 'zustand/shallow';
|
|
8
|
+
|
|
9
|
+
import { useSessionStore } from '@/store/session';
|
|
10
|
+
|
|
11
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
12
|
+
active: css`
|
|
13
|
+
background: ${token.colorFill};
|
|
14
|
+
|
|
15
|
+
&:hover {
|
|
16
|
+
background: ${token.colorFill};
|
|
17
|
+
}
|
|
18
|
+
`,
|
|
19
|
+
container: css`
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
background: ${token.colorFillTertiary};
|
|
22
|
+
border-radius: 6px;
|
|
23
|
+
|
|
24
|
+
&:hover {
|
|
25
|
+
background: ${token.colorFillSecondary};
|
|
26
|
+
}
|
|
27
|
+
`,
|
|
28
|
+
split: css`
|
|
29
|
+
border-bottom: 1px solid ${token.colorSplit};
|
|
30
|
+
`,
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
export interface ConfigCellProps {
|
|
34
|
+
active?: boolean;
|
|
35
|
+
fav?: boolean;
|
|
36
|
+
id?: string;
|
|
37
|
+
showFav?: boolean;
|
|
38
|
+
title: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const TopicItem = memo<ConfigCellProps>(({ title, active, id, showFav, fav }) => {
|
|
42
|
+
const { styles, theme, cx } = useStyles();
|
|
43
|
+
|
|
44
|
+
const [dispatchTopic, toggleTopic] = useSessionStore(
|
|
45
|
+
(s) => [s.dispatchTopic, s.toggleTopic],
|
|
46
|
+
shallow,
|
|
47
|
+
);
|
|
48
|
+
const starIcon = (fav ? StarFilled : StarOutlined) as LucideIcon;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Flexbox
|
|
52
|
+
align={'center'}
|
|
53
|
+
className={cx(styles.container, active && styles.active)}
|
|
54
|
+
distribution={'space-between'}
|
|
55
|
+
horizontal
|
|
56
|
+
onClick={() => {
|
|
57
|
+
toggleTopic(id);
|
|
58
|
+
}}
|
|
59
|
+
padding={'10px 12px'}
|
|
60
|
+
>
|
|
61
|
+
{title}
|
|
62
|
+
{!showFav ? undefined : (
|
|
63
|
+
<ActionIcon
|
|
64
|
+
icon={starIcon}
|
|
65
|
+
onClick={() => {
|
|
66
|
+
if (!id) return;
|
|
67
|
+
|
|
68
|
+
dispatchTopic({ id, key: 'favorite', type: 'updateChatTopic', value: !fav });
|
|
69
|
+
}}
|
|
70
|
+
size={'small'}
|
|
71
|
+
style={{
|
|
72
|
+
color: fav ? theme.yellow : undefined,
|
|
73
|
+
}}
|
|
74
|
+
/>
|
|
75
|
+
)}
|
|
76
|
+
</Flexbox>
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export default TopicItem;
|
|
@@ -1,21 +1,80 @@
|
|
|
1
1
|
import { ActionIcon } from '@lobehub/ui';
|
|
2
|
-
import { Popconfirm } from 'antd';
|
|
3
|
-
import { BrainCog, Eraser, Thermometer
|
|
2
|
+
import { Dropdown, Popconfirm, Popover } from 'antd';
|
|
3
|
+
import { BrainCog, Eraser, Thermometer } from 'lucide-react';
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { shallow } from 'zustand/shallow';
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import SliderWithInput from '@/components/SliderWithInput';
|
|
9
|
+
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
10
|
+
import { LanguageModel } from '@/types/llm';
|
|
9
11
|
|
|
10
12
|
const InputActions = memo(() => {
|
|
11
13
|
const { t } = useTranslation('setting');
|
|
12
|
-
const [clearMessage] = useSessionStore(
|
|
14
|
+
const [clearMessage, updateAgentConfig] = useSessionStore(
|
|
15
|
+
(s) => [s.clearMessage, s.updateAgentConfig],
|
|
16
|
+
shallow,
|
|
17
|
+
);
|
|
18
|
+
const [model, temperature] = useSessionStore((s) => {
|
|
19
|
+
const config = agentSelectors.currentAgentConfigSafe(s);
|
|
20
|
+
return [
|
|
21
|
+
config.model,
|
|
22
|
+
config.params.temperature,
|
|
23
|
+
// config.historyCount
|
|
24
|
+
];
|
|
25
|
+
}, shallow);
|
|
13
26
|
|
|
14
27
|
return (
|
|
15
28
|
<>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
<Dropdown
|
|
30
|
+
menu={{
|
|
31
|
+
activeKey: model,
|
|
32
|
+
items: Object.values(LanguageModel).map((i) => ({ key: i, label: i })),
|
|
33
|
+
onClick: (e) => {
|
|
34
|
+
updateAgentConfig({ model: e.key as LanguageModel });
|
|
35
|
+
},
|
|
36
|
+
}}
|
|
37
|
+
trigger={['click']}
|
|
38
|
+
>
|
|
39
|
+
<ActionIcon icon={BrainCog} title={t('settingModel.model.title')} />
|
|
40
|
+
</Dropdown>
|
|
41
|
+
<Popover
|
|
42
|
+
content={
|
|
43
|
+
<SliderWithInput
|
|
44
|
+
max={1}
|
|
45
|
+
min={0}
|
|
46
|
+
onChange={(v) => {
|
|
47
|
+
updateAgentConfig({ params: { temperature: v } });
|
|
48
|
+
}}
|
|
49
|
+
step={0.1}
|
|
50
|
+
style={{ width: 120 }}
|
|
51
|
+
value={temperature}
|
|
52
|
+
/>
|
|
53
|
+
}
|
|
54
|
+
trigger={'click'}
|
|
55
|
+
>
|
|
56
|
+
<ActionIcon
|
|
57
|
+
icon={Thermometer}
|
|
58
|
+
title={t('settingModel.temperature.titleWithValue', { value: temperature })}
|
|
59
|
+
/>
|
|
60
|
+
</Popover>
|
|
61
|
+
{/*TODO 历史记录功能实现 */}
|
|
62
|
+
{/*<Popover*/}
|
|
63
|
+
{/* content={*/}
|
|
64
|
+
{/* <SliderWithInput*/}
|
|
65
|
+
{/* min={0}*/}
|
|
66
|
+
{/* onChange={(v) => {*/}
|
|
67
|
+
{/* updateAgentConfig({ historyCount: v });*/}
|
|
68
|
+
{/* }}*/}
|
|
69
|
+
{/* step={1}*/}
|
|
70
|
+
{/* style={{ width: 120 }}*/}
|
|
71
|
+
{/* value={historyCount}*/}
|
|
72
|
+
{/* />*/}
|
|
73
|
+
{/* }*/}
|
|
74
|
+
{/* trigger={'click'}*/}
|
|
75
|
+
{/*>*/}
|
|
76
|
+
{/* <ActionIcon icon={Timer} title={t('settingChat.historyCount.title')} />*/}
|
|
77
|
+
{/*</Popover>*/}
|
|
19
78
|
<Popconfirm
|
|
20
79
|
cancelText={t('cancel', { ns: 'common' })}
|
|
21
80
|
okButtonProps={{ danger: true }}
|
|
@@ -18,12 +18,15 @@ const ChatInput = () => {
|
|
|
18
18
|
const [text, setText] = useState('');
|
|
19
19
|
|
|
20
20
|
const [inputHeight] = useSettings((s) => [s.inputHeight], shallow);
|
|
21
|
-
const [sendMessage] = useSessionStore(
|
|
21
|
+
const [sendMessage, saveToTopic] = useSessionStore(
|
|
22
|
+
(s) => [s.createOrSendMsg, s.saveToTopic],
|
|
23
|
+
shallow,
|
|
24
|
+
);
|
|
22
25
|
|
|
23
26
|
const footer = useMemo(
|
|
24
27
|
() => (
|
|
25
28
|
<Tooltip title={t('topic.saveCurrentMessages')}>
|
|
26
|
-
<Button icon={<Icon icon={LucideGalleryVerticalEnd} />} />
|
|
29
|
+
<Button icon={<Icon icon={LucideGalleryVerticalEnd} />} onClick={saveToTopic} />
|
|
27
30
|
</Tooltip>
|
|
28
31
|
),
|
|
29
32
|
[],
|
|
@@ -12,10 +12,18 @@ import { agentSelectors, useSessionStore } from '@/store/session';
|
|
|
12
12
|
const Header = memo(() => {
|
|
13
13
|
const { t } = useTranslation('common');
|
|
14
14
|
|
|
15
|
-
const [
|
|
16
|
-
(s) => [
|
|
15
|
+
const [title, description, avatar, backgroundColor, id, model] = useSessionStore(
|
|
16
|
+
(s) => [
|
|
17
|
+
agentSelectors.currentAgentTitle(s),
|
|
18
|
+
agentSelectors.currentAgentDescription(s),
|
|
19
|
+
agentSelectors.currentAgentAvatar(s),
|
|
20
|
+
agentSelectors.currentAgentBackgroundColor(s),
|
|
21
|
+
s.activeId,
|
|
22
|
+
agentSelectors.currentAgentModel(s),
|
|
23
|
+
],
|
|
17
24
|
shallow,
|
|
18
25
|
);
|
|
26
|
+
|
|
19
27
|
const [showAgentSettings, toggleConfig] = useSessionStore(
|
|
20
28
|
(s) => [s.showAgentSettings, s.toggleConfig],
|
|
21
29
|
shallow,
|
|
@@ -26,20 +34,16 @@ const Header = memo(() => {
|
|
|
26
34
|
left={
|
|
27
35
|
<>
|
|
28
36
|
<Avatar
|
|
29
|
-
avatar={
|
|
30
|
-
background={
|
|
37
|
+
avatar={avatar}
|
|
38
|
+
background={backgroundColor}
|
|
31
39
|
onClick={() => {
|
|
32
40
|
Router.push(`/chat/${id}/edit`);
|
|
33
41
|
}}
|
|
34
42
|
size={40}
|
|
35
43
|
style={{ cursor: 'pointer' }}
|
|
36
|
-
title={
|
|
37
|
-
/>
|
|
38
|
-
<HeaderTitle
|
|
39
|
-
desc={meta?.description || t('noDescription')}
|
|
40
|
-
tag={<Tag>{modle}</Tag>}
|
|
41
|
-
title={meta?.title || t('defaultAgent')}
|
|
44
|
+
title={title}
|
|
42
45
|
/>
|
|
46
|
+
<HeaderTitle desc={description} tag={<Tag>{model}</Tag>} title={title} />
|
|
43
47
|
</>
|
|
44
48
|
}
|
|
45
49
|
right={
|
|
@@ -6,10 +6,10 @@ import { debounce } from 'lodash-es';
|
|
|
6
6
|
import { BrainCog, MessagesSquare } from 'lucide-react';
|
|
7
7
|
import { useMemo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import SliderWithInput from 'src/components/SliderWithInput';
|
|
9
10
|
import { shallow } from 'zustand/shallow';
|
|
10
11
|
|
|
11
12
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
12
|
-
import SliderWithInput from '@/features/SliderWithInput';
|
|
13
13
|
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
14
14
|
import { LanguageModel } from '@/types/llm';
|
|
15
15
|
import type { LobeAgentConfig } from '@/types/session';
|
|
@@ -17,8 +17,8 @@ const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
|
|
17
17
|
initI18n.finally();
|
|
18
18
|
}, []);
|
|
19
19
|
|
|
20
|
-
const [activeSession] = useSessionStore((s) => {
|
|
21
|
-
return [s.activeSession];
|
|
20
|
+
const [activeSession, toggleTopic] = useSessionStore((s) => {
|
|
21
|
+
return [s.activeSession, s.toggleTopic];
|
|
22
22
|
}, shallow);
|
|
23
23
|
|
|
24
24
|
const router = useRouter();
|
|
@@ -26,10 +26,16 @@ const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
|
|
26
26
|
|
|
27
27
|
useEffect(() => {
|
|
28
28
|
const hasRehydrated = useSessionStore.persist.hasHydrated();
|
|
29
|
-
//
|
|
30
|
-
if (
|
|
29
|
+
// 只有当水合完毕后再开始做操作
|
|
30
|
+
if (!hasRehydrated) return;
|
|
31
|
+
|
|
32
|
+
// 1. 正常激活会话
|
|
33
|
+
if (typeof id === 'string') {
|
|
31
34
|
activeSession(id);
|
|
32
35
|
}
|
|
36
|
+
|
|
37
|
+
// 将话题重置为默认值
|
|
38
|
+
toggleTopic();
|
|
33
39
|
}, [id]);
|
|
34
40
|
|
|
35
41
|
useEffect(() => {
|
|
@@ -5,11 +5,11 @@ import { debounce } from 'lodash-es';
|
|
|
5
5
|
import { AppWindow, BrainCog, MessagesSquare, Palette, Webhook } from 'lucide-react';
|
|
6
6
|
import { memo, useCallback, useMemo } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import SliderWithInput from 'src/components/SliderWithInput';
|
|
8
9
|
import { shallow } from 'zustand/shallow';
|
|
9
10
|
|
|
10
11
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
11
12
|
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
12
|
-
import SliderWithInput from '@/features/SliderWithInput';
|
|
13
13
|
import { options } from '@/locales/options';
|
|
14
14
|
import { settingsSelectors, useSettings } from '@/store/settings';
|
|
15
15
|
import { DEFAULT_SETTINGS } from '@/store/settings/initialState';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OpenAIChatMessage, OpenAIStreamPayload } from '@/types/openai';
|
|
2
|
+
|
|
3
|
+
export const promptSummaryTitle = (
|
|
4
|
+
messages: OpenAIChatMessage[],
|
|
5
|
+
): Partial<OpenAIStreamPayload> => ({
|
|
6
|
+
messages: [
|
|
7
|
+
{
|
|
8
|
+
content:
|
|
9
|
+
'你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不需要包含标点符号',
|
|
10
|
+
role: 'system',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
|
14
|
+
|
|
15
|
+
请总结上述对话为10个字以内的标题,不需要包含标点符号`,
|
|
16
|
+
role: 'user',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
});
|
|
@@ -17,6 +17,9 @@ const currentAgentMeta = (s: SessionStore): MetaData => {
|
|
|
17
17
|
|
|
18
18
|
const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s)?.title || t('defaultSession');
|
|
19
19
|
|
|
20
|
+
const currentAgentDescription = (s: SessionStore) =>
|
|
21
|
+
currentAgentMeta(s)?.description || t('noDescription');
|
|
22
|
+
|
|
20
23
|
const currentAgentBackgroundColor = (s: SessionStore) => {
|
|
21
24
|
const session = sessionSelectors.currentSession(s);
|
|
22
25
|
if (!session) return DEFAULT_BACKGROUND_COLOR;
|
|
@@ -62,6 +65,7 @@ export const agentSelectors = {
|
|
|
62
65
|
currentAgentBackgroundColor,
|
|
63
66
|
currentAgentConfig,
|
|
64
67
|
currentAgentConfigSafe,
|
|
68
|
+
currentAgentDescription,
|
|
65
69
|
currentAgentMeta,
|
|
66
70
|
currentAgentModel,
|
|
67
71
|
currentAgentSystemRole,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
|
2
|
+
|
|
3
|
+
import { SessionStore } from '@/store/session';
|
|
4
|
+
|
|
5
|
+
import { ChatMessageAction, chatMessage } from './message';
|
|
6
|
+
import { ChatTopicAction, chatTopic } from './topic';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 聊天操作
|
|
10
|
+
*/
|
|
11
|
+
export interface ChatAction extends ChatTopicAction, ChatMessageAction {}
|
|
12
|
+
|
|
13
|
+
export const createChatSlice: StateCreator<
|
|
14
|
+
SessionStore,
|
|
15
|
+
[['zustand/devtools', never]],
|
|
16
|
+
[],
|
|
17
|
+
ChatAction
|
|
18
|
+
> = (...params) => ({
|
|
19
|
+
...chatTopic(...params),
|
|
20
|
+
...chatMessage(...params),
|
|
21
|
+
});
|
|
@@ -7,14 +7,14 @@ import { fetchSSE } from '@/utils/fetch';
|
|
|
7
7
|
import { isFunctionMessage } from '@/utils/message';
|
|
8
8
|
import { nanoid } from '@/utils/uuid';
|
|
9
9
|
|
|
10
|
-
import { MessageDispatch, messagesReducer } from '
|
|
10
|
+
import { MessageDispatch, messagesReducer } from '../reducers/message';
|
|
11
11
|
|
|
12
12
|
const LOADING_FLAT = '...';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* 聊天操作
|
|
16
16
|
*/
|
|
17
|
-
export interface
|
|
17
|
+
export interface ChatMessageAction {
|
|
18
18
|
/**
|
|
19
19
|
* 清除消息
|
|
20
20
|
*/
|
|
@@ -57,6 +57,7 @@ export interface ChatAction {
|
|
|
57
57
|
* @param id - 消息 ID
|
|
58
58
|
*/
|
|
59
59
|
resendMessage: (id: string) => Promise<void>;
|
|
60
|
+
|
|
60
61
|
/**
|
|
61
62
|
* 发送消息
|
|
62
63
|
* @param text - 消息文本
|
|
@@ -64,14 +65,20 @@ export interface ChatAction {
|
|
|
64
65
|
sendMessage: (text: string) => Promise<void>;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
export const
|
|
68
|
+
export const chatMessage: StateCreator<
|
|
68
69
|
SessionStore,
|
|
69
70
|
[['zustand/devtools', never]],
|
|
70
71
|
[],
|
|
71
|
-
|
|
72
|
+
ChatMessageAction
|
|
72
73
|
> = (set, get) => ({
|
|
73
74
|
clearMessage: () => {
|
|
74
|
-
|
|
75
|
+
const { dispatchMessage, activeTopicId, dispatchTopic } = get();
|
|
76
|
+
|
|
77
|
+
dispatchMessage({ topicId: activeTopicId, type: 'resetMessages' });
|
|
78
|
+
|
|
79
|
+
if (activeTopicId) {
|
|
80
|
+
dispatchTopic({ id: activeTopicId, type: 'deleteChatTopic' });
|
|
81
|
+
}
|
|
75
82
|
},
|
|
76
83
|
|
|
77
84
|
createOrSendMsg: async (message) => {
|
|
@@ -155,7 +162,7 @@ export const createChatSlice: StateCreator<
|
|
|
155
162
|
},
|
|
156
163
|
|
|
157
164
|
realFetchAIResponse: async (messages, userMessageId) => {
|
|
158
|
-
const { dispatchMessage, generateMessage } = get();
|
|
165
|
+
const { dispatchMessage, generateMessage, activeTopicId } = get();
|
|
159
166
|
|
|
160
167
|
// 添加 systemRole
|
|
161
168
|
const { systemRole, model } = agentSelectors.currentAgentConfigSafe(get());
|
|
@@ -165,49 +172,46 @@ export const createChatSlice: StateCreator<
|
|
|
165
172
|
|
|
166
173
|
// 再添加一个空的信息用于放置 ai 响应,注意顺序不能反
|
|
167
174
|
// 因为如果顺序反了,messages 中将包含新增的 ai message
|
|
168
|
-
const
|
|
175
|
+
const mid = nanoid();
|
|
169
176
|
|
|
170
177
|
dispatchMessage({
|
|
171
|
-
id:
|
|
178
|
+
id: mid,
|
|
172
179
|
message: LOADING_FLAT,
|
|
173
180
|
parentId: userMessageId,
|
|
174
181
|
role: 'assistant',
|
|
175
182
|
type: 'addMessage',
|
|
176
183
|
});
|
|
177
184
|
|
|
185
|
+
// 如果有 activeTopicId,则添加 topicId
|
|
186
|
+
if (activeTopicId) {
|
|
187
|
+
dispatchMessage({ id: mid, key: 'topicId', type: 'updateMessage', value: activeTopicId });
|
|
188
|
+
}
|
|
189
|
+
|
|
178
190
|
// 为模型添加 fromModel 的额外信息
|
|
179
|
-
dispatchMessage({
|
|
180
|
-
id: assistantId,
|
|
181
|
-
key: 'fromModel',
|
|
182
|
-
type: 'updateMessageExtra',
|
|
183
|
-
value: model,
|
|
184
|
-
});
|
|
191
|
+
dispatchMessage({ id: mid, key: 'fromModel', type: 'updateMessageExtra', value: model });
|
|
185
192
|
|
|
186
193
|
// 生成 ai message
|
|
187
|
-
const { output, isFunctionCall } = await generateMessage(messages,
|
|
194
|
+
const { output, isFunctionCall } = await generateMessage(messages, mid);
|
|
188
195
|
|
|
189
196
|
// 如果是 function,则发送函数调用方法
|
|
190
197
|
if (isFunctionCall) {
|
|
191
198
|
const { function_call } = JSON.parse(output);
|
|
192
199
|
|
|
193
200
|
dispatchMessage({
|
|
194
|
-
id:
|
|
201
|
+
id: mid,
|
|
195
202
|
key: 'function_call',
|
|
196
203
|
type: 'updateMessage',
|
|
197
204
|
value: function_call,
|
|
198
205
|
});
|
|
199
206
|
|
|
200
207
|
await generateMessage(
|
|
201
|
-
[
|
|
202
|
-
|
|
203
|
-
{ content: '', function_call, id: assistantId, role: 'assistant' } as ChatMessage,
|
|
204
|
-
],
|
|
205
|
-
assistantId,
|
|
208
|
+
[...messages, { content: '', function_call, id: mid, role: 'assistant' } as ChatMessage],
|
|
209
|
+
mid,
|
|
206
210
|
true,
|
|
207
211
|
);
|
|
208
212
|
|
|
209
213
|
dispatchMessage({
|
|
210
|
-
id:
|
|
214
|
+
id: mid,
|
|
211
215
|
key: 'role',
|
|
212
216
|
type: 'updateMessage',
|
|
213
217
|
value: 'assistant',
|
|
@@ -243,13 +247,19 @@ export const createChatSlice: StateCreator<
|
|
|
243
247
|
},
|
|
244
248
|
|
|
245
249
|
sendMessage: async (message) => {
|
|
246
|
-
const { dispatchMessage, realFetchAIResponse, autocompleteSessionAgentMeta } =
|
|
250
|
+
const { dispatchMessage, realFetchAIResponse, autocompleteSessionAgentMeta, activeTopicId } =
|
|
251
|
+
get();
|
|
247
252
|
const session = sessionSelectors.currentSession(get());
|
|
248
253
|
if (!session || !message) return;
|
|
249
254
|
|
|
250
255
|
const userId = nanoid();
|
|
251
256
|
dispatchMessage({ id: userId, message, role: 'user', type: 'addMessage' });
|
|
252
257
|
|
|
258
|
+
// 如果有 activeTopicId,则添加 topicId
|
|
259
|
+
if (activeTopicId) {
|
|
260
|
+
dispatchMessage({ id: userId, key: 'topicId', type: 'updateMessage', value: activeTopicId });
|
|
261
|
+
}
|
|
262
|
+
|
|
253
263
|
// 先拿到当前的 messages
|
|
254
264
|
const messages = chatSelectors.currentChats(get());
|
|
255
265
|
|