@lobehub/chat 0.115.12 → 0.115.13

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.115.13](https://github.com/lobehub/lobe-chat/compare/v0.115.12...v0.115.13)
6
+
7
+ <sup>Released on **2023-12-26**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix remove tts and translate not working.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix remove tts and translate not working, closes [#818](https://github.com/lobehub/lobe-chat/issues/818) ([4a275e9](https://github.com/lobehub/lobe-chat/commit/4a275e9))
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.115.12](https://github.com/lobehub/lobe-chat/compare/v0.115.11...v0.115.12)
6
31
 
7
32
  <sup>Released on **2023-12-25**</sup>
package/README.md CHANGED
@@ -36,6 +36,7 @@ Supports one-click free deployment of your private ChatGPT/LLM web application.
36
36
  [![][share-whatsapp-shield]][share-whatsapp-link]
37
37
  [![][share-reddit-shield]][share-reddit-link]
38
38
  [![][share-weibo-shield]][share-weibo-link]
39
+ [![][share-mastodon-shield]][share-mastodon-link]
39
40
 
40
41
  <sup>Pioneering the new age of thinking and creating. Built for you, the Super Individual.</sup>
41
42
 
@@ -625,6 +626,8 @@ This project is [MIT](./LICENSE) licensed.
625
626
  [pr-welcome-link]: https://github.com/lobehub/lobe-chat/pulls
626
627
  [pr-welcome-shield]: https://img.shields.io/badge/🤯_pr_welcome-%E2%86%92-ffcb47?labelColor=black&style=for-the-badge
627
628
  [profile-link]: https://github.com/lobehub
629
+ [share-mastodon-link]: https://mastodon.social/share?text=Check%20this%20GitHub%20repository%20out%20%F0%9F%A4%AF%20LobeChat%20-%20An%20open-source,%20extensible%20(Function%20Calling),%20high-performance%20chatbot%20framework.%20It%20supports%20one-click%20free%20deployment%20of%20your%20private%20ChatGPT/LLM%20web%20application.%20https://github.com/lobehub/lobe-chat%20#chatbot%20#chatGPT%20#openAI
630
+ [share-mastodon-shield]: https://img.shields.io/badge/-share%20on%20mastodon-black?labelColor=black&logo=mastodon&logoColor=white&style=flat-square
628
631
  [share-reddit-link]: https://www.reddit.com/submit?title=Check%20this%20GitHub%20repository%20out%20%F0%9F%A4%AF%20LobeChat%20-%20An%20open-source%2C%20extensible%20%28Function%20Calling%29%2C%20high-performance%20chatbot%20framework.%20It%20supports%20one-click%20free%20deployment%20of%20your%20private%20ChatGPT%2FLLM%20web%20application.%20%23chatbot%20%23chatGPT%20%23openAI&url=https%3A%2F%2Fgithub.com%2Flobehub%2Flobe-chat
629
632
  [share-reddit-shield]: https://img.shields.io/badge/-share%20on%20reddit-black?labelColor=black&logo=reddit&logoColor=white&style=flat-square
630
633
  [share-telegram-link]: https://t.me/share/url"?text=Check%20this%20GitHub%20repository%20out%20%F0%9F%A4%AF%20LobeChat%20-%20An%20open-source%2C%20extensible%20%28Function%20Calling%29%2C%20high-performance%20chatbot%20framework.%20It%20supports%20one-click%20free%20deployment%20of%20your%20private%20ChatGPT%2FLLM%20web%20application.%20%23chatbot%20%23chatGPT%20%23openAI&url=https%3A%2F%2Fgithub.com%2Flobehub%2Flobe-chat
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.115.12",
3
+ "version": "0.115.13",
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",
@@ -179,11 +179,7 @@ class _MessageModel extends BaseModel {
179
179
  tts,
180
180
  ...item
181
181
  }: DBModel<DB_Message>): ChatMessage => {
182
- return {
183
- ...item,
184
- extra: { fromModel: fromModel, translate: translate, tts: tts },
185
- meta: {},
186
- };
182
+ return { ...item, extra: { fromModel, translate, tts }, meta: {} };
187
183
  };
188
184
  }
