@lobehub/chat 1.66.6 → 1.67.1

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 (105) hide show
  1. package/.env.example +3 -3
  2. package/CHANGELOG.md +42 -0
  3. package/Dockerfile +3 -9
  4. package/Dockerfile.database +11 -11
  5. package/Dockerfile.pglite +3 -9
  6. package/README.md +17 -20
  7. package/README.zh-CN.md +6 -6
  8. package/changelog/v1.json +14 -0
  9. package/locales/ar/models.json +9 -3
  10. package/locales/ar/plugin.json +12 -0
  11. package/locales/bg-BG/models.json +9 -3
  12. package/locales/bg-BG/plugin.json +12 -0
  13. package/locales/de-DE/models.json +9 -3
  14. package/locales/de-DE/plugin.json +12 -0
  15. package/locales/en-US/models.json +9 -3
  16. package/locales/en-US/plugin.json +12 -0
  17. package/locales/es-ES/models.json +9 -3
  18. package/locales/es-ES/plugin.json +12 -0
  19. package/locales/fa-IR/models.json +9 -3
  20. package/locales/fa-IR/plugin.json +12 -0
  21. package/locales/fr-FR/models.json +9 -3
  22. package/locales/fr-FR/plugin.json +12 -0
  23. package/locales/it-IT/models.json +9 -3
  24. package/locales/it-IT/plugin.json +12 -0
  25. package/locales/ja-JP/models.json +9 -3
  26. package/locales/ja-JP/plugin.json +12 -0
  27. package/locales/ko-KR/models.json +9 -3
  28. package/locales/ko-KR/plugin.json +12 -0
  29. package/locales/nl-NL/models.json +9 -3
  30. package/locales/nl-NL/plugin.json +12 -0
  31. package/locales/pl-PL/models.json +9 -3
  32. package/locales/pl-PL/plugin.json +12 -0
  33. package/locales/pt-BR/models.json +9 -3
  34. package/locales/pt-BR/plugin.json +12 -0
  35. package/locales/ru-RU/models.json +9 -3
  36. package/locales/ru-RU/plugin.json +12 -0
  37. package/locales/tr-TR/models.json +9 -3
  38. package/locales/tr-TR/plugin.json +12 -0
  39. package/locales/vi-VN/models.json +9 -3
  40. package/locales/vi-VN/plugin.json +12 -0
  41. package/locales/zh-CN/models.json +9 -3
  42. package/locales/zh-CN/plugin.json +12 -0
  43. package/locales/zh-TW/models.json +9 -3
  44. package/locales/zh-TW/plugin.json +12 -0
  45. package/next.config.ts +3 -0
  46. package/package.json +5 -1
  47. package/packages/web-crawler/README.md +34 -0
  48. package/packages/web-crawler/package.json +13 -0
  49. package/packages/web-crawler/src/crawImpl/browserless.ts +62 -0
  50. package/packages/web-crawler/src/crawImpl/index.ts +11 -0
  51. package/packages/web-crawler/src/crawImpl/jina.ts +37 -0
  52. package/packages/web-crawler/src/crawImpl/naive.ts +84 -0
  53. package/packages/web-crawler/src/crawler.ts +66 -0
  54. package/packages/web-crawler/src/index.ts +2 -0
  55. package/packages/web-crawler/src/type.ts +42 -0
  56. package/packages/web-crawler/src/urlRules.ts +34 -0
  57. package/packages/web-crawler/src/utils/__snapshots__/htmlToMarkdown.test.ts.snap +638 -0
  58. package/packages/web-crawler/src/utils/appUrlRules.test.ts +26 -0
  59. package/packages/web-crawler/src/utils/appUrlRules.ts +40 -0
  60. package/packages/web-crawler/src/utils/errorType.ts +12 -0
  61. package/packages/web-crawler/src/utils/html/terms.html +1222 -0
  62. package/packages/web-crawler/src/utils/html/yingchao.html +1001 -0
  63. package/packages/web-crawler/src/utils/htmlToMarkdown.test.ts +35 -0
  64. package/packages/web-crawler/src/utils/htmlToMarkdown.ts +45 -0
  65. package/packages/web-crawler/tsconfig.json +20 -0
  66. package/pnpm-workspace.yaml +3 -0
  67. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +4 -35
  68. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +1 -1
  69. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +3 -0
  70. package/src/features/PluginsUI/Render/index.tsx +1 -0
  71. package/src/features/Portal/Plugins/Body/ToolRender.tsx +1 -0
  72. package/src/locales/default/plugin.ts +12 -0
  73. package/src/server/routers/tools/search.ts +23 -0
  74. package/src/services/search.ts +8 -0
  75. package/src/store/chat/slices/builtinTool/actions/searXNG.ts +50 -0
  76. package/src/store/chat/slices/builtinTool/initialState.ts +1 -0
  77. package/src/tools/web-browsing/Portal/PageContent/index.tsx +190 -0
  78. package/src/tools/web-browsing/Portal/PageContents/index.tsx +23 -0
  79. package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/Video.tsx +1 -1
  80. package/src/tools/web-browsing/Portal/Search/index.tsx +69 -0
  81. package/src/tools/web-browsing/Portal/index.tsx +28 -64
  82. package/src/tools/web-browsing/Render/PageContent/Loading.tsx +57 -0
  83. package/src/tools/web-browsing/Render/PageContent/Result.tsx +142 -0
  84. package/src/tools/web-browsing/Render/PageContent/index.tsx +41 -0
  85. package/src/tools/web-browsing/Render/{SearchQuery → Search/SearchQuery}/SearchView.tsx +1 -1
  86. package/src/tools/web-browsing/Render/{SearchQuery → Search/SearchQuery}/index.tsx +1 -1
  87. package/src/tools/web-browsing/Render/{SearchResult → Search/SearchResult}/ShowMore.tsx +1 -1
  88. package/src/tools/web-browsing/Render/Search/index.tsx +62 -0
  89. package/src/tools/web-browsing/Render/index.tsx +35 -44
  90. package/src/tools/web-browsing/index.ts +43 -47
  91. package/src/tools/web-browsing/systemRole.ts +109 -0
  92. package/src/types/tool/builtin.ts +2 -0
  93. package/src/types/tool/crawler.ts +19 -0
  94. package/src/types/tool/search.ts +1 -0
  95. /package/src/tools/web-browsing/Portal/{Footer.tsx → Search/Footer.tsx} +0 -0
  96. /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/CategoryAvatar.tsx +0 -0
  97. /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/TitleExtra.tsx +0 -0
  98. /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/index.tsx +0 -0
  99. /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/index.tsx +0 -0
  100. /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/Form.tsx +0 -0
  101. /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/SearchXNGIcon.tsx +0 -0
  102. /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/index.tsx +0 -0
  103. /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/style.tsx +0 -0
  104. /package/src/tools/web-browsing/Render/{SearchResult → Search/SearchResult}/SearchResultItem.tsx +0 -0
  105. /package/src/tools/web-browsing/Render/{SearchResult → Search/SearchResult}/index.tsx +0 -0
