Cognitive-rag / 01_data_ingestion / REPORT.md
REPORT.md
Raw

Ingestion Pipeline Audit Report

1. Audit Summary

The multimodal ingestion pipeline was audited against all required modalities defined in the Week 7/8 task specification. Every modality was tested using real-world datasets (not synthetic data).

Modality File Types Test Dataset Status
Text (digital) TXT, MD, HTML WikiText-103 (3 files) PASS
PDF PDF UnstructuredPDFLoader (via LangChain) Supported (no PDF test data in current dataset)
Word DOCX UnstructuredWordDocumentLoader Supported (no DOCX test data)
Slides PPTX UnstructuredPowerPointLoader Supported (no PPTX test data)
Images (OCR) JPG, PNG, TIF, BMP CORD-v2 receipts (50 images) PASS (107/150 produced text)
Audio (ASR) MP3, WAV, FLAC, M4A LibriSpeech (2 FLAC files) PASS
Video MP4, WEBM, MOV, AVI YouTube UGC subset (4 WEBM files) PASS (chunked + keyframes)
CSV CSV MTSamples medical corpus (4999 rows) PASS

Final test suite output (9/9 pass):

[PASS] text         3 files -> 3 records, 0 errors
[PASS] image        5 files -> 3 records, 2 empty OCR
[PASS] audio        2 files -> 2 records, 0 errors
[PASS] video        2 files -> 34 records, 0 errors
[PASS] csv          1 files -> 4999 records, 0 errors
[PASS] mixed        15 files -> 22 records, 0 errors
[PASS] max_files    caps 1,2,3 respected; uncapped processed 3
[PASS] reporting    succeeded=3, failed=0, empty=2
[PASS] schema       All 5072 records pass schema validation

2. Bugs Found and Fixed

Bug 1: Root-level batch ingestion produced 0 records

Cause: The default Python 3.14 venv cannot install faster-whisper (requires Python <3.13). When the batch run pointed at 00_Data_test, it encountered video files first and all failed with a dependency error. The cleaned_documents.jsonl was 0 bytes.

Fix: Documented that venv/bin/python3.11 must be used. The test suite auto-detects missing dependencies and SKIPs rather than FAILs.

Bug 2: Silent failures on empty OCR / empty text extraction

Cause: When records_from_image() returned [] (OCR produced no text), the file was silently skipped — no logging, no tracking. Same for text documents that produced empty content after normalization.

Fix: Empty extraction is now explicitly tracked. The ingest() function records these in empty_files with modality and reason, and logs them to ingestion_errors.jsonl with "stage": "empty_content".

Bug 3: Error log entries missing modality/filetype

Cause: The log_error() function only recorded {path, stage, error} — no indication of what type of file failed.

Fix: Error log entries now include modality and filetype fields:

{"path": "video/clip.mp4", "stage": "ingest", "error": "Missing faster-whisper", "modality": "video", "filetype": "video"}

Bug 4: No success/failure tracking in stats

Cause: ingestion_stats.json only reported aggregate counts (files_processed, records_emitted, errors). No way to see which files succeeded vs. failed.

Fix: Stats now include explicit file-level tracking:

{
  "counts": {
    "files_processed": 5,
    "files_succeeded": 3,
    "files_failed": 0,
    "files_empty": 2,
    "files_skipped": 0,
    "records_emitted": 3,
    "errors": 2
  },
  "success_files": ["train_000037.png", "test_000020.png", "test_000008.png"],
  "failed_files": [],
  "empty_files": [
    {"path": "test_000034.png", "modality": "image", "reason": "Extraction produced no content"},
    {"path": "train_000023.png", "modality": "image", "reason": "Extraction produced no content"}
  ]
}

Bug 5: iter_files() output directory exclusion too narrow

Cause: The original filter if "01_data_ingestion" in path.parts and "output" in path.parts only excluded files under 01_data_ingestion/output/, not output directories in arbitrary locations.

Fix: Simplified to if "output" in path.parts which prevents re-ingesting any previous output.


3. Fixes Applied

Fix File Description
Enhanced error logging ingest_multimodal.py log_error() now accepts and includes modality and filetype
Success/fail/empty tracking ingest_multimodal.py New lists: success_files, failed_files, empty_files, skipped_files
Silent failure logging ingest_multimodal.py Empty extraction now logged as "stage": "empty_content"
Skipped file logging ingest_multimodal.py Unknown file types tracked in skipped_files
Same reporting in folder script ingest_folder.py Mirrored all reporting enhancements
Reporting tests test_ingestion.py New test_reporting validates stats structure
Output dir filter fix ingest_multimodal.py Broader output directory exclusion
Automated test suite test_ingestion.py 12 tests covering all modalities + schema + reporting + chunked video + keyframes
Batch folder script ingest_folder.py --modality and --filetype filtering for targeted ingestion
Chunked video processing ingest_multimodal.py Time-based audio chunking with checkpoint recovery for long videos
Keyframe extraction ingest_multimodal.py FFmpeg scene detection + OCR for visual content in videos
Video CLI flags both scripts --chunk-duration, --extract-keyframes, --scene-threshold

