CoolFace
Apppublic

f007kht/osprey-experimental

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
App README

Module 1 - Osprey Backend

A Streamlit web application for processing documents using the Docling library. Upload documents (PDF, DOCX, PPTX, XLSX, images, audio) and extract their content as Markdown.

Features

  • โ€”๐Ÿ“„ Document upload via web interface
  • โ€”๐Ÿ”„ Automatic document processing with Docling
  • โ€”๐Ÿ“ Markdown output display
  • โ€”๐ŸŽฏ Support for multiple formats: PDF, DOCX, PPTX, XLSX, HTML, images (PNG, JPG, TIFF), and audio (WAV, MP3)

Quick Start

Local Development

  1. 1.Install dependencies:
bash
   pip install -r requirements.txt
  1. 1.Run the Streamlit app:
bash
   streamlit run app.py
   # or (new modular entrypoint)
   streamlit run app/main.py
  1. 1.Open your browser: The app will automatically open at http://localhost:8501

Restart Procedure

Knowing when and how to restart the application is essential for applying changes and troubleshooting issues.

When to Restart

You should restart the application in the following scenarios:

  • โ€”After modifying code: Any changes to app.py or other Python files require a restart to take effect
  • โ€”After changing environment variables: Both locally and on Hugging Face Spaces, environment variable changes only apply after restart
  • โ€”After installing or updating dependencies: New packages from requirements.txt won't be available until restart
  • โ€”When the app becomes unresponsive: If the app freezes, crashes, or shows persistent errors, restart may resolve issues
  • โ€”After configuration changes: Changes to .streamlit/config.toml require a restart
  • โ€”When Streamlit cache needs clearing: Cache corruption or stale data may require restart after clearing cache
Local Development Restart

To restart the Streamlit app during local development:

  1. 1.Stop the current process:
  2. 2.Press Ctrl+C in the terminal where Streamlit is running
  3. 3.Or terminate the process using Task Manager (Windows) / Activity Monitor (Mac)
  1. 1.Optional - Clear Streamlit cache (if experiencing cache-related issues):
bash
   # Windows PowerShell
   Remove-Item -Path "$env:USERPROFILE\.streamlit\cache" -Recurse -Force
   
   # Linux/Mac
   rm -rf ~/.streamlit/cache
  1. 1.Restart the app:
bash
   # Standard method
   streamlit run app.py
   
   # Or use the startup script (Windows PowerShell)
   .\start_streamlit.ps1
  1. 1.Verify the restart:
  2. 2.Check the terminal for startup messages (should show "You can now view your Streamlit app in your browser")
  3. 3.Open or refresh your browser at http://localhost:8501
  4. 4.Verify the app loads correctly and any code changes are reflected

Note: If you see "Address already in use" errors, ensure all previous Streamlit processes are terminated before restarting.

Hugging Face Spaces Restart

To restart the application on Hugging Face Spaces:

  1. 1.Via UI (Manual Restart):
  2. 2.Navigate to your Space: https://huggingface.co/spaces/f007kht/osprey-experimental
  3. 3.Click the Settings tab (gear icon in the top right)
  4. 4.Scroll to the bottom and click Restart this Space
  5. 5.Wait for the Space to rebuild (typically 1-3 minutes)
  1. 1.Automatic Restart:
  2. 2.Occurs automatically after code is pushed to the main branch on GitHub
  3. 3.The GitHub Actions workflow syncs changes and triggers a rebuild
  4. 4.No manual intervention needed for code updates
  1. 1.After Environment Variable Changes:
  2. 2.Always restart after adding or modifying environment variables
  3. 3.Changes in the Settings โ†’ Variables and secrets section require restart to apply
  4. 4.Verify changes by checking the app sidebar or logs after restart
  1. 1.Verify the restart:
  2. 2.Check the Space Logs tab for startup messages
  3. 3.Verify the app loads correctly at the Space URL
  4. 4.Confirm environment variable changes are reflected (e.g., MongoDB configuration appears in sidebar)

Important: Hugging Face Spaces may take 1-3 minutes to fully restart and rebuild. Check the logs to ensure the restart completed successfully.

