@@ -48,103 +48,121 @@ def approve_deployment(
4848 """
4949 # Timestamp for record-keeping
5050 timestamp = datetime .now ().isoformat ()
51-
51+
5252 print ("\n " + "=" * 60 )
5353 print (" HUMAN OVERSIGHT REQUIRED (EU AI Act Article 14) " )
5454 print ("=" * 60 )
55-
55+
5656 # Extract metrics for display
5757 metrics = evaluation_results .get ("metrics" , {})
5858 fairness_data = evaluation_results .get ("fairness" , {})
5959 fairness_metrics = fairness_data .get ("fairness_metrics" , {})
6060 bias_flag = fairness_data .get ("bias_flag" , False )
61-
61+
6262 # Performance metrics summary
6363 print ("\n 📊 PERFORMANCE METRICS:" )
6464 print (f" • Accuracy: { metrics .get ('accuracy' , 'N/A' ):.4f} " )
6565 print (f" • Precision: { metrics .get ('precision' , 'N/A' ):.4f} " )
6666 print (f" • Recall: { metrics .get ('recall' , 'N/A' ):.4f} " )
6767 print (f" • F1 Score: { metrics .get ('f1_score' , 'N/A' ):.4f} " )
6868 print (f" • AUC-ROC: { metrics .get ('auc_roc' , 'N/A' ):.4f} " )
69- print (f" • Average Precision: { metrics .get ('average_precision' , 'N/A' ):.4f} " )
70- print (f" • Balanced Accuracy: { metrics .get ('balanced_accuracy' , 'N/A' ):.4f} " )
71-
69+ print (
70+ f" • Average Precision: { metrics .get ('average_precision' , 'N/A' ):.4f} "
71+ )
72+ print (
73+ f" • Balanced Accuracy: { metrics .get ('balanced_accuracy' , 'N/A' ):.4f} "
74+ )
75+
7276 # Financial impact metrics
7377 print ("\n 💰 FINANCIAL IMPACT:" )
74- print (f" • Optimal Threshold: { metrics .get ('optimal_threshold' , 'N/A' ):.4f} " )
78+ print (
79+ f" • Optimal Threshold: { metrics .get ('optimal_threshold' , 'N/A' ):.4f} "
80+ )
7581 print (f" • Normalized Cost: { metrics .get ('normalized_cost' , 'N/A' ):.4f} " )
76-
77- # Fairness summary (aggregated, not per-group)
82+
83+ # Fairness summary (aggregated, not per-group)
7884 print (f"\n ⚖️ FAIRNESS ASSESSMENT:" )
7985 if bias_flag :
8086 print (" 🚨 BIAS DETECTED - Requires careful review" )
81-
87+
8288 # Show worst disparity without listing all groups
8389 max_disparity = 0
8490 worst_attribute = None
85-
91+
8692 for attribute , attr_metrics in fairness_metrics .items ():
8793 disparity = abs (attr_metrics .get ("selection_rate_disparity" , 0 ))
8894 if disparity > max_disparity :
8995 max_disparity = disparity
9096 worst_attribute = attribute
91-
97+
9298 if worst_attribute :
93- print (f" • Highest disparity: { max_disparity :.3f} ({ worst_attribute } )" )
94-
99+ print (
100+ f" • Highest disparity: { max_disparity :.3f} ({ worst_attribute } )"
101+ )
102+
95103 print (f" • Protected attributes analyzed: { len (fairness_metrics )} " )
96104 else :
97105 print (" ✅ No significant bias detected across protected groups" )
98106 print (f" • Protected attributes analyzed: { len (fairness_metrics )} " )
99-
107+
100108 # Risk assessment
101109 print (f"\n ⚠️ RISK ASSESSMENT:" )
102110 print (f" • Overall Risk Score: { risk_scores .get ('overall' , 0 ):.3f} " )
103111 print (f" • Risk Level: { risk_scores .get ('risk_level' , 'Unknown' )} " )
104-
112+
105113 high_risk_count = len (risk_scores .get ("high_risk_factors" , []))
106114 if high_risk_count > 0 :
107115 print (f" • High-risk factors identified: { high_risk_count } " )
108-
116+
109117 # Approval criteria check
110118 threshold_checks = {
111- "Performance" : metrics .get ("accuracy" , 0 ) >= approval_thresholds .get ("accuracy" , 0.7 ),
119+ "Performance" : metrics .get ("accuracy" , 0 )
120+ >= approval_thresholds .get ("accuracy" , 0.7 ),
112121 "Fairness" : not bias_flag ,
113- "Risk" : risk_scores .get ("overall" , 1 ) <= approval_thresholds .get ("risk_score" , 0.8 ),
122+ "Risk" : risk_scores .get ("overall" , 1 )
123+ <= approval_thresholds .get ("risk_score" , 0.8 ),
114124 }
115-
125+
116126 print (f"\n 🔍 APPROVAL CRITERIA:" )
117127 all_passed = True
118128 for check_name , passed in threshold_checks .items ():
119129 status = "✅ PASS" if passed else "❌ FAIL"
120130 print (f" • { check_name } : { status } " )
121131 if not passed :
122132 all_passed = False
123-
124- print (f"\n 📝 RECOMMENDATION: { '✅ APPROVE' if all_passed else '⚠️ REVIEW REQUIRED' } " )
125-
133+
134+ print (
135+ f"\n 📝 RECOMMENDATION: { '✅ APPROVE' if all_passed else '⚠️ REVIEW REQUIRED' } "
136+ )
137+
126138 # Get decision
127139 decision = os .getenv ("DEPLOY_APPROVAL" )
128-
140+
129141 if decision is None :
130142 if bias_flag :
131143 print ("\n ⚠️ WARNING: Review fairness implications before approval" )
132-
133- decision = input (f"\n Approve deployment? ({ 'Y/n' if all_passed else 'y/N' } ): " ).strip ().lower ()
144+
145+ decision = (
146+ input (
147+ f"\n Approve deployment? ({ 'Y/n' if all_passed else 'y/N' } ): "
148+ )
149+ .strip ()
150+ .lower ()
151+ )
134152 approver = os .getenv ("USER" , input ("Approver name: " ).strip ())
135153 rationale = input ("Decision rationale: " ).strip ()
136154 decision_mode = "interactive"
137155 else :
138156 approver = os .getenv ("APPROVER" , "automated" )
139157 rationale = os .getenv ("APPROVAL_RATIONALE" , "Automated approval" )
140158 decision_mode = "automated"
141-
159+
142160 # Handle default approval logic
143161 if decision == "" :
144162 approved = all_passed # Default to approve only if all criteria pass
145163 else :
146164 approved = decision in ["y" , "yes" ]
147-
165+
148166 # Create approval record
149167 approval_record = {
150168 "approval_id" : f"approval_{ timestamp .replace (':' , '-' )} " ,
@@ -163,17 +181,21 @@ def approve_deployment(
163181 "risk_score" : risk_scores .get ("overall" ),
164182 },
165183 "protected_attributes_count" : len (fairness_metrics ),
166- "max_bias_disparity" : max ([
167- abs (attr_metrics .get ("selection_rate_disparity" , 0 ))
168- for attr_metrics in fairness_metrics .values ()
169- ]) if fairness_metrics else 0 ,
184+ "max_bias_disparity" : max (
185+ [
186+ abs (attr_metrics .get ("selection_rate_disparity" , 0 ))
187+ for attr_metrics in fairness_metrics .values ()
188+ ]
189+ )
190+ if fairness_metrics
191+ else 0 ,
170192 }
171-
193+
172194 # Final status
173195 if approved :
174196 print (f"\n ✅ DEPLOYMENT APPROVED by { approver } " )
175197 else :
176198 print (f"\n ❌ DEPLOYMENT REJECTED by { approver } " )
177199 raise RuntimeError (f"Deployment rejected by { approver } : { rationale } " )
178-
179- return approved , approval_record
200+
201+ return approved , approval_record
0 commit comments