feat(ai): add ADR-036 unified OCR architecture and frontend test coverage
CI / CD Pipeline / build (push) Failing after 6m24s
CI / CD Pipeline / deploy (push) Has been skipped

- 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:
2026-06-14 06:34:07 +07:00
parent e3503b6a77
commit 7e8f4859cd
108 changed files with 33914 additions and 339 deletions
+14 -10
View File
@@ -9,6 +9,7 @@
// - 2026-05-30: นำเข้าและแสดงผล OcrEngineSelector component ใน Overview tab (T019, T020)
// - 2026-06-02: เพิ่มตัวบ่งชี้โมเดลหลักที่กำลังใช้งาน (Active Global Model badge) บนการ์ด System Toggle (T010, ADR-033)
// - 2026-06-13: [235] ลบ AI Model Management (ADR-027) และ OCR Engine Selector ออก; แก้ System Toggle แสดง canonical names (np-dms-ai/np-dms-ocr); แก้ label OCR Sidecar
// - 2026-06-13: ADR-036 — ใช้ canonical model constants สำหรับหน้า AI Admin Console
'use client';
@@ -45,6 +46,9 @@ interface VramLoadedModelView {
vramUsageMB?: number;
}
const MAIN_MODEL_NAME = 'np-dms-ai';
const OCR_MODEL_NAME = 'np-dms-ocr';
function ensureArray<T>(value: unknown): T[] {
return Array.isArray(value) ? value : [];
}
@@ -58,9 +62,9 @@ function normalizeLoadedModels(value: unknown): VramLoadedModelView[] {
const name = item.toLowerCase();
let normName = item;
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) {
normName = 'np-dms-ocr';
} else if (name.includes('typhoon') || name.includes('np-dms-ai')) {
normName = 'np-dms-ai';
normName = OCR_MODEL_NAME;
} else if (name.includes('typhoon') || name.includes(MAIN_MODEL_NAME)) {
normName = MAIN_MODEL_NAME;
}
return {
modelId: `${item}-${index}`,
@@ -78,9 +82,9 @@ function normalizeLoadedModels(value: unknown): VramLoadedModelView[] {
const name = rawName.toLowerCase();
let normName = rawName;
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) {
normName = 'np-dms-ocr';
} else if (name.includes('typhoon') || name.includes('np-dms-ai')) {
normName = 'np-dms-ai';
normName = OCR_MODEL_NAME;
} else if (name.includes('typhoon') || name.includes(MAIN_MODEL_NAME)) {
normName = MAIN_MODEL_NAME;
}
return {
modelId: model.modelId ?? rawName,
@@ -97,8 +101,8 @@ function normalizeLoadedModels(value: unknown): VramLoadedModelView[] {
function toCanonicalModel(rawName: string): string {
const name = rawName.toLowerCase();
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) return 'np-dms-ocr';
if (name.includes('typhoon') || name.includes('np-dms-ai')) return 'np-dms-ai';
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) return OCR_MODEL_NAME;
if (name.includes('typhoon') || name.includes(MAIN_MODEL_NAME)) return MAIN_MODEL_NAME;
return rawName;
}
@@ -135,8 +139,8 @@ export default function AiAdminConsolePage() {
const rawHealthOllamaModels = ensureArray<string>(health?.ollama?.models);
const healthOllamaModels = Array.from(new Set(rawHealthOllamaModels.map((m) => {
const name = m.toLowerCase();
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) return 'np-dms-ocr';
if (name.includes('typhoon') || name.includes('np-dms-ai')) return 'np-dms-ai';
if (name.includes('ocr') || name.includes('typhoon-np-dms-ocr')) return OCR_MODEL_NAME;
if (name.includes('typhoon') || name.includes(MAIN_MODEL_NAME)) return MAIN_MODEL_NAME;
return m;
})));
const healthQdrantCollections = ensureArray<string>(health?.qdrant?.collections);