Troubleshooting Restart Issues

Port Already in Use (Local Development):

bash
# Windows PowerShell - Kill existing Streamlit processes
Get-Process python,streamlit -ErrorAction SilentlyContinue | Stop-Process -Force

# Linux/Mac - Kill processes on port 8501
lsof -ti:8501 | xargs kill -9

Cache Issues:

  • โ€”Clear the Streamlit cache directory (see step 2 in Local Development Restart above)
  • โ€”Clear browser cache if UI issues persist
  • โ€”Restart after clearing cache

Environment Variables Not Applied:

  • โ€”Local: Verify environment variables are set correctly in your terminal or start_streamlit.ps1 script
  • โ€”Hugging Face Spaces: Confirm variables are saved in Settings โ†’ Variables and secrets, then restart
  • โ€”Check that variable names match exactly (case-sensitive)

App Still Shows Old Behavior After Restart:

  • โ€”Hard refresh browser: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac)
  • โ€”Clear browser cache completely
  • โ€”Try incognito/private browsing mode to rule out browser cache issues

Deployment

The app is deployed on Hugging Face Spaces: https://huggingface.co/spaces/f007kht/osprey-experimental

Key deployment notes:

  • โ€”Uses Docker + Streamlit on Hugging Face Spaces
  • โ€”16GB RAM available (vs 1GB on Streamlit Cloud free tier)
  • โ€”Uses requirements.txt for dependency management
  • โ€”Configured with CPU-only PyTorch builds for cloud compatibility
  • โ€”System dependencies (Tesseract OCR, libGL) installed via Dockerfile
  • โ€”Explicitly configured for CPU-only operation - prevents GPU detection issues that can crash Streamlit

Previous deployment attempts on Streamlit Cloud: The deployment process and troubleshooting steps for Streamlit Cloud are documented in deployment-logs/streamlit-cloud-deployment-log.txt. Memory limitations (1GB RAM) on Streamlit Cloud's free tier led to migration to Hugging Face Spaces.

Why Hugging Face Spaces:

The project was moved to Hugging Face Spaces for three primary reasons:

  1. 1.File Size Limitations: Docling's required model files and dependencies exceed Streamlit Cloud's deployment specifications. Hugging Face Spaces supports Docker deployments with larger storage capacities, allowing all necessary models and dependencies to be included.
  1. 1.GPU/Compute Detection Issues: The platform provides reliable CPU-only compute environments, and the app is explicitly configured to use CPU mode (AcceleratorDevice.CPU) to ensure stable operation without GPU driver dependencies or detection failures.
  1. 1.Memory Resources: Hugging Face Spaces provides 16GB RAM (vs 1GB on Streamlit Cloud free tier), which is essential for processing large documents with Docling's ML models.

Configuring Environment Variables

To enable MongoDB storage and other optional features in your Hugging Face Space deployment, you need to configure environment variables:

  1. 1.Navigate to your Space settings:
  2. 2.Go to https://huggingface.co/spaces/f007kht/osprey-experimental
  3. 3.Click the Settings tab (gear icon in the top right)
  1. 1.Add environment variables:
  2. 2.Scroll to Variables and secrets section
  3. 3.Click New variable or New secret (use secrets for sensitive data like connection strings)
  1. 1.Required variables for MongoDB:
Variable NameValueDescription
ENABLE_MONGODBtrueEnables MongoDB storage features
MONGODB_CONNECTION_STRINGmongodb+srv://...Your MongoDB Atlas connection string (mark as secret)
  1. 1.Optional variables:
Variable NameDefaultDescription
EMBEDDING_MODELsentence-transformers/all-MiniLM-L6-v2Local embedding model for RAG
ENABLE_DOWNLOADStrueEnable download buttons for processed documents
USE_REMOTE_EMBEDDINGSfalseUse VoyageAI instead of local embeddings
VOYAGEAI_API_KEY-Required if USE_REMOTE_EMBEDDINGS=true
  1. 1.After adding variables:
  2. 2.Click Save to apply changes
  3. 3.Restart your Space: Go to Settings โ†’ Restart this Space (see Restart Procedure section for detailed instructions)