189
185
 
@@ -24,7 +24,7 @@ export const DB_MessageSchema = z.object({
24
24
  plugin: PluginSchema.optional(),
25
25
  pluginState: z.any().optional(),
26
26
  fromModel: z.string().optional(),
27
- translate: TranslateSchema.optional().or(z.null()),
27
+ translate: TranslateSchema.optional().or(z.literal(false)),
28
28
  tts: z.any().optional(),
29
29
 
30
30
  // foreign keys
@@ -153,21 +153,6 @@ describe('MessageService', () => {
153
153
  });
154
154
  });
155
155
 
156
- describe('updateMessageContent', () => {
157
- it('should update the content of a message', async () => {
158
- // Setup
159
- const newContent = 'Updated message content';
160
- (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, content: newContent });
161
-
162
- // Execute
163
- const result = await messageService.updateMessageContent(mockMessageId, newContent);
164
-
165
- // Assert
166
- expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { content: newContent });
167
- expect(result).toEqual({ ...mockMessage, content: newContent });
168
- });
169
- });
170
-
171
156
  describe('removeMessages', () => {
172
157
  it('should batch remove messages by assistantId and topicId', async () => {
173
158
  // Setup
@@ -243,51 +228,6 @@ describe('MessageService', () => {
243
228
  });
244
229
  });
245
230
 
246
- describe('updateMessageTranslate', () => {
247
- it('should update the translate field of a message', async () => {
248
- // Setup
249
- const newTranslate = { content: 'Translated text', to: 'es' } as ChatTranslate;
250
- (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, translate: newTranslate });
251
-
252
- // Execute
253
- const result = await messageService.updateMessageTranslate(mockMessageId, newTranslate);
254
-
255
- // Assert
256
- expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { translate: newTranslate });
257
- expect(result).toEqual({ ...mockMessage, translate: newTranslate });
258
- });
259
- });
260
-
261
- describe('updateMessageTTS', () => {
262
- it('should update the tts field of a message', async () => {
263
- // Setup
264
- const newTTS = { init: false } as ChatTTS;
265
- (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, tts: newTTS });
266
-
267
- // Execute
268
- const result = await messageService.updateMessageTTS(mockMessageId, newTTS);
269
-
270
- // Assert
271
- expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { tts: newTTS });
272
- expect(result).toEqual({ ...mockMessage, tts: newTTS });
273
- });
274
- });
275
-
276
- describe('updateMessageRole', () => {
277
- it('should update the role of a message', async () => {
278
- // Setup
279
- const newRole = 'user';
280
- (MessageModel.update as Mock).mockResolvedValue({ ...mockMessage, role: newRole });
281
-
282
- // Execute
283
- const result = await messageService.updateMessageRole(mockMessageId, newRole);
284
-
285
- // Assert
286
- expect(MessageModel.update).toHaveBeenCalledWith(mockMessageId, { role: newRole });
287
- expect(result).toEqual({ ...mockMessage, role: newRole });
288
- });
289
- });
290
-
291
231
  describe('updateMessagePlugin', () => {
292
232
  it('should update the plugin payload of a message', async () => {
293
233
  // Setup
@@ -1,13 +1,6 @@
1
1
  import { CreateMessageParams, MessageModel } from '@/database/models/message';
2
2
  import { DB_Message } from '@/database/schemas/message';
3
- import { LLMRoleType } from '@/types/llm';
4
- import {
5
- ChatMessage,
6
- ChatMessageError,
7
- ChatPluginPayload,
8
- ChatTTS,
9
- ChatTranslate,
10
- } from '@/types/message';
3
+ import { ChatMessage, ChatMessageError, ChatPluginPayload } from '@/types/message';
11
4
 
12
5
  export class MessageService {
13
6
  async create(data: CreateMessageParams) {
@@ -37,22 +30,10 @@ export class MessageService {
37
30
  return MessageModel.queryBySessionId(sessionId);
38
31
  }
39
32
 
40
- async updateMessageContent(id: string, content: string) {
41
- return MessageModel.update(id, { content });
42
- }
43
-
44
33
  async updateMessageError(id: string, error: ChatMessageError) {
45
34
  return MessageModel.update(id, { error });
46
35
  }
47
36
 
48
- async updateMessageTranslate(id: string, data: Partial<ChatTranslate> | null) {
49
- return MessageModel.update(id, { translate: data as ChatTranslate });
50
- }
51
-
52
- async updateMessageTTS(id: string, data: Partial<ChatTTS> | null) {
53
- return MessageModel.update(id, { tts: data as ChatTTS });
54
- }
55
-
56
37
  async removeMessages(assistantId: string, topicId?: string) {
57
38
  return MessageModel.batchDelete(assistantId, topicId);
58
39
  }
@@ -69,10 +50,6 @@ export class MessageService {
69
50
  return MessageModel.update(id, message);
70
51
  }
71
52
 
72
- async updateMessageRole(id: string, role: LLMRoleType) {
73
- return MessageModel.update(id, { role });
74
- }
75
-
76
53
  async updateMessagePlugin(id: string, plugin: ChatPluginPayload) {
77
54
  return MessageModel.update(id, { plugin });
78
55
  }
@@ -12,7 +12,7 @@ import { useChatStore } from '../../store';
12
12
  vi.mock('@/services/message', () => ({
13
13
  messageService: {
14
14
  updateMessageTTS: vi.fn(),
15
- updateMessageTranslate: vi.fn(),
15
+ updateMessage: vi.fn(),
16
16
  },
17
17
  }));
18
18
 
@@ -59,7 +59,7 @@ describe('ChatEnhanceAction', () => {
59
59
  await result.current.clearTTS(messageId);
60
60
  });
61
61
 
62
- expect(messageService.updateMessageTTS).toHaveBeenCalledWith(messageId, null);
62
+ expect(messageService.updateMessage).toHaveBeenCalledWith(messageId, { tts: false });
63
63
  });
64
64
  });
65
65
 
@@ -102,7 +102,7 @@ describe('ChatEnhanceAction', () => {
102
102
  await result.current.translateMessage(messageId, targetLang);
103
103
  });
104
104
 
105
- expect(messageService.updateMessageTranslate).toHaveBeenCalled();
105
+ expect(messageService.updateMessage).toHaveBeenCalled();
106
106
  });