@@ -1,77 +1,41 @@
1
- import { Skeleton } from 'antd';
2
- import { uniq } from 'lodash-es';
3
1
  import { memo } from 'react';
4
- import { Flexbox } from 'react-layout-kit';
5
2
 
6
- import { useChatStore } from '@/store/chat';
7
- import { chatToolSelectors } from '@/store/chat/selectors';
8
- import { SearchResponse } from '@/types/tool/search';
3
+ import { WebBrowsingApiName } from '@/tools/web-browsing';
4
+ import { BuiltinPortalProps } from '@/types/tool';
5
+ import { CrawlPluginState } from '@/types/tool/crawler';
6
+ import { SearchQuery } from '@/types/tool/search';
9
7
 
10
- import SearchBar from '../components/SearchBar';
11
- import Footer from './Footer';
12
- import ResultList from './ResultList';
8
+ import PageContent from './PageContent';
9
+ import PageContents from './PageContents';
10
+ import Search from './Search';
13
11
 
14
- interface SearchArguments {
15
- query: string;
16
- searchEngine?: string[];
17
- }
12
+ const Inspector = memo<BuiltinPortalProps>(({ arguments: args, messageId, state, apiName }) => {
13
+ switch (apiName) {
14
+ case WebBrowsingApiName.searchWithSearXNG: {
15
+ return <Search messageId={messageId} query={args as SearchQuery} response={state} />;
16
+ }
18
17
 
19
- interface InspectorUIProps<T = Record<string, any>, S = any> {
20
- arguments: T;
21
- identifier: string;
22
- messageId: string;
23
- state: S;
24
- }
18
+ case WebBrowsingApiName.crawlSinglePage: {
19
+ const url = args.url;
20
+ const result = (state as CrawlPluginState).results.find(
21
+ (result) => result.originalUrl === url,
22
+ );
25
23
 
26
- const Inspector = memo<InspectorUIProps<SearchArguments, SearchResponse>>(
27
- ({ arguments: args, messageId, state }) => {
28
- const engines = uniq((state.results || []).map((result) => result.engine));
29
- const defaultEngines = engines.length > 0 ? engines : args.searchEngine || [];
30
- const loading = useChatStore(chatToolSelectors.isSearXNGSearching(messageId));
24
+ return <PageContent messageId={messageId} result={result} />;
25
+ }
31
26
 
32
- if (loading) {
27
+ case WebBrowsingApiName.crawlMultiPages: {
33
28
  return (
34
- <Flexbox gap={12} height={'100%'}>
35
- <SearchBar
36
- aiSummary={false}
37
- defaultEngines={defaultEngines}
38
- defaultQuery={args.query}
39
- messageId={messageId}
40
- tooltip={false}
41
- />
42
-
43
- <Flexbox gap={16} paddingBlock={16} paddingInline={12}>
44
- {[1, 2, 3, 4, 6].map((id) => (
45
- <Skeleton
46
- active
47
- key={id}
48
- paragraph={{ rows: 3, width: `${(id % 4) + 5}0%` }}
49
- title={false}
50
- />
51
- ))}
52
- </Flexbox>
53
- </Flexbox>
29
+ <PageContents
30
+ messageId={messageId}
31
+ results={(state as CrawlPluginState).results}
32
+ urls={args.urls}
33
+ />
54
34
  );
55
35
  }
36
+ }
56
37
 
57
- return (
58
- <Flexbox gap={0} height={'100%'}>
59
- <Flexbox gap={12} height={'100%'}>
60
- <SearchBar
61
- aiSummary={false}
62
- defaultEngines={defaultEngines}
63
- defaultQuery={args.query}
64
- messageId={messageId}
65
- tooltip={false}
66
- />
67
- <Flexbox height={'100%'} width={'100%'}>
68
- <ResultList dataSources={state.results} />
69
- </Flexbox>
70
- </Flexbox>
71
- <Footer />
72
- </Flexbox>
73
- );
74
- },
75
- );
38
+ return null;
39
+ });
76
40
 
77
41
  export default Inspector;
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+
3
+ import { CopyButton } from '@lobehub/ui';
4
+ import { createStyles } from 'antd-style';
5
+ import Link from 'next/link';
6
+ import { memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import { shinyTextStylish } from '@/styles/loading';
11
+
12
+ const useStyles = createStyles(({ token, css }) => {
13
+ return {
14
+ cardBody: css`
15
+ padding-block: 12px 8px;
16
+ padding-inline: 12px;
17
+ `,
18
+ container: css`
19
+ overflow: hidden;
20
+ justify-content: space-between;
21
+
22
+ max-width: 360px;
23
+ border: 1px solid ${token.colorBorderSecondary};
24
+ border-radius: 12px;
25
+ `,
26
+
27
+ footer: css`
28
+ padding-block: 8px;
29
+ padding-inline: 12px;
30
+
31
+ font-size: ${token.fontSizeSM}px;
32
+ color: ${token.colorTextTertiary};
33
+
34
+ background-color: ${token.colorFillQuaternary};
35
+ `,
36
+ shining: shinyTextStylish(token),
37
+ };
38
+ });
39
+
40
+ const LoadingCard = memo<{ url: string }>(({ url }) => {
41
+ const { t } = useTranslation('plugin');
42
+ const { styles } = useStyles();
43
+
44
+ return (
45
+ <Flexbox className={styles.container}>
46
+ <Flexbox className={styles.cardBody} horizontal>
47
+ <Link href={url} rel={'nofollow'} target={'_blank'}>
48
+ <div className={styles.shining}>{url}</div>
49
+ </Link>
50
+ <CopyButton content={url} size={'small'} />
51
+ </Flexbox>
52
+ <div className={styles.footer}>{t('search.crawPages.crawling')}</div>
53
+ </Flexbox>
54
+ );
55
+ });
56
+
57
+ export default LoadingCard;
@@ -0,0 +1,142 @@
1
+ 'use client';
2
+
3
+ import { CrawlSuccessResult } from '@lobechat/web-crawler';
4
+ import { Icon } from '@lobehub/ui';
5
+ import { Descriptions, Typography } from 'antd';
6
+ import { createStyles } from 'antd-style';
7
+ import { ExternalLink } from 'lucide-react';
8
+ import Link from 'next/link';
9
+ import { memo } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+ import { Center, Flexbox } from 'react-layout-kit';
12
+
13
+ import { useChatStore } from '@/store/chat';
14
+ import { WebBrowsingManifest } from '@/tools/web-browsing';
15
+
16
+ const { Paragraph } = Typography;
17
+
18
+ const useStyles = createStyles(({ token, css }) => {
19
+ return {
20
+ cardBody: css`
21
+ padding-block: 12px 8px;
22
+ padding-inline: 16px;
23
+ `,
24
+ container: css`
25
+ cursor: pointer;
26
+
27
+ overflow: hidden;
28
+
29
+ max-width: 360px;
30
+ border: 1px solid ${token.colorBorderSecondary};
31
+ border-radius: 12px;
32
+
33
+ transition: border-color 0.2s;
34
+
35
+ :hover {
36
+ border-color: ${token.colorPrimary};
37
+ }
38
+ `,
39
+ description: css`
40
+ margin-block: 0 4px !important;
41
+ color: ${token.colorTextTertiary};
42
+ `,
43
+ detailsSection: css`
44
+ padding-block: ${token.paddingSM}px;
45
+ `,
46
+ externalLink: css`
47
+ color: ${token.colorTextQuaternary};
48
+
49
+ :hover {
50
+ color: ${token.colorText};
51
+ }
52
+ `,
53
+ footer: css`
54
+ padding: ${token.paddingXS}px;
55
+ text-align: center;
56
+ background-color: ${token.colorFillQuaternary};
57
+ `,
58
+ footerText: css`
59
+ font-size: ${token.fontSizeSM}px;
60
+ color: ${token.colorTextTertiary} !important;
61
+ `,
62
+ metaInfo: css`
63
+ display: flex;
64
+ align-items: center;
65
+ color: ${token.colorTextSecondary};
66
+ `,
67
+ title: css`
68
+ overflow: hidden;
69
+ display: -webkit-box;
70
+ -webkit-box-orient: vertical;
71
+ -webkit-line-clamp: 1;
72
+
73
+ margin-block-end: 0;
74
+ `,
75
+ titleRow: css`
76
+ color: ${token.colorText};
77
+ `,
78
+ };
79
+ });
80
+
81
+ interface CrawlerData {
82
+ crawler: string;
83
+ messageId: string;
84
+ originalUrl: string;
85
+ result: CrawlSuccessResult;
86
+ }
87
+
88
+ const CrawlerResultCard = memo<CrawlerData>(({ result, messageId, crawler, originalUrl }) => {
89
+ const { t } = useTranslation('plugin');
90
+ const { styles } = useStyles();
91
+ const [openToolUI, togglePageContent] = useChatStore((s) => [s.openToolUI, s.togglePageContent]);
92
+
93
+ const { url, title, description } = result;
94
+
95
+ return (
96
+ <Flexbox
97
+ className={styles.container}
98
+ justify={'space-between'}
99
+ onClick={() => {
100
+ openToolUI(messageId, WebBrowsingManifest.identifier);
101
+ togglePageContent(originalUrl);
102
+ }}
103
+ >
104
+ <Flexbox className={styles.cardBody} gap={8}>
105
+ <Flexbox align={'center'} className={styles.titleRow} horizontal justify={'space-between'}>
106
+ <Flexbox>
107
+ <div className={styles.title}>{title || originalUrl}</div>
108
+ </Flexbox>
109
+ <Link href={url} onClick={(e) => e.stopPropagation()} target={'_blank'}>
110
+ <Center className={styles.externalLink}>
111
+ <Icon icon={ExternalLink} />
112
+ </Center>
113
+ </Link>
114
+ </Flexbox>
115
+ <Paragraph className={styles.description} ellipsis={{ expandable: false, rows: 2 }}>
116
+ {description || result.content?.slice(0, 40)}
117
+ </Paragraph>
118
+ </Flexbox>
119
+ <div className={styles.footer}>
120
+ <Descriptions
121
+ classNames={{
122
+ content: styles.footerText,
123
+ }}
124
+ column={2}
125
+ items={[
126
+ {
127
+ children: result.content?.length,
128
+ label: t('search.crawPages.meta.words'),
129
+ },
130
+ {
131
+ children: crawler,
132
+ label: t('search.crawPages.meta.crawler'),
133
+ },
134
+ ]}
135
+ size="small"
136
+ />
137
+ </div>
138
+ </Flexbox>
139
+ );
140
+ });
141
+
142
+ export default CrawlerResultCard;
@@ -0,0 +1,41 @@
1
+ import { memo } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
3
+
4
+ import { CrawlPluginState } from '@/types/tool/crawler';
5
+
6
+ import Loading from './Loading';
7
+ import Result from './Result';
8
+
9
+ interface PagesContentProps {
10
+ messageId: string;
11
+ results?: CrawlPluginState['results'];
12
+ urls: string[];
13
+ }
14
+
15
+ const PagesContent = memo<PagesContentProps>(({ results, messageId, urls }) => {
16
+ if (!results || results.length === 0) {
17
+ return (
18
+ <Flexbox gap={12} horizontal>
19
+ {urls.map((url) => (
20
+ <Loading key={url} url={url} />
21
+ ))}
22
+ </Flexbox>
23
+ );
24
+ }
25
+
26
+ return (
27
+ <Flexbox gap={12} horizontal>
28
+ {results.map((result) => (
29
+ <Result
30
+ crawler={result.crawler}
31
+ key={result.originalUrl}
32
+ messageId={messageId}
33
+ originalUrl={result.originalUrl}
34
+ result={result.data}
35
+ />
36
+ ))}
37
+ </Flexbox>
38
+ );
39
+ });
40
+
41
+ export default PagesContent;
@@ -8,7 +8,7 @@ import { Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { useIsMobile } from '@/hooks/useIsMobile';
10
10
 
11
- import { EngineAvatarGroup } from '../../components/EngineAvatar';
11
+ import { EngineAvatarGroup } from '../../../components/EngineAvatar';
12
12
 
13
13
  const useStyles = createStyles(({ css, token }) => ({
14
14
  font: css`
@@ -10,7 +10,7 @@ import { useChatStore } from '@/store/chat';
10
10
  import { chatToolSelectors } from '@/store/chat/selectors';
11
11
  import { SearchQuery, SearchResponse } from '@/types/tool/search';
12
12
 
13
- import SearchBar from '../../components/SearchBar';
13
+ import SearchBar from '../../../components/SearchBar';
14
14
  import SearchView from './SearchView';
15
15
 
16
16
  interface SearchQueryViewProps {
@@ -6,7 +6,7 @@ import { Flexbox } from 'react-layout-kit';
6
6
  import { useChatStore } from '@/store/chat';
7
7
  import { WebBrowsingManifest } from '@/tools/web-browsing';
8
8
 
9
- import { EngineAvatarGroup } from '../../components/EngineAvatar';
9
+ import { EngineAvatarGroup } from '../../../components/EngineAvatar';
10
10
 
11
11
  const useStyles = createStyles(({ css, token }) => ({
12
12
  container: css`
@@ -0,0 +1,62 @@
1
+ import { Alert, Highlighter } from '@lobehub/ui';
2
+ import { memo, useState } from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import { ChatMessagePluginError } from '@/types/message';
6
+ import { SearchQuery, SearchResponse } from '@/types/tool/search';
7
+
8
+ import ConfigForm from './ConfigForm';
9
+ import SearchQueryView from './SearchQuery';
10
+ import SearchResult from './SearchResult';
11
+
12
+ interface SearchProps {
13
+ messageId: string;
14
+ pluginError: ChatMessagePluginError;
15
+ searchQuery: SearchQuery;
16
+ searchResponse?: SearchResponse;
17
+ }
18
+
19
+ const Search = memo<SearchProps>(({ messageId, searchQuery, searchResponse, pluginError }) => {
20
+ const [editing, setEditing] = useState(false);
21
+
22
+ if (pluginError) {
23
+ if (pluginError?.type === 'PluginSettingsInvalid') {
24
+ return <ConfigForm id={messageId} provider={pluginError.body?.provider} />;
25
+ }
26
+
27
+ return (
28
+ <Alert
29
+ extra={
30
+ <Flexbox>
31
+ <Highlighter copyButtonSize={'small'} language={'json'} type={'pure'}>
32
+ {JSON.stringify(pluginError.body?.data || pluginError.body, null, 2)}
33
+ </Highlighter>
34
+ </Flexbox>
35
+ }
36
+ message={pluginError?.message}
37
+ type={'error'}
38
+ />
39
+ );
40
+ }
41
+
42
+ return (
43
+ <Flexbox gap={8}>
44
+ <SearchQueryView
45
+ args={searchQuery}
46
+ editing={editing}
47
+ messageId={messageId}
48
+ pluginState={searchResponse}
49
+ setEditing={setEditing}
50
+ />
51
+ <SearchResult
52
+ args={searchQuery}
53
+ editing={editing}
54
+ messageId={messageId}
55
+ pluginState={searchResponse}
56
+ setEditing={setEditing}
57
+ />
58
+ </Flexbox>
59
+ );
60
+ });
61
+
62
+ export default Search;
@@ -1,56 +1,47 @@
1
- import { Alert, Highlighter } from '@lobehub/ui';
2
- import { memo, useState } from 'react';
3
- import { Flexbox } from 'react-layout-kit';
1
+ import { memo } from 'react';
4
2
 
3
+ import { WebBrowsingApiName } from '@/tools/web-browsing';
4
+ import PageContent from '@/tools/web-browsing/Render/PageContent';
5
5
  import { BuiltinRenderProps } from '@/types/tool';
6
+ import { CrawlMultiPagesQuery, CrawlPluginState, CrawlSinglePageQuery } from '@/types/tool/crawler';
6
7
  import { SearchContent, SearchQuery, SearchResponse } from '@/types/tool/search';
7
8
 
8
- import ConfigForm from './ConfigForm';
9
- import SearchQueryView from './SearchQuery';
10
- import SearchResult from './SearchResult';
9
+ import Search from './Search';
11
10
 
12
- const WebBrowsing = memo<BuiltinRenderProps<SearchContent[], SearchQuery, SearchResponse>>(
13
- ({ messageId, args, pluginState, pluginError }) => {
14
- const [editing, setEditing] = useState(false);
11
+ const WebBrowsing = memo<BuiltinRenderProps<SearchContent[]>>(
12
+ ({ messageId, args, pluginState, pluginError, apiName }) => {
13
+ switch (apiName) {
14
+ case WebBrowsingApiName.searchWithSearXNG: {
15
+ return (
16
+ <Search
17
+ messageId={messageId}
18
+ pluginError={pluginError}
19
+ searchQuery={args as SearchQuery}
20
+ searchResponse={pluginState as SearchResponse}
21
+ />
22
+ );
23
+ }
15
24
 
16
- if (pluginError) {
17
- if (pluginError?.type === 'PluginSettingsInvalid') {
18
- return <ConfigForm id={messageId} provider={pluginError.body?.provider} />;
25
+ case WebBrowsingApiName.crawlSinglePage: {
26
+ return (
27
+ <PageContent
28
+ messageId={messageId}
29
+ results={(pluginState as CrawlPluginState)?.results}
30
+ urls={[(args as CrawlSinglePageQuery).url]}
31
+ />
32
+ );
19
33
  }
20
34
 
21
- return (
22
- <Alert
23
- extra={
24
- <Flexbox>
25
- <Highlighter copyButtonSize={'small'} language={'json'} type={'pure'}>
26
- {JSON.stringify(pluginError.body?.data || pluginError.body, null, 2)}
27
- </Highlighter>
28
- </Flexbox>
29
- }
30
- message={pluginError?.message}
31
- type={'error'}
32
- />
33
- );
35
+ case WebBrowsingApiName.crawlMultiPages: {
36
+ return (
37
+ <PageContent
38
+ messageId={messageId}
39
+ results={(pluginState as CrawlPluginState)?.results}
40
+ urls={(args as CrawlMultiPagesQuery).urls}
41
+ />
42
+ );
43
+ }
34
44
  }
35
-
36
- return (
37
- <Flexbox gap={8}>
38
- <SearchQueryView
39
- args={args}
40
- editing={editing}
41
- messageId={messageId}
42
- pluginState={pluginState}
43
- setEditing={setEditing}
44
- />
45
- <SearchResult
46
- args={args}
47
- editing={editing}
48
- messageId={messageId}
49
- pluginState={pluginState}
50
- setEditing={setEditing}
51
- />
52
- </Flexbox>
53
- );
54
45
  },
55
46
  );
56
47
 
@@ -2,12 +2,20 @@ import dayjs from 'dayjs';
2
2
 
3
3
  import { BuiltinToolManifest } from '@/types/tool';
4
4
 
5
+ import { systemPrompt } from './systemRole';
6
+
7
+ export const WebBrowsingApiName = {
8
+ crawlMultiPages: 'crawlMultiPages',
9
+ crawlSinglePage: 'crawlSinglePage',
10
+ searchWithSearXNG: 'searchWithSearXNG',
11
+ };
12
+
5
13
  export const WebBrowsingManifest: BuiltinToolManifest = {
6
14
  api: [
7
15
  {
8
16
  description:
9
17
  'A meta search engine. Useful for when you need to answer questions about current events. Input should be a search query. Output is a JSON array of the query results',
10
- name: 'searchWithSearXNG',
18
+ name: WebBrowsingApiName.searchWithSearXNG,
11
19
  parameters: {
12
20
  properties: {
13
21
  query: {
@@ -46,57 +54,45 @@ export const WebBrowsingManifest: BuiltinToolManifest = {
46
54
  type: 'object',
47
55
  },
48
56
  },
57
+ {
58
+ description:
59
+ 'A crawler can visit page content. Output is a JSON object of title, content, url and website',
60
+ name: WebBrowsingApiName.crawlSinglePage,
61
+ parameters: {
62
+ properties: {
63
+ url: {
64
+ description: 'The url need to be crawled',
65
+ type: 'string',
66
+ },
67
+ },
68
+ required: ['url'],
69
+ type: 'object',
70
+ },
71
+ },
72
+ {
73
+ description:
74
+ 'A crawler can visit multi pages. If need to visit multi website, use this one. Output is an array of JSON object of title, content, url and website',
75
+ name: WebBrowsingApiName.crawlMultiPages,
76
+ parameters: {
77
+ properties: {
78
+ urls: {
79
+ items: {
80
+ description: 'The url need to be crawled',
81
+ type: 'string',
82
+ },
83
+ type: 'array',
84
+ },
85
+ },
86
+ required: ['urls'],
87
+ type: 'object',
88
+ },
89
+ },
49
90
  ],
50
91
  identifier: 'lobe-web-browsing',
51
92
  meta: {
52
93
  avatar: '🌐',
53
94
  title: 'Web Browsing',
54
95
  },
55
- systemRole: `You are a search tool that uses SearxNG, a meta search engine. When given a search query, you will return relevant results from various search engines. Your results will be in JSON format, containing titles, links, and snippets of relevant web pages. If no good results are found, you will say so. You can also provide answers, infoboxes, or suggestions if available.
56
-
57
- SearXNG combine with these engines:
58
- - Google: The world's most popular search engine, offering a wide range of general web results.,
59
- - Bilibili: A Chinese video sharing website themed around animation, comic, and games (ACG) ,(又名 B 站),
60
- - Bing: Microsoft's search engine, providing web results with a focus on visual search.,
61
- - DuckDuckGo: A privacy-focused search engine that doesn't track users.,
62
- - npm: The package manager for JavaScript, used to find Node.js packages.,
63
- - PyPI: The Python Package Index, used to find Python packages.,
64
- - GitHub: A platform for version control and collaboration, search for code repositories.,
65
- - arXiv: A repository of electronic preprints for scientific papers.,
66
- - Google Scholar: A freely accessible web search engine for scholarly literature.,
67
- - Z-Library: A shadow library project for file-sharing access to scholarly journal articles and books.,
68
- - Reddit: A network of communities based on people's interests, search for discussions and content.,
69
- - IMDb: An online database of information related to films, TV programs, and video games.,
70
- - Brave: A privacy-focused browser with its own search engine.,
71
- - Wikipedia: A free online encyclopedia, search for articles on various topics.,
72
- - Pinterest: An image sharing and social media service, search for images and ideas.,
73
- - Unsplash: A website dedicated to sharing stock photography, search for high-quality images.,
74
- - Vimeo: A video hosting, sharing, and services platform.,
75
- - YouTube: A video sharing platform, search for a wide variety of video content.
76
-
77
- SearXNG comes with a search syntax by with you can modify the categories, engines, languages and more.
78
-
79
- ## \`!\` select engine and category
80
-
81
- To set category and/or engine names use a \`!\` prefix. To give a few examples:
82
-
83
- - search in category **map** for **paris**
84
- - \`!map paris\`
85
- - image search
86
- - \`!images Wau Holland\`
87
-
88
- Abbreviations of the engines and languages are also accepted. Engine/category modifiers are chain able and inclusive. E.g. with \`!map !ddg !wp paris\` search in map category and DuckDuckGo and Wikipedia for **paris**.
89
-
90
- ## \`:\` select language
91
-
92
- To select language filter use a \`:\` prefix. To give an example:
93
-
94
- - search Wikipedia by a custom language
95
- - \`:fr !wp Wau Holland\`
96
-
97
- You need to summarize in the language of the user's question. If you use search content in your reply, you must use Markdown footnote format to indicate the source, Such as [^1].
98
-
99
- current date: ${dayjs(new Date()).format('YYYY-MM-DD')}
100
- `,
96
+ systemRole: systemPrompt(dayjs(new Date()).format('YYYY-MM-DD')),
101
97
  type: 'builtin',
102
98
  };