Stencil.js สำหรับ ML Dashboard
Stencil.js เป็น Compiler สร้าง Web Components ใช้ได้ทุก Framework TypeScript JSX Native Web Components ขนาดเล็ก Performance สูง
ML Pipeline Dashboard ต้องการ Components แสดง Model Metrics, Training Progress, Prediction Results ใช้ Stencil.js สร้าง Reusable Components
Stencil.js Component Development
// === Stencil.js ML Dashboard Components ===
// npm init stencil
// เลือก component
// 1. ML Metric Card Component
// src/components/ml-metric-card/ml-metric-card.tsx
import { Component, Prop, h, State, Watch } from '@stencil/core';
@Component({
tag: 'ml-metric-card',
styleUrl: 'ml-metric-card.css',
shadow: true,
})
export class MlMetricCard {
@Prop() metricName: string = '';
@Prop() metricValue: number = 0;
@Prop() unit: string = '';
@Prop() trend: 'up' | 'down' | 'stable' = 'stable';
@Prop() threshold: number = 0;
@State() isAlert: boolean = false;
@Watch('metricValue')
handleValueChange(newValue: number) {
this.isAlert = this.threshold > 0 && newValue < this.threshold;
}
componentWillLoad() {
this.handleValueChange(this.metricValue);
}
private getTrendIcon(): string {
switch (this.trend) {
case 'up': return '▲';
case 'down': return '▼';
default: return '●';
}
}
private getTrendColor(): string {
switch (this.trend) {
case 'up': return '#22c55e';
case 'down': return '#ef4444';
default: return '#6b7280';
}
}
render() {
return (
{this.metricName}
{this.metricValue.toFixed(4)}
{this.unit}
{this.getTrendIcon()} {this.trend}
);
}
}
// 2. Training Progress Component
// src/components/ml-training-progress/ml-training-progress.tsx
@Component({
tag: 'ml-training-progress',
styleUrl: 'ml-training-progress.css',
shadow: true,
})
export class MlTrainingProgress {
@Prop() currentEpoch: number = 0;
@Prop() totalEpochs: number = 100;
@Prop() loss: number = 0;
@Prop() accuracy: number = 0;
@Prop() status: 'training' | 'completed' | 'failed' = 'training';
private getProgressPercent(): number {
return (this.currentEpoch / this.totalEpochs) * 100;
}
render() {
const percent = this.getProgressPercent();
return (
Training Progress
{this.status}
Epoch: {this.currentEpoch}/{this.totalEpochs}
Loss: {this.loss.toFixed(4)}
Accuracy: {(this.accuracy * 100).toFixed(1)}%
);
}
}
// 3. Prediction Result Component
@Component({
tag: 'ml-prediction',
shadow: true,
})
export class MlPrediction {
@Prop() predictions: string = '[]'; // JSON string
@State() parsedPredictions: Array<{label: string, confidence: number}> = [];
componentWillLoad() {
try {
this.parsedPredictions = JSON.parse(this.predictions);
} catch (e) {
this.parsedPredictions = [];
}
}
render() {
return (
Predictions
{this.parsedPredictions.map(pred => (
{pred.label}
{(pred.confidence * 100).toFixed(1)}%
))}
);
}
}
console.log("Stencil.js ML Components:");
console.log(" ml-metric-card: แสดง Metric พร้อม Trend");
console.log(" ml-training-progress: Training Progress Bar");
console.log(" ml-prediction: Prediction Results");
ML Model Integration
# ml_api_server.py — FastAPI ML Model Server
# pip install fastapi uvicorn scikit-learn numpy
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Dict, Optional
from datetime import datetime
import numpy as np
app = FastAPI(title="ML Pipeline API")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# Models
class PredictionRequest(BaseModel):
features: List[float]
model_name: str = "default"
class PredictionResponse(BaseModel):
predictions: List[Dict[str, float]]
model_name: str
latency_ms: float
class TrainingStatus(BaseModel):
model_name: str
current_epoch: int
total_epochs: int
loss: float
accuracy: float
status: str # training, completed, failed
class ModelMetrics(BaseModel):
model_name: str
accuracy: float
precision: float
recall: float
f1_score: float
latency_p50_ms: float
latency_p99_ms: float
predictions_today: int
# In-memory state
training_status = TrainingStatus(
model_name="image-classifier-v2",
current_epoch=75, total_epochs=100,
loss=0.0823, accuracy=0.9456,
status="training",
)
@app.get("/api/metrics", response_model=ModelMetrics)
async def get_metrics():
"""Get Model Metrics สำหรับ Dashboard"""
return ModelMetrics(
model_name="image-classifier-v2",
accuracy=0.9456,
precision=0.9312,
recall=0.9589,
f1_score=0.9449,
latency_p50_ms=12.5,
latency_p99_ms=45.2,
predictions_today=15234,
)
@app.get("/api/training/status", response_model=TrainingStatus)
async def get_training_status():
"""Get Training Status"""
return training_status
@app.post("/api/predict", response_model=PredictionResponse)
async def predict(request: PredictionRequest):
"""Run Prediction"""
start = datetime.now()
# Simulated prediction
np.random.seed(42)
labels = ["cat", "dog", "bird", "fish"]
confidences = np.random.dirichlet(np.ones(len(labels)))
predictions = [
{"label": label, "confidence": float(conf)}
for label, conf in sorted(
zip(labels, confidences),
key=lambda x: x[1], reverse=True
)
]
latency = (datetime.now() - start).total_seconds() * 1000
return PredictionResponse(
predictions=predictions,
model_name=request.model_name,
latency_ms=latency,
)
@app.get("/api/pipeline/status")
async def pipeline_status():
"""Pipeline Status Overview"""
return {
"stages": [
{"name": "Data Ingestion", "status": "completed", "records": 50000},
{"name": "Feature Engineering", "status": "completed", "features": 128},
{"name": "Model Training", "status": "running", "epoch": 75},
{"name": "Evaluation", "status": "pending"},
{"name": "Deployment", "status": "pending"},
],
"last_updated": datetime.now().isoformat(),
}
# uvicorn ml_api_server:app --host 0.0.0.0 --port 8000
print("ML API Server:")
print(" GET /api/metrics - Model Metrics")
print(" GET /api/training/status - Training Status")
print(" POST /api/predict - Run Prediction")
print(" GET /api/pipeline/status - Pipeline Overview")
Dashboard Integration
