@@ -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
134130def _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
145226def _monitor_data_drift () -> dict :
0 commit comments