@lobehub/chat 1.28.5 → 1.29.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 +50 -0
- package/locales/ar/chat.json +1 -0
- package/locales/ar/setting.json +7 -2
- package/locales/bg-BG/chat.json +1 -0
- package/locales/bg-BG/setting.json +7 -2
- package/locales/de-DE/chat.json +1 -0
- package/locales/de-DE/setting.json +7 -2
- package/locales/en-US/chat.json +1 -0
- package/locales/en-US/setting.json +7 -2
- package/locales/es-ES/chat.json +1 -0
- package/locales/es-ES/setting.json +7 -2
- package/locales/fa-IR/chat.json +1 -0
- package/locales/fa-IR/setting.json +7 -2
- package/locales/fr-FR/chat.json +1 -0
- package/locales/fr-FR/setting.json +7 -2
- package/locales/it-IT/chat.json +1 -0
- package/locales/it-IT/setting.json +7 -2
- package/locales/ja-JP/chat.json +1 -0
- package/locales/ja-JP/setting.json +7 -2
- package/locales/ko-KR/chat.json +1 -0
- package/locales/ko-KR/setting.json +7 -2
- package/locales/nl-NL/chat.json +1 -0
- package/locales/nl-NL/setting.json +7 -2
- package/locales/pl-PL/chat.json +1 -0
- package/locales/pl-PL/setting.json +7 -2
- package/locales/pt-BR/chat.json +1 -0
- package/locales/pt-BR/setting.json +7 -2
- package/locales/ru-RU/chat.json +1 -0
- package/locales/ru-RU/setting.json +7 -2
- package/locales/tr-TR/chat.json +1 -0
- package/locales/tr-TR/setting.json +7 -2
- package/locales/vi-VN/chat.json +1 -0
- package/locales/vi-VN/setting.json +7 -2
- package/locales/zh-CN/chat.json +1 -0
- package/locales/zh-CN/setting.json +7 -2
- package/locales/zh-TW/chat.json +1 -0
- package/locales/zh-TW/setting.json +7 -2
- package/package.json +1 -1
- package/src/app/(main)/settings/system-agent/index.tsx +1 -0
- package/src/chains/__tests__/__snapshots__/summaryHistory.test.ts.snap +21 -0
- package/src/chains/__tests__/summaryHistory.test.ts +24 -0
- package/src/chains/summaryHistory.ts +19 -0
- package/src/const/settings/agent.ts +3 -1
- package/src/const/settings/systemAgent.ts +1 -0
- package/src/database/client/models/__tests__/session.test.ts +0 -1
- package/src/database/server/migrations/0011_add_topic_history_summary.sql +2 -0
- package/src/database/server/migrations/meta/0011_snapshot.json +3196 -0
- package/src/database/server/migrations/meta/_journal.json +7 -0
- package/src/database/server/models/__tests__/topic.test.ts +4 -0
- package/src/database/server/models/topic.ts +16 -0
- package/src/database/server/schemas/lobechat/topic.ts +3 -2
- package/src/features/AgentSetting/AgentChat/index.tsx +4 -18
- package/src/features/ChatInput/ActionBar/History.tsx +24 -21
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +22 -7
- package/src/features/Conversation/Actions/index.ts +2 -2
- package/src/features/Conversation/components/ChatItem/index.tsx +6 -6
- package/src/features/Conversation/components/History/index.tsx +71 -0
- package/src/features/Conversation/types/index.tsx +1 -1
- package/src/libs/next-auth/sso-providers/microsoft-entra-id-helper.ts +1 -1
- package/src/locales/default/chat.ts +1 -0
- package/src/locales/default/setting.ts +7 -2
- package/src/prompts/chatMessages/index.test.ts +94 -0
- package/src/prompts/chatMessages/index.ts +11 -0
- package/src/prompts/systemRole/index.ts +22 -0
- package/src/server/routers/lambda/topic.ts +7 -0
- package/src/services/__tests__/chat.test.ts +13 -61
- package/src/services/chat.ts +45 -11
- package/src/store/agent/slices/chat/__snapshots__/selectors.test.ts.snap +3 -1
- package/src/store/chat/helpers.ts +6 -2
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +21 -8
- package/src/store/chat/slices/aiChat/actions/helpers.ts +9 -0
- package/src/store/chat/slices/aiChat/actions/index.ts +3 -1
- package/src/store/chat/slices/aiChat/actions/memory.ts +52 -0
- package/src/store/chat/slices/message/selectors.ts +1 -3
- package/src/store/chat/slices/topic/selectors.ts +24 -12
- package/src/store/chat/slices/{enchance → translate}/action.test.ts +0 -13
- package/src/store/chat/slices/{enchance → translate}/action.ts +5 -24
- package/src/store/chat/slices/tts/action.test.ts +63 -0
- package/src/store/chat/slices/tts/action.ts +35 -0
- package/src/store/chat/store.ts +6 -3
- package/src/store/file/reducers/uploadFileList.test.ts +197 -0
- package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +3 -1
- package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
- package/src/types/agent/index.ts +2 -4
- package/src/types/topic.ts +13 -0
- package/src/types/user/settings/systemAgent.ts +1 -0
- package/src/utils/tokenizer/client.ts +1 -1
- /package/src/features/Conversation/components/{ChatItem → History}/HistoryDivider.tsx +0 -0
@@ -28,6 +28,8 @@ import {
|
|
28
28
|
import { AgentRuntime } from '@/libs/agent-runtime';
|
29
29
|
import { useToolStore } from '@/store/tool';
|
30
30
|
import { UserStore } from '@/store/user';
|
31
|
+
import { useUserStore } from '@/store/user';
|
32
|
+
import { modelConfigSelectors } from '@/store/user/selectors';
|
31
33
|
import { UserSettingsState, initialSettingsState } from '@/store/user/slices/settings/initialState';
|
32
34
|
import { DalleManifest } from '@/tools/dalle';
|
33
35
|
import { ChatMessage } from '@/types/message';
|
@@ -35,8 +37,6 @@ import { ChatStreamPayload, type OpenAIChatMessage } from '@/types/openai/chat';
|
|
35
37
|
import { LobeTool } from '@/types/tool';
|
36
38
|
|
37
39
|
import { chatService, initializeWithClientStore } from '../chat';
|
38
|
-
import { useUserStore } from '@/store/user';
|
39
|
-
import {modelConfigSelectors} from "@/store/user/selectors";
|
40
40
|
|
41
41
|
// Mocking external dependencies
|
42
42
|
vi.mock('i18next', () => ({
|
@@ -150,8 +150,6 @@ describe('ChatService', () => {
|
|
150
150
|
},
|
151
151
|
],
|
152
152
|
}, // Message with files
|
153
|
-
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with tool role
|
154
|
-
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
155
153
|
] as ChatMessage[];
|
156
154
|
|
157
155
|
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
@@ -177,15 +175,6 @@ describe('ChatService', () => {
|
|
177
175
|
],
|
178
176
|
role: 'user',
|
179
177
|
},
|
180
|
-
{
|
181
|
-
content: 'Hi',
|
182
|
-
name: 'plugin1____api1',
|
183
|
-
role: 'tool',
|
184
|
-
},
|
185
|
-
{
|
186
|
-
content: 'Hey',
|
187
|
-
role: 'assistant',
|
188
|
-
},
|
189
178
|
],
|
190
179
|
model: 'gpt-4-vision-preview',
|
191
180
|
},
|
@@ -196,7 +185,6 @@ describe('ChatService', () => {
|
|
196
185
|
it('should not include image with vision models when can not find the image', async () => {
|
197
186
|
const messages = [
|
198
187
|
{ content: 'Hello', role: 'user', files: ['file2'] }, // Message with files
|
199
|
-
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with function role
|
200
188
|
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
201
189
|
] as ChatMessage[];
|
202
190
|
|
@@ -207,7 +195,6 @@ describe('ChatService', () => {
|
|
207
195
|
{
|
208
196
|
messages: [
|
209
197
|
{ content: 'Hello', role: 'user' },
|
210
|
-
{ content: 'Hi', name: 'plugin1____api1', role: 'tool' },
|
211
198
|
{ content: 'Hey', role: 'assistant' },
|
212
199
|
],
|
213
200
|
},
|
@@ -536,7 +523,9 @@ describe('ChatService', () => {
|
|
536
523
|
};
|
537
524
|
|
538
525
|
vi.spyOn(useUserStore, 'getState').mockImplementationOnce(() => mockUserStore as any);
|
539
|
-
vi.spyOn(modelConfigSelectors, 'isProviderFetchOnClient').mockImplementationOnce(
|
526
|
+
vi.spyOn(modelConfigSelectors, 'isProviderFetchOnClient').mockImplementationOnce(
|
527
|
+
mockModelConfigSelectors.isProviderFetchOnClient,
|
528
|
+
);
|
540
529
|
|
541
530
|
const params: Partial<ChatStreamPayload> = {
|
542
531
|
model: 'test-model',
|
@@ -550,18 +539,15 @@ describe('ChatService', () => {
|
|
550
539
|
...params,
|
551
540
|
};
|
552
541
|
|
553
|
-
const result = await chatService.getChatCompletion(params,options);
|
542
|
+
const result = await chatService.getChatCompletion(params, options);
|
554
543
|
|
555
|
-
expect(global.fetch).toHaveBeenCalledWith(
|
556
|
-
|
557
|
-
{
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
method: 'POST',
|
563
|
-
},
|
564
|
-
);
|
544
|
+
expect(global.fetch).toHaveBeenCalledWith(expect.any(String), {
|
545
|
+
body: JSON.stringify(expectedPayload),
|
546
|
+
headers: expect.objectContaining({
|
547
|
+
'Content-Type': 'application/json',
|
548
|
+
}),
|
549
|
+
method: 'POST',
|
550
|
+
});
|
565
551
|
expect(result.status).toBe(401);
|
566
552
|
});
|
567
553
|
|
@@ -683,14 +669,6 @@ describe('ChatService', () => {
|
|
683
669
|
id: 'tool_call_nXxXHW8Z',
|
684
670
|
type: 'function',
|
685
671
|
},
|
686
|
-
{
|
687
|
-
function: {
|
688
|
-
arguments: '{"query":"LobeHub","searchEngines":["bilibili"]}',
|
689
|
-
name: 'lobe-web-browsing____searchWithSearXNG____builtin',
|
690
|
-
},
|
691
|
-
id: 'tool_call_2f3CEKz9',
|
692
|
-
type: 'function',
|
693
|
-
},
|
694
672
|
],
|
695
673
|
},
|
696
674
|
{
|
@@ -749,14 +727,6 @@ describe('ChatService', () => {
|
|
749
727
|
id: 'tool_call_nXxXHW8Z',
|
750
728
|
type: 'function',
|
751
729
|
},
|
752
|
-
{
|
753
|
-
function: {
|
754
|
-
arguments: '{"query":"LobeHub","searchEngines":["bilibili"]}',
|
755
|
-
name: 'lobe-web-browsing____searchWithSearXNG____builtin',
|
756
|
-
},
|
757
|
-
id: 'tool_call_2f3CEKz9',
|
758
|
-
type: 'function',
|
759
|
-
},
|
760
730
|
],
|
761
731
|
},
|
762
732
|
{
|
@@ -771,12 +741,6 @@ describe('ChatService', () => {
|
|
771
741
|
role: 'tool',
|
772
742
|
tool_call_id: 'tool_call_nXxXHW8Z',
|
773
743
|
},
|
774
|
-
{
|
775
|
-
content: '[]',
|
776
|
-
name: 'lobe-web-browsing____searchWithSearXNG____builtin',
|
777
|
-
role: 'tool',
|
778
|
-
tool_call_id: 'tool_call_2f3CEKz9',
|
779
|
-
},
|
780
744
|
{
|
781
745
|
content: 'LobeHub 是一个专注于设计和开发现代人工智能生成内容(AIGC)工具和组件的团队。',
|
782
746
|
role: 'assistant',
|
@@ -826,7 +790,6 @@ describe('ChatService', () => {
|
|
826
790
|
},
|
827
791
|
],
|
828
792
|
}, // Message with files
|
829
|
-
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with tool role
|
830
793
|
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
831
794
|
] as ChatMessage[];
|
832
795
|
|
@@ -861,11 +824,6 @@ describe('ChatService', () => {
|
|
861
824
|
],
|
862
825
|
role: 'user',
|
863
826
|
},
|
864
|
-
{
|
865
|
-
content: 'Hi',
|
866
|
-
name: 'plugin1____api1',
|
867
|
-
role: 'tool',
|
868
|
-
},
|
869
827
|
{
|
870
828
|
content: 'Hey',
|
871
829
|
role: 'assistant',
|
@@ -894,7 +852,6 @@ describe('ChatService', () => {
|
|
894
852
|
},
|
895
853
|
],
|
896
854
|
}, // Message with files
|
897
|
-
{ content: 'Hi', role: 'tool', plugin: { identifier: 'plugin1', apiName: 'api1' } }, // Message with tool role
|
898
855
|
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
899
856
|
] as ChatMessage[];
|
900
857
|
|
@@ -929,11 +886,6 @@ describe('ChatService', () => {
|
|
929
886
|
],
|
930
887
|
role: 'user',
|
931
888
|
},
|
932
|
-
{
|
933
|
-
content: 'Hi',
|
934
|
-
name: 'plugin1____api1',
|
935
|
-
role: 'tool',
|
936
|
-
},
|
937
889
|
{
|
938
890
|
content: 'Hey',
|
939
891
|
role: 'assistant',
|
package/src/services/chat.ts
CHANGED
@@ -8,8 +8,14 @@ import { INBOX_SESSION_ID } from '@/const/session';
|
|
8
8
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
9
9
|
import { TracePayload, TraceTagMap } from '@/const/trace';
|
10
10
|
import { isServerMode } from '@/const/version';
|
11
|
-
import {
|
11
|
+
import {
|
12
|
+
AgentRuntime,
|
13
|
+
AgentRuntimeError,
|
14
|
+
ChatCompletionErrorPayload,
|
15
|
+
ModelProvider,
|
16
|
+
} from '@/libs/agent-runtime';
|
12
17
|
import { filesPrompts } from '@/prompts/files';
|
18
|
+
import { BuiltinSystemRolePrompts } from '@/prompts/systemRole';
|
13
19
|
import { useSessionStore } from '@/store/session';
|
14
20
|
import { sessionMetaSelectors } from '@/store/session/selectors';
|
15
21
|
import { useToolStore } from '@/store/tool';
|
@@ -34,6 +40,7 @@ import { createHeaderWithAuth, getProviderAuthPayload } from './_auth';
|
|
34
40
|
import { API_ENDPOINTS } from './_url';
|
35
41
|
|
36
42
|
interface FetchOptions extends FetchSSEOptions {
|
43
|
+
historySummary?: string;
|
37
44
|
isWelcomeQuestion?: boolean;
|
38
45
|
signal?: AbortSignal | undefined;
|
39
46
|
trace?: TracePayload;
|
@@ -60,6 +67,7 @@ interface FetchAITaskResultParams extends FetchSSEOptions {
|
|
60
67
|
|
61
68
|
interface CreateAssistantMessageStream extends FetchSSEOptions {
|
62
69
|
abortController?: AbortController;
|
70
|
+
historySummary?: string;
|
63
71
|
isWelcomeQuestion?: boolean;
|
64
72
|
params: GetChatCompletionPayload;
|
65
73
|
trace?: TracePayload;
|
@@ -234,8 +242,10 @@ class ChatService {
|
|
234
242
|
onFinish,
|
235
243
|
trace,
|
236
244
|
isWelcomeQuestion,
|
245
|
+
historySummary,
|
237
246
|
}: CreateAssistantMessageStream) => {
|
238
247
|
await this.createAssistantMessage(params, {
|
248
|
+
historySummary,
|
239
249
|
isWelcomeQuestion,
|
240
250
|
onAbort,
|
241
251
|
onErrorHandle,
|
@@ -463,7 +473,7 @@ class ChatService {
|
|
463
473
|
}
|
464
474
|
|
465
475
|
default: {
|
466
|
-
return { content: m.content, role: m.role };
|
476
|
+
return { content: m.content, role: m.role as any };
|
467
477
|
}
|
468
478
|
}
|
469
479
|
});
|
@@ -483,9 +493,11 @@ class ChatService {
|
|
483
493
|
const toolsSystemRoles =
|
484
494
|
hasFC && toolSelectors.enabledSystemRoles(tools)(useToolStore.getState());
|
485
495
|
|
486
|
-
const injectSystemRoles =
|
487
|
-
|
488
|
-
|
496
|
+
const injectSystemRoles = BuiltinSystemRolePrompts({
|
497
|
+
historySummary: options?.historySummary,
|
498
|
+
plugins: toolsSystemRoles as string,
|
499
|
+
welcome: inboxGuideSystemRole as string,
|
500
|
+
});
|
489
501
|
|
490
502
|
if (!injectSystemRoles) return;
|
491
503
|
|
@@ -549,18 +561,40 @@ class ChatService {
|
|
549
561
|
* see https://github.com/lobehub/lobe-chat/pull/3155
|
550
562
|
*/
|
551
563
|
private reorderToolMessages = (messages: OpenAIChatMessage[]): OpenAIChatMessage[] => {
|
552
|
-
|
553
|
-
const
|
564
|
+
// 1. 先收集所有 assistant 消息中的有效 tool_call_id
|
565
|
+
const validToolCallIds = new Set<string>();
|
566
|
+
messages.forEach((message) => {
|
567
|
+
if (message.role === 'assistant' && message.tool_calls) {
|
568
|
+
message.tool_calls.forEach((toolCall) => {
|
569
|
+
validToolCallIds.add(toolCall.id);
|
570
|
+
});
|
571
|
+
}
|
572
|
+
});
|
554
573
|
|
555
|
-
//
|
574
|
+
// 2. 收集所有有效的 tool 消息
|
575
|
+
const toolMessages: Record<string, OpenAIChatMessage> = {};
|
556
576
|
messages.forEach((message) => {
|
557
|
-
if (
|
577
|
+
if (
|
578
|
+
message.role === 'tool' &&
|
579
|
+
message.tool_call_id &&
|
580
|
+
validToolCallIds.has(message.tool_call_id)
|
581
|
+
) {
|
558
582
|
toolMessages[message.tool_call_id] = message;
|
559
583
|
}
|
560
584
|
});
|
561
585
|
|
562
|
-
//
|
586
|
+
// 3. 重新排序消息
|
587
|
+
const reorderedMessages: OpenAIChatMessage[] = [];
|
563
588
|
messages.forEach((message) => {
|
589
|
+
// 跳过无效的 tool 消息
|
590
|
+
if (
|
591
|
+
message.role === 'tool' &&
|
592
|
+
(!message.tool_call_id || !validToolCallIds.has(message.tool_call_id))
|
593
|
+
) {
|
594
|
+
return;
|
595
|
+
}
|
596
|
+
|
597
|
+
// 检查是否已经添加过该 tool 消息
|
564
598
|
const hasPushed = reorderedMessages.some(
|
565
599
|
(m) => !!message.tool_call_id && m.tool_call_id === message.tool_call_id,
|
566
600
|
);
|
@@ -569,12 +603,12 @@ class ChatService {
|
|
569
603
|
|
570
604
|
reorderedMessages.push(message);
|
571
605
|
|
606
|
+
// 如果是 assistant 消息且有 tool_calls,添加对应的 tool 消息
|
572
607
|
if (message.role === 'assistant' && message.tool_calls) {
|
573
608
|
message.tool_calls.forEach((toolCall) => {
|
574
609
|
const correspondingToolMessage = toolMessages[toolCall.id];
|
575
610
|
if (correspondingToolMessage) {
|
576
611
|
reorderedMessages.push(correspondingToolMessage);
|
577
|
-
// 从 toolMessages 中删除已处理的消息,避免重复
|
578
612
|
delete toolMessages[toolCall.id];
|
579
613
|
}
|
580
614
|
});
|
@@ -6,7 +6,9 @@ exports[`agentSelectors > defaultAgentConfig > should merge DEFAULT_AGENT_CONFIG
|
|
6
6
|
"autoCreateTopicThreshold": 2,
|
7
7
|
"displayMode": "chat",
|
8
8
|
"enableAutoCreateTopic": true,
|
9
|
-
"
|
9
|
+
"enableCompressHistory": true,
|
10
|
+
"enableHistoryCount": true,
|
11
|
+
"historyCount": 8,
|
10
12
|
},
|
11
13
|
"model": "gpt-3.5-turbo",
|
12
14
|
"params": {
|
@@ -12,15 +12,19 @@ export const getMessageById = (messages: ChatMessage[], id: string) =>
|
|
12
12
|
const getSlicedMessagesWithConfig = (
|
13
13
|
messages: ChatMessage[],
|
14
14
|
config: LobeAgentChatConfig,
|
15
|
+
includeNewUserMessage?: boolean,
|
15
16
|
): ChatMessage[] => {
|
16
17
|
// if historyCount is not enabled or set to 0, return all messages
|
17
18
|
if (!config.enableHistoryCount || !config.historyCount) return messages;
|
18
19
|
|
20
|
+
// if user send message, history will include this message so the total length should +1
|
21
|
+
const messagesCount = !!includeNewUserMessage ? config.historyCount + 1 : config.historyCount;
|
22
|
+
|
19
23
|
// if historyCount is negative, return empty array
|
20
|
-
if (
|
24
|
+
if (messagesCount <= 0) return [];
|
21
25
|
|
22
26
|
// if historyCount is positive, return last N messages
|
23
|
-
return messages.slice(-
|
27
|
+
return messages.slice(-messagesCount);
|
24
28
|
};
|
25
29
|
|
26
30
|
export const chatHelpers = {
|
@@ -5,13 +5,13 @@ import { template } from 'lodash-es';
|
|
5
5
|
import { StateCreator } from 'zustand/vanilla';
|
6
6
|
|
7
7
|
import { LOADING_FLAT, MESSAGE_CANCEL_FLAT } from '@/const/message';
|
8
|
+
import { DEFAULT_AGENT_CHAT_CONFIG } from '@/const/settings';
|
8
9
|
import { TraceEventType, TraceNameMap } from '@/const/trace';
|
9
10
|
import { isServerMode } from '@/const/version';
|
10
11
|
import { knowledgeBaseQAPrompts } from '@/prompts/knowledgeBaseQA';
|
11
12
|
import { chatService } from '@/services/chat';
|
12
13
|
import { messageService } from '@/services/message';
|
13
14
|
import { useAgentStore } from '@/store/agent';
|
14
|
-
import { agentSelectors } from '@/store/agent/selectors';
|
15
15
|
import { chatHelpers } from '@/store/chat/helpers';
|
16
16
|
import { ChatStore } from '@/store/chat/store';
|
17
17
|
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
@@ -21,6 +21,7 @@ import { MessageSemanticSearchChunk } from '@/types/rag';
|
|
21
21
|
import { setNamespace } from '@/utils/storeDebug';
|
22
22
|
|
23
23
|
import { chatSelectors, topicSelectors } from '../../../selectors';
|
24
|
+
import { getAgentChatConfig, getAgentConfig, getAgentKnowledge } from './helpers';
|
24
25
|
|
25
26
|
const n = setNamespace('ai');
|
26
27
|
|
@@ -93,10 +94,6 @@ export interface AIGenerateAction {
|
|
93
94
|
internal_toggleToolCallingStreaming: (id: string, streaming: boolean[] | undefined) => void;
|
94
95
|
}
|
95
96
|
|
96
|
-
const getAgentConfig = () => agentSelectors.currentAgentConfig(useAgentStore.getState());
|
97
|
-
const getAgentChatConfig = () => agentSelectors.currentAgentChatConfig(useAgentStore.getState());
|
98
|
-
const getAgentKnowledge = () => agentSelectors.currentEnabledKnowledge(useAgentStore.getState());
|
99
|
-
|
100
97
|
export const generateAIChat: StateCreator<
|
101
98
|
ChatStore,
|
102
99
|
[['zustand/devtools', never]],
|
@@ -111,7 +108,7 @@ export const generateAIChat: StateCreator<
|
|
111
108
|
// trace the delete and regenerate message
|
112
109
|
get().internal_traceMessage(id, { eventType: TraceEventType.DeleteAndRegenerateMessage });
|
113
110
|
},
|
114
|
-
regenerateMessage: async (id
|
111
|
+
regenerateMessage: async (id) => {
|
115
112
|
const traceId = chatSelectors.getTraceIdByMessageId(id)(get());
|
116
113
|
await get().internal_resendMessage(id, traceId);
|
117
114
|
|
@@ -265,7 +262,7 @@ export const generateAIChat: StateCreator<
|
|
265
262
|
// create a new array to avoid the original messages array change
|
266
263
|
const messages = [...originalMessages];
|
267
264
|
|
268
|
-
const { model, provider } = getAgentConfig();
|
265
|
+
const { model, provider, chatConfig } = getAgentConfig();
|
269
266
|
|
270
267
|
let fileChunks: MessageSemanticSearchChunk[] | undefined;
|
271
268
|
let ragQueryId;
|
@@ -325,6 +322,20 @@ export const generateAIChat: StateCreator<
|
|
325
322
|
await refreshMessages();
|
326
323
|
await triggerToolCalls(assistantId);
|
327
324
|
}
|
325
|
+
|
326
|
+
// 5. summary history if context messages is larger than historyCount
|
327
|
+
const historyCount =
|
328
|
+
chatConfig.historyCount || (DEFAULT_AGENT_CHAT_CONFIG.historyCount as number);
|
329
|
+
|
330
|
+
if (
|
331
|
+
chatConfig.enableHistoryCount &&
|
332
|
+
chatConfig.enableCompressHistory &&
|
333
|
+
originalMessages.length > historyCount
|
334
|
+
) {
|
335
|
+
const historyMessages = originalMessages.slice(0, -historyCount);
|
336
|
+
|
337
|
+
await get().internal_summaryHistory(historyMessages);
|
338
|
+
}
|
328
339
|
},
|
329
340
|
internal_fetchAIChatMessage: async (messages, assistantId, params) => {
|
330
341
|
const {
|
@@ -351,7 +362,7 @@ export const generateAIChat: StateCreator<
|
|
351
362
|
// ================================== //
|
352
363
|
|
353
364
|
// 1. slice messages with config
|
354
|
-
let preprocessMsgs = chatHelpers.getSlicedMessagesWithConfig(messages, chatConfig);
|
365
|
+
let preprocessMsgs = chatHelpers.getSlicedMessagesWithConfig(messages, chatConfig, true);
|
355
366
|
|
356
367
|
// 2. replace inputMessage template
|
357
368
|
preprocessMsgs = !chatConfig.inputTemplate
|
@@ -394,6 +405,7 @@ export const generateAIChat: StateCreator<
|
|
394
405
|
let msgTraceId: string | undefined;
|
395
406
|
let output = '';
|
396
407
|
|
408
|
+
const historySummary = topicSelectors.currentActiveTopicSummary(get());
|
397
409
|
await chatService.createAssistantMessageStream({
|
398
410
|
abortController,
|
399
411
|
params: {
|
@@ -403,6 +415,7 @@ export const generateAIChat: StateCreator<
|
|
403
415
|
...agentConfig.params,
|
404
416
|
plugins: agentConfig.plugins,
|
405
417
|
},
|
418
|
+
historySummary: historySummary?.content,
|
406
419
|
trace: {
|
407
420
|
traceId: params?.traceId,
|
408
421
|
sessionId: get().activeId,
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { useAgentStore } from '@/store/agent';
|
2
|
+
import { agentSelectors } from '@/store/agent/selectors';
|
3
|
+
|
4
|
+
export const getAgentConfig = () => agentSelectors.currentAgentConfig(useAgentStore.getState());
|
5
|
+
export const getAgentChatConfig = () =>
|
6
|
+
agentSelectors.currentAgentChatConfig(useAgentStore.getState());
|
7
|
+
|
8
|
+
export const getAgentKnowledge = () =>
|
9
|
+
agentSelectors.currentEnabledKnowledge(useAgentStore.getState());
|
@@ -3,9 +3,10 @@ import { StateCreator } from 'zustand/vanilla';
|
|
3
3
|
import { ChatStore } from '@/store/chat/store';
|
4
4
|
|
5
5
|
import { AIGenerateAction, generateAIChat } from './generateAIChat';
|
6
|
+
import { ChatMemoryAction, chatMemory } from './memory';
|
6
7
|
import { ChatRAGAction, chatRag } from './rag';
|
7
8
|
|
8
|
-
export interface ChatAIChatAction extends ChatRAGAction, AIGenerateAction {
|
9
|
+
export interface ChatAIChatAction extends ChatRAGAction, ChatMemoryAction, AIGenerateAction {
|
9
10
|
/**/
|
10
11
|
}
|
11
12
|
|
@@ -17,4 +18,5 @@ export const chatAiChat: StateCreator<
|
|
17
18
|
> = (...params) => ({
|
18
19
|
...chatRag(...params),
|
19
20
|
...generateAIChat(...params),
|
21
|
+
...chatMemory(...params),
|
20
22
|
});
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
2
|
+
|
3
|
+
import { chainSummaryHistory } from '@/chains/summaryHistory';
|
4
|
+
import { chatService } from '@/services/chat';
|
5
|
+
import { topicService } from '@/services/topic';
|
6
|
+
import { ChatStore } from '@/store/chat';
|
7
|
+
import { useUserStore } from '@/store/user';
|
8
|
+
import { systemAgentSelectors } from '@/store/user/selectors';
|
9
|
+
import { ChatMessage } from '@/types/message';
|
10
|
+
|
11
|
+
import { getAgentConfig } from './helpers';
|
12
|
+
|
13
|
+
export interface ChatMemoryAction {
|
14
|
+
internal_summaryHistory: (messages: ChatMessage[]) => Promise<void>;
|
15
|
+
}
|
16
|
+
|
17
|
+
export const chatMemory: StateCreator<
|
18
|
+
ChatStore,
|
19
|
+
[['zustand/devtools', never]],
|
20
|
+
[],
|
21
|
+
ChatMemoryAction
|
22
|
+
> = (set, get) => ({
|
23
|
+
internal_summaryHistory: async (messages) => {
|
24
|
+
const topicId = get().activeTopicId;
|
25
|
+
if (messages.length <= 1 || !topicId) return;
|
26
|
+
|
27
|
+
const { model, provider } = getAgentConfig();
|
28
|
+
|
29
|
+
const historyCompressConfig = systemAgentSelectors.historyCompress(useUserStore.getState());
|
30
|
+
|
31
|
+
let historySummary = '';
|
32
|
+
await chatService.fetchPresetTaskResult({
|
33
|
+
onFinish: async (text) => {
|
34
|
+
historySummary = text;
|
35
|
+
},
|
36
|
+
|
37
|
+
params: {
|
38
|
+
...chainSummaryHistory(messages),
|
39
|
+
model: historyCompressConfig.model,
|
40
|
+
provider: historyCompressConfig.provider,
|
41
|
+
stream: false,
|
42
|
+
},
|
43
|
+
});
|
44
|
+
|
45
|
+
await topicService.updateTopic(topicId, {
|
46
|
+
metadata: { model, provider },
|
47
|
+
summary: historySummary,
|
48
|
+
});
|
49
|
+
await get().refreshTopic();
|
50
|
+
await get().refreshMessages();
|
51
|
+
},
|
52
|
+
});
|
@@ -75,9 +75,7 @@ const showInboxWelcome = (s: ChatStoreState): boolean => {
|
|
75
75
|
if (!isInbox) return false;
|
76
76
|
|
77
77
|
const data = currentChats(s);
|
78
|
-
|
79
|
-
|
80
|
-
return isBrandNewChat;
|
78
|
+
return data.length === 0;
|
81
79
|
};
|
82
80
|
|
83
81
|
// Custom message for new assistant initialization
|
@@ -1,36 +1,47 @@
|
|
1
1
|
import { t } from 'i18next';
|
2
2
|
|
3
|
-
import { ChatTopic, GroupedTopic } from '@/types/topic';
|
3
|
+
import { ChatTopic, ChatTopicSummary, GroupedTopic } from '@/types/topic';
|
4
4
|
import { groupTopicsByTime } from '@/utils/client/topic';
|
5
5
|
|
6
|
-
import {
|
6
|
+
import { ChatStoreState } from '../../initialState';
|
7
7
|
|
8
|
-
const currentTopics = (s:
|
8
|
+
const currentTopics = (s: ChatStoreState): ChatTopic[] | undefined => s.topicMaps[s.activeId];
|
9
9
|
|
10
|
-
const currentActiveTopic = (s:
|
10
|
+
const currentActiveTopic = (s: ChatStoreState): ChatTopic | undefined => {
|
11
11
|
return currentTopics(s)?.find((topic) => topic.id === s.activeTopicId);
|
12
12
|
};
|
13
|
-
const searchTopics = (s:
|
13
|
+
const searchTopics = (s: ChatStoreState): ChatTopic[] => s.searchTopics;
|
14
14
|
|
15
|
-
const displayTopics = (s:
|
15
|
+
const displayTopics = (s: ChatStoreState): ChatTopic[] | undefined =>
|
16
16
|
s.isSearchingTopic ? searchTopics(s) : currentTopics(s);
|
17
17
|
|
18
|
-
const currentFavTopics = (s:
|
18
|
+
const currentFavTopics = (s: ChatStoreState): ChatTopic[] =>
|
19
19
|
currentTopics(s)?.filter((s) => s.favorite) || [];
|
20
20
|
|
21
|
-
const currentUnFavTopics = (s:
|
21
|
+
const currentUnFavTopics = (s: ChatStoreState): ChatTopic[] =>
|
22
22
|
currentTopics(s)?.filter((s) => !s.favorite) || [];
|
23
23
|
|
24
|
-
const currentTopicLength = (s:
|
24
|
+
const currentTopicLength = (s: ChatStoreState): number => currentTopics(s)?.length || 0;
|
25
25
|
|
26
26
|
const getTopicById =
|
27
27
|
(id: string) =>
|
28
|
-
(s:
|
28
|
+
(s: ChatStoreState): ChatTopic | undefined =>
|
29
29
|
currentTopics(s)?.find((topic) => topic.id === id);
|
30
30
|
|
31
|
-
const
|
31
|
+
const currentActiveTopicSummary = (s: ChatStoreState): ChatTopicSummary | undefined => {
|
32
|
+
const activeTopic = currentActiveTopic(s);
|
33
|
+
if (!activeTopic) return undefined;
|
32
34
|
|
33
|
-
|
35
|
+
return {
|
36
|
+
content: activeTopic.summary || '',
|
37
|
+
model: activeTopic.metadata?.model || '',
|
38
|
+
provider: activeTopic.metadata?.provider || '',
|
39
|
+
};
|
40
|
+
};
|
41
|
+
|
42
|
+
const isCreatingTopic = (s: ChatStoreState) => s.creatingTopic;
|
43
|
+
|
44
|
+
const groupedTopicsSelector = (s: ChatStoreState): GroupedTopic[] => {
|
34
45
|
const topics = currentTopics(s);
|
35
46
|
|
36
47
|
if (!topics) return [];
|
@@ -51,6 +62,7 @@ const groupedTopicsSelector = (s: ChatStore): GroupedTopic[] => {
|
|
51
62
|
|
52
63
|
export const topicSelectors = {
|
53
64
|
currentActiveTopic,
|
65
|
+
currentActiveTopicSummary,
|
54
66
|
currentTopicLength,
|
55
67
|
currentTopics,
|
56
68
|
currentUnFavTopics,
|
@@ -52,19 +52,6 @@ afterEach(() => {
|
|
52
52
|
});
|
53
53
|
|
54
54
|
describe('ChatEnhanceAction', () => {
|
55
|
-
describe('clearTTS', () => {
|
56
|
-
it('should clear TTS for a message and refresh messages', async () => {
|
57
|
-
const { result } = renderHook(() => useChatStore());
|
58
|
-
const messageId = 'message-id';
|
59
|
-
|
60
|
-
await act(async () => {
|
61
|
-
await result.current.clearTTS(messageId);
|
62
|
-
});
|
63
|
-
|
64
|
-
expect(messageService.updateMessageTTS).toHaveBeenCalledWith(messageId, false);
|
65
|
-
});
|
66
|
-
});
|
67
|
-
|
68
55
|
describe('translateMessage', () => {
|
69
56
|
it('should translate a message to the target language and refresh messages', async () => {
|
70
57
|
const { result } = renderHook(() => useChatStore());
|
@@ -11,38 +11,28 @@ import { chatSelectors } from '@/store/chat/selectors';
|
|
11
11
|
import { ChatStore } from '@/store/chat/store';
|
12
12
|
import { useUserStore } from '@/store/user';
|
13
13
|
import { systemAgentSelectors } from '@/store/user/selectors';
|
14
|
-
import {
|
14
|
+
import { ChatTranslate } from '@/types/message';
|
15
15
|
import { merge } from '@/utils/merge';
|
16
16
|
import { setNamespace } from '@/utils/storeDebug';
|
17
17
|
|
18
18
|
const n = setNamespace('enhance');
|
19
19
|
|
20
20
|
/**
|
21
|
-
*
|
21
|
+
* chat translate
|
22
22
|
*/
|
23
|
-
export interface
|
24
|
-
clearTTS: (id: string) => Promise<void>;
|
23
|
+
export interface ChatTranslateAction {
|
25
24
|
clearTranslate: (id: string) => Promise<void>;
|
26
25
|
getCurrentTracePayload: (data: Partial<TracePayload>) => TracePayload;
|
27
26
|
translateMessage: (id: string, targetLang: string) => Promise<void>;
|
28
|
-
ttsMessage: (
|
29
|
-
id: string,
|
30
|
-
state?: { contentMd5?: string; file?: string; voice?: string },
|
31
|
-
) => Promise<void>;
|
32
|
-
updateMessageTTS: (id: string, data: Partial<ChatTTS> | false) => Promise<void>;
|
33
27
|
updateMessageTranslate: (id: string, data: Partial<ChatTranslate> | false) => Promise<void>;
|
34
28
|
}
|
35
29
|
|
36
|
-
export const
|
30
|
+
export const chatTranslate: StateCreator<
|
37
31
|
ChatStore,
|
38
32
|
[['zustand/devtools', never]],
|
39
33
|
[],
|
40
|
-
|
34
|
+
ChatTranslateAction
|
41
35
|
> = (set, get) => ({
|
42
|
-
clearTTS: async (id) => {
|
43
|
-
await get().updateMessageTTS(id, false);
|
44
|
-
},
|
45
|
-
|
46
36
|
clearTranslate: async (id) => {
|
47
37
|
await get().updateMessageTranslate(id, false);
|
48
38
|
},
|
@@ -107,15 +97,6 @@ export const chatEnhance: StateCreator<
|
|
107
97
|
});
|
108
98
|
},
|
109
99
|
|
110
|
-
ttsMessage: async (id, state = {}) => {
|
111
|
-
await get().updateMessageTTS(id, state);
|
112
|
-
},
|
113
|
-
|
114
|
-
updateMessageTTS: async (id, data) => {
|
115
|
-
await messageService.updateMessageTTS(id, data);
|
116
|
-
await get().refreshMessages();
|
117
|
-
},
|
118
|
-
|
119
100
|
updateMessageTranslate: async (id, data) => {
|
120
101
|
await messageService.updateMessageTranslate(id, data);
|
121
102
|
|