LeonardoMdSA commited on
Commit
91a9dcd
·
1 Parent(s): b4fadea

Dependencies fix

Browse files
README.md CHANGED
@@ -11,7 +11,15 @@ license: mit
11
 
12
  # Under Construction
13
 
 
14
 
 
 
 
 
 
 
 
15
 
16
  # Repo Structure
17
 
 
11
 
12
  # Under Construction
13
 
14
+ py -3.9 -m venv .venv
15
 
16
+ .venv\Scripts\activate
17
+
18
+ python -m pip install --upgrade pip
19
+
20
+ pip install -r requirements.txt
21
+
22
+ uvicorn app.main:app --reload
23
 
24
  # Repo Structure
25
 
app/api/routes.py CHANGED
@@ -6,6 +6,7 @@ from app.inference.predictor import Predictor
6
  from app.core.logging import log_prediction
7
  from app.monitoring.data_loader import load_production_data
8
  from app.monitoring.drift import run_drift_check
 
9
 
10
 
11
  router = APIRouter()
@@ -38,3 +39,12 @@ def run_drift():
38
  "status": "drift_check_completed",
39
  "report_path": report_path
40
  }
 
 
 
 
 
 
 
 
 
 
6
  from app.core.logging import log_prediction
7
  from app.monitoring.data_loader import load_production_data
8
  from app.monitoring.drift import run_drift_check
9
+ import pandas as pd
10
 
11
 
12
  router = APIRouter()
 
39
  "status": "drift_check_completed",
40
  "report_path": report_path
41
  }
42
+
43
+ @router.get("/monitoring/run")
44
+ def monitoring_run():
45
+ # Example: load some data
46
+ current_data = pd.read_csv("data/current.csv")
47
+ reference_data = pd.read_csv("data/reference.csv")
48
+
49
+ alerts = run_drift_check(current_data, reference_data, model_version="v1")
50
+ return {"alerts": alerts}
app/monitoring/alerts.py DELETED
@@ -1 +0,0 @@
1
- # threshold evaluation
 
 
app/monitoring/drift.py CHANGED
@@ -4,6 +4,7 @@ import os
4
  import pandas as pd
5
  from evidently.report import Report
6
  from evidently.metric_preset import DataDriftPreset
 
7
 
8
  REFERENCE_DATA_PATH = "models/v1/reference_data.csv"
9
  REPORT_DIR = "reports/evidently"
@@ -27,3 +28,20 @@ def run_drift_check(current_df: pd.DataFrame):
27
  report.save_html(REPORT_PATH)
28
 
29
  return REPORT_PATH
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  import pandas as pd
5
  from evidently.report import Report
6
  from evidently.metric_preset import DataDriftPreset
7
+ from app.monitoring.governance import Governance
8
 
9
  REFERENCE_DATA_PATH = "models/v1/reference_data.csv"
10
  REPORT_DIR = "reports/evidently"
 
28
  report.save_html(REPORT_PATH)
29
 
30
  return REPORT_PATH
