diff --git a/backend/src/modules/ai/processors/ai-batch.processor.spec.ts b/backend/src/modules/ai/processors/ai-batch.processor.spec.ts index 460390e8..d7d8e7bc 100644 --- a/backend/src/modules/ai/processors/ai-batch.processor.spec.ts +++ b/backend/src/modules/ai/processors/ai-batch.processor.spec.ts @@ -57,7 +57,7 @@ describe('AiBatchProcessor', () => { detectAndExtract: jest.fn().mockResolvedValue({ text: 'OCR text LCBP3-CIV-001 Civil', ocrUsed: true, - engineUsed: 'typhoon-ocr-3b', + engineUsed: 'typhoon-np-dms-ocr', fallbackUsed: false, }), }; diff --git a/backend/src/modules/ai/processors/typhoon-ocr.processor.ts b/backend/src/modules/ai/processors/typhoon-ocr.processor.ts index 699f2252..dd940ed2 100644 --- a/backend/src/modules/ai/processors/typhoon-ocr.processor.ts +++ b/backend/src/modules/ai/processors/typhoon-ocr.processor.ts @@ -194,7 +194,7 @@ export class TyphoonOcrProcessor extends WorkerHost { const log = this.auditLogRepo.create({ documentPublicId: params.documentPublicId, aiModel: 'typhoon-ocr', - modelName: 'scb10x/typhoon-ocr1.5-3b', + modelName: 'typhoon-np-dms-ocr:latest', modelType: params.engineType, status: params.status, processingTimeMs: params.processingTimeMs, diff --git a/backend/src/modules/ai/services/ocr.service.ts b/backend/src/modules/ai/services/ocr.service.ts index 705f5eaf..135aa86b 100644 --- a/backend/src/modules/ai/services/ocr.service.ts +++ b/backend/src/modules/ai/services/ocr.service.ts @@ -330,7 +330,7 @@ export class OcrService { new Blob([fileBuffer], { type: 'application/pdf' }), 'upload.pdf' ); - form.append('engine', 'typhoon-ocr1.5-3b'); + form.append('engine', 'typhoon-np-dms-ocr'); const response = await axios.post( `${this.ocrApiUrl}/ocr-upload`, form, @@ -346,7 +346,7 @@ export class OcrService { await this.writeAuditLog({ documentPublicId: input.documentPublicId, aiModel: 'typhoon-ocr', - modelName: 'typhoon-ocr1.5-3b', + modelName: 'typhoon-np-dms-ocr:latest', modelType: 'typhoon-ocr', status: AiAuditStatus.SUCCESS, processingTimeMs: durationMs, diff --git a/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/app.py b/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/app.py index c1a5d4f9..01663839 100644 --- a/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/app.py +++ b/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/app.py @@ -13,6 +13,8 @@ # - 2026-06-04: ให้ SYSTEM ใน Modelfile ทำงานแทน — ลบ prompt ซ้าซ้อน; sync options ให้ตรงกับ Modelfile (temperature 0.1, top_p 0.1, repeat_penalty 1.1) # - 2026-06-04: รับค่า temperature/top_p/repeat_penalty จาก frontend sandbox ได้ (optional override) # - 2026-06-04: แก้ bug prompt="" ทำให้ Ollama ไม่ generate — เปลี่ยนเป็น minimal trigger prompt +# - 2026-06-04: เพิ่ม alias normalization สำหรับ engine name เก่า (typhoon-ocr1.5-3b → typhoon-np-dms-ocr) +# - 2026-06-04: เปลี่ยน keep_alive จาก 0 เป็น 300s เพื่อไม่ให้ unload model ระหว่าง sandbox session (ลด cold-start) # - 2026-06-02: เพิ่มการตรวจสอบ API Key (X-API-Key Header) สำหรับ endpoints หลัก เพื่อความมั่นคงปลอดภัยตามข้อเสนอแนะ Code Review import os @@ -145,8 +147,17 @@ def health(): } +# alias map สำหรับ engine name เก่า → canonical name +_ENGINE_ALIASES: dict[str, str] = { + "typhoon-ocr1.5-3b": "typhoon-np-dms-ocr", + "typhoon-ocr-3b": "typhoon-np-dms-ocr", + "typhoon_ocr": "typhoon-np-dms-ocr", +} + + def _process_pdf_doc(doc: fitz.Document, selected_engine: str, max_pages: int, typhoon_options: dict = {}) -> OcrResponse: """ประมวลผล fitz.Document ด้วย engine ที่เลือก — shared logic สำหรับ /ocr และ /ocr-upload""" + selected_engine = _ENGINE_ALIASES.get(selected_engine, selected_engine) pages_to_process = list(range(min(len(doc), max_pages) if max_pages > 0 else len(doc))) page_count = len(pages_to_process) @@ -229,7 +240,7 @@ def process_with_typhoon_ocr(pil_image: Image.Image, options_override: dict = {} "images": [image_base64], "stream": False, "options": options, - "keep_alive": 0, + "keep_alive": 300, # คง model ไว้ใน VRAM/RAM 5 นาที เพื่อลด cold-start ระหว่าง sandbox session } with httpx.Client(timeout=TYPHOON_OCR_TIMEOUT) as client: response = client.post(f"{OLLAMA_API_URL}/api/generate", json=payload) diff --git a/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/docker-compose.yml b/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/docker-compose.yml index 5a943bfd..183356f0 100644 --- a/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/docker-compose.yml +++ b/specs/04-Infrastructure-OPS/04-00-docker-compose/Desk-5439/ocr-sidecar/docker-compose.yml @@ -43,8 +43,8 @@ services: # (proxy ไม่ forward /api/generate ได้ถูกต้อง — ทำให้ response ว่าง) OLLAMA_API_URL: "http://host.docker.internal:11434" TYPHOON_OCR_MODEL: "typhoon-np-dms-ocr:latest" - # Timeout 120 วินาที/หน้า (budget สำหรับ 3B model บน RTX 2060 Super) - TYPHOON_OCR_TIMEOUT: "120" + # Timeout 360 วินาที/หน้า — รองรับ cold-start โหลด model (~70s) + inference (10GB model, CPU offload) + TYPHOON_OCR_TIMEOUT: "360" logging: driver: "json-file" options: