Field Media Ingest — Content Classifier (TB-26, 2026-07-29)
Category-less uploads through /field-media/ingest and /upload used to
default silently to PHOTOS, so pier logs sent without a category cue never
landed in PIERLOGS and task_parse_pierlog_vision OCR never fired (the
trigger was the label, not the content). Bit us 2026-07-29: Darius’s pier
logs rode a general upload and Dustin re-classified them by hand.
Shipped in entirely-api a0228fc:
classify_document_type(path)inservices/field_photo_analyzer.py— one narrow Vision call (config.MODEL_VISION_OCR, same client as the SOW proposal path): PIERLOG / PHOTO / PERMIT / PLAN / OTHER + confidence. PDFs classify page 1 only. Never raises — any failure returns PHOTO/low so filing behaves exactly as before.- Sentinel contract:
categorydefault on/ingest+/uploadis now empty = “caller didn’t choose” → defaults to PHOTOS but the classifier may re-route. An explicit category from any caller always wins — Kilroy’s verbal-cuecategory=PIERLOGSpath is untouched. - Only promotes INTO PIERLOG (medium+ confidence), re-filed before
save_fileso the existing OCR-queue block fires naturally. PERMIT/PLAN/OTHER verdicts are logged for tuning, never auto-filed (v1). - Burst guard:
field_media:classify_tries:<project>— max 8 Vision classify calls / project / 10 min; past the cap, spillover routes to PIERLOG by inference only if the burst already detected one (field_media:classified_pierlog:<project>). Fails open on Redis errors. - Response surfaces
auto_classified+classifier_confidenceso Kilroy can say “filed 6 to pier logs (auto-detected)“. Dedup guards unchanged. - Side fix: PyMuPDF 1.28
Pixmap.tobytes(jpg_quality=)kwarg — the oldjpeg_quality=crashed the PDF rasterize path (same latent bug fixed inpermit_parser._pdf_to_image_blocks).
Verified live in-container against real files: pier-log JPG → PIERLOG/high,
pier-log PDF → PIERLOG/high, site photo → PHOTO/high. 19 tests in
tests/test_tb26_ingest_classifier.py. /ingest-batch deliberately not
wired in v1 (doesn’t share _file_and_register).
Deploy pending: container rebuild/restart left deliberate per brief.
Related: field-media-dedup-transparency, field-photo-analyzer, pier-log-ocr