This commit is contained in:
hzywhite 2025-09-04 11:24:06 +08:00
parent 7c8db78057
commit 2a453fbe37
8 changed files with 104 additions and 65 deletions

View file

@ -17,7 +17,6 @@ import RetrievalTesting from '@/features/RetrievalTesting'
import ApiSite from '@/features/ApiSite' import ApiSite from '@/features/ApiSite'
import { SchemeProvider } from '@/contexts/SchemeContext'; import { SchemeProvider } from '@/contexts/SchemeContext';
import { Tabs, TabsContent } from '@/components/ui/Tabs' import { Tabs, TabsContent } from '@/components/ui/Tabs'
function App() { function App() {

View file

@ -35,7 +35,6 @@ export type LightragStatus = {
embedding_binding: string embedding_binding: string
embedding_binding_host: string embedding_binding_host: string
embedding_model: string embedding_model: string
max_tokens: number
kv_storage: string kv_storage: string
doc_status_storage: string doc_status_storage: string
graph_storage: string graph_storage: string
@ -43,6 +42,7 @@ export type LightragStatus = {
workspace?: string workspace?: string
max_graph_nodes?: string max_graph_nodes?: string
enable_rerank?: boolean enable_rerank?: boolean
rerank_binding?: string | null
rerank_model?: string | null rerank_model?: string | null
rerank_binding_host?: string | null rerank_binding_host?: string | null
summary_language: string summary_language: string
@ -599,6 +599,7 @@ export const uploadDocument = async (
const formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
formData.append('schemeId', schemeId.toString()) formData.append('schemeId', schemeId.toString())
const response = await axiosInstance.post('/documents/upload', formData, { const response = await axiosInstance.post('/documents/upload', formData, {
headers: { headers: {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'

View file

@ -142,7 +142,7 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
// Register the events // Register the events
registerEvents(events) registerEvents(events)
}, [registerEvents, enableEdgeEvents]) }, [registerEvents, enableEdgeEvents, sigma])
/** /**
* When edge size settings change, recalculate edge sizes and refresh the sigma instance * When edge size settings change, recalculate edge sizes and refresh the sigma instance

View file

@ -140,9 +140,9 @@ const GraphLabels = () => {
searchInputClassName="max-h-8" searchInputClassName="max-h-8"
triggerTooltip={t('graphPanel.graphLabels.selectTooltip')} triggerTooltip={t('graphPanel.graphLabels.selectTooltip')}
fetcher={fetchData} fetcher={fetchData}
renderOption={(item) => <div>{item}</div>} renderOption={(item) => <div style={{ whiteSpace: 'pre' }}>{item}</div>}
getOptionValue={(item) => item} getOptionValue={(item) => item}
getDisplayValue={(item) => <div>{item}</div>} getDisplayValue={(item) => <div style={{ whiteSpace: 'pre' }}>{item}</div>}
notFound={<div className="py-6 text-center text-sm">No labels found</div>} notFound={<div className="py-6 text-center text-sm">No labels found</div>}
label={t('graphPanel.graphLabels.label')} label={t('graphPanel.graphLabels.label')}
placeholder={t('graphPanel.graphLabels.placeholder')} placeholder={t('graphPanel.graphLabels.placeholder')}

View file

@ -52,7 +52,7 @@ const StatusCard = ({ status }: { status: LightragStatus | null }) => {
<span>{t('graphPanel.statusCard.rerankerBindingHost')}:</span> <span>{t('graphPanel.statusCard.rerankerBindingHost')}:</span>
<span>{status.configuration.rerank_binding_host || '-'}</span> <span>{status.configuration.rerank_binding_host || '-'}</span>
<span>{t('graphPanel.statusCard.rerankerModel')}:</span> <span>{t('graphPanel.statusCard.rerankerModel')}:</span>
<span>{status.configuration.rerank_model || '-'}</span> <span>{(status.configuration.rerank_binding || '-')} : {(status.configuration.rerank_model || '-')}</span>
</div> </div>
</div> </div>
)} )}

View file

@ -245,22 +245,34 @@ export function AsyncSelect<T>({
</CommandEmpty> </CommandEmpty>
))} ))}
<CommandGroup> <CommandGroup>
{options.map((option) => ( {options.map((option, index) => {
<CommandItem const optionValue = getOptionValue(option);
key={getOptionValue(option)} // Use index as a safe value that won't be trimmed by cmdk
value={getOptionValue(option)} const safeValue = `option-${index}-${optionValue.length}`;
onSelect={handleSelect}
className="truncate" return (
> <CommandItem
{renderOption(option)} key={optionValue}
<Check value={safeValue}
className={cn( onSelect={(selectedSafeValue) => {
'ml-auto h-3 w-3', // Extract the original value from the safe value
selectedValue === getOptionValue(option) ? 'opacity-100' : 'opacity-0' const selectedIndex = parseInt(selectedSafeValue.split('-')[1]);
)} const originalValue = getOptionValue(options[selectedIndex]);
/> console.log(`CommandItem onSelect: safeValue='${selectedSafeValue}', originalValue='${originalValue}' (length: ${originalValue.length})`);
</CommandItem> handleSelect(originalValue);
))} }}
className="truncate"
>
{renderOption(option)}
<Check
className={cn(
'ml-auto h-3 w-3',
selectedValue === optionValue ? 'opacity-100' : 'opacity-0'
)}
/>
</CommandItem>
);
})}
</CommandGroup> </CommandGroup>
</CommandList> </CommandList>
</Command> </Command>

View file

@ -153,7 +153,7 @@ type SortDirection = 'asc' | 'desc';
export default function DocumentManager() { export default function DocumentManager() {
const { selectedScheme } = useScheme(); const { selectedScheme } = useScheme();
console.log('selectedScheme in DocumentManager:', selectedScheme);
// Track component mount status // Track component mount status
const isMountedRef = useRef(true); const isMountedRef = useRef(true);
@ -189,7 +189,7 @@ export default function DocumentManager() {
const setDocumentsPageSize = useSettingsStore.use.setDocumentsPageSize() const setDocumentsPageSize = useSettingsStore.use.setDocumentsPageSize()
// New pagination state // New pagination state
const [, setCurrentPageDocs] = useState<DocStatusResponse[]>([]) const [currentPageDocs, setCurrentPageDocs] = useState<DocStatusResponse[]>([])
const [pagination, setPagination] = useState<PaginationInfo>({ const [pagination, setPagination] = useState<PaginationInfo>({
page: 1, page: 1,
page_size: documentsPageSize, page_size: documentsPageSize,
@ -302,6 +302,16 @@ export default function DocumentManager() {
type DocStatusWithStatus = DocStatusResponse & { status: DocStatus }; type DocStatusWithStatus = DocStatusResponse & { status: DocStatus };
const filteredAndSortedDocs = useMemo(() => { const filteredAndSortedDocs = useMemo(() => {
// Use currentPageDocs directly if available (from paginated API)
// This preserves the backend's sort order and prevents status grouping
if (currentPageDocs && currentPageDocs.length > 0) {
return currentPageDocs.map(doc => ({
...doc,
status: doc.status as DocStatus
})) as DocStatusWithStatus[];
}
// Fallback to legacy docs structure for backward compatibility
if (!docs) return null; if (!docs) return null;
// Create a flat array of documents with status information // Create a flat array of documents with status information
@ -334,7 +344,7 @@ export default function DocumentManager() {
} }
return allDocuments; return allDocuments;
}, [docs, sortField, sortDirection, statusFilter, sortDocuments]); }, [currentPageDocs, docs, sortField, sortDirection, statusFilter, sortDocuments]);
// Calculate current page selection state (after filteredAndSortedDocs is defined) // Calculate current page selection state (after filteredAndSortedDocs is defined)
const currentPageDocIds = useMemo(() => { const currentPageDocIds = useMemo(() => {
@ -481,13 +491,6 @@ export default function DocumentManager() {
}; };
}, [docs]); }, [docs]);
<<<<<<< Updated upstream
// New paginated data fetching function
const fetchPaginatedDocuments = useCallback(async (
page: number,
pageSize: number,
statusFilter: StatusFilter
=======
// Utility function to update component state // Utility function to update component state
const updateComponentState = useCallback((response: any) => { const updateComponentState = useCallback((response: any) => {
setPagination(response.pagination); setPagination(response.pagination);
@ -500,9 +503,9 @@ export default function DocumentManager() {
processed: response.documents.filter((doc: DocStatusResponse) => doc.status === 'processed'), processed: response.documents.filter((doc: DocStatusResponse) => doc.status === 'processed'),
processing: response.documents.filter((doc: DocStatusResponse) => doc.status === 'processing'), processing: response.documents.filter((doc: DocStatusResponse) => doc.status === 'processing'),
pending: response.documents.filter((doc: DocStatusResponse) => doc.status === 'pending'), pending: response.documents.filter((doc: DocStatusResponse) => doc.status === 'pending'),
failed: response.documents.filter((doc: DocStatusResponse) => doc.status === 'failed'),
ready: response.documents.filter((doc: DocStatusResponse) => doc.status === 'ready'), ready: response.documents.filter((doc: DocStatusResponse) => doc.status === 'ready'),
handling: response.documents.filter((doc: DocStatusResponse) => doc.status === 'handling') handling: response.documents.filter((doc: DocStatusResponse) => doc.status === 'handling'),
failed: response.documents.filter((doc: DocStatusResponse) => doc.status === 'failed')
} }
}; };
@ -513,18 +516,19 @@ export default function DocumentManager() {
const handleIntelligentRefresh = useCallback(async ( const handleIntelligentRefresh = useCallback(async (
targetPage?: number, // Optional target page, defaults to current page targetPage?: number, // Optional target page, defaults to current page
resetToFirst?: boolean // Whether to force reset to first page resetToFirst?: boolean // Whether to force reset to first page
>>>>>>> Stashed changes
) => { ) => {
try { try {
if (!isMountedRef.current) return; if (!isMountedRef.current) return;
setIsRefreshing(true); setIsRefreshing(true);
// Prepare request parameters // Determine target page
const pageToFetch = resetToFirst ? 1 : (targetPage || pagination.page);
const request: DocumentsRequest = { const request: DocumentsRequest = {
status_filter: statusFilter === 'all' ? null : statusFilter, status_filter: statusFilter === 'all' ? null : statusFilter,
page, page: pageToFetch,
page_size: pageSize, page_size: pagination.page_size,
sort_field: sortField, sort_field: sortField,
sort_direction: sortDirection sort_direction: sortDirection
}; };
@ -533,27 +537,35 @@ export default function DocumentManager() {
if (!isMountedRef.current) return; if (!isMountedRef.current) return;
// Update pagination state // Boundary case handling: if target page has no data but total count > 0
setPagination(response.pagination); if (response.documents.length === 0 && response.pagination.total_count > 0) {
setCurrentPageDocs(response.documents); // Calculate last page
setStatusCounts(response.status_counts); const lastPage = Math.max(1, response.pagination.total_pages);
// Update legacy docs state for backward compatibility if (pageToFetch !== lastPage) {
const legacyDocs: DocsStatusesResponse = { // Re-request last page
statuses: { const lastPageRequest: DocumentsRequest = {
processed: response.documents.filter(doc => doc.status === 'processed'), ...request,
processing: response.documents.filter(doc => doc.status === 'processing'), page: lastPage
pending: response.documents.filter(doc => doc.status === 'pending'), };
failed: response.documents.filter(doc => doc.status === 'failed')
const lastPageResponse = await getDocumentsPaginated(lastPageRequest);
if (!isMountedRef.current) return;
// Update page state to last page
setPageByStatus(prev => ({ ...prev, [statusFilter]: lastPage }));
updateComponentState(lastPageResponse);
return;
} }
};
if (response.pagination.total_count > 0) {
setDocs(legacyDocs);
} else {
setDocs(null);
} }
// Normal case: update state
if (pageToFetch !== pagination.page) {
setPageByStatus(prev => ({ ...prev, [statusFilter]: pageToFetch }));
}
updateComponentState(response);
} catch (err) { } catch (err) {
if (isMountedRef.current) { if (isMountedRef.current) {
toast.error(t('documentPanel.documentManager.errors.loadFailed', { error: errorMessage(err) })); toast.error(t('documentPanel.documentManager.errors.loadFailed', { error: errorMessage(err) }));
@ -563,7 +575,20 @@ export default function DocumentManager() {
setIsRefreshing(false); setIsRefreshing(false);
} }
} }
}, [sortField, sortDirection, t]); }, [statusFilter, pagination.page, pagination.page_size, sortField, sortDirection, t, updateComponentState]);
// New paginated data fetching function
const fetchPaginatedDocuments = useCallback(async (
page: number,
pageSize: number,
_statusFilter: StatusFilter // eslint-disable-line @typescript-eslint/no-unused-vars
) => {
// Update pagination state
setPagination(prev => ({ ...prev, page, page_size: pageSize }));
// Use intelligent refresh
await handleIntelligentRefresh(page);
}, [handleIntelligentRefresh]);
// Legacy fetchDocuments function for backward compatibility // Legacy fetchDocuments function for backward compatibility
const fetchDocuments = useCallback(async () => { const fetchDocuments = useCallback(async () => {
@ -608,7 +633,7 @@ export default function DocumentManager() {
if (!selectedScheme) { if (!selectedScheme) {
toast.error(t('documentPanel.documentManager.errors.missingSchemeId')); toast.error(t('documentPanel.documentManager.errors.missingSchemeId'));
return; // 直接返回,不继续执行 return;
} }
const framework = selectedScheme.config?.framework; const framework = selectedScheme.config?.framework;
@ -699,9 +724,10 @@ export default function DocumentManager() {
processed: response.documents.filter(doc => doc.status === 'processed'), processed: response.documents.filter(doc => doc.status === 'processed'),
processing: response.documents.filter(doc => doc.status === 'processing'), processing: response.documents.filter(doc => doc.status === 'processing'),
pending: response.documents.filter(doc => doc.status === 'pending'), pending: response.documents.filter(doc => doc.status === 'pending'),
failed: response.documents.filter(doc => doc.status === 'failed'), ready: response.documents.filter((doc: DocStatusResponse) => doc.status === 'ready'),
ready: response.documents.filter(doc => doc.status === 'ready'), handling: response.documents.filter((doc: DocStatusResponse) => doc.status === 'handling'),
handling: response.documents.filter(doc => doc.status === 'handling'), } failed: response.documents.filter(doc => doc.status === 'failed')
}
}; };
if (response.pagination.total_count > 0) { if (response.pagination.total_count > 0) {
@ -728,9 +754,10 @@ export default function DocumentManager() {
if (prevPipelineBusyRef.current !== undefined && prevPipelineBusyRef.current !== pipelineBusy) { if (prevPipelineBusyRef.current !== undefined && prevPipelineBusyRef.current !== pipelineBusy) {
// pipelineBusy state has changed, trigger immediate refresh // pipelineBusy state has changed, trigger immediate refresh
if (currentTab === 'documents' && health && isMountedRef.current) { if (currentTab === 'documents' && health && isMountedRef.current) {
handleManualRefresh(); // Use intelligent refresh to preserve current page
handleIntelligentRefresh();
// Reset polling timer after manual refresh // Reset polling timer after intelligent refresh
const hasActiveDocuments = (statusCounts.processing || 0) > 0 || (statusCounts.pending || 0) > 0; const hasActiveDocuments = (statusCounts.processing || 0) > 0 || (statusCounts.pending || 0) > 0;
const pollingInterval = hasActiveDocuments ? 5000 : 30000; const pollingInterval = hasActiveDocuments ? 5000 : 30000;
startPollingInterval(pollingInterval); startPollingInterval(pollingInterval);
@ -738,7 +765,7 @@ export default function DocumentManager() {
} }
// Update the previous state // Update the previous state
prevPipelineBusyRef.current = pipelineBusy; prevPipelineBusyRef.current = pipelineBusy;
}, [pipelineBusy, currentTab, health, handleManualRefresh, statusCounts.processing, statusCounts.pending, startPollingInterval]); }, [pipelineBusy, currentTab, health, handleIntelligentRefresh, statusCounts.processing, statusCounts.pending, startPollingInterval]);
// Set up intelligent polling with dynamic interval based on document status // Set up intelligent polling with dynamic interval based on document status
useEffect(() => { useEffect(() => {