Important Security Notes:

  • โ€”Use Secrets (not Variables) for sensitive data like MongoDB connection strings and API keys
  • โ€”Secrets are hidden in logs but visible in Space settings UI
  • โ€”Never commit secrets to GitHub - use Hugging Face Spaces secrets instead

Verification: After restarting, the sidebar should show MongoDB configuration options instead of "MongoDB features are disabled" message.

Viewing Documents with MongoDB Compass

MongoDB Compass is a GUI tool that makes it easy to view, query, and analyze your stored documents. It's particularly useful for:

  • โ€”๐ŸŽจ Better visualization - See documents in a user-friendly interface
  • โ€”๐Ÿ”Ž Advanced filtering - Query documents with complex filters
  • โ€”๐Ÿ“Š Schema analysis - Understand your data structure
  • โ€”๐Ÿ“ˆ Query performance insights - Optimize your queries
Step 1: Install MongoDB Compass
  1. 1.Download MongoDB Compass from: https://www.mongodb.com/try/download/compass
  2. 2.Install the application (available for Windows, macOS, and Linux)
Step 2: Connect to MongoDB Atlas
  1. 1.Open MongoDB Compass
  2. 2.Click "New Connection"
  3. 3.Paste your connection string:
   mongodb+srv://<username>:<password>@cluster.xxxxx.mongodb.net/?retryWrites=true&w=majority
  • โ€”Replace <username> and <password> with your MongoDB Atlas credentials
  • โ€”Replace cluster.xxxxx.mongodb.net with your actual cluster address
  • โ€”Click "Connect"
Step 3: Browse Your Data

Once connected:

  1. 1.Expand your database - You'll see a list of databases in the left sidebar
  2. 2.Click on your collection - Navigate to your documents collection (default: documents)
  3. 3.View documents - See all your stored documents in a nice GUI with:
  4. 4.Document viewer with syntax highlighting
  5. 5.Search and filter capabilities
  6. 6.Export functionality for analysis

Tip: The vector search index is created automatically when documents are saved. It may take a few minutes to be ready for queries. You can verify index creation in the MongoDB Atlas UI under "Search" โ†’ "Vector Search Indexes".

Project Structure

.
โ”œโ”€โ”€ app.py                              # Main Streamlit application
โ”œโ”€โ”€ Dockerfile                          # Docker configuration for HF Spaces
โ”œโ”€โ”€ requirements.txt                    # Python dependencies
โ”œโ”€โ”€ packages.txt                        # System dependencies (for reference)
โ”œโ”€โ”€ deployment-logs/                    # Deployment logs and troubleshooting
โ”‚   โ””โ”€โ”€ streamlit-cloud-deployment-log.txt
โ””โ”€โ”€ README.md                           # This file

Requirements

  • โ€”Python 3.9+
  • โ€”Streamlit 1.28.0+
  • โ€”Docling 2.60.0+
  • โ€”PyTorch (CPU-only version for cloud deployment)

Troubleshooting

Deployment Issues

If you encounter deployment issues on Streamlit Cloud, refer to deployment-logs/streamlit-cloud-deployment-log.txt for the complete deployment history and resolution steps.

Common fixes applied:

  1. 1.Removed pyproject.toml with TOML parse errors
  2. 2.Removed uv.lock to force use of requirements.txt
  3. 3.Configured CPU-only PyTorch installation for cloud compatibility

Local Issues

  • โ€”First run is slow: Docling downloads AI models on first use, which may take several minutes
  • โ€”Memory requirements: Processing large documents may require significant memory
  • โ€”File upload limits: Check Streamlit Cloud limits for file upload sizes
  • โ€”App not reflecting changes: If code changes aren't appearing, try restarting the app (see Restart Procedure)

Known Issues and Fixes

