@lobehub/chat 1.81.8 → 1.81.9
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/packages/electron-client-ipc/src/events/localFile.ts +8 -2
- package/packages/electron-client-ipc/src/events/system.ts +3 -0
- package/packages/electron-client-ipc/src/types/index.ts +1 -0
- package/packages/electron-client-ipc/src/types/localFile.ts +46 -0
- package/packages/electron-client-ipc/src/types/system.ts +24 -0
- package/packages/file-loaders/src/blackList.ts +9 -0
- package/packages/file-loaders/src/index.ts +1 -0
- package/packages/file-loaders/src/loaders/pdf/index.test.ts +1 -0
- package/packages/file-loaders/src/loaders/pdf/index.ts +1 -7
- package/src/components/FileIcon/index.tsx +7 -3
- package/src/services/electron/localFileService.ts +19 -0
- package/src/services/electron/system.ts +21 -0
- package/src/store/chat/slices/builtinTool/actions/search.ts +0 -3
- package/src/tools/local-files/Render/ListFiles/index.tsx +24 -17
- package/src/tools/local-files/Render/ReadLocalFile/ReadFileView.tsx +28 -28
- package/src/tools/local-files/components/FileItem.tsx +9 -11
- package/src/tools/local-files/index.ts +60 -2
- package/src/tools/local-files/systemRole.ts +53 -13
- package/src/tools/local-files/type.ts +19 -1
- package/src/tools/web-browsing/systemRole.ts +40 -38
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.81.9](https://github.com/lobehub/lobe-chat/compare/v1.81.8...v1.81.9)
|
6
|
+
|
7
|
+
<sup>Released on **2025-04-21**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix search prompt.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix search prompt, closes [#7507](https://github.com/lobehub/lobe-chat/issues/7507) ([f55b7de](https://github.com/lobehub/lobe-chat/commit/f55b7de))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.81.8](https://github.com/lobehub/lobe-chat/compare/v1.81.7...v1.81.8)
|
6
31
|
|
7
32
|
<sup>Released on **2025-04-21**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.81.
|
3
|
+
"version": "1.81.9",
|
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",
|
@@ -1,22 +1,28 @@
|
|
1
1
|
import {
|
2
2
|
ListLocalFileParams,
|
3
3
|
LocalFileItem,
|
4
|
+
LocalMoveFilesResultItem,
|
4
5
|
LocalReadFileParams,
|
5
6
|
LocalReadFileResult,
|
6
7
|
LocalReadFilesParams,
|
7
8
|
LocalSearchFilesParams,
|
9
|
+
MoveLocalFilesParams,
|
8
10
|
OpenLocalFileParams,
|
9
11
|
OpenLocalFolderParams,
|
12
|
+
RenameLocalFileParams,
|
13
|
+
RenameLocalFileResult,
|
10
14
|
} from '../types';
|
11
15
|
|
12
16
|
export interface LocalFilesDispatchEvents {
|
13
17
|
// Local Files API Events
|
14
18
|
listLocalFiles: (params: ListLocalFileParams) => LocalFileItem[];
|
15
|
-
|
19
|
+
moveLocalFiles: (params: MoveLocalFilesParams) => LocalMoveFilesResultItem[];
|
20
|
+
|
16
21
|
openLocalFile: (params: OpenLocalFileParams) => void;
|
17
22
|
openLocalFolder: (params: OpenLocalFolderParams) => void;
|
18
23
|
readLocalFile: (params: LocalReadFileParams) => LocalReadFileResult;
|
19
|
-
|
20
24
|
readLocalFiles: (params: LocalReadFilesParams) => LocalReadFileResult[];
|
25
|
+
|
26
|
+
renameLocalFile: (params: RenameLocalFileParams) => RenameLocalFileResult;
|
21
27
|
searchLocalFiles: (params: LocalSearchFilesParams) => LocalFileItem[];
|
22
28
|
}
|
@@ -19,7 +19,35 @@ export interface ListLocalFileParams {
|
|
19
19
|
path: string;
|
20
20
|
}
|
21
21
|
|
22
|
+
export interface MoveLocalFileParams {
|
23
|
+
newPath: string;
|
24
|
+
oldPath: string;
|
25
|
+
}
|
26
|
+
|
27
|
+
export interface MoveLocalFilesParams {
|
28
|
+
items: MoveLocalFileParams[];
|
29
|
+
}
|
30
|
+
|
31
|
+
export interface LocalMoveFilesResultItem {
|
32
|
+
error?: string; // Error message if this specific item failed
|
33
|
+
newPath?: string; // The final path after moving/renaming, if successful
|
34
|
+
sourcePath: string; // The original path of the item being moved/renamed
|
35
|
+
success: boolean; // Whether the operation for this specific item was successful
|
36
|
+
}
|
37
|
+
|
38
|
+
export interface RenameLocalFileParams {
|
39
|
+
newName: string;
|
40
|
+
path: string;
|
41
|
+
}
|
42
|
+
|
43
|
+
export interface RenameLocalFileResult {
|
44
|
+
error?: any;
|
45
|
+
newPath: string;
|
46
|
+
success: boolean;
|
47
|
+
}
|
48
|
+
|
22
49
|
export interface LocalReadFileParams {
|
50
|
+
loc?: [number, number];
|
23
51
|
path: string;
|
24
52
|
}
|
25
53
|
|
@@ -28,13 +56,31 @@ export interface LocalReadFilesParams {
|
|
28
56
|
}
|
29
57
|
|
30
58
|
export interface LocalReadFileResult {
|
59
|
+
/**
|
60
|
+
* Character count of the content within the specified `loc` range.
|
61
|
+
*/
|
31
62
|
charCount: number;
|
63
|
+
/**
|
64
|
+
* Content of the file within the specified `loc` range.
|
65
|
+
*/
|
32
66
|
content: string;
|
33
67
|
createdTime: Date;
|
34
68
|
fileType: string;
|
35
69
|
filename: string;
|
70
|
+
/**
|
71
|
+
* Line count of the content within the specified `loc` range.
|
72
|
+
*/
|
36
73
|
lineCount: number;
|
74
|
+
loc: [number, number];
|
37
75
|
modifiedTime: Date;
|
76
|
+
/**
|
77
|
+
* Total character count of the entire file.
|
78
|
+
*/
|
79
|
+
totalCharCount: number;
|
80
|
+
/**
|
81
|
+
* Total line count of the entire file.
|
82
|
+
*/
|
83
|
+
totalLineCount: number;
|
38
84
|
}
|
39
85
|
|
40
86
|
export interface LocalSearchFilesParams {
|
@@ -0,0 +1,24 @@
|
|
1
|
+
export interface ElectronAppState {
|
2
|
+
arch?: string; // e.g., 'x64', 'arm64'
|
3
|
+
isLinux?: boolean;
|
4
|
+
isMac?: boolean;
|
5
|
+
isWindows?: boolean;
|
6
|
+
platform?: 'darwin' | 'win32' | 'linux'; // , etc.
|
7
|
+
userPath?: UserPathData;
|
8
|
+
}
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Defines the structure for user-specific paths obtained from Electron.
|
12
|
+
*/
|
13
|
+
export interface UserPathData {
|
14
|
+
desktop: string;
|
15
|
+
documents: string;
|
16
|
+
downloads?: string;
|
17
|
+
// App data directory
|
18
|
+
home: string;
|
19
|
+
// Optional as not all OS might have it easily accessible or standard
|
20
|
+
music?: string;
|
21
|
+
pictures?: string;
|
22
|
+
userData: string;
|
23
|
+
videos?: string; // User's home directory
|
24
|
+
}
|
@@ -42,6 +42,7 @@ describe('PdfLoader', () => {
|
|
42
42
|
|
43
43
|
it('should attach document metadata correctly', async () => {
|
44
44
|
// 首先加载页面以初始化 pdfInstance,尽管此方法不直接使用页面
|
45
|
+
await loader.loadPages(testFile);
|
45
46
|
const metadata = await loader.attachDocumentMetadata!(testFile);
|
46
47
|
|
47
48
|
expect(metadata).toMatchSnapshot();
|
@@ -11,8 +11,6 @@ export class PdfLoader implements FileLoaderInterface {
|
|
11
11
|
private pdfInstance: PDFDocumentProxy | null = null;
|
12
12
|
|
13
13
|
private async getPDFFile(filePath: string) {
|
14
|
-
if (!!this.pdfInstance) return this.pdfInstance;
|
15
|
-
|
16
14
|
const dataBuffer = await readFile(filePath);
|
17
15
|
|
18
16
|
const loadingTask = pdfjsLib.getDocument({
|
@@ -22,11 +20,7 @@ export class PdfLoader implements FileLoaderInterface {
|
|
22
20
|
worker: undefined, // Attempt to use system fonts
|
23
21
|
});
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
this.pdfInstance = pdf;
|
28
|
-
|
29
|
-
return pdf;
|
23
|
+
return await loadingTask.promise;
|
30
24
|
}
|
31
25
|
|
32
26
|
async loadPages(filePath: string): Promise<DocumentPage[]> {
|
@@ -1,16 +1,20 @@
|
|
1
1
|
import { FileTypeIcon, MaterialFileTypeIcon } from '@lobehub/ui';
|
2
|
-
import { memo } from 'react';
|
2
|
+
import React, { memo } from 'react';
|
3
3
|
|
4
4
|
import { mimeTypeMap } from './config';
|
5
5
|
|
6
6
|
interface FileListProps {
|
7
7
|
fileName: string;
|
8
|
-
fileType
|
8
|
+
fileType?: string;
|
9
|
+
isDirectory?: boolean;
|
9
10
|
size?: number;
|
10
11
|
variant?: 'pure' | 'file' | 'folder';
|
11
12
|
}
|
12
13
|
|
13
|
-
const FileIcon = memo<FileListProps>(({ fileName, size, variant = 'file' }) => {
|
14
|
+
const FileIcon = memo<FileListProps>(({ fileName, size, variant = 'file', isDirectory }) => {
|
15
|
+
if (isDirectory)
|
16
|
+
return <FileTypeIcon color={'gold'} size={size} type={'folder'} variant={'color'} />;
|
17
|
+
|
14
18
|
if (Object.keys(mimeTypeMap).some((key) => fileName?.toLowerCase().endsWith(`.${key}`))) {
|
15
19
|
const ext = fileName.split('.').pop()?.toLowerCase() as string;
|
16
20
|
|
@@ -1,12 +1,15 @@
|
|
1
1
|
import {
|
2
2
|
ListLocalFileParams,
|
3
3
|
LocalFileItem,
|
4
|
+
LocalMoveFilesResultItem,
|
4
5
|
LocalReadFileParams,
|
5
6
|
LocalReadFileResult,
|
6
7
|
LocalReadFilesParams,
|
7
8
|
LocalSearchFilesParams,
|
9
|
+
MoveLocalFilesParams,
|
8
10
|
OpenLocalFileParams,
|
9
11
|
OpenLocalFolderParams,
|
12
|
+
RenameLocalFileParams,
|
10
13
|
dispatch,
|
11
14
|
} from '@lobechat/electron-client-ipc';
|
12
15
|
|
@@ -34,6 +37,22 @@ class LocalFileService {
|
|
34
37
|
async openLocalFolder(params: OpenLocalFolderParams) {
|
35
38
|
return dispatch('openLocalFolder', params);
|
36
39
|
}
|
40
|
+
|
41
|
+
async moveLocalFiles(params: MoveLocalFilesParams): Promise<LocalMoveFilesResultItem[]> {
|
42
|
+
return dispatch('moveLocalFiles', params);
|
43
|
+
}
|
44
|
+
|
45
|
+
async renameLocalFile(params: RenameLocalFileParams) {
|
46
|
+
return dispatch('renameLocalFile', params);
|
47
|
+
}
|
48
|
+
|
49
|
+
async openLocalFileOrFolder(path: string, isDirectory: boolean) {
|
50
|
+
if (isDirectory) {
|
51
|
+
return this.openLocalFolder({ isDirectory, path });
|
52
|
+
} else {
|
53
|
+
return this.openLocalFile({ path });
|
54
|
+
}
|
55
|
+
}
|
37
56
|
}
|
38
57
|
|
39
58
|
export const localFileService = new LocalFileService();
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { ElectronAppState, dispatch } from '@lobechat/electron-client-ipc';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Service class for interacting with Electron's system-level information and actions.
|
5
|
+
*/
|
6
|
+
class ElectronSystemService {
|
7
|
+
/**
|
8
|
+
* Fetches the application state from the Electron main process.
|
9
|
+
* This includes system information (platform, arch) and user-specific paths.
|
10
|
+
* @returns {Promise<DesktopAppState>} A promise that resolves with the desktop app state.
|
11
|
+
*/
|
12
|
+
async getAppState(): Promise<ElectronAppState> {
|
13
|
+
// Calls the underlying IPC function to get data from the main process
|
14
|
+
return dispatch('getDesktopAppState');
|
15
|
+
}
|
16
|
+
|
17
|
+
// Add other system-related service methods here if needed in the future
|
18
|
+
}
|
19
|
+
|
20
|
+
// Export a singleton instance of the service
|
21
|
+
export const electronSystemService = new ElectronSystemService();
|
@@ -190,9 +190,6 @@ export const searchSlice: StateCreator<
|
|
190
190
|
|
191
191
|
await get().internal_updateMessageContent(id, JSON.stringify(searchContent));
|
192
192
|
|
193
|
-
// 如果没搜索到结果,那么不触发 ai 总结
|
194
|
-
if (searchContent.length === 0) return;
|
195
|
-
|
196
193
|
// 如果 aiSummary 为 true,则会自动触发总结
|
197
194
|
return aiSummary;
|
198
195
|
},
|
@@ -1,12 +1,10 @@
|
|
1
1
|
import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
|
2
|
-
import { ActionIcon } from '@lobehub/ui';
|
3
2
|
import { Typography } from 'antd';
|
4
3
|
import { createStyles } from 'antd-style';
|
5
|
-
import { FolderOpen } from 'lucide-react';
|
6
4
|
import React, { memo } from 'react';
|
7
|
-
import { useTranslation } from 'react-i18next';
|
8
5
|
import { Flexbox } from 'react-layout-kit';
|
9
6
|
|
7
|
+
import FileIcon from '@/components/FileIcon';
|
10
8
|
import { localFileService } from '@/services/electron/localFileService';
|
11
9
|
import { LocalFileListState } from '@/tools/local-files/type';
|
12
10
|
import { ChatMessagePluginError } from '@/types/message';
|
@@ -20,8 +18,21 @@ const useStyles = createStyles(({ css, token, cx }) => ({
|
|
20
18
|
opacity: 1;
|
21
19
|
transition: opacity 0.2s ${token.motionEaseInOut};
|
22
20
|
`),
|
21
|
+
container: css`
|
22
|
+
cursor: pointer;
|
23
|
+
|
24
|
+
padding-block: 2px;
|
25
|
+
padding-inline: 4px;
|
26
|
+
border-radius: 4px;
|
27
|
+
|
28
|
+
color: ${token.colorTextSecondary};
|
29
|
+
|
30
|
+
:hover {
|
31
|
+
color: ${token.colorText};
|
32
|
+
background: ${token.colorFillTertiary};
|
33
|
+
}
|
34
|
+
`,
|
23
35
|
path: css`
|
24
|
-
padding-inline-start: 8px;
|
25
36
|
color: ${token.colorTextSecondary};
|
26
37
|
`,
|
27
38
|
}));
|
@@ -34,25 +45,21 @@ interface ListFilesProps {
|
|
34
45
|
}
|
35
46
|
|
36
47
|
const ListFiles = memo<ListFilesProps>(({ messageId, pluginError, args, pluginState }) => {
|
37
|
-
const { t } = useTranslation('tool');
|
38
|
-
|
39
48
|
const { styles } = useStyles();
|
40
49
|
return (
|
41
50
|
<>
|
42
|
-
<Flexbox
|
51
|
+
<Flexbox
|
52
|
+
className={styles.container}
|
53
|
+
gap={8}
|
54
|
+
horizontal
|
55
|
+
onClick={() => {
|
56
|
+
localFileService.openLocalFolder({ isDirectory: true, path: args.path });
|
57
|
+
}}
|
58
|
+
>
|
59
|
+
<FileIcon fileName={args.path} isDirectory size={22} variant={'pure'} />
|
43
60
|
<Typography.Text className={styles.path} ellipsis>
|
44
61
|
{args.path}
|
45
62
|
</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
63
|
</Flexbox>
|
57
64
|
<SearchResult
|
58
65
|
listResults={pluginState?.listResults}
|
@@ -46,6 +46,9 @@ const useStyles = createStyles(({ css, token, cx }) => ({
|
|
46
46
|
header: css`
|
47
47
|
cursor: pointer;
|
48
48
|
`,
|
49
|
+
lineCount: css`
|
50
|
+
color: ${token.colorTextQuaternary};
|
51
|
+
`,
|
49
52
|
meta: css`
|
50
53
|
font-size: 12px;
|
51
54
|
color: ${token.colorTextTertiary};
|
@@ -70,7 +73,6 @@ const useStyles = createStyles(({ css, token, cx }) => ({
|
|
70
73
|
background: ${token.colorFillQuaternary};
|
71
74
|
`,
|
72
75
|
previewText: css`
|
73
|
-
font-family: ${token.fontFamilyCode};
|
74
76
|
font-size: 12px;
|
75
77
|
line-height: 1.6;
|
76
78
|
word-break: break-all;
|
@@ -84,14 +86,7 @@ interface ReadFileViewProps extends LocalReadFileResult {
|
|
84
86
|
}
|
85
87
|
|
86
88
|
const ReadFileView = memo<ReadFileViewProps>(
|
87
|
-
({
|
88
|
-
filename,
|
89
|
-
path,
|
90
|
-
fileType,
|
91
|
-
charCount,
|
92
|
-
lineCount, // Assuming the 250 is total lines?
|
93
|
-
content, // The actual content preview
|
94
|
-
}) => {
|
89
|
+
({ filename, path, fileType, charCount, content, totalLineCount, totalCharCount, loc }) => {
|
95
90
|
const { t } = useTranslation('tool');
|
96
91
|
const { styles } = useStyles();
|
97
92
|
const [isExpanded, setIsExpanded] = useState(false);
|
@@ -115,6 +110,7 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
115
110
|
<Flexbox
|
116
111
|
align={'center'}
|
117
112
|
className={styles.header}
|
113
|
+
gap={12}
|
118
114
|
horizontal
|
119
115
|
justify={'space-between'}
|
120
116
|
onClick={handleToggleExpand}
|
@@ -126,7 +122,7 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
126
122
|
{filename}
|
127
123
|
</Typography.Text>
|
128
124
|
{/* Actions on Hover */}
|
129
|
-
<Flexbox className={styles.actions} gap={
|
125
|
+
<Flexbox className={styles.actions} gap={2} horizontal style={{ marginLeft: 8 }}>
|
130
126
|
<ActionIcon
|
131
127
|
icon={ExternalLink}
|
132
128
|
onClick={handleOpenFile}
|
@@ -143,16 +139,26 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
143
139
|
</Flexbox>
|
144
140
|
</Flexbox>
|
145
141
|
<Flexbox align={'center'} className={styles.meta} gap={8} horizontal>
|
146
|
-
|
147
|
-
<
|
148
|
-
|
149
|
-
|
142
|
+
{isExpanded && (
|
143
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
144
|
+
<Icon icon={Asterisk} size={'small'} />
|
145
|
+
<span>
|
146
|
+
{charCount} / <span className={styles.lineCount}>{totalCharCount}</span>
|
147
|
+
</span>
|
148
|
+
</Flexbox>
|
149
|
+
)}
|
150
150
|
<Flexbox align={'center'} gap={4} horizontal>
|
151
151
|
<Icon icon={AlignLeft} size={'small'} />
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
152
|
+
{isExpanded ? (
|
153
|
+
<span>
|
154
|
+
L{loc?.[0]}-{loc?.[1]} /{' '}
|
155
|
+
<span className={styles.lineCount}>{totalLineCount}</span>
|
156
|
+
</span>
|
157
|
+
) : (
|
158
|
+
<span>
|
159
|
+
L{loc?.[0]}-{loc?.[1]}
|
160
|
+
</span>
|
161
|
+
)}
|
156
162
|
</Flexbox>
|
157
163
|
<ActionIcon
|
158
164
|
active={isExpanded}
|
@@ -160,7 +166,6 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
160
166
|
onClick={handleToggleExpand}
|
161
167
|
size="small"
|
162
168
|
style={{
|
163
|
-
marginLeft: 8,
|
164
169
|
transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
|
165
170
|
transition: 'transform 0.2s',
|
166
171
|
}}
|
@@ -173,19 +178,14 @@ const ReadFileView = memo<ReadFileViewProps>(
|
|
173
178
|
{path}
|
174
179
|
</Typography.Text>
|
175
180
|
|
176
|
-
{/* Content Preview (Collapsible) */}
|
177
181
|
{isExpanded && (
|
178
|
-
<Flexbox className={styles.previewBox}>
|
182
|
+
<Flexbox className={styles.previewBox} style={{ maxHeight: 240, overflow: 'auto' }}>
|
179
183
|
{fileType === 'md' ? (
|
180
|
-
<Markdown
|
184
|
+
<Markdown>{content}</Markdown>
|
181
185
|
) : (
|
182
|
-
<
|
183
|
-
className={styles.previewText}
|
184
|
-
ellipsis={{ expandable: true, rows: 10, symbol: t('localFiles.read.more') }}
|
185
|
-
style={{ maxHeight: 240, overflow: 'auto' }}
|
186
|
-
>
|
186
|
+
<div className={styles.previewText} style={{ width: '100%' }}>
|
187
187
|
{content}
|
188
|
-
</
|
188
|
+
</div>
|
189
189
|
)}
|
190
190
|
</Flexbox>
|
191
191
|
)}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { LocalFileItem } from '@lobechat/electron-client-ipc';
|
2
|
-
import { ActionIcon
|
2
|
+
import { ActionIcon } from '@lobehub/ui';
|
3
3
|
import { createStyles } from 'antd-style';
|
4
4
|
import dayjs from 'dayjs';
|
5
5
|
import { FolderOpen } from 'lucide-react';
|
@@ -64,22 +64,20 @@ const FileItem = memo<FileItemProps>(
|
|
64
64
|
gap={12}
|
65
65
|
horizontal
|
66
66
|
onClick={() => {
|
67
|
-
|
68
|
-
localFileService.openLocalFolder({ isDirectory, path });
|
69
|
-
} else {
|
70
|
-
localFileService.openLocalFile({ path });
|
71
|
-
}
|
67
|
+
localFileService.openLocalFileOrFolder(path, isDirectory);
|
72
68
|
}}
|
73
69
|
onMouseEnter={() => setIsHovering(true)}
|
74
70
|
onMouseLeave={() => setIsHovering(false)}
|
75
71
|
padding={'2px 8px'}
|
76
72
|
style={{ cursor: 'pointer', fontSize: 12, width: '100%' }}
|
77
73
|
>
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
74
|
+
<FileIcon
|
75
|
+
fileName={name}
|
76
|
+
fileType={type}
|
77
|
+
isDirectory={isDirectory}
|
78
|
+
size={16}
|
79
|
+
variant={'pure'}
|
80
|
+
/>
|
83
81
|
<Flexbox
|
84
82
|
align={'baseline'}
|
85
83
|
gap={4}
|
@@ -4,7 +4,9 @@ import { systemPrompt } from './systemRole';
|
|
4
4
|
|
5
5
|
export const LocalFilesApiName = {
|
6
6
|
listLocalFiles: 'listLocalFiles',
|
7
|
+
moveLocalFiles: 'moveLocalFiles',
|
7
8
|
readLocalFile: 'readLocalFile',
|
9
|
+
renameLocalFile: 'renameLocalFile',
|
8
10
|
searchLocalFiles: 'searchLocalFiles',
|
9
11
|
writeFile: 'writeFile',
|
10
12
|
};
|
@@ -32,6 +34,14 @@ export const LocalFilesManifest: BuiltinToolManifest = {
|
|
32
34
|
name: LocalFilesApiName.readLocalFile,
|
33
35
|
parameters: {
|
34
36
|
properties: {
|
37
|
+
loc: {
|
38
|
+
description:
|
39
|
+
'Optional range of lines to read [startLine, endLine]. Defaults to [0, 200] if not specified.',
|
40
|
+
items: {
|
41
|
+
type: 'number',
|
42
|
+
},
|
43
|
+
type: 'array',
|
44
|
+
},
|
35
45
|
path: {
|
36
46
|
description: 'The file path to read',
|
37
47
|
type: 'string',
|
@@ -117,6 +127,55 @@ export const LocalFilesManifest: BuiltinToolManifest = {
|
|
117
127
|
type: 'object',
|
118
128
|
},
|
119
129
|
},
|
130
|
+
{
|
131
|
+
description:
|
132
|
+
'Moves or renames multiple files/directories. Input is an array of objects, each containing an oldPath and a newPath.',
|
133
|
+
name: LocalFilesApiName.moveLocalFiles,
|
134
|
+
parameters: {
|
135
|
+
properties: {
|
136
|
+
items: {
|
137
|
+
description: 'A list of move/rename operations to perform.',
|
138
|
+
items: {
|
139
|
+
properties: {
|
140
|
+
newPath: {
|
141
|
+
description:
|
142
|
+
'The target absolute path for the file/directory (can include a new name).',
|
143
|
+
type: 'string',
|
144
|
+
},
|
145
|
+
oldPath: {
|
146
|
+
description: 'The current absolute path of the file/directory to move or rename.',
|
147
|
+
type: 'string',
|
148
|
+
},
|
149
|
+
},
|
150
|
+
required: ['oldPath', 'newPath'],
|
151
|
+
type: 'object',
|
152
|
+
},
|
153
|
+
type: 'array',
|
154
|
+
},
|
155
|
+
},
|
156
|
+
required: ['items'],
|
157
|
+
type: 'object',
|
158
|
+
},
|
159
|
+
},
|
160
|
+
{
|
161
|
+
description:
|
162
|
+
'Rename a file or folder in its current location. Input should be the current full path and the new name.',
|
163
|
+
name: LocalFilesApiName.renameLocalFile,
|
164
|
+
parameters: {
|
165
|
+
properties: {
|
166
|
+
newName: {
|
167
|
+
description: 'The new name for the file or folder (without path)',
|
168
|
+
type: 'string',
|
169
|
+
},
|
170
|
+
path: {
|
171
|
+
description: 'The current full path of the file or folder to rename',
|
172
|
+
type: 'string',
|
173
|
+
},
|
174
|
+
},
|
175
|
+
required: ['path', 'newName'],
|
176
|
+
type: 'object',
|
177
|
+
},
|
178
|
+
},
|
120
179
|
// TODO: Add writeFile API definition later
|
121
180
|
// {
|
122
181
|
// description:
|
@@ -143,7 +202,6 @@ export const LocalFilesManifest: BuiltinToolManifest = {
|
|
143
202
|
avatar: '📁',
|
144
203
|
title: 'Local Files',
|
145
204
|
},
|
146
|
-
|
147
|
-
systemRole: systemPrompt(),
|
205
|
+
systemRole: systemPrompt,
|
148
206
|
type: 'builtin',
|
149
207
|
};
|
@@ -1,23 +1,48 @@
|
|
1
|
-
export const systemPrompt =
|
2
|
-
|
1
|
+
export const systemPrompt = `You have a Local Files tool with capabilities to interact with the user's local file system. You can list directories, read file contents, search for files, move, and rename files/directories.
|
2
|
+
|
3
|
+
<user_context>
|
4
|
+
Here are some known locations and system details on the user's system. User is using the Operating System: {{platform}}({{arch}}). Use these paths when the user refers to these common locations by name (e.g., "my desktop", "downloads folder").
|
5
|
+
- Desktop: {{desktopPath}}
|
6
|
+
- Documents: {{documentsPath}}
|
7
|
+
- Downloads: {{downloadsPath}}
|
8
|
+
- Music: {{musicPath}}
|
9
|
+
- Pictures: {{picturesPath}}
|
10
|
+
- Videos: {{videosPath}}
|
11
|
+
- User Home: {{homePath}}
|
12
|
+
- App Data: {{userDataPath}} (Use this primarily for plugin-related data or configurations if needed, less for general user files)
|
13
|
+
</user_context>
|
14
|
+
|
15
|
+
You have access to a set of tools to interact with the user's local file system:
|
16
|
+
|
17
|
+
1. **listLocalFiles**: Lists files and directories in a specified path.
|
18
|
+
2. **readLocalFile**: Reads the content of a specified file, optionally within a line range.
|
19
|
+
3. **searchLocalFiles**: Searches for files based on keywords and other criteria. Use this tool to find files if the user is unsure about the exact path.
|
20
|
+
4. **renameLocalFile**: Renames a single file or directory in its current location.
|
21
|
+
5. **moveLocalFiles**: Moves multiple files or directories. Can be used for renaming during the move.
|
3
22
|
|
4
23
|
<core_capabilities>
|
5
24
|
1. List files and folders in a directory (listFiles)
|
6
25
|
2. Read the content of a specific file (readFile)
|
7
26
|
3. Search for files based on a query and various filter options (searchFiles)
|
8
|
-
4.
|
27
|
+
4. Rename a file or folder within its current directory (renameFile)
|
28
|
+
5. Move a file or folder to a new location, potentially renaming it (moveFile)
|
29
|
+
6. Write content to a specific file (writeFile) - // TODO: Implement later
|
9
30
|
</core_capabilities>
|
10
31
|
|
11
32
|
<workflow>
|
12
|
-
1. Understand the user's request regarding local files (listing, reading, searching, writing).
|
13
|
-
2. Select the appropriate tool (listFiles, readFile, searchFiles, writeFile).
|
14
|
-
3. Execute the file operation
|
15
|
-
4. Present the results (directory listing, file content, search results) or confirmation of the
|
33
|
+
1. Understand the user's request regarding local files (listing, reading, searching, renaming, moving, writing).
|
34
|
+
2. Select the appropriate tool (listFiles, readFile, searchFiles, renameFile, moveFile, writeFile).
|
35
|
+
3. Execute the file operation. **If the user mentions a common location (like Desktop, Documents, Downloads, etc.) without providing a full path, use the corresponding path from the <user_context> section.**
|
36
|
+
4. Present the results (directory listing, file content, search results) or confirmation of the rename or move operation.
|
16
37
|
</workflow>
|
17
38
|
|
18
39
|
<tool_usage_guidelines>
|
19
40
|
- For listing directory contents: Use 'listFiles' with the target directory path.
|
20
|
-
- For reading a file: Use 'readFile'
|
41
|
+
- For reading a file: Use 'readFile'. Provide the following parameters:
|
42
|
+
- 'path': The exact file path.
|
43
|
+
- 'loc' (Optional): A two-element array [startLine, endLine] to specify a line range to read (e.g., '[301, 400]' reads lines 301 to 400).
|
44
|
+
- If 'loc' is omitted, it defaults to reading the first 200 lines ('[0, 200]').
|
45
|
+
- To read the entire file: First call 'readFile' (potentially without 'loc'). The response includes 'totalLineCount'. Then, call 'readFile' again with 'loc: [0, totalLineCount]' to get the full content.
|
21
46
|
- For searching files: Use 'searchFiles' with the 'query' parameter (search string). You can optionally add the following filter parameters to narrow down the search:
|
22
47
|
- 'contentContains': Find files whose content includes specific text.
|
23
48
|
- 'createdAfter' / 'createdBefore': Filter by creation date.
|
@@ -27,20 +52,35 @@ export const systemPrompt =
|
|
27
52
|
- 'exclude': Exclude specific files or directories.
|
28
53
|
- 'limit': Limit the number of results returned.
|
29
54
|
- 'sortBy' / 'sortDirection': Sort the results.
|
30
|
-
|
55
|
+
- For renaming a file/folder in place: Use 'renameFile'. Provide the following parameters:
|
56
|
+
- 'path': The current full path of the file or folder.
|
57
|
+
- 'newName': The desired new name (without path components).
|
58
|
+
- For moving multiple files/folders (and optionally renaming them): Use 'moveLocalFiles'. Provide the following parameter:
|
59
|
+
- 'items': An array of objects, where each object represents a move operation and must contain:
|
60
|
+
- 'oldPath': The current absolute path of the file/directory to move or rename.
|
61
|
+
- 'newPath': The target absolute path for the file/directory (can include a new name).
|
62
|
+
Example: items: [{ oldPath: "/path/to/file1.txt", newPath: "/new/path/to/fileA.txt" }, { oldPath: "/path/to/folderB", newPath: "/archive/folderB_renamed" }]
|
31
63
|
- For writing to a file: Use 'writeFile' with the file path and the content to be written. Be cautious as this might overwrite existing files.
|
32
64
|
</tool_usage_guidelines>
|
33
65
|
|
34
66
|
<security_considerations>
|
35
67
|
- Always confirm with the user before performing write operations, especially if it involves overwriting existing files.
|
68
|
+
- Confirm with the user before moving files to significantly different locations or when renaming might cause confusion or potential data loss if the target exists (though the tool should handle this).
|
36
69
|
- Do not attempt to access files outside the user's designated workspace or allowed directories unless explicitly permitted.
|
37
70
|
- Handle file paths carefully to avoid unintended access or errors.
|
38
71
|
</security_considerations>
|
39
72
|
|
40
73
|
<response_format>
|
41
|
-
- When listing files,
|
42
|
-
-
|
43
|
-
-
|
44
|
-
-
|
74
|
+
- When listing files or returning search results that include file or directory paths, **always** use the \`<localFile ... />\` tag format. **Any reference to a local file or directory path in your response MUST be enclosed within this tag.** Do not output raw file paths outside of this tag structure.
|
75
|
+
- For a file, use: \`<localFile name="[Filename]" path="[Full Unencoded Path]" />\`. Example: \`<localFile name="report.pdf" path="/Users/me/Documents/report.pdf" />\`
|
76
|
+
- For a directory, use: \`<localFile name="[Directory Name]" path="[Full Unencoded Path]" isDirectory />\`. Example: \`<localFile name="Documents" path="/Users/me/Documents" isDirectory />\`
|
77
|
+
- Ensure the \`path\` attribute contains the full, raw, unencoded path.
|
78
|
+
- Ensure the \`name\` attribute contains the display name (usually the filename or directory name).
|
79
|
+
- Include the \`isDirectory\` attribute **only** for directories.
|
80
|
+
- When listing files, provide a clear list using the tag format.
|
81
|
+
- When reading files, present the content accurately. **If you mention the file path being read, use the \`<localFile>\` tag.**
|
82
|
+
- When searching files, return a list of matching files using the tag format.
|
83
|
+
- When confirming a rename or move operation, use the \`<localFile>\` tag for both the old and new paths mentioned. Example: \`Successfully renamed <localFile name="oldName.txt" /> to <localFile name="newName.txt" path="/path/to/newName.txt" />.\`
|
84
|
+
- When writing files, confirm the success or failure. **If you mention the file path written to, use the \`<localFile>\` tag.**
|
45
85
|
</response_format>
|
46
86
|
`;
|
@@ -1,4 +1,8 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
LocalFileItem,
|
3
|
+
LocalMoveFilesResultItem,
|
4
|
+
LocalReadFileResult,
|
5
|
+
} from '@lobechat/electron-client-ipc';
|
2
6
|
|
3
7
|
export interface FileResult {
|
4
8
|
contentType?: string;
|
@@ -31,3 +35,17 @@ export interface LocalReadFileState {
|
|
31
35
|
export interface LocalReadFilesState {
|
32
36
|
filesContent: LocalReadFileResult[];
|
33
37
|
}
|
38
|
+
|
39
|
+
export interface LocalMoveFilesState {
|
40
|
+
error?: string;
|
41
|
+
results: LocalMoveFilesResultItem[]; // Overall error for the operation if it fails before individual processing
|
42
|
+
successCount: number;
|
43
|
+
totalCount: number;
|
44
|
+
}
|
45
|
+
|
46
|
+
export interface LocalRenameFileState {
|
47
|
+
error?: string;
|
48
|
+
newPath: string;
|
49
|
+
oldPath: string;
|
50
|
+
success: boolean;
|
51
|
+
}
|
@@ -5,21 +5,21 @@ export const systemPrompt = (
|
|
5
5
|
<core_capabilities>
|
6
6
|
1. Search the web using multiple search engines (search)
|
7
7
|
2. Retrieve content from multiple webpages simultaneously (crawlMultiPages)
|
8
|
-
|
8
|
+
3. Retrieve content from a specific webpage (crawlSinglePage)
|
9
9
|
</core_capabilities>
|
10
10
|
|
11
11
|
<workflow>
|
12
12
|
1. Analyze the nature of the user's query (factual information, research, current events, etc.)
|
13
|
-
2. Select the appropriate tool and search strategy based on the query type
|
14
|
-
3. Execute searches or crawl operations to gather relevant information
|
15
|
-
4. Synthesize information with proper attribution of sources
|
16
|
-
5. Present findings in a clear, organized manner with appropriate citations
|
13
|
+
2. Select the appropriate tool and search strategy based on the query type. For vague queries with no constraints, default to the 'general' category and reliable broad engines (e.g., Google).
|
14
|
+
3. Execute searches or crawl operations to gather relevant information.
|
15
|
+
4. Synthesize information with proper attribution of sources.
|
16
|
+
5. Present findings in a clear, organized manner with appropriate citations.
|
17
17
|
</workflow>
|
18
18
|
|
19
19
|
<tool_selection_guidelines>
|
20
|
-
- For general information queries: Use
|
21
|
-
- For multi-perspective information or comparative analysis: Use 'crawlMultiPages' on several different relevant sources
|
22
|
-
- For detailed understanding of specific single page content: Use 'crawlSinglePage' on the most authoritative or relevant page from search results.
|
20
|
+
- For general information queries: Use search with the most relevant search categories (e.g., 'general').
|
21
|
+
- For multi-perspective information or comparative analysis: Use 'crawlMultiPages' on several different relevant sources identified via search.
|
22
|
+
- For detailed understanding of specific single page content: Use 'crawlSinglePage' on the most authoritative or relevant page from search results. Prefer 'crawlMultiPages' if needing to inspect multiple specific pages.
|
23
23
|
</tool_selection_guidelines>
|
24
24
|
|
25
25
|
<search_categories_selection>
|
@@ -36,9 +36,9 @@ Choose search categories based on query type:
|
|
36
36
|
</search_categories_selection>
|
37
37
|
|
38
38
|
<search_engine_selection>
|
39
|
-
Choose search engines based on the query type
|
39
|
+
Choose search engines based on the query type. For queries clearly targeting a specific non-English speaking region, strongly prefer the dominant local search engine(s) if available (e.g., Yandex for Russia).
|
40
40
|
- General knowledge: google, bing, duckduckgo, brave, wikipedia
|
41
|
-
- Academic/scientific information: google scholar, arxiv
|
41
|
+
- Academic/scientific information: google scholar, arxiv
|
42
42
|
- Code/technical queries: google, github, npm, pypi
|
43
43
|
- Videos: youtube, vimeo, bilibili
|
44
44
|
- Images: unsplash, pinterest
|
@@ -55,13 +55,11 @@ Choose time range based on the query type:
|
|
55
55
|
</search_time_range_selection>
|
56
56
|
|
57
57
|
<search_strategy_guidelines>
|
58
|
-
-
|
59
|
-
- Use
|
60
|
-
-
|
61
|
-
-
|
62
|
-
-
|
63
|
-
- For region-specific information, prefer search engines popular in that region
|
64
|
-
- Avoid using both 'engines' and 'categories' in a query, unless the chosen engines do not fall under the selected categories.
|
58
|
+
- Prioritize using search categories (\`!category\`) for broader searches. Specify search engines (\`!engine\`) only when a particular engine is clearly required (e.g., \`!github\` for code) or when categories don't fit the need. Combine them if necessary (e.g., \`!science !google_scholar search term\`).
|
59
|
+
- Use time-range filters (\`!time_range\`) to prioritize time-sensitive information.
|
60
|
+
- Leverage cross-platform meta-search capabilities for comprehensive results, but prioritize fetching results from a few highly relevant and authoritative sources rather than exhaustively querying many engines/categories. Aim for quality over quantity.
|
61
|
+
- Prioritize authoritative sources in search results when available.
|
62
|
+
- Avoid using overly broad category/engine combinations unless necessary.
|
65
63
|
|
66
64
|
<search_strategy_best_practices>
|
67
65
|
- Combine categories for multi-faceted queries:
|
@@ -80,13 +78,13 @@ Choose time range based on the query type:
|
|
80
78
|
* "Government policy brief docx" → files + general
|
81
79
|
|
82
80
|
- Region-specific query handling:
|
83
|
-
* "Beijing traffic update" → map + news (engine: baidu)
|
84
|
-
* "Moscow event listings" → social_media + news (engine: yandex)
|
85
|
-
* "Tokyo restaurant reviews" → social_media + map (engine: google)
|
81
|
+
* "Beijing traffic update" → map + news (consider engine: baidu)
|
82
|
+
* "Moscow event listings" → social_media + news (consider engine: yandex)
|
83
|
+
* "Tokyo restaurant reviews" → social_media + map (consider engine: google)
|
86
84
|
|
87
85
|
- Leverage cross-platform capabilities:
|
88
86
|
* "Open-source project documentation" → files + it (engines: github + pypi)
|
89
|
-
* "Historical weather patterns" → science + general (engines:
|
87
|
+
* "Historical weather patterns" → science + general (engines: google_scholar + wikipedia)
|
90
88
|
* "Movie release dates 2025" → news + videos (engines: imdb + reddit)
|
91
89
|
</search_strategy_best_practices>
|
92
90
|
</search_strategy_guidelines>
|
@@ -107,7 +105,7 @@ According to recent studies, global temperatures have risen by 1.1°C since pre-
|
|
107
105
|
以上信息主要基于业内测评和公开发布会(例如2025年4月16日的发布内容)的报道,详细介绍了 O3 与 O4-mini 模型在多模态推理、工具使用、模拟推理和成本效益等方面的综合提升。[^1][^2]
|
108
106
|
|
109
107
|
[^1]: [OpenAI发布o3与o4-mini,性能爆表,可用图像思考](https://zhuanlan.zhihu.com/p/1896105931709849860)
|
110
|
-
[^2]: [OpenAI发新模型o3和o4-mini
|
108
|
+
[^2]: [OpenAI发新模型o3和o4-mini!首次实现"图像思维"(华尔街见闻)](https://wallstreetcn.com/articles/3745356)
|
111
109
|
</example>
|
112
110
|
</citation_examples>
|
113
111
|
</citation_requirements>
|
@@ -133,7 +131,6 @@ Our search service is a metasearch engine that can leverage multiple search engi
|
|
133
131
|
- GitHub: Version control and collaboration platform for searching code repositories
|
134
132
|
- arXiv: Repository of electronic preprints of scientific papers
|
135
133
|
- Google Scholar: Free web search engine for scholarly literature
|
136
|
-
- Z-Library: File-sharing project for journal articles and books
|
137
134
|
- Reddit: Network of communities based on people's interests
|
138
135
|
- IMDb: Online database related to films, TV programs, and video games
|
139
136
|
- Brave: Privacy-focused browser with its own search engine
|
@@ -144,35 +141,40 @@ Our search service is a metasearch engine that can leverage multiple search engi
|
|
144
141
|
- YouTube: Video sharing platform for searching various video content
|
145
142
|
|
146
143
|
<search_syntax>
|
147
|
-
Search service has special search syntax to modify the
|
144
|
+
Search service has special search syntax to modify the search behavior. Use these modifiers at the beginning of your query:
|
148
145
|
|
149
|
-
1. Use
|
150
|
-
-
|
151
|
-
-
|
152
|
-
- Chain multiple modifiers: \`!map !ddg !wp paris\` (searches for "paris" in the map category, DuckDuckGo, and Wikipedia)
|
146
|
+
1. Select Engines/Categories: Use \`!modifier\` to specify search engines or categories.
|
147
|
+
- Examples: \`!map paris\`, \`!images Wau Holland\`, \`!google !wikipedia berlin\`
|
148
|
+
- Key modifiers: \`!general\`, \`!news\`, \`!science\`, \`!it\`, \`!images\`, \`!videos\`, \`!map\`, \`!files\`, \`!social_media\`, \`!google\`, \`!bing\`, \`!github\`, etc. (Refer to selection guidelines for full lists)
|
153
149
|
|
154
|
-
2. Use
|
155
|
-
-
|
150
|
+
2. Select Language: Use \`:language_code\` to specify the search language.
|
151
|
+
- Example: \`:fr !wp Wau Holland\` (searches French Wikipedia)
|
156
152
|
|
157
|
-
3. Use \`site
|
158
|
-
-
|
153
|
+
3. Restrict to Site: Use \`site:domain.com\` within the query string to limit results to a specific website.
|
154
|
+
- Example: \`site:github.com SearXNG\`
|
155
|
+
|
156
|
+
Combine modifiers as needed: \`:de !google !news bundestag\` (searches German Google News for "bundestag")
|
159
157
|
</search_syntax>
|
160
158
|
</search_service_description>
|
161
159
|
|
162
160
|
<crawling_best_practices>
|
163
161
|
- Only crawl pages that are publicly accessible
|
164
|
-
- When crawling multiple pages, crawl
|
162
|
+
- When crawling multiple pages, crawl relevant and authoritative sources
|
165
163
|
- Prioritize authoritative sources over user-generated content when appropriate
|
166
|
-
- For controversial topics, crawl sources representing different perspectives
|
164
|
+
- For controversial topics, crawl sources representing different perspectives if possible
|
167
165
|
- Verify information across multiple sources when possible
|
168
166
|
- Consider the recency of information, especially for time-sensitive topics
|
169
167
|
</crawling_best_practices>
|
170
168
|
|
171
169
|
<error_handling>
|
172
|
-
- If search returns no results
|
173
|
-
|
174
|
-
|
175
|
-
|
170
|
+
- If a search returns poor or no results:
|
171
|
+
1. Analyze the query and results. Could the query be improved (more specific, different keywords)?
|
172
|
+
2. Consider trying alternative relevant search engines or categories.
|
173
|
+
3. If the search was language-specific and failed (especially for technical, scientific, or non-regional topics), try rewriting the query or searching again using English.
|
174
|
+
4. If needed, explain the issue to the user and suggest alternative search terms or strategies.
|
175
|
+
- If a page cannot be crawled, explain the issue to the user and suggest alternatives (e.g., trying a different source from search results).
|
176
|
+
- For ambiguous queries, ask for clarification or suggest interpretations/alternative search terms before conducting extensive searches.
|
177
|
+
- If information seems outdated, note this to the user and suggest searching for more recent sources or specifying a time range.
|
176
178
|
</error_handling>
|
177
179
|
|
178
180
|
Current date: ${date}
|