@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 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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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.44.4",
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": "^5",
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
- padding: 12px;
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, useMemo } from 'react';
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
- useSessionHydrated,
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 = useSessionHydrated();
36
+ const init = useSessionChatInit();
39
37
  const { t } = useTranslation('common');
40
38
 
41
- const data = useSessionStore(chatSelectors.currentChats, isEqual);
39
+ const data = useSessionStore(chatSelectors.currentChatsWithGuideMessage, isEqual);
42
40
 
43
- const [isInbox, displayMode, chatLoadingId, deleteMessage, resendMessage, dispatchMessage] =
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={displayDataSource}
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 [sendMessage, hasTopic, saveToTopic] = useSessionStore((s) => [
25
- s.sendMessage,
26
- !!s.activeTopicId,
27
- s.saveToTopic,
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, useSessionHydrated, useSessionStore } from '@/store/session';
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 = useSessionHydrated();
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, useSessionHydrated, useSessionStore } from '@/store/session';
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 hydrated = useSessionHydrated();
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} padding={'0 16px 16px'}>
68
- {hydrated ? (
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, useSessionHydrated, useSessionStore } from '@/store/session';
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 hydrated = useSessionHydrated();
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 !hydrated ? (
25
+ return !init ? (
26
26
  <SkeletonList />
27
27
  ) : (
28
28
  <Flexbox gap={2} style={{ marginBottom: 12 }}>
@@ -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
  }
@@ -1,2 +1,3 @@
1
1
  export * from './useOnFinishHydrationSession';
2
+ export * from './useSessionChatInit';
2
3
  export * from './useSessionHydrated';
@@ -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
- set(
104
- { chatLoadingId: assistantId },
105
- false,
106
- t('generateMessage(start)', { assistantId, messages }),
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
- messages: postMessages,
143
- model: config.model,
144
- ...config.params,
145
- plugins: config.plugins,
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
- set({ chatLoadingId: undefined }, false, t('generateMessage(end)'));
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 data = await fetchPlugin(payload);
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,5 @@
1
1
  export interface ChatState {
2
+ abortController?: AbortController;
2
3
  activeTopicId?: string;
3
4
  chatLoadingId?: string;
4
5
  renameTopicId?: string;
@@ -1,4 +1,7 @@
1
- import { DEFAULT_USER_AVATAR } from '@/const/meta';
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,
package/vitest.config.ts CHANGED
@@ -6,6 +6,10 @@ export default defineConfig({
6
6
  alias: {
7
7
  '@': resolve(__dirname, './src'),
8
8
  },
9
+ coverage: {
10
+ provider: 'v8',
11
+ reporter: ['text', 'json', 'lcov', 'text-summary'],
12
+ },
9
13
  environment: 'jsdom',
10
14
  globals: true,
11
15
  },