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,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
);