Files
goodgo-platform/libs/ai-services/app/routers/avm_v2.py
Ho Ngoc Hai a6e53e3d06 feat(ai-services): add AVM v2 A/B comparison endpoint and tests
Add POST /avm/v2/compare-v1 endpoint that runs both v1 (single-model)
and v2 (ensemble) AVM predictions on the same property and returns a
side-by-side comparison with price diff, confidence delta, and a
recommendation on which model to prefer.

- ABComparisonRequest/Response schemas in avm_v2 models
- compare_v1() method in AVMv2EnsembleService
- 4 new integration tests for the comparison endpoint
- All 47 Python tests pass

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 17:35:30 +07:00

52 lines
1.7 KiB
Python

"""AVM v2 ensemble router — residential property valuation."""
from fastapi import APIRouter
from app.models.avm_v2 import (
ABComparisonRequest,
ABComparisonResponse,
AVMv2ModelInfo,
AVMv2PredictRequest,
AVMv2PredictResponse,
AVMv2TrainRequest,
AVMv2TrainResponse,
)
from app.services.avm_v2_service import avm_v2_service
router = APIRouter(prefix="/avm/v2", tags=["AVM v2 Ensemble"])
@router.post("/predict", response_model=AVMv2PredictResponse)
def predict_v2(req: AVMv2PredictRequest) -> AVMv2PredictResponse:
"""Predict residential property price using the multi-model ensemble.
Ensemble: XGBoost (0.4) + LightGBM (0.35) + CatBoost (0.25).
Falls back to heuristic when trained models are not available.
"""
return avm_v2_service.predict(req)
@router.post("/train", response_model=AVMv2TrainResponse)
def train_v2(req: AVMv2TrainRequest) -> AVMv2TrainResponse:
"""Trigger model retraining with Optuna hyperparameter optimization.
Requires training data pipeline (Phase 3). Currently returns scaffold.
"""
return avm_v2_service.train(req)
@router.post("/compare-v1", response_model=ABComparisonResponse)
def compare_v1(req: ABComparisonRequest) -> ABComparisonResponse:
"""Compare v1 (single-model) vs v2 (ensemble) predictions side by side.
Runs both models on the same property and returns price difference,
confidence delta, and a recommendation on which to prefer.
"""
return avm_v2_service.compare_v1(req)
@router.get("/model-info", response_model=AVMv2ModelInfo)
def model_info_v2() -> AVMv2ModelInfo:
"""Get current active ensemble model information."""
return avm_v2_service.get_model_info()