107
107
  });
108
108
 
@@ -115,25 +115,7 @@ describe('ChatEnhanceAction', () => {
115
115
  await result.current.clearTranslate(messageId);
116
116
  });
117
117
 
118
- expect(messageService.updateMessageTranslate).toHaveBeenCalledWith(messageId, null);
119
- });
120
- });
121
-
122
- describe('ttsMessage', () => {
123
- it('should update TTS state for a message and refresh messages', async () => {
124
- const { result } = renderHook(() => useChatStore());
125
- const messageId = 'message-id';
126
- const ttsState = {
127
- contentMd5: 'some-md5',
128
- file: 'path-to-tts-file',
129
- voice: 'voice-type',
130
- };
131
-
132
- await act(async () => {
133
- await result.current.ttsMessage(messageId, ttsState);
134
- });
135
-
136
- expect(messageService.updateMessageTTS).toHaveBeenCalledWith(messageId, ttsState);
118
+ expect(messageService.updateMessage).toHaveBeenCalledWith(messageId, { translate: false });
137
119
  });
138
120
  });
139
121
  });
@@ -11,6 +11,7 @@ import { imageGenerationService } from '@/services/imageGeneration';
11
11
  import { messageService } from '@/services/message';
12
12
  import { chatSelectors } from '@/store/chat/selectors';
