CLAUDE.md 5.5 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What This Is

NestJS backend for the PalmOilAI system. Provides server-side YOLOv8 ONNX inference, SQLite history persistence (with disk image archiving), WebSocket gateways for real-time vision streaming and n8n chat proxying, and a process surveillance monitor for n8n and Ollama.

Detection classes: Ripe, Unripe, Underripe, Overripe, Abnormal, Empty_Bunch (MPOB standard).

Commands

npm install
npm run start:dev               # Watch mode dev server (port 3000)
npm run start                   # Single-run dev server
npm run start:prod              # Production (from dist/)
npm run build                   # Compile → dist/
npm run lint                    # ESLint with auto-fix
npm run test                    # Jest unit tests
npm run test:watch              # Jest watch mode
npm run test:cov                # Coverage report
npm run test:e2e                # End-to-end tests
jest --testPathPattern=palm-oil # Run a single test file

Set PORT env var to override port 3000. Set N8N_WEBHOOK_URL in .env for chat proxy and agent readiness probing.

Architecture

Modules

PalmOilModule (src/palm-oil/)

ScannerProvider — ONNX inference engine loaded on OnModuleInit:

  • preprocess(buffer)sharp resize to 640×640, strip alpha, convert HWC→CHW, normalize to [0.0, 1.0], output [1, 3, 640, 640] tensor
  • inference(tensor) — runs onnxruntime-node session
  • postprocess(tensor, origW, origH, threshold=0.25) — output is [1, N, 6] (x1, y1, x2, y2, confidence, class_index):
    • Captures first 5 raw rows as raw_tensor_sample for technical evidence
    • Filters by confidence threshold
    • Scales coords to original image dimensions
    • Maps class_indexMPOB_CLASSES
    • Sets is_health_alert for Abnormal | Empty_Bunch

PalmOilService — Orchestrates inference + persistence. analyzeImage(imageBuffer, filename, batchId?) pipeline:

  1. Preprocess via ScannerProvider
  2. Inference with timing (inference_ms, processing_ms)
  3. Postprocess → detections + raw_tensor_sample
  4. Save image buffer to archive/ directory on disk
  5. Persist to SQLite (HistoryEntity) — fully awaited before returning (guarantees DB write before socket emit)
  6. Returns AnalysisResponse with Base64 image, archive_id, full technical_evidence block

Additional methods: getHistory() (last 50 records), getRecordByArchiveId(), deleteRecord() (removes DB row + disk image), clearAllHistory() (removes all records + disk images).

VisionGateway — WebSocket gateway on /vision namespace:

  • vision:analyze handler: receives VisionStreamPayload (frame: string, sourceLabel?, batchId?), strips data-URI prefix, decodes Base64 → Buffer, calls PalmOilService.analyzeImage(), emits vision:result or vision:error
  • chat:send handler: receives ChatPayload, POSTs to N8N_WEBHOOK_URL server-to-server (bypasses browser CORS), unwraps array response to first element, emits chat:result or chat:error
  • Hard rule: accepts raw, uncompressed Base64 strings only — no binary frames, no WebRTC

HistoryEntity — TypeORM entity fields:

  • id (PK auto), archive_id (unique), batch_id (nullable, indexed), filename
  • total_count, industrial_summary (simple-json), detections (simple-json array)
  • inference_ms, processing_ms, image_path (disk reference)
  • created_at (auto timestamp)

mpob-standards.ts (src/palm-oil/constants/) — Source of truth:

  • MPOB_CLASSES: index 0–5 → class name strings
  • GRADE_COLORS: class name → hex color
  • HEALTH_ALERT_CLASSES: ['Abnormal', 'Empty_Bunch']

SurveillanceModule (src/surveillance/)

SurveillanceService — Boots on OnModuleInit, runs two background loops:

  • 500ms poll loop: discovers PIDs for n8n (Node.js process containing "n8n" in cmd) and Ollama (ollama_llama_server or ollama), then calls pidusage for CPU/memory metrics
  • Port-level heartbeat: n8n PID only included if port 5678 actively accepts TCP connections (800ms timeout); evicts PID if port goes silent or process dies
  • Exposes: getLatestMetrics(), callback registration for tick events

SurveillanceGateway — WebSocket gateway on /monitor namespace:

  • Wires SurveillanceService callbacks: every 500ms tick broadcasts monitor:data to all clients
  • On client connect: immediately pushes latest snapshot (no blank load screen)
  • monitor:subscribe handler: acknowledges and re-sends current snapshot

Database

SQLite file palm_history.db in the project root. Managed by TypeORM with synchronize: true (auto-creates/migrates tables — dev only). No external DB setup required.

Required Files

  • best.onnx — YOLOv8 ONNX model, must be placed in the project root (not src/). Loaded by ScannerProvider at startup.
  • archive/ directory — created automatically by PalmOilService for disk image storage.

Configuration (.env)

N8N_WEBHOOK_URL=<n8n webhook URL>
PORT=3000   # optional

WebSocket Event Contracts

Namespace Client → Server Payload Server → Client
/vision vision:analyze { frame: string, sourceLabel?: string, batchId?: string } vision:result, vision:error
/vision chat:send { message: string } chat:result, chat:error
/monitor monitor:subscribe monitor:data (MonitorPayload[])