@lobehub/chat 0.147.15 → 0.147.17
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/contributing/Basic/Feature-Development.md +7 -7
- package/contributing/Basic/Feature-Development.zh-CN.md +7 -7
- package/next.config.mjs +61 -13
- package/package.json +2 -1
- package/sentry.client.config.ts +30 -0
- package/sentry.edge.config.ts +17 -0
- package/sentry.server.config.ts +19 -0
- package/src/app/api/auth/[...nextauth]/route.ts +1 -1
- package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +7 -1
- package/src/app/chat/(desktop)/features/ChatInput/TextArea.tsx +7 -1
- package/src/app/home/Redirect.tsx +2 -2
- package/src/app/layout.tsx +2 -2
- package/src/config/server/app.ts +0 -18
- package/src/config/server/auth.ts +71 -0
- package/src/config/server/index.ts +3 -1
- package/src/database/client/models/__tests__/session.test.ts +1 -3
- package/src/database/client/models/session.ts +4 -4
- package/src/layout/AuthProvider/NextAuth/index.tsx +10 -0
- package/src/layout/AuthProvider/index.tsx +3 -7
- package/src/layout/DefaultLayout/Desktop/SideBar/Avatar.tsx +11 -0
- package/src/layout/{GlobalLayout → DefaultLayout}/Desktop/SideBar/index.tsx +2 -2
- package/src/layout/{GlobalLayout/Mobile/Client.tsx → DefaultLayout/Mobile/index.tsx} +2 -0
- package/src/layout/DefaultLayout/index.ts +2 -0
- package/src/layout/{GlobalLayout/Desktop/index.tsx → routes/Desktop.tsx} +5 -3
- package/src/layout/{GlobalLayout/Mobile/index.tsx → routes/Mobile.tsx} +3 -4
- package/src/layout/{GlobalLayout → routes}/index.tsx +2 -2
- package/src/{app/api/auth/next-auth.ts → libs/next-auth/index.ts} +1 -1
- package/src/middleware.ts +1 -1
- package/src/services/config.ts +4 -4
- package/src/services/file/client.test.ts +2 -2
- package/src/services/file/client.ts +35 -33
- package/src/services/file/index.ts +8 -2
- package/src/services/file/type.ts +11 -0
- package/src/services/message/client.test.ts +6 -32
- package/src/services/message/client.ts +24 -37
- package/src/services/message/index.test.ts +48 -0
- package/src/services/message/index.ts +22 -2
- package/src/services/message/type.ts +33 -0
- package/src/services/plugin/client.test.ts +2 -2
- package/src/services/plugin/client.ts +1 -1
- package/src/services/plugin/index.ts +9 -3
- package/src/services/session/client.test.ts +37 -44
- package/src/services/session/client.ts +30 -22
- package/src/services/session/index.ts +9 -2
- package/src/services/session/type.ts +44 -0
- package/src/services/topic/client.test.ts +18 -22
- package/src/services/topic/client.ts +31 -23
- package/src/services/topic/index.ts +10 -2
- package/src/services/topic/type.ts +32 -0
- package/src/services/user/client.ts +1 -1
- package/src/services/user/index.ts +10 -2
- package/src/store/chat/slices/message/action.test.ts +12 -12
- package/src/store/chat/slices/message/action.ts +4 -4
- package/src/store/chat/slices/plugin/action.test.ts +5 -6
- package/src/store/chat/slices/plugin/action.ts +1 -1
- package/src/store/chat/slices/topic/action.test.ts +11 -6
- package/src/store/chat/slices/topic/action.ts +7 -5
- package/src/store/session/slices/agent/action.test.ts +175 -0
- package/src/store/session/slices/agent/action.ts +1 -1
- package/src/store/session/slices/session/action.test.ts +14 -15
- package/src/store/session/slices/session/action.ts +4 -4
- package/src/store/session/slices/sessionGroup/action.test.ts +6 -4
- package/src/store/session/slices/sessionGroup/action.ts +3 -3
- /package/src/layout/{GlobalLayout → DefaultLayout}/Desktop/SideBar/BottomActions.tsx +0 -0
- /package/src/layout/{GlobalLayout → DefaultLayout}/Desktop/SideBar/TopActions.tsx +0 -0
- /package/src/layout/{GlobalLayout/Desktop/Client.tsx → DefaultLayout/Desktop/index.tsx} +0 -0
- /package/src/{app/api/auth → libs/next-auth}/sso-providers/auth0.ts +0 -0
- /package/src/{app/api/auth → libs/next-auth}/sso-providers/authentik.ts +0 -0
- /package/src/{app/api/auth → libs/next-auth}/sso-providers/azure-ad.ts +0 -0
- /package/src/{app/api/auth → libs/next-auth}/sso-providers/github.ts +0 -0
- /package/src/{app/api/auth → libs/next-auth}/sso-providers/index.ts +0 -0
- /package/src/{app/api/auth → libs/next-auth}/sso-providers/zitadel.ts +0 -0
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
import { usePathname } from 'next/navigation';
|
|
4
4
|
import { PropsWithChildren, memo } from 'react';
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { DefaultLayoutDesktop } from '@/layout/DefaultLayout';
|
|
7
|
+
|
|
8
|
+
const defaultLayoutRoutes = new Set(['/']);
|
|
7
9
|
|
|
8
10
|
const DesktopLayout = memo<PropsWithChildren>(({ children }) => {
|
|
9
11
|
const pathname = usePathname();
|
|
10
12
|
|
|
11
|
-
if (pathname
|
|
13
|
+
if (defaultLayoutRoutes.has(pathname)) return children;
|
|
12
14
|
|
|
13
|
-
return <
|
|
15
|
+
return <DefaultLayoutDesktop>{children}</DefaultLayoutDesktop>;
|
|
14
16
|
});
|
|
15
17
|
|
|
16
18
|
export default DesktopLayout;
|
|
@@ -5,8 +5,7 @@ import { PropsWithChildren, memo } from 'react';
|
|
|
5
5
|
|
|
6
6
|
import { useActiveTabKey } from '@/hooks/useActiveTabKey';
|
|
7
7
|
import { useIsSubSlug } from '@/hooks/useIsSubSlug';
|
|
8
|
-
|
|
9
|
-
import Layout from './Client';
|
|
8
|
+
import { DefaultLayoutMobile } from '@/layout/DefaultLayout';
|
|
10
9
|
|
|
11
10
|
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
12
11
|
const pathname = usePathname();
|
|
@@ -16,9 +15,9 @@ const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
|
16
15
|
if (pathname === '/') return children;
|
|
17
16
|
|
|
18
17
|
return (
|
|
19
|
-
<
|
|
18
|
+
<DefaultLayoutMobile showTabBar={!isSubPath} tabBarKey={tabBarKey}>
|
|
20
19
|
{children}
|
|
21
|
-
</
|
|
20
|
+
</DefaultLayoutMobile>
|
|
22
21
|
);
|
|
23
22
|
});
|
|
24
23
|
|
|
@@ -3,6 +3,6 @@ import ServerLayout from '@/components/server/ServerLayout';
|
|
|
3
3
|
import Desktop from './Desktop';
|
|
4
4
|
import Mobile from './Mobile';
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const LayoutRoutes = ServerLayout({ Desktop, Mobile });
|
|
7
7
|
|
|
8
|
-
export default
|
|
8
|
+
export default LayoutRoutes;
|
|
@@ -32,7 +32,7 @@ const nextAuth = NextAuth({
|
|
|
32
32
|
async session({ session, token }) {
|
|
33
33
|
// Pick userid from token
|
|
34
34
|
if (session.user) {
|
|
35
|
-
session.user.id = token.userId ?? session.user.id;
|
|
35
|
+
session.user.id = (token.userId ?? session.user.id) as string;
|
|
36
36
|
}
|
|
37
37
|
return session;
|
|
38
38
|
},
|
package/src/middleware.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
2
|
|
|
3
3
|
import { getServerConfig } from '@/config/server';
|
|
4
|
+
import { auth } from '@/libs/next-auth';
|
|
4
5
|
|
|
5
|
-
import { auth } from './app/api/auth/next-auth';
|
|
6
6
|
import { OAUTH_AUTHORIZED } from './const/auth';
|
|
7
7
|
|
|
8
8
|
export const config = {
|
package/src/services/config.ts
CHANGED
|
@@ -33,7 +33,7 @@ class ConfigService {
|
|
|
33
33
|
return await sessionService.batchCreateSessions(sessions);
|
|
34
34
|
};
|
|
35
35
|
importMessages = async (messages: ChatMessage[]) => {
|
|
36
|
-
return messageService.
|
|
36
|
+
return messageService.batchCreateMessages(messages);
|
|
37
37
|
};
|
|
38
38
|
importSettings = async (settings: GlobalSettings) => {
|
|
39
39
|
useGlobalStore.getState().importAppSettings(settings);
|
|
@@ -105,7 +105,7 @@ class ConfigService {
|
|
|
105
105
|
* export all agents
|
|
106
106
|
*/
|
|
107
107
|
exportAgents = async () => {
|
|
108
|
-
const agents = await sessionService.
|
|
108
|
+
const agents = await sessionService.getSessionsByType('agent');
|
|
109
109
|
const sessionGroups = await sessionService.getSessionGroups();
|
|
110
110
|
|
|
111
111
|
const config = createConfigFile('agents', { sessionGroups, sessions: agents });
|
|
@@ -117,7 +117,7 @@ class ConfigService {
|
|
|
117
117
|
* export all sessions
|
|
118
118
|
*/
|
|
119
119
|
exportSessions = async () => {
|
|
120
|
-
const sessions = await sessionService.
|
|
120
|
+
const sessions = await sessionService.getSessionsByType();
|
|
121
121
|
const sessionGroups = await sessionService.getSessionGroups();
|
|
122
122
|
const messages = await messageService.getAllMessages();
|
|
123
123
|
const topics = await topicService.getAllTopics();
|
|
@@ -188,7 +188,7 @@ class ConfigService {
|
|
|
188
188
|
* export all data
|
|
189
189
|
*/
|
|
190
190
|
exportAll = async () => {
|
|
191
|
-
const sessions = await sessionService.
|
|
191
|
+
const sessions = await sessionService.getSessionsByType();
|
|
192
192
|
const sessionGroups = await sessionService.getSessionGroups();
|
|
193
193
|
const messages = await messageService.getAllMessages();
|
|
194
194
|
const topics = await topicService.getAllTopics();
|
|
@@ -3,9 +3,9 @@ import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
3
3
|
import { FileModel } from '@/database/client/models/file';
|
|
4
4
|
import { DB_File } from '@/database/client/schemas/files';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ClientService } from './client';
|
|
7
7
|
|
|
8
|
-
const fileService = new
|
|
8
|
+
const fileService = new ClientService();
|
|
9
9
|
|
|
10
10
|
// Mocks for the FileModel
|
|
11
11
|
vi.mock('@/database/client/models/file', () => ({
|
|
@@ -4,12 +4,9 @@ import { FilePreview } from '@/types/files';
|
|
|
4
4
|
import compressImage from '@/utils/compressImage';
|
|
5
5
|
|
|
6
6
|
import { API_ENDPOINTS } from '../_url';
|
|
7
|
+
import { IFileService } from './type';
|
|
7
8
|
|
|
8
|
-
export class
|
|
9
|
-
private isImage(fileType: string) {
|
|
10
|
-
const imageRegex = /^image\//;
|
|
11
|
-
return imageRegex.test(fileType);
|
|
12
|
-
}
|
|
9
|
+
export class ClientService implements IFileService {
|
|
13
10
|
async uploadFile(file: DB_File) {
|
|
14
11
|
// 跳过图片上传测试
|
|
15
12
|
const isTestData = file.size === 1;
|
|
@@ -22,26 +19,6 @@ export class FileService {
|
|
|
22
19
|
return FileModel.create(file);
|
|
23
20
|
}
|
|
24
21
|
|
|
25
|
-
async uploadImageFile(file: DB_File) {
|
|
26
|
-
// 加载图片
|
|
27
|
-
const url = file.url || URL.createObjectURL(new Blob([file.data]));
|
|
28
|
-
|
|
29
|
-
const img = new Image();
|
|
30
|
-
img.src = url;
|
|
31
|
-
await (() =>
|
|
32
|
-
new Promise((resolve) => {
|
|
33
|
-
img.addEventListener('load', resolve);
|
|
34
|
-
}))();
|
|
35
|
-
|
|
36
|
-
// 压缩图片
|
|
37
|
-
const base64String = compressImage({ img, type: file.fileType });
|
|
38
|
-
const binaryString = atob(base64String.split('base64,')[1]);
|
|
39
|
-
const uint8Array = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
|
|
40
|
-
file.data = uint8Array.buffer;
|
|
41
|
-
|
|
42
|
-
return FileModel.create(file);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
22
|
async uploadImageByUrl(url: string, file: Pick<DB_File, 'name' | 'metadata'>) {
|
|
46
23
|
const res = await fetch(API_ENDPOINTS.proxy, { body: url, method: 'POST' });
|
|
47
24
|
const data = await res.arrayBuffer();
|
|
@@ -57,14 +34,6 @@ export class FileService {
|
|
|
57
34
|
});
|
|
58
35
|
}
|
|
59
36
|
|
|
60
|
-
async removeFile(id: string) {
|
|
61
|
-
return FileModel.delete(id);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async removeAllFiles() {
|
|
65
|
-
return FileModel.clear();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
37
|
async getFile(id: string): Promise<FilePreview> {
|
|
69
38
|
const item = await FileModel.findById(id);
|
|
70
39
|
if (!item) {
|
|
@@ -83,4 +52,37 @@ export class FileService {
|
|
|
83
52
|
url,
|
|
84
53
|
};
|
|
85
54
|
}
|
|
55
|
+
|
|
56
|
+
async removeFile(id: string) {
|
|
57
|
+
return FileModel.delete(id);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async removeAllFiles() {
|
|
61
|
+
return FileModel.clear();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private isImage(fileType: string) {
|
|
65
|
+
const imageRegex = /^image\//;
|
|
66
|
+
return imageRegex.test(fileType);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async uploadImageFile(file: DB_File) {
|
|
70
|
+
// 加载图片
|
|
71
|
+
const url = file.url || URL.createObjectURL(new Blob([file.data]));
|
|
72
|
+
|
|
73
|
+
const img = new Image();
|
|
74
|
+
img.src = url;
|
|
75
|
+
await (() =>
|
|
76
|
+
new Promise((resolve) => {
|
|
77
|
+
img.addEventListener('load', resolve);
|
|
78
|
+
}))();
|
|
79
|
+
|
|
80
|
+
// 压缩图片
|
|
81
|
+
const base64String = compressImage({ img, type: file.fileType });
|
|
82
|
+
const binaryString = atob(base64String.split('base64,')[1]);
|
|
83
|
+
const uint8Array = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
|
|
84
|
+
file.data = uint8Array.buffer;
|
|
85
|
+
|
|
86
|
+
return FileModel.create(file);
|
|
87
|
+
}
|
|
86
88
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
// import { getClientConfig } from '@/config/client';
|
|
2
|
+
import { ClientService } from './client';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
// import { ServerService } from './server';
|
|
5
|
+
//
|
|
6
|
+
// const { ENABLED_SERVER_SERVICE } = getClientConfig();
|
|
7
|
+
//
|
|
8
|
+
// export const fileService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
|
|
9
|
+
export const fileService = new ClientService();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* eslint-disable typescript-sort-keys/interface */
|
|
2
|
+
import { DB_File } from '@/database/client/schemas/files';
|
|
3
|
+
import { FilePreview } from '@/types/files';
|
|
4
|
+
|
|
5
|
+
export interface IFileService {
|
|
6
|
+
uploadFile(file: DB_File): Promise<any>;
|
|
7
|
+
uploadImageByUrl(url: string, file: Pick<DB_File, 'name' | 'metadata'>): Promise<any>;
|
|
8
|
+
removeFile(id: string): Promise<any>;
|
|
9
|
+
removeAllFiles(): Promise<any>;
|
|
10
|
+
getFile(id: string): Promise<FilePreview>;
|
|
11
|
+
}
|
|
@@ -3,9 +3,9 @@ import { Mock, describe, expect, it, vi } from 'vitest';
|
|
|
3
3
|
import { CreateMessageParams, MessageModel } from '@/database/client/models/message';
|
|
4
4
|
import { ChatMessage, ChatMessageError, ChatPluginPayload } from '@/types/message';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ClientService } from './client';
|
|
7
7
|
|
|
8
|
-
const messageService = new
|
|
8
|
+
const messageService = new ClientService();
|
|
9
9
|
|
|
10
10
|
// Mock the MessageModel
|
|
11
11
|
vi.mock('@/database/client/models/message', () => {
|
|
@@ -27,7 +27,7 @@ vi.mock('@/database/client/models/message', () => {
|
|
|
27
27
|
};
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
describe('
|
|
30
|
+
describe('MessageClientService', () => {
|
|
31
31
|
// Mock data
|
|
32
32
|
const mockMessageId = 'mock-message-id';
|
|
33
33
|
const mockMessage = {
|
|
@@ -57,7 +57,7 @@ describe('MessageService', () => {
|
|
|
57
57
|
(MessageModel.create as Mock).mockResolvedValue({ id: mockMessageId });
|
|
58
58
|
|
|
59
59
|
// Execute
|
|
60
|
-
const messageId = await messageService.
|
|
60
|
+
const messageId = await messageService.createMessage(createParams);
|
|
61
61
|
|
|
62
62
|
// Assert
|
|
63
63
|
expect(MessageModel.create).toHaveBeenCalledWith(createParams);
|
|
@@ -71,7 +71,7 @@ describe('MessageService', () => {
|
|
|
71
71
|
(MessageModel.batchCreate as Mock).mockResolvedValue(mockMessages);
|
|
72
72
|
|
|
73
73
|
// Execute
|
|
74
|
-
const result = await messageService.
|
|
74
|
+
const result = await messageService.batchCreateMessages(mockMessages);
|
|
75
75
|
|
|
76
76
|
// Assert
|
|
77
77
|
expect(MessageModel.batchCreate).toHaveBeenCalledWith(mockMessages);
|
|
@@ -79,32 +79,6 @@ describe('MessageService', () => {
|
|
|
79
79
|
});
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
describe('hasMessages', () => {
|
|
83
|
-
it('should return true if there are messages', async () => {
|
|
84
|
-
// Setup
|
|
85
|
-
(MessageModel.count as Mock).mockResolvedValue(1);
|
|
86
|
-
|
|
87
|
-
// Execute
|
|
88
|
-
const hasMessages = await messageService.hasMessages();
|
|
89
|
-
|
|
90
|
-
// Assert
|
|
91
|
-
expect(MessageModel.count).toHaveBeenCalled();
|
|
92
|
-
expect(hasMessages).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('should return false if there are no messages', async () => {
|
|
96
|
-
// Setup
|
|
97
|
-
(MessageModel.count as Mock).mockResolvedValue(0);
|
|
98
|
-
|
|
99
|
-
// Execute
|
|
100
|
-
const hasMessages = await messageService.hasMessages();
|
|
101
|
-
|
|
102
|
-
// Assert
|
|
103
|
-
expect(MessageModel.count).toHaveBeenCalled();
|
|
104
|
-
expect(hasMessages).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
82
|
describe('removeMessage', () => {
|
|
109
83
|
it('should remove a message by id', async () => {
|
|
110
84
|
// Setup
|
|
@@ -172,7 +146,7 @@ describe('MessageService', () => {
|
|
|
172
146
|
(MessageModel.clearTable as Mock).mockResolvedValue(true);
|
|
173
147
|
|
|
174
148
|
// Execute
|
|
175
|
-
const result = await messageService.
|
|
149
|
+
const result = await messageService.removeAllMessages();
|
|
176
150
|
|
|
177
151
|
// Assert
|
|
178
152
|
expect(MessageModel.clearTable).toHaveBeenCalled();
|
|
@@ -2,42 +2,29 @@ import { MessageModel } from '@/database/client/models/message';
|
|
|
2
2
|
import { DB_Message } from '@/database/client/schemas/message';
|
|
3
3
|
import { ChatMessage, ChatMessageError, ChatPluginPayload } from '@/types/message';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
extends Partial<Omit<ChatMessage, 'content' | 'role'>>,
|
|
7
|
-
Pick<ChatMessage, 'content' | 'role'> {
|
|
8
|
-
fromModel?: string;
|
|
9
|
-
fromProvider?: string;
|
|
10
|
-
sessionId: string;
|
|
11
|
-
traceId?: string;
|
|
12
|
-
}
|
|
5
|
+
import { CreateMessageParams, IMessageService } from './type';
|
|
13
6
|
|
|
14
|
-
export class
|
|
15
|
-
async
|
|
7
|
+
export class ClientService implements IMessageService {
|
|
8
|
+
async createMessage(data: CreateMessageParams) {
|
|
16
9
|
const { id } = await MessageModel.create(data);
|
|
17
10
|
|
|
18
11
|
return id;
|
|
19
12
|
}
|
|
20
13
|
|
|
21
|
-
async
|
|
14
|
+
async batchCreateMessages(messages: ChatMessage[]) {
|
|
22
15
|
return MessageModel.batchCreate(messages);
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
async hasMessages() {
|
|
26
|
-
const number = await MessageModel.count();
|
|
27
|
-
return number > 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async messageCountToCheckTrace() {
|
|
31
|
-
const number = await MessageModel.count();
|
|
32
|
-
return number >= 4;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
18
|
async getMessages(sessionId: string, topicId?: string): Promise<ChatMessage[]> {
|
|
36
19
|
return MessageModel.query({ sessionId, topicId });
|
|
37
20
|
}
|
|
38
21
|
|
|
39
|
-
async
|
|
40
|
-
return MessageModel.
|
|
22
|
+
async getAllMessages() {
|
|
23
|
+
return MessageModel.queryAll();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async countMessages() {
|
|
27
|
+
return MessageModel.count();
|
|
41
28
|
}
|
|
42
29
|
|
|
43
30
|
async getAllMessagesInSession(sessionId: string) {
|
|
@@ -48,18 +35,6 @@ export class MessageService {
|
|
|
48
35
|
return MessageModel.update(id, { error });
|
|
49
36
|
}
|
|
50
37
|
|
|
51
|
-
async removeMessages(assistantId: string, topicId?: string) {
|
|
52
|
-
return MessageModel.batchDelete(assistantId, topicId);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async clearAllMessage() {
|
|
56
|
-
return MessageModel.clearTable();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async bindMessagesToTopic(topicId: string, messageIds: string[]) {
|
|
60
|
-
return MessageModel.batchUpdate(messageIds, { topicId });
|
|
61
|
-
}
|
|
62
|
-
|
|
63
38
|
async updateMessage(id: string, message: Partial<DB_Message>) {
|
|
64
39
|
return MessageModel.update(id, message);
|
|
65
40
|
}
|
|
@@ -72,7 +47,19 @@ export class MessageService {
|
|
|
72
47
|
return MessageModel.updatePluginState(id, key, value);
|
|
73
48
|
}
|
|
74
49
|
|
|
75
|
-
async
|
|
76
|
-
return MessageModel.
|
|
50
|
+
async bindMessagesToTopic(topicId: string, messageIds: string[]) {
|
|
51
|
+
return MessageModel.batchUpdate(messageIds, { topicId });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async removeMessage(id: string) {
|
|
55
|
+
return MessageModel.delete(id);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async removeMessages(assistantId: string, topicId?: string) {
|
|
59
|
+
return MessageModel.batchDelete(assistantId, topicId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async removeAllMessages() {
|
|
63
|
+
return MessageModel.clearTable();
|
|
77
64
|
}
|
|
78
65
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Mock, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { CreateMessageParams, MessageModel } from '@/database/client/models/message';
|
|
4
|
+
import { ChatMessage, ChatMessageError, ChatPluginPayload } from '@/types/message';
|
|
5
|
+
|
|
6
|
+
import { messageService } from './index';
|
|
7
|
+
|
|
8
|
+
// Mock the MessageModel
|
|
9
|
+
vi.mock('@/database/client/models/message', () => {
|
|
10
|
+
return {
|
|
11
|
+
MessageModel: {
|
|
12
|
+
count: vi.fn(),
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('MessageService', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
// Reset all mocks before running each test case
|
|
20
|
+
vi.resetAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('hasMessages', () => {
|
|
24
|
+
it('should return true if there are messages', async () => {
|
|
25
|
+
// Setup
|
|
26
|
+
(MessageModel.count as Mock).mockResolvedValue(1);
|
|
27
|
+
|
|
28
|
+
// Execute
|
|
29
|
+
const hasMessages = await messageService.hasMessages();
|
|
30
|
+
|
|
31
|
+
// Assert
|
|
32
|
+
expect(MessageModel.count).toHaveBeenCalled();
|
|
33
|
+
expect(hasMessages).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should return false if there are no messages', async () => {
|
|
37
|
+
// Setup
|
|
38
|
+
(MessageModel.count as Mock).mockResolvedValue(0);
|
|
39
|
+
|
|
40
|
+
// Execute
|
|
41
|
+
const hasMessages = await messageService.hasMessages();
|
|
42
|
+
|
|
43
|
+
// Assert
|
|
44
|
+
expect(MessageModel.count).toHaveBeenCalled();
|
|
45
|
+
expect(hasMessages).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
// import { getClientConfig } from '@/config/client';
|
|
2
|
+
// import { ServerService } from './server';
|
|
3
|
+
// import { ClientService } from './client';
|
|
4
|
+
//
|
|
5
|
+
// const { ENABLED_SERVER_SERVICE } = getClientConfig();
|
|
6
|
+
//
|
|
7
|
+
// export const messageService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
|
|
8
|
+
import { ClientService } from './client';
|
|
9
|
+
|
|
10
|
+
export type { CreateMessageParams } from './type';
|
|
11
|
+
|
|
12
|
+
class MessageService extends ClientService {
|
|
13
|
+
async hasMessages() {
|
|
14
|
+
const number = await this.countMessages();
|
|
15
|
+
return number > 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async messageCountToCheckTrace() {
|
|
19
|
+
const number = await this.countMessages();
|
|
20
|
+
return number >= 4;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
2
23
|
|
|
3
24
|
export const messageService = new MessageService();
|
|
4
|
-
export type { CreateMessageParams } from './client';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DB_Message } from '@/database/client/schemas/message';
|
|
2
|
+
import { ChatMessage, ChatMessageError, ChatPluginPayload } from '@/types/message';
|
|
3
|
+
|
|
4
|
+
/* eslint-disable typescript-sort-keys/interface */
|
|
5
|
+
|
|
6
|
+
export interface CreateMessageParams
|
|
7
|
+
extends Partial<Omit<ChatMessage, 'content' | 'role'>>,
|
|
8
|
+
Pick<ChatMessage, 'content' | 'role'> {
|
|
9
|
+
fromModel?: string;
|
|
10
|
+
fromProvider?: string;
|
|
11
|
+
sessionId: string;
|
|
12
|
+
traceId?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IMessageService {
|
|
16
|
+
createMessage(data: CreateMessageParams): Promise<string>;
|
|
17
|
+
batchCreateMessages(messages: ChatMessage[]): Promise<any>;
|
|
18
|
+
|
|
19
|
+
getMessages(sessionId: string, topicId?: string): Promise<ChatMessage[]>;
|
|
20
|
+
getAllMessages(): Promise<ChatMessage[]>;
|
|
21
|
+
getAllMessagesInSession(sessionId: string): Promise<ChatMessage[]>;
|
|
22
|
+
countMessages(): Promise<number>;
|
|
23
|
+
|
|
24
|
+
updateMessageError(id: string, error: ChatMessageError): Promise<any>;
|
|
25
|
+
updateMessage(id: string, message: Partial<DB_Message>): Promise<any>;
|
|
26
|
+
updateMessagePlugin(id: string, plugin: ChatPluginPayload): Promise<any>;
|
|
27
|
+
updateMessagePluginState(id: string, key: string, value: any): Promise<any>;
|
|
28
|
+
bindMessagesToTopic(topicId: string, messageIds: string[]): Promise<any>;
|
|
29
|
+
|
|
30
|
+
removeMessage(id: string): Promise<any>;
|
|
31
|
+
removeMessages(assistantId: string, topicId?: string): Promise<any>;
|
|
32
|
+
removeAllMessages(): Promise<any>;
|
|
33
|
+
}
|
|
@@ -6,9 +6,9 @@ import { DB_Plugin } from '@/database/client/schemas/plugin';
|
|
|
6
6
|
import { LobeTool } from '@/types/tool';
|
|
7
7
|
import { LobeToolCustomPlugin } from '@/types/tool/plugin';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { ClientService, InstallPluginParams } from './client';
|
|
10
10
|
|
|
11
|
-
const pluginService = new
|
|
11
|
+
const pluginService = new ClientService();
|
|
12
12
|
|
|
13
13
|
// Mocking modules and functions
|
|
14
14
|
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
// import { getClientConfig } from '@/config/client';
|
|
2
|
+
import { ClientService } from './client';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
// import { ServerService } from './server';
|
|
5
|
+
//
|
|
6
|
+
// export type { InstallPluginParams } from './client';
|
|
7
|
+
//
|
|
8
|
+
// const { ENABLED_SERVER_SERVICE } = getClientConfig();
|
|
4
9
|
|
|
5
|
-
export const pluginService = new
|
|
10
|
+
// export const pluginService = ENABLED_SERVER_SERVICE ? new ServerService() : new ClientService();
|
|
11
|
+
export const pluginService = new ClientService();
|