690603:2041 ADR-034-134 #01
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# Specification Quality Checklist: Thai-Optimized AI Model Stack
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-06-03
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded (nomic-embed-text excluded; n8n boundary explicit)
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows (OCR, main AI, health check)
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- ADR-034 is the authoritative source; spec captures user-value perspective
|
||||
- SC-001 OCR quality metric (>90% readable) is aspirational; verify with real docs post-deploy
|
||||
- A1 (internet access on Desk-5439) should be confirmed before deploy
|
||||
@@ -0,0 +1,142 @@
|
||||
// File: specs/100-Infrastructures/134-ai-model-change/contracts/ollama-service-methods.md
|
||||
// Change Log:
|
||||
// - 2026-06-03: API contracts for OllamaService model management methods
|
||||
|
||||
# Contract: OllamaService Model Management Methods
|
||||
|
||||
---
|
||||
|
||||
## Method: unloadModel
|
||||
|
||||
**Signature**: `async unloadModel(modelName: string): Promise<void>`
|
||||
**Location**: `backend/src/modules/ai/services/ollama.service.ts`
|
||||
**Purpose**: Force unload a model from Ollama VRAM
|
||||
|
||||
### Ollama API Call
|
||||
|
||||
```http
|
||||
POST http://{OLLAMA_BASE_URL}/api/generate
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "{modelName}",
|
||||
"prompt": "",
|
||||
"keep_alive": 0
|
||||
}
|
||||
```
|
||||
|
||||
### Success Behavior
|
||||
|
||||
- HTTP 200 → model unloaded from VRAM
|
||||
- Model already unloaded / not found → log warn; no-op (not fatal)
|
||||
|
||||
### Error Handling
|
||||
|
||||
| Error | Behavior |
|
||||
|-------|----------|
|
||||
| HTTP timeout (> 5s) | Throw `Error('Failed to unload model {modelName}: timeout')` |
|
||||
| Network error | Throw `Error('Failed to unload model {modelName}: {axiosError.message}')` |
|
||||
| HTTP 404 (model not found) | Log warn; resolve without throwing |
|
||||
|
||||
---
|
||||
|
||||
## Method: loadModel
|
||||
|
||||
**Signature**: `async loadModel(modelName: string, keepAlive?: number | string): Promise<void>`
|
||||
**Location**: `backend/src/modules/ai/services/ollama.service.ts`
|
||||
**Purpose**: Load / warm a model into Ollama VRAM
|
||||
|
||||
### Ollama API Call
|
||||
|
||||
```http
|
||||
POST http://{OLLAMA_BASE_URL}/api/generate
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "{modelName}",
|
||||
"prompt": "",
|
||||
"keep_alive": {keepAlive ?? -1}
|
||||
}
|
||||
```
|
||||
|
||||
### keepAlive Values
|
||||
|
||||
| Value | Behavior |
|
||||
|-------|----------|
|
||||
| `-1` | Model stays in VRAM indefinitely (main model) |
|
||||
| `0` | Model unloads after request completes (OCR model) |
|
||||
| `"5m"` | Model unloads after 5 minutes (alternative) |
|
||||
|
||||
### Success Behavior
|
||||
|
||||
- HTTP 200 → model loaded into VRAM
|
||||
|
||||
### Error Handling
|
||||
|
||||
| Error | Behavior |
|
||||
|-------|----------|
|
||||
| HTTP timeout (> 60s, cold start) | Throw `Error('Failed to load model {modelName}: timeout (cold start may take up to 60s)')` |
|
||||
| HTTP 404 (model not found) | Throw `Error('Model {modelName} not found on Ollama. Run: ollama create {modelName} -f ./{modelName}.model.md')` |
|
||||
| Network error | Throw `Error('Failed to load model {modelName}: {axiosError.message}')` |
|
||||
|
||||
---
|
||||
|
||||
## BullMQ Processor: Model Switching Contract
|
||||
|
||||
**Location**: `backend/src/modules/ai/processors/ai-batch.processor.ts`
|
||||
|
||||
### OCR Job Types Constant
|
||||
|
||||
```typescript
|
||||
const OCR_JOB_TYPES: string[] = ['ocr-extract', 'sandbox-ocr-only'];
|
||||
```
|
||||
|
||||
### Switching Sequence (Pseudocode)
|
||||
|
||||
```
|
||||
processJob(job: Job):
|
||||
if job.data.jobType ∈ OCR_JOB_TYPES:
|
||||
log.log(`[ModelSwitch] Unloading ${DEFAULT_MODEL}`)
|
||||
await ollamaService.unloadModel(DEFAULT_MODEL) // free ~2.5GB
|
||||
|
||||
log.log(`[ModelSwitch] Loading ${OCR_MODEL} (keep_alive: 0)`)
|
||||
await ollamaService.loadModel(OCR_MODEL, 0) // load ~3.2GB
|
||||
|
||||
result = await runOcrJob(job)
|
||||
// OCR model auto-unloads via keep_alive: 0
|
||||
|
||||
log.log(`[ModelSwitch] Reloading ${DEFAULT_MODEL} (keep_alive: -1)`)
|
||||
await ollamaService.loadModel(DEFAULT_MODEL, -1) // restore main model
|
||||
|
||||
return result
|
||||
else:
|
||||
return await runMainModelJob(job)
|
||||
```
|
||||
|
||||
### Concurrency Constraint
|
||||
|
||||
- BullMQ concurrency = 1 (ป้องกัน VRAM overflow)
|
||||
- ไม่มี parallel OCR + main jobs
|
||||
- ถ้า job fail ระหว่าง switching → BullMQ retry จาก ต้นใหม่ทั้ง sequence
|
||||
|
||||
---
|
||||
|
||||
## Health Response Contract Update
|
||||
|
||||
**Endpoint**: `GET /api/ai/health`
|
||||
**Location**: `backend/src/modules/ai/ai.service.ts`
|
||||
|
||||
### Updated Response Shape
|
||||
|
||||
```typescript
|
||||
interface SystemHealthResponse {
|
||||
ollama: {
|
||||
status: 'HEALTHY' | 'DOWN';
|
||||
mainModel: string; // 'typhoon2.5-np-dms:latest'
|
||||
ocrModel: string; // 'typhoon-np-dms-ocr:latest'
|
||||
latencyMs?: number;
|
||||
};
|
||||
ocr: OcrHealthResult;
|
||||
// ... existing fields
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,116 @@
|
||||
// File: specs/100-Infrastructures/134-ai-model-change/data-model.md
|
||||
// Change Log:
|
||||
// - 2026-06-03: Data model for Thai-Optimized AI Model Stack
|
||||
|
||||
# Data Model: Thai-Optimized AI Model Stack
|
||||
|
||||
---
|
||||
|
||||
## Database Schema Changes
|
||||
|
||||
**ไม่มี new tables หรือ column changes** — ADR-009 compliant; ไม่มี TypeORM migration
|
||||
|
||||
### Optional: ai_available_models Seed Update
|
||||
|
||||
ตาราง `ai_available_models` (จาก ADR-027 Session 6) ควร update เพื่อความสอดคล้อง:
|
||||
|
||||
**Update existing main model record:**
|
||||
|
||||
```sql
|
||||
UPDATE ai_available_models
|
||||
SET model_name = 'typhoon2.5-np-dms:latest',
|
||||
display_name = 'Typhoon 2.5 NP-DMS (Thai)',
|
||||
updated_at = NOW()
|
||||
WHERE model_type = 'main' AND is_active = 1;
|
||||
```
|
||||
|
||||
**Insert OCR model record (if column model_type supports 'ocr'):**
|
||||
|
||||
```sql
|
||||
INSERT INTO ai_available_models (model_name, display_name, model_type, is_active, created_at, updated_at)
|
||||
VALUES ('typhoon-np-dms-ocr:latest', 'Typhoon OCR 3B (Thai)', 'ocr', 1, NOW(), NOW())
|
||||
ON DUPLICATE KEY UPDATE display_name = VALUES(display_name), updated_at = NOW();
|
||||
```
|
||||
|
||||
> **Note**: ตรวจสอบ schema จริงใน `lcbp3-v1.9.0-schema-02-tables.sql` ก่อน run delta — column names และ model_type ENUM อาจแตกต่าง
|
||||
|
||||
---
|
||||
|
||||
## Code Configuration Model
|
||||
|
||||
### AiSettingsService Constants
|
||||
|
||||
| Constant | เดิม | ใหม่ |
|
||||
|----------|------|------|
|
||||
| `DEFAULT_MODEL` | `'gemma4:e2b'` | `'typhoon2.5-np-dms:latest'` |
|
||||
| `OCR_MODEL` (เพิ่มใหม่) | — | `'typhoon-np-dms-ocr:latest'` |
|
||||
|
||||
**File**: `backend/src/modules/ai/services/ai-settings.service.ts`
|
||||
|
||||
---
|
||||
|
||||
## Custom Ollama Model Configurations
|
||||
|
||||
### typhoon2.5-np-dms (Main Model)
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Custom Name | `typhoon2.5-np-dms:latest` |
|
||||
| Base Model | `scb10x/typhoon2.5-qwen3-4b:latest` |
|
||||
| Size | ~2.5GB VRAM |
|
||||
| keep_alive | Indefinite (`-1`) — standby ตลอด |
|
||||
| num_ctx | 8192 |
|
||||
| num_predict | 2048 |
|
||||
| temperature | 0.1 |
|
||||
| top_p | 0.85 |
|
||||
| repeat_penalty | 1.15 |
|
||||
| Role | Extraction, RAG Q&A, AI Suggestion, OCR Post-processing |
|
||||
| Modelfile Path | `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/typhoon2.5-np-dms.model.md` |
|
||||
|
||||
### typhoon-np-dms-ocr (OCR Model)
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Custom Name | `typhoon-np-dms-ocr:latest` |
|
||||
| Base Model | `scb10x/typhoon-ocr1.5-3b:latest` |
|
||||
| Size | ~3.2GB VRAM |
|
||||
| keep_alive | `0` — auto-unload immediately after job |
|
||||
| num_ctx | 8192 |
|
||||
| num_predict | 4096 |
|
||||
| temperature | 0.1 |
|
||||
| top_p | 0.1 |
|
||||
| repeat_penalty | 1.1 |
|
||||
| Role | Thai OCR extraction from PDF images |
|
||||
| Modelfile Path | `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/typhoon-np-dms-ocr.model.md` |
|
||||
|
||||
---
|
||||
|
||||
## VRAM Budget Analysis
|
||||
|
||||
| State | Models in VRAM | Estimated VRAM Usage |
|
||||
|-------|---------------|---------------------|
|
||||
| Idle | typhoon2.5-np-dms (standby) | ~2.5GB |
|
||||
| Main AI job | typhoon2.5-np-dms (active) | ~3.5–4GB peak |
|
||||
| OCR transition | unloading main → loading OCR | ~3.2GB (OCR only) |
|
||||
| OCR processing | typhoon-np-dms-ocr (active) | ~4–5GB peak |
|
||||
| Post-OCR reload | loading main back | ~2.5GB |
|
||||
| **Max peak** | OCR model active | **~5GB** (safe under 8GB limit) |
|
||||
|
||||
---
|
||||
|
||||
## OllamaService New Methods Signatures
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* unloadModel: Force unload model จาก VRAM
|
||||
* @param modelName - ชื่อ Ollama model ที่ต้องการ unload
|
||||
*/
|
||||
async unloadModel(modelName: string): Promise<void>
|
||||
|
||||
/**
|
||||
* loadModel: Load / warm model เข้า VRAM
|
||||
* @param modelName - ชื่อ Ollama model ที่ต้องการ load
|
||||
* @param keepAlive - -1 = indefinite (default สำหรับ main model); 0 = auto-unload
|
||||
*/
|
||||
async loadModel(modelName: string, keepAlive?: number | string): Promise<void>
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
// File: specs/100-Infrastructures/134-ai-model-change/plan.md
|
||||
// Change Log:
|
||||
// - 2026-06-03: Initial implementation plan for Thai-Optimized AI Model Stack (ADR-034)
|
||||
|
||||
# Implementation Plan: Thai-Optimized AI Model Stack
|
||||
|
||||
**Branch**: `134-ai-model-change` | **Date**: 2026-06-03 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `specs/100-Infrastructures/134-ai-model-change/spec.md`
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
เปลี่ยน AI model stack จาก `gemma4:e2b` เป็น custom Typhoon models: `typhoon2.5-np-dms:latest` (main AI) + `typhoon-np-dms-ocr:latest` (Thai OCR) ตาม ADR-034. ต้อง implement:
|
||||
1. Custom Modelfiles บน Desk-5439 (มีอยู่แล้วใน `specs/04-Infrastructure-OPS/...`)
|
||||
2. `unloadModel()` + `loadModel()` ใน `OllamaService`
|
||||
3. Model switching logic ใน `ai-batch.processor.ts`
|
||||
4. อัปเดต `AiSettingsService.DEFAULT_MODEL`
|
||||
5. SQL delta สำหรับ `ai_available_models` table
|
||||
|
||||
---
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.x, NestJS 11, Node.js 20
|
||||
**Primary Dependencies**: `@nestjs/bull`, `bullmq`, `axios` (Ollama HTTP client), Redis
|
||||
**Storage**: ไม่มี schema changes — optional SQL delta สำหรับ `ai_available_models` seed
|
||||
**Testing**: Jest (unit tests: OllamaService methods + ai-batch.processor switching)
|
||||
**Target Platform**: QNAP NAS backend (Docker) + Desk-5439 (Ollama runtime, RTX 2060 Super 8GB)
|
||||
**Performance Goals**: OCR cold start ≤ 60s; main model warm ≤ 5s; zero VRAM OOM
|
||||
**Constraints**: VRAM ≤ 8GB; BullMQ concurrency=1; ADR-033 VRAM monitor preserved; ADR-023A AI boundary
|
||||
**Scale/Scope**: 1 Desk-5439 Ollama instance; 1 concurrent AI job at a time (BullMQ)
|
||||
|
||||
---
|
||||
|
||||
## Constitution Check
|
||||
|
||||
_GATE: Must pass before Phase 0 research. Re-checked after Phase 1 design._
|
||||
|
||||
| Rule | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| ADR-019 UUID: ไม่มี parseInt บน UUID | ✅ PASS | ไม่มี UUID changes ใน feature นี้ |
|
||||
| ADR-009 Schema: ไม่มี TypeORM migration | ✅ PASS | ไม่มี table ใหม่; SQL delta สำหรับ seed update เท่านั้น |
|
||||
| ADR-023/023A AI Boundary: Ollama บน Desk-5439 | ✅ PASS | ยังคง pattern เดิม; ไม่มี direct DB/storage access |
|
||||
| ADR-033 VRAM Monitor: mechanism ยังใช้งาน | ✅ PASS | ชื่อ model เปลี่ยน; mechanism เดิม |
|
||||
| ADR-008 BullMQ: Background jobs ผ่าน queue | ✅ PASS | ไม่มี inline AI call ใหม่ |
|
||||
| ADR-007 Error Handling: Error classification | ✅ PASS | unload/load errors ต้องมี descriptive messages |
|
||||
| ADR-016 Security: CASL Guard | ✅ PASS | ไม่มี new public endpoints |
|
||||
| Forbidden: console.log, any type | ✅ PASS | ต้องตรวจสอบขณะ implement (ESLint enforce) |
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/100-Infrastructures/134-ai-model-change/
|
||||
├── spec.md # Feature specification
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0: decisions
|
||||
├── data-model.md # Phase 1: config/model data
|
||||
├── quickstart.md # Phase 1: verification guide
|
||||
├── contracts/
|
||||
│ └── ollama-service-methods.md # Method signatures + Ollama API
|
||||
├── checklists/
|
||||
│ └── requirements.md # Spec quality checklist
|
||||
└── tasks.md # Phase 2: task list
|
||||
```
|
||||
|
||||
### Infrastructure Files (already exist)
|
||||
|
||||
```text
|
||||
specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/
|
||||
├── typhoon2.5-np-dms.model.md # Main model Modelfile ✅ exists
|
||||
└── typhoon-np-dms-ocr.model.md # OCR model Modelfile ✅ exists
|
||||
```
|
||||
|
||||
### Source Code Changes
|
||||
|
||||
```text
|
||||
backend/src/modules/ai/
|
||||
├── services/
|
||||
│ ├── ollama.service.ts # เพิ่ม unloadModel() + loadModel()
|
||||
│ └── ai-settings.service.ts # อัปเดต DEFAULT_MODEL + เพิ่ม OCR_MODEL
|
||||
└── processors/
|
||||
└── ai-batch.processor.ts # เพิ่ม OCR model switching logic
|
||||
```
|
||||
|
||||
### Optional Delta
|
||||
|
||||
```text
|
||||
specs/03-Data-and-Storage/deltas/
|
||||
└── 2026-06-03-update-ai-available-models-typhoon.sql
|
||||
```
|
||||
|
||||
**Structure Decision**: Web application (backend only) — ไม่มี frontend changes ที่จำเป็น; health UI อาจ update model names เองจาก dynamic data
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Research
|
||||
|
||||
ผลการ research ดู [research.md](./research.md) — decisions ถูก resolve จาก ADR-034 แล้ว
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Design & Contracts
|
||||
|
||||
### Data Model
|
||||
|
||||
ดู [data-model.md](./data-model.md)
|
||||
|
||||
- ไม่มี new DB entities
|
||||
- Code constants ใน `AiSettingsService` อัปเดต
|
||||
- Optional: `ai_available_models` seed delta
|
||||
|
||||
### API Contracts
|
||||
|
||||
ดู [contracts/ollama-service-methods.md](./contracts/ollama-service-methods.md)
|
||||
|
||||
- `OllamaService.unloadModel(modelName: string): Promise<void>`
|
||||
- `OllamaService.loadModel(modelName: string, keepAlive?: number | string): Promise<void>`
|
||||
- BullMQ processor switching pseudocode
|
||||
|
||||
### Quickstart Verification
|
||||
|
||||
ดู [quickstart.md](./quickstart.md)
|
||||
|
||||
---
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
ไม่มี Constitution Check violations ที่ต้องจัดการ
|
||||
@@ -0,0 +1,111 @@
|
||||
// File: specs/100-Infrastructures/134-ai-model-change/quickstart.md
|
||||
// Change Log:
|
||||
// - 2026-06-03: Verification guide for Thai-Optimized AI Model Stack
|
||||
|
||||
# Quickstart: Thai-Optimized AI Model Stack Verification
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Desk-5439 รัน Ollama service (port 11434)
|
||||
- Internet access บน Desk-5439 (สำหรับ pull base models จาก registry)
|
||||
- QNAP backend container running (port 3001)
|
||||
- Model files อยู่ที่ `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/`
|
||||
|
||||
---
|
||||
|
||||
## Step 1: สร้าง Custom Models บน Desk-5439
|
||||
|
||||
```powershell
|
||||
# บน Desk-5439 Windows — เปิด PowerShell ใน directory ที่มี Modelfiles
|
||||
# Path: specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/
|
||||
|
||||
ollama create typhoon2.5-np-dms -f .\typhoon2.5-np-dms.model.md
|
||||
# คาดว่าใช้เวลา: 5-15 นาที (download base model ~2.5GB)
|
||||
|
||||
ollama create typhoon-np-dms-ocr -f .\typhoon-np-dms-ocr.model.md
|
||||
# คาดว่าใช้เวลา: 5-15 นาที (download base model ~3.2GB)
|
||||
|
||||
# ตรวจสอบ
|
||||
ollama list
|
||||
# ต้องเห็น:
|
||||
# typhoon2.5-np-dms:latest
|
||||
# typhoon-np-dms-ocr:latest
|
||||
# nomic-embed-text:latest (ยังคงอยู่ — embedding model ไม่เปลี่ยน)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Apply SQL Delta (ถ้า ai_available_models table มีอยู่)
|
||||
|
||||
```powershell
|
||||
# รัน delta ผ่าน DB admin tool หรือ mysql client
|
||||
# File: specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Deploy Backend
|
||||
|
||||
ปฏิบัติตาม `/deploy` workflow ปกติ (QNAP Container Station)
|
||||
|
||||
---
|
||||
|
||||
## Step 4: ตรวจสอบ Health Endpoint
|
||||
|
||||
```powershell
|
||||
Invoke-RestMethod -Uri "http://localhost:3001/api/ai/health" -Method GET |
|
||||
ConvertTo-Json -Depth 5
|
||||
# ตรวจสอบ:
|
||||
# ollama.mainModel = "typhoon2.5-np-dms:latest"
|
||||
# ollama.status = "HEALTHY"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: ทดสอบ OCR Job
|
||||
|
||||
```powershell
|
||||
# ส่ง OCR job ผ่าน AI Admin Console → OCR Sandbox
|
||||
# หรือ POST /api/ai/admin/sandbox/ocr ด้วย PDF ภาษาไทย
|
||||
# ตรวจสอบ result:
|
||||
# - ocrText มีภาษาไทยที่อ่านออกได้
|
||||
# - ไม่มี VRAM OOM error ใน logs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 6: ตรวจสอบ BullMQ Model Switching Logs
|
||||
|
||||
```powershell
|
||||
# ดู backend logs ขณะ OCR job กำลังทำงาน
|
||||
docker compose logs -f backend | Select-String "ModelSwitch"
|
||||
# ต้องเห็น:
|
||||
# [ModelSwitch] Unloading typhoon2.5-np-dms:latest
|
||||
# [ModelSwitch] Loading typhoon-np-dms-ocr:latest (keep_alive: 0)
|
||||
# [ModelSwitch] Reloading typhoon2.5-np-dms:latest (keep_alive: -1)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 7: ตรวจสอบ VRAM ไม่เกิน 8GB
|
||||
|
||||
```powershell
|
||||
# บน Desk-5439 ขณะ OCR job กำลังทำงาน
|
||||
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
|
||||
# ต้องไม่เกิน 8192 MiB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
|
||||
หากพบปัญหาหลัง deploy:
|
||||
|
||||
1. สร้าง custom model ใหม่จาก `gemma4:e2b`:
|
||||
```powershell
|
||||
ollama create typhoon2.5-np-dms -f .\gemma4-fallback.model.md
|
||||
```
|
||||
2. หรือ revert `AiSettingsService.DEFAULT_MODEL` กลับเป็น `'gemma4:e2b'` แล้ว redeploy
|
||||
3. ดูรายละเอียดใน ADR-034 Section 5 Rollback Strategy
|
||||
@@ -0,0 +1,70 @@
|
||||
// File: specs/100-Infrastructures/134-ai-model-change/research.md
|
||||
// Change Log:
|
||||
// - 2026-06-03: Phase 0 research for Thai-Optimized AI Model Stack
|
||||
|
||||
# Research: Thai-Optimized AI Model Stack
|
||||
|
||||
**Status**: Complete — all decisions resolved from ADR-034
|
||||
|
||||
---
|
||||
|
||||
## Decision 1: Model Unloading via Ollama API
|
||||
|
||||
**Decision**: ใช้ `POST /api/generate` พร้อม `keep_alive: 0` และ `prompt: ""` เพื่อ unload model จาก VRAM
|
||||
**Rationale**: Ollama ไม่มี dedicated unload endpoint; การส่ง `keep_alive: 0` จะทำให้ model unload ทันทีหลัง request complete; ส่ง empty prompt เพื่อเรียก endpoint โดยไม่ generate output
|
||||
**Alternatives considered**:
|
||||
- Restart Ollama service — disruptive; ใช้เวลานาน; ทำลาย in-flight jobs
|
||||
- `/api/delete` — ลบ model ออกจาก registry ไม่ใช่แค่ unload จาก VRAM
|
||||
|
||||
---
|
||||
|
||||
## Decision 2: Model Loading via Ollama API
|
||||
|
||||
**Decision**: ใช้ `POST /api/generate` พร้อม `keep_alive: -1` สำหรับ main model; `keep_alive: 0` สำหรับ OCR model
|
||||
**Rationale**: การส่ง request ไปยัง model ที่ยังไม่ได้ load จะ trigger Ollama auto-load; `keep_alive: -1` = infinite standby (ตาม ADR-034 "Stand by ตลอด"); `keep_alive: 0` = auto-unload หลัง job
|
||||
**Alternatives considered**:
|
||||
- `POST /api/chat` — ใช้ได้เช่นกัน แต่ `/api/generate` มี API surface เล็กกว่า เหมาะสำหรับ keepalive ping
|
||||
|
||||
---
|
||||
|
||||
## Decision 3: OCR Job Type List
|
||||
|
||||
**Decision**: OCR job types = `['ocr-extract', 'sandbox-ocr-only']`
|
||||
**Rationale**: ทั้งสอง job types ต้องการ OCR model; `ocr-extract` = production; `sandbox-ocr-only` = admin sandbox; types อื่นทั้งหมดใช้ main model
|
||||
**Alternatives considered**:
|
||||
- Config-driven job types — over-engineering; ADR-034 กำหนดไว้แล้ว
|
||||
- DB-driven routing — เพิ่ม complexity โดยไม่จำเป็น
|
||||
|
||||
---
|
||||
|
||||
## Decision 4: DEFAULT_MODEL Strategy — Hardcode
|
||||
|
||||
**Decision**: Hardcode `DEFAULT_MODEL = 'typhoon2.5-np-dms:latest'` ใน `AiSettingsService`
|
||||
**Rationale**: ADR-034 ระบุชัดเจน "ไม่ต้อง update ai_settings table — ใช้ hardcode value เพื่อความเร็วในการ deploy"; การ hardcode ป้องกัน misconfiguration จาก DB
|
||||
**Alternatives considered**:
|
||||
- อ่านจาก `ai_settings` DB table — ถูก reject ใน ADR-034 เพื่อความง่ายใน deployment
|
||||
- อ่านจาก ENV variable — อาจ conflict กับ ADR-027 model management; hardcode ชัดกว่า
|
||||
|
||||
---
|
||||
|
||||
## Decision 5: ai_available_models Table Update
|
||||
|
||||
**Decision**: สร้าง SQL delta `2026-06-03-update-ai-available-models-typhoon.sql` อัปเดต seed
|
||||
**Rationale**: ADR-027 Admin Console อ่านจาก `ai_available_models` table; ถ้าไม่อัปเดตจะแสดงชื่อ model เก่า ทำให้ admin เห็น inconsistency
|
||||
**Alternatives considered**:
|
||||
- ไม่อัปเดต table — admin console แสดงข้อมูลเก่า; เสี่ยง confusion
|
||||
|
||||
---
|
||||
|
||||
## Decision 6: Error Handling for Model Switching Failure
|
||||
|
||||
**Decision**: Throw `Error` พร้อม descriptive message ที่ระบุ model name; NestJS Logger บันทึก context; ไม่ retry ภายใน switching logic (ปล่อยให้ BullMQ retry ตาม job policy)
|
||||
**Rationale**: Model load failure ควร fail fast; BullMQ ที่มี retry config จะ re-attempt job ทั้งหมด รวมถึง switching sequence ด้วย; การ retry ภายใน switching เสี่ยง VRAM state inconsistency
|
||||
**Alternatives considered**:
|
||||
- Retry unload/load ภายใน service — อาจทำให้ VRAM state confused ถ้า partial load
|
||||
|
||||
---
|
||||
|
||||
## Unresolved Items
|
||||
|
||||
ไม่มี — decisions ทั้งหมดถูก resolve แล้ว
|
||||
@@ -0,0 +1,127 @@
|
||||
// File: specs/100-Infrastructures/134-ai-model-change/spec.md
|
||||
// Change Log:
|
||||
// - 2026-06-03: Initial specification for Thai-Optimized AI Model Stack (ADR-034)
|
||||
|
||||
# Feature Specification: Thai-Optimized AI Model Stack
|
||||
|
||||
**Feature Branch**: `134-ai-model-change`
|
||||
**Created**: 2026-06-03
|
||||
**Status**: Draft
|
||||
**Category**: 100-Infrastructures
|
||||
**ADR Reference**: ADR-034 (Supersedes ADR-023A Section 2.1)
|
||||
**Input**: User description: "Implement ADR-034 — Switch AI model stack from gemma4:e2b to Thai-optimized Typhoon series: typhoon2.5-np-dms:latest (main AI) + typhoon-np-dms-ocr:latest (Thai OCR). Requires model switching logic in BullMQ processor, new unload/load methods in OllamaService, updated default model config. VRAM budget: RTX 2060 Super 8GB."
|
||||
|
||||
---
|
||||
|
||||
## User Scenarios & Testing _(mandatory)_
|
||||
|
||||
### User Story 1 — Thai OCR Extraction With New OCR Model (Priority: P1)
|
||||
|
||||
ในฐานะ DevOps Engineer ฉันต้องการให้งาน OCR ภาษาไทยใช้ Typhoon OCR-3B ที่ถูก fine-tune มาสำหรับภาษาไทย เพื่อให้ข้อความที่สกัดจาก PDF ภาษาไทยมีความแม่นยำสูงกว่า gemma4:e2b
|
||||
|
||||
**Why this priority**: งาน OCR ภาษาไทยคือจุดอ่อนหลักของระบบปัจจุบัน การเปลี่ยนเป็น Typhoon OCR model จะส่งผลโดยตรงต่อคุณภาพการ extract metadata และ text จากเอกสาร
|
||||
|
||||
**Independent Test**: ส่ง OCR job (`jobType: 'ocr-extract'`) เข้า BullMQ ด้วย PDF ภาษาไทย และตรวจสอบว่า (1) job สำเร็จโดยไม่มี VRAM OOM error (2) ข้อความ OCR อ่านออกได้ (3) หลัง OCR เสร็จ main model ยังพร้อมใช้งาน
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `typhoon-np-dms-ocr:latest` ถูก create บน Desk-5439, **When** BullMQ ได้รับ job `ocr-extract` หรือ `sandbox-ocr-only`, **Then** processor unload main model → load OCR model (keep_alive:0) → ประมวลผล → OCR model unload อัตโนมัติ
|
||||
2. **Given** OCR job เสร็จแล้ว, **When** main model job เข้ามาต่อ, **Then** processor reload main model และประมวลผลสำเร็จภายใน 60 วินาที
|
||||
3. **Given** VRAM ถูกใช้งานสูงขณะมี OCR job, **When** job เข้าคิว, **Then** BullMQ concurrency=1 ป้องกัน VRAM overflow — ไม่เกิด OOM error
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 — General AI Tasks Use Thai-Optimized Main Model (Priority: P2)
|
||||
|
||||
ในฐานะ DevOps Engineer ฉันต้องการให้ระบบ AI ใช้ `typhoon2.5-np-dms:latest` เป็น main model สำหรับงาน extraction, RAG Q&A, และ AI suggestion แทน gemma4:e2b เพื่อประสิทธิภาพภาษาไทยที่ดีขึ้น
|
||||
|
||||
**Why this priority**: เมื่อ OCR ทำงานได้แล้ว ต้องมั่นใจว่า AI extraction/RAG ที่ใช้ main model ทำงานได้กับโมเดลใหม่
|
||||
|
||||
**Independent Test**: ส่ง `ai-extract` job เข้า BullMQ → ตรวจสอบว่า backend เรียก `typhoon2.5-np-dms:latest` (ไม่ใช่ `gemma4:e2b`) และได้ผลลัพธ์ที่ถูกต้อง
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** `typhoon2.5-np-dms:latest` ถูก create บน Desk-5439, **When** backend start up, **Then** `AiSettingsService.DEFAULT_MODEL = 'typhoon2.5-np-dms:latest'`
|
||||
2. **Given** main model พร้อม, **When** ส่ง extraction/RAG/suggestion job, **Then** ผลลัพธ์ return โดยไม่มี "model not found" error
|
||||
3. **Given** การ reload main model หลัง OCR job, **When** warm reload, **Then** ใช้เวลา ≤ 60 วินาที (cold start acceptable)
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 — System Health Reflects New Model Stack (Priority: P3)
|
||||
|
||||
ในฐานะ AI Admin ฉันต้องการเห็นสถานะของ model stack ใหม่ใน AI Admin Console เพื่อยืนยันว่าระบบทำงานถูกต้องหลัง deploy
|
||||
|
||||
**Why this priority**: Operations team ต้องการ observability เพื่อตรวจสอบหลัง deployment
|
||||
|
||||
**Independent Test**: เปิด AI Admin Console → System Health → เห็นชื่อ `typhoon2.5-np-dms` และ `typhoon-np-dms-ocr` แทน gemma4
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** backend รันกับ model stack ใหม่, **When** GET `/api/ai/health`, **Then** response แสดง main model = `typhoon2.5-np-dms:latest` และ ocr model = `typhoon-np-dms-ocr:latest`
|
||||
2. **Given** มี model switching เกิดขึ้น, **When** ดู BullMQ job logs, **Then** เห็น log บันทึก unload/load พร้อม model name และ timestamp
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- VRAM เต็ม (>8GB) ขณะ load OCR model → BullMQ retry; ไม่ crash; error log ชัดเจน
|
||||
- Ollama API timeout ขณะ `unloadModel`/`loadModel` → throw descriptive error; ไม่ swallow silently
|
||||
- โมเดล `typhoon2.5-np-dms` ไม่ถูก create บน Desk-5439 → backend start ได้; job fail พร้อม clear "model not found" error
|
||||
- OCR job และ main job เข้าคิวพร้อมกัน → BullMQ concurrency=1 handle อยู่แล้ว; ไม่ต้องแก้
|
||||
- Cold start OCR (30–60s) ขณะ user รอ → ยอมรับได้; job polling แสดงสถานะ
|
||||
|
||||
---
|
||||
|
||||
## Requirements _(mandatory)_
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: ระบบ MUST ใช้ `typhoon2.5-np-dms:latest` เป็น default main model สำหรับทุก AI job ที่ไม่ใช่ OCR
|
||||
- **FR-002**: ระบบ MUST ใช้ `typhoon-np-dms-ocr:latest` เฉพาะ job type `ocr-extract` และ `sandbox-ocr-only`
|
||||
- **FR-003**: BullMQ processor MUST unload main model ก่อน load OCR model ทุกครั้งที่มี OCR job
|
||||
- **FR-004**: OCR model MUST unload อัตโนมัติหลัง job เสร็จ (keep_alive: 0)
|
||||
- **FR-005**: หลัง OCR job เสร็จ processor MUST reload main model (keep_alive: -1 หรือ indefinite)
|
||||
- **FR-006**: OllamaService MUST expose `unloadModel(modelName)` และ `loadModel(modelName, keepAlive?)` methods
|
||||
- **FR-007**: `AiSettingsService.DEFAULT_MODEL` MUST hardcoded เป็น `'typhoon2.5-np-dms:latest'` (ตาม ADR-034)
|
||||
- **FR-008**: Embedding model `nomic-embed-text` MUST ไม่เปลี่ยนแปลง (นอก scope)
|
||||
- **FR-009**: n8n MUST ไม่เรียก Ollama โดยตรง — ยังคง pattern `POST /api/ai/jobs` → BullMQ (ADR-023A)
|
||||
- **FR-010**: BullMQ concurrency MUST = 1 เพื่อป้องกัน VRAM overflow
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **Custom Ollama Model `typhoon2.5-np-dms`**: Configuration ของ main AI model — base: typhoon2.5-qwen3-4b, keep_alive: indefinite, size ~2.5GB
|
||||
- **Custom Ollama Model `typhoon-np-dms-ocr`**: Configuration ของ OCR model — base: typhoon-ocr1.5-3b, keep_alive: 0, size ~3.2GB
|
||||
- **OllamaService.unloadModel / loadModel**: Methods สำหรับ explicit model management ผ่าน Ollama API
|
||||
- **AiSettingsService.DEFAULT_MODEL**: Hardcoded constant ชี้ไปยัง main model ใหม่
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria _(mandatory)_
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: OCR job ประมวลผล PDF ภาษาไทยได้ข้อความที่อ่านออกได้ (ไม่มี garbled Thai characters) > 90% ของ pages
|
||||
- **SC-002**: ไม่มี VRAM OOM error ตลอดการ process OCR + main jobs ต่อเนื่อง 10 jobs
|
||||
- **SC-003**: OCR cold start ≤ 60 วินาที; main model warm response ≤ 5 วินาที
|
||||
- **SC-004**: Deploy custom models บน Desk-5439 (`ollama create`) ใช้เวลา ≤ 15 นาทีต่อ model (download ขึ้นกับ network)
|
||||
- **SC-005**: ไม่มี regression ใน AI extraction/RAG functionality ที่เดิมทำงานได้
|
||||
|
||||
---
|
||||
|
||||
## Assumptions
|
||||
|
||||
- **A1**: Desk-5439 มี internet access สำหรับ pull base models (`scb10x/typhoon*`) จาก Ollama registry
|
||||
- **A2**: ADR-033 VRAM monitoring mechanism ใช้งานได้กับ Typhoon models
|
||||
- **A3**: Custom Modelfiles ถูก define ใน `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/` แล้ว
|
||||
- **A4**: Embedding model `nomic-embed-text` ไม่เปลี่ยนแปลง
|
||||
- **A5**: `ai_available_models` table (ADR-027) จำเป็นต้อง update seed เพื่อแสดง model ใหม่ใน admin console
|
||||
|
||||
---
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-06-03
|
||||
|
||||
- Q: nomic-embed-text embedding model เปลี่ยนหรือไม่? → A: ไม่เปลี่ยน — ADR-034 supersedes LLM stack เท่านั้น
|
||||
- Q: Keep-alive value สำหรับ main model หลัง reload คือเท่าไร? → A: -1 (indefinite) ตาม ADR-034 "Stand by ตลอด (ไม่ใช่ 0)"
|
||||
- Q: `sandbox-ocr-only` ต้องใช้ OCR model switching ด้วยหรือไม่? → A: ใช่ — เป็น OCR job type เหมือน `ocr-extract`
|
||||
@@ -0,0 +1,156 @@
|
||||
// File: specs/100-Infrastructures/134-ai-model-change/tasks.md
|
||||
// Change Log:
|
||||
// - 2026-06-03: Initial task list for Thai-Optimized AI Model Stack
|
||||
|
||||
# Tasks: Thai-Optimized AI Model Stack
|
||||
|
||||
**Input**: Design documents from `specs/100-Infrastructures/134-ai-model-change/`
|
||||
**ADR Reference**: ADR-034 (Supersedes ADR-023A Section 2.1)
|
||||
**Prerequisites**: plan.md ✅ spec.md ✅ research.md ✅ data-model.md ✅ contracts/ ✅
|
||||
|
||||
## Format: `[ID] [P?] [Story?] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no shared state)
|
||||
- **[Story]**: User story label (US1/US2/US3)
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup (Infrastructure Verification)
|
||||
|
||||
**Purpose**: ยืนยัน Model files และ directory structure พร้อม; ไม่ต้องสร้างใหม่เพราะมีอยู่แล้ว
|
||||
|
||||
- [X] T001 [P] ตรวจสอบ `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/typhoon2.5-np-dms.model.md` ว่า content ตรงกับ ADR-034 Section 1
|
||||
- [X] T002 [P] ตรวจสอบ `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/typhoon-np-dms-ocr.model.md` ว่า content ตรงกับ ADR-034 Section 1
|
||||
- [X] T003 [P] สร้าง SQL delta `specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.sql` — UPDATE main model + INSERT ocr model ใน `ai_available_models` table (ตรวจสอบ schema ก่อน run)
|
||||
- [X] T003b [P] สร้าง rollback `specs/03-Data-and-Storage/deltas/2026-06-03-update-ai-available-models-typhoon.rollback.sql` — revert model_name กลับเป็น gemma4:e2b + DELETE ocr model record (ADR-009: ทุก delta ต้องมี rollback)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (OllamaService + AiSettingsService)
|
||||
|
||||
**Purpose**: Backend services support model ใหม่ — BLOCKS Phases 3, 4, 5
|
||||
|
||||
**⚠️ CRITICAL**: ต้องเสร็จก่อนเริ่ม US phases
|
||||
|
||||
- [X] T004 เพิ่ม method `unloadModel(modelName: string): Promise<void>` ใน `backend/src/modules/ai/services/ollama.service.ts` — POST /api/generate keep_alive:0 + error handling ตาม contract
|
||||
- [X] T005 เพิ่ม method `loadModel(modelName: string, keepAlive?: number | string): Promise<void>` ใน `backend/src/modules/ai/services/ollama.service.ts` — POST /api/generate keep_alive:{keepAlive ?? -1} + timeout 60s + "model not found" error
|
||||
- [X] T006 [P] อัปเดต `DEFAULT_MODEL = 'typhoon2.5-np-dms:latest'` และเพิ่ม `OCR_MODEL = 'typhoon-np-dms-ocr:latest'` ใน `backend/src/modules/ai/services/ai-settings.service.ts`
|
||||
- [X] T007 [P] เขียน unit tests สำหรับ `unloadModel()` และ `loadModel()` ใน `backend/src/modules/ai/services/ollama.service.spec.ts` — mock axios; test timeout, 404, และ success cases
|
||||
|
||||
**Checkpoint**: Foundation พร้อม — processor switching สามารถ implement ได้
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 — Thai OCR Processing (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: BullMQ processor switch models สำหรับ OCR jobs; ไม่มี VRAM OOM
|
||||
|
||||
**Independent Test**: ส่ง `ocr-extract` job → ตรวจสอบ log มี ModelSwitch entries + job สำเร็จ
|
||||
|
||||
- [X] T008 [US1] เพิ่ม constant `const OCR_JOB_TYPES = ['ocr-extract', 'sandbox-ocr-only'] as const` ใน `backend/src/modules/ai/processors/ai-batch.processor.ts`
|
||||
- [X] T009 [US1] Implement model switching block ใน `processJob()`: unload main → load OCR (keep_alive:0) → process → reload main (keep_alive:-1) ใน `backend/src/modules/ai/processors/ai-batch.processor.ts`
|
||||
- [X] T010 [US1] เพิ่ม NestJS `Logger` log สำหรับ model switch events (model name + timestamp) ใน `backend/src/modules/ai/processors/ai-batch.processor.ts`
|
||||
- [X] T011 [P] [US1] เขียน unit tests สำหรับ OCR model switching logic ใน `backend/src/modules/ai/processors/ai-batch.processor.spec.ts` — mock OllamaService methods; test switching sequence + non-OCR bypass
|
||||
|
||||
**Checkpoint**: OCR job ทำงานกับ Typhoon OCR model; main model reload หลัง OCR สำเร็จ
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 — Main AI Tasks With New Model (Priority: P2)
|
||||
|
||||
**Goal**: ทุก non-OCR AI job ใช้ `typhoon2.5-np-dms:latest`
|
||||
|
||||
**Independent Test**: ส่ง `ai-extract` job → ตรวจสอบ Ollama call ใช้ model ใหม่ (ไม่ใช่ gemma4)
|
||||
|
||||
- [X] T012 [US2] ตรวจสอบและอัปเดต `OllamaService.generate()` ให้อ้างอิง `AiSettingsService.DEFAULT_MODEL` แทน hardcoded model name ใน `backend/src/modules/ai/services/ollama.service.ts`
|
||||
- [X] T013 [P] [US2] grep codebase ด้วย pattern `gemma4|OLLAMA_MODEL_MAIN|OLLAMA_RAG_MODEL` ใน `backend/src/` หา hardcoded model references เก่าทั้งหมด → อัปเดตให้ใช้ `AiSettingsService.DEFAULT_MODEL` หรือ `AiSettingsService.OCR_MODEL`
|
||||
- [X] T014 [US2] อัปเดต comment/note ใน `backend/.env.example` — อธิบายว่า model names ถูก hardcode ใน `AiSettingsService` ตาม ADR-034
|
||||
|
||||
**Checkpoint**: ทุก non-OCR AI job ใช้ model ใหม่
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 — System Health Visibility (Priority: P3)
|
||||
|
||||
**Goal**: Admin Console แสดงชื่อ model ที่ถูกต้อง
|
||||
|
||||
**Independent Test**: GET /api/ai/health → JSON มี `mainModel` = typhoon2.5-np-dms, `ocrModel` = typhoon-np-dms-ocr
|
||||
|
||||
- [X] T015 [P] [US3] อัปเดต health response structure ใน `backend/src/modules/ai/ai.service.ts` — เพิ่ม `mainModel` และ `ocrModel` fields ที่อ่านจาก `AiSettingsService`
|
||||
- [X] T016 [P] [US3] ตรวจสอบ `frontend/app/(admin)/admin/ai/page.tsx` ว่า health card แสดง model names ใหม่ (ถ้า dynamic จาก API ไม่ต้องแก้; ถ้า hardcoded ให้แก้)
|
||||
- [ ] T017 [US3] รัน SQL delta `2026-06-03-update-ai-available-models-typhoon.sql` บน production DB (manual step — ผ่าน DB admin tool)
|
||||
|
||||
**Checkpoint**: Admin Console แสดง model stack ใหม่ถูกต้อง
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Documentation update + compliance verification
|
||||
|
||||
- [X] T018 [P] อัปเดต `AGENTS.md` — Current Decisions D10: เปลี่ยน `gemma4:e4b Q8_0` เป็น `typhoon2.5-np-dms:latest (main) + typhoon-np-dms-ocr:latest (OCR)`; อัปเดต version เป็น v1.9.9 และ sync date
|
||||
- [X] T019 [P] อัปเดต `memory/agent-memory.md` — Section 2.5 model names + Section 5 D10 + Section 7 Ollama row + Section 8 Recent Rollouts entry
|
||||
- [X] T020 [P] อัปเดต `.agents/rules/11-ai-integration.md` — 2-model stack: `gemma4:e2b → typhoon2.5-np-dms:latest`
|
||||
- [ ] T021 [P] รัน type check: `pnpm --filter backend build` — ต้องผ่าน 0 errors
|
||||
- [ ] T022 [P] รัน lint: `pnpm --filter backend lint` — ตรวจสอบ no console.log, no any
|
||||
- [ ] T023 [P] รัน unit tests ที่เพิ่มใหม่: `pnpm --filter backend test -- --testPathPattern="ollama.service|ai-batch.processor"`
|
||||
- [ ] T024 รัน quickstart.md verification บน Desk-5439 + QNAP ตามขั้นตอนใน `quickstart.md` — รวมถึงตรวจสอบ BullMQ concurrency=1 ใน `ai-batch.processor.ts` (FR-010)
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Phase 1 (Setup)**: ไม่มี dependency — เริ่มทันที
|
||||
- **Phase 2 (Foundational)**: ไม่มี dependency — เริ่มทันที; **BLOCKS Phase 3, 4, 5**
|
||||
- **Phase 3 (US1)**: ต้องรอ Phase 2 สมบูรณ์
|
||||
- **Phase 4 (US2)**: ต้องรอ Phase 2; สามารถ parallel กับ Phase 3
|
||||
- **Phase 5 (US3)**: ต้องรอ Phase 3 + Phase 4
|
||||
- **Phase 6 (Polish)**: ต้องรอทุก phase
|
||||
|
||||
### Within Phase 2
|
||||
|
||||
- T004 → T005 (sequential — เป็น paired methods)
|
||||
- T006, T007 → parallel ได้
|
||||
|
||||
### Parallel Opportunities Per Phase
|
||||
|
||||
```
|
||||
Phase 1: T001 ∥ T002 ∥ T003
|
||||
Phase 2: (T004→T005) ∥ T006 ∥ T007
|
||||
Phase 3: (T008→T009→T010) + T011 parallel กับ T010
|
||||
Phase 4: T012 → T013 → T014
|
||||
Phase 5: T015 ∥ T016; T017 manual last
|
||||
Phase 6: T018 ∥ T019 ∥ T020 ∥ T021 ∥ T022 ∥ T023 → T024 last
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (US1 — OCR Model Switching)
|
||||
|
||||
1. Phase 1: Verify Modelfiles (T001-T002)
|
||||
2. Phase 2: OllamaService + AiSettings (T004-T007)
|
||||
3. Phase 3: OCR switching (T008-T011)
|
||||
4. **Validate**: Test OCR job + confirm main reload
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. MVP (Phase 1-3) → OCR ทำงานกับ Typhoon
|
||||
2. US2 (Phase 4) → Main model jobs ใช้ model ใหม่
|
||||
3. US3 (Phase 5) → Admin health visibility
|
||||
4. Polish (Phase 6) → Docs + type check + deploy
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- T001-T002: Model files มีอยู่แล้วใน `specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/` — เป็น verification task ไม่ใช่ creation
|
||||
- FR-008 (nomic-embed-text) + FR-009 (n8n boundary): Preserved by existing architecture — ไม่มี task เพราะ "do nothing" คือ correct action
|
||||
- T018: ตรวจสอบ AGENTS.md version จริงก่อน bump เป็น v1.9.9
|
||||
- T003: ตรวจสอบ `lcbp3-v1.9.0-schema-02-tables.sql` ก่อน write delta — ai_available_models schema อาจต่างจากที่คาด
|
||||
- T013: ใช้ grep/code_search เพื่อหา references ทั้งหมด — อย่า hardcode path ไปเอง
|
||||
- T017: Manual step; ต้องทำผ่าน DBA หรือ migration pipeline
|
||||
- T024: Real-app verification ตาม `/check-real-app` workflow
|
||||
Reference in New Issue
Block a user