Backend → doc-intel migration audit¶
Date: 2026-05-19
Author: investigation triggered by autofill silent-failure debugging
Scope: backend code performing document/PDF/OCR work that should live in kwiloai_doc_intelligence
The autofill bug surfaced a broader pattern: backend code calling phantom or
local doc-handling paths despite an explicit migration of doc responsibility
to the doc-intelligence microservice ("we had memory issue in converting
the pdf to images etc so we moved the complete responsibility to doc
intelligence"). This document lists the call sites still on the wrong side.
Tier 1 — Silently broken in production¶
These call sites depend on a /ocr/extract-document-text endpoint that has
never existed on doc-intel. They have been silently returning empty data
for ~3 months (since commit b538c09e, 2026-02-24). Every caller masks the
404 with a fallback path so individual features look "fine but degraded".
| File | Sites | Feature impact |
|---|---|---|
services/attachment_context.py |
L132, L161, L265 | AI Tutor uploaded-file context — silently runs without OCR text |
services/document_context.py |
L221 | AI content generation from Google Drive / uploads — same |
api/v1/homework.py |
4 call sites | Homework OCR enhancement — same |
api/v1/admin_analytics.py |
4 call sites | Admin analytics on documents — same |
Action: add a /ocr/extract-text-from-document (or equivalent) endpoint
to doc-intel that takes a signed URL or base64 PDF and returns plain text +
page count. Rewrite DocIntelligenceClient.extract_document_text to call
it. Without this, the migration is incomplete and 11+ call sites stay
degraded.
Tier 2 — Backend renders PDFs locally (direct violation of migration goal)¶
PyMuPDF (fitz) is doing PDF-to-image conversion on the backend in
multiple places. This is the workload the migration explicitly targeted.
| File | Sites | What it does |
|---|---|---|
services/answer_sheet_service.py |
L425, L437–440, L452–457 | Renders student answer-sheet PDFs to PNG pages before OCR |
api/v1/physical_exam.py |
L1353–1354 | Renders an uploaded PDF page (2× zoom) inline |
services/pdf_crop.py |
L55, L69–70 | Crops a sub-region of a PDF page to PNG using clip= |
Action: move rasterization to doc-intel. answer_sheet_service is the
biggest win — it's the same memory pattern that drove the migration.
pdf_crop is small and tightly scoped (one rect → PNG) but still backend
RAM at upload time. Either lift the whole flow into doc-intel or expose a
/ocr/crop-pdf-region helper there.
Tier 3 — Duplicate of a doc-intel capability¶
| File | Backend purpose | doc-intel equivalent |
|---|---|---|
services/ocr_service.py |
Gemini Vision + Google Cloud Vision OCR for submissions | /api/v1/ocr/extract |
services/pdf_export.py |
Assignment PDF generation | /api/v1/export/pdf/assignment, /api/v1/export/docx, /api/v1/export/pptx, /api/v1/export/pdf/analytics, /api/v1/export/excel/analytics (all already live) |
Action: drop the backend implementations, route all calls through the
existing doc-intel endpoints. pdf_export.py in particular is duplicated
work — doc-intel already exposes the export surface.
Tier 4 — Worth a closer look but not clearly mis-placed¶
| File | Notes |
|---|---|
services/document_extractor.py |
Routing-layer only by its docstring ("routing attachments to the appropriate processing path"). Probably stays — but worth confirming nothing leaked into it. |
services/platform_content_extractor.py |
"Extracting text content from platform resources" — could be OCR-flavored or pure DB lookups. Audit before migrating. |
services/insight_extractor.py |
LLM on doc content for AI-Tutor memory. Not strictly doc handling; the LLM call is about the conversation, not the doc. Keep on backend. |
Recommended order of operations¶
- Add the missing
extract-text-from-documentdoc-intel endpoint — unblocks Tier 1's ~11 silently-degraded call sites in one move. - Migrate
answer_sheet_servicerasterization — highest-value Tier 2 win, directly attacks the memory pressure the migration is meant to solve. - Retire
services/ocr_service.py— pure duplication. - Retire
services/pdf_export.py— pure duplication. - Migrate
pdf_crop+ the inlinefitzuse inphysical_exam.py— smaller scope, can be batched.
After (1) and (2), the backend should hold approximately zero fitz
imports outside of legitimate use cases (e.g. validating page count for an
upload). Tracking that count as a regression metric would surface future
drift early.
Notes for whoever picks this up¶
- Every fallback path on the backend currently swallows extraction failures
silently (
_empty_result(),pass,try/except: return None). When you retire each call site, also audit its caller — silent fallbacks hid this problem for 3 months and will hide the next one. - The
Doc intelligence extraction failed: DocIntelligenceErrorwarning previously logged only the exception class. PR MySetu-AI/kwiloai_webapp#933 fixed that for the metadata path; apply the same%s: %s, type, excpattern when you touch the other call sites.