setu / docs /pdf_processing_architecture.md
khagu's picture
chore: finally untrack large database files
3998131

PDF Processing System Architecture

System Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Frontend (Next.js)                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  PDF Upload Component                                     β”‚   β”‚
β”‚  β”‚  β€’ File input                                             β”‚   β”‚
β”‚  β”‚  β€’ Drag & drop                                            β”‚   β”‚
β”‚  β”‚  β€’ Progress indicator                                     β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                      HTTP/FormData
                           β”‚
                           β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      FastAPI Backend                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                   β”‚
β”‚  POST /api/v1/process-pdf                                        β”‚
β”‚  └─► Extract sentences only                                      β”‚
β”‚                                                                   β”‚
β”‚  POST /api/v1/process-pdf-to-bias                                β”‚
β”‚  └─► Extract + Analyze bias (complete pipeline)                 β”‚
β”‚                                                                   β”‚
β”‚  GET /api/v1/pdf-health                                          β”‚
β”‚  └─► Service status check                                        β”‚
β”‚                                                                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚                                   β”‚
         β–Ό                                   β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ PDF Bytes  β”‚                   β”‚ PDFProcessor    β”‚
    β”‚            β”‚                   β”‚ (utility/)      β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                                   β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  Step 1: Extract Text       β”‚
         β”‚  PyMuPDF (fitz)             β”‚
         β”‚  β€’ Read PDF pages           β”‚
         β”‚  β€’ Extract raw text         β”‚
         β”‚  β€’ Handle multi-page        β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  Step 2: Clean Text         β”‚
         β”‚  Regex Processing           β”‚
         β”‚  β€’ Remove extra whitespace  β”‚
         β”‚  β€’ Normalize formatting     β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  Step 3: Segment Sentences  β”‚
         β”‚  Nepali-aware Regex         β”‚
         β”‚  β€’ Split on ΰ€¦ΰ€£ΰ₯ΰ€‘ (ΰ₯€)         β”‚
         β”‚  β€’ Handle punctuation       β”‚
         β”‚  β€’ Filter short fragments   β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
                      β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  Step 4: Refine (Optional)  β”‚
         β”‚  Mistral LLM API            β”‚
         β”‚  β€’ Correct segmentation     β”‚
         β”‚  β€’ Remove duplicates        β”‚
         β”‚  β€’ JSON formatting          β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚                         β”‚
         β–Ό                         β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   Sentences  β”‚        β”‚  Return JSON    β”‚
    β”‚   List       β”‚        β”‚  Array          β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜        β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                         β”‚
           β”‚            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚            β”‚                          β”‚
           β”‚            β–Ό                          β–Ό
           β”‚      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚      β”‚ Option A:       β”‚      β”‚ Option B:        β”‚
           β”‚      β”‚ Return to       β”‚      β”‚ Pass to Bias     β”‚
           β”‚      β”‚ User            β”‚      β”‚ Detection        β”‚
           β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                                        β”‚
           β”‚                                        β–Ό
           β”‚                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚                              β”‚  Bias Detection Modelβ”‚
           β”‚                              β”‚  DistilBERT Nepali   β”‚
           β”‚                              β”‚  (module_b)          β”‚
           β”‚                              β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
           β”‚                              β”‚ β€’ Classify sentence  β”‚
           β”‚                              β”‚ β€’ 11 categories:     β”‚
           β”‚                              β”‚  - neutral           β”‚
           β”‚                              β”‚  - gender            β”‚
           β”‚                              β”‚  - caste             β”‚
           β”‚                              β”‚  - religion          β”‚
           β”‚                              β”‚  - political         β”‚
           β”‚                              β”‚  - age               β”‚
           β”‚                              β”‚  - disability        β”‚
           β”‚                              β”‚  - appearance        β”‚
           β”‚                              β”‚  - social status     β”‚
           β”‚                              β”‚  - religiosity       β”‚
           β”‚                              β”‚  - ambiguity         β”‚
           β”‚                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚                                       β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚       Response to User                β”‚
        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
        β”‚ β€’ Extracted sentences                β”‚
        β”‚ β€’ Bias classification results        β”‚
        β”‚ β€’ Confidence scores                  β”‚
        β”‚ β€’ Biased/neutral counts              β”‚
        β”‚ β€’ Processing metadata                β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚  Frontend Display    β”‚
            β”‚  β€’ Show results      β”‚
            β”‚  β€’ Highlight biases  β”‚
            β”‚  β€’ Display stats     β”‚
            β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Data Flow Diagram

