@lobehub/chat 1.81.3 → 1.81.5
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/.eslintrc.js +1 -0
- package/.github/workflows/release.yml +5 -0
- package/.github/workflows/test.yml +5 -0
- package/CHANGELOG.md +58 -0
- package/changelog/v1.json +21 -0
- package/locales/ar/common.json +2 -0
- package/locales/ar/electron.json +32 -0
- package/locales/ar/models.json +129 -3
- package/locales/ar/plugin.json +1 -0
- package/locales/ar/tool.json +25 -0
- package/locales/bg-BG/common.json +2 -0
- package/locales/bg-BG/electron.json +32 -0
- package/locales/bg-BG/models.json +129 -3
- package/locales/bg-BG/plugin.json +1 -0
- package/locales/bg-BG/tool.json +25 -0
- package/locales/de-DE/common.json +2 -0
- package/locales/de-DE/electron.json +32 -0
- package/locales/de-DE/models.json +129 -3
- package/locales/de-DE/plugin.json +1 -0
- package/locales/de-DE/tool.json +25 -0
- package/locales/en-US/common.json +2 -0
- package/locales/en-US/electron.json +32 -0
- package/locales/en-US/models.json +129 -3
- package/locales/en-US/plugin.json +1 -0
- package/locales/en-US/tool.json +25 -0
- package/locales/es-ES/common.json +2 -0
- package/locales/es-ES/electron.json +32 -0
- package/locales/es-ES/models.json +129 -3
- package/locales/es-ES/plugin.json +1 -0
- package/locales/es-ES/tool.json +25 -0
- package/locales/fa-IR/common.json +2 -0
- package/locales/fa-IR/electron.json +32 -0
- package/locales/fa-IR/models.json +129 -3
- package/locales/fa-IR/plugin.json +1 -0
- package/locales/fa-IR/tool.json +25 -0
- package/locales/fr-FR/common.json +2 -0
- package/locales/fr-FR/electron.json +32 -0
- package/locales/fr-FR/models.json +129 -3
- package/locales/fr-FR/plugin.json +1 -0
- package/locales/fr-FR/tool.json +25 -0
- package/locales/it-IT/common.json +2 -0
- package/locales/it-IT/electron.json +32 -0
- package/locales/it-IT/models.json +129 -3
- package/locales/it-IT/plugin.json +1 -0
- package/locales/it-IT/tool.json +25 -0
- package/locales/ja-JP/common.json +2 -0
- package/locales/ja-JP/electron.json +32 -0
- package/locales/ja-JP/models.json +129 -3
- package/locales/ja-JP/plugin.json +1 -0
- package/locales/ja-JP/tool.json +25 -0
- package/locales/ko-KR/common.json +2 -0
- package/locales/ko-KR/electron.json +32 -0
- package/locales/ko-KR/models.json +129 -3
- package/locales/ko-KR/plugin.json +1 -0
- package/locales/ko-KR/tool.json +25 -0
- package/locales/nl-NL/common.json +2 -0
- package/locales/nl-NL/electron.json +32 -0
- package/locales/nl-NL/models.json +129 -3
- package/locales/nl-NL/plugin.json +1 -0
- package/locales/nl-NL/tool.json +25 -0
- package/locales/pl-PL/common.json +2 -0
- package/locales/pl-PL/electron.json +32 -0
- package/locales/pl-PL/models.json +129 -3
- package/locales/pl-PL/plugin.json +1 -0
- package/locales/pl-PL/tool.json +25 -0
- package/locales/pt-BR/common.json +2 -0
- package/locales/pt-BR/electron.json +32 -0
- package/locales/pt-BR/models.json +129 -3
- package/locales/pt-BR/plugin.json +1 -0
- package/locales/pt-BR/tool.json +25 -0
- package/locales/ru-RU/common.json +2 -0
- package/locales/ru-RU/electron.json +32 -0
- package/locales/ru-RU/models.json +129 -3
- package/locales/ru-RU/plugin.json +1 -0
- package/locales/ru-RU/tool.json +25 -0
- package/locales/tr-TR/common.json +2 -0
- package/locales/tr-TR/electron.json +32 -0
- package/locales/tr-TR/models.json +129 -3
- package/locales/tr-TR/plugin.json +1 -0
- package/locales/tr-TR/tool.json +25 -0
- package/locales/vi-VN/common.json +2 -0
- package/locales/vi-VN/electron.json +32 -0
- package/locales/vi-VN/models.json +129 -3
- package/locales/vi-VN/plugin.json +1 -0
- package/locales/vi-VN/tool.json +25 -0
- package/locales/zh-CN/common.json +2 -0
- package/locales/zh-CN/electron.json +32 -0
- package/locales/zh-CN/models.json +134 -8
- package/locales/zh-CN/plugin.json +1 -0
- package/locales/zh-CN/tool.json +25 -0
- package/locales/zh-TW/common.json +2 -0
- package/locales/zh-TW/electron.json +32 -0
- package/locales/zh-TW/models.json +129 -3
- package/locales/zh-TW/plugin.json +1 -0
- package/locales/zh-TW/tool.json +25 -0
- package/package.json +4 -3
- package/packages/electron-client-ipc/src/events/index.ts +5 -5
- package/packages/electron-client-ipc/src/events/localFile.ts +22 -0
- package/packages/electron-client-ipc/src/events/{file.ts → upload.ts} +1 -1
- package/packages/electron-client-ipc/src/types/index.ts +2 -1
- package/packages/electron-client-ipc/src/types/localFile.ts +52 -0
- package/packages/file-loaders/README.md +63 -0
- package/packages/file-loaders/package.json +42 -0
- package/packages/file-loaders/src/index.ts +2 -0
- package/packages/file-loaders/src/loadFile.ts +206 -0
- package/packages/file-loaders/src/loaders/docx/__snapshots__/index.test.ts.snap +74 -0
- package/packages/file-loaders/src/loaders/docx/fixtures/test.docx +0 -0
- package/packages/file-loaders/src/loaders/docx/index.test.ts +41 -0
- package/packages/file-loaders/src/loaders/docx/index.ts +73 -0
- package/packages/file-loaders/src/loaders/excel/__snapshots__/index.test.ts.snap +58 -0
- package/packages/file-loaders/src/loaders/excel/fixtures/test.xlsx +0 -0
- package/packages/file-loaders/src/loaders/excel/index.test.ts +47 -0
- package/packages/file-loaders/src/loaders/excel/index.ts +121 -0
- package/packages/file-loaders/src/loaders/index.ts +19 -0
- package/packages/file-loaders/src/loaders/pdf/__snapshots__/index.test.ts.snap +98 -0
- package/packages/file-loaders/src/loaders/pdf/index.test.ts +49 -0
- package/packages/file-loaders/src/loaders/pdf/index.ts +133 -0
- package/packages/file-loaders/src/loaders/pptx/__snapshots__/index.test.ts.snap +40 -0
- package/packages/file-loaders/src/loaders/pptx/fixtures/test.pptx +0 -0
- package/packages/file-loaders/src/loaders/pptx/index.test.ts +47 -0
- package/packages/file-loaders/src/loaders/pptx/index.ts +186 -0
- package/packages/file-loaders/src/loaders/text/__snapshots__/index.test.ts.snap +15 -0
- package/packages/file-loaders/src/loaders/text/fixtures/test.txt +2 -0
- package/packages/file-loaders/src/loaders/text/index.test.ts +38 -0
- package/packages/file-loaders/src/loaders/text/index.ts +53 -0
- package/packages/file-loaders/src/types.ts +200 -0
- package/packages/file-loaders/src/utils/isTextReadableFile.ts +68 -0
- package/packages/file-loaders/src/utils/parser-utils.ts +112 -0
- package/packages/file-loaders/test/__snapshots__/loaders.test.ts.snap +93 -0
- package/packages/file-loaders/test/fixtures/test.csv +4 -0
- package/packages/file-loaders/test/fixtures/test.docx +0 -0
- package/packages/file-loaders/test/fixtures/test.epub +0 -0
- package/packages/file-loaders/test/fixtures/test.md +3 -0
- package/packages/file-loaders/test/fixtures/test.pptx +0 -0
- package/packages/file-loaders/test/fixtures/test.txt +3 -0
- package/packages/file-loaders/test/loaders.test.ts +39 -0
- package/scripts/prebuild.mts +5 -1
- package/src/app/(backend)/trpc/desktop/[trpc]/route.ts +26 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/ObjectEntity.tsx +81 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/ValueCell.tsx +43 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/index.tsx +120 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +75 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/KeyValueEditor.tsx +214 -0
- package/src/features/User/UserPanel/useMenu.tsx +8 -1
- package/src/libs/agent-runtime/google/index.ts +3 -0
- package/src/libs/trpc/client/desktop.ts +14 -0
- package/src/locales/default/common.ts +2 -0
- package/src/locales/default/electron.ts +34 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/tool.ts +25 -0
- package/src/server/routers/desktop/index.ts +9 -0
- package/src/server/routers/desktop/pgTable.ts +43 -0
- package/src/services/electron/autoUpdate.ts +17 -0
- package/src/services/electron/file.ts +31 -0
- package/src/services/electron/localFileService.ts +39 -0
- package/src/services/electron/remoteServer.ts +40 -0
- package/src/store/chat/index.ts +1 -1
- package/src/store/chat/slices/builtinTool/actions/index.ts +3 -1
- package/src/store/chat/slices/builtinTool/actions/localFile.ts +129 -0
- package/src/store/chat/slices/builtinTool/initialState.ts +2 -0
- package/src/store/chat/slices/builtinTool/selectors.ts +2 -0
- package/src/store/chat/slices/plugin/action.ts +3 -3
- package/src/store/chat/store.ts +2 -0
- package/src/store/electron/actions/sync.ts +117 -0
- package/src/store/electron/index.ts +1 -0
- package/src/store/electron/initialState.ts +18 -0
- package/src/store/electron/selectors/index.ts +1 -0
- package/src/store/electron/selectors/sync.ts +9 -0
- package/src/store/electron/store.ts +29 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/local-files/Render/ListFiles/Result.tsx +42 -0
- package/src/tools/local-files/Render/ListFiles/index.tsx +68 -0
- package/src/tools/local-files/Render/ReadLocalFile/ReadFileSkeleton.tsx +50 -0
- package/src/tools/local-files/Render/ReadLocalFile/ReadFileView.tsx +197 -0
- package/src/tools/local-files/Render/ReadLocalFile/index.tsx +31 -0
- package/src/tools/local-files/Render/ReadLocalFile/style.ts +37 -0
- package/src/tools/local-files/Render/SearchFiles/Result.tsx +42 -0
- package/src/tools/local-files/Render/SearchFiles/SearchQuery/SearchView.tsx +77 -0
- package/src/tools/local-files/Render/SearchFiles/SearchQuery/index.tsx +72 -0
- package/src/tools/local-files/Render/SearchFiles/index.tsx +32 -0
- package/src/tools/local-files/Render/index.tsx +36 -0
- package/src/tools/local-files/components/FileItem.tsx +117 -0
- package/src/tools/local-files/index.ts +149 -0
- package/src/tools/local-files/systemRole.ts +46 -0
- package/src/tools/local-files/type.ts +33 -0
- package/src/tools/renders.ts +3 -0
- package/packages/electron-client-ipc/src/events/search.ts +0 -4
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments.tsx +0 -165
- /package/packages/electron-client-ipc/src/types/{file.ts → upload.ts} +0 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
import { dispatch } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
class AutoUpdateService {
|
4
|
+
checkUpdate = async () => {
|
5
|
+
return dispatch('checkUpdate');
|
6
|
+
};
|
7
|
+
|
8
|
+
installNow = async () => {
|
9
|
+
return dispatch('installNow');
|
10
|
+
};
|
11
|
+
|
12
|
+
installLater = async () => {
|
13
|
+
return dispatch('installLater');
|
14
|
+
};
|
15
|
+
}
|
16
|
+
|
17
|
+
export const autoUpdateService = new AutoUpdateService();
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { dispatch } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
import { FileMetadata } from '@/types/files';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* 桌面应用文件API客户端服务
|
7
|
+
*/
|
8
|
+
class DesktopFileAPI {
|
9
|
+
/**
|
10
|
+
* 上传文件到桌面应用
|
11
|
+
* @param file 文件对象
|
12
|
+
* @param hash 文件哈希
|
13
|
+
* @returns 上传结果
|
14
|
+
*/
|
15
|
+
async uploadFile(
|
16
|
+
file: File,
|
17
|
+
hash: string,
|
18
|
+
): Promise<{ metadata: FileMetadata; success: boolean }> {
|
19
|
+
const arrayBuffer = await file.arrayBuffer();
|
20
|
+
|
21
|
+
return dispatch('createFile', {
|
22
|
+
content: arrayBuffer,
|
23
|
+
filename: file.name,
|
24
|
+
hash,
|
25
|
+
path: file.name,
|
26
|
+
type: file.type,
|
27
|
+
});
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
export const desktopFileAPI = new DesktopFileAPI();
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import {
|
2
|
+
ListLocalFileParams,
|
3
|
+
LocalFileItem,
|
4
|
+
LocalReadFileParams,
|
5
|
+
LocalReadFileResult,
|
6
|
+
LocalReadFilesParams,
|
7
|
+
LocalSearchFilesParams,
|
8
|
+
OpenLocalFileParams,
|
9
|
+
OpenLocalFolderParams,
|
10
|
+
dispatch,
|
11
|
+
} from '@lobechat/electron-client-ipc';
|
12
|
+
|
13
|
+
class LocalFileService {
|
14
|
+
async listLocalFiles(params: ListLocalFileParams): Promise<LocalFileItem[]> {
|
15
|
+
return dispatch('listLocalFiles', params);
|
16
|
+
}
|
17
|
+
|
18
|
+
async readLocalFile(params: LocalReadFileParams): Promise<LocalReadFileResult> {
|
19
|
+
return dispatch('readLocalFile', params);
|
20
|
+
}
|
21
|
+
|
22
|
+
async readLocalFiles(params: LocalReadFilesParams): Promise<LocalReadFileResult[]> {
|
23
|
+
return dispatch('readLocalFiles', params);
|
24
|
+
}
|
25
|
+
|
26
|
+
async searchLocalFiles(params: LocalSearchFilesParams): Promise<LocalFileItem[]> {
|
27
|
+
return dispatch('searchLocalFiles', params);
|
28
|
+
}
|
29
|
+
|
30
|
+
async openLocalFile(params: OpenLocalFileParams) {
|
31
|
+
return dispatch('openLocalFile', params);
|
32
|
+
}
|
33
|
+
|
34
|
+
async openLocalFolder(params: OpenLocalFolderParams) {
|
35
|
+
return dispatch('openLocalFolder', params);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
export const localFileService = new LocalFileService();
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { RemoteServerConfig, dispatch } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
class RemoteServerService {
|
4
|
+
/**
|
5
|
+
* 获取远程服务器配置
|
6
|
+
*/
|
7
|
+
getRemoteServerConfig = async () => {
|
8
|
+
return dispatch('getRemoteServerConfig');
|
9
|
+
};
|
10
|
+
|
11
|
+
/**
|
12
|
+
* 设置远程服务器配置
|
13
|
+
*/
|
14
|
+
setRemoteServerConfig = async (config: RemoteServerConfig) => {
|
15
|
+
return dispatch('setRemoteServerConfig', config);
|
16
|
+
};
|
17
|
+
|
18
|
+
/**
|
19
|
+
* 清除远程服务器配置
|
20
|
+
*/
|
21
|
+
clearRemoteServerConfig = async () => {
|
22
|
+
return dispatch('clearRemoteServerConfig');
|
23
|
+
};
|
24
|
+
|
25
|
+
/**
|
26
|
+
* 请求授权
|
27
|
+
*/
|
28
|
+
requestAuthorization = async (serverUrl: string) => {
|
29
|
+
return dispatch('requestAuthorization', serverUrl);
|
30
|
+
};
|
31
|
+
|
32
|
+
/**
|
33
|
+
* 刷新访问令牌
|
34
|
+
*/
|
35
|
+
refreshAccessToken = async () => {
|
36
|
+
return dispatch('refreshAccessToken');
|
37
|
+
};
|
38
|
+
}
|
39
|
+
|
40
|
+
export const remoteServerService = new RemoteServerService();
|
package/src/store/chat/index.ts
CHANGED
@@ -3,9 +3,10 @@ import { StateCreator } from 'zustand/vanilla';
|
|
3
3
|
import { ChatStore } from '@/store/chat/store';
|
4
4
|
|
5
5
|
import { ChatDallEAction, dalleSlice } from './dalle';
|
6
|
+
import { LocalFileAction, localFileSlice } from './localFile';
|
6
7
|
import { SearchAction, searchSlice } from './search';
|
7
8
|
|
8
|
-
export interface ChatBuiltinToolAction extends ChatDallEAction, SearchAction {}
|
9
|
+
export interface ChatBuiltinToolAction extends ChatDallEAction, SearchAction, LocalFileAction {}
|
9
10
|
|
10
11
|
export const chatToolSlice: StateCreator<
|
11
12
|
ChatStore,
|
@@ -15,4 +16,5 @@ export const chatToolSlice: StateCreator<
|
|
15
16
|
> = (...params) => ({
|
16
17
|
...dalleSlice(...params),
|
17
18
|
...searchSlice(...params),
|
19
|
+
...localFileSlice(...params),
|
18
20
|
});
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import {
|
2
|
+
ListLocalFileParams,
|
3
|
+
LocalReadFileParams,
|
4
|
+
LocalReadFilesParams,
|
5
|
+
LocalSearchFilesParams,
|
6
|
+
} from '@lobechat/electron-client-ipc';
|
7
|
+
import { StateCreator } from 'zustand/vanilla';
|
8
|
+
|
9
|
+
import { localFileService } from '@/services/electron/localFileService';
|
10
|
+
import { ChatStore } from '@/store/chat/store';
|
11
|
+
import {
|
12
|
+
LocalFileListState,
|
13
|
+
LocalFileSearchState,
|
14
|
+
LocalReadFileState,
|
15
|
+
LocalReadFilesState,
|
16
|
+
} from '@/tools/local-files/type';
|
17
|
+
|
18
|
+
export interface LocalFileAction {
|
19
|
+
listLocalFiles: (id: string, params: ListLocalFileParams) => Promise<boolean>;
|
20
|
+
reSearchLocalFiles: (id: string, params: LocalSearchFilesParams) => Promise<boolean>;
|
21
|
+
readLocalFile: (id: string, params: LocalReadFileParams) => Promise<boolean>;
|
22
|
+
readLocalFiles: (id: string, params: LocalReadFilesParams) => Promise<boolean>;
|
23
|
+
searchLocalFiles: (id: string, params: LocalSearchFilesParams) => Promise<boolean>;
|
24
|
+
toggleLocalFileLoading: (id: string, loading: boolean) => void;
|
25
|
+
}
|
26
|
+
|
27
|
+
export const localFileSlice: StateCreator<
|
28
|
+
ChatStore,
|
29
|
+
[['zustand/devtools', never]],
|
30
|
+
[],
|
31
|
+
LocalFileAction
|
32
|
+
> = (set, get) => ({
|
33
|
+
listLocalFiles: async (id, params) => {
|
34
|
+
get().toggleLocalFileLoading(id, true);
|
35
|
+
try {
|
36
|
+
const data = await localFileService.listLocalFiles(params);
|
37
|
+
console.log(data);
|
38
|
+
await get().updatePluginState(id, { listResults: data } as LocalFileListState);
|
39
|
+
await get().internal_updateMessageContent(id, JSON.stringify(data));
|
40
|
+
} catch (error) {
|
41
|
+
console.error('Error listing local files:', error);
|
42
|
+
await get().internal_updateMessagePluginError(id, {
|
43
|
+
body: error,
|
44
|
+
message: (error as Error).message,
|
45
|
+
type: 'PluginServerError',
|
46
|
+
});
|
47
|
+
}
|
48
|
+
get().toggleLocalFileLoading(id, false);
|
49
|
+
|
50
|
+
return true;
|
51
|
+
},
|
52
|
+
|
53
|
+
reSearchLocalFiles: async (id, params) => {
|
54
|
+
get().toggleLocalFileLoading(id, true);
|
55
|
+
|
56
|
+
await get().updatePluginArguments(id, params);
|
57
|
+
|
58
|
+
return get().searchLocalFiles(id, params);
|
59
|
+
},
|
60
|
+
|
61
|
+
readLocalFile: async (id, params) => {
|
62
|
+
get().toggleLocalFileLoading(id, true);
|
63
|
+
|
64
|
+
try {
|
65
|
+
const result = await localFileService.readLocalFile(params);
|
66
|
+
|
67
|
+
await get().updatePluginState(id, { fileContent: result } as LocalReadFileState);
|
68
|
+
await get().internal_updateMessageContent(id, JSON.stringify(result));
|
69
|
+
} catch (error) {
|
70
|
+
console.error('Error reading local file:', error);
|
71
|
+
await get().internal_updateMessagePluginError(id, {
|
72
|
+
body: error,
|
73
|
+
message: (error as Error).message,
|
74
|
+
type: 'PluginServerError',
|
75
|
+
});
|
76
|
+
}
|
77
|
+
get().toggleLocalFileLoading(id, false);
|
78
|
+
return true;
|
79
|
+
},
|
80
|
+
|
81
|
+
readLocalFiles: async (id, params) => {
|
82
|
+
get().toggleLocalFileLoading(id, true);
|
83
|
+
|
84
|
+
try {
|
85
|
+
const results = await localFileService.readLocalFiles(params);
|
86
|
+
await get().updatePluginState(id, { filesContent: results } as LocalReadFilesState);
|
87
|
+
await get().internal_updateMessageContent(id, JSON.stringify(results));
|
88
|
+
} catch (error) {
|
89
|
+
console.error('Error reading local files:', error);
|
90
|
+
await get().internal_updateMessagePluginError(id, {
|
91
|
+
body: error,
|
92
|
+
message: (error as Error).message,
|
93
|
+
type: 'PluginServerError',
|
94
|
+
});
|
95
|
+
}
|
96
|
+
get().toggleLocalFileLoading(id, false);
|
97
|
+
|
98
|
+
return true;
|
99
|
+
},
|
100
|
+
|
101
|
+
searchLocalFiles: async (id, params) => {
|
102
|
+
get().toggleLocalFileLoading(id, true);
|
103
|
+
try {
|
104
|
+
const data = await localFileService.searchLocalFiles(params);
|
105
|
+
await get().updatePluginState(id, { searchResults: data } as LocalFileSearchState);
|
106
|
+
await get().internal_updateMessageContent(id, JSON.stringify(data));
|
107
|
+
} catch (error) {
|
108
|
+
console.error('Error searching local files:', error);
|
109
|
+
await get().internal_updateMessagePluginError(id, {
|
110
|
+
body: error,
|
111
|
+
message: (error as Error).message,
|
112
|
+
type: 'PluginServerError',
|
113
|
+
});
|
114
|
+
}
|
115
|
+
get().toggleLocalFileLoading(id, false);
|
116
|
+
|
117
|
+
return true;
|
118
|
+
},
|
119
|
+
toggleLocalFileLoading: (id, loading) => {
|
120
|
+
// Assuming a loading state structure similar to searchLoading
|
121
|
+
set(
|
122
|
+
(state) => ({
|
123
|
+
localFileLoading: { ...state.localFileLoading, [id]: loading },
|
124
|
+
}),
|
125
|
+
false,
|
126
|
+
`toggleLocalFileLoading/${loading ? 'start' : 'end'}`,
|
127
|
+
);
|
128
|
+
},
|
129
|
+
});
|
@@ -4,11 +4,13 @@ export interface ChatToolState {
|
|
4
4
|
activePageContentUrl?: string;
|
5
5
|
dalleImageLoading: Record<string, boolean>;
|
6
6
|
dalleImageMap: Record<string, FileItem>;
|
7
|
+
localFileLoading: Record<string, boolean>;
|
7
8
|
searchLoading: Record<string, boolean>;
|
8
9
|
}
|
9
10
|
|
10
11
|
export const initialToolState: ChatToolState = {
|
11
12
|
dalleImageLoading: {},
|
12
13
|
dalleImageMap: {},
|
14
|
+
localFileLoading: {},
|
13
15
|
searchLoading: {},
|
14
16
|
};
|
@@ -6,9 +6,11 @@ const isGeneratingDallEImage = (s: ChatStoreState) =>
|
|
6
6
|
Object.values(s.dalleImageLoading).some(Boolean);
|
7
7
|
|
8
8
|
const isSearXNGSearching = (id: string) => (s: ChatStoreState) => s.searchLoading[id];
|
9
|
+
const isSearchingLocalFiles = (id: string) => (s: ChatStoreState) => s.localFileLoading[id];
|
9
10
|
|
10
11
|
export const chatToolSelectors = {
|
11
12
|
isDallEImageGenerating,
|
12
13
|
isGeneratingDallEImage,
|
13
14
|
isSearXNGSearching,
|
15
|
+
isSearchingLocalFiles,
|
14
16
|
};
|
@@ -59,7 +59,7 @@ export interface ChatPluginAction {
|
|
59
59
|
params?: { threadId?: string; inPortalThread?: boolean; inSearchWorkflow?: boolean },
|
60
60
|
) => Promise<void>;
|
61
61
|
updatePluginState: (id: string, value: any) => Promise<void>;
|
62
|
-
updatePluginArguments: <T = any>(id: string, value: T) => Promise<void>;
|
62
|
+
updatePluginArguments: <T = any>(id: string, value: T, replace?: boolean) => Promise<void>;
|
63
63
|
|
64
64
|
internal_addToolToAssistantMessage: (id: string, tool: ChatToolPayload) => Promise<void>;
|
65
65
|
internal_removeToolToAssistantMessage: (id: string, tool_call_id?: string) => Promise<void>;
|
@@ -296,7 +296,7 @@ export const chatPlugin: StateCreator<
|
|
296
296
|
await refreshMessages();
|
297
297
|
},
|
298
298
|
|
299
|
-
updatePluginArguments: async (id, value) => {
|
299
|
+
updatePluginArguments: async (id, value, replace = false) => {
|
300
300
|
const { refreshMessages } = get();
|
301
301
|
const toolMessage = chatSelectors.getMessageById(id)(get());
|
302
302
|
if (!toolMessage || !toolMessage?.tool_call_id) return;
|
@@ -305,7 +305,7 @@ export const chatPlugin: StateCreator<
|
|
305
305
|
|
306
306
|
const prevArguments = toolMessage?.plugin?.arguments;
|
307
307
|
const prevJson = safeParseJSON(prevArguments || '');
|
308
|
-
const nextValue = merge(prevJson || {}, value);
|
308
|
+
const nextValue = replace ? (value as any) : merge(prevJson || {}, value);
|
309
309
|
if (isEqual(prevJson, nextValue)) return;
|
310
310
|
|
311
311
|
// optimistic update
|
package/src/store/chat/store.ts
CHANGED
@@ -0,0 +1,117 @@
|
|
1
|
+
import { RemoteServerConfig } from '@lobechat/electron-client-ipc';
|
2
|
+
import useSWR, { SWRResponse, mutate } from 'swr';
|
3
|
+
import type { StateCreator } from 'zustand/vanilla';
|
4
|
+
|
5
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
6
|
+
import { remoteServerService } from '@/services/electron/remoteServer';
|
7
|
+
|
8
|
+
import { initialState } from '../initialState';
|
9
|
+
import type { ElectronStore } from '../store';
|
10
|
+
|
11
|
+
/**
|
12
|
+
* 设置操作
|
13
|
+
*/
|
14
|
+
export interface ElectronRemoteServerAction {
|
15
|
+
connectRemoteServer: (params: { isSelfHosted: boolean; serverUrl?: string }) => Promise<void>;
|
16
|
+
disconnectRemoteServer: () => Promise<void>;
|
17
|
+
refreshServerConfig: () => Promise<void>;
|
18
|
+
refreshUserData: () => Promise<void>;
|
19
|
+
useRemoteServerConfig: () => SWRResponse;
|
20
|
+
}
|
21
|
+
|
22
|
+
const REMOTE_SERVER_CONFIG_KEY = 'electron:getRemoteServerConfig';
|
23
|
+
|
24
|
+
export const remoteSyncSlice: StateCreator<
|
25
|
+
ElectronStore,
|
26
|
+
[['zustand/devtools', never]],
|
27
|
+
[],
|
28
|
+
ElectronRemoteServerAction
|
29
|
+
> = (set, get) => ({
|
30
|
+
connectRemoteServer: async (values) => {
|
31
|
+
if (!values.serverUrl) return;
|
32
|
+
|
33
|
+
set({ isConnectingServer: true });
|
34
|
+
try {
|
35
|
+
// 获取当前配置
|
36
|
+
const config = await remoteServerService.getRemoteServerConfig();
|
37
|
+
|
38
|
+
// 如果已经激活,需要先清除
|
39
|
+
if (config.active) {
|
40
|
+
await remoteServerService.clearRemoteServerConfig();
|
41
|
+
}
|
42
|
+
|
43
|
+
// 请求授权
|
44
|
+
const result = await remoteServerService.requestAuthorization(values.serverUrl);
|
45
|
+
|
46
|
+
if (!result.success) {
|
47
|
+
console.error('请求授权失败:', result.error);
|
48
|
+
|
49
|
+
set({
|
50
|
+
remoteServerSyncError: { message: result.error, type: 'AUTH_ERROR' },
|
51
|
+
});
|
52
|
+
}
|
53
|
+
// 刷新状态
|
54
|
+
await get().refreshServerConfig();
|
55
|
+
} catch (error) {
|
56
|
+
console.error('远程服务器配置出错:', error);
|
57
|
+
set({
|
58
|
+
remoteServerSyncError: { message: (error as Error).message, type: 'CONFIG_ERROR' },
|
59
|
+
});
|
60
|
+
} finally {
|
61
|
+
set({ isConnectingServer: false });
|
62
|
+
}
|
63
|
+
},
|
64
|
+
|
65
|
+
disconnectRemoteServer: async () => {
|
66
|
+
set({ isConnectingServer: false });
|
67
|
+
try {
|
68
|
+
await remoteServerService.clearRemoteServerConfig();
|
69
|
+
// 更新表单URL为空
|
70
|
+
set({ remoteServerConfig: initialState.remoteServerConfig });
|
71
|
+
// 刷新状态
|
72
|
+
await get().refreshServerConfig();
|
73
|
+
} catch (error) {
|
74
|
+
console.error('断开连接失败:', error);
|
75
|
+
set({
|
76
|
+
remoteServerSyncError: { message: (error as Error).message, type: 'DISCONNECT_ERROR' },
|
77
|
+
});
|
78
|
+
} finally {
|
79
|
+
set({ isConnectingServer: false });
|
80
|
+
}
|
81
|
+
},
|
82
|
+
|
83
|
+
refreshServerConfig: async () => {
|
84
|
+
await mutate(REMOTE_SERVER_CONFIG_KEY);
|
85
|
+
},
|
86
|
+
|
87
|
+
refreshUserData: async () => {
|
88
|
+
const { getSessionStoreState } = await import('@/store/session');
|
89
|
+
const { getChatStoreState } = await import('@/store/chat');
|
90
|
+
const { getUserStoreState } = await import('@/store/user');
|
91
|
+
|
92
|
+
await getSessionStoreState().refreshSessions();
|
93
|
+
await getChatStoreState().refreshMessages();
|
94
|
+
await getChatStoreState().refreshTopic();
|
95
|
+
await getUserStoreState().refreshUserState();
|
96
|
+
getSessionStoreState().switchSession(INBOX_SESSION_ID);
|
97
|
+
},
|
98
|
+
|
99
|
+
useRemoteServerConfig: () =>
|
100
|
+
useSWR<RemoteServerConfig>(
|
101
|
+
REMOTE_SERVER_CONFIG_KEY,
|
102
|
+
async () => {
|
103
|
+
try {
|
104
|
+
return await remoteServerService.getRemoteServerConfig();
|
105
|
+
} catch (error) {
|
106
|
+
console.error('获取远程服务器配置失败:', error);
|
107
|
+
throw error;
|
108
|
+
}
|
109
|
+
},
|
110
|
+
{
|
111
|
+
onSuccess: (data) => {
|
112
|
+
set({ isInitRemoteServerConfig: true, remoteServerConfig: data });
|
113
|
+
get().refreshUserData();
|
114
|
+
},
|
115
|
+
},
|
116
|
+
),
|
117
|
+
});
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './store';
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { RemoteServerConfig } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
export type RemoteServerError = 'CONFIG_ERROR' | 'AUTH_ERROR' | 'DISCONNECT_ERROR';
|
4
|
+
|
5
|
+
export interface ElectronState {
|
6
|
+
isConnectingServer?: boolean;
|
7
|
+
isInitRemoteServerConfig: boolean;
|
8
|
+
isSyncActive?: boolean;
|
9
|
+
remoteServerConfig: RemoteServerConfig;
|
10
|
+
remoteServerSyncError?: { message?: string; type: RemoteServerError };
|
11
|
+
}
|
12
|
+
|
13
|
+
export const initialState: ElectronState = {
|
14
|
+
isConnectingServer: false,
|
15
|
+
isInitRemoteServerConfig: false,
|
16
|
+
isSyncActive: false,
|
17
|
+
remoteServerConfig: { active: false, isSelfHosted: false },
|
18
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './sync';
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { shallow } from 'zustand/shallow';
|
2
|
+
import { createWithEqualityFn } from 'zustand/traditional';
|
3
|
+
import { StateCreator } from 'zustand/vanilla';
|
4
|
+
|
5
|
+
import { createDevtools } from '../middleware/createDevtools';
|
6
|
+
import { type ElectronRemoteServerAction, remoteSyncSlice } from './actions/sync';
|
7
|
+
import { type ElectronState, initialState } from './initialState';
|
8
|
+
|
9
|
+
// =============== 聚合 createStoreFn ============ //
|
10
|
+
|
11
|
+
export interface ElectronStore extends ElectronState, ElectronRemoteServerAction {
|
12
|
+
/* empty */
|
13
|
+
}
|
14
|
+
|
15
|
+
const createStore: StateCreator<ElectronStore, [['zustand/devtools', never]]> = (
|
16
|
+
...parameters
|
17
|
+
) => ({
|
18
|
+
...initialState,
|
19
|
+
...remoteSyncSlice(...parameters),
|
20
|
+
});
|
21
|
+
|
22
|
+
// =============== 实装 useStore ============ //
|
23
|
+
|
24
|
+
const devtools = createDevtools('electron');
|
25
|
+
|
26
|
+
export const useElectronStore = createWithEqualityFn<ElectronStore>()(
|
27
|
+
devtools(createStore),
|
28
|
+
shallow,
|
29
|
+
);
|
package/src/tools/index.ts
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
import { isDesktop } from '@/const/version';
|
1
2
|
import { LobeBuiltinTool } from '@/types/tool';
|
2
3
|
|
3
4
|
import { ArtifactsManifest } from './artifacts';
|
4
5
|
import { DalleManifest } from './dalle';
|
6
|
+
import { LocalFilesManifest } from './local-files';
|
5
7
|
import { WebBrowsingManifest } from './web-browsing';
|
6
8
|
|
7
9
|
export const builtinTools: LobeBuiltinTool[] = [
|
@@ -15,6 +17,12 @@ export const builtinTools: LobeBuiltinTool[] = [
|
|
15
17
|
manifest: DalleManifest,
|
16
18
|
type: 'builtin',
|
17
19
|
},
|
20
|
+
{
|
21
|
+
hidden: !isDesktop,
|
22
|
+
identifier: LocalFilesManifest.identifier,
|
23
|
+
manifest: LocalFilesManifest,
|
24
|
+
type: 'builtin',
|
25
|
+
},
|
18
26
|
{
|
19
27
|
hidden: true,
|
20
28
|
identifier: WebBrowsingManifest.identifier,
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import { LocalFileItem } from '@lobechat/electron-client-ipc';
|
2
|
+
import { Skeleton } from 'antd';
|
3
|
+
import { memo } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import { useChatStore } from '@/store/chat';
|
7
|
+
import { chatToolSelectors } from '@/store/chat/selectors';
|
8
|
+
import FileItem from '@/tools/local-files/components/FileItem';
|
9
|
+
import { ChatMessagePluginError } from '@/types/message';
|
10
|
+
|
11
|
+
interface SearchFilesProps {
|
12
|
+
listResults?: LocalFileItem[];
|
13
|
+
messageId: string;
|
14
|
+
pluginError: ChatMessagePluginError;
|
15
|
+
}
|
16
|
+
|
17
|
+
const SearchFiles = memo<SearchFilesProps>(({ listResults = [], messageId }) => {
|
18
|
+
const loading = useChatStore(chatToolSelectors.isSearchingLocalFiles(messageId));
|
19
|
+
|
20
|
+
if (loading) {
|
21
|
+
return (
|
22
|
+
<Flexbox gap={4}>
|
23
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
24
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
25
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
26
|
+
<Skeleton.Button active block style={{ height: 16 }} />
|
27
|
+
</Flexbox>
|
28
|
+
);
|
29
|
+
}
|
30
|
+
|
31
|
+
return (
|
32
|
+
<Flexbox gap={2} style={{ maxHeight: 260, overflow: 'scroll' }}>
|
33
|
+
{listResults.map((item) => (
|
34
|
+
<FileItem key={item.path} {...item} showTime />
|
35
|
+
))}
|
36
|
+
</Flexbox>
|
37
|
+
);
|
38
|
+
});
|
39
|
+
|
40
|
+
SearchFiles.displayName = 'SearchFiles';
|
41
|
+
|
42
|
+
export default SearchFiles;
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
|
2
|
+
import { ActionIcon } from '@lobehub/ui';
|
3
|
+
import { Typography } from 'antd';
|
4
|
+
import { createStyles } from 'antd-style';
|
5
|
+
import { FolderOpen } from 'lucide-react';
|
6
|
+
import React, { memo } from 'react';
|
7
|
+
import { useTranslation } from 'react-i18next';
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
9
|
+
|
10
|
+
import { localFileService } from '@/services/electron/localFileService';
|
11
|
+
import { LocalFileListState } from '@/tools/local-files/type';
|
12
|
+
import { ChatMessagePluginError } from '@/types/message';
|
13
|
+
|
14
|
+
import SearchResult from './Result';
|
15
|
+
|
16
|
+
const useStyles = createStyles(({ css, token, cx }) => ({
|
17
|
+
actions: cx(css`
|
18
|
+
cursor: pointer;
|
19
|
+
color: ${token.colorTextTertiary};
|
20
|
+
opacity: 1;
|
21
|
+
transition: opacity 0.2s ${token.motionEaseInOut};
|
22
|
+
`),
|
23
|
+
path: css`
|
24
|
+
padding-inline-start: 8px;
|
25
|
+
color: ${token.colorTextSecondary};
|
26
|
+
`,
|
27
|
+
}));
|
28
|
+
|
29
|
+
interface ListFilesProps {
|
30
|
+
args: ListLocalFileParams;
|
31
|
+
messageId: string;
|
32
|
+
pluginError: ChatMessagePluginError;
|
33
|
+
pluginState?: LocalFileListState;
|
34
|
+
}
|
35
|
+
|
36
|
+
const ListFiles = memo<ListFilesProps>(({ messageId, pluginError, args, pluginState }) => {
|
37
|
+
const { t } = useTranslation('tool');
|
38
|
+
|
39
|
+
const { styles } = useStyles();
|
40
|
+
return (
|
41
|
+
<>
|
42
|
+
<Flexbox gap={8} horizontal>
|
43
|
+
<Typography.Text className={styles.path} ellipsis>
|
44
|
+
{args.path}
|
45
|
+
</Typography.Text>
|
46
|
+
<Flexbox className={styles.actions} gap={8} horizontal style={{ marginLeft: 8 }}>
|
47
|
+
<ActionIcon
|
48
|
+
icon={FolderOpen}
|
49
|
+
onClick={() => {
|
50
|
+
localFileService.openLocalFolder({ isDirectory: true, path: args.path });
|
51
|
+
}}
|
52
|
+
size="small"
|
53
|
+
title={t('localFiles.openFolder')}
|
54
|
+
/>
|
55
|
+
</Flexbox>
|
56
|
+
</Flexbox>
|
57
|
+
<SearchResult
|
58
|
+
listResults={pluginState?.listResults}
|
59
|
+
messageId={messageId}
|
60
|
+
pluginError={pluginError}
|
61
|
+
/>
|
62
|
+
</>
|
63
|
+
);
|
64
|
+
});
|
65
|
+
|
66
|
+
ListFiles.displayName = 'ListFiles';
|
67
|
+
|
68
|
+
export default ListFiles;
|