@lobehub/chat 0.151.1 → 0.151.2

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,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.151.2](https://github.com/lobehub/lobe-chat/compare/v0.151.1...v0.151.2)
6
+
7
+ <sup>Released on **2024-04-29**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix only inject welcome question in inbox.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix only inject welcome question in inbox, closes [#2289](https://github.com/lobehub/lobe-chat/issues/2289) ([cc8edd3](https://github.com/lobehub/lobe-chat/commit/cc8edd3))
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
+
5
30
  ### [Version 0.151.1](https://github.com/lobehub/lobe-chat/compare/v0.151.0...v0.151.1)
6
31
 
7
32
  <sup>Released on **2024-04-29**</sup>
package/next.config.mjs CHANGED
@@ -23,6 +23,7 @@ const nextConfig = {
23
23
  '@lobehub/ui',
24
24
  'gpt-tokenizer',
25
25
  'chroma-js',
26
+ 'shiki',
26
27
  ],
27
28
  webVitalsAttribution: ['CLS', 'LCP'],
28
29
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.151.1",
3
+ "version": "0.151.2",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -126,7 +126,7 @@
126
126
  "lucide-react": "latest",
127
127
  "modern-screenshot": "^4.4.39",
128
128
  "nanoid": "^5.0.7",
129
- "next": "14.1.4",
129
+ "next": "^14.2.1",
130
130
  "next-auth": "5.0.0-beta.15",
131
131
  "next-sitemap": "^4.2.3",
132
132
  "numeral": "^2.0.6",
@@ -45,7 +45,7 @@ const SendMore = memo(() => {
45
45
  hotKey,
46
46
  (keyboardEvent, hotkeysEvent) => {
47
47
  console.log(keyboardEvent, hotkeysEvent);
48
- sendMessage(true);
48
+ sendMessage({ onlyAddUserMessage: true });
49
49
  },
50
50
  {
51
51
  enableOnFormTags: true,
@@ -94,7 +94,7 @@ const SendMore = memo(() => {
94
94
  </Flexbox>
95
95
  ),
96
96
  onClick: () => {
97
- sendMessage(true);
97
+ sendMessage({ onlyAddUserMessage: true });
98
98
  },
99
99
  },
100
100
  ],
@@ -1,15 +1,21 @@
1
1
  import { useCallback } from 'react';
2
2
 
3
3
  import { useChatStore } from '@/store/chat';
4
+ import { SendMessageParams } from '@/store/chat/slices/message/action';
4
5
  import { filesSelectors, useFileStore } from '@/store/file';
5
6
 
7
+ export type UseSendMessageParams = Pick<
8
+ SendMessageParams,
9
+ 'onlyAddUserMessage' | 'isWelcomeQuestion'
10
+ >;
11
+
6
12
  export const useSendMessage = () => {
7
13
  const [sendMessage, updateInputMessage] = useChatStore((s) => [
8
14
  s.sendMessage,
9
15
  s.updateInputMessage,
10
16
  ]);
11
17
 
12
- return useCallback((onlyAddUserMessage?: boolean) => {
18
+ return useCallback((params: UseSendMessageParams = {}) => {
13
19
  const store = useChatStore.getState();
14
20
  if (!!store.chatLoadingId) return;
15
21
  if (!store.inputMessage) return;
@@ -19,7 +25,7 @@ export const useSendMessage = () => {
19
25
  sendMessage({
20
26
  files: imageList,
21
27
  message: store.inputMessage,
22
- onlyAddUserMessage: onlyAddUserMessage,
28
+ ...params,
23
29
  });
24
30
 
25
31
  updateInputMessage('');
@@ -10,7 +10,8 @@ import { useTranslation } from 'react-i18next';
10
10
  import { Flexbox } from 'react-layout-kit';
11
11
 
12
12
  import { USAGE_DOCUMENTS } from '@/const/url';
13
- import { useChatInput } from '@/features/ChatInput/useChatInput';
13
+ import { useSendMessage } from '@/features/ChatInput/useSend';
14
+ import { useChatStore } from '@/store/chat';
14
15
 
15
16
  const useStyles = createStyles(({ css, token }) => ({
16
17
  card: css`
@@ -54,14 +55,10 @@ const qa = shuffle([
54
55
  ]).slice(0, 5);
55
56
 
56
57
  const QuestionSuggest = memo(() => {
57
- const { onInput, onSend } = useChatInput();
58
+ const [updateInputMessage] = useChatStore((s) => [s.updateInputMessage]);
58
59
  const { t } = useTranslation('welcome');
59
60
  const { styles } = useStyles();
60
-
61
- const handoleSend = (qa: string) => {
62
- onInput(qa);
63
- onSend();
64
- };
61
+ const sendMessage = useSendMessage();
65
62
 
66
63
  return (
67
64
  <Flexbox gap={8} width={'100%'}>
@@ -85,7 +82,10 @@ const QuestionSuggest = memo(() => {
85
82
  gap={8}
86
83
  horizontal
87
84
  key={item}
88
- onClick={() => handoleSend(text)}
85
+ onClick={() => {
86
+ updateInputMessage(text);
87
+ sendMessage({ isWelcomeQuestion: true });
88
+ }}
89
89
  >
90
90
  {t(text)}
91
91
  </Flexbox>
@@ -7,9 +7,11 @@ import { useChatStore } from '@/store/chat';
7
7
  import { chatSelectors } from '@/store/chat/selectors';
8
8
  import { isMobileScreen } from '@/utils/screen';
9
9
 
10
+ import { useInitConversation } from '../../hooks/useInitConversation';
10
11
  import AutoScroll from '../AutoScroll';
11
12
  import Item from '../ChatItem';
12
13
  import InboxWelcome from '../InboxWelcome';
14
+ import SkeletonList from '../SkeletonList';
13
15
 
14
16
  const WELCOME_ID = 'welcome';
15
17
 
@@ -29,6 +31,8 @@ interface VirtualizedListProps {
29
31
  mobile?: boolean;
30
32
  }
31
33
  const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
34
+ useInitConversation();
35
+
32
36
  const virtuosoRef = useRef<VirtuosoHandle>(null);
33
37
  const [atBottom, setAtBottom] = useState(true);
34
38
 
@@ -52,7 +56,9 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
52
56
  // overscan should be 1.5 times the height of the window
53
57
  const overscan = typeof window !== 'undefined' ? window.innerHeight * 1.5 : 0;
54
58
 
55
- return chatLoading && data.length === 2 ? null : (
59
+ return chatLoading ? (
60
+ <SkeletonList mobile={mobile} />
61
+ ) : (
56
62
  <Flexbox height={'100%'}>
57
63
  <Virtuoso
58
64
  atBottomStateChange={setAtBottom}
@@ -1,13 +1,12 @@
1
1
  import { createStyles } from 'antd-style';
2
- import { ReactNode, memo } from 'react';
2
+ import { ReactNode, Suspense, lazy, memo } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
5
  import ChatHydration from '@/components/StoreHydration/ChatHydration';
6
- import { useChatStore } from '@/store/chat';
7
6
 
8
7
  import SkeletonList from './components/SkeletonList';
9
- import ChatList from './components/VirtualizedList';
10
- import { useInitConversation } from './hooks/useInitConversation';
8
+
9
+ const ChatList = lazy(() => import('./components/VirtualizedList'));
11
10
 
12
11
  const useStyles = createStyles(
13
12
  ({ css, responsive, stylish }) => css`
@@ -30,10 +29,6 @@ interface ConversationProps {
30
29
  const Conversation = memo<ConversationProps>(({ chatInput, mobile }) => {
31
30
  const { styles } = useStyles();
32
31
 
33
- useInitConversation();
34
-
35
- const [messagesInit] = useChatStore((s) => [s.messagesInit]);
36
-
37
32
  return (
38
33
  <Flexbox
39
34
  flex={1}
@@ -41,7 +36,9 @@ const Conversation = memo<ConversationProps>(({ chatInput, mobile }) => {
41
36
  style={{ position: 'relative' }}
42
37
  >
43
38
  <div className={styles}>
44
- {messagesInit ? <ChatList mobile={mobile} /> : <SkeletonList mobile={mobile} />}
39
+ <Suspense fallback={<SkeletonList mobile={mobile} />}>
40
+ <ChatList mobile={mobile} />
41
+ </Suspense>
45
42
  </div>
46
43
  {chatInput}
47
44
  <ChatHydration />
@@ -31,6 +31,7 @@ import { createHeaderWithAuth, getProviderAuthPayload } from './_auth';
31
31
  import { API_ENDPOINTS } from './_url';
32
32
 
33
33
  interface FetchOptions {
34
+ isWelcomeQuestion?: boolean;
34
35
  signal?: AbortSignal | undefined;
35
36
  trace?: TracePayload;
36
37
  }
@@ -65,6 +66,7 @@ interface FetchAITaskResultParams {
65
66
 
66
67
  interface CreateAssistantMessageStream extends FetchSSEOptions {
67
68
  abortController?: AbortController;
69
+ isWelcomeQuestion?: boolean;
68
70
  params: GetChatCompletionPayload;
69
71
  trace?: TracePayload;
70
72
  }
@@ -220,10 +222,12 @@ class ChatService {
220
222
  onErrorHandle,
221
223
  onFinish,
222
224
  trace,
225
+ isWelcomeQuestion,
223
226
  }: CreateAssistantMessageStream) => {
224
227
  await fetchSSE(
225
228
  () =>
226
229
  this.createAssistantMessage(params, {
230
+ isWelcomeQuestion,
227
231
  signal: abortController?.signal,
228
232
  trace: this.mapTrace(trace, TraceTagMap.Chat),
229
233
  }),
@@ -432,9 +436,11 @@ class ChatService {
432
436
  });
433
437
 
434
438
  return produce(postMessages, (draft) => {
435
- // Inject InboxGuide SystemRole
439
+ // if it's a welcome question, inject InboxGuide SystemRole
436
440
  const inboxGuideSystemRole =
437
- options?.trace?.sessionId === INBOX_SESSION_ID && INBOX_GUIDE_SYSTEMROLE;
441
+ options?.isWelcomeQuestion &&
442
+ options?.trace?.sessionId === INBOX_SESSION_ID &&
443
+ INBOX_GUIDE_SYSTEMROLE;
438
444
 
439
445
  // Inject Tool SystemRole
440
446
  const hasTools = tools && tools?.length > 0;
@@ -385,11 +385,7 @@ describe('chatMessage actions', () => {
385
385
  });
386
386
 
387
387
  expect(messageService.removeMessage).not.toHaveBeenCalledWith(messageId);
388
- expect(mockState.coreProcessMessage).toHaveBeenCalledWith(
389
- expect.any(Array),
390
- messageId,
391
- undefined,
392
- );
388
+ expect(mockState.coreProcessMessage).toHaveBeenCalledWith(expect.any(Array), messageId, {});
393
389
  });
394
390
 
395
391
  it('should not perform any action if the message id does not exist', async () => {
@@ -29,10 +29,20 @@ const n = setNamespace('message');
29
29
 
30
30
  const SWR_USE_FETCH_MESSAGES = 'SWR_USE_FETCH_MESSAGES';
31
31
 
32
- interface SendMessageParams {
32
+ export interface SendMessageParams {
33
33
  message: string;
34
34
  files?: { id: string; url: string }[];
35
35
  onlyAddUserMessage?: boolean;
36
+ /**
37
+ *
38
+ * https://github.com/lobehub/lobe-chat/pull/2086
39
+ */
40
+ isWelcomeQuestion?: boolean;
41
+ }
42
+
43
+ interface ProcessMessageParams {
44
+ traceId?: string;
45
+ isWelcomeQuestion?: boolean;
36
46
  }
37
47
 
38
48
  export interface ChatMessageAction {
@@ -77,7 +87,7 @@ export interface ChatMessageAction {
77
87
  coreProcessMessage: (
78
88
  messages: ChatMessage[],
79
89
  parentId: string,
80
- traceId?: string,
90
+ params?: ProcessMessageParams,
81
91
  ) => Promise<void>;
82
92
  /**
83
93
  * 实际获取 AI 响应
@@ -87,7 +97,7 @@ export interface ChatMessageAction {
87
97
  fetchAIChatMessage: (
88
98
  messages: ChatMessage[],
89
99
  assistantMessageId: string,
90
- traceId?: string,
100
+ params?: ProcessMessageParams,
91
101
  ) => Promise<{
92
102
  content: string;
93
103
  functionCallAtEnd: boolean;
@@ -173,7 +183,7 @@ export const chatMessage: StateCreator<
173
183
  await messageService.removeAllMessages();
174
184
  await refreshMessages();
175
185
  },
176
- sendMessage: async ({ message, files, onlyAddUserMessage }) => {
186
+ sendMessage: async ({ message, files, onlyAddUserMessage, isWelcomeQuestion }) => {
177
187
  const { coreProcessMessage, activeTopicId, activeId } = get();
178
188
  if (!activeId) return;
179
189
 
@@ -200,7 +210,7 @@ export const chatMessage: StateCreator<
200
210
  // Get the current messages to generate AI response
201
211
  const messages = chatSelectors.currentChats(get());
202
212
 
203
- await coreProcessMessage(messages, id);
213
+ await coreProcessMessage(messages, id, { isWelcomeQuestion });
204
214
 
205
215
  // check activeTopic and then auto create topic
206
216
  const chats = chatSelectors.currentChats(get());
@@ -263,6 +273,8 @@ export const chatMessage: StateCreator<
263
273
  async ([, sessionId, topicId]: [string, string, string | undefined]) =>
264
274
  messageService.getMessages(sessionId, topicId),
265
275
  {
276
+ suspense: true,
277
+ fallbackData: [],
266
278
  onSuccess: (messages, key) => {
267
279
  set(
268
280
  { activeId: sessionId, messages, messagesInit: true },
@@ -280,7 +292,7 @@ export const chatMessage: StateCreator<
280
292
  },
281
293
 
282
294
  // the internal process method of the AI message
283
- coreProcessMessage: async (messages, userMessageId, trace) => {
295
+ coreProcessMessage: async (messages, userMessageId, params) => {
284
296
  const { fetchAIChatMessage, triggerFunctionCall, refreshMessages, activeTopicId } = get();
285
297
 
286
298
  const { model, provider } = getAgentConfig();
@@ -301,7 +313,7 @@ export const chatMessage: StateCreator<
301
313
 
302
314
  // 2. fetch the AI response
303
315
  const { isFunctionCall, content, functionCallAtEnd, functionCallContent, traceId } =
304
- await fetchAIChatMessage(messages, mid, trace);
316
+ await fetchAIChatMessage(messages, mid, params);
305
317
 
306
318
  // 3. if it's the function call message, trigger the function method
307
319
  if (isFunctionCall) {
@@ -341,7 +353,7 @@ export const chatMessage: StateCreator<
341
353
 
342
354
  set({ messages }, false, n(`dispatchMessage/${payload.type}`, payload));
343
355
  },
344
- fetchAIChatMessage: async (messages, assistantId, traceId) => {
356
+ fetchAIChatMessage: async (messages, assistantId, params) => {
345
357
  const {
346
358
  toggleChatLoading,
347
359
  refreshMessages,
@@ -421,11 +433,12 @@ export const chatMessage: StateCreator<
421
433
  plugins: config.plugins,
422
434
  },
423
435
  trace: {
424
- traceId,
436
+ traceId: params?.traceId,
425
437
  sessionId: get().activeId,
426
438
  topicId: get().activeTopicId,
427
439
  traceName: TraceNameMap.Conversation,
428
440
  },
441
+ isWelcomeQuestion: params?.isWelcomeQuestion,
429
442
  onErrorHandle: async (error) => {
430
443
  await messageService.updateMessageError(assistantId, error);
431
444
  await refreshMessages();
@@ -567,7 +580,7 @@ export const chatMessage: StateCreator<
567
580
 
568
581
  if (!latestMsg) return;
569
582
 
570
- await coreProcessMessage(contextMessages, latestMsg.id, traceId);
583
+ await coreProcessMessage(contextMessages, latestMsg.id, { traceId });
571
584
  },
572
585
 
573
586
  internalUpdateMessageContent: async (id, content) => {
@@ -61,7 +61,7 @@ describe('ChatPluginAction', () => {
61
61
  expect(result.current.coreProcessMessage).toHaveBeenCalledWith(
62
62
  mockCurrentChats,
63
63
  messageId,
64
- undefined,
64
+ {},
65
65
  );
66
66
  });
67
67
  it('should update message content and not trigger ai message', async () => {
@@ -174,7 +174,7 @@ export const chatPlugin: StateCreator<
174
174
  triggerAIMessage: async (id, traceId) => {
175
175
  const { coreProcessMessage } = get();
176
176
  const chats = chatSelectors.currentChats(get());
177
- await coreProcessMessage(chats, id, traceId);
177
+ await coreProcessMessage(chats, id, { traceId });
178
178
  },
179
179
 
180
180
  triggerFunctionCall: async (id) => {