INPUT: PDF File
   β”‚
   β”œβ”€ Metadata
   β”‚  β”œβ”€ Filename
   β”‚  β”œβ”€ File size
   β”‚  └─ Upload timestamp
   β”‚
   └─ Binary content
      β”‚
      β–Ό
   PDFProcessor.process_pdf_from_bytes()
      β”‚
      β”œβ”€ extract_text_from_pdf()
      β”‚  β”‚ Uses: PyMuPDF.fitz.open()
      β”‚  β”‚ Output: Raw text string + page count
      β”‚  └─ ~200-500ms
      β”‚
      β”œβ”€ clean_text()
      β”‚  β”‚ Uses: Regex replacements
      β”‚  β”‚ Output: Normalized text string
      β”‚  └─ ~50ms
      β”‚
      β”œβ”€ split_into_sentences()
      β”‚  β”‚ Uses: Nepali-aware regex patterns
      β”‚  β”‚ Output: List[sentences]
      β”‚  └─ ~50-150ms
      β”‚
      └─ refine_sentences_with_llm() [OPTIONAL]
         β”‚ Uses: Mistral API
         β”‚ Input: JSON-formatted sentences
         β”‚ Output: Refined List[sentences]
         └─ ~2-5s (includes API latency)
         
   β–Ό
OUTPUT 1 (Extract Only):
   {
       "success": true,
       "sentences": ["ΰ€΅ΰ€Ύΰ€•ΰ₯ΰ€― ΰ₯§", "ΰ€΅ΰ€Ύΰ€•ΰ₯ΰ€― ΰ₯¨", ...],
       "total_sentences": 15,
       "raw_text": "ΰ€«ΰ₯ΰ€² text...",
       "filename": "doc.pdf"
   }

OUTPUT 2 (Extract + Bias):
   {
       "success": true,
       "total_sentences": 15,
       "biased_count": 2,
       "neutral_count": 13,
       "results": [
           {
               "sentence": "ΰ€΅ΰ€Ύΰ€•ΰ₯ΰ€― ΰ₯§",
               "category": "neutral",
               "confidence": 0.95,
               "is_biased": false
           },
           ...
       ],
       "filename": "doc.pdf"
   }

