@lobehub/lobehub 2.0.0-next.98 → 2.0.0-next.99
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/apps/desktop/src/main/services/__tests__/fileSrv.test.ts +603 -0
- package/changelog/v1.json +9 -0
- package/codecov.yml +1 -0
- package/locales/ar/plugin.json +34 -22
- package/locales/ar/tool.json +8 -0
- package/locales/bg-BG/plugin.json +34 -22
- package/locales/bg-BG/tool.json +8 -0
- package/locales/de-DE/plugin.json +34 -22
- package/locales/de-DE/tool.json +8 -0
- package/locales/en-US/plugin.json +34 -22
- package/locales/en-US/tool.json +8 -0
- package/locales/es-ES/plugin.json +34 -22
- package/locales/es-ES/tool.json +8 -0
- package/locales/fa-IR/plugin.json +34 -22
- package/locales/fa-IR/tool.json +8 -0
- package/locales/fr-FR/plugin.json +34 -22
- package/locales/fr-FR/tool.json +8 -0
- package/locales/it-IT/plugin.json +34 -22
- package/locales/it-IT/tool.json +8 -0
- package/locales/ja-JP/plugin.json +34 -22
- package/locales/ja-JP/tool.json +8 -0
- package/locales/ko-KR/plugin.json +34 -22
- package/locales/ko-KR/tool.json +8 -0
- package/locales/nl-NL/plugin.json +34 -22
- package/locales/nl-NL/tool.json +8 -0
- package/locales/pl-PL/plugin.json +34 -22
- package/locales/pl-PL/tool.json +8 -0
- package/locales/pt-BR/plugin.json +34 -22
- package/locales/pt-BR/tool.json +8 -0
- package/locales/ru-RU/plugin.json +34 -22
- package/locales/ru-RU/tool.json +8 -0
- package/locales/tr-TR/plugin.json +34 -22
- package/locales/tr-TR/tool.json +8 -0
- package/locales/vi-VN/plugin.json +34 -22
- package/locales/vi-VN/tool.json +8 -0
- package/locales/zh-CN/plugin.json +34 -22
- package/locales/zh-CN/tool.json +8 -0
- package/locales/zh-TW/plugin.json +34 -22
- package/locales/zh-TW/tool.json +8 -0
- package/package.json +1 -1
- package/packages/database/src/models/__tests__/document.test.ts +149 -0
- package/packages/database/src/models/chunk.ts +3 -1
- package/packages/database/src/models/document.ts +8 -2
- package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatFileContents.test.ts.snap +75 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatNoSearchResults.test.ts.snap +45 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/__snapshots__/formatSearchResults.test.ts.snap +82 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatFileContents.test.ts +118 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatFileContents.ts +31 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatNoSearchResults.test.ts +25 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatNoSearchResults.ts +13 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatSearchResults.test.ts +191 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/formatSearchResults.ts +50 -0
- package/packages/prompts/src/prompts/knowledgeBaseQA/index.ts +6 -0
- package/packages/types/src/rag.ts +13 -4
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Token/TokenTagForGroupChat.tsx +2 -2
- package/src/features/ChatList/Messages/Group/Tool/Inspector/ToolTitle.tsx +5 -23
- package/src/features/ChatList/Messages/Tool/Inspector/ToolTitle.tsx +5 -25
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -1
- package/src/helpers/toolEngineering/index.test.ts +3 -3
- package/src/helpers/toolEngineering/index.ts +17 -4
- package/src/libs/trpc/client/lambda.ts +0 -6
- package/src/locales/default/plugin.ts +34 -22
- package/src/locales/default/tool.ts +13 -5
- package/src/server/routers/lambda/chunk.ts +168 -41
- package/src/services/chat/chat.test.ts +3 -3
- package/src/services/chat/index.ts +2 -2
- package/src/services/rag.ts +6 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +11 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +0 -87
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +2 -69
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +0 -2
- package/src/store/chat/slices/aiChat/actions/rag.ts +0 -47
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +8 -69
- package/src/store/chat/slices/builtinTool/actions/index.ts +4 -1
- package/src/store/chat/slices/builtinTool/actions/knowledgeBase.ts +174 -0
- package/src/store/chat/slices/operation/types.ts +1 -0
- package/src/store/chat/slices/thread/action.test.ts +0 -1
- package/src/store/chat/slices/thread/action.ts +0 -1
- package/src/tools/executionRuntimes.ts +3 -0
- package/src/tools/identifiers.ts +13 -0
- package/src/tools/index.ts +7 -0
- package/src/tools/knowledge-base/ExecutionRuntime/index.ts +96 -0
- package/src/tools/knowledge-base/Render/ReadKnowledge/FileCard.tsx +135 -0
- package/src/tools/knowledge-base/Render/ReadKnowledge/index.tsx +27 -0
- package/src/tools/knowledge-base/Render/SearchKnowledgeBase/Item/index.tsx +54 -0
- package/src/tools/knowledge-base/Render/SearchKnowledgeBase/Item/style.ts +51 -0
- package/src/tools/knowledge-base/Render/SearchKnowledgeBase/index.tsx +23 -0
- package/src/tools/knowledge-base/Render/index.ts +7 -0
- package/src/tools/knowledge-base/index.ts +64 -0
- package/src/tools/knowledge-base/systemRole.ts +102 -0
- package/src/tools/knowledge-base/type.ts +25 -0
- package/src/tools/local-system/Intervention/WriteFile/index.tsx +1 -1
- package/src/tools/renders.ts +4 -0
- package/src/store/chat/agents/createToolEngine.ts +0 -22
package/src/tools/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { isDesktop } from '@/const/version';
|
|
|
4
4
|
|
|
5
5
|
import { ArtifactsManifest } from './artifacts';
|
|
6
6
|
import { CodeInterpreterManifest } from './code-interpreter';
|
|
7
|
+
import { KnowledgeBaseManifest } from './knowledge-base';
|
|
7
8
|
import { LocalSystemManifest } from './local-system';
|
|
8
9
|
import { WebBrowsingManifest } from './web-browsing';
|
|
9
10
|
|
|
@@ -30,4 +31,10 @@ export const builtinTools: LobeBuiltinTool[] = [
|
|
|
30
31
|
manifest: CodeInterpreterManifest,
|
|
31
32
|
type: 'builtin',
|
|
32
33
|
},
|
|
34
|
+
{
|
|
35
|
+
hidden: true,
|
|
36
|
+
identifier: KnowledgeBaseManifest.identifier,
|
|
37
|
+
manifest: KnowledgeBaseManifest,
|
|
38
|
+
type: 'builtin',
|
|
39
|
+
},
|
|
33
40
|
];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { formatSearchResults, promptFileContents, promptNoSearchResults } from '@lobechat/prompts';
|
|
2
|
+
import { BuiltinServerRuntimeOutput } from '@lobechat/types';
|
|
3
|
+
|
|
4
|
+
import { ragService } from '@/services/rag';
|
|
5
|
+
|
|
6
|
+
import { ReadKnowledgeState, SearchKnowledgeBaseArgs, SearchKnowledgeBaseState } from '../type';
|
|
7
|
+
|
|
8
|
+
export interface ReadKnowledgeArgs {
|
|
9
|
+
fileIds: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class KnowledgeBaseExecutionRuntime {
|
|
13
|
+
/**
|
|
14
|
+
* Search knowledge base and return file summaries with relevant chunks
|
|
15
|
+
*/
|
|
16
|
+
async searchKnowledgeBase(
|
|
17
|
+
args: SearchKnowledgeBaseArgs,
|
|
18
|
+
options?: {
|
|
19
|
+
fileIds?: string[];
|
|
20
|
+
knowledgeBaseIds?: string[];
|
|
21
|
+
messageId?: string;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
},
|
|
24
|
+
): Promise<BuiltinServerRuntimeOutput> {
|
|
25
|
+
try {
|
|
26
|
+
const { query, topK = 20 } = args;
|
|
27
|
+
|
|
28
|
+
// Call the existing RAG service
|
|
29
|
+
const { chunks, fileResults } = await ragService.semanticSearchForChat(
|
|
30
|
+
{ fileIds: options?.fileIds, knowledgeIds: options?.knowledgeBaseIds, query, topK },
|
|
31
|
+
options?.signal,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (chunks.length === 0) {
|
|
35
|
+
const state: SearchKnowledgeBaseState = { chunks: [], fileResults: [], totalResults: 0 };
|
|
36
|
+
|
|
37
|
+
return { content: promptNoSearchResults(query), state, success: true };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Format search results for AI
|
|
41
|
+
const formattedContent = formatSearchResults(fileResults, query);
|
|
42
|
+
|
|
43
|
+
const state: SearchKnowledgeBaseState = { chunks, fileResults, totalResults: chunks.length };
|
|
44
|
+
|
|
45
|
+
return { content: formattedContent, state, success: true };
|
|
46
|
+
} catch (e) {
|
|
47
|
+
return {
|
|
48
|
+
content: `Error searching knowledge base: ${(e as Error).message}`,
|
|
49
|
+
error: e,
|
|
50
|
+
success: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Read full content of specific files from knowledge base
|
|
57
|
+
*/
|
|
58
|
+
async readKnowledge(
|
|
59
|
+
args: ReadKnowledgeArgs,
|
|
60
|
+
options?: { signal?: AbortSignal },
|
|
61
|
+
): Promise<BuiltinServerRuntimeOutput> {
|
|
62
|
+
try {
|
|
63
|
+
const { fileIds } = args;
|
|
64
|
+
|
|
65
|
+
if (!fileIds || fileIds.length === 0) {
|
|
66
|
+
return {
|
|
67
|
+
content: 'Error: No file IDs provided',
|
|
68
|
+
success: false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const fileContents = await ragService.getFileContents(fileIds, options?.signal);
|
|
73
|
+
|
|
74
|
+
const formattedContent = promptFileContents(fileContents);
|
|
75
|
+
|
|
76
|
+
const state: ReadKnowledgeState = {
|
|
77
|
+
files: fileContents.map((file) => ({
|
|
78
|
+
error: file.error,
|
|
79
|
+
fileId: file.fileId,
|
|
80
|
+
filename: file.filename,
|
|
81
|
+
preview: file.preview,
|
|
82
|
+
totalCharCount: file.totalCharCount,
|
|
83
|
+
totalLineCount: file.totalLineCount,
|
|
84
|
+
})),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return { content: formattedContent, state, success: true };
|
|
88
|
+
} catch (e) {
|
|
89
|
+
return {
|
|
90
|
+
content: `Error reading knowledge: ${(e as Error).message}`,
|
|
91
|
+
error: e,
|
|
92
|
+
success: false,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Alert, Text } from '@lobehub/ui';
|
|
4
|
+
import { Descriptions } from 'antd';
|
|
5
|
+
import { createStyles } from 'antd-style';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
import FileIcon from '@/components/FileIcon';
|
|
11
|
+
|
|
12
|
+
import { FileContentDetail } from '../../type';
|
|
13
|
+
|
|
14
|
+
const useStyles = createStyles(({ token, css }) => {
|
|
15
|
+
return {
|
|
16
|
+
cardBody: css`
|
|
17
|
+
padding-block: 12px 8px;
|
|
18
|
+
padding-inline: 16px;
|
|
19
|
+
`,
|
|
20
|
+
container: css`
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
|
|
23
|
+
min-width: 360px;
|
|
24
|
+
max-width: 360px;
|
|
25
|
+
border: 1px solid ${token.colorBorderSecondary};
|
|
26
|
+
border-radius: 12px;
|
|
27
|
+
`,
|
|
28
|
+
description: css`
|
|
29
|
+
margin-block: 0 4px !important;
|
|
30
|
+
color: ${token.colorTextTertiary};
|
|
31
|
+
`,
|
|
32
|
+
footer: css`
|
|
33
|
+
padding-block: 8px;
|
|
34
|
+
padding-inline: 16px;
|
|
35
|
+
border-radius: 8px;
|
|
36
|
+
|
|
37
|
+
text-align: center;
|
|
38
|
+
|
|
39
|
+
background-color: ${token.colorFillQuaternary};
|
|
40
|
+
`,
|
|
41
|
+
footerText: css`
|
|
42
|
+
font-size: 12px !important;
|
|
43
|
+
color: ${token.colorTextTertiary} !important;
|
|
44
|
+
`,
|
|
45
|
+
icon: css`
|
|
46
|
+
color: ${token.colorTextSecondary};
|
|
47
|
+
`,
|
|
48
|
+
preview: css`
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
|
|
51
|
+
max-height: 80px;
|
|
52
|
+
padding: 8px;
|
|
53
|
+
border-radius: 6px;
|
|
54
|
+
|
|
55
|
+
font-family: ${token.fontFamilyCode};
|
|
56
|
+
font-size: 12px;
|
|
57
|
+
line-height: 1.5;
|
|
58
|
+
color: ${token.colorTextSecondary};
|
|
59
|
+
`,
|
|
60
|
+
title: css`
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
display: -webkit-box;
|
|
63
|
+
-webkit-box-orient: vertical;
|
|
64
|
+
-webkit-line-clamp: 1;
|
|
65
|
+
|
|
66
|
+
margin-block-end: 0;
|
|
67
|
+
`,
|
|
68
|
+
titleRow: css`
|
|
69
|
+
color: ${token.colorText};
|
|
70
|
+
`,
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
interface FileCardProps {
|
|
75
|
+
file: FileContentDetail;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const FileCard = memo<FileCardProps>(({ file }) => {
|
|
79
|
+
const { t } = useTranslation('tool');
|
|
80
|
+
const { styles } = useStyles();
|
|
81
|
+
|
|
82
|
+
if (file.error) {
|
|
83
|
+
return (
|
|
84
|
+
<Flexbox className={styles.container} gap={8}>
|
|
85
|
+
<Flexbox className={styles.cardBody} gap={8}>
|
|
86
|
+
<Flexbox align={'center'} className={styles.titleRow} gap={8} horizontal>
|
|
87
|
+
<FileIcon fileName={file.filename} size={16} />
|
|
88
|
+
<div className={styles.title}>{file.filename}</div>
|
|
89
|
+
</Flexbox>
|
|
90
|
+
</Flexbox>
|
|
91
|
+
<div className={styles.footer}>
|
|
92
|
+
<Alert message={file.error} type={'error'} variant={'borderless'} />
|
|
93
|
+
</div>
|
|
94
|
+
</Flexbox>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<Flexbox className={styles.container} justify={'space-between'}>
|
|
100
|
+
<Flexbox className={styles.cardBody} gap={8}>
|
|
101
|
+
<Flexbox align={'center'} className={styles.titleRow} gap={8} horizontal>
|
|
102
|
+
<FileIcon fileName={file.filename} size={16} />
|
|
103
|
+
<div className={styles.title}>{file.filename}</div>
|
|
104
|
+
</Flexbox>
|
|
105
|
+
{file.preview && (
|
|
106
|
+
<Text className={styles.preview} ellipsis={{ rows: 4 }}>
|
|
107
|
+
{file.preview}...
|
|
108
|
+
</Text>
|
|
109
|
+
)}
|
|
110
|
+
</Flexbox>
|
|
111
|
+
<div className={styles.footer}>
|
|
112
|
+
<Descriptions
|
|
113
|
+
classNames={{
|
|
114
|
+
content: styles.footerText,
|
|
115
|
+
label: styles.footerText,
|
|
116
|
+
}}
|
|
117
|
+
column={2}
|
|
118
|
+
items={[
|
|
119
|
+
{
|
|
120
|
+
children: file.totalCharCount?.toLocaleString(),
|
|
121
|
+
label: t('lobe-knowledge-base.readKnowledge.meta.chars'),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
children: file.totalLineCount?.toLocaleString(),
|
|
125
|
+
label: t('lobe-knowledge-base.readKnowledge.meta.lines'),
|
|
126
|
+
},
|
|
127
|
+
]}
|
|
128
|
+
size="small"
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</Flexbox>
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export default FileCard;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BuiltinRenderProps } from '@lobechat/types';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
|
|
5
|
+
import { ReadKnowledgeArgs } from '../../ExecutionRuntime';
|
|
6
|
+
import { ReadKnowledgeState } from '../../type';
|
|
7
|
+
import FileCard from './FileCard';
|
|
8
|
+
|
|
9
|
+
const ReadKnowledge = memo<BuiltinRenderProps<ReadKnowledgeArgs, ReadKnowledgeState>>(
|
|
10
|
+
({ pluginState }) => {
|
|
11
|
+
const { files } = pluginState || {};
|
|
12
|
+
|
|
13
|
+
if (!files || files.length === 0) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Flexbox gap={12} horizontal style={{ flexWrap: 'wrap' }}>
|
|
19
|
+
{files.map((file) => (
|
|
20
|
+
<FileCard file={file} key={file.fileId} />
|
|
21
|
+
))}
|
|
22
|
+
</Flexbox>
|
|
23
|
+
);
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export default ReadKnowledge;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { FileSearchResult } from '@lobechat/types';
|
|
2
|
+
import { Text, Tooltip } from '@lobehub/ui';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import FileIcon from '@/components/FileIcon';
|
|
7
|
+
import { useIsMobile } from '@/hooks/useIsMobile';
|
|
8
|
+
import { useChatStore } from '@/store/chat';
|
|
9
|
+
|
|
10
|
+
import { useStyles } from './style';
|
|
11
|
+
|
|
12
|
+
export interface FileItemProps extends FileSearchResult {
|
|
13
|
+
index: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const FileItem = memo<FileItemProps>(({ fileId, fileName, relevanceScore, topChunks }) => {
|
|
17
|
+
const { styles, cx } = useStyles();
|
|
18
|
+
const openFilePreview = useChatStore((s) => s.openFilePreview);
|
|
19
|
+
|
|
20
|
+
const isMobile = useIsMobile();
|
|
21
|
+
|
|
22
|
+
// Use the first chunk for preview
|
|
23
|
+
const firstChunk = topChunks[0];
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Flexbox
|
|
27
|
+
align={'center'}
|
|
28
|
+
className={cx(styles.container, isMobile && styles.mobile)}
|
|
29
|
+
gap={4}
|
|
30
|
+
horizontal
|
|
31
|
+
key={fileId}
|
|
32
|
+
onClick={(e) => {
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
if (firstChunk) {
|
|
35
|
+
openFilePreview({
|
|
36
|
+
chunkId: firstChunk.id,
|
|
37
|
+
chunkText: firstChunk.text,
|
|
38
|
+
fileId,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<FileIcon fileName={fileName} size={20} variant={'raw'} />
|
|
44
|
+
<Flexbox gap={12} horizontal justify={'space-between'} style={{ maxWidth: 200 }}>
|
|
45
|
+
<Text ellipsis>{fileName}</Text>
|
|
46
|
+
<Tooltip title={`Relevance: ${(relevanceScore * 100).toFixed(1)}%`}>
|
|
47
|
+
<Center className={styles.badge}>{relevanceScore.toFixed(2)}</Center>
|
|
48
|
+
</Tooltip>
|
|
49
|
+
</Flexbox>
|
|
50
|
+
</Flexbox>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export default FileItem;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createStyles } from 'antd-style';
|
|
2
|
+
import { lighten } from 'polished';
|
|
3
|
+
|
|
4
|
+
export const useStyles = createStyles(({ css, token, isDarkMode }) => ({
|
|
5
|
+
badge: css`
|
|
6
|
+
padding-block: 4px;
|
|
7
|
+
padding-inline: 6px;
|
|
8
|
+
border-radius: 2222px;
|
|
9
|
+
|
|
10
|
+
font-size: 12px;
|
|
11
|
+
line-height: 12px;
|
|
12
|
+
color: ${token.colorTextSecondary};
|
|
13
|
+
|
|
14
|
+
background: ${token.colorFillSecondary};
|
|
15
|
+
`,
|
|
16
|
+
|
|
17
|
+
container: css`
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
|
|
20
|
+
width: fit-content;
|
|
21
|
+
padding-block: 6px;
|
|
22
|
+
padding-inline: 8px;
|
|
23
|
+
padding-inline-end: 12px;
|
|
24
|
+
border-radius: 8px;
|
|
25
|
+
|
|
26
|
+
color: ${token.colorText};
|
|
27
|
+
|
|
28
|
+
background: ${lighten(0.1, token.colorBgElevated)};
|
|
29
|
+
box-shadow: ${token.boxShadowTertiary};
|
|
30
|
+
|
|
31
|
+
transition: all 0.2s;
|
|
32
|
+
|
|
33
|
+
&:hover {
|
|
34
|
+
background: ${isDarkMode ? lighten(0.15, token.colorBgElevated) : ''};
|
|
35
|
+
box-shadow: ${token.boxShadowSecondary};
|
|
36
|
+
}
|
|
37
|
+
`,
|
|
38
|
+
filename: css`
|
|
39
|
+
overflow: hidden;
|
|
40
|
+
display: -webkit-box;
|
|
41
|
+
-webkit-box-orient: vertical;
|
|
42
|
+
-webkit-line-clamp: 1;
|
|
43
|
+
|
|
44
|
+
font-size: 12px;
|
|
45
|
+
text-overflow: ellipsis;
|
|
46
|
+
`,
|
|
47
|
+
|
|
48
|
+
mobile: css`
|
|
49
|
+
width: 100%;
|
|
50
|
+
`,
|
|
51
|
+
}));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BuiltinRenderProps } from '@lobechat/types';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
|
|
5
|
+
import { SearchKnowledgeBaseArgs, SearchKnowledgeBaseState } from '@/tools/knowledge-base/type';
|
|
6
|
+
|
|
7
|
+
import FileItem from './Item';
|
|
8
|
+
|
|
9
|
+
const SearchKnowledgeBase = memo<
|
|
10
|
+
BuiltinRenderProps<SearchKnowledgeBaseArgs, SearchKnowledgeBaseState>
|
|
11
|
+
>(({ pluginState }) => {
|
|
12
|
+
const { fileResults } = pluginState || {};
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Flexbox gap={8} horizontal wrap={'wrap'}>
|
|
16
|
+
{fileResults?.map((file, index) => {
|
|
17
|
+
return <FileItem index={index} key={file.fileId} {...file} />;
|
|
18
|
+
})}
|
|
19
|
+
</Flexbox>
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export default SearchKnowledgeBase;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { BuiltinToolManifest } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
import { systemPrompt } from './systemRole';
|
|
4
|
+
|
|
5
|
+
export const KnowledgeBaseApiName = {
|
|
6
|
+
readKnowledge: 'readKnowledge',
|
|
7
|
+
searchKnowledgeBase: 'searchKnowledgeBase',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const KnowledgeBaseManifest: BuiltinToolManifest = {
|
|
11
|
+
api: [
|
|
12
|
+
{
|
|
13
|
+
description:
|
|
14
|
+
'Search through knowledge base using semantic vector search to find relevant files and chunks. Returns a summary of matching files with their relevance scores and brief excerpts. Use this first to discover which files contain relevant information. IMPORTANT: Since this uses vector-based search, always resolve pronouns and references to concrete entities (e.g., use "authentication system" instead of "it").',
|
|
15
|
+
name: KnowledgeBaseApiName.searchKnowledgeBase,
|
|
16
|
+
parameters: {
|
|
17
|
+
properties: {
|
|
18
|
+
query: {
|
|
19
|
+
description:
|
|
20
|
+
'The search query to find relevant information. Be specific and use concrete entities. IMPORTANT: Resolve all pronouns and references (like "it", "that", "this") to actual entity names before searching, as this uses semantic vector search which works best with concrete terms.',
|
|
21
|
+
type: 'string',
|
|
22
|
+
},
|
|
23
|
+
topK: {
|
|
24
|
+
default: 15,
|
|
25
|
+
description:
|
|
26
|
+
'Number of top relevant chunks to return (default: 15). Each file will include the most relevant chunks.',
|
|
27
|
+
maximum: 100,
|
|
28
|
+
minimum: 5,
|
|
29
|
+
type: 'number',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ['query'],
|
|
33
|
+
type: 'object',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
description:
|
|
38
|
+
'Read the full content of specific files from the knowledge base. Use this after searchKnowledgeBase to get complete information from relevant files. You can read multiple files at once.',
|
|
39
|
+
name: KnowledgeBaseApiName.readKnowledge,
|
|
40
|
+
parameters: {
|
|
41
|
+
properties: {
|
|
42
|
+
fileIds: {
|
|
43
|
+
description:
|
|
44
|
+
'Array of file IDs to read. Get these IDs from searchKnowledgeBase results.',
|
|
45
|
+
items: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
},
|
|
48
|
+
type: 'array',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ['fileIds'],
|
|
52
|
+
type: 'object',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
identifier: 'lobe-knowledge-base',
|
|
57
|
+
meta: {
|
|
58
|
+
avatar: '📚',
|
|
59
|
+
description: 'Search and retrieve information from knowledge bases',
|
|
60
|
+
title: 'Knowledge Base',
|
|
61
|
+
},
|
|
62
|
+
systemRole: systemPrompt,
|
|
63
|
+
type: 'builtin',
|
|
64
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export const systemPrompt = `You have access to a Knowledge Base tool with powerful semantic search and document retrieval capabilities. You can search through knowledge bases to find relevant information and read the full content of files.
|
|
2
|
+
|
|
3
|
+
<core_capabilities>
|
|
4
|
+
1. Search through knowledge base using semantic search (searchKnowledgeBase)
|
|
5
|
+
2. Read the full content of specific files from the knowledge base (readKnowledge)
|
|
6
|
+
</core_capabilities>
|
|
7
|
+
|
|
8
|
+
<workflow>
|
|
9
|
+
1. Understand the user's information need or question
|
|
10
|
+
2. Formulate a clear, specific search query
|
|
11
|
+
3. Use searchKnowledgeBase to discover relevant files and chunks
|
|
12
|
+
4. Review search results to identify the most relevant files
|
|
13
|
+
5. Use readKnowledge to retrieve full content from selected files
|
|
14
|
+
6. Synthesize information and answer the user's question
|
|
15
|
+
7. Always cite source files when providing information
|
|
16
|
+
</workflow>
|
|
17
|
+
|
|
18
|
+
<tool_selection_guidelines>
|
|
19
|
+
- **searchKnowledgeBase**: Use this first to discover which files contain relevant information
|
|
20
|
+
- Uses semantic vector search to find relevant content
|
|
21
|
+
- Returns file names, relevance scores, and brief excerpts
|
|
22
|
+
- Helps you decide which files to read in full
|
|
23
|
+
- You can adjust topK parameter (5-100, default: 15) based on how many results you need
|
|
24
|
+
- **IMPORTANT**: Since this uses vector-based semantic search, always resolve references and use concrete entities in your query
|
|
25
|
+
- BAD: "What does it do?" or "Tell me about that feature"
|
|
26
|
+
- GOOD: "What does the authentication system do?" or "Tell me about the JWT authentication feature"
|
|
27
|
+
|
|
28
|
+
- **readKnowledge**: Use this after searching to get complete file content
|
|
29
|
+
- Can read multiple files at once by providing their file IDs
|
|
30
|
+
- Get file IDs from searchKnowledgeBase results
|
|
31
|
+
- Provides complete context from the selected files
|
|
32
|
+
</tool_selection_guidelines>
|
|
33
|
+
|
|
34
|
+
<search_strategy_guidelines>
|
|
35
|
+
- **Coreference Resolution**: Always resolve pronouns and references to concrete entities before searching
|
|
36
|
+
- Replace "it", "that", "this", "them" with the actual entity names
|
|
37
|
+
- Use full names instead of abbreviations when first searching
|
|
38
|
+
- Include relevant context in the query itself
|
|
39
|
+
- Examples:
|
|
40
|
+
- User asks: "What are its features?" (after discussing "authentication system")
|
|
41
|
+
- Search query should be: "authentication system features" (NOT "its features")
|
|
42
|
+
- User asks: "How does that work?"
|
|
43
|
+
- Search query should include the specific topic being discussed
|
|
44
|
+
- Formulate clear and specific search queries that capture the core information need
|
|
45
|
+
- For broad topics, start with a general query then refine if needed
|
|
46
|
+
- For specific questions, use precise terminology
|
|
47
|
+
- You can perform multiple searches with different queries or perspectives if needed
|
|
48
|
+
- Adjust topK based on result quality - increase if you need more context, decrease for focused searches
|
|
49
|
+
- Review the relevance scores and excerpts to select the most pertinent files
|
|
50
|
+
</search_strategy_guidelines>
|
|
51
|
+
|
|
52
|
+
<reading_strategy_guidelines>
|
|
53
|
+
- Read only the files that are most relevant to avoid information overload
|
|
54
|
+
- You can read multiple files at once if they all contain relevant information
|
|
55
|
+
- Prioritize files with higher relevance scores from search results
|
|
56
|
+
- If search results show many relevant files, consider reading them in batches
|
|
57
|
+
</reading_strategy_guidelines>
|
|
58
|
+
|
|
59
|
+
<citation_requirements>
|
|
60
|
+
- Always cite the source files when providing information
|
|
61
|
+
- Reference file names clearly in your response
|
|
62
|
+
- If specific information comes from a particular file, mention it explicitly
|
|
63
|
+
- Help users understand which knowledge base files support your answers
|
|
64
|
+
</citation_requirements>
|
|
65
|
+
|
|
66
|
+
<response_format>
|
|
67
|
+
When providing information from the knowledge base:
|
|
68
|
+
1. Start with a direct answer to the user's question when possible
|
|
69
|
+
2. Provide relevant details and context from the knowledge base files
|
|
70
|
+
3. Clearly cite which files the information comes from
|
|
71
|
+
4. If information is insufficient or not found, inform the user clearly
|
|
72
|
+
5. Suggest related searches if the initial search doesn't yield good results
|
|
73
|
+
</response_format>
|
|
74
|
+
|
|
75
|
+
<best_practices>
|
|
76
|
+
- Always start with searchKnowledgeBase before reading files
|
|
77
|
+
- Don't read files blindly - review search results first
|
|
78
|
+
- Be selective about which files to read based on relevance
|
|
79
|
+
- If no relevant information is found, clearly inform the user
|
|
80
|
+
- Suggest alternative search queries if initial results are poor
|
|
81
|
+
- Respect the knowledge base's scope - it may not contain all information
|
|
82
|
+
- Combine information from multiple files when appropriate
|
|
83
|
+
- Maintain accuracy - only cite information actually present in the files
|
|
84
|
+
</best_practices>
|
|
85
|
+
|
|
86
|
+
<error_handling>
|
|
87
|
+
- If search returns no results:
|
|
88
|
+
1. Try reformulating the query with different keywords or broader terms
|
|
89
|
+
2. Suggest alternative search approaches to the user
|
|
90
|
+
3. Inform the user if the topic appears to be outside the knowledge base's scope
|
|
91
|
+
|
|
92
|
+
- If file reading fails:
|
|
93
|
+
1. Inform the user which files couldn't be accessed
|
|
94
|
+
2. Work with successfully retrieved files if any
|
|
95
|
+
3. Suggest searching again if necessary
|
|
96
|
+
|
|
97
|
+
- If search results are ambiguous:
|
|
98
|
+
1. Ask for clarification from the user
|
|
99
|
+
2. Provide a summary of what types of information were found
|
|
100
|
+
3. Let the user guide which direction to explore further
|
|
101
|
+
</error_handling>
|
|
102
|
+
`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ChatSemanticSearchChunk, FileSearchResult } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export interface SearchKnowledgeBaseArgs {
|
|
5
|
+
query: string;
|
|
6
|
+
topK?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SearchKnowledgeBaseState {
|
|
9
|
+
chunks: ChatSemanticSearchChunk[];
|
|
10
|
+
fileResults: FileSearchResult[];
|
|
11
|
+
totalResults: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface FileContentDetail {
|
|
15
|
+
error?: string;
|
|
16
|
+
fileId: string;
|
|
17
|
+
filename: string;
|
|
18
|
+
preview?: string;
|
|
19
|
+
totalCharCount?: number;
|
|
20
|
+
totalLineCount?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ReadKnowledgeState {
|
|
24
|
+
files: FileContentDetail[];
|
|
25
|
+
}
|
|
@@ -11,7 +11,7 @@ import { LocalFile, LocalFolder } from '@/features/LocalFile';
|
|
|
11
11
|
|
|
12
12
|
const WriteFile = memo<BuiltinInterventionProps<WriteLocalFileParams>>(({ args }) => {
|
|
13
13
|
const { t } = useTranslation('tool');
|
|
14
|
-
const { base, dir, ext } = path.parse(args.path);
|
|
14
|
+
const { base, dir, ext } = path.parse(args.path || '');
|
|
15
15
|
|
|
16
16
|
// Detect language from file extension
|
|
17
17
|
const language = useMemo(() => {
|
package/src/tools/renders.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { BuiltinRender } from '@lobechat/types';
|
|
|
2
2
|
|
|
3
3
|
import { CodeInterpreterManifest } from './code-interpreter';
|
|
4
4
|
import CodeInterpreterRender from './code-interpreter/Render';
|
|
5
|
+
// knowledge-base
|
|
6
|
+
import { KnowledgeBaseManifest } from './knowledge-base';
|
|
7
|
+
import { KnowledgeBaseRenders } from './knowledge-base/Render';
|
|
5
8
|
// local-system
|
|
6
9
|
import { LocalSystemManifest } from './local-system';
|
|
7
10
|
import { LocalSystemRenders } from './local-system/Render';
|
|
@@ -16,6 +19,7 @@ import { WebBrowsingRenders } from './web-browsing/Render';
|
|
|
16
19
|
const BuiltinToolsRenders: Record<string, Record<string, BuiltinRender>> = {
|
|
17
20
|
[LocalSystemManifest.identifier]: LocalSystemRenders as Record<string, BuiltinRender>,
|
|
18
21
|
[WebBrowsingManifest.identifier]: WebBrowsingRenders as Record<string, BuiltinRender>,
|
|
22
|
+
[KnowledgeBaseManifest.identifier]: KnowledgeBaseRenders as Record<string, BuiltinRender>,
|
|
19
23
|
[CodeInterpreterManifest.identifier]: {
|
|
20
24
|
python: CodeInterpreterRender as BuiltinRender,
|
|
21
25
|
},
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { WorkingModel } from '@lobechat/types';
|
|
2
|
-
|
|
3
|
-
import { getSearchConfig } from '@/helpers/getSearchConfig';
|
|
4
|
-
import { createToolsEngine } from '@/helpers/toolEngineering';
|
|
5
|
-
import { WebBrowsingManifest } from '@/tools/web-browsing';
|
|
6
|
-
|
|
7
|
-
export const createAgentToolsEngine = (workingModel: WorkingModel) =>
|
|
8
|
-
createToolsEngine({
|
|
9
|
-
// Add WebBrowsingManifest as default tool
|
|
10
|
-
defaultToolIds: [WebBrowsingManifest.identifier],
|
|
11
|
-
// Create search-aware enableChecker for this request
|
|
12
|
-
enableChecker: ({ pluginId }) => {
|
|
13
|
-
// For WebBrowsingManifest, apply search logic
|
|
14
|
-
if (pluginId === WebBrowsingManifest.identifier) {
|
|
15
|
-
const searchConfig = getSearchConfig(workingModel.model, workingModel.provider);
|
|
16
|
-
return searchConfig.useApplicationBuiltinSearchTool;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// For all other plugins, enable by default
|
|
20
|
-
return true;
|
|
21
|
-
},
|
|
22
|
-
});
|