31
+
32
+ # Thresholds configuration
33
+ thresholds = {
34
+ "psi": 0.2,
35
+ "accuracy_drop": 0.05,
36
+ "f1": 0.7
37
+ }
38
+
39
+ governance = Governance(thresholds=thresholds)
40
+
41
+ def run_drift_check(current_data, reference_data, model_version="v1"):
42
+ report = Report(metrics=[DataDriftPreset()])
43
+ report.run(current_data=current_data, reference_data=reference_data)
44
+
45
+ # Governance check
46
+ alerts = governance.check_metrics(report.as_dict(), model_version=model_version)
47
+ return alerts
app/monitoring/governance.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file implements threshold checking, governance signals logging, and notifications.
2
+
3
+ import json
4
+ import logging
5
+ from datetime import datetime
6
+ from app.utils.alerts import send_email_alert, send_slack_alert
7
+ import os
8
+
9
+ os.makedirs("logs", exist_ok=True)
10
+ logger = logging.getLogger("governance")
11
+ logger.setLevel(logging.INFO)
12
+ handler = logging.FileHandler("logs/governance_alerts.log")
13
+ formatter = logging.Formatter('%(asctime)s | %(levelname)s | %(message)s')
14
+ handler.setFormatter(formatter)
15
+ logger.addHandler(handler)
16
+
17
+
18
+ class Governance:
19
+ def __init__(self, thresholds: dict):
20
+ """
21
+ thresholds example:
22
+ {
23
+ "psi": 0.2,
24
+ "accuracy_drop": 0.05,
25
+ "f1": 0.7
26
+ }
27
+ """
28
+ self.thresholds = thresholds
29
+
30
+ def check_metrics(self, report_dict: dict, model_version: str):
31
+ alerts = []
32
+
33
+ # Example: data drift
34
+ psi = report_dict.get("metrics", {}).get("DataDriftPreset", {}).get("result", {}).get("dataset_drift", 0)
35
+ if psi > self.thresholds.get("psi", 0.2):
36
+ alerts.append(f"Data drift detected (PSI={psi})")
37
+
38
+ # Example: classification performance
39
+ f1 = report_dict.get("metrics", {}).get("ClassificationPreset", {}).get("result", {}).get("f1_score", 1.0)
40
+ if f1 < self.thresholds.get("f1", 0.7):
41
+ alerts.append(f"F1 drop detected (F1={f1})")
42
+
43
+ # Example: regression accuracy
44
+ accuracy_drop = report_dict.get("metrics", {}).get("RegressionPreset", {}).get("result", {}).get("accuracy_drop", 0)
45
+ if accuracy_drop > self.thresholds.get("accuracy_drop", 0.05):
46
+ alerts.append(f"Accuracy drop detected ({accuracy_drop})")
47
+
48
+ # Log alerts
49
+ for alert in alerts:
50
+ self.log_alert(alert, model_version)
51
+
52
+ # Optional notifications
53
+ for alert in alerts:
54
+ send_email_alert(alert)
55
+ send_slack_alert(alert)
56
+
57
+ return alerts
58
+
59
+ @staticmethod
60
+ def log_alert(message: str, model_version: str):
61
+ log_entry = {
62
+ "timestamp": datetime.utcnow().isoformat(),
63
+ "model_version": model_version,
64
+ "alert": message
65
+ }
66
+ logger.info(json.dumps(log_entry))
app/utils/alerts.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Helper functions for sending notifications.
2
+
3
+ import smtplib
4
+ from email.message import EmailMessage
5
+ import requests
6
+
7
+ def send_email_alert(message: str):
8
+ # Configure your SMTP settings here
9
+ try:
10
+ email = EmailMessage()
11
+ email.set_content(message)
12
+ email["Subject"] = "ML Governance Alert"
13
+ email["From"] = "ml.alerts@example.com"
14
+ email["To"] = "ops-team@example.com"
15
+
16
+ with smtplib.SMTP("localhost") as smtp:
17
+ smtp.send_message(email)
18
+ except Exception as e:
19
+ print(f"Failed to send email alert: {e}")
20
+
21
+
22
+ def send_slack_alert(message: str):
23
+ # Slack webhook URL
24
+ webhook_url = "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ"
25
+ try:
26
+ requests.post(webhook_url, json={"text": message})
27
+ except Exception as e:
28
+ print(f"Failed to send Slack alert: {e}")
reports/evidently/drift_report.html CHANGED
The diff for this file is too large to render. See raw diff
 
requirements-dev.txt CHANGED
@@ -1,5 +1,12 @@
1
  evidently==0.4.15
2
- fastapi
3
- uvicorn
4
- pandas
5
- scikit-learn
 
 
 
 
 
 
 
 
1
  evidently==0.4.15
2
+ fastapi>=0.100.0,<0.130.0
3
+ uvicorn>=0.21.1,<0.40.0
4
+ pandas>=1.5.0,<2.0.0
5
+ scikit-learn==1.6.1
6
+ pydantic==1.10.12
7
+ plotly
8
+ numpy<2.0.0
9
+ requests
10
+ scipy>=1.10.0,<2.0.0
11
+ python-multipart>=0.0.6
12
+ typing-extensions>=4.0.0
requirements.txt CHANGED
@@ -1,5 +1,12 @@
1
  evidently==0.4.15
2
- fastapi
3
- uvicorn
4
- pandas
5
- scikit-learn
 
 
 
 
 
 
 
 
1
  evidently==0.4.15
2
+ fastapi>=0.100.0,<0.130.0
3
+ uvicorn>=0.21.1,<0.40.0
4
+ pandas>=1.5.0,<2.0.0
5
+ scikit-learn==1.6.1
6
+ pydantic==1.10.12
7
+ plotly
8
+ numpy<2.0.0
9
+ requests
10
+ scipy>=1.10.0,<2.0.0
11
+ python-multipart>=0.0.6
12
+ typing-extensions>=4.0.0