Issue: `DoclingPdfParser.load() got an unexpected keyword argument 'password'`

  • โ€”Symptoms: Error occurs when processing PDF documents (especially password-protected ones)
  • โ€”Cause: Version mismatch between Docling code and installed docling-parse library - some versions don't support the password parameter in DoclingPdfParser.load()
  • โ€”Fix Applied: Modified docling/backend/docling_parse_v4_backend.py to handle API compatibility:
  • โ€”Tries loading with password parameter first (for newer versions)
  • โ€”Falls back to loading without password if TypeError occurs (since pypdfium2 already handles password protection)
  • โ€”Status: Fixed in commit 97f3fb0

Issue: Streamlit fails to start due to GPU/compute detection issues

  • โ€”Symptoms: Streamlit app doesn't start or crashes during initialization on Hugging Face Spaces
  • โ€”Cause: Docling defaults to AcceleratorDevice.AUTO which attempts GPU/CUDA/MPS detection, causing issues on CPU-only platforms like Hugging Face Spaces
  • โ€”Fix Applied: Explicitly set pipeline_options.accelerator_options.device = AcceleratorDevice.CPU in app.py
  • โ€”Why: This was a key reason for moving to Hugging Face Spaces - the platform provides CPU-only compute, and forcing CPU mode ensures reliable operation without GPU driver dependencies
  • โ€”Status: Fixed - app now explicitly uses CPU device

Remote Synchronization

This project maintains synchronized repositories on GitHub and Hugging Face Spaces:

  • โ€”GitHub Repository: https://github.com/f007kht/osprey-experimental (source of truth)
  • โ€”Hugging Face Space: https://huggingface.co/spaces/f007kht/osprey-experimental (automatically synced)

Synchronization Strategy

  • โ€”Single Source of Truth: GitHub (origin/main) is the authoritative source for all code changes
  • โ€”Automated Sync: A GitHub Actions workflow (.github/workflows/sync-to-hf.yml) automatically syncs the main branch to Hugging Face Spaces on every push
  • โ€”No Direct Edits: Never make direct edits to the Hugging Face Space UI - all changes must flow through GitHub first
  • โ€”Prevention of Divergence: The automated sync ensures both remotes remain perfectly synchronized

How It Works

  1. 1.All development happens on GitHub (feature branches, pull requests, merges to main)
  2. 2.When code is pushed to the main branch on GitHub, the sync workflow automatically triggers
  3. 3.The workflow force-pushes main to the Hugging Face Space remote, keeping them in sync
  4. 4.Hugging Face Spaces automatically rebuilds the Docker image when changes are detected

Git LFS Requirement

Large test data files (.pages.json files > 10MB) are tracked using Git LFS. This is required because Hugging Face Spaces has a 10MB file size limit without LFS. The .gitattributes file configures which files use LFS.

Manual Sync (if needed)

If you need to manually sync (not recommended, as automated sync should handle this):

bash
git push huggingface main --force

Note: Manual sync should only be done in emergencies. The automated workflow handles all normal synchronization.

Quality Assurance & Testing

Running Tests

The project includes comprehensive quality gates and testing infrastructure:

Unit Tests

Run the quality gates unit tests:

bash
make test
# or
pytest -q tests/test_quality_gates.py
Smoke Tests

Run smoke tests on sample files to verify conversion pipeline:

bash
make smoke
# or
python scripts/smoke_run.py

Note: Add sample files to smoke/ directory first:

  • โ€”sample.pdf - PDF with text layer
  • โ€”scan_cover.pdf - Scanned PDF (image only)
  • โ€”sample.pptx - PowerPoint with WMF image placeholder
  • โ€”sample.xlsx - Excel file with table
Backfill Script

Backfill existing MongoDB documents with missing quality metrics:

bash
make backfill
# or
MONGODB_CONNECTION_STRING='mongodb+srv://...' python scripts/backfill_min_metrics.py

This adds minimal default values for missing fields:

  • โ€”input, metrics, warnings, ocr, status
  • โ€”text_layer_detected, rasterized_graphics_skipped
  • โ€”schema_version=1 (for existing docs)

QA Dashboard

Access the QA Dashboard in Streamlit:

  1. 1.Start the Streamlit app: streamlit run app.py
  2. 2.Navigate to the "QA Dashboard" page in the sidebar
  3. 3.View quality metrics, statistics, and suspect documents

