@lobehub/chat 1.56.2 → 1.56.3
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 +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/CategoryContent/useCategory.tsx +2 -2
- package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +1 -1
- package/src/features/AgentSetting/AgentMeta/index.tsx +4 -1
- package/src/server/routers/lambda/agent.test.ts +306 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.56.3](https://github.com/lobehub/lobe-chat/compare/v1.56.2...v1.56.3)
|
6
|
+
|
7
|
+
<sup>Released on **2025-02-16**</sup>
|
8
|
+
|
9
|
+
#### 💄 Styles
|
10
|
+
|
11
|
+
- **misc**: Improve inbox agent settings.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Styles
|
19
|
+
|
20
|
+
- **misc**: Improve inbox agent settings, closes [#6197](https://github.com/lobehub/lobe-chat/issues/6197) ([37b70f0](https://github.com/lobehub/lobe-chat/commit/37b70f0))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.56.2](https://github.com/lobehub/lobe-chat/compare/v1.56.1...v1.56.2)
|
6
31
|
|
7
32
|
<sup>Released on **2025-02-16**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.56.
|
3
|
+
"version": "1.56.3",
|
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",
|
@@ -27,11 +27,11 @@ export const useCategory = ({ mobile }: UseCategoryOptions = {}) => {
|
|
27
27
|
key: ChatSettingsTabs.Meta,
|
28
28
|
label: t('agentTab.meta'),
|
29
29
|
}) as MenuItemType,
|
30
|
-
|
30
|
+
{
|
31
31
|
icon: <Icon icon={Bot} size={iconSize} />,
|
32
32
|
key: ChatSettingsTabs.Prompt,
|
33
33
|
label: t('agentTab.prompt'),
|
34
|
-
}
|
34
|
+
},
|
35
35
|
{
|
36
36
|
icon: <Icon icon={MessagesSquare} size={iconSize} />,
|
37
37
|
key: ChatSettingsTabs.Chat,
|
@@ -41,7 +41,7 @@ const AgentSettings = memo(() => {
|
|
41
41
|
]);
|
42
42
|
const isInbox = id === INBOX_SESSION_ID;
|
43
43
|
|
44
|
-
const [tab, setTab] = useState(isInbox ? ChatSettingsTabs.
|
44
|
+
const [tab, setTab] = useState(isInbox ? ChatSettingsTabs.Prompt : ChatSettingsTabs.Meta);
|
45
45
|
|
46
46
|
const ref = useRef<any>(null);
|
47
47
|
const theme = useTheme();
|
@@ -9,6 +9,7 @@ import { memo } from 'react';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
10
10
|
|
11
11
|
import { FORM_STYLE } from '@/const/layoutTokens';
|
12
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
12
13
|
|
13
14
|
import { useStore } from '../store';
|
14
15
|
import { SessionLoadingState } from '../store/initialState';
|
@@ -26,9 +27,11 @@ const AgentMeta = memo(() => {
|
|
26
27
|
s.autocompleteMeta,
|
27
28
|
s.autocompleteAllMeta,
|
28
29
|
]);
|
29
|
-
const loading = useStore((s) => s.autocompleteLoading);
|
30
|
+
const [isInbox, loading] = useStore((s) => [s.id === INBOX_SESSION_ID, s.autocompleteLoading]);
|
30
31
|
const meta = useStore((s) => s.meta, isEqual);
|
31
32
|
|
33
|
+
if (isInbox) return;
|
34
|
+
|
32
35
|
const basic = [
|
33
36
|
{
|
34
37
|
Render: AutoGenerateInput,
|
@@ -0,0 +1,306 @@
|
|
1
|
+
// @vitest-environment node
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
3
|
+
|
4
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
5
|
+
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
6
|
+
import { serverDB } from '@/database/server';
|
7
|
+
import { AgentModel } from '@/database/server/models/agent';
|
8
|
+
import { FileModel } from '@/database/server/models/file';
|
9
|
+
import { KnowledgeBaseModel } from '@/database/server/models/knowledgeBase';
|
10
|
+
import { SessionModel } from '@/database/server/models/session';
|
11
|
+
import { UserModel } from '@/database/server/models/user';
|
12
|
+
import { AgentService } from '@/server/services/agent';
|
13
|
+
import { KnowledgeType } from '@/types/knowledgeBase';
|
14
|
+
|
15
|
+
import { agentRouter } from './agent';
|
16
|
+
|
17
|
+
vi.mock('@/database/server/models/user', () => ({
|
18
|
+
UserModel: {
|
19
|
+
findById: vi.fn(),
|
20
|
+
},
|
21
|
+
}));
|
22
|
+
|
23
|
+
vi.mock('@/database/server/models/agent', () => ({
|
24
|
+
AgentModel: vi.fn(),
|
25
|
+
}));
|
26
|
+
|
27
|
+
vi.mock('@/database/server/models/session', () => ({
|
28
|
+
SessionModel: vi.fn(),
|
29
|
+
}));
|
30
|
+
|
31
|
+
vi.mock('@/database/server/models/file', () => ({
|
32
|
+
FileModel: vi.fn(),
|
33
|
+
}));
|
34
|
+
|
35
|
+
vi.mock('@/database/server/models/knowledgeBase', () => ({
|
36
|
+
KnowledgeBaseModel: vi.fn(),
|
37
|
+
}));
|
38
|
+
|
39
|
+
vi.mock('@/server/services/agent', () => ({
|
40
|
+
AgentService: vi.fn(),
|
41
|
+
}));
|
42
|
+
|
43
|
+
describe('agentRouter', () => {
|
44
|
+
const userId = 'testUserId';
|
45
|
+
let mockCtx: any;
|
46
|
+
let agentModelMock: any;
|
47
|
+
let sessionModelMock: any;
|
48
|
+
let fileModelMock: any;
|
49
|
+
let knowledgeBaseModelMock: any;
|
50
|
+
let agentServiceMock: any;
|
51
|
+
|
52
|
+
beforeEach(() => {
|
53
|
+
vi.clearAllMocks();
|
54
|
+
|
55
|
+
agentModelMock = {
|
56
|
+
createAgentFiles: vi.fn(),
|
57
|
+
createAgentKnowledgeBase: vi.fn(),
|
58
|
+
deleteAgentFile: vi.fn(),
|
59
|
+
deleteAgentKnowledgeBase: vi.fn(),
|
60
|
+
findBySessionId: vi.fn(),
|
61
|
+
getAgentAssignedKnowledge: vi.fn(),
|
62
|
+
toggleFile: vi.fn(),
|
63
|
+
toggleKnowledgeBase: vi.fn(),
|
64
|
+
};
|
65
|
+
vi.mocked(AgentModel).mockImplementation(() => agentModelMock);
|
66
|
+
|
67
|
+
sessionModelMock = {
|
68
|
+
findByIdOrSlug: vi.fn(),
|
69
|
+
};
|
70
|
+
vi.mocked(SessionModel).mockImplementation(() => sessionModelMock);
|
71
|
+
|
72
|
+
fileModelMock = {
|
73
|
+
query: vi.fn(),
|
74
|
+
};
|
75
|
+
vi.mocked(FileModel).mockImplementation(() => fileModelMock);
|
76
|
+
|
77
|
+
knowledgeBaseModelMock = {
|
78
|
+
query: vi.fn(),
|
79
|
+
};
|
80
|
+
vi.mocked(KnowledgeBaseModel).mockImplementation(() => knowledgeBaseModelMock);
|
81
|
+
|
82
|
+
agentServiceMock = {
|
83
|
+
createInbox: vi.fn(),
|
84
|
+
};
|
85
|
+
vi.mocked(AgentService).mockImplementation(() => agentServiceMock);
|
86
|
+
|
87
|
+
mockCtx = {
|
88
|
+
userId,
|
89
|
+
agentModel: agentModelMock,
|
90
|
+
agentService: agentServiceMock,
|
91
|
+
fileModel: fileModelMock,
|
92
|
+
knowledgeBaseModel: knowledgeBaseModelMock,
|
93
|
+
sessionModel: sessionModelMock,
|
94
|
+
};
|
95
|
+
});
|
96
|
+
|
97
|
+
describe('getAgentConfig', () => {
|
98
|
+
it('should return default config if user not found when getting inbox config', async () => {
|
99
|
+
vi.mocked(UserModel.findById).mockResolvedValue(undefined);
|
100
|
+
sessionModelMock.findByIdOrSlug.mockResolvedValue(undefined);
|
101
|
+
|
102
|
+
const caller = agentRouter.createCaller(mockCtx);
|
103
|
+
const result = await caller.getAgentConfig({ sessionId: INBOX_SESSION_ID });
|
104
|
+
|
105
|
+
expect(result).toEqual(DEFAULT_AGENT_CONFIG);
|
106
|
+
});
|
107
|
+
|
108
|
+
it('should create inbox session if user exists but no inbox session', async () => {
|
109
|
+
const mockUser = { id: userId };
|
110
|
+
const mockSession = { id: 'inboxSessionId' };
|
111
|
+
|
112
|
+
vi.mocked(UserModel.findById).mockResolvedValue(mockUser as any);
|
113
|
+
sessionModelMock.findByIdOrSlug
|
114
|
+
.mockResolvedValueOnce(undefined)
|
115
|
+
.mockResolvedValueOnce(mockSession);
|
116
|
+
agentModelMock.findBySessionId.mockResolvedValue(DEFAULT_AGENT_CONFIG);
|
117
|
+
|
118
|
+
const caller = agentRouter.createCaller(mockCtx);
|
119
|
+
const result = await caller.getAgentConfig({ sessionId: INBOX_SESSION_ID });
|
120
|
+
|
121
|
+
expect(agentServiceMock.createInbox).toHaveBeenCalled();
|
122
|
+
expect(result).toEqual(DEFAULT_AGENT_CONFIG);
|
123
|
+
});
|
124
|
+
|
125
|
+
it('should find agent by session id if session exists', async () => {
|
126
|
+
const mockSession = { id: 'session1' };
|
127
|
+
sessionModelMock.findByIdOrSlug.mockResolvedValue(mockSession);
|
128
|
+
agentModelMock.findBySessionId.mockResolvedValue(DEFAULT_AGENT_CONFIG);
|
129
|
+
|
130
|
+
const caller = agentRouter.createCaller(mockCtx);
|
131
|
+
const result = await caller.getAgentConfig({ sessionId: 'session1' });
|
132
|
+
|
133
|
+
expect(agentModelMock.findBySessionId).toHaveBeenCalledWith('session1');
|
134
|
+
expect(result).toEqual(DEFAULT_AGENT_CONFIG);
|
135
|
+
});
|
136
|
+
});
|
137
|
+
|
138
|
+
describe('getKnowledgeBasesAndFiles', () => {
|
139
|
+
it('should return combined knowledge bases and files', async () => {
|
140
|
+
const mockFiles = [
|
141
|
+
{ id: 'file1', name: 'File 1', fileType: 'text' },
|
142
|
+
{ id: 'file2', name: 'File 2', fileType: 'pdf' },
|
143
|
+
];
|
144
|
+
|
145
|
+
const mockKnowledgeBases = [
|
146
|
+
{ id: 'kb1', name: 'KB 1', description: 'desc 1', avatar: 'avatar1' },
|
147
|
+
{ id: 'kb2', name: 'KB 2', description: 'desc 2', avatar: 'avatar2' },
|
148
|
+
];
|
149
|
+
|
150
|
+
const mockKnowledge = {
|
151
|
+
files: [{ id: 'file1', enabled: true }],
|
152
|
+
knowledgeBases: [{ id: 'kb1', enabled: true }],
|
153
|
+
};
|
154
|
+
|
155
|
+
fileModelMock.query.mockResolvedValue(mockFiles);
|
156
|
+
knowledgeBaseModelMock.query.mockResolvedValue(mockKnowledgeBases);
|
157
|
+
agentModelMock.getAgentAssignedKnowledge.mockResolvedValue(mockKnowledge);
|
158
|
+
|
159
|
+
const caller = agentRouter.createCaller(mockCtx);
|
160
|
+
const result = await caller.getKnowledgeBasesAndFiles({ agentId: 'agent1' });
|
161
|
+
|
162
|
+
expect(result).toEqual([
|
163
|
+
{
|
164
|
+
enabled: true,
|
165
|
+
fileType: 'text',
|
166
|
+
id: 'file1',
|
167
|
+
name: 'File 1',
|
168
|
+
type: KnowledgeType.File,
|
169
|
+
},
|
170
|
+
{
|
171
|
+
enabled: false,
|
172
|
+
fileType: 'pdf',
|
173
|
+
id: 'file2',
|
174
|
+
name: 'File 2',
|
175
|
+
type: KnowledgeType.File,
|
176
|
+
},
|
177
|
+
{
|
178
|
+
avatar: 'avatar1',
|
179
|
+
description: 'desc 1',
|
180
|
+
enabled: true,
|
181
|
+
id: 'kb1',
|
182
|
+
name: 'KB 1',
|
183
|
+
type: KnowledgeType.KnowledgeBase,
|
184
|
+
},
|
185
|
+
{
|
186
|
+
avatar: 'avatar2',
|
187
|
+
description: 'desc 2',
|
188
|
+
enabled: false,
|
189
|
+
id: 'kb2',
|
190
|
+
name: 'KB 2',
|
191
|
+
type: KnowledgeType.KnowledgeBase,
|
192
|
+
},
|
193
|
+
]);
|
194
|
+
});
|
195
|
+
});
|
196
|
+
|
197
|
+
describe('createAgentFiles', () => {
|
198
|
+
it('should create agent files', async () => {
|
199
|
+
const mockInput = {
|
200
|
+
agentId: 'agent1',
|
201
|
+
fileIds: ['file1', 'file2'],
|
202
|
+
enabled: true,
|
203
|
+
};
|
204
|
+
|
205
|
+
const caller = agentRouter.createCaller(mockCtx);
|
206
|
+
await caller.createAgentFiles(mockInput);
|
207
|
+
|
208
|
+
expect(agentModelMock.createAgentFiles).toHaveBeenCalledWith(
|
209
|
+
mockInput.agentId,
|
210
|
+
mockInput.fileIds,
|
211
|
+
mockInput.enabled,
|
212
|
+
);
|
213
|
+
});
|
214
|
+
});
|
215
|
+
|
216
|
+
describe('deleteAgentFile', () => {
|
217
|
+
it('should delete agent file', async () => {
|
218
|
+
const mockInput = {
|
219
|
+
agentId: 'agent1',
|
220
|
+
fileId: 'file1',
|
221
|
+
};
|
222
|
+
|
223
|
+
const caller = agentRouter.createCaller(mockCtx);
|
224
|
+
await caller.deleteAgentFile(mockInput);
|
225
|
+
|
226
|
+
expect(agentModelMock.deleteAgentFile).toHaveBeenCalledWith(
|
227
|
+
mockInput.agentId,
|
228
|
+
mockInput.fileId,
|
229
|
+
);
|
230
|
+
});
|
231
|
+
});
|
232
|
+
|
233
|
+
describe('toggleFile', () => {
|
234
|
+
it('should toggle file', async () => {
|
235
|
+
const mockInput = {
|
236
|
+
agentId: 'agent1',
|
237
|
+
fileId: 'file1',
|
238
|
+
enabled: true,
|
239
|
+
};
|
240
|
+
|
241
|
+
const caller = agentRouter.createCaller(mockCtx);
|
242
|
+
await caller.toggleFile(mockInput);
|
243
|
+
|
244
|
+
expect(agentModelMock.toggleFile).toHaveBeenCalledWith(
|
245
|
+
mockInput.agentId,
|
246
|
+
mockInput.fileId,
|
247
|
+
mockInput.enabled,
|
248
|
+
);
|
249
|
+
});
|
250
|
+
});
|
251
|
+
|
252
|
+
describe('createAgentKnowledgeBase', () => {
|
253
|
+
it('should create agent knowledge base', async () => {
|
254
|
+
const mockInput = {
|
255
|
+
agentId: 'agent1',
|
256
|
+
knowledgeBaseId: 'kb1',
|
257
|
+
enabled: true,
|
258
|
+
};
|
259
|
+
|
260
|
+
const caller = agentRouter.createCaller(mockCtx);
|
261
|
+
await caller.createAgentKnowledgeBase(mockInput);
|
262
|
+
|
263
|
+
expect(agentModelMock.createAgentKnowledgeBase).toHaveBeenCalledWith(
|
264
|
+
mockInput.agentId,
|
265
|
+
mockInput.knowledgeBaseId,
|
266
|
+
mockInput.enabled,
|
267
|
+
);
|
268
|
+
});
|
269
|
+
});
|
270
|
+
|
271
|
+
describe('deleteAgentKnowledgeBase', () => {
|
272
|
+
it('should delete agent knowledge base', async () => {
|
273
|
+
const mockInput = {
|
274
|
+
agentId: 'agent1',
|
275
|
+
knowledgeBaseId: 'kb1',
|
276
|
+
};
|
277
|
+
|
278
|
+
const caller = agentRouter.createCaller(mockCtx);
|
279
|
+
await caller.deleteAgentKnowledgeBase(mockInput);
|
280
|
+
|
281
|
+
expect(agentModelMock.deleteAgentKnowledgeBase).toHaveBeenCalledWith(
|
282
|
+
mockInput.agentId,
|
283
|
+
mockInput.knowledgeBaseId,
|
284
|
+
);
|
285
|
+
});
|
286
|
+
});
|
287
|
+
|
288
|
+
describe('toggleKnowledgeBase', () => {
|
289
|
+
it('should toggle knowledge base', async () => {
|
290
|
+
const mockInput = {
|
291
|
+
agentId: 'agent1',
|
292
|
+
knowledgeBaseId: 'kb1',
|
293
|
+
enabled: true,
|
294
|
+
};
|
295
|
+
|
296
|
+
const caller = agentRouter.createCaller(mockCtx);
|
297
|
+
await caller.toggleKnowledgeBase(mockInput);
|
298
|
+
|
299
|
+
expect(agentModelMock.toggleKnowledgeBase).toHaveBeenCalledWith(
|
300
|
+
mockInput.agentId,
|
301
|
+
mockInput.knowledgeBaseId,
|
302
|
+
mockInput.enabled,
|
303
|
+
);
|
304
|
+
});
|
305
|
+
});
|
306
|
+
});
|