Skip to content

Commit 9099a4f

Browse files
author
marwan37
committed
add slack alerter for approval step
1 parent d4dde3d commit 9099a4f

File tree

11 files changed

+353
-478
lines changed

11 files changed

+353
-478
lines changed

credit-scorer/README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,26 @@ pip install -r requirements.txt
9595
zenml init
9696
```
9797

98-
3. Install [WhyLogs integration](https://docs.zenml.io/stacks/stack-components/data-validators/whylogs):
98+
3. Install [WhyLogs integration](https://docs.zenml.io/stacks/stack-components/data-validators/whylogs) for data profiling:
9999

100100
```bash
101-
zenml integration install whylogs -y
101+
zenml integration install whylogs
102102
zenml data-validator register whylogs_data_validator --flavor=whylogs
103103
zenml stack update <STACK_NAME> -dv whylogs_data_validator
104104
```
105105

106+
4. Install [Slack integration](https://docs.zenml.io/stacks/stack-components/alerters/slack) for deployment approval gate and incident reporting:
107+
108+
```bash
109+
zenml integration install slack
110+
zenml secret create slack_token --oauth_token=<SLACK_TOKEN>
111+
zenml alerter register slack_alerter \
112+
--flavor=slack \
113+
--slack_token={{slack_token.oauth_token}} \
114+
--slack_channel_id=<SLACK_CHANNEL_ID>
115+
zenml stack update <STACK_NAME> -al slack_alerter
116+
```
117+
106118
## 📊 Running Pipelines
107119

108120
### Basic Commands

credit-scorer/modal_app/modal_deployment.py

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ def create_modal_app(python_version: str = "3.12.9"):
6868
"src/constants/annotations.py",
6969
remote_path="/root/src/constants/annotations.py",
7070
)
71-
.add_local_file(
72-
"src/utils/incidents.py",
73-
remote_path="/root/src/utils/incidents.py",
74-
)
7571
.add_local_file(
7672
"src/utils/storage.py",
7773
remote_path="/root/src/utils/storage.py",
@@ -133,13 +129,98 @@ def _load_pipeline() -> Any:
133129

134130
def _report_incident(incident_data: dict, model_checksum: str) -> dict:
135131
"""Report incidents to compliance team and log them (Article 18)."""
136-
from src.utils.incidents import create_incident_report
132+
import json
133+
from datetime import datetime
134+
from pathlib import Path
135+
136+
import requests
137+
from src.constants.config import SlackConfig as SC
138+
139+
# Format incident report
140+
incident = {
141+
"incident_id": f"incident_{datetime.now().isoformat().replace(':', '-')}",
142+
"timestamp": datetime.now().isoformat(),
143+
"model_name": "credit_scoring_model",
144+
"model_version": model_checksum,
145+
"severity": incident_data.get("severity", "medium"),
146+
"description": incident_data.get(
147+
"description", "Unspecified incident"
148+
),
149+
"source": "modal_api",
150+
"data": incident_data,
151+
}
152+
153+
try:
154+
# 1. Append to local log (if accessible)
155+
incident_log_path = "docs/risk/incident_log.json"
156+
try:
157+
existing = []
158+
if Path(incident_log_path).exists():
159+
try:
160+
with open(incident_log_path, "r") as f:
161+
existing = json.load(f)
162+
except json.JSONDecodeError:
163+
existing = []
164+
165+
existing.append(incident)
166+
with open(incident_log_path, "w") as f:
167+
json.dump(existing, f, indent=2)
168+
except Exception as e:
169+
logger.warning(f"Could not write to local incident log: {e}")
170+
171+
# 2. Direct Slack notification for high/critical severity (not using ZenML)
172+
if incident["severity"] in ("high", "critical"):
173+
try:
174+
slack_token = os.getenv("SLACK_BOT_TOKEN")
175+
slack_channel = os.getenv("SLACK_CHANNEL_ID", SC.CHANNEL_ID)
176+
177+
if slack_token and slack_channel:
178+
emoji = {"high": "🔴", "critical": "🚨"}[
179+
incident["severity"]
180+
]
181+
message = (
182+
f"{emoji} *Incident from Modal API:* {incident['description']}\n"
183+
f">*Severity:* {incident['severity']}\n"
184+
f">*Source:* {incident['source']}\n"
185+
f">*Model Version:* {incident['model_version']}\n"
186+
f">*Time:* {incident['timestamp']}\n"
187+
f">*ID:* {incident['incident_id']}"
188+
)
189+
190+
# Direct Slack API call
191+
response = requests.post(
192+
"https://slack.com/api/chat.postMessage",
193+
headers={"Authorization": f"Bearer {slack_token}"},
194+
json={
195+
"channel": slack_channel,
196+
"text": message,
197+
"username": "Modal Incident Bot",
198+
},
199+
)
200+
201+
if response.status_code == 200:
202+
incident["slack_notified"] = True
203+
logger.info("Slack notification sent successfully")
204+
else:
205+
logger.warning(
206+
f"Slack notification failed: {response.text}"
207+
)
208+
else:
209+
logger.info(
210+
"Slack credentials not available, skipping notification"
211+
)
212+
except Exception as e:
213+
logger.warning(f"Failed to send Slack notification: {e}")
137214

138-
# Add deployment context
139-
incident_data["source"] = "modal_api"
215+
return {
216+
"status": "reported",
217+
"incident_id": incident["incident_id"],
218+
"slack_notified": incident.get("slack_notified", False),
219+
}
140220

141-
# Create the incident report
142-
return create_incident_report(incident_data, model_checksum[:8])
221+
except Exception as e:
222+
logger.error(f"Error reporting incident: {e}")
223+
return {"status": "error", "message": str(e)}
143224

144225

145226
def _monitor_data_drift() -> dict:

credit-scorer/src/configs/deployment.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ steps:
4040
approve_deployment:
4141
parameters:
4242
approval_thresholds:
43-
accuracy: 0.80
43+
accuracy: 0.70
4444
bias_disparity: 0.20
4545
risk_score: 0.40

credit-scorer/src/constants.py

Lines changed: 0 additions & 121 deletions
This file was deleted.

credit-scorer/src/constants/config.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ def get_volume_metadata(cls) -> Dict[str, str]:
6666
}
6767

6868

69-
class Incident:
70-
"""Incident reporting configuration."""
69+
class SlackConfig:
70+
"""Slack configuration parameters."""
7171

72-
SLACK_CHANNEL = "#credit-scoring-alerts"
73-
SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN")
72+
CHANNEL_ID = "C03ES6D8X0X"
73+
BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN")
7474

7575

7676
# Initialize required directories at module import

0 commit comments

Comments
 (0)