@lobehub/chat 1.66.5 → 1.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/changelog/v1.json +18 -0
- package/locales/ar/models.json +9 -3
- package/locales/ar/plugin.json +12 -0
- package/locales/bg-BG/models.json +9 -3
- package/locales/bg-BG/plugin.json +12 -0
- package/locales/de-DE/models.json +9 -3
- package/locales/de-DE/plugin.json +12 -0
- package/locales/en-US/models.json +9 -3
- package/locales/en-US/plugin.json +12 -0
- package/locales/es-ES/models.json +9 -3
- package/locales/es-ES/plugin.json +12 -0
- package/locales/fa-IR/models.json +9 -3
- package/locales/fa-IR/plugin.json +12 -0
- package/locales/fr-FR/models.json +9 -3
- package/locales/fr-FR/plugin.json +12 -0
- package/locales/it-IT/models.json +9 -3
- package/locales/it-IT/plugin.json +12 -0
- package/locales/ja-JP/models.json +9 -3
- package/locales/ja-JP/plugin.json +12 -0
- package/locales/ko-KR/models.json +9 -3
- package/locales/ko-KR/plugin.json +12 -0
- package/locales/nl-NL/models.json +9 -3
- package/locales/nl-NL/plugin.json +12 -0
- package/locales/pl-PL/models.json +9 -3
- package/locales/pl-PL/plugin.json +12 -0
- package/locales/pt-BR/models.json +9 -3
- package/locales/pt-BR/plugin.json +12 -0
- package/locales/ru-RU/models.json +9 -3
- package/locales/ru-RU/plugin.json +12 -0
- package/locales/tr-TR/models.json +9 -3
- package/locales/tr-TR/plugin.json +12 -0
- package/locales/vi-VN/models.json +9 -3
- package/locales/vi-VN/plugin.json +12 -0
- package/locales/zh-CN/models.json +9 -3
- package/locales/zh-CN/plugin.json +12 -0
- package/locales/zh-TW/models.json +9 -3
- package/locales/zh-TW/plugin.json +12 -0
- package/package.json +10 -6
- package/packages/web-crawler/README.md +34 -0
- package/packages/web-crawler/package.json +13 -0
- package/packages/web-crawler/src/crawImpl/browserless.ts +62 -0
- package/packages/web-crawler/src/crawImpl/index.ts +11 -0
- package/packages/web-crawler/src/crawImpl/jina.ts +37 -0
- package/packages/web-crawler/src/crawImpl/naive.ts +84 -0
- package/packages/web-crawler/src/crawler.ts +66 -0
- package/packages/web-crawler/src/index.ts +2 -0
- package/packages/web-crawler/src/type.ts +42 -0
- package/packages/web-crawler/src/urlRules.ts +34 -0
- package/packages/web-crawler/src/utils/__snapshots__/htmlToMarkdown.test.ts.snap +638 -0
- package/packages/web-crawler/src/utils/appUrlRules.test.ts +26 -0
- package/packages/web-crawler/src/utils/appUrlRules.ts +40 -0
- package/packages/web-crawler/src/utils/errorType.ts +12 -0
- package/packages/web-crawler/src/utils/html/terms.html +1222 -0
- package/packages/web-crawler/src/utils/html/yingchao.html +1001 -0
- package/packages/web-crawler/src/utils/htmlToMarkdown.test.ts +35 -0
- package/packages/web-crawler/src/utils/htmlToMarkdown.ts +45 -0
- package/packages/web-crawler/tsconfig.json +20 -0
- package/pnpm-workspace.yaml +3 -0
- package/src/config/aiModels/openai.ts +29 -5
- package/src/database/server/models/__tests__/message.test.ts +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +4 -35
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +1 -1
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +3 -0
- package/src/features/PluginsUI/Render/index.tsx +1 -0
- package/src/features/Portal/Plugins/Body/ToolRender.tsx +1 -0
- package/src/locales/default/plugin.ts +12 -0
- package/src/server/routers/tools/search.ts +23 -0
- package/src/services/search.ts +8 -0
- package/src/store/chat/slices/builtinTool/actions/searXNG.ts +50 -0
- package/src/store/chat/slices/builtinTool/initialState.ts +1 -0
- package/src/tools/web-browsing/Portal/PageContent/index.tsx +190 -0
- package/src/tools/web-browsing/Portal/PageContents/index.tsx +23 -0
- package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/Video.tsx +1 -1
- package/src/tools/web-browsing/Portal/Search/index.tsx +69 -0
- package/src/tools/web-browsing/Portal/index.tsx +28 -64
- package/src/tools/web-browsing/Render/PageContent/Loading.tsx +57 -0
- package/src/tools/web-browsing/Render/PageContent/Result.tsx +142 -0
- package/src/tools/web-browsing/Render/PageContent/index.tsx +41 -0
- package/src/tools/web-browsing/Render/{SearchQuery → Search/SearchQuery}/SearchView.tsx +1 -1
- package/src/tools/web-browsing/Render/{SearchQuery → Search/SearchQuery}/index.tsx +1 -1
- package/src/tools/web-browsing/Render/{SearchResult → Search/SearchResult}/ShowMore.tsx +1 -1
- package/src/tools/web-browsing/Render/Search/index.tsx +62 -0
- package/src/tools/web-browsing/Render/index.tsx +35 -44
- package/src/tools/web-browsing/index.ts +43 -47
- package/src/tools/web-browsing/systemRole.ts +109 -0
- package/src/types/tool/builtin.ts +2 -0
- package/src/types/tool/crawler.ts +19 -0
- package/src/types/tool/search.ts +1 -0
- /package/src/tools/web-browsing/Portal/{Footer.tsx → Search/Footer.tsx} +0 -0
- /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/CategoryAvatar.tsx +0 -0
- /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/TitleExtra.tsx +0 -0
- /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/SearchItem/index.tsx +0 -0
- /package/src/tools/web-browsing/Portal/{ResultList → Search/ResultList}/index.tsx +0 -0
- /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/Form.tsx +0 -0
- /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/SearchXNGIcon.tsx +0 -0
- /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/index.tsx +0 -0
- /package/src/tools/web-browsing/Render/{ConfigForm → Search/ConfigForm}/style.tsx +0 -0
- /package/src/tools/web-browsing/Render/{SearchResult → Search/SearchResult}/SearchResultItem.tsx +0 -0
- /package/src/tools/web-browsing/Render/{SearchResult → Search/SearchResult}/index.tsx +0 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
2
|
+
import * as path from 'node:path';
|
3
|
+
import { expect } from 'vitest';
|
4
|
+
|
5
|
+
import { FilterOptions } from '../type';
|
6
|
+
import { htmlToMarkdown } from './htmlToMarkdown';
|
7
|
+
|
8
|
+
interface TestItem {
|
9
|
+
file: string;
|
10
|
+
url: string;
|
11
|
+
filterOptions?: FilterOptions;
|
12
|
+
}
|
13
|
+
const list: TestItem[] = [
|
14
|
+
{
|
15
|
+
file: 'terms.html',
|
16
|
+
url: 'https://lobehub.com/terms',
|
17
|
+
},
|
18
|
+
{
|
19
|
+
file: 'yingchao.html',
|
20
|
+
url: 'https://www.qiumiwu.com/standings/yingchao',
|
21
|
+
filterOptions: { pureText: true, enableReadability: false },
|
22
|
+
},
|
23
|
+
];
|
24
|
+
|
25
|
+
describe('htmlToMarkdown', () => {
|
26
|
+
list.forEach((item) => {
|
27
|
+
it(`should transform ${item.file} to markdown`, () => {
|
28
|
+
const html = readFileSync(path.join(__dirname, `./html/${item.file}`), { encoding: 'utf8' });
|
29
|
+
|
30
|
+
const data = htmlToMarkdown(html, { url: item.url, filterOptions: item.filterOptions || {} });
|
31
|
+
|
32
|
+
expect(data).toMatchSnapshot();
|
33
|
+
});
|
34
|
+
});
|
35
|
+
});
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { Readability } from '@mozilla/readability';
|
2
|
+
import { Window } from 'happy-dom';
|
3
|
+
import { NodeHtmlMarkdown, type TranslatorConfigObject } from 'node-html-markdown';
|
4
|
+
|
5
|
+
import { FilterOptions } from '../type';
|
6
|
+
|
7
|
+
export const htmlToMarkdown = (
|
8
|
+
html: string,
|
9
|
+
{ url, filterOptions }: { filterOptions: FilterOptions; url: string },
|
10
|
+
) => {
|
11
|
+
const window = new Window({ url });
|
12
|
+
|
13
|
+
const document = window.document;
|
14
|
+
document.body.innerHTML = html;
|
15
|
+
|
16
|
+
// @ts-expect-error reason: Readability expects a Document type
|
17
|
+
const parsedContent = new Readability(document).parse();
|
18
|
+
|
19
|
+
const useReadability = filterOptions.enableReadability ?? true;
|
20
|
+
|
21
|
+
let htmlNode = html;
|
22
|
+
|
23
|
+
if (useReadability && parsedContent?.content) {
|
24
|
+
htmlNode = parsedContent?.content;
|
25
|
+
}
|
26
|
+
|
27
|
+
const customTranslators = (
|
28
|
+
filterOptions.pureText
|
29
|
+
? {
|
30
|
+
a: {
|
31
|
+
postprocess: (_: string, content: string) => content,
|
32
|
+
},
|
33
|
+
img: {
|
34
|
+
ignore: true,
|
35
|
+
},
|
36
|
+
}
|
37
|
+
: {}
|
38
|
+
) as TranslatorConfigObject;
|
39
|
+
|
40
|
+
const nodeHtmlMarkdown = new NodeHtmlMarkdown({}, customTranslators);
|
41
|
+
|
42
|
+
const content = nodeHtmlMarkdown.translate(htmlNode);
|
43
|
+
|
44
|
+
return { ...parsedContent, content };
|
45
|
+
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"compilerOptions": {
|
3
|
+
"module": "CommonJS",
|
4
|
+
"target": "ESNext",
|
5
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
6
|
+
"sourceMap": true,
|
7
|
+
"skipDefaultLibCheck": true,
|
8
|
+
"jsx": "react-jsx",
|
9
|
+
"baseUrl": ".",
|
10
|
+
"allowSyntheticDefaultImports": true,
|
11
|
+
"moduleResolution": "node",
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
13
|
+
"noImplicitReturns": true,
|
14
|
+
"noUnusedLocals": true,
|
15
|
+
"resolveJsonModule": true,
|
16
|
+
"skipLibCheck": true,
|
17
|
+
"strict": true,
|
18
|
+
"types": ["vitest/globals"]
|
19
|
+
}
|
20
|
+
}
|
@@ -21,6 +21,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
21
21
|
id: 'o3-mini',
|
22
22
|
maxOutput: 100_000,
|
23
23
|
pricing: {
|
24
|
+
cachedInput: 0.55,
|
24
25
|
input: 1.1,
|
25
26
|
output: 4.4,
|
26
27
|
},
|
@@ -39,6 +40,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
39
40
|
id: 'o1-mini',
|
40
41
|
maxOutput: 65_536,
|
41
42
|
pricing: {
|
43
|
+
cachedInput: 0.55,
|
42
44
|
input: 1.1,
|
43
45
|
output: 4.4,
|
44
46
|
},
|
@@ -58,6 +60,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
58
60
|
id: 'o1',
|
59
61
|
maxOutput: 100_000,
|
60
62
|
pricing: {
|
63
|
+
cachedInput: 7.5,
|
61
64
|
input: 15,
|
62
65
|
output: 60,
|
63
66
|
},
|
@@ -82,6 +85,26 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
82
85
|
releasedAt: '2024-09-12',
|
83
86
|
type: 'chat',
|
84
87
|
},
|
88
|
+
{
|
89
|
+
abilities: {
|
90
|
+
functionCall: true,
|
91
|
+
vision: true,
|
92
|
+
},
|
93
|
+
contextWindowTokens: 128_000,
|
94
|
+
description:
|
95
|
+
'GPT-4.5 的研究预览版,它是我们迄今为止最大、最强大的 GPT 模型。它拥有广泛的世界知识,并能更好地理解用户意图,使其在创造性任务和自主规划方面表现出色。GPT-4.5 可接受文本和图像输入,并生成文本输出(包括结构化输出)。支持关键的开发者功能,如函数调用、批量 API 和流式输出。在需要创造性、开放式思考和对话的任务(如写作、学习或探索新想法)中,GPT-4.5 表现尤为出色。知识截止日期为 2023 年 10 月。',
|
96
|
+
displayName: 'GPT-4.5 Preview',
|
97
|
+
enabled: true,
|
98
|
+
id: 'gpt-4.5-preview',
|
99
|
+
maxOutput: 16_384,
|
100
|
+
pricing: {
|
101
|
+
cachedInput: 37.5,
|
102
|
+
input: 75,
|
103
|
+
output: 150,
|
104
|
+
},
|
105
|
+
releasedAt: '2025-02-27',
|
106
|
+
type: 'chat',
|
107
|
+
},
|
85
108
|
{
|
86
109
|
abilities: {
|
87
110
|
functionCall: true,
|
@@ -93,8 +116,9 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
93
116
|
displayName: 'GPT-4o mini',
|
94
117
|
enabled: true,
|
95
118
|
id: 'gpt-4o-mini',
|
96
|
-
maxOutput:
|
119
|
+
maxOutput: 16_384,
|
97
120
|
pricing: {
|
121
|
+
cachedInput: 0.075,
|
98
122
|
input: 0.15,
|
99
123
|
output: 0.6,
|
100
124
|
},
|
@@ -110,7 +134,6 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
110
134
|
description:
|
111
135
|
'ChatGPT-4o 是一款动态模型,实时更新以保持当前最新版本。它结合了强大的语言理解与生成能力,适合于大规模应用场景,包括客户服务、教育和技术支持。',
|
112
136
|
displayName: 'GPT-4o 1120',
|
113
|
-
enabled: true,
|
114
137
|
id: 'gpt-4o-2024-11-20',
|
115
138
|
pricing: {
|
116
139
|
input: 2.5,
|
@@ -131,6 +154,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
131
154
|
enabled: true,
|
132
155
|
id: 'gpt-4o',
|
133
156
|
pricing: {
|
157
|
+
cachedInput: 1.25,
|
134
158
|
input: 2.5,
|
135
159
|
output: 10,
|
136
160
|
},
|
@@ -350,7 +374,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
350
374
|
abilities: {
|
351
375
|
functionCall: true,
|
352
376
|
},
|
353
|
-
contextWindowTokens:
|
377
|
+
contextWindowTokens: 16_384,
|
354
378
|
description:
|
355
379
|
'GPT 3.5 Turbo,适用于各种文本生成和理解任务,Currently points to gpt-3.5-turbo-0125',
|
356
380
|
displayName: 'GPT-3.5 Turbo',
|
@@ -365,7 +389,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
365
389
|
abilities: {
|
366
390
|
functionCall: true,
|
367
391
|
},
|
368
|
-
contextWindowTokens:
|
392
|
+
contextWindowTokens: 16_384,
|
369
393
|
description:
|
370
394
|
'GPT 3.5 Turbo,适用于各种文本生成和理解任务,Currently points to gpt-3.5-turbo-0125',
|
371
395
|
displayName: 'GPT-3.5 Turbo 0125',
|
@@ -381,7 +405,7 @@ export const openaiChatModels: AIChatModelCard[] = [
|
|
381
405
|
abilities: {
|
382
406
|
functionCall: true,
|
383
407
|
},
|
384
|
-
contextWindowTokens:
|
408
|
+
contextWindowTokens: 16_384,
|
385
409
|
description:
|
386
410
|
'GPT 3.5 Turbo,适用于各种文本生成和理解任务,Currently points to gpt-3.5-turbo-0125',
|
387
411
|
displayName: 'GPT-3.5 Turbo 1106',
|
@@ -1271,7 +1271,7 @@ describe('MessageModel', () => {
|
|
1271
1271
|
const result = await messageModel.getHeatmaps();
|
1272
1272
|
|
1273
1273
|
// 断言结果
|
1274
|
-
expect(result.length).
|
1274
|
+
expect(result.length).toBeGreaterThanOrEqual(366);
|
1275
1275
|
expect(result.length).toBeLessThan(368);
|
1276
1276
|
|
1277
1277
|
// 检查两天前的数据
|
@@ -1389,7 +1389,7 @@ describe('MessageModel', () => {
|
|
1389
1389
|
const result = await messageModel.getHeatmaps();
|
1390
1390
|
|
1391
1391
|
// 断言结果
|
1392
|
-
expect(result.length).
|
1392
|
+
expect(result.length).toBeGreaterThanOrEqual(366);
|
1393
1393
|
expect(result.length).toBeLessThan(368);
|
1394
1394
|
|
1395
1395
|
// 检查所有数据的 count 和 level 是否为 0
|
@@ -1,14 +1,9 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import { useTheme } from 'antd-style';
|
4
|
-
import { LucideSquareArrowLeft, LucideSquareArrowRight } from 'lucide-react';
|
5
|
-
import { memo, useContext, useEffect } from 'react';
|
6
|
-
import { useTranslation } from 'react-i18next';
|
7
|
-
import { Center, Flexbox } from 'react-layout-kit';
|
1
|
+
import { memo, useEffect } from 'react';
|
2
|
+
import { Flexbox } from 'react-layout-kit';
|
8
3
|
|
9
4
|
import PluginRender from '@/features/PluginsUI/Render';
|
10
5
|
import { useChatStore } from '@/store/chat';
|
11
|
-
import {
|
6
|
+
import { chatSelectors } from '@/store/chat/selectors';
|
12
7
|
import { ChatMessage } from '@/types/message';
|
13
8
|
|
14
9
|
import Arguments from './Arguments';
|
@@ -30,14 +25,7 @@ const CustomRender = memo<CustomRenderProps>(
|
|
30
25
|
setShowPluginRender,
|
31
26
|
pluginError,
|
32
27
|
}) => {
|
33
|
-
const [loading
|
34
|
-
chatSelectors.isPluginApiInvoking(id)(s),
|
35
|
-
chatPortalSelectors.isPluginUIOpen(id)(s),
|
36
|
-
]);
|
37
|
-
const { direction } = useContext(ConfigProvider.ConfigContext);
|
38
|
-
const { t } = useTranslation('plugin');
|
39
|
-
|
40
|
-
const theme = useTheme();
|
28
|
+
const [loading] = useChatStore((s) => [chatSelectors.isPluginApiInvoking(id)(s)]);
|
41
29
|
|
42
30
|
useEffect(() => {
|
43
31
|
if (!plugin?.type || loading) return;
|
@@ -45,25 +33,6 @@ const CustomRender = memo<CustomRenderProps>(
|
|
45
33
|
setShowPluginRender(plugin?.type !== 'default');
|
46
34
|
}, [plugin?.type, loading]);
|
47
35
|
|
48
|
-
if (isMessageToolUIOpen)
|
49
|
-
return (
|
50
|
-
<Center paddingBlock={8} style={{ background: theme.colorFillQuaternary, borderRadius: 4 }}>
|
51
|
-
<Empty
|
52
|
-
description={t('showInPortal')}
|
53
|
-
image={
|
54
|
-
<Icon
|
55
|
-
color={theme.colorTextQuaternary}
|
56
|
-
icon={direction === 'rtl' ? LucideSquareArrowLeft : LucideSquareArrowRight}
|
57
|
-
size={'large'}
|
58
|
-
/>
|
59
|
-
}
|
60
|
-
styles={{
|
61
|
-
image: { height: 24 },
|
62
|
-
}}
|
63
|
-
/>
|
64
|
-
</Center>
|
65
|
-
);
|
66
|
-
|
67
36
|
if (loading) return <Arguments arguments={requestArgs} shine />;
|
68
37
|
|
69
38
|
return (
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import { Suspense, memo } from 'react';
|
2
2
|
|
3
3
|
import { LOADING_FLAT } from '@/const/message';
|
4
|
-
import ErrorResponse from '@/features/Conversation/Messages/Assistant/Tool/Render/ErrorResponse';
|
5
4
|
import { useChatStore } from '@/store/chat';
|
6
5
|
import { chatSelectors } from '@/store/chat/selectors';
|
7
6
|
|
8
7
|
import Arguments from './Arguments';
|
9
8
|
import CustomRender from './CustomRender';
|
9
|
+
import ErrorResponse from './ErrorResponse';
|
10
10
|
|
11
11
|
interface RenderProps {
|
12
12
|
messageId: string;
|
@@ -7,6 +7,7 @@ import Loading from '../Loading';
|
|
7
7
|
import { useParseContent } from '../useParseContent';
|
8
8
|
|
9
9
|
export interface BuiltinTypeProps {
|
10
|
+
apiName?: string;
|
10
11
|
arguments?: string;
|
11
12
|
content: string;
|
12
13
|
id: string;
|
@@ -25,6 +26,7 @@ const BuiltinType = memo<BuiltinTypeProps>(
|
|
25
26
|
identifier,
|
26
27
|
loading,
|
27
28
|
pluginError,
|
29
|
+
apiName,
|
28
30
|
}) => {
|
29
31
|
const { isJSON, data } = useParseContent(content);
|
30
32
|
|
@@ -40,6 +42,7 @@ const BuiltinType = memo<BuiltinTypeProps>(
|
|
40
42
|
|
41
43
|
return (
|
42
44
|
<Render
|
45
|
+
apiName={apiName}
|
43
46
|
args={args}
|
44
47
|
content={data}
|
45
48
|
identifier={identifier}
|
@@ -141,6 +141,18 @@ export default {
|
|
141
141
|
close: '删除',
|
142
142
|
confirm: '已完成配置并重试',
|
143
143
|
},
|
144
|
+
crawPages: {
|
145
|
+
crawling: '链接识别中',
|
146
|
+
detail: {
|
147
|
+
preview: '预览',
|
148
|
+
raw: '原始文本',
|
149
|
+
tooLong: '文本内容过长,对话上下文仅保留前 10000 字符,超过部分不计入会话上下文',
|
150
|
+
},
|
151
|
+
meta: {
|
152
|
+
crawler: '抓取模式',
|
153
|
+
words: '字符数',
|
154
|
+
},
|
155
|
+
},
|
144
156
|
searchxng: {
|
145
157
|
baseURL: '请输入',
|
146
158
|
description: '请输入 SearchXNG 的网址,即可开始联网搜索',
|
@@ -1,4 +1,6 @@
|
|
1
|
+
import { Crawler } from '@lobechat/web-crawler';
|
1
2
|
import { TRPCError } from '@trpc/server';
|
3
|
+
import pMap from 'p-map';
|
2
4
|
import { z } from 'zod';
|
3
5
|
|
4
6
|
import { toolsEnv } from '@/config/tools';
|
@@ -10,6 +12,27 @@ import { SEARCH_SEARXNG_NOT_CONFIG } from '@/types/tool/search';
|
|
10
12
|
const searchProcedure = isServerMode ? authedProcedure : passwordProcedure;
|
11
13
|
|
12
14
|
export const searchRouter = router({
|
15
|
+
crawlPages: searchProcedure
|
16
|
+
.input(
|
17
|
+
z.object({
|
18
|
+
impls: z.string().array().optional(),
|
19
|
+
urls: z.string().array(),
|
20
|
+
}),
|
21
|
+
)
|
22
|
+
.mutation(async ({ input }) => {
|
23
|
+
const crawler = new Crawler();
|
24
|
+
|
25
|
+
const results = await pMap(
|
26
|
+
input.urls,
|
27
|
+
async (url) => {
|
28
|
+
return await crawler.crawl({ impls: input.impls, url });
|
29
|
+
},
|
30
|
+
{ concurrency: 10 },
|
31
|
+
);
|
32
|
+
|
33
|
+
return { results };
|
34
|
+
}),
|
35
|
+
|
13
36
|
query: searchProcedure
|
14
37
|
.input(
|
15
38
|
z.object({
|
package/src/services/search.ts
CHANGED
@@ -4,6 +4,14 @@ class SearchService {
|
|
4
4
|
search(query: string, searchEngine?: string[]) {
|
5
5
|
return toolsClient.search.query.query({ query, searchEngine });
|
6
6
|
}
|
7
|
+
|
8
|
+
crawlPage(url: string) {
|
9
|
+
return toolsClient.search.crawlPages.mutate({ urls: [url] });
|
10
|
+
}
|
11
|
+
|
12
|
+
crawlPages(urls: string[]) {
|
13
|
+
return toolsClient.search.crawlPages.mutate({ urls });
|
14
|
+
}
|
7
15
|
}
|
8
16
|
|
9
17
|
export const searchService = new SearchService();
|
@@ -13,6 +13,16 @@ import {
|
|
13
13
|
import { nanoid } from '@/utils/uuid';
|
14
14
|
|
15
15
|
export interface SearchAction {
|
16
|
+
crawlMultiPages: (
|
17
|
+
id: string,
|
18
|
+
params: { urls: string[] },
|
19
|
+
aiSummary?: boolean,
|
20
|
+
) => Promise<boolean | undefined>;
|
21
|
+
crawlSinglePage: (
|
22
|
+
id: string,
|
23
|
+
params: { url: string },
|
24
|
+
aiSummary?: boolean,
|
25
|
+
) => Promise<boolean | undefined>;
|
16
26
|
/**
|
17
27
|
* 重新发起搜索
|
18
28
|
* @description 会更新插件的 arguments 参数,然后再次搜索
|
@@ -28,6 +38,7 @@ export interface SearchAction {
|
|
28
38
|
data: SearchQuery,
|
29
39
|
aiSummary?: boolean,
|
30
40
|
) => Promise<void | boolean>;
|
41
|
+
togglePageContent: (url: string) => void;
|
31
42
|
toggleSearchLoading: (id: string, loading: boolean) => void;
|
32
43
|
}
|
33
44
|
|
@@ -37,12 +48,47 @@ export const searchSlice: StateCreator<
|
|
37
48
|
[],
|
38
49
|
SearchAction
|
39
50
|
> = (set, get) => ({
|
51
|
+
crawlMultiPages: async (id, params, aiSummary = true) => {
|
52
|
+
const { internal_updateMessageContent } = get();
|
53
|
+
get().toggleSearchLoading(id, true);
|
54
|
+
const response = await searchService.crawlPages(params.urls);
|
55
|
+
|
56
|
+
await get().updatePluginState(id, response);
|
57
|
+
get().toggleSearchLoading(id, false);
|
58
|
+
const { results } = response;
|
59
|
+
|
60
|
+
if (!results) return;
|
61
|
+
|
62
|
+
const content = results.map((item) =>
|
63
|
+
'errorMessage' in item
|
64
|
+
? item
|
65
|
+
: {
|
66
|
+
...item.data,
|
67
|
+
// if crawl too many content
|
68
|
+
// slice the top 10000 char
|
69
|
+
content: item.data.content?.slice(0, 10_000),
|
70
|
+
},
|
71
|
+
);
|
72
|
+
|
73
|
+
await internal_updateMessageContent(id, JSON.stringify(content));
|
74
|
+
|
75
|
+
// if aiSummary is true, then trigger ai message
|
76
|
+
return aiSummary;
|
77
|
+
},
|
78
|
+
|
79
|
+
crawlSinglePage: async (id, params, aiSummary) => {
|
80
|
+
const { crawlMultiPages } = get();
|
81
|
+
|
82
|
+
return await crawlMultiPages(id, { urls: [params.url] }, aiSummary);
|
83
|
+
},
|
84
|
+
|
40
85
|
reSearchWithSearXNG: async (id, data, options) => {
|
41
86
|
get().toggleSearchLoading(id, true);
|
42
87
|
await get().updatePluginArguments(id, data);
|
43
88
|
|
44
89
|
await get().searchWithSearXNG(id, data, options?.aiSummary);
|
45
90
|
},
|
91
|
+
|
46
92
|
saveSearXNGSearchResult: async (id) => {
|
47
93
|
const message = chatSelectors.getMessageById(id)(get());
|
48
94
|
if (!message || !message.plugin) return;
|
@@ -133,6 +179,10 @@ export const searchSlice: StateCreator<
|
|
133
179
|
return aiSummary;
|
134
180
|
},
|
135
181
|
|
182
|
+
togglePageContent: (url) => {
|
183
|
+
set({ activePageContentUrl: url });
|
184
|
+
},
|
185
|
+
|
136
186
|
toggleSearchLoading: (id, loading) => {
|
137
187
|
set(
|
138
188
|
{ searchLoading: { ...get().searchLoading, [id]: loading } },
|