rti-ethics-engine 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ """
2
+ RTI Ethics Engine - Production AI Ethics Framework for RTI Applications
3
+ India's first granular 9-dimension AI ethics evaluator (36 metrics)
4
+
5
+ Quick Start:
6
+ from rti_ethics_engine import evaluate
7
+
8
+ metrics = {
9
+ "fairness": {"spd": 0.02, "di": 1.0, "eop": 0.05, "aod": 0.03},
10
+ "legal": {"lawfulness": 1.0, "cvr": 0.98},
11
+ # ... other dimensions
12
+ }
13
+
14
+ result = evaluate(metrics)
15
+ print(result.final_label) # "ETHICAL"
16
+ print(result.s_llm) # 0.92
17
+
18
+ # Granular access
19
+ print(result.get_l1_metric("fairness", "spd")) # 0.02
20
+ print(result.get_failed_dims()) # []
21
+ """
22
+
23
+ from .core import EthicsEngine
24
+ from .results import EthicsResult, DimensionResult, RiskBand, FinalLabel
25
+ from .config import (
26
+ L1_THRESHOLDS,
27
+ DIMENSION_WEIGHTS,
28
+ RISK_THRESHOLDS,
29
+ DIMENSION_INFO,
30
+ METRIC_CATALOG
31
+ )
32
+ from .metrics import build_default_metrics
33
+
34
+ __version__ = "1.0.0"
35
+ __author__ = "RTI Ethics Engine Team"
36
+ __license__ = "MIT"
37
+
38
+ __all__ = [
39
+ # Core classes
40
+ "EthicsEngine",
41
+ "EthicsResult",
42
+ "DimensionResult",
43
+ # Enums
44
+ "RiskBand",
45
+ "FinalLabel",
46
+ # Config
47
+ "L1_THRESHOLDS",
48
+ "DIMENSION_WEIGHTS",
49
+ "RISK_THRESHOLDS",
50
+ "DIMENSION_INFO",
51
+ "METRIC_CATALOG",
52
+ # Helpers
53
+ "evaluate",
54
+ "build_default_metrics",
55
+ ]
56
+
57
+
58
+ def evaluate(metrics: dict) -> EthicsResult:
59
+ """
60
+ Quick evaluation entrypoint.
61
+
62
+ Args:
63
+ metrics: Dict of dimension -> metrics mapping.
64
+
65
+ Returns:
66
+ EthicsResult with complete evaluation and granular accessors.
67
+
68
+ Example:
69
+ result = evaluate({"fairness": {"spd": 0.02}, "legal": {"lawfulness": 1.0}})
70
+ print(result.final_label) # "ETHICAL"
71
+ """
72
+ engine = EthicsEngine()
73
+ return engine.run_protocol(metrics)
@@ -0,0 +1,169 @@
1
+ """
2
+ RTI Ethics Engine - Configuration Module
3
+ Centralized thresholds and weights for all 36 metrics.
4
+ """
5
+
6
+ from typing import Dict, Any
7
+
8
+ # ================== L1 THRESHOLDS (Hard Gates) ==================
9
+ # These are the strict mathematical gates that must pass for L1 approval.
10
+ # Violation of ANY threshold results in L1 FAIL for that dimension.
11
+
12
+ L1_THRESHOLDS: Dict[str, Dict[str, float]] = {
13
+ "fairness": {
14
+ "spd": 0.1, # Statistical Parity Difference: |SPD| <= 0.1
15
+ "di_min": 0.8, # Disparate Impact: 0.8 <= DI <= 1.25
16
+ "di_max": 1.25,
17
+ "eop": 0.1, # Equal Opportunity Difference: |EOP| <= 0.1
18
+ "aod": 0.1 # Average Odds Difference: |AOD| <= 0.1
19
+ },
20
+ "drift": {
21
+ "psi": 0.2, # Population Stability Index: PSI <= 0.2
22
+ "ks": 0.05 # Kolmogorov-Smirnov statistic: KS <= 0.05
23
+ },
24
+ "environmental": {
25
+ "inf_cost_max": 0.05, # Max inference cost (USD)
26
+ "gpu_hours_max": 0.1 # Max GPU hours per query
27
+ },
28
+ "explainability": {
29
+ "confidence_min": 0.6, # Minimum model confidence
30
+ "fi_min": 0.1 # Minimum feature importance coverage
31
+ },
32
+ "ux": {
33
+ "tcr_min": 0.85, # Task Completion Rate: TCR >= 0.85
34
+ "ftr_max": 0.15 # Frustration Rate: FTR <= 0.15
35
+ },
36
+ "legal": {
37
+ "lawfulness": 1.0, # Must have legal basis (binary)
38
+ "cvr_min": 0.95 # Consent Validity Rate: CVR >= 0.95
39
+ },
40
+ "performance": {
41
+ "accuracy_min": 0.8, # Minimum accuracy
42
+ "recall_min": 0.75 # Minimum recall
43
+ },
44
+ "monitoring": {
45
+ "alc_min": 0.95, # Audit Log Completeness: ALC >= 0.95
46
+ "sla_min": 0.99 # SLA Compliance: SLA >= 0.99
47
+ },
48
+ "security": {
49
+ "adv_acc_min": 0.7, # Adversarial Accuracy: AdvAcc >= 0.7
50
+ "pisr_max": 0.05 # Prompt Injection Success Rate: PISR <= 0.05
51
+ }
52
+ }
53
+
54
+ # ================== DIMENSION WEIGHTS (Step 904) ==================
55
+ # Authoritative Policy Weights - NO NORMALIZATION (Sum = 1.24)
56
+ # These reflect the relative importance of each dimension in RTI context.
57
+
58
+ DIMENSION_WEIGHTS: Dict[str, float] = {
59
+ "legal": 0.20,
60
+ "fairness": 0.20,
61
+ "explainability": 0.16,
62
+ "security": 0.16,
63
+ "monitoring": 0.16,
64
+ "performance": 0.12,
65
+ "ux": 0.12,
66
+ "drift": 0.08,
67
+ "environmental": 0.04
68
+ }
69
+
70
+ # ================== RISK THRESHOLDS ==================
71
+ # Tau values for categorical decision boundaries
72
+
73
+ RISK_THRESHOLDS: Dict[str, float] = {
74
+ "tau1": 0.3, # Below this: LOW risk
75
+ "tau2": 0.7 # Above this: HIGH risk (0.3-0.7 = MEDIUM)
76
+ }
77
+
78
+ # ================== DIMENSION METADATA ==================
79
+ # Human-readable names and descriptions for each dimension
80
+
81
+ DIMENSION_INFO: Dict[str, Dict[str, str]] = {
82
+ "legal": {
83
+ "name": "Legal Compliance",
84
+ "description": "Statutory alignment with the RTI Act 2005."
85
+ },
86
+ "fairness": {
87
+ "name": "Bias & Fairness",
88
+ "description": "Ensures equitable outcomes across demographics."
89
+ },
90
+ "explainability": {
91
+ "name": "Explainability & Transparency",
92
+ "description": "Ensures system reasoning is interpretable."
93
+ },
94
+ "security": {
95
+ "name": "Security & Robustness",
96
+ "description": "Resistance to prompt injection and poisoning."
97
+ },
98
+ "monitoring": {
99
+ "name": "Monitoring & Compliance",
100
+ "description": "Integrity of the audit trail."
101
+ },
102
+ "performance": {
103
+ "name": "Model Performance",
104
+ "description": "Core statistical accuracy and recall."
105
+ },
106
+ "ux": {
107
+ "name": "Human-AI UX",
108
+ "description": "Evaluates user friction and reading complexity."
109
+ },
110
+ "drift": {
111
+ "name": "Data & Model Drift",
112
+ "description": "Detects performance degradation over time."
113
+ },
114
+ "environmental": {
115
+ "name": "Environmental & Cost",
116
+ "description": "Monitors carbon footprint and compute efficiency."
117
+ }
118
+ }
119
+
120
+ # ================== METRIC CATALOG ==================
121
+ # Complete catalog of all 36 metrics organized by layer and dimension
122
+
123
+ METRIC_CATALOG: Dict[str, Dict[str, list]] = {
124
+ "fairness": {
125
+ "l1": ["spd", "di", "eop", "aod"],
126
+ "l2": ["fnrd", "pred_parity", "refusal_disp"],
127
+ "l3": ["counterfactual", "mi_leakage"]
128
+ },
129
+ "drift": {
130
+ "l1": ["psi", "ks"],
131
+ "l2": ["pred_drift", "conf_drift"],
132
+ "l3": ["emb_drift"]
133
+ },
134
+ "environmental": {
135
+ "l1": ["inf_cost", "gpu_hours"],
136
+ "l2": ["compute_intensity", "hardware_util"],
137
+ "l3": ["cur"]
138
+ },
139
+ "explainability": {
140
+ "l1": ["confidence", "fi"],
141
+ "l2": ["counterfactual_dist"],
142
+ "l3": ["exp_stability"]
143
+ },
144
+ "ux": {
145
+ "l1": ["tcr", "ftr"],
146
+ "l2": ["dtc", "cognitive_load"],
147
+ "l3": ["readability_fk"]
148
+ },
149
+ "legal": {
150
+ "l1": ["lawfulness", "cvr"],
151
+ "l2": ["proportionality"],
152
+ "l3": ["dpia"]
153
+ },
154
+ "performance": {
155
+ "l1": ["accuracy", "recall"],
156
+ "l2": ["group_f1"],
157
+ "l3": ["acc_lt"]
158
+ },
159
+ "monitoring": {
160
+ "l1": ["alc", "sla"],
161
+ "l2": ["pvr"],
162
+ "l3": ["irf"]
163
+ },
164
+ "security": {
165
+ "l1": ["adv_acc", "pisr"],
166
+ "l2": ["mir"],
167
+ "l3": ["pdr"]
168
+ }
169
+ }
@@ -0,0 +1,184 @@
1
+ """
2
+ RTI Ethics Engine - Core Orchestrator
3
+ Main EthicsEngine class that coordinates evaluation across all dimensions.
4
+ """
5
+
6
+ from datetime import datetime
7
+ from typing import Dict, Any
8
+
9
+ from .config import DIMENSION_WEIGHTS, RISK_THRESHOLDS, DIMENSION_INFO
10
+ from .dimensions import create_all_dimensions
11
+ from .results import EthicsResult, DimensionResult
12
+
13
+
14
+ class EthicsEngine:
15
+ """
16
+ RTI Ethics Engine - Production AI Ethics Evaluator
17
+
18
+ Implements a 3-layer evaluation protocol:
19
+ - Layer 1 (L1): Hard mathematical gates (pass/fail)
20
+ - Layer 2 (L2): Weighted risk scoring [0, 1]
21
+ - Layer 3 (L3): Forensic evidence bundle for CIC auditors
22
+
23
+ Example usage:
24
+ engine = EthicsEngine()
25
+ result = engine.run_protocol(metrics)
26
+
27
+ # Access results
28
+ print(result.final_label) # "ETHICAL"
29
+ print(result.get_l1_metric("fairness", "spd")) # 0.023
30
+ print(result.get_failed_dims()) # []
31
+ """
32
+
33
+ def __init__(self):
34
+ """Initialize the ethics engine with all 9 dimensions."""
35
+ self.dimensions = create_all_dimensions()
36
+ self.weights = DIMENSION_WEIGHTS
37
+
38
+ def run_protocol(self, all_metrics: Dict[str, Dict[str, Any]]) -> EthicsResult:
39
+ """
40
+ Execute the complete 3-layer ethics evaluation protocol.
41
+
42
+ Args:
43
+ all_metrics: Dict of dimension -> metrics mapping.
44
+ Example: {
45
+ "fairness": {"spd": 0.02, "di": 1.0, "eop": 0.05, "aod": 0.03},
46
+ "legal": {"lawfulness": 1.0, "cvr": 0.98},
47
+ ...
48
+ }
49
+
50
+ Returns:
51
+ EthicsResult with complete evaluation data and granular accessors.
52
+ """
53
+ # Initialize tracking
54
+ total_weighted_risk = 0.0
55
+ risk_vector: Dict[str, float] = {}
56
+ audit_trail: list = []
57
+ l1_failed_dims: list = []
58
+ dimension_results: Dict[str, DimensionResult] = {}
59
+ layer3_explanations: Dict[str, Any] = {}
60
+
61
+ # Generate audit ID
62
+ audit_id = f"RTI-{datetime.now().strftime('%Y%j%H%M%S')}"
63
+
64
+ # STEP 1: Evaluate ALL dimensions (NO early return)
65
+ for key, dim in self.dimensions.items():
66
+ m = all_metrics.get(key, {})
67
+
68
+ # Layer 1: Hard gate evaluation
69
+ l1_status, l1_reasons = dim.evaluate_layer1(m)
70
+
71
+ # L1 FAIL -> L2 = 1.0 MAX RISK (hard gate enforcement)
72
+ if l1_status == "FAIL":
73
+ l2_risk = 1.0
74
+ l2_indicators = ["L1_HARD_GATE_VIOLATION"]
75
+ l1_failed_dims.append(dim.name)
76
+ audit_trail.append(f"L1 {dim.name}: ❌ FAIL -> L2=1.00")
77
+ else:
78
+ # Layer 2: Risk scoring
79
+ l2_risk, l2_indicators = dim.evaluate_layer2(m)
80
+ audit_trail.append(f"L1 {dim.name}: ✅ PASS -> L2={l2_risk:.2f}")
81
+
82
+ # Layer 3: Forensic evidence
83
+ l3_evidence = dim.get_layer3_evidence(
84
+ m, (l1_status, l1_reasons), (l2_risk, l2_indicators)
85
+ )
86
+
87
+ # Store dimension result
88
+ dimension_results[key] = DimensionResult(
89
+ key=key,
90
+ name=dim.name,
91
+ l1_status=l1_status,
92
+ l1_reasons=l1_reasons,
93
+ l2_risk=l2_risk,
94
+ l2_indicators=l2_indicators,
95
+ l3_evidence=l3_evidence,
96
+ raw_metrics=m
97
+ )
98
+
99
+ # Weighted risk aggregation
100
+ total_weighted_risk += l2_risk * self.weights[key]
101
+ risk_vector[dim.name] = round(l2_risk, 4)
102
+ layer3_explanations[key] = l3_evidence
103
+
104
+ # STEP 2: Calculate S_LLM (LLM Ethics Score)
105
+ s_llm = 1.0 - total_weighted_risk
106
+
107
+ # STEP 3: Determine final decision based on weighted risk
108
+ final_label, risk_band, required_action = self._determine_decision(
109
+ total_weighted_risk, l1_failed_dims, dimension_results
110
+ )
111
+
112
+ # Determine overall L1 status
113
+ layer1_status = "FAIL" if l1_failed_dims else "PASS"
114
+
115
+ # Build and return EthicsResult
116
+ return EthicsResult(
117
+ final_label=final_label,
118
+ layer1_status=layer1_status,
119
+ layer2_risk_score=round(total_weighted_risk, 4),
120
+ s_llm=round(s_llm, 4),
121
+ risk_band=risk_band,
122
+ required_action=required_action,
123
+ audit_id=audit_id,
124
+ risk_vector=risk_vector,
125
+ dimension_results=dimension_results,
126
+ layer3_evidence={
127
+ "audit_trail": audit_trail,
128
+ "explanations": layer3_explanations
129
+ }
130
+ )
131
+
132
+ def _determine_decision(
133
+ self,
134
+ total_risk: float,
135
+ l1_failed: list,
136
+ dimension_results: Dict[str, DimensionResult]
137
+ ) -> tuple:
138
+ """
139
+ Determine final label, risk band, and required action.
140
+
141
+ Decision logic:
142
+ 1. If all L1 fail AND high risk -> CRITICAL / BLOCK_AND_PURGE
143
+ 2. If risk > tau2 (0.7) -> UNETHICAL / HIGH / BLOCK_AND_REVIEW
144
+ 3. If any L1 fail -> CONDITIONALLY_ETHICAL / MEDIUM / REVIEW_FAILED_DIMS
145
+ 4. If risk > tau1 (0.3) -> CONDITIONALLY_ETHICAL / MEDIUM / HUMAN_REVIEW
146
+ 5. Otherwise -> ETHICAL / LOW / NONE
147
+ """
148
+ tau1 = RISK_THRESHOLDS["tau1"]
149
+ tau2 = RISK_THRESHOLDS["tau2"]
150
+
151
+ if total_risk > tau2:
152
+ # High aggregate risk (>= 0.70)
153
+ all_failed = all(v.l1_status == "FAIL" for v in dimension_results.values())
154
+ if all_failed:
155
+ return "UNETHICAL", "CRITICAL", "BLOCK_AND_PURGE"
156
+ return "UNETHICAL", "HIGH", "BLOCK_AND_REVIEW"
157
+
158
+ elif l1_failed:
159
+ # At least one dimension failed L1, but total risk is manageable
160
+ return "CONDITIONALLY_ETHICAL", "MEDIUM", f"REVIEW_FAILED_DIMS ({', '.join(l1_failed)})"
161
+
162
+ elif total_risk > tau1:
163
+ # All L1 pass, but moderate aggregate risk (> 0.30)
164
+ return "CONDITIONALLY_ETHICAL", "MEDIUM", "HUMAN_REVIEW (Risk)"
165
+
166
+ else:
167
+ # Low risk (< 0.30) and all L1 pass
168
+ return "ETHICAL", "LOW", "NONE"
169
+
170
+ def evaluate(self, all_metrics: Dict[str, Dict[str, Any]]) -> EthicsResult:
171
+ """Alias for run_protocol() for convenience."""
172
+ return self.run_protocol(all_metrics)
173
+
174
+ def get_dimension_names(self) -> list:
175
+ """Get list of all dimension names."""
176
+ return [dim.name for dim in self.dimensions.values()]
177
+
178
+ def get_dimension_keys(self) -> list:
179
+ """Get list of all dimension keys."""
180
+ return list(self.dimensions.keys())
181
+
182
+ def get_weights(self) -> Dict[str, float]:
183
+ """Get dimension weights."""
184
+ return self.weights.copy()