690619:1226 240 #03
This commit is contained in:
@@ -0,0 +1,97 @@
|
|||||||
|
// File: frontend/lib/services/__tests__/admin-ai.service.test.ts
|
||||||
|
// Change Log:
|
||||||
|
// - 2026-06-19: เพิ่ม regression tests สำหรับ AI Admin response normalization
|
||||||
|
|
||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
import apiClient from '@/lib/api/client';
|
||||||
|
import { adminAiService } from '../admin-ai.service';
|
||||||
|
import { adminAiPromptService } from '../admin-ai-prompt.service';
|
||||||
|
|
||||||
|
const promptVersion = {
|
||||||
|
publicId: '0195aaaa-aaaa-7000-8000-aaaaaaaaaaaa',
|
||||||
|
promptType: 'ocr_system',
|
||||||
|
versionNumber: 1,
|
||||||
|
version: 3,
|
||||||
|
template: 'OCR prompt',
|
||||||
|
isActive: true,
|
||||||
|
createdAt: '2026-06-19T00:00:00.000Z',
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('admin AI service normalization', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ควร unwrap VRAM response ที่ถูกห่อ data ซ้อนกัน', async () => {
|
||||||
|
vi.mocked(apiClient.get).mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
data: {
|
||||||
|
totalVramMb: 16384,
|
||||||
|
usedVramMb: 4096,
|
||||||
|
freeVramMb: 12288,
|
||||||
|
hasCapacity: true,
|
||||||
|
loadedModels: [
|
||||||
|
{
|
||||||
|
modelId: 'np-dms-ai:latest',
|
||||||
|
modelName: 'np-dms-ai:latest',
|
||||||
|
vramUsageMB: 4096,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await adminAiService.getVramStatus();
|
||||||
|
|
||||||
|
expect(result.totalVRAMMB).toBe(16384);
|
||||||
|
expect(result.usedVRAMMB).toBe(4096);
|
||||||
|
expect(result.usagePercent).toBe(25);
|
||||||
|
expect(result.canLoadModel).toBe(true);
|
||||||
|
expect(result.loadedModels).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ควรไม่แสดง OOM Guard เมื่อ backend ยังไม่ส่ง total VRAM', async () => {
|
||||||
|
vi.mocked(apiClient.get).mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
data: {
|
||||||
|
loadedModels: [],
|
||||||
|
hasCapacity: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await adminAiService.getVramStatus();
|
||||||
|
|
||||||
|
expect(result.totalVRAMMB).toBe(0);
|
||||||
|
expect(result.usedVRAMMB).toBe(0);
|
||||||
|
expect(result.canLoadModel).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ควรคืน prompt list เป็น array เมื่อ response ถูกห่อ data ซ้อนกัน', async () => {
|
||||||
|
vi.mocked(apiClient.get).mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
data: {
|
||||||
|
data: [promptVersion],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await adminAiPromptService.getPrompts('ocr_system');
|
||||||
|
|
||||||
|
expect(result).toEqual([promptVersion]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ควรคืน empty array เมื่อ prompt payload ไม่ใช่ array', async () => {
|
||||||
|
vi.mocked(apiClient.get).mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
data: {
|
||||||
|
message: 'unexpected payload',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await adminAiPromptService.getPrompts('ocr_system');
|
||||||
|
|
||||||
|
expect(result).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// File: frontend/lib/services/admin-ai-prompt.service.ts
|
// File: frontend/lib/services/admin-ai-prompt.service.ts
|
||||||
// Change Log
|
// Change Log
|
||||||
// - 2026-06-17: Created adminAiPromptService for prompt management UI (Feature 238)
|
// - 2026-06-17: Created adminAiPromptService for prompt management UI (Feature 238)
|
||||||
|
// - 2026-06-19: Normalize prompt response envelopes to prevent non-array prompt history crashes
|
||||||
|
|
||||||
import client from '../api/client';
|
import client from '../api/client';
|
||||||
|
|
||||||
@@ -18,6 +19,23 @@ export interface AiPromptVersion {
|
|||||||
createdBy?: number;
|
createdBy?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extractData = <T>(value: unknown): T => {
|
||||||
|
let current = value;
|
||||||
|
for (let depth = 0; depth < 3; depth += 1) {
|
||||||
|
if (current && typeof current === 'object' && 'data' in current) {
|
||||||
|
current = (current as { data: unknown }).data;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return current as T;
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizePromptList = (value: unknown): AiPromptVersion[] => {
|
||||||
|
const data = extractData<unknown>(value);
|
||||||
|
return Array.isArray(data) ? (data as AiPromptVersion[]) : [];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service สำหรับจัดการ AI Prompt Versions ใน Admin Console
|
* Service สำหรับจัดการ AI Prompt Versions ใน Admin Console
|
||||||
*/
|
*/
|
||||||
@@ -26,10 +44,10 @@ export const adminAiPromptService = {
|
|||||||
* ดึงรายการ prompt versions ทั้งหมดสำหรับ prompt_type ที่กำหนด
|
* ดึงรายการ prompt versions ทั้งหมดสำหรับ prompt_type ที่กำหนด
|
||||||
*/
|
*/
|
||||||
async getPrompts(promptType: string): Promise<AiPromptVersion[]> {
|
async getPrompts(promptType: string): Promise<AiPromptVersion[]> {
|
||||||
const response = await client.get<{ data: AiPromptVersion[] }>(
|
const response = await client.get<unknown>(
|
||||||
`/ai/prompts/${promptType}`
|
`/ai/prompts/${promptType}`
|
||||||
);
|
);
|
||||||
return response.data.data;
|
return normalizePromptList(response.data);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,7 +59,7 @@ export const adminAiPromptService = {
|
|||||||
contextConfig?: Record<string, unknown>
|
contextConfig?: Record<string, unknown>
|
||||||
): Promise<AiPromptVersion> {
|
): Promise<AiPromptVersion> {
|
||||||
const idempotencyKey = crypto.randomUUID();
|
const idempotencyKey = crypto.randomUUID();
|
||||||
const response = await client.post<{ data: AiPromptVersion }>(
|
const response = await client.post<unknown>(
|
||||||
`/ai/prompts/${promptType}`,
|
`/ai/prompts/${promptType}`,
|
||||||
{ template, contextConfig },
|
{ template, contextConfig },
|
||||||
{
|
{
|
||||||
@@ -50,7 +68,7 @@ export const adminAiPromptService = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return response.data.data;
|
return extractData<AiPromptVersion>(response.data);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,7 +80,7 @@ export const adminAiPromptService = {
|
|||||||
expectedVersion?: number
|
expectedVersion?: number
|
||||||
): Promise<AiPromptVersion> {
|
): Promise<AiPromptVersion> {
|
||||||
const idempotencyKey = crypto.randomUUID();
|
const idempotencyKey = crypto.randomUUID();
|
||||||
const response = await client.post<{ data: AiPromptVersion }>(
|
const response = await client.post<unknown>(
|
||||||
`/ai/prompts/${promptType}/${versionNumber}/activate`,
|
`/ai/prompts/${promptType}/${versionNumber}/activate`,
|
||||||
expectedVersion !== undefined ? { expectedVersion } : {},
|
expectedVersion !== undefined ? { expectedVersion } : {},
|
||||||
{
|
{
|
||||||
@@ -71,7 +89,7 @@ export const adminAiPromptService = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return response.data.data;
|
return extractData<AiPromptVersion>(response.data);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,10 +109,10 @@ export const adminAiPromptService = {
|
|||||||
versionNumber: number,
|
versionNumber: number,
|
||||||
manualNote: string | null
|
manualNote: string | null
|
||||||
): Promise<AiPromptVersion> {
|
): Promise<AiPromptVersion> {
|
||||||
const response = await client.patch<{ data: AiPromptVersion }>(
|
const response = await client.patch<unknown>(
|
||||||
`/ai/prompts/${promptType}/${versionNumber}/note`,
|
`/ai/prompts/${promptType}/${versionNumber}/note`,
|
||||||
{ manualNote }
|
{ manualNote }
|
||||||
);
|
);
|
||||||
return response.data.data;
|
return extractData<AiPromptVersion>(response.data);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
// - 2026-06-13: T027-T029 — เพิ่ม getSandboxProfile, saveSandboxProfile, resetSandboxProfile สำหรับ sandbox parameter management
|
// - 2026-06-13: T027-T029 — เพิ่ม getSandboxProfile, saveSandboxProfile, resetSandboxProfile สำหรับ sandbox parameter management
|
||||||
// - 2026-06-13: T042-T043 — เพิ่ม applyProfile และ getProductionDefaults สำหรับปรับใช้และดึงค่า production parameters
|
// - 2026-06-13: T042-T043 — เพิ่ม applyProfile และ getProductionDefaults สำหรับปรับใช้และดึงค่า production parameters
|
||||||
// - 2026-06-13: US4 — อัปเดต submitSandboxExtract และ submitSandboxAiExtract ให้รองรับ project/contract publicId
|
// - 2026-06-13: US4 — อัปเดต submitSandboxExtract และ submitSandboxAiExtract ให้รองรับ project/contract publicId
|
||||||
|
// - 2026-06-19: แก้ response envelope ซ้อนกันเพื่อป้องกัน VRAM แสดง 0/0 และ OOM Guard ผิดพลาด
|
||||||
|
|
||||||
import api from '../api/client';
|
import api from '../api/client';
|
||||||
import { AiJobResponse } from '../../types/ai';
|
import { AiJobResponse } from '../../types/ai';
|
||||||
@@ -172,10 +173,15 @@ export interface ExecutionProfile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const extractData = <T>(value: unknown): T => {
|
const extractData = <T>(value: unknown): T => {
|
||||||
if (value && typeof value === 'object' && 'data' in value) {
|
let current = value;
|
||||||
return (value as { data: T }).data;
|
for (let depth = 0; depth < 3; depth += 1) {
|
||||||
|
if (current && typeof current === 'object' && 'data' in current) {
|
||||||
|
current = (current as { data: unknown }).data;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
return value as T;
|
break;
|
||||||
|
}
|
||||||
|
return current as T;
|
||||||
};
|
};
|
||||||
|
|
||||||
const normalizeLoadedModels = (models: Array<string | LoadedModelInfo> | undefined): LoadedModelInfo[] => {
|
const normalizeLoadedModels = (models: Array<string | LoadedModelInfo> | undefined): LoadedModelInfo[] => {
|
||||||
@@ -199,6 +205,7 @@ const normalizeVramStatus = (value: unknown): VramStatusResponse => {
|
|||||||
const totalVRAMMB = raw.totalVRAMMB ?? raw.totalVramMb ?? 0;
|
const totalVRAMMB = raw.totalVRAMMB ?? raw.totalVramMb ?? 0;
|
||||||
const usedVRAMMB = raw.usedVRAMMB ?? raw.usedVramMb ?? 0;
|
const usedVRAMMB = raw.usedVRAMMB ?? raw.usedVramMb ?? 0;
|
||||||
const usagePercent = raw.usagePercent ?? (totalVRAMMB > 0 ? Math.round((usedVRAMMB / totalVRAMMB) * 100) : 0);
|
const usagePercent = raw.usagePercent ?? (totalVRAMMB > 0 ? Math.round((usedVRAMMB / totalVRAMMB) * 100) : 0);
|
||||||
|
const hasKnownCapacity = totalVRAMMB > 0;
|
||||||
|
|
||||||
// Backend now sends loadedModels with vramUsageMB directly
|
// Backend now sends loadedModels with vramUsageMB directly
|
||||||
const loadedModels = normalizeLoadedModels(raw.loadedModels);
|
const loadedModels = normalizeLoadedModels(raw.loadedModels);
|
||||||
@@ -209,7 +216,7 @@ const normalizeVramStatus = (value: unknown): VramStatusResponse => {
|
|||||||
usagePercent,
|
usagePercent,
|
||||||
thresholdPercent: raw.thresholdPercent ?? 90,
|
thresholdPercent: raw.thresholdPercent ?? 90,
|
||||||
loadedModels,
|
loadedModels,
|
||||||
canLoadModel: raw.canLoadModel ?? raw.hasCapacity ?? false,
|
canLoadModel: hasKnownCapacity ? (raw.canLoadModel ?? raw.hasCapacity ?? false) : true,
|
||||||
lastUpdated: raw.lastUpdated ?? new Date().toISOString(),
|
lastUpdated: raw.lastUpdated ?? new Date().toISOString(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -45,6 +45,9 @@
|
|||||||
| D15 | SandboxTabs ต้องโหลด active prompts ทั้ง ocr_system และ ocr_extraction จาก service เพื่อแสดง prompt info ทั้ง 2 steps ตาม FR-009, FR-010 (Feature-238) | Feature-238 |
|
| D15 | SandboxTabs ต้องโหลด active prompts ทั้ง ocr_system และ ocr_extraction จาก service เพื่อแสดง prompt info ทั้ง 2 steps ตาม FR-009, FR-010 (Feature-238) | Feature-238 |
|
||||||
| D16 | Backend VRAM service ต้องส่ง loadedModels พร้อม vramUsageMB (bytes → MB) เพื่อให้ frontend แสดงผล VRAM usage ของแต่ละ model ได้ถูกต้อง | Session 2026-06-18 |
|
| D16 | Backend VRAM service ต้องส่ง loadedModels พร้อม vramUsageMB (bytes → MB) เพื่อให้ frontend แสดงผล VRAM usage ของแต่ละ model ได้ถูกต้อง | Session 2026-06-18 |
|
||||||
| D17 | สถานะพับ/คลี่ของการ์ดและเซกชันในหน้า AI Admin Console จะเก็บลงใน localStorage เพื่อรักษาสถานะ และการพับไม่มีผลต่อ background query polling | Feature-240 |
|
| D17 | สถานะพับ/คลี่ของการ์ดและเซกชันในหน้า AI Admin Console จะเก็บลงใน localStorage เพื่อรักษาสถานะ และการพับไม่มีผลต่อ background query polling | Feature-240 |
|
||||||
|
| D18 | Deploy script ต้องตรวจสอบ ClamAV health status ก่อน recreation — ถ้า healthy ให้ recreate เฉพาะ backend/frontend (skip 5-minute healthcheck delay) | Session 2026-06-19 |
|
||||||
|
| D19 | CI timeout ต้องอย่างน้อย 30 minutes เพื่อรองรับ ClamAV startup กรณีต้อง recreate full stack | Session 2026-06-19 |
|
||||||
|
| D20 | AI Admin frontend services ต้อง normalize API response envelope ที่อาจซ้อน `data` ก่อน render; VRAM `totalVRAMMB = 0` คือ unknown capacity ไม่ใช่ OOM Guard | Session 2026-06-19 |
|
||||||
|
|
||||||
## Environment & Services
|
## Environment & Services
|
||||||
|
|
||||||
@@ -173,4 +176,3 @@ QDRANT_URL
|
|||||||
- [x] **Background Polling:** การพับเก็บไม่มีผลกระทบต่อการดึงข้อมูลสถานะในพื้นหลังผ่าน TanStack Query
|
- [x] **Background Polling:** การพับเก็บไม่มีผลกระทบต่อการดึงข้อมูลสถานะในพื้นหลังผ่าน TanStack Query
|
||||||
- [x] **Validation & Quality:** ผ่านการตรวจสอบประเภท (tsc) และ Lint (eslint) พร้อมสร้างรายงาน validation-report.md
|
- [x] **Validation & Quality:** ผ่านการตรวจสอบประเภท (tsc) และ Lint (eslint) พร้อมสร้างรายงาน validation-report.md
|
||||||
- **Branch:** `240-ai-console-collapsible-cards`
|
- **Branch:** `240-ai-console-collapsible-cards`
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: 'Data & Storage: Data Dictionary and Data Model Architecture'
|
title: 'Data & Storage: Data Dictionary and Data Model Architecture'
|
||||||
version: 1.9.1
|
version: 1.9.2
|
||||||
status: released
|
status: released
|
||||||
owner: Nattanin Peancharoen
|
owner: Nattanin Peancharoen
|
||||||
last_updated: 2026-05-16
|
last_updated: 2026-06-19
|
||||||
related:
|
related:
|
||||||
- specs/01-requirements/02-architecture.md
|
- specs/01-requirements/02-architecture.md
|
||||||
- specs/01-requirements/03-functional-requirements.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 |
|
| `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) |
|
| `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 |
|
| `model_name` | VARCHAR(100) | NO | Local model name used by ADR-023 AI pipeline |
|
||||||
| `ai_suggestion_json` | JSON | YES | AI suggested metadata |
|
| `effective_profile` | VARCHAR(50) | YES | ExecutionProfile ที่ backend กำหนด: interactive\|standard\|quality\|deep-analysis (Feature-235) |
|
||||||
| `human_override_json` | JSON | YES | Human approved or overridden metadata |
|
| `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 |
|
| `processing_time_ms` | INT | YES | Legacy processing duration field |
|
||||||
| `confidence_score` | DECIMAL(4,3) | YES | AI confidence score 0.000-1.000 |
|
| `confidence_score` | DECIMAL(4,3) | YES | AI confidence score 0.000-1.000 |
|
||||||
| `input_hash` | VARCHAR(64) | YES | Legacy SHA-256 input hash |
|
| `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_model_name (model_name)
|
||||||
- KEY idx_ai_audit_status (status)
|
- KEY idx_ai_audit_status (status)
|
||||||
- KEY idx_ai_audit_confirmed_by (confirmed_by_user_id)
|
- 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
|
- CONSTRAINT fk_ai_audit_confirmed_by_user FOREIGN KEY (confirmed_by_user_id) REFERENCES users (user_id) ON DELETE SET NULL
|
||||||
|
|
||||||
#### Business Rules
|
#### 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
|
**วัตถุประสงค์:** เก็บ 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.000–1.000)',
|
|
||||||
created_by INT COMMENT 'ผู้เชื่อมโยงแท็ก',
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่เชื่อมโยง',
|
|
||||||
PRIMARY KEY (correspondence_id, tag_id),
|
|
||||||
FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL,
|
|
||||||
INDEX idx_correspondence_tags_lookup (tag_id)
|
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมโยงความสัมพันธ์แบบ M:N ระหว่างเอกสารและแท็ก';
|
|
||||||
@@ -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;
|
|
||||||
-15
@@ -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;
|
|
||||||
-28
@@ -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;
|
|
||||||
-108
@@ -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'
|
|
||||||
);
|
|
||||||
-11
@@ -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;
|
|
||||||
-16
@@ -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;
|
|
||||||
-18
@@ -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 ไว้แล้ว
|
|
||||||
-19
@@ -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);
|
|
||||||
-16
@@ -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 'ผู้แก้ไขล่าสุด',
|
updated_by INT COMMENT 'ผู้แก้ไขล่าสุด',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL,
|
FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE
|
||||||
|
SET NULL,
|
||||||
INDEX idx_system_settings_category (category),
|
INDEX idx_system_settings_category (category),
|
||||||
INDEX idx_system_settings_is_public (is_public)
|
INDEX idx_system_settings_is_public (is_public)
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บข้อมูลการตั้งค่าระบบไดนามิก';
|
) 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 เก็บ "บทบาท" ของผู้ใช้ในระบบ
|
-- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ
|
||||||
CREATE TABLE roles (
|
CREATE TABLE roles (
|
||||||
role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
|
role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
|
||||||
@@ -395,31 +378,37 @@ CREATE TABLE correspondence_revisions (
|
|||||||
-- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ
|
-- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ
|
||||||
CREATE TABLE tags (
|
CREATE TABLE tags (
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
|
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)',
|
project_id INT NULL COMMENT 'ID โครงการ (NULL = Global Tag)',
|
||||||
tag_name VARCHAR(100) NOT NULL COMMENT 'ชื่อ Tag',
|
tag_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแท็ก',
|
||||||
color_code VARCHAR(30) DEFAULT 'default' COMMENT 'รหัสสี หรือชื่อคลาสสำหรับ UI',
|
color_code VARCHAR(30) DEFAULT 'default' COMMENT 'รหัสสีสำหรับ UI',
|
||||||
description TEXT COMMENT 'คำอธิบายแท็ก',
|
description TEXT COMMENT 'คำอธิบายเพิ่มเติม',
|
||||||
|
created_by INT COMMENT 'ผู้สร้างแท็ก',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง',
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง',
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด',
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด',
|
||||||
created_by INT COMMENT 'ผู้สร้าง',
|
deleted_at TIMESTAMP NULL COMMENT 'วันที่ลบ (Soft Delete)',
|
||||||
deleted_at DATETIME NULL COMMENT 'ลบแบบ Soft Delete',
|
|
||||||
-- Constraints & Indexes
|
|
||||||
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
|
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (created_by) REFERENCES users (user_id) ON DELETE
|
FOREIGN KEY (created_by) REFERENCES users (user_id) ON DELETE
|
||||||
SET NULL,
|
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)
|
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)
|
-- ตารางเชื่อมระหว่าง correspondences และ tags (M:N)
|
||||||
CREATE TABLE correspondence_tags (
|
CREATE TABLE correspondence_tags (
|
||||||
correspondence_id INT COMMENT 'ID ของเอกสาร',
|
correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร',
|
||||||
tag_id INT COMMENT 'ID ของ Tag',
|
tag_id INT NOT NULL COMMENT 'ID ของแท็ก',
|
||||||
|
is_ai_suggested BOOLEAN DEFAULT FALSE COMMENT 'แท็กนี้แนะนำโดย AI หรือไม่',
|
||||||
|
confidence DECIMAL(4, 3) NULL COMMENT 'ค่าความมั่นใจของ AI (0.000–1.000)',
|
||||||
|
created_by INT COMMENT 'ผู้เชื่อมโยงแท็ก',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่เชื่อมโยง',
|
||||||
PRIMARY KEY (correspondence_id, tag_id),
|
PRIMARY KEY (correspondence_id, tag_id),
|
||||||
FOREIGN KEY (correspondence_id) REFERENCES correspondences (id) ON DELETE CASCADE,
|
FOREIGN KEY (correspondence_id) REFERENCES correspondences (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE,
|
FOREIGN KEY (tag_id) REFERENCES tags (id) ON DELETE CASCADE,
|
||||||
INDEX idx_tag_lookup (tag_id)
|
FOREIGN KEY (created_by) REFERENCES users (user_id) ON DELETE
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง correspondences และ tags (M:N)';
|
SET NULL,
|
||||||
|
INDEX idx_correspondence_tags_lookup (tag_id)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมโยงความสัมพันธ์แบบ M:N ระหว่างเอกสารและแท็ก';
|
||||||
|
|
||||||
-- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N)
|
-- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N)
|
||||||
CREATE TABLE correspondence_references (
|
CREATE TABLE correspondence_references (
|
||||||
@@ -1017,10 +1006,7 @@ CREATE TABLE contract_drawing_attachments (
|
|||||||
'OTHER '
|
'OTHER '
|
||||||
) COMMENT 'ประเภทไฟล์',
|
) COMMENT 'ประเภทไฟล์',
|
||||||
is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)',
|
is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)',
|
||||||
PRIMARY KEY (
|
PRIMARY KEY (contract_drawing_id, attachment_id),
|
||||||
contract_drawing_id,
|
|
||||||
attachment_id
|
|
||||||
),
|
|
||||||
FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings (id) ON DELETE CASCADE,
|
FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (attachment_id) REFERENCES attachments (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)';
|
) 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 'วันที่แก้ไขล่าสุด',
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด',
|
||||||
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
|
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
|
||||||
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types (id) ON DELETE CASCADE,
|
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types (id) ON DELETE CASCADE,
|
||||||
UNIQUE KEY unique_format (
|
UNIQUE KEY unique_format (project_id, correspondence_type_id)
|
||||||
project_id,
|
|
||||||
correspondence_type_id
|
|
||||||
)
|
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร';
|
) 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_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'ประเภท (DATABASE, FILES, FULL)',
|
||||||
backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง',
|
backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง',
|
||||||
file_size BIGINT COMMENT 'ขนาดไฟล์',
|
file_size BIGINT COMMENT 'ขนาดไฟล์',
|
||||||
STATUS ENUM(
|
STATUS ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'สถานะ',
|
||||||
'STARTED',
|
|
||||||
'COMPLETED',
|
|
||||||
'FAILED'
|
|
||||||
) NOT NULL COMMENT 'สถานะ',
|
|
||||||
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น',
|
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น',
|
||||||
completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น',
|
completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น',
|
||||||
error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)'
|
error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)'
|
||||||
@@ -1508,10 +1487,16 @@ CREATE TABLE migration_review_queue (
|
|||||||
idempotency_key VARCHAR(200) NOT NULL COMMENT 'Idempotency-Key สำหรับป้องกัน queue ซ้ำ',
|
idempotency_key VARCHAR(200) NOT NULL COMMENT 'Idempotency-Key สำหรับป้องกัน queue ซ้ำ',
|
||||||
original_filename VARCHAR(500) NOT NULL COMMENT 'ชื่อไฟล์ต้นฉบับจาก legacy source',
|
original_filename VARCHAR(500) NOT NULL COMMENT 'ชื่อไฟล์ต้นฉบับจาก legacy source',
|
||||||
storage_temp_path VARCHAR(1000) NOT NULL COMMENT 'temp storage path ก่อน import',
|
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',
|
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 หรือไม่',
|
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)',
|
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_by INT NULL COMMENT 'Internal users.user_id ของผู้ review',
|
||||||
reviewed_at DATETIME NULL COMMENT 'เวลาที่ review record',
|
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',
|
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',
|
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',
|
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',
|
effective_profile VARCHAR(50) NULL COMMENT 'ExecutionProfile ที่ backend กำหนด: interactive|standard|quality|deep-analysis (Feature-235)',
|
||||||
human_override_json JSON NULL COMMENT 'Human approved or overridden metadata',
|
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',
|
processing_time_ms INT NULL COMMENT 'Legacy processing duration field',
|
||||||
confidence_score DECIMAL(4, 3) NULL COMMENT 'AI confidence score 0.000-1.000',
|
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',
|
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_model_name (model_name),
|
||||||
KEY idx_ai_audit_status (STATUS),
|
KEY idx_ai_audit_status (STATUS),
|
||||||
KEY idx_ai_audit_confirmed_by (confirmed_by_user_id),
|
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
|
CONSTRAINT fk_ai_audit_confirmed_by_user FOREIGN KEY (confirmed_by_user_id) REFERENCES users (user_id) ON DELETE
|
||||||
SET NULL
|
SET NULL
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ADR-023 AI development feedback log';
|
) 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
|
-- Intent Definitions Table
|
||||||
CREATE TABLE IF NOT EXISTS ai_intent_definitions (
|
CREATE TABLE IF NOT EXISTS ai_intent_definitions (
|
||||||
|
|||||||
@@ -18,6 +18,43 @@
|
|||||||
-- 2.1 username = migration_bot
|
-- 2.1 username = migration_bot
|
||||||
-- 2.2
|
-- 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)
|
INSERT INTO organization_roles (id, role_name)
|
||||||
VALUES (1, 'OWNER'),
|
VALUES (1, 'OWNER'),
|
||||||
(2, 'DESIGNER'),
|
(2, 'DESIGNER'),
|
||||||
@@ -2648,3 +2685,156 @@ VALUES (
|
|||||||
NOW(),
|
NOW(),
|
||||||
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()
|
||||||
|
);
|
||||||
|
|||||||
@@ -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 | 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-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 | 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
|
||||||
Reference in New Issue
Block a user