@lobehub/chat 0.45.0 → 0.46.1

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 CHANGED
@@ -2,6 +2,65 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.46.1](https://github.com/lobehub/lobe-chat/compare/v0.46.0...v0.46.1)
6
+
7
+ <sup>Released on **2023-08-14**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Fix SystemRole Skeleton padding.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Fix SystemRole Skeleton padding ([ce485a0](https://github.com/lobehub/lobe-chat/commit/ce485a0))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ## [Version 0.46.0](https://github.com/lobehub/lobe-chat/compare/v0.45.0...v0.46.0)
31
+
32
+ <sup>Released on **2023-08-14**</sup>
33
+
34
+ #### ✨ Features
35
+
36
+ - **misc**: Update styling and functionality of AgentPrompt and EditableMessage components, 支持停止生成消息.
37
+
38
+ #### 🐛 Bug Fixes
39
+
40
+ - **misc**: Remove input highlight.
41
+
42
+ <br/>
43
+
44
+ <details>
45
+ <summary><kbd>Improvements and Fixes</kbd></summary>
46
+
47
+ #### What's improved
48
+
49
+ - **misc**: Update styling and functionality of AgentPrompt and EditableMessage components ([80b521c](https://github.com/lobehub/lobe-chat/commit/80b521c))
50
+ - **misc**: 支持停止生成消息, closes [#78](https://github.com/lobehub/lobe-chat/issues/78) ([9eeca80](https://github.com/lobehub/lobe-chat/commit/9eeca80))
51
+
52
+ #### What's fixed
53
+
54
+ - **misc**: Remove input highlight ([ad2001a](https://github.com/lobehub/lobe-chat/commit/ad2001a))
55
+
56
+ </details>
57
+
58
+ <div align="right">
59
+
60
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
61
+
62
+ </div>
63
+
5
64
  ## [Version 0.45.0](https://github.com/lobehub/lobe-chat/compare/v0.44.4...v0.45.0)
6
65
 
7
66
  <sup>Released on **2023-08-14**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.45.0",
3
+ "version": "0.46.1",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -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' })}
@@ -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'),
@@ -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}
@@ -13,7 +13,11 @@ 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 {
@@ -64,9 +68,15 @@ 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'}>
71
+ <Flexbox className={styles.promptBox} height={200}>
68
72
  {!init ? (
69
- <Skeleton active avatar={false} round style={{ marginTop: 12 }} title={false} />
73
+ <Skeleton
74
+ active
75
+ avatar={false}
76
+ round
77
+ style={{ marginTop: 12, paddingInline: 16 }}
78
+ title={false}
79
+ />
70
80
  ) : (
71
81
  <>
72
82
  <EditableMessage
@@ -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;
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
  },