File size: 4,850 Bytes
0f8fe33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# backend/routes/predict_route.py
from flask import Blueprint, request, jsonify
import time
import numpy as np
import pandas as pd
from utils.model_selector import load_model, get_active_model
from utils.logger import classify_risk

predict_bp = Blueprint("predict", __name__)

@predict_bp.route("/", methods=["GET"])
def info():
    active = get_active_model()
    return jsonify({
        "message": "POST JSON to /api/predict/ to get model prediction.",
        "active_model": active,
        "note": "For 'bcc' model send ordered features or dict; for 'cicids' send named features matching artifacts['features']."
    })

@predict_bp.route("/", methods=["POST"])
def predict():
    active = get_active_model()
    mdl = load_model(active)

    if active == "bcc":
        model = mdl.get("model")
        scaler = mdl.get("scaler")
        encoder = mdl.get("encoder")

        if model is None or scaler is None or encoder is None:
            return jsonify({"error": "BCC model/scaler/encoder not loaded on server."}), 500

        data = request.get_json(force=True, silent=True)
        if data is None:
            return jsonify({"error": "No JSON body provided"}), 400

        # Accept either list/array or dict of features
        # You must keep the same feature order as used in training (15 values)
        if isinstance(data, dict):
            # if the client provides named keys, try to coerce to ordered list
            # fallback: take values in insertion order
            vals = list(data.values())
        else:
            vals = list(data)

        try:
            X = np.array([float(v) for v in vals], dtype=float).reshape(1, -1)
        except Exception as e:
            return jsonify({"error": f"Failed to coerce input to numeric vector: {e}"}), 400

        try:
            Xs = scaler.transform(X)
        except Exception:
            # fallback: try prediction without scaler
            Xs = X

        try:
            pred_idx = model.predict(Xs)[0]
            conf = None
            if hasattr(model, "predict_proba"):
                conf = float(np.max(model.predict_proba(Xs))) * 100.0
            label = encoder.inverse_transform([int(pred_idx)])[0]
            risk = classify_risk(label)
            return jsonify({
                "prediction": str(label),
                "confidence": round(conf, 2) if conf is not None else None,
                "risk_level": risk
            })
        except Exception as e:
            return jsonify({"error": f"Model predict failed: {str(e)}"}), 500

    elif active == "cicids":
        obj = mdl.get("artifacts", None)
        model = mdl.get("model", None)
        if model is None or obj is None:
            return jsonify({"error": "CICIDS model or artifacts not available on server."}), 500

        # artifacts expected to have 'features' and 'scaler'
        features = obj.get("features") or obj.get("features_used") or obj.get("feature_list")
        scaler = obj.get("scaler") or obj.get("scaler_object")

        if not features or scaler is None:
            return jsonify({"error": "CICIDS artifacts missing features or scaler."}), 500

        data = request.get_json(force=True, silent=True)
        if data is None:
            return jsonify({"error": "No JSON body provided"}), 400

        # Accept dict of named features or list
        if isinstance(data, dict):
            # build row using artifacts feature order (missing -> 0)
            row = [float(data.get(f, 0)) for f in features]
        else:
            # list or array
            try:
                row = [float(x) for x in data]
            except Exception as e:
                return jsonify({"error": "Provided input must be array or dict of numbers."}), 400

            if len(row) != len(features):
                return jsonify({"error": f"Expecting {len(features)} features for cicids: {features}"}), 400

        X_df = pd.DataFrame([row], columns=features)
        try:
            Xs = scaler.transform(X_df)
        except Exception:
            Xs = X_df.values

        try:
            pred = model.predict(Xs)[0]
            conf = None
            if hasattr(model, "predict_proba"):
                conf = float(np.max(model.predict_proba(Xs))) * 100.0
            # label may already be string; try safe conversion
            try:
                label = str(pred)
            except Exception:
                label = repr(pred)

            risk = classify_risk(label)
            return jsonify({
                "prediction": label,
                "confidence": round(conf, 2) if conf else None,
                "risk_level": risk
            })
        except Exception as e:
            return jsonify({"error": f"CICIDS predict failed: {str(e)}"}), 500

    else:
        return jsonify({"error": "Unknown active model"}), 500