From d418d791a466050556b0b8e226592b7665075a70 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 19 Jun 2026 12:26:36 +0700 Subject: [PATCH] 690619:1226 240 #03 --- .../__tests__/admin-ai.service.test.ts | 97 ++++++++ .../lib/services/admin-ai-prompt.service.ts | 34 ++- frontend/lib/services/admin-ai.service.ts | 15 +- memory/project-memory-override.md | 6 +- .../03-01-data-dictionary.md | 157 ++++++++++++- ...-alter-migration-review-queue.rollback.sql | 14 -- ...026-05-22-alter-migration-review-queue.sql | 16 -- ...-create-system-settings-table.rollback.sql | 10 - ...026-05-22-create-system-settings-table.sql | 48 ---- ...2026-05-22-create-tags-tables.rollback.sql | 14 -- .../deltas/2026-05-22-create-tags-tables.sql | 47 ---- ...6-05-22-drop-migration-tables.rollback.sql | 97 -------- .../2026-05-22-drop-migration-tables.sql | 26 --- ...5-23-alter-migration-review-queue-enum.sql | 15 -- ...4-add-migration-errors-job-id.rollback.sql | 10 - ...2026-05-24-add-migration-errors-job-id.sql | 11 - ...25-create-ai-available-models.rollback.sql | 8 - .../2026-05-25-create-ai-available-models.sql | 43 ---- .../2026-05-25-create-ai-prompts.rollback.sql | 4 - .../deltas/2026-05-25-create-ai-prompts.sql | 73 ------ ...-ai-permissions-to-superadmin.rollback.sql | 15 -- ...-25-grant-ai-permissions-to-superadmin.sql | 23 -- ...ext-aware-prompts-and-cleanup.rollback.sql | 28 --- ...-add-context-aware-prompts-and-cleanup.sql | 108 --------- .../2026-05-30-add-ai-prompts-publicId.sql | 21 -- .../2026-05-30-add-typhoon-ocr-prompt.sql | 50 ---- .../2026-05-30-extend-ai-audit-logs.sql | 21 -- .../2026-05-30-seed-typhoon-ai-models.sql | 24 -- ...-id-to-migration-review-queue.rollback.sql | 11 - ...ttachment-id-to-migration-review-queue.sql | 16 -- ...e-ai-available-models-typhoon.rollback.sql | 18 -- ...-03-update-ai-available-models-typhoon.sql | 49 ---- ...06-05-add-rag-chunking-prompt.rollback.sql | 8 - .../2026-06-05-add-rag-chunking-prompt.sql | 47 ---- .../2026-06-06-add-ai-prompts-public-id.sql | 23 -- ...-create-ai-execution-profiles.rollback.sql | 6 - ...026-06-11-create-ai-execution-profiles.sql | 38 ---- ...-ai-audit-logs-runtime-policy.rollback.sql | 19 -- ...11-extend-ai-audit-logs-runtime-policy.sql | 37 --- ...end-ai-execution-profiles-ocr.rollback.sql | 16 -- ...06-13-extend-ai-execution-profiles-ocr.sql | 58 ----- ...026-06-14-create-ai-execution-profiles.sql | 42 ---- ...026-06-14-seed-additional-prompt-types.sql | 54 ----- .../2026-06-14-seed-execution-profiles.sql | 21 -- .../2026-06-15-fix-ai-prompts-columns.sql | 10 - .../2026-06-17-seed-ocr-system-prompt.sql | 25 -- .../lcbp3-v1.9.0-schema-02-tables.sql | 213 +++++++++++++----- .../lcbp3-v1.9.0-seed-basic.sql | 190 ++++++++++++++++ specs/88-logs/rollouts.md | 2 + ...6-06-19-ai-admin-response-normalization.md | 28 +++ ...ssion-2026-06-19-deployment-timeout-fix.md | 34 +++ 51 files changed, 706 insertions(+), 1294 deletions(-) create mode 100644 frontend/lib/services/__tests__/admin-ai.service.test.ts delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-23-alter-migration-review-queue-enum.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-30-add-ai-prompts-publicId.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-30-add-typhoon-ocr-prompt.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-30-extend-ai-audit-logs.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-05-30-seed-typhoon-ai-models.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-06-add-ai-prompts-public-id.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.rollback.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-14-create-ai-execution-profiles.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-14-seed-additional-prompt-types.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-14-seed-execution-profiles.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-15-fix-ai-prompts-columns.sql delete mode 100644 specs/03-Data-and-Storage/deltas/2026-06-17-seed-ocr-system-prompt.sql create mode 100644 specs/88-logs/session-2026-06-19-ai-admin-response-normalization.md create mode 100644 specs/88-logs/session-2026-06-19-deployment-timeout-fix.md diff --git a/frontend/lib/services/__tests__/admin-ai.service.test.ts b/frontend/lib/services/__tests__/admin-ai.service.test.ts new file mode 100644 index 00000000..f080765a --- /dev/null +++ b/frontend/lib/services/__tests__/admin-ai.service.test.ts @@ -0,0 +1,97 @@ +// File: frontend/lib/services/__tests__/admin-ai.service.test.ts +// Change Log: +// - 2026-06-19: เพิ่ม regression tests สำหรับ AI Admin response normalization + +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import apiClient from '@/lib/api/client'; +import { adminAiService } from '../admin-ai.service'; +import { adminAiPromptService } from '../admin-ai-prompt.service'; + +const promptVersion = { + publicId: '0195aaaa-aaaa-7000-8000-aaaaaaaaaaaa', + promptType: 'ocr_system', + versionNumber: 1, + version: 3, + template: 'OCR prompt', + isActive: true, + createdAt: '2026-06-19T00:00:00.000Z', +}; + +describe('admin AI service normalization', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('ควร unwrap VRAM response ที่ถูกห่อ data ซ้อนกัน', async () => { + vi.mocked(apiClient.get).mockResolvedValue({ + data: { + data: { + totalVramMb: 16384, + usedVramMb: 4096, + freeVramMb: 12288, + hasCapacity: true, + loadedModels: [ + { + modelId: 'np-dms-ai:latest', + modelName: 'np-dms-ai:latest', + vramUsageMB: 4096, + }, + ], + }, + }, + }); + + const result = await adminAiService.getVramStatus(); + + expect(result.totalVRAMMB).toBe(16384); + expect(result.usedVRAMMB).toBe(4096); + expect(result.usagePercent).toBe(25); + expect(result.canLoadModel).toBe(true); + expect(result.loadedModels).toHaveLength(1); + }); + + it('ควรไม่แสดง OOM Guard เมื่อ backend ยังไม่ส่ง total VRAM', async () => { + vi.mocked(apiClient.get).mockResolvedValue({ + data: { + data: { + loadedModels: [], + hasCapacity: false, + }, + }, + }); + + const result = await adminAiService.getVramStatus(); + + expect(result.totalVRAMMB).toBe(0); + expect(result.usedVRAMMB).toBe(0); + expect(result.canLoadModel).toBe(true); + }); + + it('ควรคืน prompt list เป็น array เมื่อ response ถูกห่อ data ซ้อนกัน', async () => { + vi.mocked(apiClient.get).mockResolvedValue({ + data: { + data: { + data: [promptVersion], + }, + }, + }); + + const result = await adminAiPromptService.getPrompts('ocr_system'); + + expect(result).toEqual([promptVersion]); + }); + + it('ควรคืน empty array เมื่อ prompt payload ไม่ใช่ array', async () => { + vi.mocked(apiClient.get).mockResolvedValue({ + data: { + data: { + message: 'unexpected payload', + }, + }, + }); + + const result = await adminAiPromptService.getPrompts('ocr_system'); + + expect(result).toEqual([]); + }); +}); diff --git a/frontend/lib/services/admin-ai-prompt.service.ts b/frontend/lib/services/admin-ai-prompt.service.ts index 2c9b8a22..237074a4 100644 --- a/frontend/lib/services/admin-ai-prompt.service.ts +++ b/frontend/lib/services/admin-ai-prompt.service.ts @@ -1,6 +1,7 @@ // File: frontend/lib/services/admin-ai-prompt.service.ts // Change Log // - 2026-06-17: Created adminAiPromptService for prompt management UI (Feature 238) +// - 2026-06-19: Normalize prompt response envelopes to prevent non-array prompt history crashes import client from '../api/client'; @@ -18,6 +19,23 @@ export interface AiPromptVersion { createdBy?: number; } +const extractData = (value: unknown): T => { + let current = value; + for (let depth = 0; depth < 3; depth += 1) { + if (current && typeof current === 'object' && 'data' in current) { + current = (current as { data: unknown }).data; + continue; + } + break; + } + return current as T; +}; + +const normalizePromptList = (value: unknown): AiPromptVersion[] => { + const data = extractData(value); + return Array.isArray(data) ? (data as AiPromptVersion[]) : []; +}; + /** * Service สำหรับจัดการ AI Prompt Versions ใน Admin Console */ @@ -26,10 +44,10 @@ export const adminAiPromptService = { * ดึงรายการ prompt versions ทั้งหมดสำหรับ prompt_type ที่กำหนด */ async getPrompts(promptType: string): Promise { - const response = await client.get<{ data: AiPromptVersion[] }>( + const response = await client.get( `/ai/prompts/${promptType}` ); - return response.data.data; + return normalizePromptList(response.data); }, /** @@ -41,7 +59,7 @@ export const adminAiPromptService = { contextConfig?: Record ): Promise { const idempotencyKey = crypto.randomUUID(); - const response = await client.post<{ data: AiPromptVersion }>( + const response = await client.post( `/ai/prompts/${promptType}`, { template, contextConfig }, { @@ -50,7 +68,7 @@ export const adminAiPromptService = { }, } ); - return response.data.data; + return extractData(response.data); }, /** @@ -62,7 +80,7 @@ export const adminAiPromptService = { expectedVersion?: number ): Promise { const idempotencyKey = crypto.randomUUID(); - const response = await client.post<{ data: AiPromptVersion }>( + const response = await client.post( `/ai/prompts/${promptType}/${versionNumber}/activate`, expectedVersion !== undefined ? { expectedVersion } : {}, { @@ -71,7 +89,7 @@ export const adminAiPromptService = { }, } ); - return response.data.data; + return extractData(response.data); }, /** @@ -91,10 +109,10 @@ export const adminAiPromptService = { versionNumber: number, manualNote: string | null ): Promise { - const response = await client.patch<{ data: AiPromptVersion }>( + const response = await client.patch( `/ai/prompts/${promptType}/${versionNumber}/note`, { manualNote } ); - return response.data.data; + return extractData(response.data); }, }; diff --git a/frontend/lib/services/admin-ai.service.ts b/frontend/lib/services/admin-ai.service.ts index b470ced8..e75de6d5 100644 --- a/frontend/lib/services/admin-ai.service.ts +++ b/frontend/lib/services/admin-ai.service.ts @@ -17,6 +17,7 @@ // - 2026-06-13: T027-T029 — เพิ่ม getSandboxProfile, saveSandboxProfile, resetSandboxProfile สำหรับ sandbox parameter management // - 2026-06-13: T042-T043 — เพิ่ม applyProfile และ getProductionDefaults สำหรับปรับใช้และดึงค่า production parameters // - 2026-06-13: US4 — อัปเดต submitSandboxExtract และ submitSandboxAiExtract ให้รองรับ project/contract publicId +// - 2026-06-19: แก้ response envelope ซ้อนกันเพื่อป้องกัน VRAM แสดง 0/0 และ OOM Guard ผิดพลาด import api from '../api/client'; import { AiJobResponse } from '../../types/ai'; @@ -172,10 +173,15 @@ export interface ExecutionProfile { } const extractData = (value: unknown): T => { - if (value && typeof value === 'object' && 'data' in value) { - return (value as { data: T }).data; + let current = value; + for (let depth = 0; depth < 3; depth += 1) { + if (current && typeof current === 'object' && 'data' in current) { + current = (current as { data: unknown }).data; + continue; + } + break; } - return value as T; + return current as T; }; const normalizeLoadedModels = (models: Array | undefined): LoadedModelInfo[] => { @@ -199,6 +205,7 @@ const normalizeVramStatus = (value: unknown): VramStatusResponse => { const totalVRAMMB = raw.totalVRAMMB ?? raw.totalVramMb ?? 0; const usedVRAMMB = raw.usedVRAMMB ?? raw.usedVramMb ?? 0; const usagePercent = raw.usagePercent ?? (totalVRAMMB > 0 ? Math.round((usedVRAMMB / totalVRAMMB) * 100) : 0); + const hasKnownCapacity = totalVRAMMB > 0; // Backend now sends loadedModels with vramUsageMB directly const loadedModels = normalizeLoadedModels(raw.loadedModels); @@ -209,7 +216,7 @@ const normalizeVramStatus = (value: unknown): VramStatusResponse => { usagePercent, thresholdPercent: raw.thresholdPercent ?? 90, loadedModels, - canLoadModel: raw.canLoadModel ?? raw.hasCapacity ?? false, + canLoadModel: hasKnownCapacity ? (raw.canLoadModel ?? raw.hasCapacity ?? false) : true, lastUpdated: raw.lastUpdated ?? new Date().toISOString(), }; }; diff --git a/memory/project-memory-override.md b/memory/project-memory-override.md index b6265b69..73ab0a56 100644 --- a/memory/project-memory-override.md +++ b/memory/project-memory-override.md @@ -44,7 +44,10 @@ | 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 | | D15 | SandboxTabs ต้องโหลด active prompts ทั้ง ocr_system และ ocr_extraction จาก service เพื่อแสดง prompt info ทั้ง 2 steps ตาม FR-009, FR-010 (Feature-238) | Feature-238 | | D16 | Backend VRAM service ต้องส่ง loadedModels พร้อม vramUsageMB (bytes → MB) เพื่อให้ frontend แสดงผล VRAM usage ของแต่ละ model ได้ถูกต้อง | Session 2026-06-18 | -| D17 | สถานะพับ/คลี่ของการ์ดและเซกชันในหน้า AI Admin Console จะเก็บลงใน localStorage เพื่อรักษาสถานะ และการพับไม่มีผลต่อ background query polling | Feature-240 | +| D17 | สถานะพับ/คลี่ของการ์ดและเซกชันในหน้า AI Admin Console จะเก็บลงใน localStorage เพื่อรักษาสถานะ และการพับไม่มีผลต่อ background query polling | Feature-240 | +| D18 | Deploy script ต้องตรวจสอบ ClamAV health status ก่อน recreation — ถ้า healthy ให้ recreate เฉพาะ backend/frontend (skip 5-minute healthcheck delay) | Session 2026-06-19 | +| D19 | CI timeout ต้องอย่างน้อย 30 minutes เพื่อรองรับ ClamAV startup กรณีต้อง recreate full stack | Session 2026-06-19 | +| D20 | AI Admin frontend services ต้อง normalize API response envelope ที่อาจซ้อน `data` ก่อน render; VRAM `totalVRAMMB = 0` คือ unknown capacity ไม่ใช่ OOM Guard | Session 2026-06-19 | ## Environment & Services @@ -173,4 +176,3 @@ QDRANT_URL - [x] **Background Polling:** การพับเก็บไม่มีผลกระทบต่อการดึงข้อมูลสถานะในพื้นหลังผ่าน TanStack Query - [x] **Validation & Quality:** ผ่านการตรวจสอบประเภท (tsc) และ Lint (eslint) พร้อมสร้างรายงาน validation-report.md - **Branch:** `240-ai-console-collapsible-cards` - diff --git a/specs/03-Data-and-Storage/03-01-data-dictionary.md b/specs/03-Data-and-Storage/03-01-data-dictionary.md index 43d75bb7..1a62a467 100644 --- a/specs/03-Data-and-Storage/03-01-data-dictionary.md +++ b/specs/03-Data-and-Storage/03-01-data-dictionary.md @@ -1,9 +1,9 @@ --- title: 'Data & Storage: Data Dictionary and Data Model Architecture' -version: 1.9.1 +version: 1.9.2 status: released owner: Nattanin Peancharoen -last_updated: 2026-05-16 +last_updated: 2026-06-19 related: - specs/01-requirements/02-architecture.md - specs/01-requirements/03-functional-requirements.md @@ -2345,8 +2345,14 @@ PENDING_REVIEW ──→ VERIFIED ──→ IMPORTED (terminal) | `document_public_id` | UUID | YES | Imported document publicId when available | | `ai_model` | VARCHAR(50) | NO | Legacy AI model column used by current gateway service (default: gemma4) | | `model_name` | VARCHAR(100) | NO | Local model name used by ADR-023 AI pipeline | -| `ai_suggestion_json` | JSON | YES | AI suggested metadata | -| `human_override_json` | JSON | YES | Human approved or overridden metadata | +| `effective_profile` | VARCHAR(50) | YES | ExecutionProfile ที่ backend กำหนด: interactive\|standard\|quality\|deep-analysis (Feature-235) | +| `canonical_model` | VARCHAR(50) | YES | Canonical model identity: np-dms-ai หรือ np-dms-ocr (Feature-235, ADR-023) | +| `snapshot_params_json` | LONGTEXT | YES | Runtime parameters snapshot ณ เวลา dispatch — ใช้จริงใน Ollama call (FR-A09, Feature-235) | +| `model_type` | VARCHAR(50) | YES | ประเภท OCR/LLM model ที่ใช้ เช่น tesseract, typhoon-ocr-3b | +| `vram_usage_mb` | INT | YES | VRAM ที่ใช้จริง (MB) ณ เวลาประมวลผล | +| `cache_hit` | TINYINT(1) | NO | 1 = ผลลัพธ์มาจาก Redis cache, 0 = OCR ใหม่ | +| `ai_suggestion_json` | LONGTEXT | YES | AI suggested metadata | +| `human_override_json` | LONGTEXT | YES | Human approved or overridden metadata | | `processing_time_ms` | INT | YES | Legacy processing duration field | | `confidence_score` | DECIMAL(4,3) | YES | AI confidence score 0.000-1.000 | | `input_hash` | VARCHAR(64) | YES | Legacy SHA-256 input hash | @@ -2365,6 +2371,9 @@ PENDING_REVIEW ──→ VERIFIED ──→ IMPORTED (terminal) - KEY idx_ai_audit_model_name (model_name) - KEY idx_ai_audit_status (status) - KEY idx_ai_audit_confirmed_by (confirmed_by_user_id) +- KEY idx_ai_audit_model_type (model_type) +- KEY idx_ai_audit_effective_profile (effective_profile) +- KEY idx_ai_audit_canonical_model (canonical_model) - CONSTRAINT fk_ai_audit_confirmed_by_user FOREIGN KEY (confirmed_by_user_id) REFERENCES users (user_id) ON DELETE SET NULL #### Business Rules @@ -2375,7 +2384,145 @@ PENDING_REVIEW ──→ VERIFIED ──→ IMPORTED (terminal) --- -### 19.3 `document_chunks` +### 19.3 `ai_available_models` + +**วัตถุประสงค์:** เก็บรายการโมเดล AI ที่ให้เลือกใช้งานในระบบ (ADR-027, ADR-034) + +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| `id` | INT AUTO_INCREMENT | NO | ID ของตาราง | +| `model_name` | VARCHAR(100) | NO | ชื่อโมเดล เช่น gemma4:e2b, gemma4:e4b | +| `model_version` | VARCHAR(50) | NO | เวอร์ชั่นของโมเดล | +| `description` | VARCHAR(500) | YES | รายละเอียดโมเดล | +| `vram_gb` | DECIMAL(4,2) | YES | VRAM ที่ใช้โดยประมาณ (GB) | +| `is_active` | TINYINT(1) | NO | สถานะใช้งาน | +| `is_default` | TINYINT(1) | NO | โมเดลเริ่มต้น | +| `created_by` | INT | YES | ผู้สร้าง | +| `updated_by` | INT | YES | ผู้แก้ไขล่าสุด | +| `created_at` | DATETIME(3) | NO | วันที่สร้าง | +| `updated_at` | DATETIME(3) | NO | วันที่แก้ไขล่าสุด | +| `deleted_at` | DATETIME(3) | YES | วันที่ลบ (Soft Delete) | + +**Indexes**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_model_name (model_name) +- KEY idx_is_active (is_active) +- KEY idx_is_default (is_default) + +--- + +### 19.4 `ai_prompts` + +**วัตถุประสงค์:** ตาราง versioned prompt templates สำหรับ OCR extraction (ADR-029) + +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| `id` | INT AUTO_INCREMENT | NO | ID ภายใน (ไม่ expose ใน API) | +| `public_id` | UUID | NO | UUID Public Identifier (ADR-019) | +| `prompt_type` | VARCHAR(50) | NO | ประเภท prompt เช่น ocr_extraction | +| `version_number` | INT | NO | เลข version ต่อเนื่องต่อ prompt_type (1, 2, 3...) | +| `template` | TEXT | NO | prompt template ที่มี {{ocr_text}} placeholder บังคับ | +| `field_schema` | LONGTEXT | YES | definition ของ fields ที่คาดหวังในผลลัพธ์ JSON | +| `is_active` | TINYINT(1) | NO | 1 = version นี้ใช้งานจริงทั้ง sandbox และ migrate-document (1 per prompt_type) | +| `test_result_json` | LONGTEXT | YES | ผลลัพธ์ JSON จาก sandbox run ล่าสุด (auto-save โดย processor) | +| `manual_note` | TEXT | YES | หมายเหตุ/annotation จาก admin (manual input) | +| `last_tested_at` | TIMESTAMP | YES | เวลาที่ sandbox รันครั้งล่าสุดสำหรับ version นี้ | +| `activated_at` | TIMESTAMP | YES | เวลาที่ version นี้ถูก activate เป็น active | +| `created_by` | INT | NO | user_id ของผู้สร้าง version นี้ | +| `created_at` | TIMESTAMP | NO | วันที่สร้าง | +| `context_config` | LONGTEXT | YES | Configuration สำหรับ context ที่ backend ต้องส่งให้ AI (filter, pageSize, language, etc.) | +| `version` | INT | NO | Optimistic locking version | + +**Indexes**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_type_version (prompt_type, version_number) +- KEY idx_prompt_type_active (prompt_type, is_active) +- KEY created_by (created_by) +- CONSTRAINT ai_prompts_ibfk_1 FOREIGN KEY (created_by) REFERENCES users (user_id) + +--- + +### 19.5 `ai_execution_profiles` + +**วัตถุประสงค์:** ตาราง execution profile parameters สำหรับ np-dms-ai (ADR-025, ADR-027) + +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| `id` | INT AUTO_INCREMENT | NO | ID ภายใน | +| `profile_name` | VARCHAR(50) | NO | ชื่อ profile | +| `canonical_model` | VARCHAR(20) | NO | Model identity (default: np-dms-ai) | +| `temperature` | DECIMAL(4,3) | NO | LLM temperature | +| `top_p` | DECIMAL(4,3) | NO | LLM top_p | +| `max_tokens` | INT | YES | Maximum tokens | +| `num_ctx` | INT | YES | Context window size | +| `repeat_penalty` | DECIMAL(5,3) | NO | Repeat penalty | +| `keep_alive_seconds` | INT | NO | Model keep_alive in seconds | +| `is_active` | TINYINT(1) | NO | 1 = active; 0 = disabled | +| `updated_by` | INT | YES | user_id | +| `updated_at` | TIMESTAMP | NO | วันที่แก้ไขล่าสุด | +| `created_at` | TIMESTAMP | NO | วันที่สร้าง | + +**Indexes**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_profile_name (profile_name) +- KEY idx_profile_active (profile_name, is_active) +- KEY updated_by (updated_by) +- CONSTRAINT ai_execution_profiles_ibfk_1 FOREIGN KEY (updated_by) REFERENCES users (user_id) + +--- + +### 19.6 `ai_sandbox_profiles` + +**วัตถุประสงค์:** ตาราง sandbox profile parameters + +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| `id` | INT AUTO_INCREMENT | NO | ID ภายใน | +| `profile_name` | VARCHAR(50) | NO | ชื่อ profile | +| `canonical_model` | VARCHAR(20) | NO | Model identity (default: np-dms-ai) | +| `temperature` | DECIMAL(4,3) | NO | LLM temperature | +| `top_p` | DECIMAL(4,3) | NO | LLM top_p | +| `max_tokens` | INT | YES | Maximum tokens | +| `num_ctx` | INT | NO | Context window size | +| `repeat_penalty` | DECIMAL(5,3) | NO | Repeat penalty | +| `keep_alive_seconds` | INT | NO | Model keep_alive in seconds | +| `updated_by` | INT | YES | user_id | +| `updated_at` | TIMESTAMP | NO | วันที่แก้ไขล่าสุด | +| `created_at` | TIMESTAMP | NO | วันที่สร้าง | + +**Indexes**: +- PRIMARY KEY (id) +- UNIQUE KEY uk_profile_name (profile_name) +- KEY idx_ai_sandbox_profile_model (canonical_model) +- KEY updated_by (updated_by) +- CONSTRAINT ai_sandbox_profiles_ibfk_1 FOREIGN KEY (updated_by) REFERENCES users (user_id) + +--- + +### 19.7 `migration_errors` + +**วัตถุประสงค์:** ตาราง Error Log สำหรับ Migration (ลบได้หลัง Migration เสร็จ) (ADR-028) + +| Column | Type | Nullable | Description | +|--------|------|----------|-------------| +| `id` | INT AUTO_INCREMENT | NO | ID | +| `batch_id` | VARCHAR(50) | YES | n8n batch identifier | +| `document_number` | VARCHAR(100) | YES | เลขที่เอกสาร | +| `error_type` | ENUM | YES | FILE_NOT_FOUND, MISSING_FILENAME, FILE_ERROR, AI_PARSE_ERROR, API_ERROR, DB_ERROR, SECURITY, UNKNOWN | +| `error_message` | TEXT | YES | ข้อความ error | +| `job_id` | VARCHAR(100) | YES | BullMQ Job ID | +| `raw_ai_response` | TEXT | YES | AI response ดิบ | +| `created_at` | TIMESTAMP | NO | วันที่สร้าง | + +**Indexes**: +- PRIMARY KEY (id) +- KEY idx_batch_id (batch_id) +- KEY idx_job_id (job_id) +- KEY idx_error_type (error_type) + +--- + +### 19.8 `document_chunks` **วัตถุประสงค์:** เก็บ vector metadata สำหรับ RAG ingestion ตาม ADR-022 diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.rollback.sql deleted file mode 100644 index 76b25f1f..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.rollback.sql +++ /dev/null @@ -1,14 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.rollback.sql --- Change Log: --- - 2026-05-22: ลบคอลัมน์ ai_job_id ออกจากตาราง migration_review_queue ตาม ADR-028 - --- Delta Rollback: ลบคอลัมน์ ai_job_id ในตาราง migration_review_queue --- Date: 2026-05-22 --- Related ADR: ADR-028, ADR-023A - --- ------------------------------------------------------------ --- การลบคอลัมน์ (Rollback changes) --- ------------------------------------------------------------ - -ALTER TABLE migration_review_queue -DROP COLUMN ai_job_id; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.sql deleted file mode 100644 index af32d702..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.sql +++ /dev/null @@ -1,16 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-22-alter-migration-review-queue.sql --- Change Log: --- - 2026-05-22: เพิ่มคอลัมน์ ai_job_id ในตาราง migration_review_queue ตาม ADR-028 - --- Delta: เพิ่มคอลัมน์ ai_job_id ในตาราง migration_review_queue --- Date: 2026-05-22 --- Related ADR: ADR-028, ADR-023A --- Applied in: v1.9.0 -> v1.9.5 - --- ------------------------------------------------------------ --- การปรับปรุงตาราง migration_review_queue (Schema changes) --- ------------------------------------------------------------ - -ALTER TABLE migration_review_queue -ADD COLUMN ai_job_id VARCHAR(36) NULL COMMENT 'BullMQ Job ID สำหรับงานประมวลผล AI' -AFTER storage_temp_path; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.rollback.sql deleted file mode 100644 index a38a62fa..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.rollback.sql +++ /dev/null @@ -1,10 +0,0 @@ --- Delta Rollback: ลบตาราง system_settings --- Date: 2026-05-22 --- Related ADR: ADR-027 --- Applied in: v1.9.0 -> v1.9.5 - --- ------------------------------------------------------------ --- การย้อนกลับโครงสร้างฐานข้อมูล (Rollback changes) --- ------------------------------------------------------------ - -DROP TABLE IF EXISTS system_settings; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.sql deleted file mode 100644 index 35ad797f..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-create-system-settings-table.sql +++ /dev/null @@ -1,48 +0,0 @@ --- Delta: สร้างตาราง system_settings และ Seed ข้อมูล AI_FEATURES_ENABLED --- Date: 2026-05-22 --- Related ADR: ADR-027 --- Applied in: v1.9.0 -> v1.9.5 - --- ------------------------------------------------------------ --- การเปลี่ยนแปลงโครงสร้างฐานข้อมูล (Schema changes) --- ------------------------------------------------------------ - -CREATE TABLE IF NOT EXISTS system_settings ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - setting_key VARCHAR(100) NOT NULL UNIQUE COMMENT 'คีย์การตั้งค่าระบบ', - setting_value TEXT NOT NULL COMMENT 'ค่าที่บันทึก (stringified)', - data_type ENUM('string', 'number', 'boolean', 'json') NOT NULL DEFAULT 'string' COMMENT 'ประเภทข้อมูลสำหรับ validation', - category VARCHAR(50) COMMENT 'หมวดหมู่', - is_encrypted TINYINT(1) DEFAULT 0 COMMENT 'เข้ารหัสค่า sensitive', - validation_rules JSON COMMENT 'กฎ validation', - description TEXT COMMENT 'คำอธิบายข้อมูลการตั้งค่า', - is_public TINYINT(1) DEFAULT 0 COMMENT 'เผยแพร่ให้ frontend อ่านได้หรือ admin only', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL, - INDEX idx_system_settings_category (category), - INDEX idx_system_settings_is_public (is_public) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก'; - --- ------------------------------------------------------------ --- การเพิ่มข้อมูลพื้นฐาน (Seed Data) --- ------------------------------------------------------------ - -INSERT INTO system_settings ( - setting_key, - setting_value, - data_type, - category, - description, - is_public -) -VALUES ( - 'AI_FEATURES_ENABLED', - 'true', - 'boolean', - 'ai', - 'สถานะเปิด/ปิดการใช้งานฟีเจอร์ AI ทั้งระบบ สำหรับผู้ใช้ทั่วไป', - 1 -) -ON DUPLICATE KEY UPDATE setting_key = setting_key; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.rollback.sql deleted file mode 100644 index eb97f359..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.rollback.sql +++ /dev/null @@ -1,14 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.rollback.sql --- Change Log: --- - 2026-05-22: ย้อนกลับตาราง tags และ correspondence_tags ตาม ADR-028 - --- Delta Rollback: ลบตาราง tags และ correspondence_tags --- Date: 2026-05-22 --- Related ADR: ADR-028 - --- ------------------------------------------------------------ --- การลบตาราง (Rollback changes) --- ------------------------------------------------------------ - -DROP TABLE IF EXISTS correspondence_tags; -DROP TABLE IF EXISTS tags; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.sql deleted file mode 100644 index 83b0da70..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.sql +++ /dev/null @@ -1,47 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-22-create-tags-tables.sql --- Change Log: --- - 2026-05-22: สร้างตาราง tags และ correspondence_tags ตาม ADR-028 - --- Delta: สร้างตาราง tags และ correspondence_tags --- Date: 2026-05-22 --- Related ADR: ADR-028, ADR-019 --- Applied in: v1.9.0 -> v1.9.5 - --- ------------------------------------------------------------ --- การสร้างตาราง tags (Schema changes) --- ------------------------------------------------------------ - -CREATE TABLE IF NOT EXISTS tags ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ภายในระบบ', - public_id CHAR(36) NOT NULL UNIQUE COMMENT 'UUIDv7 สำหรับการใช้งานภายนอก (ADR-019)', - project_id INT NULL COMMENT 'ID โครงการ (NULL = Global Tag)', - tag_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแท็ก', - color_code VARCHAR(30) DEFAULT 'default' COMMENT 'รหัสสีสำหรับ UI', - description TEXT COMMENT 'คำอธิบายเพิ่มเติม', - created_by INT COMMENT 'ผู้สร้างแท็ก', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - deleted_at TIMESTAMP NULL COMMENT 'วันที่ลบ (Soft Delete)', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, - UNIQUE KEY uq_tag_project (project_id, tag_name), - INDEX idx_tags_deleted_at (deleted_at) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลแท็กจัดหมวดหมู่เอกสาร'; - --- ------------------------------------------------------------ --- การสร้างตาราง correspondence_tags (Schema changes) --- ------------------------------------------------------------ - -CREATE TABLE IF NOT EXISTS correspondence_tags ( - correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร', - tag_id INT NOT NULL COMMENT 'ID ของแท็ก', - is_ai_suggested BOOLEAN DEFAULT FALSE COMMENT 'แท็กนี้แนะนำโดย AI หรือไม่', - confidence DECIMAL(4,3) NULL COMMENT 'ค่าความมั่นใจของ AI (0.000–1.000)', - created_by INT COMMENT 'ผู้เชื่อมโยงแท็ก', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่เชื่อมโยง', - PRIMARY KEY (correspondence_id, tag_id), - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, - INDEX idx_correspondence_tags_lookup (tag_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมโยงความสัมพันธ์แบบ M:N ระหว่างเอกสารและแท็ก'; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.rollback.sql deleted file mode 100644 index 71c480dd..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.rollback.sql +++ /dev/null @@ -1,97 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.rollback.sql --- Change Log: --- - 2026-05-22: กู้คืนโครงสร้างตาราง staging ทั้งหมด 5 ตารางสำหรับระบบย้ายข้อมูลกรณีเกิดเหตุฉุกเฉิน (Phase 6) - --- Delta Rollback: กู้คืนตาราง Staging ชั่วคราว (Recreate Staging Tables) --- Date: 2026-05-22 --- Related ADR: ADR-028 - --- ------------------------------------------------------------ --- การกู้คืนตาราง Staging ทั้งหมด 5 ตาราง --- ------------------------------------------------------------ - --- 1. กู้คืนตารางความคืบหน้าของ Migration Progress -CREATE TABLE IF NOT EXISTS migration_progress ( - batch_id VARCHAR(50) PRIMARY KEY, - last_processed_index INT DEFAULT 0, - STATUS ENUM('RUNNING', 'COMPLETED', 'FAILED') DEFAULT 'RUNNING', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Batch Progress'; - --- 2. กู้คืนตารางคิวตรวจสอบสำหรับเอกสาร (Review Queue) -CREATE TABLE IF NOT EXISTS migration_review_queue ( - id INT NOT NULL AUTO_INCREMENT, - uuid CHAR(36) NOT NULL DEFAULT (uuid()) COMMENT 'UUID Public Identifier (ADR-019)', - document_number VARCHAR(100) NOT NULL, - subject TEXT COMMENT 'หัวข้อเรื่องภาษาไทยหรืออังกฤษ', - original_subject TEXT COMMENT 'หัวข้อเรื่องเดิมจากระบบจัดเก็บเดิม', - body TEXT NULL COMMENT 'เนื้อความย่อจาก AI', - ai_suggested_category VARCHAR(50), - ai_confidence DECIMAL(4, 3), - ai_issues JSON, - review_reason VARCHAR(255), - status ENUM('PENDING', 'APPROVED', 'IMPORTED', 'REJECTED') NOT NULL DEFAULT 'PENDING', - reviewed_by VARCHAR(100), - reviewed_at TIMESTAMP NULL, - project_id INT NULL COMMENT 'Project ID ของโครงการ', - sender_organization_id INT NULL COMMENT 'Sender ID ของผู้ส่ง', - receiver_organization_id INT NULL COMMENT 'Receiver ID ของผู้รับ', - received_date DATE NULL, - issued_date DATE NULL, - remarks TEXT, - ai_summary TEXT, - extracted_tags JSON, - temp_attachment_id INT NULL, - ai_job_id VARCHAR(36) NULL COMMENT 'BullMQ Job ID สำหรับงานประมวลผล AI', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (id), - UNIQUE KEY uq_doc_number (document_number), - UNIQUE KEY uq_migration_review_uuid (uuid) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Review Queue'; - --- 3. กู้คืนตารางแสดงประวัติข้อผิดพลาดการย้ายข้อมูล (Error Log) -CREATE TABLE IF NOT EXISTS migration_errors ( - id INT AUTO_INCREMENT PRIMARY KEY, - batch_id VARCHAR(50), - document_number VARCHAR(100), - error_type ENUM( - 'FILE_NOT_FOUND', - 'MISSING_FILENAME', - 'FILE_ERROR', - 'AI_PARSE_ERROR', - 'API_ERROR', - 'DB_ERROR', - 'SECURITY', - 'UNKNOWN' - ), - error_message TEXT, - job_id VARCHAR(100) NULL, - raw_ai_response TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - INDEX idx_batch_id (batch_id), - INDEX idx_job_id (job_id), - INDEX idx_error_type (error_type) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Error Log'; - --- 4. กู้คืนตารางสถานะสำหรับ AI Model Fallback State -CREATE TABLE IF NOT EXISTS migration_fallback_state ( - id INT AUTO_INCREMENT PRIMARY KEY, - batch_id VARCHAR(50) UNIQUE, - recent_error_count INT DEFAULT 0, - is_fallback_active BOOLEAN DEFAULT FALSE, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Fallback Model State'; - --- 5. กู้คืนตารางแสดงข้อมูลสรุปรายวันของ Migration (Daily Summary) -CREATE TABLE IF NOT EXISTS migration_daily_summary ( - id INT AUTO_INCREMENT PRIMARY KEY, - batch_id VARCHAR(50), - summary_date DATE, - total_processed INT DEFAULT 0, - auto_ingested INT DEFAULT 0, - sent_to_review INT DEFAULT 0, - rejected INT DEFAULT 0, - errors INT DEFAULT 0, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY uq_batch_date (batch_id, summary_date) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Daily Summary'; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.sql b/specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.sql deleted file mode 100644 index a2a46fc4..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.sql +++ /dev/null @@ -1,26 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-22-drop-migration-tables.sql --- Change Log: --- - 2026-05-22: ดรอปตาราง staging ทั้งหมดหลังย้ายข้อมูลเสร็จสิ้น (Phase 6) โดยยังคงรักษาตาราง import_transactions ไว้ป้องกันการย้ายข้อมูลซ้ำ - --- Delta: ดรอปตาราง Staging ชั่วคราว (Post-Migration Cleanup) --- Date: 2026-05-22 --- Related ADR: ADR-028 - --- ------------------------------------------------------------ --- การล้างตาราง Staging เพื่อประหยัดพื้นที่ระบบจัดเก็บข้อมูล (Cleanups) --- ------------------------------------------------------------ - --- ลบตารางแสดงข้อมูลสรุปรายวันของ Migration -DROP TABLE IF EXISTS migration_daily_summary; - --- ลบตารางสถานะสำหรับ AI Model Fallback State -DROP TABLE IF EXISTS migration_fallback_state; - --- ลบตารางแสดงประวัติข้อผิดพลาดการย้ายข้อมูล -DROP TABLE IF EXISTS migration_errors; - --- ลบตารางคิวตรวจสอบสำหรับเอกสาร -DROP TABLE IF EXISTS migration_review_queue; - --- ลบตารางความคืบหน้าของ Migration Progress -DROP TABLE IF EXISTS migration_progress; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-23-alter-migration-review-queue-enum.sql b/specs/03-Data-and-Storage/deltas/2026-05-23-alter-migration-review-queue-enum.sql deleted file mode 100644 index cde75b6f..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-23-alter-migration-review-queue-enum.sql +++ /dev/null @@ -1,15 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-23-alter-migration-review-queue-enum.sql --- Change Log: --- - 2026-05-23: เพิ่ม PENDING_REVIEW เข้า status ENUM ของ migration_review_queue (TypeORM-managed) --- n8n workflow ไม่ใช้ MySQL direct access แล้ว — ใช้ POST /api/ai/migration/queue/record แทน (ADR-023A) --- Delta: เพิ่ม PENDING_REVIEW ใน status column ของ migration_review_queue --- Date: 2026-05-23 --- Related: ADR-028, ADR-023A --- รันก่อน deploy backend ที่มี PENDING_REVIEW ใน MigrationReviewRecordStatus enum -ALTER TABLE migration_review_queue -MODIFY COLUMN STATUS ENUM( - 'PENDING', - 'PENDING_REVIEW', - 'IMPORTED', - 'REJECTED' - ) DEFAULT 'PENDING'; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.rollback.sql deleted file mode 100644 index 4a3fe1dc..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.rollback.sql +++ /dev/null @@ -1,10 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.rollback.sql --- Change Log: --- - 2026-05-24: Rollback สำหรับลบ job_id ออกจาก migration_errors - --- Delta Rollback: ลบคอลัมน์ job_id สำหรับ Migration Error Log --- Related ADR: ADR-009, ADR-023A, ADR-028 - -ALTER TABLE migration_errors -DROP INDEX idx_migration_errors_job_id, -DROP COLUMN job_id; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.sql b/specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.sql deleted file mode 100644 index 6631508a..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.sql +++ /dev/null @@ -1,11 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-24-add-migration-errors-job-id.sql --- Change Log: --- - 2026-05-24: เพิ่ม job_id ใน migration_errors เพื่อผูก error log กับ BullMQ AI job - --- Delta: เพิ่มคอลัมน์ job_id สำหรับ Migration Error Log --- Related ADR: ADR-009, ADR-023A, ADR-028 - -ALTER TABLE migration_errors -ADD COLUMN job_id VARCHAR(100) NULL COMMENT 'BullMQ Job ID สำหรับ trace error ของ AI migration' -AFTER error_message, -ADD INDEX idx_migration_errors_job_id (job_id); diff --git a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.rollback.sql deleted file mode 100644 index 2e1b784e..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.rollback.sql +++ /dev/null @@ -1,8 +0,0 @@ --- Rollback: Drop ai_available_models table --- Date: 2026-05-25 - --- Remove system setting first -DELETE FROM system_settings WHERE setting_key = 'AI_ACTIVE_MODEL'; - --- Drop table -DROP TABLE IF EXISTS ai_available_models; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.sql b/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.sql deleted file mode 100644 index bd13a77d..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-available-models.sql +++ /dev/null @@ -1,43 +0,0 @@ --- Delta: Create ai_available_models table for dynamic AI model selection --- Date: 2026-05-25 --- Author: AI Assistant --- Related: ADR-027 AI Admin Console - Dynamic model control - --- Create table for available AI models -CREATE TABLE IF NOT EXISTS ai_available_models ( - id INT AUTO_INCREMENT PRIMARY KEY, - model_name VARCHAR(100) NOT NULL COMMENT 'ชื่อโมเดล เช่น gemma4:e2b, gemma4:e4b', - model_version VARCHAR(50) NOT NULL COMMENT 'เวอร์ชั่นของโมเดล', - description VARCHAR(500) NULL COMMENT 'รายละเอียดโมเดล', - vram_gb DECIMAL(4,2) NULL COMMENT 'VRAM ที่ใช้โดยประมาณ (GB)', - is_active BOOLEAN DEFAULT TRUE COMMENT 'สถานะใช้งาน', - is_default BOOLEAN DEFAULT FALSE COMMENT 'โมเดลเริ่มต้น', - created_by INT NULL, - updated_by INT NULL, - created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3), - updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), - deleted_at DATETIME(3) NULL, - - UNIQUE KEY uk_model_name (model_name), - INDEX idx_is_active (is_active), - INDEX idx_is_default (is_default) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci -COMMENT='ตารางเก็บรายการโมเดล AI ที่ให้เลือกใช้งานในระบบ (ADR-027)'; - --- Insert default models per ADR-023A -INSERT INTO ai_available_models (model_name, model_version, description, vram_gb, is_active, is_default) VALUES -('gemma4:e2b', 'e2b', 'Gemma 4 E2B - 2-bit quantized, ~2GB VRAM, recommended per ADR-023A', 2.00, TRUE, TRUE), -('gemma4:e4b', 'e4b', 'Gemma 4 E4B - 4-bit quantized, ~4GB VRAM', 4.00, TRUE, FALSE); - --- Add system setting for active model (reference to ai_available_models) -INSERT INTO system_settings (setting_key, setting_value, data_type, category, description, is_public, created_at, updated_at) -SELECT - 'AI_ACTIVE_MODEL', - (SELECT model_name FROM ai_available_models WHERE is_default = TRUE LIMIT 1), - 'string', - 'ai', - 'โมเดล AI ที่ใช้งานอยู่ในระบบ (global)', - TRUE, - CURRENT_TIMESTAMP(3), - CURRENT_TIMESTAMP(3) -WHERE NOT EXISTS (SELECT 1 FROM system_settings WHERE setting_key = 'AI_ACTIVE_MODEL'); diff --git a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.rollback.sql deleted file mode 100644 index 5284b9fc..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.rollback.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Rollback: ลบตาราง ai_prompts (ADR-029) --- Date: 2026-05-25 - -DROP TABLE IF EXISTS ai_prompts; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.sql b/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.sql deleted file mode 100644 index 349ffa6e..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-25-create-ai-prompts.sql +++ /dev/null @@ -1,73 +0,0 @@ --- Delta: สร้างตาราง ai_prompts สำหรับ Dynamic Prompt Management --- Date: 2026-05-25 --- Related ADR: ADR-029 --- Applied in: v1.9.0 -> v1.9.6 --- ------------------------------------------------------------ --- การเปลี่ยนแปลงโครงสร้างฐานข้อมูล (Schema changes) --- ------------------------------------------------------------ -CREATE TABLE IF NOT EXISTS ai_prompts ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ภายใน (ไม่ expose ใน API)', - prompt_type VARCHAR(50) NOT NULL COMMENT 'ประเภท prompt เช่น ocr_extraction', - version_number INT NOT NULL COMMENT 'เลข version ต่อเนื่องต่อ prompt_type (1, 2, 3...)', - template TEXT NOT NULL COMMENT 'prompt template ที่มี {{ocr_text}} placeholder บังคับ', - field_schema JSON NULL COMMENT 'definition ของ fields ที่คาดหวังในผลลัพธ์ JSON', - is_active TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = version นี้ใช้งานจริงทั้ง sandbox และ migrate-document (1 per prompt_type)', - test_result_json JSON NULL COMMENT 'ผลลัพธ์ JSON จาก sandbox run ล่าสุด (auto-save โดย processor)', - manual_note TEXT NULL COMMENT 'หมายเหตุ/annotation จาก admin (manual input)', - last_tested_at TIMESTAMP NULL COMMENT 'เวลาที่ sandbox รันครั้งล่าสุดสำหรับ version นี้', - activated_at TIMESTAMP NULL COMMENT 'เวลาที่ version นี้ถูก activate เป็น active', - created_by INT NOT NULL COMMENT 'user_id ของผู้สร้าง version นี้', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY uk_type_version (prompt_type, version_number), - INDEX idx_prompt_type_active (prompt_type, is_active), - FOREIGN KEY (created_by) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตาราง versioned prompt templates สำหรับ OCR extraction (ADR-029)'; - --- ------------------------------------------------------------ --- Seed: migrate hardcoded prompt เป็น active version 1 --- (8 fields รวม category, tags, summary — ใช้ร่วมกันทั้ง sandbox และ migrate-document) --- ------------------------------------------------------------ -INSERT INTO ai_prompts ( - prompt_type, - version_number, - template, - field_schema, - is_active, - manual_note, - activated_at, - created_by - ) -VALUES ( - 'ocr_extraction', - 1, - 'You are a professional document intelligence engine.\nAnalyze the following OCR text extracted from a project document and extract the metadata fields.\n\nOCR TEXT:\n{{ocr_text}}\n\nExtract these fields:\n1. documentNumber: The official document number or code. If not found, return null.\n2. subject: The main subject, title, or topic of the document. If not found, return null.\n3. discipline: Must be exactly one of: "Civil", "Mechanical", "Electrical", "Architectural", or null if not specified.\n4. category: Must be exactly one of: "Correspondence", "Transmittal", "Circulation", "RFA", "Shop Drawing", "Contract Drawing", or null if not specified.\n5. date: The issue/document date in YYYY-MM-DD format. If not found, return null.\n6. confidence: A float between 0.0 and 1.0 indicating your confidence in this extraction.\n7. tags: An array of tags/keywords (strings) that describe the document.\n8. summary: A short 1-2 sentence summary of the document contents.\nReturn ONLY a valid JSON object matching this schema. Do NOT include markdown code blocks, HTML, or any conversational text. Example:\n{\n "documentNumber": "LCBP3-CIV-001",\n "subject": "Foundation Inspection Report",\n "discipline": "Civil",\n "category": "Correspondence",\n "date": "2026-05-20",\n "confidence": 0.95,\n "tags": ["foundation", "inspection", "concrete"],\n "summary": "This document is a foundation inspection report for the LCBP3 project, confirming concrete strength."\n}', - JSON_OBJECT( - 'documentNumber', - 'string|null', - 'subject', - 'string|null', - 'discipline', - 'enum:Civil,Mechanical,Electrical,Architectural|null', - 'category', - 'enum:Correspondence,Transmittal,Circulation,RFA,Shop Drawing,Contract Drawing|null', - 'date', - 'date:YYYY-MM-DD|null', - 'confidence', - 'float:0-1', - 'tags', - 'string[]', - 'summary', - 'string|null' - ), - 1, - 'Migrated from hardcoded prompt in processSandboxExtract / processMigrateDocument (ADR-029)', - CURRENT_TIMESTAMP, - ( - SELECT user_id - FROM users - WHERE username = 'superadmin' - LIMIT 1 - ) -- PREREQUISITE: user seed (user.seed.ts) MUST run before this delta; - -- 'superadmin' is always the first user inserted per standard deployment order - ) ON DUPLICATE KEY -UPDATE prompt_type = prompt_type; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.rollback.sql deleted file mode 100644 index 915221f4..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.rollback.sql +++ /dev/null @@ -1,15 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.rollback.sql --- Change Log: --- - 2026-05-25: Rollback — ลบ ai.* permissions ออกจาก role_id=1 (Superadmin) --- ========================================================== -DELETE rp FROM role_permissions rp -JOIN permissions p ON rp.permission_id = p.permission_id -WHERE rp.role_id = 1 -AND p.permission_name IN ( - 'ai.suggest', - 'ai.rag_query', - 'ai.migration_manage', - 'ai.audit_log_delete', - 'ai.read_analytics', - 'ai.delete_audit' -); diff --git a/specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.sql b/specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.sql deleted file mode 100644 index ff806342..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.sql +++ /dev/null @@ -1,23 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-25-grant-ai-permissions-to-superadmin.sql --- Change Log: --- - 2026-05-25: Grant ai.* permissions ให้ Superadmin (role_id=1) ที่ขาดหายไปจาก seed --- ========================================================== --- Root Cause: --- Seed script (lcbp3-v1.9.0-seed-permissions.sql) รัน: --- INSERT INTO role_permissions SELECT 1, permission_id FROM permissions WHERE is_active = 1 --- ก่อนที่ ai.* permissions (permission_id 181-186) จะถูก INSERT เข้า permissions table --- ทำให้ role_id=1 (Superadmin) ไม่มี ai.* ใน role_permissions --- ผลกระทบ: migration_bot (user_id=5, role_id=1) ถูก RbacGuard block ที่ POST /api/ai/jobs --- ========================================================== -INSERT IGNORE INTO role_permissions (role_id, permission_id) -SELECT 1, permission_id -FROM permissions -WHERE permission_name IN ( - 'ai.suggest', - 'ai.rag_query', - 'ai.migration_manage', - 'ai.audit_log_delete', - 'ai.read_analytics', - 'ai.delete_audit' -) -AND is_active = 1; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.rollback.sql deleted file mode 100644 index c15015e7..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.rollback.sql +++ /dev/null @@ -1,28 +0,0 @@ --- Rollback Delta: Remove context_config & publicId from ai_prompts & Revert CC whitespace typo --- Date: 2026-05-27 --- Related ADR: ADR-030, ADR-019 --- ------------------------------------------------------------ --- 1. ย้อนกลับตาราง correspondence_recipients ให้มี whitespace ENUM เหมือนเดิม -ALTER TABLE correspondence_recipients -MODIFY COLUMN recipient_type ENUM('TO', 'CC ') NOT NULL COMMENT 'ประเภทผู้รับ (TO หรือ CC)'; - --- ย้อนคืนข้อมูลจาก CC เป็น CC -UPDATE correspondence_recipients -SET recipient_type = 'CC ' -WHERE recipient_type = 'CC'; - --- 2. ลบคอลัมน์ publicId จาก ai_prompts -ALTER TABLE ai_prompts DROP COLUMN public_id; - --- 3. ลบคอลัมน์ context_config จาก ai_prompts -ALTER TABLE ai_prompts DROP COLUMN context_config; - --- 4. ลบ Seed Prompt Version 2 และเปิดใช้งาน Version 1 แทน -DELETE FROM ai_prompts -WHERE prompt_type = 'ocr_extraction' - AND version_number = 2; - -UPDATE ai_prompts -SET is_active = 1 -WHERE prompt_type = 'ocr_extraction' - AND version_number = 1; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.sql b/specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.sql deleted file mode 100644 index b0acfc0b..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-27-add-context-aware-prompts-and-cleanup.sql +++ /dev/null @@ -1,108 +0,0 @@ --- Delta: Add context_config & publicId to ai_prompts & Clean up CC whitespace typo --- Date: 2026-05-27 --- Related ADR: ADR-030, ADR-019 --- Applied in: v1.9.7 -> main --- ------------------------------------------------------------ --- 1. ล้าง whitespace typo 'CC ' ในตารางผู้รับเอกสาร --- อัปเดตข้อมูลเก่าให้เรียบร้อยก่อนเพื่อไม่ให้เกิดข้อผิดพลาดในการเปลี่ยน Schema -UPDATE correspondence_recipients -SET recipient_type = 'CC' -WHERE recipient_type = 'CC '; - --- แก้ไขประเภทคอลัมน์ของ correspondence_recipients ตัดช่องว่างออก -ALTER TABLE correspondence_recipients -MODIFY COLUMN recipient_type ENUM('TO', 'CC') NOT NULL COMMENT 'ประเภทผู้รับ (TO หรือ CC)'; - --- 2. เพิ่มคอลัมน์ publicId (UUID) ใน ai_prompts ตาม ADR-019 -ALTER TABLE ai_prompts -ADD COLUMN public_id UUID NULL UNIQUE COMMENT 'Public UUID สำหรับ API exposure (ADR-019)'; - --- สร้าง UUID สำหรับ records ที่มีอยู่เดิม -UPDATE ai_prompts -SET public_id = UUID() -WHERE public_id IS NULL; - --- ตั้งค่า publicId เป็น NOT NULL หลังจาก populate ข้อมูลเดิม -ALTER TABLE ai_prompts -MODIFY COLUMN public_id UUID NOT NULL UNIQUE COMMENT 'Public UUID สำหรับ API exposure (ADR-019)'; - --- 3. เพิ่มคอลัมน์ context_config JSON ใน ai_prompts -ALTER TABLE ai_prompts -ADD COLUMN context_config JSON NULL COMMENT 'Configuration สำหรับ context ที่ backend ต้องส่งให้ AI (filter, pageSize, language, etc.)'; - --- 4. อัปเดต Seed Prompt Version 2 (ภาษาไทย พร้อม Context-Aware layout) --- ปิดใช้งานเวอร์ชัน 1 -UPDATE ai_prompts -SET is_active = 0 -WHERE prompt_type = 'ocr_extraction' - AND version_number = 1; - --- แทรก Prompt Version 2 (ภาษาไทย) เข้าสู่ตาราง ai_prompts -INSERT INTO ai_prompts ( - prompt_type, - version_number, - template, - field_schema, - is_active, - context_config, - manual_note, - activated_at, - created_by - ) -VALUES ( - 'ocr_extraction', - 2, - 'คุณคือเอนจิ้นสกัดข้อมูลอัจฉริยะ (Document Intelligence Engine) -วิเคราะห์ข้อความ OCR ที่ได้รับจากเอกสารของโครงการ Laem Chabang Port Phase 3 และสกัดข้อมูลเมตาดาต้าให้ออกมาเป็น JSON object ที่ถูกต้องตามโครงสร้างที่กำหนด - -ข้อความ OCR ที่สกัดได้: -{{ocr_text}} - -ข้อมูลอ้างอิงของระบบ (Master Data Context): -{{master_data_context}} - -กฎการสกัดข้อมูล: -1. วิเคราะห์และจับคู่ข้อมูลจากข้อความ OCR กับข้อมูลอ้างอิงที่ระบุใน Master Data Context เสมอ -2. สำหรับโครงการ (project) ให้ค้นหาและสกัดส่งกลับเป็น UUID ของโครงการ (projectPublicId) -3. สำหรับประเภทเอกสารโต้ตอบ (correspondence type) ให้สกัดรหัสส่งกลับมา (correspondenceTypeCode) เช่น RFA, Transmittal -4. สำหรับสาขางาน (discipline) ให้ส่งคืนรหัสส่งกลับมา (disciplineCode) เช่น GEN, STR -5. สำหรับหน่วยงานผู้ส่ง (originator) ค้นหาจาก availableOrganizations และส่งกลับมาเป็น UUID (originatorOrganizationPublicId) -6. สำหรับหน่วยงานผู้รับ (recipients) ให้ส่งกลับมาเป็นรายการ Array ของออบเจกต์ ซึ่งมี UUID ขององค์กร (organizationPublicId) และประเภทผู้รับ (recipientType: "TO" หรือ "CC") เสมอ -7. สำหรับหัวข้อเอกสาร (subject) ให้สกัดหัวข้อหรือชื่อเรื่องของเอกสารภาษาไทยหรือภาษาอังกฤษ -8. วันที่ของเอกสาร (documentDate) ให้ส่งคืนในรูปแบบ YYYY-MM-DD -9. รายการแท็ก (tags) สกัดคำสำคัญหรือคำแนะนำ Tags (สอดคล้องกับ availableTags หากมี) -10. สรุปความเนื้อหา (summary) เขียนสรุปรายละเอียดเอกสารสั้นกระชับ 4-5 ประโยคเป็นภาษาไทยอย่างสละสลวย -11. confidence: ค่าความมั่นใจในการสกัดข้อมูลนี้ (ทศนิยมระหว่าง 0.0 ถึง 1.0) - -ส่งคืนคำตอบเฉพาะ JSON Object ที่ถูกต้องเท่านั้น ห้ามใส่บล็อกโค้ด markdown หรือคำอธิบายเพิ่มเติมใดๆ -โครงสร้าง JSON ผลลัพธ์: -{ - "projectPublicId": "string หรือ null", - "correspondenceTypeCode": "string หรือ null", - "disciplineCode": "string หรือ null", - "originatorOrganizationPublicId": "string หรือ null", - "recipients": [ - { - "organizationPublicId": "string", - "recipientType": "TO หรือ CC" - } - ], - "subject": "string หรือ null", - "documentDate": "string:YYYY-MM-DD หรือ null", - "tags": ["string"], - "summary": "string หรือ null", - "confidence": 0.95 -}', - '{"projectPublicId":"string|null","correspondenceTypeCode":"string|null","disciplineCode":"string|null","originatorOrganizationPublicId":"string|null","recipients":"array:object(organizationPublicId:string|null,recipientType:string|null)","subject":"string|null","documentDate":"date:YYYY-MM-DD|null","tags":"string[]","summary":"string|null","confidence":"float:0-1"}', - 1, - '{"filter":null,"pageSize":3,"language":"th","outputLanguage":"th"}', - 'Seed Prompt ภาษาไทย เวอร์ชัน 2 รองรับ Context-Aware และการล้าง Typo CC (ADR-030)', - CURRENT_TIMESTAMP, - ( - SELECT user_id - FROM users - WHERE username = 'superadmin' - LIMIT 1 - ) - ) ON DUPLICATE KEY -UPDATE prompt_type = prompt_type; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-30-add-ai-prompts-publicId.sql b/specs/03-Data-and-Storage/deltas/2026-05-30-add-ai-prompts-publicId.sql deleted file mode 100644 index 59d360d9..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-30-add-ai-prompts-publicId.sql +++ /dev/null @@ -1,21 +0,0 @@ --- Delta: เพิ่ม publicId column ให้ ai_prompts table เพื่อ ADR-019 compliance --- Date: 2026-05-30 --- Related ADR: ADR-019 (Hybrid UUID Strategy) --- Applied in: v1.9.6 -> v1.9.7 --- ------------------------------------------------------------ --- การเปลี่ยนแปลงโครงสร้างฐานข้อมูล (Schema changes) --- ------------------------------------------------------------ --- เพิ่ม publicId column (MariaDB native UUID type ตาม ADR-019) -ALTER TABLE ai_prompts -ADD COLUMN publicId CHAR(36) UNIQUE COMMENT 'UUIDv7 public identifier (ADR-019)'; - --- Generate UUIDv7 for existing records --- ใช้ MariaDB UUID() function สำหรับสร้าง UUID v4 (fallback) --- หมายเหตุ: UUIDv7 จริงๆ ต้องใช้ timestamp แต่ MariaDB ยังไม่รองรับ UUIDv7 native --- ใช้ UUID() เป็น workaround และจะถูก replace ด้วย UUIDv7 จริงเมื่อ migrate document -UPDATE ai_prompts -SET publicId = UUID() -WHERE publicId IS NULL; - --- Add index for publicId (optional but recommended for performance) -CREATE INDEX idx_ai_prompts_publicId ON ai_prompts(publicId); diff --git a/specs/03-Data-and-Storage/deltas/2026-05-30-add-typhoon-ocr-prompt.sql b/specs/03-Data-and-Storage/deltas/2026-05-30-add-typhoon-ocr-prompt.sql deleted file mode 100644 index 1dfb58df..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-30-add-typhoon-ocr-prompt.sql +++ /dev/null @@ -1,50 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-30-add-typhoon-ocr-prompt.sql --- เพิ่ม Typhoon OCR System Prompt ลงใน ai_prompts table --- ตาม ADR-029: Dynamic Prompt Management, ADR-032: Typhoon OCR Integration --- Change Log: --- - 2026-05-30: Initial seed สำหรับ typhoon_ocr_system prompt (T005) --- - 2026-05-30: Fix: เพิ่ม public_id (UUID) และ context_config (NULL) --- ai_prompts entity มี publicId NOT NULL column ตาม ADR-019 (เพิ่มเมื่อ 2026-05-27) --- ใช้ UUID() ของ MariaDB เพื่อสร้าง UUIDv4 ที่ valid - -INSERT INTO ai_prompts ( - public_id, - prompt_type, - version_number, - template, - field_schema, - context_config, - is_active, - manual_note, - activated_at, - created_by -) -SELECT - UUID(), - 'typhoon_ocr_system', - 1, - 'สกัดข้อความภาษาไทยและอังกฤษทั้งหมดจากภาพนี้อย่างถูกต้อง รักษาโครงสร้างบรรทัดและการเว้นวรรคให้ใกล้เคียงต้นฉบับมากที่สุด ห้ามเพิ่มคำอธิบายใดๆ', - JSON_OBJECT( - 'type', 'system_prompt', - 'model', 'scb10x/typhoon-ocr-3b', - 'temperature', 0.0, - 'top_p', 0.9, - 'repeat_penalty', 1.0, - 'keep_alive', 0 - ), - NULL, - 1, - 'System prompt สำหรับ Typhoon OCR-3B เพื่อสกัดข้อความภาษาไทย/อังกฤษจากภาพเอกสาร (ADR-032)', - CURRENT_TIMESTAMP, - ( - SELECT user_id - FROM users - WHERE username = 'superadmin' - LIMIT 1 - ) -WHERE NOT EXISTS ( - SELECT 1 FROM ai_prompts - WHERE prompt_type = 'typhoon_ocr_system' - AND version_number = 1 -) -ON DUPLICATE KEY UPDATE prompt_type = prompt_type; diff --git a/specs/03-Data-and-Storage/deltas/2026-05-30-extend-ai-audit-logs.sql b/specs/03-Data-and-Storage/deltas/2026-05-30-extend-ai-audit-logs.sql deleted file mode 100644 index beaf77e8..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-30-extend-ai-audit-logs.sql +++ /dev/null @@ -1,21 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-05-30-extend-ai-audit-logs.sql --- เพิ่ม fields สำหรับ Typhoon OCR integration ใน ai_audit_logs --- ตาม ADR-032: modelType, vramUsageMB, cacheHit --- Change Log: --- - 2026-05-30: Initial delta สำหรับ Typhoon OCR audit fields (T004) - --- เพิ่ม modelType: ระบุประเภทของ model ที่ใช้ (tesseract, typhoon-ocr-3b, typhoon2.1-gemma3-4b) -ALTER TABLE ai_audit_logs - ADD COLUMN IF NOT EXISTS model_type VARCHAR(50) NULL COMMENT 'ประเภท OCR/LLM model ที่ใช้ เช่น tesseract, typhoon-ocr-3b' AFTER model_name; - --- เพิ่ม vramUsageMB: การใช้ VRAM จริง (MB) หลังประมวลผล -ALTER TABLE ai_audit_logs - ADD COLUMN IF NOT EXISTS vram_usage_mb INT NULL COMMENT 'VRAM ที่ใช้จริง (MB) ณ เวลาประมวลผล' AFTER model_type; - --- เพิ่ม cacheHit: ระบุว่าผลลัพธ์นี้มาจาก Redis cache หรือ OCR จริง -ALTER TABLE ai_audit_logs - ADD COLUMN IF NOT EXISTS cache_hit TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = ผลลัพธ์มาจาก Redis cache, 0 = OCR ใหม่' AFTER vram_usage_mb; - --- เพิ่ม index สำหรับ model_type เพื่อ analytics queries -ALTER TABLE ai_audit_logs - ADD INDEX IF NOT EXISTS idx_ai_audit_model_type (model_type); diff --git a/specs/03-Data-and-Storage/deltas/2026-05-30-seed-typhoon-ai-models.sql b/specs/03-Data-and-Storage/deltas/2026-05-30-seed-typhoon-ai-models.sql deleted file mode 100644 index 463b5e23..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-05-30-seed-typhoon-ai-models.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Delta: Seed Typhoon model option into ai_available_models --- Date: 2026-05-30 --- Related: ADR-027, ADR-032, specs/200-fullstacks/232-typhoon-ocr-integration - -INSERT INTO ai_available_models ( - model_name, - model_version, - description, - vram_gb, - is_active, - is_default -) -SELECT - 'typhoon2.1-gemma3-4b', - '4b', - 'Typhoon 2.1 Gemma3 4B - Thai-focused local LLM option for AI Admin Console', - 4.50, - TRUE, - FALSE -WHERE NOT EXISTS ( - SELECT 1 - FROM ai_available_models - WHERE model_name = 'typhoon2.1-gemma3-4b' -); diff --git a/specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.rollback.sql deleted file mode 100644 index cecd8dce..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.rollback.sql +++ /dev/null @@ -1,11 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.rollback.sql --- Change Log: --- - 2026-06-02: ลบคอลัมน์ temp_attachment_id ออกจากตาราง migration_review_queue - --- Rollback Delta: ลบคอลัมน์ temp_attachment_id ออกจากตาราง migration_review_queue --- Date: 2026-06-02 --- Related ADR: ADR-028, ADR-023A --- Applied in: v1.9.8 - -ALTER TABLE migration_review_queue -DROP COLUMN temp_attachment_id; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.sql b/specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.sql deleted file mode 100644 index eb4b46b5..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.sql +++ /dev/null @@ -1,16 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-02-add-temp-attachment-id-to-migration-review-queue.sql --- Change Log: --- - 2026-06-02: เพิ่มคอลัมน์ temp_attachment_id ในตาราง migration_review_queue เพื่อแก้บั๊ก CleanupTempFilesWorker - --- Delta: เพิ่มคอลัมน์ temp_attachment_id ในตาราง migration_review_queue --- Date: 2026-06-02 --- Related ADR: ADR-028, ADR-023A --- Applied in: v1.9.8 - --- ------------------------------------------------------------ --- การปรับปรุงตาราง migration_review_queue (Schema changes) --- ------------------------------------------------------------ - -ALTER TABLE migration_review_queue -ADD COLUMN temp_attachment_id INT NULL COMMENT 'Temporary attachment ID referencing attachments.id (ADR-028)' -AFTER STATUS; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.rollback.sql deleted file mode 100644 index def17572..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.rollback.sql +++ /dev/null @@ -1,18 +0,0 @@ --- Rollback: Revert ai_available_models to gemma4 stack (undo ADR-034 delta) --- Date: 2026-06-03 --- Pair: 2026-06-03-update-ai-available-models-typhoon.sql - --- 1. Remove Typhoon models -DELETE FROM ai_available_models -WHERE model_name IN ('typhoon2.5-np-dms:latest', 'typhoon-np-dms-ocr:latest'); - --- 2. Restore gemma4:e2b as default -UPDATE ai_available_models -SET is_default = TRUE, updated_at = NOW() -WHERE model_name = 'gemma4:e2b'; - --- 3. Revert system_settings active model -UPDATE system_settings -SET setting_value = 'gemma4:e2b', - updated_at = NOW() -WHERE setting_key = 'AI_ACTIVE_MODEL'; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.sql b/specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.sql deleted file mode 100644 index 5fe84c63..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.sql +++ /dev/null @@ -1,49 +0,0 @@ --- Delta: Update ai_available_models for Thai-Optimized Model Stack (ADR-034) --- Date: 2026-06-03 --- Author: AI Assistant --- Related: ADR-034 — Thai-Optimized AI Model Stack, supersedes ADR-023A Section 2.1 --- Rollback: 2026-06-03-update-ai-available-models-typhoon.rollback.sql - --- 1. Insert new main model (typhoon2.5-np-dms) as default, demote old defaults -INSERT INTO ai_available_models (model_name, model_version, description, vram_gb, is_active, is_default) -VALUES ( - 'typhoon2.5-np-dms:latest', - 'latest', - 'Thai-optimized main AI model based on typhoon2.5-qwen3-4b (~2.5GB VRAM, standby mode) — ADR-034', - 2.50, - TRUE, - TRUE -) -ON DUPLICATE KEY UPDATE - description = VALUES(description), - vram_gb = VALUES(vram_gb), - is_active = TRUE, - is_default = TRUE, - updated_at = NOW(); - --- Demote old gemma4 models from default status -UPDATE ai_available_models -SET is_default = FALSE, updated_at = NOW() -WHERE model_name IN ('gemma4:e2b', 'gemma4:e4b', 'typhoon2.1-gemma3-4b'); - --- 2. Insert OCR model (typhoon-np-dms-ocr) — not default, keep_alive=0 (unload after each job) -INSERT INTO ai_available_models (model_name, model_version, description, vram_gb, is_active, is_default) -VALUES ( - 'typhoon-np-dms-ocr:latest', - 'latest', - 'Thai OCR model based on typhoon-ocr1.5-3b (~3.2GB VRAM, unloads after each job) — ADR-034', - 3.20, - TRUE, - FALSE -) -ON DUPLICATE KEY UPDATE - description = VALUES(description), - vram_gb = VALUES(vram_gb), - is_active = TRUE, - updated_at = NOW(); - --- 3. Update active model in system_settings to typhoon2.5-np-dms:latest -UPDATE system_settings -SET setting_value = 'typhoon2.5-np-dms:latest', - updated_at = NOW() -WHERE setting_key = 'AI_ACTIVE_MODEL'; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.rollback.sql deleted file mode 100644 index 65618f4b..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.rollback.sql +++ /dev/null @@ -1,8 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.rollback.sql --- Rollback การเพิ่ม Prompt สำหรับ Semantic Chunking --- Change Log: --- - 2026-06-05: Initial rollback (T002) - -DELETE FROM ai_prompts -WHERE prompt_type = 'rag_chunking' - AND version_number = 1; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.sql b/specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.sql deleted file mode 100644 index 4bb6b770..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.sql +++ /dev/null @@ -1,47 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-05-add-rag-chunking-prompt.sql --- เพิ่ม Prompt สำหรับ Semantic Chunking ลงใน ai_prompts table --- ตาม ADR-035 และ FR-004a --- Change Log: --- - 2026-06-05: Initial seed สำหรับ rag_chunking prompt (T002) - -INSERT INTO ai_prompts ( - public_id, - prompt_type, - version_number, - template, - field_schema, - context_config, - is_active, - manual_note, - activated_at, - created_by -) -SELECT - UUID(), - 'rag_chunking', - 1, - 'คุณเป็นผู้ช่วยวิเคราะห์เอกสารและแบ่งเนื้อหาเป็นส่วนๆ ตามหัวข้อ (Semantic Chunking)\nหน้าที่ของคุณคืออ่านข้อความเอกสารที่ได้จาก OCR ด้านล่างนี้ แล้วแบ่งเอกสารออกเป็นชิ้นๆ (Chunks) ตามเนื้อหาและหัวข้อหลัก\nสำหรับแต่ละส่วนที่คุณแบ่ง ให้ล้อมรอบด้วยแท็ก [เนื้อหาในส่วนนี้] \n\nกฎในการแบ่งข้อมูล:\n1. ห้ามแก้ไขคำหรือข้อความใดๆ ในเอกสารเด็ดขาด ให้ใช้ข้อความดั้งเดิมจาก OCR ทั้งหมด\n2. พยายามแบ่งส่วนตามขอบเขตเนื้อหาที่สมเหตุสมผล เช่น เมื่อขึ้นหัวข้อใหม่ หรือส่วนเนื้อความที่คนละประเด็นกัน\n3. แต่ละส่วนควรมีความยาวที่อ่านเข้าใจได้และไม่ยาวจนเกินไป\n4. ห้ามตอบข้อความบทนำหรือบทสรุปใดๆ นอกเหนือจากแท็ก และข้อความภายในแท็ก\n\nข้อความเอกสาร OCR:\n{{ocr_text}}', - JSON_OBJECT( - 'type', 'semantic_chunking', - 'model', 'typhoon2.5-np-dms:latest', - 'temperature', 0.1, - 'top_p', 0.9, - 'repeat_penalty', 1.1, - 'keep_alive', -1 - ), - NULL, - 1, - 'Prompt สำหรับแบ่งข้อความจาก OCR เป็น Chunk ตามหัวข้อความหมายด้วย typhoon2.5 (ADR-035)', - CURRENT_TIMESTAMP, - ( - SELECT user_id - FROM users - WHERE username = 'superadmin' - LIMIT 1 - ) -WHERE NOT EXISTS ( - SELECT 1 FROM ai_prompts - WHERE prompt_type = 'rag_chunking' - AND version_number = 1 -) -ON DUPLICATE KEY UPDATE prompt_type = prompt_type; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-06-add-ai-prompts-public-id.sql b/specs/03-Data-and-Storage/deltas/2026-06-06-add-ai-prompts-public-id.sql deleted file mode 100644 index fdf95e7f..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-06-add-ai-prompts-public-id.sql +++ /dev/null @@ -1,23 +0,0 @@ --- Delta: เพิ่ม public_id และ context_config columns ใน ai_prompts --- Date: 2026-06-06 --- Related ADR: ADR-019 (UUID strategy), ADR-029 (Dynamic Prompts) --- ------------------------------------------------------------ --- การเปลี่ยนแปลงโครงสร้างฐานข้อมูล (Schema changes) --- ------------------------------------------------------------ - --- เพิ่ม public_id column (UUIDv7) สำหรับ ADR-019 compliance -ALTER TABLE ai_prompts -ADD COLUMN public_id UUID UNIQUE COMMENT 'Public UUID สำหรับ API (ADR-019)'; - --- เพิ่ม context_config column สำหรับ ADR-029 context filtering -ALTER TABLE ai_prompts -ADD COLUMN context_config JSON NULL COMMENT 'Configuration สำหรับ Master Data context filtering (project/contract scope)'; - --- สร้าง UUID สำหรับ records ที่มีอยู่แล้ว -UPDATE ai_prompts -SET public_id = UUID() -WHERE public_id IS NULL; - --- ตั้ง public_id เป็น NOT NULL หลังจาก populate ครบแล้ว -ALTER TABLE ai_prompts -MODIFY COLUMN public_id UUID NOT NULL; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.rollback.sql deleted file mode 100644 index 07ba79e3..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.rollback.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Rollback: ลบตาราง ai_execution_profiles --- Date: 2026-06-11 --- Related Delta: 2026-06-11-create-ai-execution-profiles.sql --- ------------------------------------------------------------ - -DROP TABLE IF EXISTS ai_execution_profiles; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.sql b/specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.sql deleted file mode 100644 index ba8b9584..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-11-create-ai-execution-profiles.sql +++ /dev/null @@ -1,38 +0,0 @@ --- Delta: สร้างตาราง ai_execution_profiles สำหรับ AI Runtime Policy Refactor --- Date: 2026-06-11 --- Related ADR: ADR-029, Feature-235 --- Source of defaults: docs/ai-profiles.md --- Applied in: v1.9.x (AI Runtime Policy Refactor cutover) --- ------------------------------------------------------------ - -CREATE TABLE IF NOT EXISTS ai_execution_profiles ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ภายใน (ไม่ expose ใน API)', - profile_name VARCHAR(50) NOT NULL COMMENT 'ชื่อ profile: interactive, standard, quality, deep-analysis', - temperature DECIMAL(4,3) NOT NULL COMMENT 'LLM temperature parameter', - top_p DECIMAL(4,3) NOT NULL COMMENT 'LLM top_p parameter', - max_tokens INT NOT NULL COMMENT 'Maximum tokens to generate', - num_ctx INT NOT NULL COMMENT 'Context window size (tokens)', - repeat_penalty DECIMAL(5,3) NOT NULL COMMENT 'Repeat penalty parameter', - keep_alive_seconds INT NOT NULL COMMENT 'Model keep_alive in seconds (0 = unload immediately)', - is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1 = profile นี้ใช้งานได้; 0 = disabled', - updated_by INT NULL COMMENT 'user_id ที่แก้ไขล่าสุด (NULL = seed default)', - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY uk_profile_name (profile_name), - INDEX idx_profile_active (profile_name, is_active), - FOREIGN KEY (updated_by) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'ตาราง execution profile parameters สำหรับ np-dms-ai (ADR-029, Feature-235); ค่า default จาก docs/ai-profiles.md'; - --- ------------------------------------------------------------ --- Seed: default profiles จาก docs/ai-profiles.md --- ------------------------------------------------------------ -INSERT INTO ai_execution_profiles ( - profile_name, temperature, top_p, max_tokens, num_ctx, repeat_penalty, keep_alive_seconds -) VALUES - ('interactive', 0.700, 0.900, 2048, 4096, 1.150, 300), -- keep_alive: "5m" - ('standard', 0.500, 0.800, 4096, 8192, 1.150, 600), -- keep_alive: "10m" - ('quality', 0.100, 0.950, 8192, 8192, 1.150, 600), -- keep_alive: "10m" - ('deep-analysis', 0.300, 0.850, 8192, 32768, 1.150, 0) -- keep_alive: "0" (admin sandbox only) -ON DUPLICATE KEY UPDATE - profile_name = profile_name; -- no-op: ไม่ overwrite ค่าที่ admin calibrate ไว้แล้ว diff --git a/specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.rollback.sql deleted file mode 100644 index b11f654e..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.rollback.sql +++ /dev/null @@ -1,19 +0,0 @@ --- Rollback: ลบ fields ที่เพิ่มสำหรับ AI Runtime Policy Refactor --- Date: 2026-06-11 --- Related Delta: 2026-06-11-extend-ai-audit-logs-runtime-policy.sql --- ------------------------------------------------------------ - -ALTER TABLE ai_audit_logs - DROP INDEX IF EXISTS idx_ai_audit_canonical_model; - -ALTER TABLE ai_audit_logs - DROP INDEX IF EXISTS idx_ai_audit_effective_profile; - -ALTER TABLE ai_audit_logs - DROP COLUMN IF EXISTS snapshot_params_json; - -ALTER TABLE ai_audit_logs - DROP COLUMN IF EXISTS canonical_model; - -ALTER TABLE ai_audit_logs - DROP COLUMN IF EXISTS effective_profile; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.sql b/specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.sql deleted file mode 100644 index 2d6a2c06..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-11-extend-ai-audit-logs-runtime-policy.sql +++ /dev/null @@ -1,37 +0,0 @@ --- Delta: เพิ่ม fields สำหรับ AI Runtime Policy Refactor ใน ai_audit_logs --- Date: 2026-06-11 --- Related ADR: ADR-023, ADR-029, Feature-235 --- Applied in: AI Runtime Policy Refactor cutover (big bang) --- ------------------------------------------------------------ --- เพิ่ม 3 columns: --- effective_profile — profile name ที่ backend กำหนด (interactive/standard/quality/deep-analysis) --- canonical_model — canonical model identity (np-dms-ai / np-dms-ocr) --- snapshot_params_json — parameters snapshot ณ เวลา dispatch (FR-A09) --- ------------------------------------------------------------ - --- effective_profile: ชื่อ ExecutionProfile ที่ backend กำหนดจาก job.type -ALTER TABLE ai_audit_logs - ADD COLUMN IF NOT EXISTS effective_profile VARCHAR(50) NULL - COMMENT 'ExecutionProfile ที่ backend กำหนด: interactive|standard|quality|deep-analysis (Feature-235)' - AFTER model_name; - --- canonical_model: ชื่อ canonical identity — ไม่ใช่ runtime tag -ALTER TABLE ai_audit_logs - ADD COLUMN IF NOT EXISTS canonical_model VARCHAR(50) NULL - COMMENT 'Canonical model identity: np-dms-ai หรือ np-dms-ocr (Feature-235, ADR-023)' - AFTER effective_profile; - --- snapshot_params_json: parameters ที่ถูก snapshot ตอน dispatch โดย AiPolicyService (FR-A09) --- { temperature, topP, maxTokens, numCtx, repeatPenalty, keepAliveSeconds } -ALTER TABLE ai_audit_logs - ADD COLUMN IF NOT EXISTS snapshot_params_json JSON NULL - COMMENT 'Runtime parameters snapshot ณ เวลา dispatch — ใช้จริงใน Ollama call (FR-A09, Feature-235)' - AFTER canonical_model; - --- index สำหรับ analytics queries ตาม profile -ALTER TABLE ai_audit_logs - ADD INDEX IF NOT EXISTS idx_ai_audit_effective_profile (effective_profile); - --- index สำหรับ canonical_model -ALTER TABLE ai_audit_logs - ADD INDEX IF NOT EXISTS idx_ai_audit_canonical_model (canonical_model); diff --git a/specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.rollback.sql b/specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.rollback.sql deleted file mode 100644 index 01cbc823..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.rollback.sql +++ /dev/null @@ -1,16 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.rollback.sql --- Change Log: --- - 2026-06-13: Rollback for ADR-036 OCR execution profile extension. - -DROP TABLE IF EXISTS ai_sandbox_profiles; - -DELETE FROM ai_execution_profiles -WHERE profile_name = 'ocr-extract' - AND canonical_model = 'np-dms-ocr'; - -ALTER TABLE ai_execution_profiles - MODIFY COLUMN max_tokens INT NOT NULL COMMENT 'Maximum tokens to generate', - MODIFY COLUMN num_ctx INT NOT NULL COMMENT 'Context window size (tokens)'; - -ALTER TABLE ai_execution_profiles - DROP COLUMN IF EXISTS canonical_model; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.sql b/specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.sql deleted file mode 100644 index b7e70439..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.sql +++ /dev/null @@ -1,58 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-13-extend-ai-execution-profiles-ocr.sql --- Change Log: --- - 2026-06-13: ADR-036 — extend execution profiles for OCR defaults and sandbox drafts. - --- ADR-036: production parameter store remains ai_execution_profiles. -ALTER TABLE ai_execution_profiles - ADD COLUMN IF NOT EXISTS canonical_model VARCHAR(20) NOT NULL DEFAULT 'np-dms-ai' - COMMENT 'Canonical model identity: np-dms-ai หรือ np-dms-ocr' - AFTER profile_name; - -ALTER TABLE ai_execution_profiles - MODIFY COLUMN max_tokens INT NULL COMMENT 'Maximum tokens to generate; NULL when model does not use token limit', - MODIFY COLUMN num_ctx INT NULL COMMENT 'Context window size; NULL when model does not use context window'; - -INSERT INTO ai_execution_profiles ( - profile_name, - canonical_model, - temperature, - top_p, - max_tokens, - num_ctx, - repeat_penalty, - keep_alive_seconds, - is_active -) VALUES ( - 'ocr-extract', - 'np-dms-ocr', - 0.100, - 0.100, - NULL, - NULL, - 1.100, - 0, - 1 -) ON DUPLICATE KEY UPDATE - canonical_model = VALUES(canonical_model), - max_tokens = VALUES(max_tokens), - num_ctx = VALUES(num_ctx); - -CREATE TABLE IF NOT EXISTS ai_sandbox_profiles ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ภายใน (ไม่ expose ใน API)', - profile_name VARCHAR(50) NOT NULL COMMENT 'ชื่อ profile หรือ model-defaults row เช่น ocr-extract', - canonical_model VARCHAR(20) NOT NULL DEFAULT 'np-dms-ai' COMMENT 'Canonical model identity: np-dms-ai หรือ np-dms-ocr', - temperature DECIMAL(4,3) NOT NULL COMMENT 'Model temperature parameter', - top_p DECIMAL(4,3) NOT NULL COMMENT 'Model top_p parameter', - max_tokens INT NULL COMMENT 'Maximum tokens to generate; NULL for OCR', - num_ctx INT NULL COMMENT 'Context window size; NULL for OCR', - repeat_penalty DECIMAL(5,3) NOT NULL COMMENT 'Repeat penalty parameter', - keep_alive_seconds INT NOT NULL COMMENT 'Model keep_alive in seconds; resource policy remains ADR-033', - updated_by INT NULL COMMENT 'user_id ที่แก้ไขล่าสุด', - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY uk_ai_sandbox_profile_name (profile_name), - INDEX idx_ai_sandbox_profile_model (canonical_model), - CONSTRAINT fk_ai_sandbox_profiles_updated_by - FOREIGN KEY (updated_by) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Sandbox draft execution profile parameters สำหรับ ADR-036'; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-14-create-ai-execution-profiles.sql b/specs/03-Data-and-Storage/deltas/2026-06-14-create-ai-execution-profiles.sql deleted file mode 100644 index bb283975..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-14-create-ai-execution-profiles.sql +++ /dev/null @@ -1,42 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-14-create-ai-execution-profiles.sql --- Change Log: --- - 2026-06-14: Created ai_execution_profiles and ai_sandbox_profiles tables (conforming to task T001) - -CREATE TABLE IF NOT EXISTS ai_execution_profiles ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ภายใน', - profile_name VARCHAR(50) NOT NULL COMMENT 'ชื่อ profile', - canonical_model VARCHAR(20) NOT NULL DEFAULT 'np-dms-ai' COMMENT 'Model identity', - temperature DECIMAL(4,3) NOT NULL COMMENT 'LLM temperature', - top_p DECIMAL(4,3) NOT NULL COMMENT 'LLM top_p', - max_tokens INT NULL COMMENT 'Maximum tokens', - num_ctx INT NULL COMMENT 'Context window size', - repeat_penalty DECIMAL(5,3) NOT NULL COMMENT 'Repeat penalty', - keep_alive_seconds INT NOT NULL COMMENT 'Model keep_alive in seconds', - is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1 = active; 0 = disabled', - updated_by INT NULL COMMENT 'user_id', - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY uk_profile_name (profile_name), - INDEX idx_profile_active (profile_name, is_active), - FOREIGN KEY (updated_by) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'ตาราง execution profile parameters สำหรับ np-dms-ai'; - -CREATE TABLE IF NOT EXISTS ai_sandbox_profiles ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ภายใน', - profile_name VARCHAR(50) NOT NULL COMMENT 'ชื่อ profile', - canonical_model VARCHAR(20) NOT NULL DEFAULT 'np-dms-ai' COMMENT 'Model identity', - temperature DECIMAL(4,3) NOT NULL COMMENT 'LLM temperature', - top_p DECIMAL(4,3) NOT NULL COMMENT 'LLM top_p', - max_tokens INT NULL COMMENT 'Maximum tokens', - num_ctx INT NULL COMMENT 'Context window size', - repeat_penalty DECIMAL(5,3) NOT NULL COMMENT 'Repeat penalty', - keep_alive_seconds INT NOT NULL COMMENT 'Model keep_alive in seconds', - updated_by INT NULL COMMENT 'user_id', - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE KEY uk_ai_sandbox_profile_name (profile_name), - INDEX idx_ai_sandbox_profile_model (canonical_model), - FOREIGN KEY (updated_by) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'ตาราง sandbox profile parameters'; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-14-seed-additional-prompt-types.sql b/specs/03-Data-and-Storage/deltas/2026-06-14-seed-additional-prompt-types.sql deleted file mode 100644 index f2b22b86..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-14-seed-additional-prompt-types.sql +++ /dev/null @@ -1,54 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-14-seed-additional-prompt-types.sql --- Change Log: --- - 2026-06-14: Seed additional prompt types for RAG Query, RAG Prep, and Classification (conforming to task T003) - -INSERT INTO ai_prompts ( - public_id, - prompt_type, - version_number, - template, - field_schema, - context_config, - is_active, - manual_note, - activated_at, - created_by -) VALUES - ( - UUID(), - 'rag_query_prompt', - 1, - 'You are a professional assistant analyzing project documents. Based on the provided context, answer the user query.\n\nContext:\n{{context}}\n\nUser Query:\n{{ocr_text}}\n\nAnswer:', - NULL, - NULL, - 1, - 'Initial seed for RAG query prompt', - CURRENT_TIMESTAMP, - (SELECT user_id FROM users WHERE username = 'superadmin' LIMIT 1) - ), - ( - UUID(), - 'rag_prep_prompt', - 1, - 'Analyze the following OCR text and prepare chunks for retrieval database.\n\nOCR TEXT:\n{{ocr_text}}\n\nChunks:', - NULL, - NULL, - 1, - 'Initial seed for RAG prep prompt', - CURRENT_TIMESTAMP, - (SELECT user_id FROM users WHERE username = 'superadmin' LIMIT 1) - ), - ( - UUID(), - 'classification_prompt', - 1, - 'Classify the following document based on its OCR text.\n\nOCR TEXT:\n{{ocr_text}}\n\nClassification (Correspondence, Transmittal, Circulation, RFA, Shop Drawing, Contract Drawing):', - NULL, - NULL, - 1, - 'Initial seed for Classification prompt', - CURRENT_TIMESTAMP, - (SELECT user_id FROM users WHERE username = 'superadmin' LIMIT 1) - ) -ON DUPLICATE KEY UPDATE - prompt_type = prompt_type; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-14-seed-execution-profiles.sql b/specs/03-Data-and-Storage/deltas/2026-06-14-seed-execution-profiles.sql deleted file mode 100644 index 5a0b4c74..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-14-seed-execution-profiles.sql +++ /dev/null @@ -1,21 +0,0 @@ --- File: specs/03-Data-and-Storage/deltas/2026-06-14-seed-execution-profiles.sql --- Change Log: --- - 2026-06-14: Seed default profiles for execution profiles (conforming to task T002) - -INSERT INTO ai_execution_profiles ( - profile_name, canonical_model, temperature, top_p, max_tokens, num_ctx, repeat_penalty, keep_alive_seconds, is_active -) VALUES - ('interactive', 'np-dms-ai', 0.700, 0.900, 2048, 4096, 1.150, 300, 1), - ('standard', 'np-dms-ai', 0.500, 0.800, 4096, 8192, 1.150, 600, 1), - ('quality', 'np-dms-ai', 0.100, 0.950, 8192, 8192, 1.150, 600, 1), - ('deep-analysis', 'np-dms-ai', 0.300, 0.850, 8192, 32768, 1.150, 0, 1), - ('ocr-extract', 'np-dms-ocr', 0.100, 0.100, NULL, NULL, 1.100, 0, 1) -ON DUPLICATE KEY UPDATE - canonical_model = VALUES(canonical_model), - temperature = VALUES(temperature), - top_p = VALUES(top_p), - max_tokens = VALUES(max_tokens), - num_ctx = VALUES(num_ctx), - repeat_penalty = VALUES(repeat_penalty), - keep_alive_seconds = VALUES(keep_alive_seconds), - is_active = VALUES(is_active); diff --git a/specs/03-Data-and-Storage/deltas/2026-06-15-fix-ai-prompts-columns.sql b/specs/03-Data-and-Storage/deltas/2026-06-15-fix-ai-prompts-columns.sql deleted file mode 100644 index 96ca6b29..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-15-fix-ai-prompts-columns.sql +++ /dev/null @@ -1,10 +0,0 @@ --- Delta: 2026-06-15-fix-ai-prompts-columns.sql --- Fix: (1) Drop duplicate camelCase publicId column (TypeORM mapping bug) --- (2) Add version column for optimistic locking (T066) --- ADR-009: Edit schema directly, no TypeORM migrations - --- ลบ duplicate column ที่ TypeORM สร้างผิด (camelCase แทน snake_case) -ALTER TABLE ai_prompts DROP COLUMN IF EXISTS `publicId`; - --- เพิ่ม version column สำหรับ @VersionColumn (optimistic locking) -ALTER TABLE ai_prompts ADD COLUMN IF NOT EXISTS `version` INT NOT NULL DEFAULT 1; diff --git a/specs/03-Data-and-Storage/deltas/2026-06-17-seed-ocr-system-prompt.sql b/specs/03-Data-and-Storage/deltas/2026-06-17-seed-ocr-system-prompt.sql deleted file mode 100644 index 33dc2a48..00000000 --- a/specs/03-Data-and-Storage/deltas/2026-06-17-seed-ocr-system-prompt.sql +++ /dev/null @@ -1,25 +0,0 @@ --- Delta: 2026-06-17-seed-ocr-system-prompt.sql --- Purpose: Seed default OCR system prompt for np-dms-ocr model (Feature 238) --- ADR-009: Edit schema directly, no TypeORM migrations - --- version column มีอยู่แล้วจาก 2026-06-15-fix-ai-prompts-columns.sql — บรรทัดนี้ idempotent เผื่อ env เก่า -ALTER TABLE ai_prompts ADD COLUMN IF NOT EXISTS `version` INT NOT NULL DEFAULT 1; - --- Seed default OCR system prompt (ถ้ายังไม่มี active ของ type นี้) --- ใช้ created_by INT FK → users(user_id) และ username='superadmin' ตาม pattern ของ delta เดิม -INSERT INTO ai_prompts ( - public_id, prompt_type, version_number, template, - context_config, is_active, activated_at, created_by -) -SELECT - UUID(), - 'ocr_system', - 1, - 'Extract all text from this PDF page accurately.', - '{"temperature": 0.1, "topP": 0.6}', - 1, - CURRENT_TIMESTAMP, - (SELECT user_id FROM users WHERE username = 'superadmin' LIMIT 1) -WHERE NOT EXISTS ( - SELECT 1 FROM ai_prompts WHERE prompt_type = 'ocr_system' AND is_active = 1 -); diff --git a/specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql b/specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql index 2d6e8a92..9fae4e6c 100644 --- a/specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql +++ b/specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql @@ -124,29 +124,12 @@ CREATE TABLE system_settings ( updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL, - INDEX idx_system_settings_category (category), - INDEX idx_system_settings_is_public (is_public) + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE + SET NULL, + INDEX idx_system_settings_category (category), + INDEX idx_system_settings_is_public (is_public) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก'; -INSERT INTO system_settings ( - setting_key, - setting_value, - data_type, - category, - description, - is_public -) -VALUES ( - 'AI_FEATURES_ENABLED', - 'true', - 'boolean', - 'ai', - 'สถานะเปิด/ปิดการใช้งานฟีเจอร์ AI ทั้งระบบ สำหรับผู้ใช้ทั่วไป', - 1 -) -ON DUPLICATE KEY UPDATE setting_key = setting_key; - -- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ CREATE TABLE roles ( role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', @@ -395,31 +378,37 @@ CREATE TABLE correspondence_revisions ( -- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ CREATE TABLE tags ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + public_id CHAR(36) NOT NULL UNIQUE COMMENT 'UUIDv7 สำหรับการใช้งานภายนอก (ADR-019)', project_id INT NULL COMMENT 'ID โครงการ (NULL = Global Tag)', - tag_name VARCHAR(100) NOT NULL COMMENT 'ชื่อ Tag', - color_code VARCHAR(30) DEFAULT 'default' COMMENT 'รหัสสี หรือชื่อคลาสสำหรับ UI', - description TEXT COMMENT 'คำอธิบายแท็ก', + tag_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแท็ก', + color_code VARCHAR(30) DEFAULT 'default' COMMENT 'รหัสสีสำหรับ UI', + description TEXT COMMENT 'คำอธิบายเพิ่มเติม', + created_by INT COMMENT 'ผู้สร้างแท็ก', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - created_by INT COMMENT 'ผู้สร้าง', - deleted_at DATETIME NULL COMMENT 'ลบแบบ Soft Delete', - -- Constraints & Indexes + deleted_at TIMESTAMP NULL COMMENT 'วันที่ลบ (Soft Delete)', FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE, FOREIGN KEY (created_by) REFERENCES users (user_id) ON DELETE SET NULL, - UNIQUE KEY ux_tag_project (project_id, tag_name), + UNIQUE KEY uq_tag_project (project_id, tag_name), INDEX idx_tags_deleted_at (deleted_at) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ Tags ย่อยตาม Project'; +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลแท็กจัดหมวดหมู่เอกสาร'; -- ตารางเชื่อมระหว่าง correspondences และ tags (M:N) CREATE TABLE correspondence_tags ( - correspondence_id INT COMMENT 'ID ของเอกสาร', - tag_id INT COMMENT 'ID ของ Tag', + correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร', + tag_id INT NOT NULL COMMENT 'ID ของแท็ก', + is_ai_suggested BOOLEAN DEFAULT FALSE COMMENT 'แท็กนี้แนะนำโดย AI หรือไม่', + confidence DECIMAL(4, 3) NULL COMMENT 'ค่าความมั่นใจของ AI (0.000–1.000)', + created_by INT COMMENT 'ผู้เชื่อมโยงแท็ก', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่เชื่อมโยง', PRIMARY KEY (correspondence_id, tag_id), FOREIGN KEY (correspondence_id) REFERENCES correspondences (id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE, - INDEX idx_tag_lookup (tag_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง correspondences และ tags (M:N)'; + FOREIGN KEY (created_by) REFERENCES users (user_id) ON DELETE + SET NULL, + INDEX idx_correspondence_tags_lookup (tag_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมโยงความสัมพันธ์แบบ M:N ระหว่างเอกสารและแท็ก'; -- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N) CREATE TABLE correspondence_references ( @@ -1017,10 +1006,7 @@ CREATE TABLE contract_drawing_attachments ( 'OTHER ' ) COMMENT 'ประเภทไฟล์', is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', - PRIMARY KEY ( - contract_drawing_id, - attachment_id - ), + PRIMARY KEY (contract_drawing_id, attachment_id), FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings (id) ON DELETE CASCADE, FOREIGN KEY (attachment_id) REFERENCES attachments (id) ON DELETE CASCADE ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม contract_drawings กับ attachments (M :N)'; @@ -1042,10 +1028,7 @@ CREATE TABLE document_number_formats ( updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE, FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types (id) ON DELETE CASCADE, - UNIQUE KEY unique_format ( - project_id, - correspondence_type_id - ) + UNIQUE KEY unique_format (project_id, correspondence_type_id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร'; -- ========================================================== @@ -1395,11 +1378,7 @@ CREATE TABLE backup_logs ( backup_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'ประเภท (DATABASE, FILES, FULL)', backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง', file_size BIGINT COMMENT 'ขนาดไฟล์', - STATUS ENUM( - 'STARTED', - 'COMPLETED', - 'FAILED' - ) NOT NULL COMMENT 'สถานะ', + STATUS ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'สถานะ', started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น', completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น', error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)' @@ -1508,10 +1487,16 @@ CREATE TABLE migration_review_queue ( idempotency_key VARCHAR(200) NOT NULL COMMENT 'Idempotency-Key สำหรับป้องกัน queue ซ้ำ', original_filename VARCHAR(500) NOT NULL COMMENT 'ชื่อไฟล์ต้นฉบับจาก legacy source', storage_temp_path VARCHAR(1000) NOT NULL COMMENT 'temp storage path ก่อน import', - ai_metadata_json JSON NOT NULL COMMENT 'AI suggestion payload เต็มสำหรับ human review', + ai_job_id VARCHAR(36) NULL COMMENT 'BullMQ Job ID สำหรับงานประมวลผล AI', + ai_metadata_json LONGTEXT NOT NULL COMMENT 'AI suggestion payload เต็มสำหรับ human review' CHECK (json_valid(`ai_metadata_json`)), confidence_score DECIMAL(5, 4) NOT NULL COMMENT 'AI confidence score 0.0000-1.0000', ocr_used TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'ระบุว่าใช้ OCR path หรือไม่', - STATUS ENUM('PENDING', 'APPROVED', 'IMPORTED', 'REJECTED') NOT NULL DEFAULT 'PENDING', + STATUS ENUM( + 'PENDING', + 'PENDING_REVIEW', + 'IMPORTED', + 'REJECTED' + ) NOT NULL DEFAULT 'PENDING', temp_attachment_id INT NULL COMMENT 'Temporary attachment ID referencing attachments.id (ADR-028)', reviewed_by INT NULL COMMENT 'Internal users.user_id ของผู้ review', reviewed_at DATETIME NULL COMMENT 'เวลาที่ review record', @@ -1536,8 +1521,14 @@ CREATE TABLE ai_audit_logs ( document_public_id UUID NULL COMMENT 'Imported document publicId when available', ai_model VARCHAR(50) NOT NULL DEFAULT 'gemma4' COMMENT 'Legacy AI model column used by current gateway service', model_name VARCHAR(100) NOT NULL COMMENT 'Local model name used by ADR-023 AI pipeline', - ai_suggestion_json JSON NULL COMMENT 'AI suggested metadata', - human_override_json JSON NULL COMMENT 'Human approved or overridden metadata', + effective_profile VARCHAR(50) NULL COMMENT 'ExecutionProfile ที่ backend กำหนด: interactive|standard|quality|deep-analysis (Feature-235)', + canonical_model VARCHAR(50) NULL COMMENT 'Canonical model identity: np-dms-ai หรือ np-dms-ocr (Feature-235, ADR-023)', + snapshot_params_json LONGTEXT NULL COMMENT 'Runtime parameters snapshot ณ เวลา dispatch — ใช้จริงใน Ollama call (FR-A09, Feature-235)' CHECK (json_valid(`snapshot_params_json`)), + model_type VARCHAR(50) NULL COMMENT 'ประเภท OCR/LLM model ที่ใช้ เช่น tesseract, typhoon-ocr-3b', + vram_usage_mb INT NULL COMMENT 'VRAM ที่ใช้จริง (MB) ณ เวลาประมวลผล', + cache_hit TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = ผลลัพธ์มาจาก Redis cache, 0 = OCR ใหม่', + ai_suggestion_json LONGTEXT NULL COMMENT 'AI suggested metadata' CHECK (json_valid(`ai_suggestion_json`)), + human_override_json LONGTEXT NULL COMMENT 'Human approved or overridden metadata' CHECK (json_valid(`human_override_json`)), processing_time_ms INT NULL COMMENT 'Legacy processing duration field', confidence_score DECIMAL(4, 3) NULL COMMENT 'AI confidence score 0.000-1.000', input_hash VARCHAR(64) NULL COMMENT 'Legacy SHA-256 input hash', @@ -1552,12 +1543,132 @@ CREATE TABLE ai_audit_logs ( KEY idx_ai_audit_model_name (model_name), KEY idx_ai_audit_status (STATUS), KEY idx_ai_audit_confirmed_by (confirmed_by_user_id), + KEY idx_ai_audit_model_type (model_type), + KEY idx_ai_audit_effective_profile (effective_profile), + KEY idx_ai_audit_canonical_model (canonical_model), CONSTRAINT fk_ai_audit_confirmed_by_user FOREIGN KEY (confirmed_by_user_id) REFERENCES users (user_id) ON DELETE SET NULL ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ADR-023 AI development feedback log'; -- ===================================================== --- 13. 🤖 Intent Classification (ADR-024) +-- 13. 🤖 AI Available Models (ADR-027, ADR-034) +-- ===================================================== +-- ตารางเก็บรายการโมเดล AI ที่ให้เลือกใช้งานในระบบ +CREATE TABLE ai_available_models ( + id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของตาราง', + model_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'ชื่อโมเดล เช่น gemma4:e2b, gemma4:e4b', + model_version VARCHAR(50) NOT NULL COMMENT 'เวอร์ชั่นของโมเดล', + description VARCHAR(500) NULL COMMENT 'รายละเอียดโมเดล', + vram_gb DECIMAL(4, 2) NULL COMMENT 'VRAM ที่ใช้โดยประมาณ (GB)', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะใช้งาน', + is_default TINYINT(1) DEFAULT 0 COMMENT 'โมเดลเริ่มต้น', + created_by INT NULL, + updated_by INT NULL, + created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3), + updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), + deleted_at DATETIME(3) NULL, + UNIQUE KEY uk_model_name (model_name), + KEY idx_is_active (is_active), + KEY idx_is_default (is_default) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตารางเก็บรายการโมเดล AI ที่ให้เลือกใช้งานในระบบ (ADR-027)'; + +-- ===================================================== +-- 14. 🤖 AI Prompts (ADR-029) +-- ===================================================== +-- ตาราง versioned prompt templates สำหรับ OCR extraction +CREATE TABLE ai_prompts ( + id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ภายใน (ไม่ expose ใน API)', + public_id UUID NOT NULL UNIQUE COMMENT 'UUID Public Identifier (ADR-019)', + prompt_type VARCHAR(50) NOT NULL COMMENT 'ประเภท prompt เช่น ocr_extraction', + version_number INT NOT NULL COMMENT 'เลข version ต่อเนื่องต่อ prompt_type (1, 2, 3...)', + template TEXT NOT NULL COMMENT 'prompt template ที่มี {{ocr_text}} placeholder บังคับ', + field_schema LONGTEXT NULL COMMENT 'definition ของ fields ที่คาดหวังในผลลัพธ์ JSON' CHECK (json_valid(`field_schema`)), + is_active TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 = version นี้ใช้งานจริงทั้ง sandbox และ migrate-document (1 per prompt_type)', + test_result_json LONGTEXT NULL COMMENT 'ผลลัพธ์ JSON จาก sandbox run ล่าสุด (auto-save โดย processor)' CHECK (json_valid(`test_result_json`)), + manual_note TEXT NULL COMMENT 'หมายเหตุ/annotation จาก admin (manual input)', + last_tested_at TIMESTAMP NULL COMMENT 'เวลาที่ sandbox รันครั้งล่าสุดสำหรับ version นี้', + activated_at TIMESTAMP NULL COMMENT 'เวลาที่ version นี้ถูก activate เป็น active', + created_by INT NOT NULL COMMENT 'user_id ของผู้สร้าง version นี้', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + context_config LONGTEXT NULL COMMENT 'Configuration สำหรับ context ที่ backend ต้องส่งให้ AI (filter, pageSize, language, etc.)' CHECK (json_valid(`context_config`)), + version INT NOT NULL DEFAULT 1, + UNIQUE KEY uk_type_version (prompt_type, version_number), + KEY idx_prompt_type_active (prompt_type, is_active), + KEY created_by (created_by), + CONSTRAINT ai_prompts_ibfk_1 FOREIGN KEY (created_by) REFERENCES users (user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตาราง versioned prompt templates สำหรับ OCR extraction (ADR-029)'; + +-- ===================================================== +-- 15. 🤖 AI Execution Profiles (ADR-025, ADR-027) +-- ===================================================== +-- ตาราง execution profile parameters สำหรับ np-dms-ai +CREATE TABLE ai_execution_profiles ( + id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ภายใน', + profile_name VARCHAR(50) NOT NULL UNIQUE COMMENT 'ชื่อ profile', + canonical_model VARCHAR(20) NOT NULL DEFAULT 'np-dms-ai' COMMENT 'Model identity', + temperature DECIMAL(4, 3) NOT NULL COMMENT 'LLM temperature', + top_p DECIMAL(4, 3) NOT NULL COMMENT 'LLM top_p', + max_tokens INT NULL COMMENT 'Maximum tokens', + num_ctx INT NULL COMMENT 'Context window size', + repeat_penalty DECIMAL(5, 3) NOT NULL COMMENT 'Repeat penalty', + keep_alive_seconds INT NOT NULL COMMENT 'Model keep_alive in seconds', + is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '1 = active; 0 = disabled', + updated_by INT NULL COMMENT 'user_id', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY idx_profile_active (profile_name, is_active), + KEY updated_by (updated_by), + CONSTRAINT ai_execution_profiles_ibfk_1 FOREIGN KEY (updated_by) REFERENCES users (user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตาราง execution profile parameters สำหรับ np-dms-ai'; + +-- ตาราง sandbox profile parameters +CREATE TABLE ai_sandbox_profiles ( + id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ภายใน', + profile_name VARCHAR(50) NOT NULL UNIQUE COMMENT 'ชื่อ profile', + canonical_model VARCHAR(20) NOT NULL DEFAULT 'np-dms-ai' COMMENT 'Model identity', + temperature DECIMAL(4, 3) NOT NULL COMMENT 'LLM temperature', + top_p DECIMAL(4, 3) NOT NULL COMMENT 'LLM top_p', + max_tokens INT NULL COMMENT 'Maximum tokens', + num_ctx INT NOT NULL COMMENT 'Context window size', + repeat_penalty DECIMAL(5, 3) NOT NULL COMMENT 'Repeat penalty', + keep_alive_seconds INT NOT NULL COMMENT 'Model keep_alive in seconds', + updated_by INT NULL COMMENT 'user_id', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + KEY idx_ai_sandbox_profile_model (canonical_model), + KEY updated_by (updated_by), + CONSTRAINT ai_sandbox_profiles_ibfk_1 FOREIGN KEY (updated_by) REFERENCES users (user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตาราง sandbox profile parameters'; + +-- ===================================================== +-- 16. 🤖 Migration Errors (ADR-028) +-- ===================================================== +-- ตาราง Error Log สำหรับ Migration (ลบได้หลัง Migration เสร็จ) +CREATE TABLE migration_errors ( + id INT AUTO_INCREMENT PRIMARY KEY, + batch_id VARCHAR(50) NULL, + document_number VARCHAR(100) NULL, + error_type ENUM( + 'FILE_NOT_FOUND', + 'MISSING_FILENAME', + 'FILE_ERROR', + 'AI_PARSE_ERROR', + 'API_ERROR', + 'DB_ERROR', + 'SECURITY', + 'UNKNOWN' + ) NULL, + error_message TEXT NULL, + job_id VARCHAR(100) NULL, + raw_ai_response TEXT NULL, + created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP, + KEY idx_batch_id (batch_id), + KEY idx_job_id (job_id), + KEY idx_error_type (error_type) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Error Log (ลบได้หลัง Migration เสร็จ)'; + +-- ===================================================== +-- 17. 🤖 Intent Classification (ADR-024) -- ===================================================== -- Intent Definitions Table CREATE TABLE IF NOT EXISTS ai_intent_definitions ( diff --git a/specs/03-Data-and-Storage/lcbp3-v1.9.0-seed-basic.sql b/specs/03-Data-and-Storage/lcbp3-v1.9.0-seed-basic.sql index d8909a6f..a46beec6 100644 --- a/specs/03-Data-and-Storage/lcbp3-v1.9.0-seed-basic.sql +++ b/specs/03-Data-and-Storage/lcbp3-v1.9.0-seed-basic.sql @@ -18,6 +18,43 @@ -- 2.1 username = migration_bot -- 2.2 -- ========================================================== +-- System Settings +INSERT INTO system_settings ( + setting_key, + setting_value, + data_type, + category, + description, + is_public + ) +VALUES ( + 'AI_FEATURES_ENABLED', + 'true', + 'boolean', + 'ai', + 'สถานะเปิด/ปิดการใช้งานฟีเจอร์ AI ทั้งระบบ สำหรับผู้ใช้ทั่วไป', + 1 + ) ON DUPLICATE KEY +UPDATE setting_key = setting_key; + +INSERT INTO system_settings ( + setting_key, + setting_value, + data_type, + category, + description, + is_public + ) +VALUES ( + 'AI_ACTIVE_MODEL', + 'typhoon2.5-np-dms:latest', + 'string', + 'ai', + 'โมเดล AI ที่ใช้งานอยู่ในระบบ (global)', + 1 + ) ON DUPLICATE KEY +UPDATE setting_key = setting_key; + INSERT INTO organization_roles (id, role_name) VALUES (1, 'OWNER'), (2, 'DESIGNER'), @@ -2648,3 +2685,156 @@ VALUES ( NOW(), NOW() ); + +-- ========================================================== +-- AI Seed Data (ADR-027, ADR-029, ADR-034) +-- ========================================================== +-- AI Available Models +INSERT INTO ai_available_models ( + model_name, + model_version, + description, + vram_gb, + is_active, + is_default, + created_at, + updated_at + ) +VALUES ( + 'typhoon2.5-np-dms:latest', + 'latest', + 'Thai-optimized main AI model based on typhoon2.5-qwen3-4b (~2.5GB VRAM, standby mode) — ADR-034', + 2.50, + 1, + 1, + NOW(3), + NOW(3) + ), + ( + 'typhoon-np-dms-ocr:latest', + 'latest', + 'Thai OCR model based on typhoon-ocr1.5-3b (~3.2GB VRAM, unloads after each job) — ADR-034', + 3.20, + 1, + 0, + NOW(3), + NOW(3) + ); + +-- AI Execution Profiles +INSERT INTO ai_execution_profiles ( + profile_name, + canonical_model, + temperature, + top_p, + max_tokens, + num_ctx, + repeat_penalty, + keep_alive_seconds, + is_active, + created_at, + updated_at + ) +VALUES ( + 'interactive', + 'np-dms-ai', + 0.7, + 0.9, + 2048, + 4096, + 1.1, + 300, + 1, + NOW(), + NOW() + ), + ( + 'standard', + 'np-dms-ai', + 0.5, + 0.8, + 4096, + 8192, + 1.15, + 300, + 1, + NOW(), + NOW() + ), + ( + 'quality', + 'np-dms-ai', + 0.3, + 0.7, + 8192, + 16384, + 1.2, + 300, + 1, + NOW(), + NOW() + ), + ( + 'deep-analysis', + 'np-dms-ai', + 0.2, + 0.6, + 16384, + 32768, + 1.25, + 300, + 1, + NOW(), + NOW() + ), + ( + 'ocr-extract', + 'np-dms-ocr', + 0.1, + 0.5, + NULL, + 8192, + 1.0, + 0, + 1, + NOW(), + NOW() + ); + +-- AI Sandbox Profiles +INSERT INTO ai_sandbox_profiles ( + profile_name, + canonical_model, + temperature, + top_p, + max_tokens, + num_ctx, + repeat_penalty, + keep_alive_seconds, + created_at, + updated_at + ) +VALUES ( + 'ocr-extract', + 'np-dms-ocr', + 0.1, + 0.5, + NULL, + 8192, + 1.0, + 0, + NOW(), + NOW() + ), + ( + 'standard', + 'np-dms-ai', + 0.5, + 0.8, + 4096, + 8192, + 1.15, + 300, + NOW(), + NOW() + ); diff --git a/specs/88-logs/rollouts.md b/specs/88-logs/rollouts.md index 100bacb8..7dee2fa5 100644 --- a/specs/88-logs/rollouts.md +++ b/specs/88-logs/rollouts.md @@ -34,4 +34,6 @@ | 2026-06-18 | v1.9.10 | Feature-238 OCR AI Prompt Separation — SandboxTabs โหลด active prompts ทั้ง ocr_system + ocr_extraction, แสดง prompt info ทั้ง 2 steps, ส่ง active version ที่ถูกต้อง | ✅ Complete | | 2026-06-18 | v1.9.10 | VRAM Monitor Fix — Backend ส่ง loadedModels พร้อม vramUsageMB, frontend รองรับ format ใหม่, แสดง VRAM usage ถูกต้อง | ✅ Complete | | 2026-06-19 | v1.9.10 | Feature-240 AI Admin Console Collapsible Cards — เพิ่มปุ่มและฟังก์ชันพับ/คลี่การ์ดและเซกชัน พร้อมบันทึกสถานะลง localStorage และรักษา background query polling | ✅ Complete | +| 2026-06-19 | v1.9.10 | Deployment Timeout Fix — Added clamav health check before recreation (skip if healthy), increased CI timeout 20→30 min | ✅ Complete | +| 2026-06-19 | v1.9.10 | AI Admin Response Normalization — recursive data unwrap for VRAM/prompt payloads, fixed Sandbox `.map()` crash and false OOM Guard | ✅ Complete | diff --git a/specs/88-logs/session-2026-06-19-ai-admin-response-normalization.md b/specs/88-logs/session-2026-06-19-ai-admin-response-normalization.md new file mode 100644 index 00000000..5e1fd362 --- /dev/null +++ b/specs/88-logs/session-2026-06-19-ai-admin-response-normalization.md @@ -0,0 +1,28 @@ +# Session — 2026-06-19 (AI Admin Response Normalization) + +## Summary + +แก้ bug หน้า `admin/ai` เมื่อกดแท็บ `3-Step Pipeline Sandbox` แล้วขึ้น `Admin Panel Error e.map is not a function` และแก้ VRAM GPU Monitor ที่แสดง `0 MB / 0 MB` แต่ขึ้น `หน่วยความจำไม่เพียงพอ (OOM Guard)` ผิดสถานะ + +## ปัญหาที่พบ (Root Cause) + +Frontend AI Admin service unwrap API response ได้เพียงชั้นเดียว ทำให้ response ที่ถูกห่อ `data` ซ้อนกันอ่านค่า VRAM เป็น `0/0` และ prompt list กลายเป็น object แทน array ก่อนส่งเข้า component ที่ใช้ `.find()` / `.map()` + +## การแก้ไข (Fix) + +| ไฟล์ | การเปลี่ยนแปลง | +| ---- | --------------- | +| `frontend/lib/services/admin-ai.service.ts` | เพิ่ม recursive `extractData()` สำหรับ API envelope ซ้อนกัน และปรับ VRAM unknown capacity (`totalVRAMMB = 0`) ไม่ให้แสดง OOM Guard | +| `frontend/lib/services/admin-ai-prompt.service.ts` | เพิ่ม response normalization ให้ `getPrompts()` คืน array เสมอ และ unwrap response สำหรับ create/activate/update note | +| `frontend/lib/services/__tests__/admin-ai.service.test.ts` | เพิ่ม regression tests สำหรับ VRAM double-wrapper, unknown VRAM, prompt list wrapper, และ non-array prompt payload | + +## กฎที่ Lock แล้ว + +- AI Admin frontend service ต้อง normalize API response envelope ที่อาจซ้อน `data` ก่อนส่งให้ UI render +- `totalVRAMMB = 0` ใน frontend หมายถึง capacity unknown ไม่ใช่ OOM; ห้ามแสดง OOM Guard จากข้อมูล VRAM ที่ไม่รู้ total + +## Verification + +- [x] `pnpm --filter lcbp3-frontend exec vitest run lib/services/__tests__/admin-ai.service.test.ts components/admin/ai/__tests__/SandboxTabs.test.tsx components/admin/ai/__tests__/sandbox-tabs.test.tsx` ผ่าน 3 files / 9 tests +- [x] `pnpm --filter lcbp3-frontend exec tsc --noEmit` ผ่าน +- [x] `git diff --check -- frontend/lib/services/admin-ai.service.ts frontend/lib/services/admin-ai-prompt.service.ts frontend/lib/services/__tests__/admin-ai.service.test.ts` ผ่าน diff --git a/specs/88-logs/session-2026-06-19-deployment-timeout-fix.md b/specs/88-logs/session-2026-06-19-deployment-timeout-fix.md new file mode 100644 index 00000000..8177f3c6 --- /dev/null +++ b/specs/88-logs/session-2026-06-19-deployment-timeout-fix.md @@ -0,0 +1,34 @@ +# Session — 2026-06-19 (Deployment Timeout Fix) + +## Summary + +Fixed CI/CD deployment timeout issue caused by ClamAV container recreation taking 5+ minutes during healthcheck, causing SSH connection to timeout before deployment completed. + +## ปัญหาที่พบ (Root Cause) + +**Error:** `context deadline exceeded` during container recreation on QNAP + +**Root Cause:** +- CI workflow SSH timeout: `ConnectTimeout=30` + `ServerAliveCountMax=10` (5 minutes total keepalive) +- ClamAV healthcheck `start_period: 300s` (5 minutes) before it's considered healthy +- Backend depends on clamav being healthy before starting +- `docker compose up -d --force-recreate` recreates clamav first, which takes 5+ minutes to become healthy +- No output during this period → SSH connection times out + +## การแก้ไข (Fix) + +| ไฟล์ | การเปลี่ยนแปลง | +| -------------- | ---------------------- | +| `scripts/deploy.sh` | Added clamav health check before recreation - if healthy, only recreate backend/frontend (skip 5-minute delay) | +| `.gitea/workflows/ci-deploy.yml` | Increased CI timeout from 20 to 30 minutes as safety net | + +## กฎที่ Lock แล้ว + +- **D18:** Deploy script must check ClamAV health status before recreation to avoid unnecessary 5-minute healthcheck delay +- **D19:** CI timeout should be at least 30 minutes to accommodate ClamAV startup if full recreation is needed + +## Verification + +- [ ] Deploy script tested locally to verify clamav health check logic +- [ ] CI workflow tested with new timeout setting +- [ ] Next deployment completes without SSH timeout