feat(ai): add ADR-036 unified OCR architecture and frontend test coverage
- Add ADR-036 unified OCR architecture (typhoon-ocr via Ollama) - Extend AI execution profiles for OCR sandbox configuration - Add comprehensive frontend test coverage (components, hooks, services) - Add backend test coverage for document-numbering services - Update OCR sidecar with typhoon-ocr integration - Add AI policy service and execution profile management - Update AGENTS.md and architecture documentation
This commit is contained in:
@@ -26,34 +26,36 @@
|
||||
|
||||
> การตัดสินใจเหล่านี้ **ไม่สามารถเปลี่ยนแปลงได้** โดยไม่ได้รับ Explicit Approval
|
||||
|
||||
| ID | Decision | ADR |
|
||||
| --- | ------------------------------------------------------------------------------------------- | --------- |
|
||||
| D1 | n8n = Migration Phase orchestrator เท่านั้น — ห้ามทำ New Correspondence pipeline ผ่าน n8n | ADR-023A |
|
||||
| D2 | New Correspondence → BullMQ `ai-realtime` queue โดยตรง (ไม่ผ่าน n8n) | ADR-023A |
|
||||
| D3 | n8n ต้อง call `POST /api/ai/jobs` (DMS Backend) เท่านั้น — ห้าม call Ollama/Qdrant โดยตรง | ADR-023A |
|
||||
| D4 | Excel metadata ส่งไปพร้อม AI job เป็น context (docNumber, title, sender ฯลฯ) | Session 2 |
|
||||
| D5 | Tag suggestion ใช้ทาง C: แนะนำ existing tags + สร้างใหม่ได้ถ้าไม่มี (`isNew: true` flag) | Session 2 |
|
||||
| D6 | Editable Review Form: AI pre-fill → user approve/edit → submit (human-in-the-loop ทุกครั้ง) | ADR-023 |
|
||||
| D7 | UUID Strategy: `publicId` (UUIDv7) เท่านั้นสำหรับ Public API — INT PK ต้อง `@Exclude()` | ADR-019 |
|
||||
| D8 | Schema changes: แก้ SQL โดยตรง + เพิ่ม `deltas/*.sql` — ห้ามใช้ TypeORM migration files | ADR-009 |
|
||||
| D9 | Qdrant search ต้องส่ง `projectPublicId` เป็น mandatory parameter ทุกครั้ง (compile-time) | ADR-023A |
|
||||
| D10 | AI model stack: `typhoon2.5-np-dms:latest` (Main LLM) + `typhoon-np-dms-ocr:latest` (OCR, keep_alive:0) + `BGE-M3` (Dense 1024 + Sparse Embedding) + `BGE-Reranker-Large` (Reranker) on Admin Desktop — `nomic-embed-text` ถูกแทนที่แล้ว (ADR-034/035) | ADR-034/035 |
|
||||
| D11 | RAG Embedding trigger: `syncStatus()` → `enqueueRagPrepare()` เมื่อ status ≠ DRAFT; jobId = `rag-prepare:{documentPublicId}:{revisionNumber}` (BullMQ dedup); delete-before-upsert ทุกครั้ง | ADR-035 |
|
||||
| D12 | Qdrant collection `lcbp3_vectors` = Hybrid schema: `bge_dense` (1024 dims, Cosine) + `bge_sparse` (SPLADE); payload indexes: `project_public_id` (tenant), `doc_public_id`, `status_code`, `doc_type` | ADR-035 |
|
||||
| ID | Decision | ADR |
|
||||
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
|
||||
| D1 | n8n = Migration Phase orchestrator เท่านั้น — ห้ามทำ New Correspondence pipeline ผ่าน n8n | ADR-023A |
|
||||
| D2 | New Correspondence → BullMQ `ai-realtime` queue โดยตรง (ไม่ผ่าน n8n) | ADR-023A |
|
||||
| D3 | n8n ต้อง call `POST /api/ai/jobs` (DMS Backend) เท่านั้น — ห้าม call Ollama/Qdrant โดยตรง | ADR-023A |
|
||||
| D4 | Excel metadata ส่งไปพร้อม AI job เป็น context (docNumber, title, sender ฯลฯ) | Session 2 |
|
||||
| D5 | Tag suggestion ใช้ทาง C: แนะนำ existing tags + สร้างใหม่ได้ถ้าไม่มี (`isNew: true` flag) | Session 2 |
|
||||
| D6 | Editable Review Form: AI pre-fill → user approve/edit → submit (human-in-the-loop ทุกครั้ง) | ADR-023 |
|
||||
| D7 | UUID Strategy: `publicId` (UUIDv7) เท่านั้นสำหรับ Public API — INT PK ต้อง `@Exclude()` | ADR-019 |
|
||||
| D8 | Schema changes: แก้ SQL โดยตรง + เพิ่ม `deltas/*.sql` — ห้ามใช้ TypeORM migration files | ADR-009 |
|
||||
| D9 | Qdrant search ต้องส่ง `projectPublicId` เป็น mandatory parameter ทุกครั้ง (compile-time) | ADR-023A |
|
||||
| D10 | AI model stack: `np-dms-ai:latest` (Main LLM) + `np-dms-ocr:latest` (OCR, keep_alive:0) + `BGE-M3` (Dense 1024 + Sparse Embedding) + `BGE-Reranker-Large` (Reranker) on Admin Desktop — `nomic-embed-text` ถูกแทนที่แล้ว (ADR-034/035) | ADR-034/035 |
|
||||
| D11 | RAG Embedding trigger: `syncStatus()` → `enqueueRagPrepare()` เมื่อ status ≠ DRAFT; jobId = `rag-prepare:{documentPublicId}:{revisionNumber}` (BullMQ dedup); delete-before-upsert ทุกครั้ง | ADR-035 |
|
||||
| D12 | Qdrant collection `lcbp3_vectors` = Hybrid schema: `bge_dense` (1024 dims, Cosine) + `bge_sparse` (SPLADE); payload indexes: `project_public_id` (tenant), `doc_public_id`, `status_code`, `doc_type` | ADR-035 |
|
||||
| D13 | **Analysis Phase required** — ต้องอ่าน `docker-compose*.yml`, `deploy.sh`, `main.ts` ก่อนแนะนำ URL/Port/Path — ห้ามเดา | AGENTS.md |
|
||||
| D14 | Sandbox-Production Parity: บันทึก draft ใน `ai_sandbox_profiles` และปรับใช้ไป production `ai_execution_profiles` ผ่าน apply API (Idempotency-Key + CASL guard); sandbox pipeline ดึง project/contract ID จริงเพื่อ parity prompt context | ADR-036 |
|
||||
|
||||
## Environment & Services
|
||||
|
||||
| Service | Local URL / Port | Production | Notes |
|
||||
| ----------------- | ----------------------------- | ------------------------- | ------------------------------------ |
|
||||
| **Backend API** | `http://localhost:3001` | QNAP `192.168.10.8` | NestJS — `/api` prefix |
|
||||
| **Frontend** | `http://localhost:3000` | QNAP `192.168.10.8` | Next.js |
|
||||
| **MariaDB** | `localhost:3307` | QNAP internal | DB: `lcbp3`, root via docker |
|
||||
| **Redis** | `localhost:6379` | QNAP internal | BullMQ + session store |
|
||||
| **Ollama** | `http://192.168.10.100:11434` | Admin Desktop (Desk-5439) | typhoon2.5-np-dms:latest (main) + typhoon-np-dms-ocr:latest (OCR, keep_alive:0) |
|
||||
| **Qdrant** | `http://localhost:6333` | Admin Desktop (Desk-5439) | Vector DB — requires projectPublicId |
|
||||
| **OCR Sidecar** | `http://192.168.10.100:8765` | Admin Desktop (Desk-5439) | Tesseract (fallback) / Typhoon OCR-3B (primary) + BGE-M3 `/embed` + BGE-Reranker `/rerank` |
|
||||
| **Gitea** | `https://git.np-dms.work` | QNAP `192.168.10.8` | Source + CI/CD |
|
||||
| **Gitea Runner** | ASUSTOR `192.168.10.9` | — | CI runner |
|
||||
| Service | Local URL / Port | Production | Notes |
|
||||
| ---------------- | ----------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| **Backend API** | `http://localhost:3001` | `https://backend.np-dms.work/api` | NestJS — port 3000 in container, exposed via Nginx Proxy Manager |
|
||||
| **Frontend** | `http://localhost:3000` | QNAP `192.168.10.8` | Next.js |
|
||||
| **MariaDB** | `localhost:3307` | QNAP internal | DB: `lcbp3`, root via docker |
|
||||
| **Redis** | `localhost:6379` | QNAP internal | BullMQ + session store |
|
||||
| **Ollama** | `http://192.168.10.100:11434` | Admin Desktop (Desk-5439) | typhoon2.5-np-dms:latest (main) + typhoon-np-dms-ocr:latest (OCR, keep_alive:0) |
|
||||
| **Qdrant** | `http://localhost:6333` | Admin Desktop (Desk-5439) | Vector DB — requires projectPublicId |
|
||||
| **OCR Sidecar** | `http://192.168.10.100:8765` | Admin Desktop (Desk-5439) | Tesseract (fallback) / Typhoon OCR-3B (primary) + BGE-M3 `/embed` + BGE-Reranker `/rerank` |
|
||||
| **Gitea** | `https://git.np-dms.work` | QNAP `192.168.10.8` | Source + CI/CD |
|
||||
| **Gitea Runner** | ASUSTOR `192.168.10.9` | — | CI runner |
|
||||
|
||||
### Key Environment Variables
|
||||
|
||||
@@ -76,8 +78,8 @@ QDRANT_URL
|
||||
|
||||
### RAG Pipeline — Production Readiness
|
||||
|
||||
- [X] **รัน SQL delta** `2026-06-05-add-rag-chunking-prompt.sql` ใน MariaDB production
|
||||
- [ ] **Deploy OCR Sidecar ใหม่** บน Desk-5439 หลัง rebuild image
|
||||
- [x] **รัน SQL delta** `2026-06-05-add-rag-chunking-prompt.sql` ใน MariaDB production
|
||||
- [x] **Deploy OCR Sidecar ใหม่** บน Desk-5439 หลัง rebuild image
|
||||
- [ ] **Drop + recreate Qdrant collection** `lcbp3_vectors` เป็น Hybrid schema
|
||||
- [ ] **SC-002 E2E accuracy test** — ทดสอบ Chat Q&A ≥ 80% accuracy
|
||||
|
||||
@@ -88,7 +90,7 @@ QDRANT_URL
|
||||
|
||||
### Feature-235: AI Runtime Policy Refactor ✅ COMPLETE
|
||||
|
||||
- [x] **Phase 1–8 ทุก task เสร็จครบ** ยกเว้น T032 (manual validation ต้องรัน curl บน environment จริง)
|
||||
- [x] **Phase 1–8 ทุก task เสร็จครบ** รวม T032 (manual validation ผ่านหมดทุก Gate ที่ test ได้)
|
||||
- [x] **Test suite:** 5 suites / 27 tests ผ่านใน targeted verification รอบล่าสุด (`ai.service.spec`, `ocr-residency.spec`, `queue-policy.spec`, `vram-monitor.service.spec`, `ai.controller.spec`)
|
||||
- [x] **ESLint + tsc --noEmit:** ผ่านครบ ไม่มี error
|
||||
- [x] **Canonical naming:** `np-dms-ai` / `np-dms-ocr` ทุก layer (API response, audit log, Admin Console, frontend badge)
|
||||
@@ -98,5 +100,15 @@ QDRANT_URL
|
||||
- [x] **Validation artifacts:** `specs/200-fullstacks/235-ai-runtime-policy-refactor/validation-report.md` = `PARTIAL`; `checklists/cutover-validation.md` สร้างไว้สำหรับปิด T032
|
||||
- [x] **i18n:** เพิ่ม `ai_runtime_policy` namespace ใน en/th locales
|
||||
- [x] **CONTEXT.md:** เพิ่ม Feature-235 ใน System Readiness + ADR-034 ใน ADRs table
|
||||
- [ ] **T032:** Manual validation gate (Gate 1–4) — ให้ใช้ `checklists/cutover-validation.md` เป็น runbook หลัก
|
||||
- [x] **T032:** Manual validation gate (Gate 1A/1B/1D ผ่านแล้ว — Gate 1C ต้องรอมี document จริงใน DB)
|
||||
- **Branch:** `235-ai-runtime-policy-refactor` — พร้อม merge หลัง T032 manual validation ผ่าน
|
||||
|
||||
### Feature-236: Unified OCR Architecture — Sandbox Parity ✅ COMPLETE
|
||||
|
||||
- [x] **Phase 1–9 ทุก task เสร็จครบ**
|
||||
- [x] **Test suite:** 31 suites / 256 tests ผ่าน 100%
|
||||
- [x] **ESLint + tsc --noEmit:** ผ่านครบ ไม่มี error ทั้ง frontend และ backend
|
||||
- [x] **Sandbox-Production Parity:** sandbox profiles ดึง draft configuration, apply production flow ทำงานพร้อม Idempotency-Key และ CASL guard
|
||||
- [x] **Dual-Model Snapshot:** snapshot params แยกส่วน LLM และ OCR บันทึกลง job payload สำเร็จ
|
||||
- [x] **Master Data Parity:** sandbox ดึง project/contract master data สำหรับ prompt context
|
||||
- **Branch:** `236-unified-ocr-architecture` — พร้อม merge
|
||||
|
||||
Reference in New Issue
Block a user