13
13
  import { ChatStore } from '@/store/chat/store';
14
+ import { ChatTTS, ChatTranslate } from '@/types/message';
14
15
  import { DallEImageItem } from '@/types/tool/dalle';
15
16
  import { setNamespace } from '@/utils/storeDebug';
16
17
 
@@ -31,6 +32,8 @@ export interface ChatEnhanceAction {
31
32
  state?: { contentMd5?: string; file?: string; voice?: string },
32
33
  ) => Promise<void>;
33
34
  updateImageItem: (id: string, updater: (data: DallEImageItem[]) => void) => Promise<void>;
35
+ updateMessageTTS: (id: string, data: Partial<ChatTTS> | false) => Promise<void>;
36
+ updateMessageTranslate: (id: string, data: Partial<ChatTranslate> | false) => Promise<void>;
34
37
  }
35
38
 
36
39
  export const chatEnhance: StateCreator<
@@ -40,13 +43,11 @@ export const chatEnhance: StateCreator<
40
43
  ChatEnhanceAction
41
44
  > = (set, get) => ({
42
45
  clearTTS: async (id) => {
43
- await messageService.updateMessageTTS(id, null);
44
- await get().refreshMessages();
46
+ await get().updateMessageTTS(id, false);
45
47
  },
46
48
 
47
49
  clearTranslate: async (id) => {
48
- await messageService.updateMessageTranslate(id, null);
49
- await get().refreshMessages();
50
+ await get().updateMessageTranslate(id, false);
50
51
  },
51
52
 
52
53
  generateImageFromPrompts: async (items, messageId) => {
@@ -94,16 +95,14 @@ export const chatEnhance: StateCreator<
94
95
  n('toggleDallEImageLoading'),
95
96
  );
96
97
  },
97
-
98
98
  translateMessage: async (id, targetLang) => {
99
- const { toggleChatLoading, dispatchMessage, refreshMessages } = get();
99
+ const { toggleChatLoading, updateMessageTranslate, dispatchMessage } = get();
100
100
 
101
101
  const message = chatSelectors.getMessageById(id)(get());
102
102
  if (!message) return;
103
103
 
104
104
  // create translate extra
105
- await messageService.updateMessageTranslate(id, { content: '', from: '', to: targetLang });
106
- await refreshMessages();
105
+ await updateMessageTranslate(id, { content: '', from: '', to: targetLang });
107
106
 
108
107
  toggleChatLoading(true, id, n('translateMessage(start)', { id }) as string);
109
108
 
@@ -118,8 +117,7 @@ export const chatEnhance: StateCreator<
118
117
  .then(async (data) => {
119
118
  if (data && supportLocales.includes(data)) from = data;
120
119
 
121
- await messageService.updateMessageTranslate(id, { content, from, to: targetLang });
122
- await refreshMessages();
120
+ await updateMessageTranslate(id, { content, from, to: targetLang });
123
121
  });
124
122
 
125
123
  // translate to target language
@@ -138,15 +136,13 @@ export const chatEnhance: StateCreator<
138
136
  params: chainTranslate(message.content, targetLang),
139
137
  });
140
138
 
141
- await messageService.updateMessageTranslate(id, { content, from, to: targetLang });
142
- await refreshMessages();
139
+ await updateMessageTranslate(id, { content, from, to: targetLang });
143
140
 
144
141
  toggleChatLoading(false);
145
142
  },
146
143
 
147
144
  ttsMessage: async (id, state = {}) => {
148
- await messageService.updateMessageTTS(id, state);
149
- await get().refreshMessages();
145
+ await get().updateMessageTTS(id, state);
150
146
  },
151
147
 
