Merge pull request #1187 from danielaskdd/pipeline-file-path

feat: Add file name display in WebUI
This commit is contained in:
Daniel.y
2025-03-25 23:02:16 +08:00
committed by GitHub
12 changed files with 267 additions and 186 deletions

View File

@@ -1 +1 @@
__api_version__ = "1.2.5" __api_version__ = "1.2.6"

View File

@@ -91,6 +91,7 @@ class DocStatusResponse(BaseModel):
chunks_count: Optional[int] = None chunks_count: Optional[int] = None
error: Optional[str] = None error: Optional[str] = None
metadata: Optional[dict[str, Any]] = None metadata: Optional[dict[str, Any]] = None
file_path: str
class DocsStatusesResponse(BaseModel): class DocsStatusesResponse(BaseModel):
@@ -370,7 +371,7 @@ async def pipeline_enqueue_file(rag: LightRAG, file_path: Path) -> bool:
# Insert into the RAG queue # Insert into the RAG queue
if content: if content:
await rag.apipeline_enqueue_documents(content) await rag.apipeline_enqueue_documents(content, file_paths=file_path.name)
logger.info(f"Successfully fetched and enqueued file: {file_path.name}") logger.info(f"Successfully fetched and enqueued file: {file_path.name}")
return True return True
else: else:
@@ -890,6 +891,7 @@ def create_document_routes(
chunks_count=doc_status.chunks_count, chunks_count=doc_status.chunks_count,
error=doc_status.error, error=doc_status.error,
metadata=doc_status.metadata, metadata=doc_status.metadata,
file_path=doc_status.file_path,
) )
) )
return response return response

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +1,17 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" /> <meta http-equiv="Expires" content="0" />
<link rel="icon" type="image/svg+xml" href="logo.png" /> <link rel="icon" type="image/svg+xml" href="logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lightrag</title> <title>Lightrag</title>
<script type="module" crossorigin src="/webui/assets/index-DJ53id6i.js"></script> <script type="module" crossorigin src="/webui/assets/index-CSLDmoSD.js"></script>
<link rel="stylesheet" crossorigin href="/webui/assets/index-BwFyYQzx.css"> <link rel="stylesheet" crossorigin href="/webui/assets/index-CR7GLPiB.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
</body>
</body> </html>

View File

