Update knowldge base layout

This commit is contained in:
Xin Wang
2026-02-09 08:01:15 +08:00
parent a12ba0c4a4
commit 0d13d6acdb

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState, useRef } from 'react'; import React, { useEffect, useState, useRef } from 'react';
import { Search, Plus, FileText, Upload, ArrowLeft, CloudUpload, File as FileIcon, X, Pencil, Trash2, Settings2, MoreVertical } from 'lucide-react'; import { Search, Plus, FileText, Upload, ArrowLeft, CloudUpload, File as FileIcon, X, Pencil, Trash2, Settings2, MoreHorizontal } from 'lucide-react';
import { Button, Input, TableHeader, TableRow, TableHead, TableCell, Card, Dialog, Badge } from '../components/UI'; import { Button, Input, TableHeader, TableRow, TableHead, TableCell, Card, Dialog, Badge } from '../components/UI';
import { KnowledgeBase } from '../types'; import { KnowledgeBase } from '../types';
import { createKnowledgeBase, deleteKnowledgeBase, deleteKnowledgeDocument, fetchKnowledgeBases, fetchLLMModels, updateKnowledgeBase, uploadKnowledgeDocument } from '../services/backendApi'; import { createKnowledgeBase, deleteKnowledgeBase, deleteKnowledgeDocument, fetchKnowledgeBases, fetchLLMModels, updateKnowledgeBase, uploadKnowledgeDocument } from '../services/backendApi';
@@ -20,6 +20,7 @@ export const KnowledgeBasePage: React.FC = () => {
const [editingKb, setEditingKb] = useState<KnowledgeBase | null>(null); const [editingKb, setEditingKb] = useState<KnowledgeBase | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [embeddingOptions, setEmbeddingOptions] = useState<string[]>(EMBEDDING_OPTIONS); const [embeddingOptions, setEmbeddingOptions] = useState<string[]>(EMBEDDING_OPTIONS);
const [hasDbEmbeddingModels, setHasDbEmbeddingModels] = useState(false);
const [openMenuKbId, setOpenMenuKbId] = useState<string | null>(null); const [openMenuKbId, setOpenMenuKbId] = useState<string | null>(null);
const [kbName, setKbName] = useState(''); const [kbName, setKbName] = useState('');
@@ -53,16 +54,20 @@ export const KnowledgeBasePage: React.FC = () => {
const loadEmbeddingModels = async () => { const loadEmbeddingModels = async () => {
try { try {
const models = await fetchLLMModels(); const models = await fetchLLMModels();
const fromDb = models const fromDb = Array.from(new Set(models
.filter((m) => m.type === 'embedding') .filter((m) => m.type === 'embedding')
.map((m) => (m.modelName || m.name || '').trim()) .map((m) => (m.modelName || m.name || '').trim())
.filter(Boolean); .filter(Boolean)));
const merged = Array.from(new Set([...fromDb, ...EMBEDDING_OPTIONS]));
if (merged.length > 0) { setHasDbEmbeddingModels(fromDb.length > 0);
setEmbeddingOptions(merged);
} // Prefer DB embedding models first; keep defaults as fallback at the end.
const defaultsTail = EMBEDDING_OPTIONS.filter((item) => !fromDb.includes(item));
const ordered = [...fromDb, ...defaultsTail];
setEmbeddingOptions(ordered.length > 0 ? ordered : EMBEDDING_OPTIONS);
} catch { } catch {
setEmbeddingOptions(EMBEDDING_OPTIONS); setEmbeddingOptions(EMBEDDING_OPTIONS);
setHasDbEmbeddingModels(false);
} }
}; };
loadEmbeddingModels(); loadEmbeddingModels();
@@ -231,7 +236,7 @@ export const KnowledgeBasePage: React.FC = () => {
<Card <Card
key={kb.id} key={kb.id}
className="p-6 hover:border-primary/50 transition-colors cursor-pointer group relative" className="p-6 hover:border-primary/50 transition-colors cursor-pointer group relative"
onClick={() => openEditKb(kb)} onClick={() => handleSelect(kb)}
> >
<div className="absolute top-3 right-3 z-20"> <div className="absolute top-3 right-3 z-20">
<Button <Button
@@ -243,22 +248,13 @@ export const KnowledgeBasePage: React.FC = () => {
setOpenMenuKbId((prev) => (prev === kb.id ? null : kb.id)); setOpenMenuKbId((prev) => (prev === kb.id ? null : kb.id));
}} }}
> >
<MoreVertical className="h-4 w-4" /> <MoreHorizontal className="h-4 w-4" />
</Button> </Button>
{openMenuKbId === kb.id && ( {openMenuKbId === kb.id && (
<div <div
className="absolute right-0 mt-1 w-36 rounded-md border border-white/10 bg-card/95 backdrop-blur-md shadow-lg overflow-hidden" className="absolute right-0 mt-1 w-36 rounded-md border border-white/10 bg-card/95 backdrop-blur-md shadow-lg overflow-hidden"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<button
className="w-full text-left px-3 py-2 text-sm hover:bg-white/10 text-foreground"
onClick={() => {
setOpenMenuKbId(null);
handleSelect(kb);
}}
>
</button>
<button <button
className="w-full text-left px-3 py-2 text-sm hover:bg-white/10 text-destructive" className="w-full text-left px-3 py-2 text-sm hover:bg-white/10 text-destructive"
onClick={() => { onClick={() => {
@@ -275,9 +271,11 @@ export const KnowledgeBasePage: React.FC = () => {
<div className="p-2 bg-primary/10 rounded-lg text-primary"> <div className="p-2 bg-primary/10 rounded-lg text-primary">
<FileText className="h-6 w-6" /> <FileText className="h-6 w-6" />
</div> </div>
<Badge variant="outline">{kb.embeddingModel || 'embedding'}</Badge>
</div> </div>
<h3 className="text-lg font-semibold group-hover:text-primary transition-colors text-white">{kb.name}</h3> <h3 className="text-lg font-semibold group-hover:text-primary transition-colors text-white">{kb.name}</h3>
<div className="mt-2">
<Badge variant="outline">{kb.embeddingModel || 'embedding'}</Badge>
</div>
<div className="mt-4 space-y-1 text-sm text-muted-foreground"> <div className="mt-4 space-y-1 text-sm text-muted-foreground">
<p>: {kb.documents.length}</p> <p>: {kb.documents.length}</p>
<p>: {kb.chunkSize ?? 500}/{kb.chunkOverlap ?? 50}</p> <p>: {kb.chunkSize ?? 500}/{kb.chunkOverlap ?? 50}</p>
@@ -319,6 +317,7 @@ export const KnowledgeBasePage: React.FC = () => {
kbChunkOverlap={kbChunkOverlap} kbChunkOverlap={kbChunkOverlap}
setKbChunkOverlap={setKbChunkOverlap} setKbChunkOverlap={setKbChunkOverlap}
embeddingOptions={embeddingOptions} embeddingOptions={embeddingOptions}
hasDbEmbeddingModels={hasDbEmbeddingModels}
/> />
</div> </div>
); );
@@ -341,6 +340,7 @@ const KnowledgeBaseModal: React.FC<{
kbChunkOverlap: number; kbChunkOverlap: number;
setKbChunkOverlap: (v: number) => void; setKbChunkOverlap: (v: number) => void;
embeddingOptions: string[]; embeddingOptions: string[];
hasDbEmbeddingModels: boolean;
}> = ({ }> = ({
isOpen, isOpen,
onClose, onClose,
@@ -358,6 +358,7 @@ const KnowledgeBaseModal: React.FC<{
kbChunkOverlap, kbChunkOverlap,
setKbChunkOverlap, setKbChunkOverlap,
embeddingOptions, embeddingOptions,
hasDbEmbeddingModels,
}) => ( }) => (
<Dialog <Dialog
isOpen={isOpen} isOpen={isOpen}
@@ -415,6 +416,12 @@ const KnowledgeBaseModal: React.FC<{
</div> </div>
</div> </div>
{!hasDbEmbeddingModels && (
<p className="text-xs text-yellow-400/90">
LLM Library `embedding` 使 embedding
</p>
)}
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
使 ChromaDB Embedding Model 使 ChromaDB Embedding Model
</p> </p>