@lobehub/chat 1.81.7 → 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.
Files changed (55) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/chat.json +10 -0
  4. package/locales/bg-BG/chat.json +10 -0
  5. package/locales/de-DE/chat.json +10 -0
  6. package/locales/en-US/chat.json +10 -0
  7. package/locales/es-ES/chat.json +10 -0
  8. package/locales/fa-IR/chat.json +10 -0
  9. package/locales/fr-FR/chat.json +10 -0
  10. package/locales/it-IT/chat.json +10 -0
  11. package/locales/ja-JP/chat.json +10 -0
  12. package/locales/ko-KR/chat.json +10 -0
  13. package/locales/nl-NL/chat.json +10 -0
  14. package/locales/pl-PL/chat.json +10 -0
  15. package/locales/pt-BR/chat.json +10 -0
  16. package/locales/ru-RU/chat.json +10 -0
  17. package/locales/tr-TR/chat.json +10 -0
  18. package/locales/vi-VN/chat.json +10 -0
  19. package/locales/zh-CN/chat.json +10 -0
  20. package/locales/zh-TW/chat.json +10 -0
  21. package/package.json +1 -1
  22. package/packages/electron-client-ipc/src/events/localFile.ts +8 -2
  23. package/packages/electron-client-ipc/src/events/system.ts +3 -0
  24. package/packages/electron-client-ipc/src/types/index.ts +1 -0
  25. package/packages/electron-client-ipc/src/types/localFile.ts +46 -0
  26. package/packages/electron-client-ipc/src/types/system.ts +24 -0
  27. package/packages/file-loaders/src/blackList.ts +9 -0
  28. package/packages/file-loaders/src/index.ts +1 -0
  29. package/packages/file-loaders/src/loaders/pdf/index.test.ts +1 -0
  30. package/packages/file-loaders/src/loaders/pdf/index.ts +1 -7
  31. package/src/components/FileIcon/index.tsx +7 -3
  32. package/src/features/Conversation/Extras/Usage/UsageDetail/index.tsx +31 -4
  33. package/src/features/Conversation/Extras/Usage/index.tsx +1 -1
  34. package/src/libs/agent-runtime/anthropic/index.ts +7 -3
  35. package/src/libs/agent-runtime/perplexity/index.test.ts +4 -1
  36. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +17 -8
  37. package/src/libs/agent-runtime/utils/streams/anthropic.test.ts +11 -5
  38. package/src/libs/agent-runtime/utils/streams/anthropic.ts +11 -2
  39. package/src/libs/agent-runtime/utils/streams/openai.ts +5 -2
  40. package/src/libs/agent-runtime/utils/streams/protocol.test.ts +67 -1
  41. package/src/libs/agent-runtime/utils/streams/protocol.ts +46 -1
  42. package/src/locales/default/chat.ts +11 -0
  43. package/src/services/electron/localFileService.ts +19 -0
  44. package/src/services/electron/system.ts +21 -0
  45. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +2 -2
  46. package/src/store/chat/slices/builtinTool/actions/search.ts +0 -3
  47. package/src/tools/local-files/Render/ListFiles/index.tsx +24 -17
  48. package/src/tools/local-files/Render/ReadLocalFile/ReadFileView.tsx +28 -28
  49. package/src/tools/local-files/components/FileItem.tsx +9 -11
  50. package/src/tools/local-files/index.ts +60 -2
  51. package/src/tools/local-files/systemRole.ts +53 -13
  52. package/src/tools/local-files/type.ts +19 -1
  53. package/src/tools/web-browsing/systemRole.ts +40 -38
  54. package/src/types/message/base.ts +8 -0
  55. package/src/utils/fetch/fetchSSE.ts +17 -1
