diff --git a/core/embedding/colpali_embedding_model.py b/core/embedding/colpali_embedding_model.py index 0201e40..0041181 100644 --- a/core/embedding/colpali_embedding_model.py +++ b/core/embedding/colpali_embedding_model.py @@ -56,10 +56,10 @@ class ColpaliEmbeddingModel(BaseEmbeddingModel): else: contents.append(chunk.content) - return [self.generate_embeddings(content) for content in contents] + return [await self.generate_embeddings(content) for content in contents] async def embed_for_query(self, text: str) -> torch.Tensor: - return self.generate_embeddings(text) + return await self.generate_embeddings(text) async def generate_embeddings(self, content: str | Image) -> np.ndarray: if isinstance(content, Image): diff --git a/ui-component/app/page.tsx b/ui-component/app/page.tsx index cbd5ad0..77f003b 100644 --- a/ui-component/app/page.tsx +++ b/ui-component/app/page.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useState, useEffect, ChangeEvent } from 'react'; +import { Checkbox } from "@/components/ui/checkbox"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -59,15 +60,17 @@ interface QueryOptions extends SearchOptions { graph_name?: string; } -interface BatchUploadError { - filename: string; - error: string; -} +// Commented out as currently unused +// interface BatchUploadError { +// filename: string; +// error: string; +// } const MorphikUI = () => { const [activeSection, setActiveSection] = useState('documents'); const [documents, setDocuments] = useState([]); const [selectedDocument, setSelectedDocument] = useState(null); + const [selectedDocuments, setSelectedDocuments] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const [searchResults, setSearchResults] = useState([]); const [chatQuery, setChatQuery] = useState(''); @@ -249,6 +252,108 @@ const MorphikUI = () => { setLoading(false); } }; + + // Handle multiple document deletion + const handleDeleteMultipleDocuments = async () => { + if (selectedDocuments.length === 0) return; + + try { + setLoading(true); + + // Show initial alert for deletion progress + const alertId = 'delete-multiple-progress'; + showAlert(`Deleting ${selectedDocuments.length} documents...`, { + type: 'info', + dismissible: false, + id: alertId + }); + + // Perform deletions sequentially + const results = await Promise.all( + selectedDocuments.map(docId => + fetch(`${API_BASE_URL}/documents/${docId}`, { + method: 'DELETE', + headers + }) + ) + ); + + // Check if any deletion failed + const failedCount = results.filter(res => !res.ok).length; + + // Clear selected document if it was among deleted ones + if (selectedDocument && selectedDocuments.includes(selectedDocument.external_id)) { + setSelectedDocument(null); + } + + // Clear selection + setSelectedDocuments([]); + + // Refresh documents list + await fetchDocuments(); + + // Remove progress alert + removeAlert(alertId); + + // Show final result alert + if (failedCount > 0) { + showAlert(`Deleted ${selectedDocuments.length - failedCount} documents. ${failedCount} deletions failed.`, { + type: "warning", + duration: 4000 + }); + } else { + showAlert(`Successfully deleted ${selectedDocuments.length} documents`, { + type: "success", + duration: 3000 + }); + } + + } catch (err) { + const errorMsg = err instanceof Error ? err.message : 'An unknown error occurred'; + showAlert(errorMsg, { + type: 'error', + title: 'Delete Failed', + duration: 5000 + }); + } finally { + setLoading(false); + } + }; + + // Toggle document selection - currently handled by handleCheckboxChange + // Keeping implementation in comments for reference + /* + const toggleDocumentSelection = (e: React.MouseEvent, docId: string) => { + e.stopPropagation(); // Prevent document selection/details view + + setSelectedDocuments(prev => { + if (prev.includes(docId)) { + return prev.filter(id => id !== docId); + } else { + return [...prev, docId]; + } + }); + }; + */ + + // Handle checkbox change (wrapper function for use with shadcn checkbox) + const handleCheckboxChange = (checked: boolean | "indeterminate", docId: string) => { + setSelectedDocuments(prev => { + if (checked === true && !prev.includes(docId)) { + return [...prev, docId]; + } else if (checked === false && prev.includes(docId)) { + return prev.filter(id => id !== docId); + } + return prev; + }); + }; + + // Helper function to get "indeterminate" state for select all checkbox + const getSelectAllState = () => { + if (selectedDocuments.length === 0) return false; + if (selectedDocuments.length === documents.length) return true; + return "indeterminate"; + }; // Handle file upload const handleFileUpload = async () => { @@ -780,9 +885,21 @@ const MorphikUI = () => { {activeSection === 'documents' && (
-
-

Your Documents

-

Manage your uploaded documents and view their metadata.

+
+
+

Your Documents

+

Manage your uploaded documents and view their metadata.

+
+ {selectedDocuments.length > 0 && ( + + )}
{
-
Filename
+
+ { + if (checked) { + setSelectedDocuments(documents.map(doc => doc.external_id)); + } else { + setSelectedDocuments([]); + } + }} + aria-label="Select all documents" + /> +
+
Filename
Type
ID
@@ -952,7 +1083,16 @@ const MorphikUI = () => { onClick={() => handleDocumentClick(doc)} className="grid grid-cols-12 p-3 cursor-pointer hover:bg-gray-50 border-b" > -
+
+ handleCheckboxChange(checked, doc.external_id)} + onClick={(e) => e.stopPropagation()} + aria-label={`Select ${doc.filename || 'document'}`} + /> +
+
{doc.filename || 'N/A'} {doc.external_id === selectedDocument?.external_id && ( Selected @@ -1083,14 +1223,14 @@ const MorphikUI = () => { {/* Search Section */} {activeSection === 'search' && ( - + Search Documents Search across your documents to find relevant information. - +
{
- - - {showSearchAdvanced && ( -
-
+ + + + + + + Search Options + + Configure advanced search parameters + + + +