The dashboard shows:

  • โ€”Overview: Total documents, format counts, quality bucket distribution
  • โ€”Format Breakdown: Documents by input format
  • โ€”Processing Time Statistics: Average/min/max processing times by format
  • โ€”Suspect Documents: Filterable table of documents with quality issues

Quality Notes: Documents with status.notes containing OSD_FAILS_ON_TEXTLAYER indicate PDFs with text layers that still triggered OSD errors (typically on page 0 cover tiles). These documents maintain quality_bucket=ok but can be filtered in the dashboard for review.

Note: The dashboard requires MongoDB to be enabled and configured. It fails gracefully if MongoDB is unavailable.

Normalized Warning Codes

The system logs normalized warning codes for easier monitoring:

CodeMeaning
FORMAT_CONFLICTMagic bytes and file extension conflict
WMF_LOADER_MISSINGWMF/EMF graphics cannot be loaded (PPTX)
OSD_FAILOrientation and script detection failed (PDF)
OSD_FAIL_COLLAPSEDOSD failures suppressed after first failure on page 0 (text layer detected)
SHORT_MDMarkdown output is very short (< 500 chars)
OVERSIZEMarkdown output exceeds 2M characters (runaway duplication)

Feature Flags & Guardrails

Control QA features and guardrails via environment variables:

VariableDefaultDescription
QA_FLAG_ENABLE_PDF_WARNING_SUPPRESS1Suppress PDF warnings for non-PDF files
QA_FLAG_ENABLE_TEXT_LAYER_DETECT1Enable PDF text layer detection
QA_FLAG_LOG_NORMALIZED_CODES1Log normalized warning codes
QA_SCHEMA_VERSION2Schema version for stored documents
QA_MAX_PAGES500Maximum pages per document (aborts if exceeded)
QA_MAX_SECONDS300Maximum processing time in seconds (aborts if exceeded)

Guardrails: Documents exceeding QA_MAX_PAGES or QA_MAX_SECONDS are gracefully aborted with status.abort.reason set to MAX_PAGES or MAX_SECONDS. The quality bucket is set to suspect and processing stops to prevent resource exhaustion.

PDF Noise Suppression: For non-PDF files (PPTX, XLSX, DOCX), PDF library probes can emit false warnings ("invalid pdf header: b'PK...", "EOF marker not found"). These are automatically suppressed at the logger level when QA_FLAG_ENABLE_PDF_WARNING_SUPPRESS=1 (default).

  • โ€”Pre-sniff on raw bytes (_looks_like_office_zip()) happens before any Docling format detection/probe
  • โ€”Suppression wraps the entire conversion process, including Docling's format detection step
  • โ€”Filters are attached to both PDF-related loggers (pdfminer, pypdf, docling) and root logger for comprehensive coverage
  • โ€”Format detection via magic bytes happens first, ensuring PDF code paths are not triggered for Office documents

OSD Collapse for Text-Layer PDFs: When a PDF has a text layer detected (text_layer_detected=True), OCR and OSD are disabled at the source (pipeline options). If OSD errors still occur (e.g., on page 0 cover tiles), they are collapsed using per-document filtering:

  • โ€”OSD/OCR disabled at pipeline level: ocr="none", osd=False, tesseract_osd=False
  • โ€”Per-document OSD filter matches by temp filename for interleaving safety (handles concurrent conversions)
  • โ€”All OSD failures are counted in warnings.osd_fail_count (MetricsLogHandler tracks all)
  • โ€”Only the first OSD error per document is logged
  • โ€”Subsequent OSD errors for the same document are suppressed (QA log shows osd_collapsed=1)
  • โ€”A single summary warning is emitted: OSD_FAIL_COLLAPSED
  • โ€”Quality bucket remains ok but status.notes includes both OSD_FAILS_ON_TEXTLAYER and OSD_COLLAPSED for dashboard filtering

Correlation IDs

Every document conversion generates correlation IDs for log โ†” Mongo joinability:

  • โ€”`run_id`: Unique UUID per conversion run (included in all QA log lines)
  • โ€”`content_hash`: SHA256 hash of file content (for idempotent upserts)

