diff --git a/web/src/app/home/knowledge/KBDetailDialog.tsx b/web/src/app/home/knowledge/KBDetailDialog.tsx
index 262d872f..7ad8d4a4 100644
--- a/web/src/app/home/knowledge/KBDetailDialog.tsx
+++ b/web/src/app/home/knowledge/KBDetailDialog.tsx
@@ -24,6 +24,7 @@ import { httpClient } from '@/app/infra/http/HttpClient';
// import { KnowledgeBase } from '@/app/infra/entities/api';
import KBForm from '@/app/home/knowledge/components/kb-form/KBForm';
import KBDoc from '@/app/home/knowledge/components/kb-docs/KBDoc';
+import KBRetrieve from '@/app/home/knowledge/components/kb-retrieve/KBRetrieve';
interface KBDetailDialogProps {
open: boolean;
@@ -81,6 +82,19 @@ export default function KBDetailDialog({
),
},
+ {
+ key: 'retrieve',
+ label: t('knowledge.retrieve'),
+ icon: (
+
+ ),
+ },
];
const confirmDelete = () => {
@@ -168,7 +182,9 @@ export default function KBDetailDialog({
{activeMenu === 'metadata'
? t('knowledge.editKnowledgeBase')
- : t('knowledge.editDocument')}
+ : activeMenu === 'documents'
+ ? t('knowledge.editDocument')
+ : t('knowledge.retrieveTest')}
@@ -180,6 +196,7 @@ export default function KBDetailDialog({
/>
)}
{activeMenu === 'documents' && }
+ {activeMenu === 'retrieve' && }
{activeMenu === 'metadata' && (
diff --git a/web/src/app/home/knowledge/components/kb-retrieve/KBRetrieve.tsx b/web/src/app/home/knowledge/components/kb-retrieve/KBRetrieve.tsx
new file mode 100644
index 00000000..46c38f5f
--- /dev/null
+++ b/web/src/app/home/knowledge/components/kb-retrieve/KBRetrieve.tsx
@@ -0,0 +1,98 @@
+'use client';
+
+import React, { useState, useEffect } from 'react';
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Input } from '@/components/ui/input';
+import { useTranslation } from 'react-i18next';
+import { httpClient } from '@/app/infra/http/HttpClient';
+import { RetrieveResult, KnowledgeBaseFile } from '@/app/infra/entities/api';
+import { toast } from 'sonner';
+
+interface KBRetrieveProps {
+ kbId: string;
+}
+
+export default function KBRetrieve({ kbId }: KBRetrieveProps) {
+ const { t } = useTranslation();
+ const [query, setQuery] = useState('');
+ const [results, setResults] = useState([]);
+ const [files, setFiles] = useState([]);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ const loadFiles = async () => {
+ try {
+ const response = await httpClient.getKnowledgeBaseFiles(kbId);
+ setFiles(response.files);
+ } catch (error) {
+ console.error('Failed to load files:', error);
+ }
+ };
+ loadFiles();
+ }, [kbId]);
+
+ const handleRetrieve = async () => {
+ if (!query.trim()) return;
+
+ setLoading(true);
+ try {
+ const response = await httpClient.retrieveKnowledgeBase(kbId, query);
+ setResults(response.results);
+ } catch (error) {
+ console.error('Retrieve failed:', error);
+ toast.error(t('knowledge.retrieveError'));
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const getFileName = (fileId: string) => {
+ const file = files.find((f) => f.uuid === fileId);
+ return file?.file_name || fileId;
+ };
+
+ return (
+
+
+ setQuery(e.target.value)}
+ placeholder={t('knowledge.queryPlaceholder')}
+ onKeyPress={(e) => e.key === 'Enter' && handleRetrieve()}
+ />
+
+
+
+
+
+ {t('knowledge.retrieveResults')} ({results.length})
+
+
+ {results.length === 0 && !loading && (
+
{t('knowledge.noResults')}
+ )}
+
+ {results.map((result) => (
+
+
+
+ {getFileName(result.metadata.file_id)}
+
+ {t('knowledge.distance')}: {result.distance.toFixed(4)}
+
+
+
+
+
+ {result.metadata.text}
+
+
+
+ ))}
+
+
+ );
+}
diff --git a/web/src/app/infra/entities/api/index.ts b/web/src/app/infra/entities/api/index.ts
index f16333b1..76d0a1f6 100644
--- a/web/src/app/infra/entities/api/index.ts
+++ b/web/src/app/infra/entities/api/index.ts
@@ -296,3 +296,18 @@ export interface ApiRespWebChatMessage {
export interface ApiRespWebChatMessages {
messages: Message[];
}
+
+export interface RetrieveResult {
+ id: string;
+ metadata: {
+ file_id: string;
+ text: string;
+ uuid: string;
+ [key: string]: unknown;
+ };
+ distance: number;
+}
+
+export interface ApiRespKnowledgeBaseRetrieve {
+ results: RetrieveResult[];
+}
diff --git a/web/src/app/infra/http/HttpClient.ts b/web/src/app/infra/http/HttpClient.ts
index 2e91683a..9a49c1e3 100644
--- a/web/src/app/infra/http/HttpClient.ts
+++ b/web/src/app/infra/http/HttpClient.ts
@@ -38,6 +38,7 @@ import {
ApiRespKnowledgeBase,
KnowledgeBase,
ApiRespKnowledgeBaseFiles,
+ ApiRespKnowledgeBaseRetrieve,
} from '@/app/infra/entities/api';
import { GetBotLogsRequest } from '@/app/infra/http/requestParam/bots/GetBotLogsRequest';
import { GetBotLogsResponse } from '@/app/infra/http/requestParam/bots/GetBotLogsResponse';
@@ -499,6 +500,13 @@ class HttpClient {
return this.delete(`/api/v1/knowledge/bases/${uuid}`);
}
+ public retrieveKnowledgeBase(
+ uuid: string,
+ query: string,
+ ): Promise {
+ return this.post(`/api/v1/knowledge/bases/${uuid}/retrieve`, { query });
+ }
+
// ============ Plugins API ============
public getPlugins(): Promise {
return this.get('/api/v1/plugins');
diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts
index 3e9089fc..6cd110f1 100644
--- a/web/src/i18n/locales/en-US.ts
+++ b/web/src/i18n/locales/en-US.ts
@@ -282,6 +282,16 @@ const enUS = {
},
deleteKnowledgeBaseConfirmation:
'Are you sure you want to delete this knowledge base? All documents in this knowledge base will be deleted.',
+ retrieve: 'Retrieve Test',
+ retrieveTest: 'Retrieve Test',
+ query: 'Query',
+ queryPlaceholder: 'Enter query text...',
+ retrieveResults: 'Retrieve Results',
+ distance: 'Distance',
+ content: 'Content',
+ fileName: 'File Name',
+ noResults: 'No results',
+ retrieveError: 'Retrieve failed',
},
register: {
title: 'Initialize LangBot 👋',
diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts
index 49c09015..e839e046 100644
--- a/web/src/i18n/locales/zh-Hans.ts
+++ b/web/src/i18n/locales/zh-Hans.ts
@@ -274,6 +274,16 @@ const zhHans = {
},
deleteKnowledgeBaseConfirmation:
'你确定要删除这个知识库吗?此知识库下的所有文档将被删除。',
+ retrieve: '检索测试',
+ retrieveTest: '检索测试',
+ query: '查询',
+ queryPlaceholder: '输入查询内容...',
+ retrieveResults: '检索结果',
+ distance: '距离',
+ content: '内容',
+ fileName: '文件名',
+ noResults: '暂无结果',
+ retrieveError: '检索失败',
},
register: {
title: '初始化 LangBot 👋',