feat(ai-services): dedicated GET /avm/v2/feature-importance endpoint (TEC-2760)

Exposes ensemble feature importance as a standalone endpoint per R5.1 spec.
Aggregates XGBoost (0.4) + LightGBM (0.35) + CatBoost (0.25) gain when trained
boosters are loaded; falls back to the curated heuristic ranking otherwise, so
callers can depend on the endpoint during scaffold/heuristic-only runs.

- Factored heuristic drivers into a shared constant (_HEURISTIC_DRIVERS)
- Added AVMv2FeatureImportanceResponse model (model_version + source + drivers)
- Added service.get_feature_importance() public method
- Added tests/test_avm_v2.py::test_feature_importance_heuristic (24 total pass)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-18 15:27:30 +07:00
parent 5731577fa9
commit 729afe2db6
4 changed files with 92 additions and 16 deletions

View File

@@ -261,6 +261,27 @@ def test_model_info_v2():
assert data["is_active"] is True
# ── Feature importance endpoint ──────────────────────────────────
def test_feature_importance_heuristic():
"""Dedicated endpoint returns heuristic drivers when no models are loaded."""
resp = client.get("/avm/v2/feature-importance")
assert resp.status_code == 200
data = resp.json()
assert data["source"] == "heuristic"
assert data["model_version"] == "ensemble-v2-heuristic"
drivers = data["drivers"]
assert len(drivers) > 0
importances = [d["importance"] for d in drivers]
assert importances == sorted(importances, reverse=True)
assert all(0 <= i <= 1 for i in importances)
feature_names = {d["feature"] for d in drivers}
assert "area_m2" in feature_names
assert "neighborhood_score" in feature_names
# ── Model versioning ────────────────────────────────────────────