690619:1226 240 #03
CI / CD Pipeline / build (push) Successful in 7m1s
CI / CD Pipeline / deploy (push) Successful in 13m31s

This commit is contained in:
2026-06-19 12:26:36 +07:00
parent 12e230332e
commit d418d791a4
51 changed files with 706 additions and 1294 deletions
@@ -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
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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.0001.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 ระหว่างเอกสารและแท็ก';
@@ -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';
@@ -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;
@@ -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';
@@ -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;
@@ -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);
@@ -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;
@@ -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');
@@ -1,4 +0,0 @@
-- Rollback: ลบตาราง ai_prompts (ADR-029)
-- Date: 2026-05-25
DROP TABLE IF EXISTS ai_prompts;
@@ -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;
@@ -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'
);
@@ -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;
@@ -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;
@@ -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;
@@ -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);
@@ -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;
@@ -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);
@@ -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'
);
@@ -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;
@@ -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;
@@ -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';
@@ -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';
@@ -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;
@@ -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สำหรับแต่ละส่วนที่คุณแบ่ง ให้ล้อมรอบด้วยแท็ก <chunk topic=\"หัวข้อหลักของเนื้อหาส่วนนี้\"> [เนื้อหาในส่วนนี้] </chunk>\n\nกฎในการแบ่งข้อมูล:\n1. ห้ามแก้ไขคำหรือข้อความใดๆ ในเอกสารเด็ดขาด ให้ใช้ข้อความดั้งเดิมจาก OCR ทั้งหมด\n2. พยายามแบ่งส่วนตามขอบเขตเนื้อหาที่สมเหตุสมผล เช่น เมื่อขึ้นหัวข้อใหม่ หรือส่วนเนื้อความที่คนละประเด็นกัน\n3. แต่ละส่วนควรมีความยาวที่อ่านเข้าใจได้และไม่ยาวจนเกินไป\n4. ห้ามตอบข้อความบทนำหรือบทสรุปใดๆ นอกเหนือจากแท็ก <chunk> และข้อความภายในแท็ก\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;
@@ -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;
@@ -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;
@@ -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 ไว้แล้ว
@@ -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;
@@ -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);
@@ -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;
@@ -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';
@@ -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';
@@ -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;
@@ -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);
@@ -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;
@@ -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
);
@@ -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.0001.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 (
@@ -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()
);
+2
View File
@@ -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 |
@@ -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` ผ่าน
@@ -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