Image-Based PDF Support: Batch Processing with Claude Vision API
Complete implementation of image-based PDF deck analysis using Claude's Vision API with intelligent batch processing. This solves the recurring issue of Docsend-exported decks that contain only images with no extractable text.
Changelog - 2025-11-21
Image-Based PDF Support: Batch Processing with Claude Vision API
Overview
Complete implementation of image-based PDF deck analysis using Claude's Vision API with intelligent batch processing. This solves the recurring issue of Docsend-exported decks that contain only images with no extractable text.
Breaking Changes
None - This is a backward-compatible addition to the deck analyst agent.
New Features
Deck Analyst Agent (src/agents/deck_analyst.py)
Image-Based PDF Detection
NEW: Automatic detection of image-based PDFs via text extraction threshold
Threshold: 1000 characters (accounts for page headers on image PDFs)
Text-based decks have substantially more extractable content
Falls back to vision API when text extraction is minimal
Batch Processing System
NEW:
analyze_pdf_with_vision()function for image-based PDFsNEW: Processes decks in batches of 5 slides to avoid API payload limits
NEW: Intelligent merging of results from all batches
NEW: Per-batch progress logging with batch numbers (e.g., "batch 1/5")
NEW: Graceful error handling - continues with remaining batches if one fails
Image Processing Pipeline
NEW: PyMuPDF (fitz) integration for page rendering
NEW: 0.5x scale rendering to reduce payload size while maintaining readability
NEW: PIL (Pillow) integration for JPEG compression
NEW: JPEG quality: 85 with optimization enabled
NEW: Base64 encoding for Claude Messages API
Batch Merging Logic
NEW: Intelligent data consolidation from multiple batches
NEW: String fields: Uses first non-"Not mentioned" value
NEW: List fields: Appends unique items (team_members, traction_metrics, etc.)
NEW: Dict fields: Merges non-"Not mentioned" values (market_size TAM/SAM/SOM)
NEW: Final page count from PDF document, not individual batch reports
Artifact Saving
FIXED: Vision-based analysis now saves deck analysis artifacts
FIXED: Added missing
save_deck_analysis_artifacts()call in vision pathFIXED: Matches artifact saving behavior of text-based analysis
Bug Fixes
Socials Enrichment Agent (src/agents/socials_enrichment.py)
Team Member Extraction
FIXED: Now handles both structured and string format team data
FIXED: Prefers structured
company.foundersformat (dict with name/title/background)FIXED: Falls back to parsing string format from
team.foundersFIXED: Improved regex parsing for name and role extraction from strings
FIXED: Filters out "Data not available" entries from key_hires
FIXED: Eliminates duplicate variable definitions
Technical Details
Vision API Integration
Batch Processing Flow
Open PDF with PyMuPDF
Calculate total batches (ceil(page_count / 5))
For each batch:
Extract 5 pages (or remaining pages for final batch)
Render each page at 0.5x scale
Convert to PIL Image
Compress as JPEG (quality=85, optimize=True)
Encode as base64
Send to Claude Vision API
Parse JSON response (with markdown code block fallback)
Collect batch results
Merge all batch results intelligently
Save final consolidated analysis
API Payload Structure
{
"model": "claude-sonnet-4-5-20250929",
"max_tokens": 4000,
"messages": [{
"role": "user",
"content": [
{"type": "image", "source": {...}}, # 5 images
{"type": "image", "source": {...}},
{"type": "image", "source": {...}},
{"type": "image", "source": {...}},
{"type": "image", "source": {...}},
{"type": "text", "text": "..."} # Analysis prompt
]
}]
}Merge Algorithm Example
# Batch 1: company_name="RubyData", team_members=[]
# Batch 2: company_name="Not mentioned", team_members=[Alice, Bob]
# Batch 3: company_name="Not mentioned", team_members=[Carol, Dan, Eve]
# Batch 4: company_name="Not mentioned", team_members=[]
# Batch 5: company_name="Not mentioned", team_members=[]
#
# Merged: company_name="RubyData", team_members=[Alice, Bob, Carol, Dan, Eve]Error Handling
Robust Failure Recovery
Individual batch failures don't crash entire pipeline
Continues processing remaining batches
Collects all successful batch results
Only fails if zero batches succeed
Per-batch error logging with specific batch number
Dependencies
New Python Packages
PyMuPDF(fitz) - PDF page renderingPillow(PIL) - Image compression and format conversionanthropic- Direct Messages API client (was already available)
Existing Dependencies
base64(stdlib) - Image encodingio.BytesIO(stdlib) - In-memory image bufferos(stdlib) - Environment variable access
Performance Improvements
Before
Image-based PDFs failed completely (no text to extract)
Docsend exports were unusable
Manual PDF text conversion required
~5 decks rejected due to image-only format
After
All 22 slides analyzed successfully in batches
Total analysis time: ~60-90 seconds for 22-page deck
Per-batch time: ~12-18 seconds (5 slides)
Zero timeouts with batch size of 5
Comprehensive data extraction: 9 team members, 23 traction metrics from RubyData deck
Testing
Test Case: RubyData Pitch Deck
Format: Docsend export (image-based PDF)
Pages: 22 slides
File size: 2.2 MB
Text extraction: Only 386 characters (page headers)
Batch processing: 5 batches (5+5+5+5+2 slides)
Results:
Batch 1 (slides 1-5): 4 traction metrics
Batch 2 (slides 6-10): 2 team members, 6 traction metrics
Batch 3 (slides 11-15): 7 team members, 5 traction metrics
Batch 4 (slides 16-20): 6 traction metrics
Batch 5 (slides 21-22): 2 traction metrics
Merged total: 9 team members, 23 traction metrics
Artifacts saved: ✓ 0-deck-analysis.json, ✓ 0-deck-analysis.md
Files Changed
Core Agents
src/agents/deck_analyst.py- Batch processing implementationsrc/agents/socials_enrichment.py- Team data extraction fix
Documentation
.claude/settings.local.json- Tool permissions (minor)CASUAL_USER_GUIDE.md- Updated deck support notescontext-vigilance/Improving-Memo-Output.md- Context updatescontext-vigilance/Format-Memo-According-to-Template-Input.md- NEW file
New Assets
templates/brand-configs/brand-avalanche-config.yaml- Avalanche brandingtemplates/fonts/Bison/- Bison font family for brandingdata/RubyData.json- RubyData company configdata/WorkBack.json- WorkBack company config
Usage Examples
Image-Based PDF (Automatic Detection)
# PDF with minimal extractable text - automatically uses vision API
python -m src.main "RubyData"Console Output:
Extracting text from PDF deck (deckFor__RubyData.pdf)...
Extracted 386 characters from deck
⚠️ Minimal text extracted - PDF appears to be image-based
Using Claude's PDF vision to analyze deck...
Extracting images from PDF...
Processing slides 1-5 (batch 1/5)...
Sending batch 1 (5 slides) to Claude...
✓ Batch 1 complete, parsing results...
✓ Batch 1: Parsed JSON from code block
✓ Batch 1: Found 0 team members, 4 traction metrics
Processing slides 6-10 (batch 2/5)...
...
Merging 5 batch analyses...
✓ Merged analysis complete: 22 pages analyzed
✓ Final totals: 9 team members, 23 traction metricsMigration Notes
For Future Development
Batch size of 5 is optimal for API limits and response times
Can adjust batch_size constant if API limits change
Merge logic handles missing/incomplete data gracefully
Vision API path now has feature parity with text-based path
API Considerations
Vision API calls are more expensive than text processing
Each batch costs ~1 image API call worth of tokens
22-page deck = 5 API calls (vs 1 call for text-based)
Consider cost when processing large decks
Known Issues
Image Quality
0.5x scale rendering may reduce readability of very small text
JPEG compression (quality=85) balances size vs clarity
Very complex slides may need manual review
Batch Size Limitation
Maximum tested batch size: 5 slides
Larger batches may exceed API payload limits
Smaller batches increase API calls and cost
Text Detection Threshold
1000 character threshold works for most decks
Edge case: Very sparse text-based decks may trigger vision API unnecessarily
Can adjust threshold in
deck_analyst_agent()function
Future Enhancements
Potential Improvements
Adaptive batch size based on slide complexity/size
OCR fallback for hybrid text+image PDFs
Parallel batch processing (currently sequential)
Configurable image quality and scale factors
Slide thumbnail generation for review
Page range selection for partial deck analysis
Contributors
Batch processing architecture and implementation
PyMuPDF and PIL integration
Intelligent merge algorithm
Error handling and progress logging
Socials enrichment team data extraction fix
References
Issue: "Fifth fucking deck" unable to analyze (Docsend exports)
Issue: Deck analysis artifacts not saved for vision-based analysis
Issue: Team member extraction failing for structured founder data
Anthropic Vision API documentation
PyMuPDF documentation for image rendering