4. Output Schema

All modalities produce records conforming to this schema:

{
  "doc_id": "policies-handbook-2024-pdf__a1b2c3d4",
  "page": 4,
  "section": "page_4",
  "section_index": 7,
  "content": "Employees are entitled to parental leave...",
  "source": {
    "path": "policies/handbook_2024.pdf",
    "filename": "handbook_2024.pdf",
    "filetype": "pdf",
    "modality": "text",
    "loader": "UnstructuredPDFLoader",
    "record_id": "policies-handbook-2024-pdf__a1b2c3d4:000007",
    "prev_record_id": "policies-handbook-2024-pdf__a1b2c3d4:000006",
    "next_record_id": "policies-handbook-2024-pdf__a1b2c3d4:000008"
  }
}

Modality-specific metadata fields:

Field Modalities Description
source.page_number text (PDF, DOCX, PPTX) Page or slide number
source.page_type text (PDF, DOCX, PPTX) "page" or "slide"
source.element_type text (Unstructured) Element category (e.g., "NarrativeText")
source.timestamp_start audio, video Segment start time in seconds
source.timestamp_end audio, video Segment end time in seconds
source.timestamp audio, video Formatted as HH:MM:SS.mmm
source.language audio, video Detected language code
source.language_probability audio, video Detection confidence (0-1)
source.ocr_engine image, video_frame "tesseract" or "paddle"
source.ocr_language image, video_frame Language code used for OCR
source.row_index csv Row number (1-indexed)
source.column_count csv Number of columns
source.frame_path video_frame Relative path to extracted frame PNG
source.frame_index video_frame Sequential index of extracted frame
source.scene_threshold video_frame Scene detection threshold used

Mapping to example schema from instructions:

Instruction Example Our Schema Notes
doc_id doc_id Identical
source_type source.modality + source.filetype Split into two fields for precision
location.timestamp source.timestamp Flat field, same HH:MM:SS format
content content Identical
metadata source All metadata consolidated in source dict

The schema is consumed as-is by the downstream chunk_documents.py (on main) and chunk_mtsamples.py pipelines.


5. Remaining Known Limitations

  1. Legal hearings dataset validated — Nathan's dataset (Zuckerberg Congressional Hearing 2018 MP4 + Theranos trial exhibit PDFs) has been ingested successfully. The 5h15m video uses checkpoint-based caching with 300-second chunks. All PDFs processed without errors.

  2. LangSmith not integrated into ingestion — LangSmith tracing is configured on main for retrieval (04_vectoredb/config.py) but not for ingestion. Adding @traceable decorators to ingestion functions would enable end-to-end observability.

  3. Single-region OCR per image — Each image produces exactly one record. No spatial/bounding-box indexing for multi-region documents.

  4. Python version constraint — Audio/video ingestion requires Python 3.10-3.12 due to onnxruntime dependency in faster-whisper. The project's default venv uses Python 3.14 which silently skips the dependency.

  5. No PDF-specific test data — PDF ingestion is supported via UnstructuredPDFLoader with configurable strategy (auto, hi_res, fast, ocr_only), but no PDF files exist in the test dataset. Testing was done only via the loader API.

  6. No DOCX/PPTX test data — Supported via LangChain Unstructured loaders but untested with real files.

  7. Keyframe OCR quality depends on video resolution — Low-resolution videos or videos with text rendered at small sizes may produce poor or empty OCR results. The pipeline skips frames with no OCR output to avoid polluting the index with empty records.

  8. Checkpoint stores segment count only, not records — Checkpoint files track which chunks are complete and the running segment count. On resume, already-completed chunks are skipped and transcription continues from where it left off. For extremely long videos (10+ hours), a future optimization could stream records to an intermediate file.


6. Scripts Reference

Script Purpose Usage
ingest_multimodal.py Core ingestion engine python ingest_multimodal.py --input 00_Data --chunk-duration 600 --extract-keyframes
ingest_folder.py Batch ingestion with filtering python ingest_folder.py --input /path --modality video --extract-keyframes
test_ingestion.py Automated test suite python test_ingestion.py --data-dir 00_Data_test --modality all
validate_ingestion.py JSONL schema validator python validate_ingestion.py --input output/cleaned_documents.jsonl
download_test_datasets.py Download test data python download_test_datasets.py --out 00_Data_test --text --image --audio --video