152
148
  updateImageItem: async (id, updater) => {
@@ -156,7 +152,15 @@ export const chatEnhance: StateCreator<
156
152
  const data: DallEImageItem[] = JSON.parse(message.content);
157
153
 
158
154
  const nextContent = produce(data, updater);
159
- await messageService.updateMessageContent(id, JSON.stringify(nextContent));
155
+ await get().updateMessageContent(id, JSON.stringify(nextContent));
156
+ },
157
+
158
+ updateMessageTTS: async (id, data) => {
159
+ await messageService.updateMessage(id, { tts: data as ChatTTS });
160
+ await get().refreshMessages();
161
+ },
162
+ updateMessageTranslate: async (id, data) => {
163
+ await messageService.updateMessage(id, { translate: data as ChatTranslate });
160
164
  await get().refreshMessages();
161
165
  },
162
166
  });
@@ -20,7 +20,7 @@ vi.mock('@/services/message', () => ({
20
20
  createAssistantMessage: vi.fn(() => Promise.resolve('content-content-content')),
21
21
  removeMessages: vi.fn(() => Promise.resolve()),
22
22
  create: vi.fn(() => Promise.resolve('new-message-id')),
23
- updateMessageContent: vi.fn(),
23
+ updateMessage: vi.fn(),
24
24
  clearAllMessage: vi.fn(() => Promise.resolve()),
25
25
  },
26
26
  }));
@@ -303,7 +303,7 @@ describe('chatMessage actions', () => {
303
303
  await result.current.updateMessageContent(messageId, newContent);
304
304
  });
305
305
 
306
- expect(messageService.updateMessageContent).toHaveBeenCalledWith(messageId, newContent);
306
+ expect(messageService.updateMessage).toHaveBeenCalledWith(messageId, { content: newContent });
307
307
  });
308
308
 
