@lobehub/lobehub 2.0.0-next.37 → 2.0.0-next.39
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/apps/desktop/src/main/modules/networkProxy/__tests__/dispatcher.test.ts +401 -0
- package/apps/desktop/src/main/modules/networkProxy/__tests__/tester.test.ts +531 -0
- package/apps/desktop/src/main/modules/networkProxy/__tests__/urlBuilder.test.ts +349 -0
- package/apps/desktop/src/main/modules/networkProxy/__tests__/validator.test.ts +492 -0
- package/changelog/v1.json +14 -0
- package/locales/ar/auth.json +45 -1
- package/locales/ar/modelProvider.json +13 -1
- package/locales/bg-BG/auth.json +45 -1
- package/locales/bg-BG/modelProvider.json +13 -1
- package/locales/de-DE/auth.json +45 -1
- package/locales/de-DE/modelProvider.json +13 -1
- package/locales/en-US/auth.json +45 -1
- package/locales/en-US/modelProvider.json +13 -1
- package/locales/es-ES/auth.json +45 -1
- package/locales/es-ES/modelProvider.json +13 -1
- package/locales/fa-IR/auth.json +45 -1
- package/locales/fa-IR/modelProvider.json +13 -1
- package/locales/fr-FR/auth.json +45 -1
- package/locales/fr-FR/modelProvider.json +13 -1
- package/locales/it-IT/auth.json +45 -1
- package/locales/it-IT/modelProvider.json +13 -1
- package/locales/ja-JP/auth.json +45 -1
- package/locales/ja-JP/modelProvider.json +13 -1
- package/locales/ko-KR/auth.json +45 -1
- package/locales/ko-KR/modelProvider.json +13 -1
- package/locales/nl-NL/auth.json +45 -1
- package/locales/nl-NL/modelProvider.json +13 -1
- package/locales/pl-PL/auth.json +45 -1
- package/locales/pl-PL/modelProvider.json +13 -1
- package/locales/pt-BR/auth.json +45 -1
- package/locales/pt-BR/modelProvider.json +13 -1
- package/locales/ru-RU/auth.json +45 -1
- package/locales/ru-RU/modelProvider.json +13 -1
- package/locales/tr-TR/auth.json +45 -1
- package/locales/tr-TR/modelProvider.json +13 -1
- package/locales/vi-VN/auth.json +45 -1
- package/locales/vi-VN/modelProvider.json +13 -1
- package/locales/zh-CN/auth.json +45 -1
- package/locales/zh-CN/modelProvider.json +13 -1
- package/locales/zh-TW/auth.json +45 -1
- package/locales/zh-TW/modelProvider.json +13 -1
- package/package.json +1 -1
- package/packages/context-engine/src/processors/MessageCleanup.ts +1 -0
- package/packages/context-engine/src/processors/__tests__/MessageCleanup.test.ts +28 -0
- package/packages/obervability-otel/package.json +3 -1
- package/packages/obervability-otel/src/api.ts +2 -0
- package/packages/obervability-otel/src/trpc/convention.ts +16 -0
- package/packages/obervability-otel/src/trpc/index.test.ts +38 -0
- package/packages/obervability-otel/src/trpc/index.ts +62 -0
- package/packages/obervability-otel/src/trpc/metrics.ts +31 -0
- package/packages/types/src/usage/usageRecord.ts +54 -0
- package/packages/web-crawler/src/crawImpl/browserless.ts +1 -1
- package/packages/web-crawler/src/crawImpl/naive.ts +9 -9
- package/packages/web-crawler/src/crawler.ts +5 -5
- package/packages/web-crawler/src/urlRules.ts +13 -13
- package/packages/web-crawler/src/utils/appUrlRules.ts +5 -5
- package/src/app/[variants]/(main)/profile/hooks/useCategory.tsx +10 -1
- package/src/app/[variants]/(main)/profile/usage/Client.tsx +114 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/ModelTable.tsx +175 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/ActiveModels/index.tsx +126 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/MonthSpend.tsx +53 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/TodaySpend.tsx +67 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageCards/index.tsx +19 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageTable.tsx +145 -0
- package/src/app/[variants]/(main)/profile/usage/features/UsageTrends.tsx +107 -0
- package/src/app/[variants]/(main)/profile/usage/features/components/UsageBarChart.tsx +48 -0
- package/src/app/[variants]/(main)/profile/usage/page.tsx +23 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +3 -3
- package/src/features/Conversation/Messages/Group/Actions/WithoutContentId.tsx +37 -14
- package/src/features/Conversation/Messages/Group/Error/index.tsx +1 -1
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +13 -35
- package/src/features/Conversation/Messages/Group/GroupItem.tsx +43 -0
- package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +1 -2
- package/src/features/Conversation/Messages/Group/Tool/Render/CustomRender.tsx +1 -1
- package/src/features/Conversation/Messages/Group/Tool/index.tsx +0 -2
- package/src/features/Conversation/Messages/Group/index.tsx +7 -2
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +3 -0
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +21 -7
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -1
- package/src/features/PluginsUI/Render/MCPType/index.tsx +52 -0
- package/src/features/PluginsUI/Render/StandaloneType/Iframe.tsx +2 -2
- package/src/features/PluginsUI/Render/index.tsx +17 -0
- package/src/libs/mcp/client.ts +3 -2
- package/src/libs/mcp/types.ts +71 -0
- package/src/libs/trpc/lambda/index.ts +5 -2
- package/src/libs/trpc/middleware/openTelemetry.ts +141 -0
- package/src/locales/default/auth.ts +44 -0
- package/src/locales/default/chat.ts +1 -0
- package/src/server/routers/desktop/mcp.ts +1 -3
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/usage.ts +36 -0
- package/src/server/routers/tools/mcp.ts +1 -3
- package/src/server/services/mcp/index.test.ts +28 -15
- package/src/server/services/mcp/index.ts +29 -18
- package/src/server/services/usage/index.test.ts +310 -0
- package/src/server/services/usage/index.ts +164 -0
- package/src/services/chat/contextEngineering.test.ts +4 -0
- package/src/services/mcp.test.ts +7 -1
- package/src/services/mcp.ts +13 -12
- package/src/services/usage.ts +13 -0
- package/src/store/chat/agents/createAgentExecutors.ts +2 -3
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +40 -1
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +13 -5
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +3 -3
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +6 -6
- package/src/store/chat/slices/builtinTool/actions/interpreter.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/search.ts +6 -6
- package/src/store/chat/slices/message/actions/publicApi.ts +19 -1
- package/src/store/chat/slices/message/initialState.ts +5 -0
- package/src/store/chat/slices/message/selectors/chat.test.ts +22 -602
- package/src/store/chat/slices/message/selectors/chat.ts +0 -2
- package/src/store/chat/slices/message/selectors/dbMessage.test.ts +51 -0
- package/src/store/chat/slices/message/selectors/displayMessage.test.ts +818 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +52 -1
- package/src/store/chat/slices/message/selectors/messageState.ts +2 -0
- package/src/store/chat/slices/plugin/action.test.ts +4 -4
- package/src/store/chat/slices/plugin/actions/index.ts +39 -0
- package/src/store/chat/slices/plugin/actions/internals.ts +83 -0
- package/src/store/chat/slices/plugin/actions/optimisticUpdate.ts +188 -0
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +213 -0
- package/src/store/chat/slices/plugin/actions/publicApi.ts +115 -0
- package/src/store/chat/slices/plugin/actions/workflow.ts +121 -0
- package/src/store/chat/store.ts +1 -1
- package/src/store/global/initialState.ts +1 -0
- package/src/store/chat/slices/plugin/action.ts +0 -539
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { UIChatMessage } from '@lobechat/types';
|
|
2
|
-
import { LobeAgentConfig } from '@lobechat/types';
|
|
3
|
-
import { act } from '@testing-library/react';
|
|
4
2
|
import { describe, expect, it } from 'vitest';
|
|
5
3
|
|
|
6
|
-
import { DEFAULT_INBOX_AVATAR } from '@/const/meta';
|
|
7
|
-
import { INBOX_SESSION_ID } from '@/const/session';
|
|
8
|
-
import { useAgentStore } from '@/store/agent';
|
|
9
4
|
import { ChatStore } from '@/store/chat';
|
|
10
5
|
import { initialState } from '@/store/chat/initialState';
|
|
11
6
|
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
|
@@ -15,7 +10,7 @@ import { merge } from '@/utils/merge';
|
|
|
15
10
|
import { chatSelectors } from './chat';
|
|
16
11
|
|
|
17
12
|
vi.mock('i18next', () => ({
|
|
18
|
-
t: vi.fn((key) => key),
|
|
13
|
+
t: vi.fn((key) => key),
|
|
19
14
|
}));
|
|
20
15
|
|
|
21
16
|
const initialStore = initialState as ChatStore;
|
|
@@ -31,108 +26,15 @@ const mockMessages = [
|
|
|
31
26
|
content: 'Goodbye World',
|
|
32
27
|
role: 'user',
|
|
33
28
|
},
|
|
34
|
-
{
|
|
35
|
-
id: 'msg3',
|
|
36
|
-
content: 'Function Message',
|
|
37
|
-
role: 'tool',
|
|
38
|
-
tools: [
|
|
39
|
-
{
|
|
40
|
-
arguments: ['arg1', 'arg2'],
|
|
41
|
-
identifier: 'func1',
|
|
42
|
-
apiName: 'ttt',
|
|
43
|
-
type: 'pluginType',
|
|
44
|
-
id: 'abc',
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
] as UIChatMessage[];
|
|
49
|
-
|
|
50
|
-
const mockReasoningMessages = [
|
|
51
|
-
{
|
|
52
|
-
id: 'msg1',
|
|
53
|
-
content: 'Hello World',
|
|
54
|
-
role: 'user',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: 'msg2',
|
|
58
|
-
content: 'Goodbye World',
|
|
59
|
-
role: 'user',
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
id: 'msg3',
|
|
63
|
-
content: 'Content Message',
|
|
64
|
-
role: 'assistant',
|
|
65
|
-
reasoning: {
|
|
66
|
-
content: 'Reasoning Content',
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
] as UIChatMessage[];
|
|
70
|
-
|
|
71
|
-
const mockedChats = [
|
|
72
|
-
{
|
|
73
|
-
id: 'msg1',
|
|
74
|
-
content: 'Hello World',
|
|
75
|
-
role: 'user',
|
|
76
|
-
meta: {
|
|
77
|
-
avatar: '😀',
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
id: 'msg2',
|
|
82
|
-
content: 'Goodbye World',
|
|
83
|
-
role: 'user',
|
|
84
|
-
meta: {
|
|
85
|
-
avatar: '😀',
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
id: 'msg3',
|
|
90
|
-
content: 'Function Message',
|
|
91
|
-
role: 'tool',
|
|
92
|
-
meta: {
|
|
93
|
-
avatar: DEFAULT_INBOX_AVATAR,
|
|
94
|
-
backgroundColor: 'rgba(0,0,0,0)',
|
|
95
|
-
description: 'inbox.desc',
|
|
96
|
-
title: 'inbox.title',
|
|
97
|
-
},
|
|
98
|
-
tools: [
|
|
99
|
-
{
|
|
100
|
-
arguments: ['arg1', 'arg2'],
|
|
101
|
-
identifier: 'func1',
|
|
102
|
-
apiName: 'ttt',
|
|
103
|
-
type: 'pluginType',
|
|
104
|
-
id: 'abc',
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
},
|
|
108
29
|
] as UIChatMessage[];
|
|
109
30
|
|
|
110
|
-
const mockChatStore = {
|
|
111
|
-
messagesMap: {
|
|
112
|
-
[messageMapKey('abc')]: mockMessages,
|
|
113
|
-
},
|
|
114
|
-
activeId: 'abc',
|
|
115
|
-
} as ChatStore;
|
|
116
|
-
|
|
117
31
|
beforeAll(() => {
|
|
118
32
|
createServerConfigStore();
|
|
119
33
|
});
|
|
120
34
|
|
|
121
|
-
|
|
122
|
-
const store = createServerConfigStore();
|
|
123
|
-
store.setState((state) => ({
|
|
124
|
-
featureFlags: { ...state.featureFlags, isAgentEditable: true },
|
|
125
|
-
}));
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('chatSelectors', () => {
|
|
35
|
+
describe('chatSelectors - Backward Compatibility Layer', () => {
|
|
129
36
|
describe('getMessageById', () => {
|
|
130
|
-
it('should
|
|
131
|
-
const message = chatSelectors.getMessageById('non-existent-id')(initialStore);
|
|
132
|
-
expect(message).toBeUndefined();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('should return the message object with the matching id', () => {
|
|
37
|
+
it('should work as backward compatibility alias for getDisplayMessageById', () => {
|
|
136
38
|
const state = merge(initialStore, {
|
|
137
39
|
messagesMap: {
|
|
138
40
|
[messageMapKey('abc')]: mockMessages,
|
|
@@ -140,218 +42,13 @@ describe('chatSelectors', () => {
|
|
|
140
42
|
activeId: 'abc',
|
|
141
43
|
});
|
|
142
44
|
const message = chatSelectors.getMessageById('msg1')(state);
|
|
143
|
-
expect(message).
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
it('should return the message with the matching id', () => {
|
|
147
|
-
const message = chatSelectors.getMessageById('msg1')(mockChatStore);
|
|
148
|
-
expect(message).toEqual(mockedChats[0]);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('should return undefined if no message matches the id', () => {
|
|
152
|
-
const message = chatSelectors.getMessageById('nonexistent')(mockChatStore);
|
|
153
|
-
expect(message).toBeUndefined();
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
describe('currentChatsWithHistoryConfig', () => {
|
|
158
|
-
it('should slice the messages according to the current agent config', () => {
|
|
159
|
-
const state = merge(initialStore, {
|
|
160
|
-
messagesMap: {
|
|
161
|
-
[messageMapKey('abc')]: mockMessages,
|
|
162
|
-
},
|
|
163
|
-
activeId: 'abc',
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const chats = chatSelectors.mainAIChatsWithHistoryConfig(state);
|
|
167
|
-
expect(chats).toHaveLength(3);
|
|
168
|
-
expect(chats).toEqual(mockedChats);
|
|
169
|
-
});
|
|
170
|
-
it('should slice the messages according to config, assuming historyCount is mocked to 2', async () => {
|
|
171
|
-
const state = merge(initialStore, {
|
|
172
|
-
messagesMap: {
|
|
173
|
-
[messageMapKey('abc')]: mockMessages,
|
|
174
|
-
},
|
|
175
|
-
activeId: 'abc',
|
|
176
|
-
});
|
|
177
|
-
act(() => {
|
|
178
|
-
useAgentStore.setState({
|
|
179
|
-
activeId: 'inbox',
|
|
180
|
-
agentMap: {
|
|
181
|
-
inbox: {
|
|
182
|
-
chatConfig: {
|
|
183
|
-
historyCount: 2,
|
|
184
|
-
enableHistoryCount: true,
|
|
185
|
-
},
|
|
186
|
-
model: 'abc',
|
|
187
|
-
} as LobeAgentConfig,
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const chats = chatSelectors.mainAIChatsWithHistoryConfig(state);
|
|
193
|
-
|
|
194
|
-
expect(chats).toHaveLength(2);
|
|
195
|
-
expect(chats).toEqual([
|
|
196
|
-
{
|
|
197
|
-
id: 'msg2',
|
|
198
|
-
content: 'Goodbye World',
|
|
199
|
-
role: 'user',
|
|
200
|
-
meta: {
|
|
201
|
-
avatar: '😀',
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
id: 'msg3',
|
|
206
|
-
content: 'Function Message',
|
|
207
|
-
role: 'tool',
|
|
208
|
-
meta: {
|
|
209
|
-
avatar: DEFAULT_INBOX_AVATAR,
|
|
210
|
-
backgroundColor: 'rgba(0,0,0,0)',
|
|
211
|
-
description: 'inbox.desc',
|
|
212
|
-
title: 'inbox.title',
|
|
213
|
-
},
|
|
214
|
-
tools: [
|
|
215
|
-
{
|
|
216
|
-
apiName: 'ttt',
|
|
217
|
-
arguments: ['arg1', 'arg2'],
|
|
218
|
-
identifier: 'func1',
|
|
219
|
-
id: 'abc',
|
|
220
|
-
type: 'pluginType',
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
},
|
|
224
|
-
]);
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
describe('chatsMessageString', () => {
|
|
229
|
-
it('should concatenate the contents of all messages returned by currentChatsWithHistoryConfig', () => {
|
|
230
|
-
// Prepare a state with a few messages
|
|
231
|
-
const state = merge(initialStore, {
|
|
232
|
-
messagesMap: {
|
|
233
|
-
[messageMapKey('active-session')]: mockMessages,
|
|
234
|
-
},
|
|
235
|
-
activeId: 'active-session',
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// Assume that the currentChatsWithHistoryConfig will return the last two messages
|
|
239
|
-
const expectedString = mockMessages
|
|
240
|
-
.slice(-2)
|
|
241
|
-
.map((m) => m.content)
|
|
242
|
-
.join('');
|
|
243
|
-
|
|
244
|
-
// Call the selector and verify the result
|
|
245
|
-
const concatenatedString = chatSelectors.mainAIChatsMessageString(state);
|
|
246
|
-
expect(concatenatedString).toBe(expectedString);
|
|
247
|
-
|
|
248
|
-
// Restore the mocks after the test
|
|
249
|
-
vi.restoreAllMocks();
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
describe('latestMessageReasoningContent', () => {
|
|
254
|
-
it('should return the reasoning content of the latest message', () => {
|
|
255
|
-
// Prepare a state with a few messages
|
|
256
|
-
const state = merge(initialStore, {
|
|
257
|
-
messagesMap: {
|
|
258
|
-
[messageMapKey('active-session')]: mockReasoningMessages,
|
|
259
|
-
},
|
|
260
|
-
activeId: 'active-session',
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
const expectedString = mockReasoningMessages.at(-1)?.reasoning?.content;
|
|
264
|
-
|
|
265
|
-
// Call the selector and verify the result
|
|
266
|
-
const reasoningContent = chatSelectors.mainAILatestMessageReasoningContent(state);
|
|
267
|
-
expect(reasoningContent).toBe(expectedString);
|
|
268
|
-
|
|
269
|
-
// Restore the mocks after the test
|
|
270
|
-
vi.restoreAllMocks();
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
describe('showInboxWelcome', () => {
|
|
275
|
-
it('should return false if the active session is not the inbox session', () => {
|
|
276
|
-
const state = merge(initialStore, { activeId: 'someActiveId' });
|
|
277
|
-
const result = chatSelectors.showInboxWelcome(state);
|
|
278
|
-
expect(result).toBe(false);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('should return false if there are existing messages in the inbox session', () => {
|
|
282
|
-
const state = merge(initialStore, {
|
|
283
|
-
activeId: INBOX_SESSION_ID,
|
|
284
|
-
messagesMap: {
|
|
285
|
-
[messageMapKey('inbox')]: mockMessages,
|
|
286
|
-
},
|
|
287
|
-
});
|
|
288
|
-
const result = chatSelectors.showInboxWelcome(state);
|
|
289
|
-
expect(result).toBe(false);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it('should return true if the active session is the inbox session and there are no existing messages', () => {
|
|
293
|
-
const state = merge(initialStore, {
|
|
294
|
-
activeId: INBOX_SESSION_ID,
|
|
295
|
-
messages: [],
|
|
296
|
-
});
|
|
297
|
-
const result = chatSelectors.showInboxWelcome(state);
|
|
298
|
-
expect(result).toBe(true);
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe('currentToolMessages', () => {
|
|
303
|
-
it('should return only tool messages', () => {
|
|
304
|
-
const messages = [
|
|
305
|
-
{ id: '1', role: 'user', content: 'Hello' },
|
|
306
|
-
{ id: '2', role: 'assistant', content: 'Hi' },
|
|
307
|
-
{ id: '3', role: 'tool', content: 'Tool message 1' },
|
|
308
|
-
{ id: '4', role: 'user', content: 'Query' },
|
|
309
|
-
{ id: '5', role: 'tool', tools: [] },
|
|
310
|
-
] as UIChatMessage[];
|
|
311
|
-
const state: Partial<ChatStore> = {
|
|
312
|
-
activeId: 'test-id',
|
|
313
|
-
dbMessagesMap: {
|
|
314
|
-
[messageMapKey('test-id')]: messages,
|
|
315
|
-
},
|
|
316
|
-
messagesMap: {
|
|
317
|
-
[messageMapKey('test-id')]: messages,
|
|
318
|
-
},
|
|
319
|
-
};
|
|
320
|
-
const result = chatSelectors.currentToolMessages(state as ChatStore);
|
|
321
|
-
expect(result).toHaveLength(2);
|
|
322
|
-
expect(result.every((msg) => msg.role === 'tool')).toBe(true);
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it('should return an empty array when no tool messages exist', () => {
|
|
326
|
-
const messages = [
|
|
327
|
-
{ id: '1', role: 'user', content: 'Hello' },
|
|
328
|
-
{ id: '2', role: 'assistant', content: 'Hi' },
|
|
329
|
-
] as UIChatMessage[];
|
|
330
|
-
const state: Partial<ChatStore> = {
|
|
331
|
-
activeId: 'test-id',
|
|
332
|
-
dbMessagesMap: {
|
|
333
|
-
[messageMapKey('test-id')]: messages,
|
|
334
|
-
},
|
|
335
|
-
messagesMap: {
|
|
336
|
-
[messageMapKey('test-id')]: messages,
|
|
337
|
-
},
|
|
338
|
-
};
|
|
339
|
-
const result = chatSelectors.currentToolMessages(state as ChatStore);
|
|
340
|
-
expect(result).toHaveLength(0);
|
|
45
|
+
expect(message?.id).toBe('msg1');
|
|
46
|
+
expect(message?.content).toBe('Hello World');
|
|
341
47
|
});
|
|
342
48
|
});
|
|
343
49
|
|
|
344
50
|
describe('currentChatKey', () => {
|
|
345
|
-
it('should
|
|
346
|
-
const state: Partial<ChatStore> = {
|
|
347
|
-
activeId: 'testId',
|
|
348
|
-
activeTopicId: undefined,
|
|
349
|
-
};
|
|
350
|
-
const result = chatSelectors.currentChatKey(state as ChatStore);
|
|
351
|
-
expect(result).toBe(messageMapKey('testId', undefined));
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it('should generate correct key with both activeId and activeTopicId', () => {
|
|
51
|
+
it('should work as backward compatibility alias for currentDisplayChatKey', () => {
|
|
355
52
|
const state: Partial<ChatStore> = {
|
|
356
53
|
activeId: 'testId',
|
|
357
54
|
activeTopicId: 'topicId',
|
|
@@ -359,314 +56,37 @@ describe('chatSelectors', () => {
|
|
|
359
56
|
const result = chatSelectors.currentChatKey(state as ChatStore);
|
|
360
57
|
expect(result).toBe(messageMapKey('testId', 'topicId'));
|
|
361
58
|
});
|
|
362
|
-
|
|
363
|
-
it('should generate key with undefined activeId', () => {
|
|
364
|
-
const state: Partial<ChatStore> = {
|
|
365
|
-
activeId: undefined,
|
|
366
|
-
activeTopicId: 'topicId',
|
|
367
|
-
};
|
|
368
|
-
const result = chatSelectors.currentChatKey(state as ChatStore);
|
|
369
|
-
expect(result).toBe(messageMapKey(undefined as any, 'topicId'));
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it('should generate key with empty string activeId', () => {
|
|
373
|
-
const state: Partial<ChatStore> = {
|
|
374
|
-
activeId: '',
|
|
375
|
-
activeTopicId: undefined,
|
|
376
|
-
};
|
|
377
|
-
const result = chatSelectors.currentChatKey(state as ChatStore);
|
|
378
|
-
expect(result).toBe(messageMapKey('', undefined));
|
|
379
|
-
});
|
|
380
59
|
});
|
|
381
60
|
|
|
382
|
-
describe('activeBaseChats
|
|
383
|
-
it('should
|
|
384
|
-
const groupChatMessages = [
|
|
385
|
-
{
|
|
386
|
-
id: 'msg1',
|
|
387
|
-
content: 'Hello from agent',
|
|
388
|
-
role: 'assistant',
|
|
389
|
-
groupId: 'group-123',
|
|
390
|
-
agentId: 'agent-456',
|
|
391
|
-
},
|
|
392
|
-
] as UIChatMessage[];
|
|
393
|
-
|
|
61
|
+
describe('activeBaseChats', () => {
|
|
62
|
+
it('should work as backward compatibility alias for activeDisplayMessages', () => {
|
|
394
63
|
const state = merge(initialStore, {
|
|
395
64
|
messagesMap: {
|
|
396
|
-
[messageMapKey('
|
|
65
|
+
[messageMapKey('abc')]: mockMessages,
|
|
397
66
|
},
|
|
398
|
-
activeId: '
|
|
67
|
+
activeId: 'abc',
|
|
399
68
|
});
|
|
400
|
-
|
|
401
69
|
const chats = chatSelectors.activeBaseChats(state);
|
|
402
|
-
expect(chats).toHaveLength(
|
|
70
|
+
expect(chats).toHaveLength(2);
|
|
403
71
|
expect(chats[0].id).toBe('msg1');
|
|
404
|
-
expect(chats[0].meta).toBeDefined();
|
|
405
72
|
});
|
|
406
73
|
});
|
|
407
74
|
|
|
408
|
-
describe('
|
|
409
|
-
it('should
|
|
410
|
-
const
|
|
411
|
-
id: '
|
|
412
|
-
role: '
|
|
413
|
-
|
|
414
|
-
children: [
|
|
415
|
-
{
|
|
416
|
-
id: 'child-1',
|
|
417
|
-
content: 'First response',
|
|
418
|
-
tools: [
|
|
419
|
-
{
|
|
420
|
-
id: 'tool-1',
|
|
421
|
-
identifier: 'test',
|
|
422
|
-
apiName: 'test',
|
|
423
|
-
arguments: '{}',
|
|
424
|
-
type: 'default',
|
|
425
|
-
},
|
|
426
|
-
],
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
id: 'child-2',
|
|
430
|
-
content: 'Second response',
|
|
431
|
-
tools: [],
|
|
432
|
-
},
|
|
433
|
-
{
|
|
434
|
-
id: 'child-3',
|
|
435
|
-
content: 'Final response',
|
|
436
|
-
},
|
|
437
|
-
],
|
|
438
|
-
} as UIChatMessage;
|
|
439
|
-
|
|
440
|
-
const state: Partial<ChatStore> = {
|
|
441
|
-
activeId: 'test-id',
|
|
442
|
-
messagesMap: {
|
|
443
|
-
[messageMapKey('test-id')]: [groupMessage],
|
|
444
|
-
},
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('group-1')(state as ChatStore);
|
|
448
|
-
expect(result).toBeDefined();
|
|
449
|
-
expect(result?.id).toBe('child-3');
|
|
450
|
-
expect(result?.content).toBe('Final response');
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it('should return null if the last child has tools', () => {
|
|
454
|
-
const groupMessage = {
|
|
455
|
-
id: 'group-2',
|
|
456
|
-
role: 'assistantGroup',
|
|
457
|
-
content: '',
|
|
458
|
-
children: [
|
|
459
|
-
{
|
|
460
|
-
id: 'child-1',
|
|
461
|
-
content: 'First response',
|
|
462
|
-
},
|
|
463
|
-
{
|
|
464
|
-
id: 'child-2',
|
|
465
|
-
content: 'Second response with tools',
|
|
466
|
-
tools: [
|
|
467
|
-
{
|
|
468
|
-
id: 'tool-1',
|
|
469
|
-
identifier: 'test',
|
|
470
|
-
apiName: 'test',
|
|
471
|
-
arguments: '{}',
|
|
472
|
-
type: 'default',
|
|
473
|
-
},
|
|
474
|
-
],
|
|
475
|
-
},
|
|
476
|
-
],
|
|
477
|
-
} as UIChatMessage;
|
|
478
|
-
|
|
479
|
-
const state: Partial<ChatStore> = {
|
|
480
|
-
activeId: 'test-id',
|
|
481
|
-
messagesMap: {
|
|
482
|
-
[messageMapKey('test-id')]: [groupMessage],
|
|
483
|
-
},
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('group-2')(state as ChatStore);
|
|
487
|
-
expect(result).toBeUndefined();
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
it('should return the last child when it has empty tools array', () => {
|
|
491
|
-
const groupMessage = {
|
|
492
|
-
id: 'group-3',
|
|
493
|
-
role: 'assistantGroup',
|
|
494
|
-
content: '',
|
|
495
|
-
children: [
|
|
496
|
-
{
|
|
497
|
-
id: 'child-1',
|
|
498
|
-
content: 'First response with tools',
|
|
499
|
-
tools: [
|
|
500
|
-
{
|
|
501
|
-
id: 'tool-1',
|
|
502
|
-
identifier: 'test',
|
|
503
|
-
apiName: 'test',
|
|
504
|
-
arguments: '{}',
|
|
505
|
-
type: 'default',
|
|
506
|
-
},
|
|
507
|
-
],
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
id: 'child-2',
|
|
511
|
-
content: 'Final response',
|
|
512
|
-
tools: [],
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
} as UIChatMessage;
|
|
516
|
-
|
|
517
|
-
const state: Partial<ChatStore> = {
|
|
518
|
-
activeId: 'test-id',
|
|
519
|
-
messagesMap: {
|
|
520
|
-
[messageMapKey('test-id')]: [groupMessage],
|
|
521
|
-
},
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('group-3')(state as ChatStore);
|
|
525
|
-
expect(result).toBeDefined();
|
|
526
|
-
expect(result?.id).toBe('child-2');
|
|
527
|
-
expect(result?.content).toBe('Final response');
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
it('should return null for non-group messages', () => {
|
|
531
|
-
const assistantMessage = {
|
|
532
|
-
id: 'msg-1',
|
|
533
|
-
role: 'assistant',
|
|
534
|
-
content: 'Regular message',
|
|
535
|
-
} as UIChatMessage;
|
|
536
|
-
|
|
537
|
-
const state: Partial<ChatStore> = {
|
|
538
|
-
activeId: 'test-id',
|
|
539
|
-
messagesMap: {
|
|
540
|
-
[messageMapKey('test-id')]: [assistantMessage],
|
|
541
|
-
},
|
|
542
|
-
};
|
|
543
|
-
|
|
544
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('msg-1')(state as ChatStore);
|
|
545
|
-
expect(result).toBeUndefined();
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
it('should return null for group messages without children', () => {
|
|
549
|
-
const groupMessage = {
|
|
550
|
-
id: 'group-4',
|
|
551
|
-
role: 'assistantGroup',
|
|
552
|
-
content: '',
|
|
553
|
-
children: undefined,
|
|
554
|
-
} as UIChatMessage;
|
|
555
|
-
|
|
556
|
-
const state: Partial<ChatStore> = {
|
|
557
|
-
activeId: 'test-id',
|
|
558
|
-
messagesMap: {
|
|
559
|
-
[messageMapKey('test-id')]: [groupMessage],
|
|
560
|
-
},
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('group-4')(state as ChatStore);
|
|
564
|
-
expect(result).toBeUndefined();
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
it('should return null for group messages with empty children array', () => {
|
|
568
|
-
const groupMessage = {
|
|
569
|
-
id: 'group-5',
|
|
570
|
-
role: 'assistantGroup',
|
|
571
|
-
content: '',
|
|
572
|
-
children: [],
|
|
573
|
-
} as unknown as UIChatMessage;
|
|
574
|
-
|
|
575
|
-
const state: Partial<ChatStore> = {
|
|
576
|
-
activeId: 'test-id',
|
|
577
|
-
messagesMap: {
|
|
578
|
-
[messageMapKey('test-id')]: [groupMessage],
|
|
579
|
-
},
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('group-5')(state as ChatStore);
|
|
583
|
-
expect(result).toBeUndefined();
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
it('should return null if all children have tools', () => {
|
|
587
|
-
const groupMessage = {
|
|
588
|
-
id: 'group-6',
|
|
589
|
-
role: 'assistantGroup',
|
|
590
|
-
content: '',
|
|
591
|
-
children: [
|
|
592
|
-
{
|
|
593
|
-
id: 'child-1',
|
|
594
|
-
content: 'First response',
|
|
595
|
-
tools: [
|
|
596
|
-
{
|
|
597
|
-
id: 'tool-1',
|
|
598
|
-
identifier: 'test',
|
|
599
|
-
apiName: 'test',
|
|
600
|
-
arguments: '{}',
|
|
601
|
-
type: 'default',
|
|
602
|
-
},
|
|
603
|
-
],
|
|
604
|
-
},
|
|
605
|
-
{
|
|
606
|
-
id: 'child-2',
|
|
607
|
-
content: 'Second response',
|
|
608
|
-
tools: [
|
|
609
|
-
{
|
|
610
|
-
id: 'tool-2',
|
|
611
|
-
identifier: 'test2',
|
|
612
|
-
apiName: 'test2',
|
|
613
|
-
arguments: '{}',
|
|
614
|
-
type: 'default',
|
|
615
|
-
},
|
|
616
|
-
],
|
|
617
|
-
},
|
|
618
|
-
],
|
|
619
|
-
} as unknown as UIChatMessage;
|
|
620
|
-
|
|
621
|
-
const state: Partial<ChatStore> = {
|
|
622
|
-
activeId: 'test-id',
|
|
623
|
-
messagesMap: {
|
|
624
|
-
[messageMapKey('test-id')]: [groupMessage],
|
|
625
|
-
},
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('group-6')(state as ChatStore);
|
|
629
|
-
expect(result).toBeUndefined();
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
it('should handle empty tools array as no tools', () => {
|
|
633
|
-
const groupMessage = {
|
|
634
|
-
id: 'group-7',
|
|
635
|
-
role: 'assistantGroup',
|
|
636
|
-
content: '',
|
|
637
|
-
children: [
|
|
638
|
-
{
|
|
639
|
-
id: 'child-1',
|
|
640
|
-
content: 'Response with empty tools',
|
|
641
|
-
tools: [],
|
|
642
|
-
},
|
|
643
|
-
],
|
|
644
|
-
} as unknown as UIChatMessage;
|
|
645
|
-
|
|
646
|
-
const state: Partial<ChatStore> = {
|
|
647
|
-
activeId: 'test-id',
|
|
648
|
-
messagesMap: {
|
|
649
|
-
[messageMapKey('test-id')]: [groupMessage],
|
|
650
|
-
},
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
const result = chatSelectors.getGroupLatestMessageWithoutTools('group-7')(state as ChatStore);
|
|
654
|
-
expect(result).toBeDefined();
|
|
655
|
-
expect(result?.id).toBe('child-1');
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
it('should return null when message is not found', () => {
|
|
75
|
+
describe('currentToolMessages', () => {
|
|
76
|
+
it('should work as backward compatibility alias for dbToolMessages', () => {
|
|
77
|
+
const messages = [
|
|
78
|
+
{ id: '1', role: 'user', content: 'Hello' },
|
|
79
|
+
{ id: '2', role: 'tool', content: 'Tool message' },
|
|
80
|
+
] as UIChatMessage[];
|
|
659
81
|
const state: Partial<ChatStore> = {
|
|
660
82
|
activeId: 'test-id',
|
|
661
|
-
|
|
662
|
-
[messageMapKey('test-id')]:
|
|
83
|
+
dbMessagesMap: {
|
|
84
|
+
[messageMapKey('test-id')]: messages,
|
|
663
85
|
},
|
|
664
86
|
};
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
);
|
|
669
|
-
expect(result).toBeUndefined();
|
|
87
|
+
const result = chatSelectors.currentToolMessages(state as ChatStore);
|
|
88
|
+
expect(result).toHaveLength(1);
|
|
89
|
+
expect(result[0].role).toBe('tool');
|
|
670
90
|
});
|
|
671
91
|
});
|
|
672
92
|
});
|
|
@@ -76,8 +76,6 @@ export const chatSelectors = {
|
|
|
76
76
|
|
|
77
77
|
getBaseChatsByKey: displayMessageSelectors.getDisplayMessagesByKey,
|
|
78
78
|
|
|
79
|
-
getGroupLatestMessageWithoutTools: displayMessageSelectors.getGroupLatestMessageWithoutTools,
|
|
80
|
-
|
|
81
79
|
getMessageById: displayMessageSelectors.getDisplayMessageById,
|
|
82
80
|
|
|
83
81
|
getSupervisorTodos: displayMessageSelectors.getSupervisorTodos,
|