Component Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    utility/ module                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                               β”‚
β”‚  PDFProcessor Class                                          β”‚
β”‚  β”œβ”€ __init__(mistral_api_key)                               β”‚
β”‚  β”‚  └─ Initialize MistralClient                             β”‚
β”‚  β”‚                                                            β”‚
β”‚  β”œβ”€ extract_text_from_pdf(pdf_path)                          β”‚
β”‚  β”‚  β”œβ”€ fitz.open(pdf_path)                                   β”‚
β”‚  β”‚  β”œβ”€ Iterate pages                                         β”‚
β”‚  β”‚  β”œβ”€ get_text("text")                                      β”‚
β”‚  β”‚  └─ Return: raw text                                      β”‚
β”‚  β”‚                                                            β”‚
β”‚  β”œβ”€ clean_text(text)                                         β”‚
β”‚  β”‚  β”œβ”€ Remove newlines                                       β”‚
β”‚  β”‚  β”œβ”€ Normalize spaces                                      β”‚
β”‚  β”‚  └─ Return: cleaned text                                  β”‚
β”‚  β”‚                                                            β”‚
β”‚  β”œβ”€ split_into_sentences(text)                               β”‚
β”‚  β”‚  β”œβ”€ Apply Nepali regex patterns                           β”‚
β”‚  β”‚  β”œβ”€ Filter short fragments                               β”‚
β”‚  β”‚  └─ Return: sentence list                                β”‚
β”‚  β”‚                                                            β”‚
β”‚  β”œβ”€ refine_sentences_with_llm(sentences)                     β”‚
β”‚  β”‚  β”œβ”€ Format as JSON                                        β”‚
β”‚  β”‚  β”œβ”€ Send to Mistral API                                   β”‚
β”‚  β”‚  β”œβ”€ Parse JSON response                                   β”‚
β”‚  β”‚  └─ Return: refined sentences                             β”‚
β”‚  β”‚                                                            β”‚
β”‚  β”œβ”€ process_pdf(pdf_path, refine_with_llm)                   β”‚
β”‚  β”‚  └─ Complete pipeline (file path)                         β”‚
β”‚  β”‚                                                            β”‚
β”‚  └─ process_pdf_from_bytes(pdf_bytes, refine_with_llm)       β”‚
β”‚     └─ Complete pipeline (bytes)                             β”‚
β”‚                                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    api/routes/ module                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                               β”‚
β”‚  pdf_processing.py (FastAPI Routes)                          β”‚
β”‚  β”œβ”€ POST /api/v1/process-pdf                                β”‚
β”‚  β”‚  β”œβ”€ Receive: file (UploadFile), refine_with_llm          β”‚
β”‚  β”‚  β”œβ”€ Call: PDFProcessor.process_pdf_from_bytes()          β”‚
β”‚  β”‚  └─ Return: PDFProcessingResponse                         β”‚
β”‚  β”‚                                                            β”‚
β”‚  β”œβ”€ POST /api/v1/process-pdf-to-bias                         β”‚
β”‚  β”‚  β”œβ”€ Receive: file, refine_with_llm, confidence_threshold  β”‚
β”‚  β”‚  β”œβ”€ Call: PDFProcessor.process_pdf_from_bytes()          β”‚
β”‚  β”‚  β”œβ”€ Call: run_bias_detection()                           β”‚
β”‚  β”‚  └─ Return: PDFToBiasDetectionResponse                    β”‚
β”‚  β”‚                                                            β”‚
β”‚  └─ GET /api/v1/pdf-health                                  β”‚
β”‚     β”œβ”€ Check: PDFProcessor availability                     β”‚
β”‚     β”œβ”€ Check: Mistral client status                         β”‚
β”‚     └─ Return: health status                                β”‚
β”‚                                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  api/schemas.py (Pydantic Models)            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                               β”‚
β”‚  PDFProcessingRequest / Response                             β”‚
β”‚  PDFToBiasDetectionRequest / Response                        β”‚
β”‚  BiasResult (reused from bias_detection)                     β”‚
β”‚                                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               External Dependencies                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                               β”‚
β”‚  PyMuPDF (fitz)                                              β”‚
β”‚  └─ PDF text extraction                                      β”‚
β”‚                                                               β”‚
β”‚  Mistral API Client                                          β”‚
β”‚  └─ LLM-based sentence refinement                            β”‚
β”‚                                                               β”‚
β”‚  FastAPI                                                      β”‚
β”‚  └─ API framework (from module_a)                            β”‚
β”‚                                                               β”‚
β”‚  Pydantic                                                     β”‚
β”‚  └─ Data validation                                          β”‚
β”‚                                                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Processing Timeline

Timeline for Single PDF (~10 KB):

Time  Component                Duration    Cumulative
────────────────────────────────────────────────────
0ms   β”œβ”€ API receives upload  ~5ms        5ms
      β”‚
5ms   β”œβ”€ Read bytes           ~10ms       15ms
      β”‚
15ms  β”œβ”€ PyMuPDF extraction   ~200ms      215ms
      β”‚
215ms β”œβ”€ Text cleaning        ~30ms       245ms
      β”‚
245ms β”œβ”€ Sentence split       ~100ms      345ms
      β”‚
345ms β”œβ”€ LLM refinement       ~3500ms     3845ms
      β”‚   (if enabled)
      β”‚
3845msβ”œβ”€ Bias detection       ~500ms      4345ms
      β”‚   (if enabled)
      β”‚
