@@ -7,6 +7,7 @@ from collections.abc import Sequence
|
||||
from fastapi import APIRouter, FastAPI, HTTPException, Request, status
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.ml.evaluation import Evaluator
|
||||
from app.ml.feature_store import FeatureVector
|
||||
from app.ml.predictor import Predictor
|
||||
from app.ml.registry.model_registry import ModelRegistry
|
||||
@@ -31,7 +32,9 @@ class PredictRequest(BaseModel):
|
||||
class PredictResponse(BaseModel):
|
||||
model_id: str
|
||||
sensor_id: str
|
||||
prediction: str
|
||||
predictions: dict[str, float]
|
||||
confidence: float
|
||||
model_type: str
|
||||
|
||||
|
||||
class BatchRequest(BaseModel):
|
||||
@@ -60,9 +63,28 @@ class RetrainRequest(BaseModel):
|
||||
class RetrainResponse(BaseModel):
|
||||
model_id: str
|
||||
supported_sensors: list[str]
|
||||
trained_features: int
|
||||
model_type: str
|
||||
replaced: bool
|
||||
|
||||
|
||||
class EvaluateRequest(BaseModel):
|
||||
model_id: str = Field(..., alias="modelId", min_length=1, max_length=128)
|
||||
samples: list[TrainingSample] = Field(min_length=1)
|
||||
|
||||
|
||||
class MetricResponse(BaseModel):
|
||||
name: str
|
||||
value: float
|
||||
threshold: float | None = None
|
||||
|
||||
|
||||
class EvaluateResponse(BaseModel):
|
||||
model_id: str
|
||||
sample_size: int
|
||||
metrics: list[MetricResponse]
|
||||
|
||||
|
||||
@router.get("/health", response_model=HealthResponse, status_code=200)
|
||||
def health() -> HealthResponse:
|
||||
return HealthResponse(status="ok")
|
||||
@@ -96,10 +118,50 @@ def retrain(payload: RetrainRequest, request: Request) -> RetrainResponse:
|
||||
return RetrainResponse(
|
||||
model_id=result.artifact.artifact_id,
|
||||
supported_sensors=list(result.artifact.supported_sensors),
|
||||
trained_features=sum(
|
||||
len(feature_models)
|
||||
for feature_models in result.artifact.feature_models.values()
|
||||
),
|
||||
model_type=result.artifact.model_type,
|
||||
replaced=result.replaced,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/evaluate", response_model=EvaluateResponse, status_code=200)
|
||||
def evaluate(payload: EvaluateRequest, request: Request) -> EvaluateResponse:
|
||||
registry = _require_registry(request)
|
||||
vectors = [
|
||||
FeatureVector(
|
||||
sensor_id=sample.sensor_id,
|
||||
values=sample.values,
|
||||
label=sample.label,
|
||||
)
|
||||
for sample in payload.samples
|
||||
]
|
||||
try:
|
||||
report = Evaluator(registry=registry).evaluate(payload.model_id, vectors)
|
||||
except ValueError as exc:
|
||||
try:
|
||||
registry.load_artifact(payload.model_id)
|
||||
except KeyError:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
return EvaluateResponse(
|
||||
model_id=report.artifact_id,
|
||||
sample_size=report.sample_size,
|
||||
metrics=[
|
||||
MetricResponse(name=metric.name, value=metric.value, threshold=metric.threshold)
|
||||
for metric in report.metrics
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@router.post("/predict", response_model=PredictResponse, status_code=200)
|
||||
def predict(payload: PredictRequest, request: Request) -> PredictResponse:
|
||||
registry = _require_registry(request)
|
||||
@@ -117,7 +179,9 @@ def predict(payload: PredictRequest, request: Request) -> PredictResponse:
|
||||
return PredictResponse(
|
||||
model_id=payload.model_id,
|
||||
sensor_id=payload.sensor_id,
|
||||
prediction=prediction,
|
||||
predictions=prediction.predictions,
|
||||
confidence=prediction.confidence,
|
||||
model_type=prediction.model_type,
|
||||
)
|
||||
|
||||
|
||||
@@ -138,7 +202,13 @@ def predict_batch(payload: BatchRequest, request: Request) -> BatchResponse:
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
responses.append(
|
||||
PredictResponse(model_id=item.model_id, sensor_id=item.sensor_id, prediction=prediction)
|
||||
PredictResponse(
|
||||
model_id=item.model_id,
|
||||
sensor_id=item.sensor_id,
|
||||
predictions=prediction.predictions,
|
||||
confidence=prediction.confidence,
|
||||
model_type=prediction.model_type,
|
||||
)
|
||||
)
|
||||
return BatchResponse(predictions=responses)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user