Document Vault System Architecture
Overview
The Document Vault system provides secure storage, processing, and AI-powered semantic search for financial documents. It allows users to upload, organize, and search documents like tax forms, insurance policies, and legal documents. The system uses Reducto for document parsing and OpenAI embeddings for semantic search.Architecture Components
Frontend Components
- Vault Page (
/app/vault/page.tsx): Main entry point for the vault UI - Folder List (
/app/vault/components/folder-list.tsx): Displays and manages folders - Document List (
/app/vault/components/document-list.tsx): Displays and manages documents - Document Viewer: Renders PDFs and other document types
- Search Interface: Allows semantic search across document contents
Backend API Routes
- Process Document (
/app/api/vault/process-document/): Handles document uploads and initiates processing - Poll Document Status (
/app/api/vault/poll-document-status/): Checks document processing status and extracts chunks - Fetch Document Chunks (
/app/api/vault/fetch-document-chunks/): Retrieves chunks for a specific document
Search Implementation
- Document Search Tool (
/app/api/chat/document-search-tool.ts): Provides a semantic search tool for the chat system - Document Retriever (
/app/api/chat/document-retriever.ts): Implements vector search using pgvector - Vector Matching: Uses Supabase’s
match_document_chunksRPC function for similarity search
External Services
- Reducto: AI document parsing service
- Processes documents (PDFs, images, etc.)
- Extracts text and metadata
- Splits documents into semantic chunks
- OpenAI: Provides embeddings for semantic search
- Supabase: Database and storage
- Stores document metadata and chunks
- Handles vector search via pgvector
Document Processing Flow
-
Upload Phase
- User uploads document through the UI
- Upload dialog closes immediately for better UX
- Toast notifications inform user of upload progress
- File is uploaded to Supabase Storage
- Document metadata is created in
vault_documentstable with status “pending” - Processing job is initiated via Reducto SDK in the background
-
Processing Phase (Separate Concerns)
- Frontend polls status endpoint to check processing progress (every 5s)
poll-document-statusendpoint only checks Reducto job status- Status progression: “pending” → “processing” → “completed”
- Document is displayed with appropriate status badge in the UI
- When Reducto job completes, frontend triggers chunk extraction
-
Chunk Extraction Phase
fetch-document-chunksendpoint is called exactly once per document- Endpoint retrieves chunks from Reducto API
- Frontend maintains tracking of processed documents to prevent duplicate calls
- Backend extracts text from various chunk formats (handles structural variations)
-
Embedding Generation Phase
- Text from each chunk is processed in batches (10 chunks at a time)
- OpenAI embeddings are generated for each chunk
- Embeddings are stored in the
vault_document_chunkstable via pgvector - Document status is updated to “completed” when all chunks are processed
-
Search Phase
- User enters a search query
- Query is converted to an embedding
- Vector similarity search is performed against chunk embeddings
- Matching chunks are returned sorted by relevance
- UI displays results with document references
Database Schema
vault_folders Table
id: Primary keyname: Folder nameclerk_id: User IDcreated_at: Timestampupdated_at: Timestamp
vault_documents Table
id: Primary keyname: Document namefolder_uuid: Foreign key to vault_foldersclerk_id: User IDpath: Storage path in Supabase storageprocessing_status: Processing status (pending, processing, completed, error)content_type: MIME type of the documentfile_size: File size in bytessource: Source of the document (e.g., “vault”)external_id: Reducto job ID for processingcreated_at: Timestampupdated_at: Timestampchunk_count: Number of chunks successfully processed
vault_document_chunks Table
id: Primary keydocument_id: Foreign key to vault_documentschunk_index: Sequential index within documentcontent: Text content of chunkmetadata: JSON containing additional chunk datablocks: Layout blockspage: Page numberdocument_type: Detected document typetitle: Chunk title or headingtext: Raw text content (may duplicate content field)value: Alternative text content fieldyear: Detected document year
embedding: Vector embedding for semantic search (pgvector type)
User Experience Flow
- User navigates to the Vault section
- The first folder is auto-selected if available
- User can:
- Create/rename/delete folders
- Upload documents to folders
- View documents in the selected folder
- Search across all documents
- Delete documents
- View document details and content
Implementation Details
Reducto Integration
The system uses the official Reducto SDK for document processing with separation of concerns:Embedding Generation
Embeddings are generated in batches using OpenAI with error handling and text preprocessing:Chunk Processing Optimizations
The system optimizes chunk processing through:- Frontend tracking of already processed documents to prevent duplicate calls
- Batch processing of embeddings (10 chunks at a time) to manage API rate limits
- Robust extraction of text from various chunk formats (string, content, text, value)
- Error tolerance - continues processing chunks even if some embedding generations fail
Best Practices
-
Document Types: The system is optimized for financial documents like:
- Tax Forms
- Insurance Policies
- Trust & Estate Documents
- Real Estate Documents
- Private Investment Documents
- Legal Documents
-
File Formats: The system supports:
- PDF files (preferred)
- Images (PNG, JPG)
- Text documents
- Processing Times: Large documents may take longer to process. The UI provides feedback during processing.
- Search Tips: Semantic search works best with specific queries rather than general terms.
Troubleshooting
Common Issues and Solutions
Document Processing Issues
-
Document stuck in “pending” status:
- Check frontend logs for errors during document upload
- Verify that the document was properly uploaded to Supabase storage
- Ensure the Reducto API key is valid and has sufficient credits
- Check if the API call to
process-documentendpoint completed successfully
-
Document stuck in “processing” status:
- Check if the Reducto job was properly created (check
external_idin document record) - Check Reducto job status directly via the Reducto dashboard
- Verify that
poll-document-statusendpoint is being called by the frontend - Look for errors in the
fetch-document-chunksAPI call - Try manually calling the
fetch-document-chunksendpoint with the document ID
- Check if the Reducto job was properly created (check
-
Chunks not being generated:
- Check if frontend is calling
fetch-document-chunksafter job completion - Look for errors in the embedding generation process in server logs
- Check that the Reducto job is actually returning chunks
- Verify the structure of chunks in the Reducto response
- Check if frontend is calling
-
Multiple calls to fetch chunks:
- Check the
chunksAlreadyFetchedSet in frontend tracking - Look for race conditions in the status polling logic
- Check the
Search Issues
- Search not returning expected results:
- Verify chunks were successfully generated with embeddings
- Check chunk count in the document record matches expected document size
- Try shorter, more specific search queries
- Look for errors during embedding generation that might have skipped chunks
Performance Issues
- Slow document processing:
- Check document size and complexity (large documents take longer)
- Monitor batch sizes for embedding generation (default: 10 chunks)
- Consider adjusting polling interval (currently 5 seconds)
Debugging Tools
- Frontend console logs for document status polling and chunk fetching
- Backend server logs for API endpoint calls and embedding generation
- Supabase database explorer for direct inspection of document records
- Reducto dashboard for job status and parsing results