4345ms└─ Return response      ~5ms        4350ms
       
Total Time:
β”œβ”€ With LLM + Bias:  ~4.3 seconds
β”œβ”€ With LLM only:    ~3.8 seconds
β”œβ”€ Without LLM:      ~0.35 seconds
└─ Bias only:        ~0.5 seconds (sentence extraction)

Error Handling Flow

API Request
   β”‚
   β”œβ”€ Validate file
   β”‚  β”œβ”€ Is PDF? β†’ No β†’ 400 Bad Request
   β”‚  β”œβ”€ Is empty? β†’ Yes β†’ 400 Bad Request
   β”‚  └─ Is valid? β†’ Continue
   β”‚
   └─ Process PDF
      β”œβ”€ Extract text
      β”‚  β”œβ”€ File not found? β†’ FileNotFoundError β†’ 500
      β”‚  β”œβ”€ Permission denied? β†’ Exception β†’ 500
      β”‚  └─ Success? β†’ Continue
      β”‚
      β”œβ”€ Split sentences
      β”‚  β”œβ”€ No sentences? β†’ Warning in response β†’ 200 (empty results)
      β”‚  └─ Success? β†’ Continue
      β”‚
      └─ Refine with LLM (if enabled)
         β”œβ”€ API key missing? β†’ Warning, use regex β†’ 200 (fallback)
         β”œβ”€ Network error? β†’ Warning, use regex β†’ 200 (fallback)
         β”œβ”€ Invalid JSON? β†’ Warning, use regex β†’ 200 (fallback)
         └─ Success? β†’ Return refined sentences β†’ 200

State Diagram

             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
             β”‚   Idle       β”‚
             β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
        User uploads PDF
                    β”‚
                    β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚   Validating     β”‚
         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
         β”‚             β”‚
    Invalid         Valid
         β”‚             β”‚
         β–Ό             β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Error  β”‚  β”‚  Extracting      β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                 β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
                 β”‚             β”‚
             Success      No Text
                 β”‚             β”‚
                 β–Ό             β–Ό
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚Splitting β”‚  β”‚  Error   β”‚
            β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
            β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
            β”‚             β”‚
        Success      No Sentences
            β”‚             β”‚
            β–Ό             β–Ό
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚Refining    β”‚  β”‚  Error   β”‚
      β”‚(Optional)  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
             β”‚
      β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
      β”‚             β”‚
   Success       Failed
      β”‚             β”‚
      β”œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€
      β”‚     β”‚       β”‚
      β–Ό     β–Ό       β–Ό
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ Formatting   β”‚ β”‚Fallbackβ”‚
   β”‚Response      β”‚ β”‚(Regex) β”‚
   β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
          β”‚             β”‚
          β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
                 β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚ Bias Detection  β”‚
        β”‚ (if enabled)    β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
          β”‚             β”‚
      Success        Error
          β”‚             β”‚
          β–Ό             β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚Format      β”‚  β”‚Return  β”‚
    β”‚Response    β”‚  β”‚Error   β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
             β”‚          β”‚
             β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                  β”‚
                  β–Ό
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚Send Response     β”‚
        β”‚to Client         β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Integration Points

Frontend (Next.js)
       β”‚
       β”œβ”€β–Ί /api/v1/process-pdf
       β”‚   └─ Use for: Sentence extraction only
       β”‚
       β”œβ”€β–Ί /api/v1/process-pdf-to-bias
       β”‚   └─ Use for: Full analysis (PDF β†’ Bias)
       β”‚
       └─► /api/v1/pdf-health
           └─ Use for: Service status check

Internal Integration
       β”‚
       β”œβ”€β–Ί PDFProcessor class
       β”‚   └─ Use in: Custom workflows
       β”‚
       └─► run_bias_detection() function
           └─ Use in: Direct bias analysis

This architecture provides: βœ… Scalable processing pipeline βœ… Clear separation of concerns βœ… Reusable components βœ… Error resilience βœ… Performance optimization options βœ… Easy integration points