@@ -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={8} horizontal style={{ marginLeft: 8 }}>
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
- <Flexbox align={'center'} gap={4} horizontal>
147
- <Icon icon={Asterisk} size={'small'} />
148
- <span>{charCount}</span>
149
- </Flexbox>
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
- <span>
153
- {content?.split('\n').length || 0} / {lineCount}
154
- </span>
155
- {/* Display preview lines / total lines */}
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 style={{ maxHeight: 240, overflow: 'auto' }}>{content}</Markdown>
184
+ <Markdown>{content}</Markdown>
181
185
  ) : (
182
- <Typography.Paragraph
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
- </Typography.Paragraph>
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, FileTypeIcon } from '@lobehub/ui';
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
- if (isDirectory) {
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
- {isDirectory ? (
79
- <FileTypeIcon size={16} type={'folder'} variant={'mono'} />
80
- ) : (
81
- <FileIcon fileName={name} fileType={type} size={16} variant={'pure'} />
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
- // Use a simplified system role for now
147
- systemRole: systemPrompt(),
205
+ systemRole: systemPrompt,
148
206
  type: 'builtin',
149
207
  };
@@ -1,23 +1,48 @@
1
- export const systemPrompt =
2
- () => `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, and potentially write files.
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. Write content to a specific file (writeFile) - // TODO: Implement later
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 based on the provided path, query, and filter options.
15
- 4. Present the results (directory listing, file content, search results) or confirmation of the write operation.
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' with the exact file path.
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
- - 'detailed': Get more detailed output information.
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, provide a clear list of files and folders.
42
- - When reading files, present the content accurately.
43
- - When searching files, return a list of matching files, including relevant metadata if detailed information was requested.
44
- - When writing files, confirm the success or failure of the operation.
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 { LocalFileItem, LocalReadFileResult } from '@lobechat/electron-client-ipc';
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
- 2. Retrieve content from a specific webpage (crawlSinglePage)
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 searchWithSearXNG with the most relevant search engines
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. If you need to visit multiple pages, prefer to use 'crawlMultiPages'
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, z-library
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
- - Use engine-based searches when a specific search engine is explicitly required
59
- - Use category-based searches when unsure about engine selection
60
- - Use time-range filters to prioritize time-sensitive information
61
- - Leverage cross-platform meta-search capabilities for comprehensive results
62
- - Prioritize authoritative sources in search results when available
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: google scholar + wikipedia)
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!首次实现“图像思维”(华尔街见闻)](https://wallstreetcn.com/articles/3745356)
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 categories, engines, and language of searches:
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 \`!\` to select engines and categories:
150
- - Search for "paris" in the "map" category: \`!map paris\`
151
- - Search for images: \`!images Wau Holland\`
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 \`:\` to select language:
155
- - Search Wikipedia in a specific language: \`:fr !wp Wau Holland\` (uses French)
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:\` to restrict results to a specific website:
158
- - Search SearXNG from a specific website: \`site:github.com SearXNG\`
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 all relevant sources
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, try alternative search terms or engines
173
- - If a page cannot be crawled, explain the issue to the user and suggest alternatives
174
- - For ambiguous queries, ask for clarification before conducting extensive searches
175
- - If information seems outdated, note this to the user and suggest searching for more recent sources
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}
@@ -44,8 +44,16 @@ export interface ModelTokensUsage {
44
44
  totalTokens?: number;
45
45
  }
46
46
 
47
+ export interface ModelSpeed {
48
+ // tokens per second
49
+ tps?: number;
50
+ // time to fist token
51
+ ttft?: number;
52
+ }
53
+
47
54
  export interface MessageMetadata extends ModelTokensUsage {
48
55
  tps?: number;
56
+ ttft?: number;
49
57
  }
50
58
 
51
59
  export type MessageRoleType = 'user' | 'system' | 'assistant' | 'tool';
@@ -11,6 +11,7 @@ import {
11
11
  MessageToolCallChunk,
12
12
  MessageToolCallSchema,
13
13
  ModelReasoning,
14
+ ModelSpeed,
14
15
  ModelTokensUsage,
15
16
  } from '@/types/message';
16
17
  import { ChatImageChunk } from '@/types/message/image';
@@ -29,6 +30,7 @@ export type OnFinishHandler = (
29
30
  images?: ChatImageChunk[];
30
31
  observationId?: string | null;
31
32
  reasoning?: ModelReasoning;
33
+ speed?: ModelSpeed;
32
34
  toolCalls?: MessageToolCall[];
33
35
  traceId?: string | null;
34
36
  type?: SSEFinishType;
@@ -41,6 +43,11 @@ export interface MessageUsageChunk {
41
43
  usage: ModelTokensUsage;
42
44
  }
43
45
 
46
+ export interface MessageSpeedChunk {
47
+ speed: ModelSpeed;
48
+ type: 'speed';
49
+ }
50
+
44
51
  export interface MessageTextChunk {
45
52
  text: string;
46
53
  type: 'text';
@@ -82,7 +89,8 @@ export interface FetchSSEOptions {
82
89
  | MessageReasoningChunk
83
90
  | MessageGroundingChunk
84
91
  | MessageUsageChunk
85
- | MessageBase64ImageChunk,
92
+ | MessageBase64ImageChunk
93
+ | MessageSpeedChunk,
86
94
  ) => void;
87
95
  smoothing?: SmoothingParams | boolean;
88
96
  }
@@ -342,6 +350,7 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
342
350
  let grounding: GroundingSearch | undefined = undefined;
343
351
  let usage: ModelTokensUsage | undefined = undefined;
344
352
  let images: ChatImageChunk[] = [];
353
+ let speed: ModelSpeed | undefined = undefined;
345
354
 
346
355
  await fetchEventSource(url, {
347
356
  body: options.body,
@@ -433,6 +442,12 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
433
442
  break;
434
443
  }
435
444
 
445
+ case 'speed': {
446
+ speed = data;
447
+ options.onMessageHandle?.({ speed: data, type: 'speed' });
448
+ break;
449
+ }
450
+
436
451
  case 'grounding': {
437
452
  grounding = data;
438
453
  options.onMessageHandle?.({ grounding: data, type: 'grounding' });
@@ -517,6 +532,7 @@ export const fetchSSE = async (url: string, options: RequestInit & FetchSSEOptio
517
532
  images: images.length > 0 ? images : undefined,
518
533
  observationId,
519
534
  reasoning: !!thinking ? { content: thinking, signature: thinkingSignature } : undefined,
535
+ speed,
520
536
  toolCalls,
521
537
  traceId,
522
538
  type: finishedType,