mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-03 04:24:36 +00:00
feat: add knowledge base retrieve test tab with Card-based UI (#1583)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Junyan Qin <Chin>, u79E6u9A8Fu8A00 in Chinese, you can call me my english name Rock Chin. <rockchinq@gmail.com>
This commit is contained in:
committed by
GitHub
parent
ea51cec57e
commit
dd1ec15a39
@@ -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({
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'retrieve',
|
||||
label: t('knowledge.retrieve'),
|
||||
icon: (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15z"></path>
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const confirmDelete = () => {
|
||||
@@ -168,7 +182,9 @@ export default function KBDetailDialog({
|
||||
<DialogTitle>
|
||||
{activeMenu === 'metadata'
|
||||
? t('knowledge.editKnowledgeBase')
|
||||
: t('knowledge.editDocument')}
|
||||
: activeMenu === 'documents'
|
||||
? t('knowledge.editDocument')
|
||||
: t('knowledge.retrieveTest')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 overflow-y-auto px-6 pb-6">
|
||||
@@ -180,6 +196,7 @@ export default function KBDetailDialog({
|
||||
/>
|
||||
)}
|
||||
{activeMenu === 'documents' && <KBDoc kbId={kbId} />}
|
||||
{activeMenu === 'retrieve' && <KBRetrieve kbId={kbId} />}
|
||||
</div>
|
||||
{activeMenu === 'metadata' && (
|
||||
<DialogFooter className="px-6 py-4 border-t shrink-0">
|
||||
|
||||
@@ -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<RetrieveResult[]>([]);
|
||||
const [files, setFiles] = useState<KnowledgeBaseFile[]>([]);
|
||||
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 (
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t('knowledge.queryPlaceholder')}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleRetrieve()}
|
||||
/>
|
||||
<Button onClick={handleRetrieve} disabled={loading || !query.trim()}>
|
||||
{loading ? t('common.loading') : t('knowledge.query')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-lg font-semibold">
|
||||
{t('knowledge.retrieveResults')} ({results.length})
|
||||
</h3>
|
||||
|
||||
{results.length === 0 && !loading && (
|
||||
<p className="text-muted-foreground">{t('knowledge.noResults')}</p>
|
||||
)}
|
||||
|
||||
{results.map((result) => (
|
||||
<Card key={result.id} className="w-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-sm font-medium flex justify-between items-center">
|
||||
<span>{getFileName(result.metadata.file_id)}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{t('knowledge.distance')}: {result.distance.toFixed(4)}
|
||||
</span>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-sm whitespace-pre-wrap">
|
||||
{result.metadata.text}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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[];
|
||||
}
|
||||
|
||||
@@ -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<ApiRespKnowledgeBaseRetrieve> {
|
||||
return this.post(`/api/v1/knowledge/bases/${uuid}/retrieve`, { query });
|
||||
}
|
||||
|
||||
// ============ Plugins API ============
|
||||
public getPlugins(): Promise<ApiRespPlugins> {
|
||||
return this.get('/api/v1/plugins');
|
||||
|
||||
@@ -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 👋',
|
||||
|
||||
@@ -274,6 +274,16 @@ const zhHans = {
|
||||
},
|
||||
deleteKnowledgeBaseConfirmation:
|
||||
'你确定要删除这个知识库吗?此知识库下的所有文档将被删除。',
|
||||
retrieve: '检索测试',
|
||||
retrieveTest: '检索测试',
|
||||
query: '查询',
|
||||
queryPlaceholder: '输入查询内容...',
|
||||
retrieveResults: '检索结果',
|
||||
distance: '距离',
|
||||
content: '内容',
|
||||
fileName: '文件名',
|
||||
noResults: '暂无结果',
|
||||
retrieveError: '检索失败',
|
||||
},
|
||||
register: {
|
||||
title: '初始化 LangBot 👋',
|
||||
|
||||
Reference in New Issue
Block a user