@@ -124,6 +124,7 @@ export type DocStatusResponse = {
chunks_count?: number chunks_count?: number
error?: string error?: string
metadata?: Record<string, any> metadata?: Record<string, any>
file_path: string
} }
export type DocsStatusesResponse = { export type DocsStatusesResponse = {

View File

@@ -12,7 +12,6 @@ import {
} from '@/components/ui/Table' } from '@/components/ui/Table'
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/Card' import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/Card'
import EmptyCard from '@/components/ui/EmptyCard' import EmptyCard from '@/components/ui/EmptyCard'
import Text from '@/components/ui/Text'
import UploadDocumentsDialog from '@/components/documents/UploadDocumentsDialog' import UploadDocumentsDialog from '@/components/documents/UploadDocumentsDialog'
import ClearDocumentsDialog from '@/components/documents/ClearDocumentsDialog' import ClearDocumentsDialog from '@/components/documents/ClearDocumentsDialog'
@@ -22,12 +21,36 @@ import { toast } from 'sonner'
import { useBackendState } from '@/stores/state' import { useBackendState } from '@/stores/state'
import { RefreshCwIcon } from 'lucide-react' import { RefreshCwIcon } from 'lucide-react'
import { DocStatusResponse } from '@/api/lightrag'
const getDisplayFileName = (doc: DocStatusResponse, maxLength: number = 20): string => {
// Check if file_path exists and is a non-empty string
if (!doc.file_path || typeof doc.file_path !== 'string' || doc.file_path.trim() === '') {
return doc.id;
}
// Try to extract filename from path
const parts = doc.file_path.split('/');
const fileName = parts[parts.length - 1];
// Ensure extracted filename is valid
if (!fileName || fileName.trim() === '') {
return doc.id;
}
// If filename is longer than maxLength, truncate it and add ellipsis
return fileName.length > maxLength
? fileName.slice(0, maxLength) + '...'
: fileName;
};
export default function DocumentManager() { export default function DocumentManager() {
const { t } = useTranslation() const { t } = useTranslation()
const health = useBackendState.use.health() const health = useBackendState.use.health()
const [docs, setDocs] = useState<DocsStatusesResponse | null>(null) const [docs, setDocs] = useState<DocsStatusesResponse | null>(null)
const currentTab = useSettingsStore.use.currentTab() const currentTab = useSettingsStore.use.currentTab()
const showFileName = useSettingsStore.use.showFileName()
const setShowFileName = useSettingsStore.use.setShowFileName()
const fetchDocuments = useCallback(async () => { const fetchDocuments = useCallback(async () => {
try { try {
@@ -107,7 +130,23 @@ export default function DocumentManager() {
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>{t('documentPanel.documentManager.uploadedTitle')}</CardTitle> <div className="flex justify-between items-center">
<CardTitle>{t('documentPanel.documentManager.uploadedTitle')}</CardTitle>
<div className="flex items-center gap-2">
<span className="text-sm text-gray-500">{t('documentPanel.documentManager.fileNameLabel')}</span>
<Button
variant="outline"
size="sm"
onClick={() => setShowFileName(!showFileName)}
className="border-gray-200 dark:border-gray-700 hover:bg-gray-100 dark:hover:bg-gray-800"
>
{showFileName
? t('documentPanel.documentManager.hideButton')
: t('documentPanel.documentManager.showButton')
}
</Button>
</div>
</div>
<CardDescription>{t('documentPanel.documentManager.uploadedDescription')}</CardDescription> <CardDescription>{t('documentPanel.documentManager.uploadedDescription')}</CardDescription>
</CardHeader> </CardHeader>
@@ -129,20 +168,45 @@ export default function DocumentManager() {
<TableHead>{t('documentPanel.documentManager.columns.chunks')}</TableHead> <TableHead>{t('documentPanel.documentManager.columns.chunks')}</TableHead>
<TableHead>{t('documentPanel.documentManager.columns.created')}</TableHead> <TableHead>{t('documentPanel.documentManager.columns.created')}</TableHead>
<TableHead>{t('documentPanel.documentManager.columns.updated')}</TableHead> <TableHead>{t('documentPanel.documentManager.columns.updated')}</TableHead>
<TableHead>{t('documentPanel.documentManager.columns.metadata')}</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody className="text-sm"> <TableBody className="text-sm">
{Object.entries(docs.statuses).map(([status, documents]) => {Object.entries(docs.statuses).map(([status, documents]) =>
documents.map((doc) => ( documents.map((doc) => (
<TableRow key={doc.id}> <TableRow key={doc.id}>
<TableCell className="truncate font-mono">{doc.id}</TableCell> <TableCell className="truncate font-mono overflow-visible">
<TableCell className="max-w-xs min-w-24 truncate"> {showFileName ? (
<Text <>
text={doc.content_summary} <div className="group relative overflow-visible">
tooltip={doc.content_summary} <div className="truncate">
tooltipClassName="max-w-none overflow-visible block" {getDisplayFileName(doc, 35)}
/> </div>
<div className="invisible group-hover:visible fixed z-[9999] mt-1 max-w-[800px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
{doc.file_path}
</div>
</div>
<div className="text-xs text-gray-500">{doc.id}</div>
</>
) : (
<div className="group relative overflow-visible">
<div className="truncate">
{doc.id}
</div>
<div className="invisible group-hover:visible fixed z-[9999] mt-1 max-w-[800px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
{doc.file_path}
</div>
</div>
)}
</TableCell>
<TableCell className="max-w-xs min-w-24 truncate overflow-visible">
<div className="group relative overflow-visible">
<div className="truncate">
{doc.content_summary}
</div>
<div className="invisible group-hover:visible fixed z-[9999] mt-1 max-w-[800px] whitespace-normal break-all rounded-md bg-black/95 px-3 py-2 text-sm text-white shadow-lg dark:bg-white/95 dark:text-black">
{doc.content_summary}
</div>
</div>
</TableCell> </TableCell>
<TableCell> <TableCell>
{status === 'processed' && ( {status === 'processed' && (
@@ -167,9 +231,6 @@ export default function DocumentManager() {
<TableCell className="truncate"> <TableCell className="truncate">
{new Date(doc.updated_at).toLocaleString()} {new Date(doc.updated_at).toLocaleString()}
</TableCell> </TableCell>
<TableCell className="max-w-xs truncate">
{doc.metadata ? JSON.stringify(doc.metadata) : '-'}
</TableCell>
</TableRow> </TableRow>
)) ))
)} )}

View File

@@ -81,9 +81,14 @@
}, },
"errors": { "errors": {
"loadFailed": "فشل تحميل المستندات\n{{error}}", "loadFailed": "فشل تحميل المستندات\n{{error}}",
"scanFailed": "فشل المسح الضوئي للمستندات\n{{error}}", "scanFailed": "فشل مسح المستندات\n{{error}}",
"scanProgressFailed": "فشل الحصول على تقدم المسح الضوئي\n{{error}}" "scanProgressFailed": "فشل الحصول على تقدم المسح\n{{error}}"
} },
"fileNameLabel": "اسم الملف",
"showButton": "عرض",
"hideButton": "إخفاء",
"showFileNameTooltip": "عرض اسم الملف",
"hideFileNameTooltip": "إخفاء اسم الملف"
} }
}, },
"graphPanel": { "graphPanel": {

View File

@@ -83,7 +83,12 @@
"loadFailed": "Failed to load documents\n{{error}}", "loadFailed": "Failed to load documents\n{{error}}",
"scanFailed": "Failed to scan documents\n{{error}}", "scanFailed": "Failed to scan documents\n{{error}}",
"scanProgressFailed": "Failed to get scan progress\n{{error}}" "scanProgressFailed": "Failed to get scan progress\n{{error}}"
} },
"fileNameLabel": "File Name",
"showButton": "Show",
"hideButton": "Hide",
"showFileNameTooltip": "Show file name",
"hideFileNameTooltip": "Hide file name"
} }
}, },
"graphPanel": { "graphPanel": {

View File

@@ -83,7 +83,12 @@
"loadFailed": "加载文档失败\n{{error}}", "loadFailed": "加载文档失败\n{{error}}",
"scanFailed": "扫描文档失败\n{{error}}", "scanFailed": "扫描文档失败\n{{error}}",
"scanProgressFailed": "获取扫描进度失败\n{{error}}" "scanProgressFailed": "获取扫描进度失败\n{{error}}"
} },
"fileNameLabel": "文件名",
"showButton": "显示",
"hideButton": "隐藏",
"showFileNameTooltip": "显示文件名",
"hideFileNameTooltip": "隐藏文件名"
} }
}, },
"graphPanel": { "graphPanel": {

View File

@@ -9,6 +9,10 @@ type Language = 'en' | 'zh' | 'fr' | 'ar'
type Tab = 'documents' | 'knowledge-graph' | 'retrieval' | 'api' type Tab = 'documents' | 'knowledge-graph' | 'retrieval' | 'api'
interface SettingsState { interface SettingsState {
// Document manager settings
showFileName: boolean
setShowFileName: (show: boolean) => void
// Graph viewer settings // Graph viewer settings
showPropertyPanel: boolean showPropertyPanel: boolean
showNodeSearchBar: boolean showNodeSearchBar: boolean
@@ -83,6 +87,7 @@ const useSettingsStoreBase = create<SettingsState>()(
apiKey: null, apiKey: null,
currentTab: 'documents', currentTab: 'documents',
showFileName: false,
retrievalHistory: [], retrievalHistory: [],
@@ -138,12 +143,14 @@ const useSettingsStoreBase = create<SettingsState>()(
updateQuerySettings: (settings: Partial<QueryRequest>) => updateQuerySettings: (settings: Partial<QueryRequest>) =>
set((state) => ({ set((state) => ({
querySettings: { ...state.querySettings, ...settings } querySettings: { ...state.querySettings, ...settings }
})) })),
setShowFileName: (show: boolean) => set({ showFileName: show })
}), }),
{ {
name: 'settings-storage', name: 'settings-storage',
storage: createJSONStorage(() => localStorage), storage: createJSONStorage(() => localStorage),
version: 8, version: 9,
migrate: (state: any, version: number) => { migrate: (state: any, version: number) => {
if (version < 2) { if (version < 2) {
state.showEdgeLabel = false state.showEdgeLabel = false
@@ -186,6 +193,9 @@ const useSettingsStoreBase = create<SettingsState>()(
state.graphMinDegree = 0 state.graphMinDegree = 0
state.language = 'en' state.language = 'en'
} }
if (version < 9) {
state.showFileName = false
}
return state return state
} }
} }