309
309
  it('should dispatch message update action', async () => {
@@ -204,7 +204,7 @@ export const chatMessage: StateCreator<
204
204
  // refs: https://medium.com/@kyledeguzmanx/what-are-optimistic-updates-483662c3e171
205
205
  dispatchMessage({ id, key: 'content', type: 'updateMessage', value: content });
206
206
 
207
- await messageService.updateMessageContent(id, content);
207
+ await messageService.updateMessage(id, { content });
208
208
  await refreshMessages();
209
209
  },
210
210
  useFetchMessages: (sessionId, activeTopicId) =>
@@ -265,7 +265,7 @@ export const chatMessage: StateCreator<
265
265
  if (functionCallAtEnd) {
266
266
  // create a new separate message and remove the function call from the prev message
267
267
 
268
- await messageService.updateMessageContent(mid, content.replace(functionCallContent, ''));
268
+ await get().updateMessageContent(mid, content.replace(functionCallContent, ''));
269
269
 
270
270
  const functionMessage: CreateMessageParams = {
271
271
  role: 'function',
@@ -294,7 +294,7 @@ export const chatMessage: StateCreator<
294
294
  set({ messages }, false, n(`dispatchMessage/${payload.type}`, payload));
295
295
  },
296
296
  fetchAIChatMessage: async (messages, assistantId) => {
297
- const { toggleChatLoading, refreshMessages } = get();
297
+ const { toggleChatLoading, refreshMessages, updateMessageContent } = get();
298
298
 
299
299
  const abortController = toggleChatLoading(
300
300
  true,
@@ -369,14 +369,12 @@ export const chatMessage: StateCreator<
369
369
  },
370
370
  onFinish: async (content) => {
371
371
  // update the content after fetch result
372
- await messageService.updateMessageContent(assistantId, content);
373
- await refreshMessages();
372
+ await updateMessageContent(assistantId, content);
374
373
  },
375
374
  onMessageHandle: async (text) => {
376
375
  output += text;
377
376
 
378
- await messageService.updateMessageContent(assistantId, output);
379
- await refreshMessages();
377
+ await updateMessageContent(assistantId, output);
380
378
 
381
379
  // is this message is just a function call
382
380
  if (isFunctionMessageAtStart(output)) isFunctionCall = true;
@@ -58,7 +58,7 @@ describe('ChatPluginAction', () => {
58
58
  });
59
59
 
60
60
  // 验证 messageService.updateMessageContent 是否被正确调用
61
- expect(messageService.updateMessageContent).toHaveBeenCalledWith(messageId, newContent);
61
+ expect(messageService.updateMessage).toHaveBeenCalledWith(messageId, { content: newContent });
62
62
 
63
63
  // 验证 refreshMessages 是否被调用
64
64
  expect(result.current.refreshMessages).toHaveBeenCalled();
@@ -95,10 +95,9 @@ describe('ChatPluginAction', () => {
95
95
  expect.any(String),
96
96
  );
97
97
  expect(chatService.runPluginApi).toHaveBeenCalledWith(pluginPayload, { signal: undefined });
98
- expect(messageService.updateMessageContent).toHaveBeenCalledWith(
99
- messageId,
100
- pluginApiResponse,
101
- );
98
+ expect(messageService.updateMessage).toHaveBeenCalledWith(messageId, {
99
+ content: pluginApiResponse,
100
+ });
102
101
  expect(initialState.refreshMessages).toHaveBeenCalled();
103
102
  expect(initialState.coreProcessMessage).toHaveBeenCalled();
104
103
  expect(initialState.toggleChatLoading).toHaveBeenCalledWith(false);
@@ -30,24 +30,22 @@ export const chatPlugin: StateCreator<
30
30
  ChatPluginAction
31
31
  > = (set, get) => ({
32
32
  fillPluginMessageContent: async (id, content) => {
33
- const { coreProcessMessage, refreshMessages } = get();
33
+ const { coreProcessMessage, updateMessageContent } = get();
34
34
 
35
- await messageService.updateMessageContent(id, content);
36
- await refreshMessages();
35
+ await updateMessageContent(id, content);
37
36
 
38
37
  const chats = chatSelectors.currentChats(get());
39
38
  await coreProcessMessage(chats, id);
40
39
  },
41
40
  invokeBuiltinTool: async (id, payload) => {
42
- const { toggleChatLoading, refreshMessages } = get();
41
+ const { toggleChatLoading, updateMessageContent } = get();
43
42
  const params = JSON.parse(payload.arguments);
44
43
  toggleChatLoading(true, id, n('invokeBuiltinTool') as string);
45
44
  const data = await useToolStore.getState().invokeBuiltinTool(payload.apiName, params);
46
45
  toggleChatLoading(false);
47
46
 
48
47
  if (data) {
49
- await messageService.updateMessageContent(id, data);
50
- await refreshMessages();
48
+ await updateMessageContent(id, data);
51
49
  }
52
50
 
53
51
  // postToolCalling
@@ -58,7 +56,7 @@ export const chatPlugin: StateCreator<
58
56
  await action(id, JSON.parse(data));
59
57
  },
60
58
  invokeDefaultTypePlugin: async (id, payload) => {
61
- const { refreshMessages, coreProcessMessage, toggleChatLoading } = get();
59
+ const { updateMessageContent, refreshMessages, coreProcessMessage, toggleChatLoading } = get();
62
60
  let data: string;
63
61
 
64
62
  try {
@@ -79,8 +77,7 @@ export const chatPlugin: StateCreator<
79
77
  // 如果报错则结束了
80
78
  if (!data) return;
81
79
 
82
- await messageService.updateMessageContent(id, data);
83
- await refreshMessages();
80
+ await updateMessageContent(id, data);
84
81
 
85
82
  const chats = chatSelectors.currentChats(get());
86
83
  await coreProcessMessage(chats, id);
@@ -37,7 +37,7 @@ export interface ChatMessage extends BaseDataModel {
37
37
  extra?: {
38
38
  fromModel?: string;
39
39
  // 翻译
40
- translate?: ChatTranslate | null;
40
+ translate?: ChatTranslate | false;
41
41
  // TTS
42
42
  tts?: ChatTTS;
43
43
  } & Record<string, any>;