4.9 KiB
4.9 KiB
Session — 2026-06-20 (OCR Sidecar Refactor Phase 6-9 Implementation)
Summary
Implemented Phases 6, 8, and 9 of the OCR Sidecar Refactor (Speckit-140) following ADR-040. Phase 6 refactored the sidecar to async I/O with lifespan context manager. Phase 8 removed the unused /normalize endpoint. Phase 9 polished Dockerfile, docker-compose.yml, created README.md, and validated quickstart.md. All 19 Python tests pass.
ปัญหาที่พบ (Root Cause)
ไม่มี bug ใน session นี้ — เป็นการ implement feature ใหม่ตาม ADR-040:
- Sync I/O bottleneck:
process_ocrใช้httpx.Clientแบบ sync ทำให้ block FastAPI event loop - Stale startup pattern:
@app.on_event("startup")deprecate แล้วใน FastAPI 0.111+ - Unused /normalize endpoint: ไม่มี consumers ใน backend codebase
- Stale Docker config:
OCR_LANG,USE_GPUเป็น Tesseract config ที่ไม่ใช้แล้ว - Missing curl: Dockerfile ไม่มี
curlทำให้ HEALTHCHECK ล้มเหลว
การแก้ไข (Fix)
Phase 6: Async I/O Performance (T041-T046)
| ไฟล์ | การเปลี่ยนแปลง |
|---|---|
specs/04-Infrastructure-OPS/.../ocr-sidecar/app.py |
process_ocr → async def, _process_pdf_doc → async def, /ocr + /ocr-upload → async def |
app.py |
เพิ่ม ollama_client global (httpx.AsyncClient) สร้างใน lifespan context manager |
app.py |
แทน @app.on_event("startup") ด้วย @asynccontextmanager lifespan |
app.py |
Model loading ผ่าน asyncio.to_thread(_load_bge_models) |
app.py |
แทน httpx.Client ด้วย await client.post() (AsyncClient) |
tests/integration/ocr-sidecar/test_async_performance.py |
New file: 6 tests (coroutine check, lifespan, ollama_client global, /normalize removed, concurrent requests) |
tests/unit/ocr-sidecar/test_residency_wiring.py |
Updated: FakeClient → FakeAsyncClient, sync → asyncio.run() |
tests/integration/ocr-sidecar/test_parameter_governance.py |
Updated: async mock, patch ollama_client |
tests/integration/ocr-sidecar/test_active_prompt.py |
Updated: async mock, patch ollama_client |
Phase 8: Remove /normalize (T054-T055)
| ไฟล์ | การเปลี่ยนแปลง |
|---|---|
app.py |
ลบ NormalizeRequest, NormalizeResponse, /normalize endpoint, pythainlp imports |
requirements.txt |
ลบ pythainlp==5.0.4 และ Pillow==10.0.0 |
| (grep verified) | ไม่มี /normalize หรือ THAI_PREPROCESS_URL consumers ใน backend |
Phase 9: Polish (T056-T063)
| ไฟล์ | การเปลี่ยนแปลง |
|---|---|
Dockerfile |
เพิ่ม curl สำหรับ HEALTHCHECK, change log entry |
docker-compose.yml |
ลบ OCR_LANG, USE_GPU; เพิ่ม OCR_SIDECAR_API_KEY, OCR_ACTIVE_PROFILE |
README.md |
New file: architecture, endpoints, env vars, deploy guide, test coverage |
quickstart.md |
แก้ stale requirements (ลบ pythainlp/Pillow), แก้ TYPHOON_OCR_MODEL → OCR_MODEL |
tasks.md |
Mark T041-T046, T054-T063 as [x] |
กฎที่ Lock แล้ว
- Async I/O pattern:
process_ocrต้องเป็นasync defและใช้httpx.AsyncClientผ่านollama_clientglobal (สร้างใน lifespan) - Lifespan over startup event: ใช้
@asynccontextmanager lifespanแทน@app.on_event("startup")— deprecate แล้วใน FastAPI 0.111+ - Model loading non-blocking: ใช้
asyncio.to_thread()สำหรับ model loading ใน lifespan เพื่อไม่ block startup - No /normalize: endpoint ถูกลบแล้ว — ไม่มี consumers ใน backend
- Test mock pattern: ใช้
FakeAsyncClient(asyncpost()+aclose()) แทนFakeClient(sync) สำหรับทุก test ที่ mock Ollama API
Verification
python -m pytest tests/ -v— 19/19 tests passed in 4.81stest_process_ocr_is_coroutine_function— process_ocr เป็น async ✅test_process_pdf_doc_is_coroutine_function— _process_pdf_doc เป็น async ✅test_app_uses_lifespan_not_startup_event— ใช้ lifespan ไม่ใช่ on_event ✅test_app_has_async_client_global— ollama_client global มีอยู่ ✅test_normalize_endpoint_removed— /normalize ถูกลบแล้ว ✅test_concurrent_ocr_requests_dont_block— 3 concurrent requests สำเร็จ ✅- Existing tests (path traversal, API key, residency, CPU fallback, parameter governance, active prompt) — ทั้งหมดผ่าน ✅