/** * BULK IMPORT FEATURE CODE SNIPPETS * * Copy these code blocks into src/App.tsx at the specified locations */ // ============================================================================ // 1. ICON IMPORT (Add to lucide-react imports, around line 78) // ============================================================================ // Add FileSpreadsheet after XCircle: /* XCircle, FileSpreadsheet, // <-- ADD THIS } from 'lucide-react'; */ // ============================================================================ // 2. MODAL DISCLOSURE HOOK (Around line 526, after isScenariosOpen) // ============================================================================ const { isOpen: isBulkImportOpen, onOpen: onBulkImportOpen, onClose: onBulkImportClose } = useDisclosure(); // ============================================================================ // 3. STATE VARIABLES (Around line 539, after importJson state) // ============================================================================ // Bulk import states const [bulkImportData, setBulkImportData] = useState(''); const [bulkImportFormat, setBulkImportFormat] = useState<'csv' | 'json'>('json'); const [bulkImportPreview, setBulkImportPreview] = useState}>>([]); const [bulkImportProgress, setBulkImportProgress] = useState({ current: 0, total: 0, errors: 0 }); const [isBulkImporting, setIsBulkImporting] = useState(false); // ============================================================================ // 4. CSV PARSER FUNCTION (Add after state declarations, around line 545) // ============================================================================ // CSV Parser for bulk import const parseCsvVectors = useCallback((csvText: string): Array<{id: string, embedding: number[], metadata?: Record}> => { const lines = csvText.trim().split('\n'); if (lines.length < 2) { throw new Error('CSV must have header row and at least one data row'); } const header = lines[0].toLowerCase().split(',').map(h => h.trim()); const idIndex = header.indexOf('id'); const embeddingIndex = header.indexOf('embedding'); const metadataIndex = header.indexOf('metadata'); if (idIndex === -1 || embeddingIndex === -1) { throw new Error('CSV must have "id" and "embedding" columns'); } const vectors: Array<{id: string, embedding: number[], metadata?: Record}> = []; for (let i = 1; i < lines.length; i++) { const line = lines[i].trim(); if (!line) continue; // Simple CSV parsing (handles quoted fields) const values: string[] = []; let current = ''; let inQuotes = false; for (let j = 0; j < line.length; j++) { const char = line[j]; if (char === '"') { inQuotes = !inQuotes; } else if (char === ',' && !inQuotes) { values.push(current.trim()); current = ''; } else { current += char; } } values.push(current.trim()); if (values.length < header.length) continue; try { const id = values[idIndex].replace(/^"(.*)"$/, '$1'); const embeddingStr = values[embeddingIndex].replace(/^"(.*)"$/, '$1'); const embedding = JSON.parse(embeddingStr); if (!Array.isArray(embedding) || !embedding.every(n => typeof n === 'number')) { throw new Error(`Invalid embedding format at row ${i + 1}`); } let metadata: Record = {}; if (metadataIndex !== -1 && values[metadataIndex]) { const metadataStr = values[metadataIndex].replace(/^"(.*)"$/, '$1').replace(/""/g, '"'); metadata = JSON.parse(metadataStr); } vectors.push({ id, embedding, metadata }); } catch (err) { console.error(`Error parsing row ${i + 1}:`, err); throw new Error(`Failed to parse row ${i + 1}: ${err instanceof Error ? err.message : String(err)}`); } } return vectors; }, []); // ============================================================================ // 5. JSON PARSER FUNCTION (After CSV parser) // ============================================================================ // JSON Parser for bulk import const parseJsonVectors = useCallback((jsonText: string): Array<{id: string, embedding: number[], metadata?: Record}> => { try { const data = JSON.parse(jsonText); if (!Array.isArray(data)) { throw new Error('JSON must be an array of vectors'); } return data.map((item, index) => { if (!item.id || !item.embedding) { throw new Error(`Vector at index ${index} missing required "id" or "embedding" field`); } if (!Array.isArray(item.embedding) || !item.embedding.every((n: unknown) => typeof n === 'number')) { throw new Error(`Vector at index ${index} has invalid embedding format`); } return { id: String(item.id), embedding: item.embedding, metadata: item.metadata || {} }; }); } catch (err) { throw new Error(`Failed to parse JSON: ${err instanceof Error ? err.message : String(err)}`); } }, []); // ============================================================================ // 6. PREVIEW HANDLER (After parsing functions) // ============================================================================ // Handle preview generation const handleGeneratePreview = useCallback(() => { if (!bulkImportData.trim()) { addLog('warning', 'No data to preview'); return; } try { const vectors = bulkImportFormat === 'csv' ? parseCsvVectors(bulkImportData) : parseJsonVectors(bulkImportData); setBulkImportPreview(vectors.slice(0, 5)); addLog('success', `Preview generated: ${vectors.length} vectors found (showing first 5)`); } catch (err) { addLog('error', `Preview failed: ${err instanceof Error ? err.message : String(err)}`); setBulkImportPreview([]); } }, [bulkImportData, bulkImportFormat, parseCsvVectors, parseJsonVectors, addLog]); // ============================================================================ // 7. BULK IMPORT HANDLER (After preview handler) // ============================================================================ // Handle bulk import execution const handleBulkImport = useCallback(async () => { if (!bulkImportData.trim()) { addLog('warning', 'No data to import'); return; } try { setIsBulkImporting(true); const vectors = bulkImportFormat === 'csv' ? parseCsvVectors(bulkImportData) : parseJsonVectors(bulkImportData); setBulkImportProgress({ current: 0, total: vectors.length, errors: 0 }); let successCount = 0; let errorCount = 0; for (let i = 0; i < vectors.length; i++) { try { const { id, embedding, metadata } = vectors[i]; insertVectorWithId(id, embedding, metadata || {}); successCount++; } catch (err) { console.error(`Failed to import vector ${vectors[i].id}:`, err); errorCount++; } setBulkImportProgress({ current: i + 1, total: vectors.length, errors: errorCount }); // Small delay to prevent UI blocking if (i % 10 === 0) { await new Promise(resolve => setTimeout(resolve, 0)); } } refreshVectors(); addLog('success', `Bulk import complete: ${successCount} success, ${errorCount} errors`); // Reset and close setTimeout(() => { setBulkImportData(''); setBulkImportPreview([]); setBulkImportProgress({ current: 0, total: 0, errors: 0 }); setIsBulkImporting(false); onBulkImportClose(); }, 1500); } catch (err) { addLog('error', `Bulk import failed: ${err instanceof Error ? err.message : String(err)}`); setIsBulkImporting(false); } }, [bulkImportData, bulkImportFormat, parseCsvVectors, parseJsonVectors, insertVectorWithId, refreshVectors, addLog, onBulkImportClose]); // ============================================================================ // 8. FILE UPLOAD HANDLER (After bulk import handler) // ============================================================================ // Handle file upload const handleBulkImportFileUpload = useCallback((event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { const text = e.target?.result as string; setBulkImportData(text); // Auto-detect format from file extension const extension = file.name.split('.').pop()?.toLowerCase(); if (extension === 'csv') { setBulkImportFormat('csv'); } else if (extension === 'json') { setBulkImportFormat('json'); } addLog('info', `File loaded: ${file.name} (${(file.size / 1024).toFixed(1)} KB)`); }; reader.onerror = () => { addLog('error', 'Failed to read file'); }; reader.readAsText(file); }, [addLog]); // ============================================================================ // 9. BULK IMPORT BUTTON (Add to Quick Actions, around line 1964) // ============================================================================ /* Add this button after the "Import Data" button in Quick Actions CardBody: */ // ============================================================================ // 10. BULK IMPORT MODAL (Add after Import Modal, around line 2306) // ============================================================================ /* Add this modal component after the {/* Import Modal *\\/} section: */ export const BulkImportModal = () => (
Bulk Import Vectors
{/* Format Selector */}
{/* File Upload */}
{/* Format Guide */}

Expected Format:

{bulkImportFormat === 'csv' ? (
{`id,embedding,metadata
vec1,"[1.0,2.0,3.0]","{\\"category\\":\\"test\\"}"
vec2,"[4.0,5.0,6.0]","{}"`}
                
) : (
{`[
  { "id": "vec1", "embedding": [1.0, 2.0, 3.0], "metadata": { "category": "test" } },
  { "id": "vec2", "embedding": [4.0, 5.0, 6.0], "metadata": {} }
]`}
                
)}
{/* Data Input */}