From b68a750e4fcfd42f8bd944b4ec59e899e102774c Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 29 May 2026 11:16:03 +0700 Subject: [PATCH] 690529:1116 ADR-030-230 context aware #04 --- backend/src/modules/ai/ai.service.ts | 16 +++++++++- .../src/modules/ai/services/ocr.service.ts | 30 +++++++++++++++++++ frontend/app/(admin)/admin/ai/page.tsx | 24 ++++++++++++++- frontend/lib/services/admin-ai.service.ts | 7 +++++ .../Desk-5439/ocr-sidecar/docker-compose.yml | 2 +- 5 files changed, 76 insertions(+), 3 deletions(-) diff --git a/backend/src/modules/ai/ai.service.ts b/backend/src/modules/ai/ai.service.ts index a34ed873..f056fb4a 100644 --- a/backend/src/modules/ai/ai.service.ts +++ b/backend/src/modules/ai/ai.service.ts @@ -3,6 +3,7 @@ // Change Log // - 2026-05-21: เพิ่ม getSystemHealth พร้อมระบบแคช Redis 30 วินาทีตาม ADR-027. // - 2026-05-21: แก้ไข ESLint unsafe return error ใน getSystemHealth โดยใช้ interface SystemHealthResponse +// - 2026-05-29: เพิ่ม OcrService.checkHealth() เข้า getSystemHealth() เพื่อแสดงสถานะ OCR sidecar import { Injectable, Logger, Optional } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { HttpService } from '@nestjs/axios'; @@ -45,6 +46,7 @@ import { AiBatchJobData } from './processors/ai-batch.processor'; import { AuditLog } from '../../common/entities/audit-log.entity'; import { OllamaService } from './services/ollama.service'; import { AiQdrantService } from './qdrant.service'; +import { OcrService, OcrHealthResult } from './services/ocr.service'; // ผลลัพธ์ของ Real-time Extraction export interface ExtractionResult { @@ -120,6 +122,7 @@ export interface SystemHealthResponse { collections?: string[]; error?: string; }; + ocr: OcrHealthResult; queues: { realtime: | { @@ -176,6 +179,8 @@ export class AiService { @Optional() private readonly qdrantService?: AiQdrantService, @Optional() + private readonly ocrService?: OcrService, + @Optional() @InjectRedis() private readonly redis?: Redis ) { @@ -816,7 +821,7 @@ export class AiService { ); } } - const [ollama, qdrant, realtimeQueueMetrics, batchQueueMetrics] = + const [ollama, qdrant, ocr, realtimeQueueMetrics, batchQueueMetrics] = await Promise.all([ this.ollamaService ? this.ollamaService.checkHealth() @@ -833,12 +838,21 @@ export class AiService { latencyMs: 0, error: 'AiQdrantService not injected', }), + this.ocrService + ? this.ocrService.checkHealth() + : Promise.resolve({ + status: 'DOWN' as const, + latencyMs: 0, + url: 'not configured', + error: 'OcrService not injected', + }), this.getQueueMetrics(this.aiRealtimeQueue), this.getQueueMetrics(this.aiBatchQueue), ]); const health = { ollama, qdrant, + ocr, queues: { realtime: realtimeQueueMetrics, batch: batchQueueMetrics, diff --git a/backend/src/modules/ai/services/ocr.service.ts b/backend/src/modules/ai/services/ocr.service.ts index 467dd26c..0984fe75 100644 --- a/backend/src/modules/ai/services/ocr.service.ts +++ b/backend/src/modules/ai/services/ocr.service.ts @@ -3,6 +3,7 @@ // - 2026-05-15: เพิ่ม OCR auto-detection service สำหรับ ADR-023A. // - 2026-05-25: แก้ไข AggregateError (empty message) จาก axios โดย wrap เป็น Error พร้อม context ที่ชัดเจน. // - 2026-05-25: เพิ่ม path remapping (OCR_UPLOAD_BASE_PATH) เพื่อแปลง local upload path เป็น path ที่ sidecar เห็นผ่าน CIFS. +// - 2026-05-29: เพิ่ม checkHealth() เพื่อตรวจสอบสุขภาพของ PaddleOCR sidecar สำหรับ getSystemHealth() (ADR-027) import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; @@ -23,6 +24,13 @@ interface PaddleOcrResponse { text?: string; } +export interface OcrHealthResult { + status: 'HEALTHY' | 'DOWN'; + latencyMs: number; + url: string; + error?: string; +} + /** บริการเลือก fast path หรือ PaddleOCR sidecar ตามจำนวนตัวอักษรที่ extract ได้ */ @Injectable() export class OcrService { @@ -56,6 +64,28 @@ export class OcrService { return localPath; } + /** ตรวจสอบสุขภาพและ latency ของ PaddleOCR sidecar ผ่าน GET /health */ + async checkHealth(): Promise { + const startTime = Date.now(); + try { + await axios.get(`${this.ocrApiUrl}/health`, { timeout: 5000 }); + return { + status: 'HEALTHY', + latencyMs: Date.now() - startTime, + url: this.ocrApiUrl, + }; + } catch (err: unknown) { + const cause = err instanceof Error ? err.message : String(err); + this.logger.warn(`OCR sidecar health check failed: ${cause}`); + return { + status: 'DOWN', + latencyMs: Date.now() - startTime, + url: this.ocrApiUrl, + error: cause, + }; + } + } + /** ตรวจสอบ text layer ก่อนเลือก OCR slow path */ async detectAndExtract( input: OcrDetectionInput diff --git a/frontend/app/(admin)/admin/ai/page.tsx b/frontend/app/(admin)/admin/ai/page.tsx index 83d49700..b590f600 100644 --- a/frontend/app/(admin)/admin/ai/page.tsx +++ b/frontend/app/(admin)/admin/ai/page.tsx @@ -10,7 +10,7 @@ import { useState, useEffect } from 'react'; import { useQuery } from '@tanstack/react-query'; -import { Brain, Loader2, Power, ShieldCheck, Cpu, Database, Activity, Search, Info, HelpCircle, AlertCircle, Settings2, Trash2 } from 'lucide-react'; +import { Brain, Loader2, Power, ShieldCheck, Cpu, Database, Activity, Search, Info, HelpCircle, AlertCircle, Settings2, Trash2, ScanText } from 'lucide-react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; @@ -268,6 +268,28 @@ export default function AiAdminConsolePage() { )} + + + + + PaddleOCR Sidecar + + {isHealthLoading ? : renderStatusBadge(health?.ocr?.status)} + + +
+ ความเร็วตอบสนอง + {health?.ocr?.latencyMs !== undefined ? `${health.ocr.latencyMs} ms` : '-'} +
+
+ URL + {health?.ocr?.url ?? '-'} +
+ {health?.ocr?.error && ( +

{health.ocr.error}

+ )} +
+
diff --git a/frontend/lib/services/admin-ai.service.ts b/frontend/lib/services/admin-ai.service.ts index eb0427f5..11b00dd9 100644 --- a/frontend/lib/services/admin-ai.service.ts +++ b/frontend/lib/services/admin-ai.service.ts @@ -5,6 +5,7 @@ // - 2026-05-21: เพิ่ม API service สำหรับ Superadmin Sandbox RAG (T037). // - 2026-05-21: เพิ่ม service method `submitSandboxExtract` สำหรับอัปโหลดไฟล์ใน OCR Sandbox (T043). // - 2026-05-25: เพิ่ม methods สำหรับจัดการโมเดล AI แบบไดนามิก (ADR-027). +// - 2026-05-29: เพิ่ม ocr field ใน AiSystemHealth interface ตาม OcrService.checkHealth() import api from '../api/client'; @@ -34,6 +35,12 @@ export interface AiSystemHealth { collections?: string[]; error?: string; }; + ocr: { + status: 'HEALTHY' | 'DOWN'; + latencyMs: number; + url: string; + error?: string; + }; queues: { realtime: QueueMetrics; batch: QueueMetrics; 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 3275ae0d..a7a3a136 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 @@ -8,7 +8,7 @@ # docker compose up -d --build # # ทดสอบ: -# curl http://localhost:8765/health +# curl http://192.168.10.100:8765/health name: lcbp3-ocr