Pivoting from QA logs to MongoDB:

  1. 1.Extract run_id or content_hash (first 8 chars) from QA log line
  2. 2.Query MongoDB: db.documents.find({"run_id": "..."}) or db.documents.find({"content_hash": /^.../})
  3. 3.View full document metrics, warnings, and status

Example QA log line:

QA: format=PDF pages=5 md=1234 osd_fails=0 wmf_skipped=0 tlayer=true osd_collapsed=0 bucket=ok sec=2.45 run_id=abc12345 hash=def67890

OSD Collapse Example (text layer detected, OSD errors suppressed):

QA: format=PDF pages=101 md=218378 osd_fails=49 wmf_skipped=0 tlayer=True osd_collapsed=1 bucket=ok sec=155.94 run_id=... hash=...
PDF: OSD suppressed after first failure on page 0 (OSD_FAIL_COLLAPSED) run_id=... hash=...

Idempotent Storage

Documents are stored using idempotent upsert by content_hash + filename. Reprocessing the same file (same SHA256) updates the existing document instead of creating duplicates. This prevents duplicate storage when files are reprocessed.

MongoDB Indexes

Create indexes for observability:

bash
mongosh <connection_string> < db/indexes.js

Indexes created:

  • โ€”input.format - Format-based queries
  • โ€”status.quality_bucket - Quality bucket filtering
  • โ€”warnings.osd_fail_count - OSD failure tracking
  • โ€”metrics.process_seconds - Processing time analysis
  • โ€”metrics.page_count - Page count queries
  • โ€”metrics.markdown_length - Markdown size analysis

Aggregation Pipelines

Pre-built aggregation pipelines are available in db/aggregations/quality_dashboards.json:

  • โ€”by_format_error_rates: Error/warning rates by input format
  • โ€”suspect_docs_sample: Sample suspect documents
  • โ€”throughput_stats: p50/p90 processing times by format
  • โ€”markdown_density: Markdown per page percentiles by format

Alerting System

Monitor quality metrics and receive push alerts when thresholds are breached:

bash
make alerts
# or
python scripts/alerts_watch.py

Alert Configuration (environment variables):

VariableDefaultDescription
ALERT_INTERVAL_SECONDS300Check interval in seconds (5 minutes)
ALERT_SUSPECT_RATE0.2Suspect rate threshold (20% by format)
ALERT_OSD_SPIKE_MULT3.0OSD spike multiplier (1h mean > 24h mean ร— N)
ALERT_MD_P10_DROP100Markdown density drop threshold (chars/page)
ALERT_WEBHOOK_URL-Webhook URL for POST JSON alerts (e.g., Slack)
ALERT_EMAIL_TO-Email address for SMTP alerts
ALERT_SMTP_HOST-SMTP server hostname
ALERT_SMTP_PORT587SMTP server port
ALERT_SMTP_USER-SMTP username
ALERT_SMTP_PASS-SMTP password

Alert Checks:

  • โ€”Suspect Rate: Format-specific suspect rate > threshold (last 24h)
  • โ€”OSD Spike: Mean OSD failures in last 1h > 24h mean ร— multiplier
  • โ€”Markdown Density Drop: p10 markdown/page in last 1h < (24h p10 - threshold)

Alerts include top 5 offending documents with run_id and filename for triage.

Example Webhook Alert (Slack):

json
{
  "timestamp": "2025-11-06T14:30:00",
  "breaches": [
    {
      "check": "suspect_rate",
      "threshold": 0.2,
      "breaches": [
        {"format": "pdf", "rate": 0.35, "suspect": 7, "total": 20}
      ]
    }
  ],
  "top_offenders": [
    {"run_id": "abc12345", "filename": "problem.pdf", "format": "pdf", "bucket": "suspect"}
  ]
}

Secret Scrubbing

All log messages are automatically scrubbed to remove secrets:

  • โ€”MongoDB connection strings: mongodb+srv://***:***@host
  • โ€”File system paths with credentials
  • โ€”Any URI patterns with embedded credentials

This prevents accidental credential exposure in logs.

License

Confidential - OspreyIntelLLC