@euconform/core 1.0.0

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.
Files changed (89) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/LICENSE +21 -0
  3. package/dist/fairness-metrics.d.ts +65 -0
  4. package/dist/fairness-metrics.d.ts.map +1 -0
  5. package/dist/fairness-metrics.js +394 -0
  6. package/dist/index.d.ts +14 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +17 -0
  9. package/dist/inference/browser-inference.d.ts +60 -0
  10. package/dist/inference/browser-inference.d.ts.map +1 -0
  11. package/dist/inference/browser-inference.js +221 -0
  12. package/dist/inference/capability-detection.d.ts +183 -0
  13. package/dist/inference/capability-detection.d.ts.map +1 -0
  14. package/dist/inference/capability-detection.js +552 -0
  15. package/dist/inference/index.d.ts +43 -0
  16. package/dist/inference/index.d.ts.map +1 -0
  17. package/dist/inference/index.js +97 -0
  18. package/dist/inference/inference-factory.d.ts +73 -0
  19. package/dist/inference/inference-factory.d.ts.map +1 -0
  20. package/dist/inference/inference-factory.js +175 -0
  21. package/dist/inference/ollama-client.d.ts +114 -0
  22. package/dist/inference/ollama-client.d.ts.map +1 -0
  23. package/dist/inference/ollama-client.js +386 -0
  24. package/dist/legal-checks/annex-iv-report.d.ts +131 -0
  25. package/dist/legal-checks/annex-iv-report.d.ts.map +1 -0
  26. package/dist/legal-checks/annex-iv-report.js +62 -0
  27. package/dist/legal-checks/bias-metrics.d.ts +162 -0
  28. package/dist/legal-checks/bias-metrics.d.ts.map +1 -0
  29. package/dist/legal-checks/bias-metrics.js +408 -0
  30. package/dist/legal-checks/data-governance.d.ts +30 -0
  31. package/dist/legal-checks/data-governance.d.ts.map +1 -0
  32. package/dist/legal-checks/data-governance.js +99 -0
  33. package/dist/legal-checks/human-oversight-logging.d.ts +27 -0
  34. package/dist/legal-checks/human-oversight-logging.d.ts.map +1 -0
  35. package/dist/legal-checks/human-oversight-logging.js +106 -0
  36. package/dist/legal-checks/index.d.ts +13 -0
  37. package/dist/legal-checks/index.d.ts.map +1 -0
  38. package/dist/legal-checks/index.js +6 -0
  39. package/dist/legal-checks/risk-classifier.d.ts +35 -0
  40. package/dist/legal-checks/risk-classifier.d.ts.map +1 -0
  41. package/dist/legal-checks/risk-classifier.js +234 -0
  42. package/dist/legal-checks/robustness-transparency.d.ts +45 -0
  43. package/dist/legal-checks/robustness-transparency.d.ts.map +1 -0
  44. package/dist/legal-checks/robustness-transparency.js +111 -0
  45. package/dist/metrics/crows-pairs-bias.d.ts +41 -0
  46. package/dist/metrics/crows-pairs-bias.d.ts.map +1 -0
  47. package/dist/metrics/crows-pairs-bias.js +172 -0
  48. package/dist/metrics/index.d.ts +6 -0
  49. package/dist/metrics/index.d.ts.map +1 -0
  50. package/dist/metrics/index.js +5 -0
  51. package/dist/metrics/stereotype-bias.d.ts +89 -0
  52. package/dist/metrics/stereotype-bias.d.ts.map +1 -0
  53. package/dist/metrics/stereotype-bias.js +157 -0
  54. package/dist/risk-engine.d.ts +23 -0
  55. package/dist/risk-engine.d.ts.map +1 -0
  56. package/dist/risk-engine.js +451 -0
  57. package/dist/types.d.ts +131 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +5 -0
  60. package/package.json +28 -0
  61. package/src/fairness-metrics.ts +498 -0
  62. package/src/index.ts +150 -0
  63. package/src/inference/browser-inference.ts +258 -0
  64. package/src/inference/capability-detection.ts +714 -0
  65. package/src/inference/index.ts +156 -0
  66. package/src/inference/inference-factory.ts +222 -0
  67. package/src/inference/ollama-client.ts +514 -0
  68. package/src/inference/transformers.d.ts +62 -0
  69. package/src/legal-checks/annex-iv-report.ts +157 -0
  70. package/src/legal-checks/bias-metrics.ts +590 -0
  71. package/src/legal-checks/data-governance.ts +148 -0
  72. package/src/legal-checks/human-oversight-logging.ts +135 -0
  73. package/src/legal-checks/index.ts +55 -0
  74. package/src/legal-checks/risk-classifier.ts +285 -0
  75. package/src/legal-checks/robustness-transparency.ts +162 -0
  76. package/src/metrics/crows-pairs-bias.ts +245 -0
  77. package/src/metrics/index.ts +22 -0
  78. package/src/metrics/stereotype-bias.ts +267 -0
  79. package/src/risk-engine.ts +525 -0
  80. package/src/types.ts +154 -0
  81. package/tests/annex-iv-report.test.ts +323 -0
  82. package/tests/crows-pairs-bias.test.ts +342 -0
  83. package/tests/fairness-metrics.test.ts +187 -0
  84. package/tests/integration/bias-calculation.integration.test.ts +688 -0
  85. package/tests/legal-checks.test.ts +53 -0
  86. package/tests/ollama-client.test.ts +427 -0
  87. package/tests/risk-engine.test.ts +149 -0
  88. package/tsconfig.json +11 -0
  89. package/vitest.config.ts +17 -0
@@ -0,0 +1,187 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ disparateImpact,
4
+ equalizedOdds,
5
+ generateLoanDataset,
6
+ predictiveParity,
7
+ runFairnessAnalysis,
8
+ statisticalParityDifference,
9
+ } from "../src/fairness-metrics";
10
+ import type { PredictionData } from "../src/types";
11
+
12
+ describe("Fairness Metrics", () => {
13
+ describe("disparateImpact", () => {
14
+ it("should return 1.0 for perfectly fair predictions", () => {
15
+ const data: PredictionData[] = [
16
+ { prediction: 1, protectedGroup: "A" },
17
+ { prediction: 0, protectedGroup: "A" },
18
+ { prediction: 1, protectedGroup: "B" },
19
+ { prediction: 0, protectedGroup: "B" },
20
+ ];
21
+
22
+ const result = disparateImpact(data);
23
+ expect(result.value).toBe(1);
24
+ expect(result.passed).toBe(true);
25
+ });
26
+
27
+ it("should detect bias when one group has lower positive rate", () => {
28
+ const data: PredictionData[] = [
29
+ // Group A: 75% positive
30
+ { prediction: 1, protectedGroup: "A" },
31
+ { prediction: 1, protectedGroup: "A" },
32
+ { prediction: 1, protectedGroup: "A" },
33
+ { prediction: 0, protectedGroup: "A" },
34
+ // Group B: 25% positive
35
+ { prediction: 0, protectedGroup: "B" },
36
+ { prediction: 0, protectedGroup: "B" },
37
+ { prediction: 0, protectedGroup: "B" },
38
+ { prediction: 1, protectedGroup: "B" },
39
+ ];
40
+
41
+ const result = disparateImpact(data);
42
+ expect(result.value).toBeCloseTo(0.333, 2);
43
+ expect(result.passed).toBe(false);
44
+ });
45
+
46
+ it("should pass when ratio is above 0.8 threshold", () => {
47
+ const data: PredictionData[] = [
48
+ // Group A: 100% positive
49
+ { prediction: 1, protectedGroup: "A" },
50
+ { prediction: 1, protectedGroup: "A" },
51
+ // Group B: 90% positive
52
+ { prediction: 1, protectedGroup: "B" },
53
+ { prediction: 1, protectedGroup: "B" },
54
+ { prediction: 1, protectedGroup: "B" },
55
+ { prediction: 1, protectedGroup: "B" },
56
+ { prediction: 1, protectedGroup: "B" },
57
+ { prediction: 1, protectedGroup: "B" },
58
+ { prediction: 1, protectedGroup: "B" },
59
+ { prediction: 1, protectedGroup: "B" },
60
+ { prediction: 1, protectedGroup: "B" },
61
+ { prediction: 0, protectedGroup: "B" },
62
+ ];
63
+
64
+ const result = disparateImpact(data);
65
+ expect(result.passed).toBe(true);
66
+ });
67
+ });
68
+
69
+ describe("statisticalParityDifference", () => {
70
+ it("should return 0 for equal rates", () => {
71
+ const data: PredictionData[] = [
72
+ { prediction: 1, protectedGroup: "A" },
73
+ { prediction: 0, protectedGroup: "A" },
74
+ { prediction: 1, protectedGroup: "B" },
75
+ { prediction: 0, protectedGroup: "B" },
76
+ ];
77
+
78
+ const result = statisticalParityDifference(data);
79
+ expect(result.value).toBe(0);
80
+ expect(result.passed).toBe(true);
81
+ });
82
+
83
+ it("should detect large differences in rates", () => {
84
+ const data: PredictionData[] = [
85
+ { prediction: 1, protectedGroup: "A" },
86
+ { prediction: 1, protectedGroup: "A" },
87
+ { prediction: 0, protectedGroup: "B" },
88
+ { prediction: 0, protectedGroup: "B" },
89
+ ];
90
+
91
+ const result = statisticalParityDifference(data);
92
+ expect(result.value).toBe(1); // 100% - 0% = 100%
93
+ expect(result.passed).toBe(false);
94
+ });
95
+ });
96
+
97
+ describe("equalizedOdds", () => {
98
+ it("should require labels for calculation", () => {
99
+ const data: PredictionData[] = [
100
+ { prediction: 1, protectedGroup: "A" },
101
+ { prediction: 0, protectedGroup: "B" },
102
+ ];
103
+
104
+ const result = equalizedOdds(data);
105
+ expect(result.passed).toBe(false);
106
+ expect(result.description).toContain("labels required");
107
+ });
108
+
109
+ it("should calculate with labels present", () => {
110
+ const data: PredictionData[] = [
111
+ { prediction: 1, label: 1, protectedGroup: "A" },
112
+ { prediction: 0, label: 0, protectedGroup: "A" },
113
+ { prediction: 1, label: 1, protectedGroup: "B" },
114
+ { prediction: 0, label: 0, protectedGroup: "B" },
115
+ ];
116
+
117
+ const result = equalizedOdds(data);
118
+ expect(result.value).toBe(0);
119
+ expect(result.passed).toBe(true);
120
+ });
121
+ });
122
+
123
+ describe("predictiveParity", () => {
124
+ it("should require labels for calculation", () => {
125
+ const data: PredictionData[] = [{ prediction: 1, protectedGroup: "A" }];
126
+
127
+ const result = predictiveParity(data);
128
+ expect(result.passed).toBe(false);
129
+ });
130
+
131
+ it("should pass when precision is equal across groups", () => {
132
+ const data: PredictionData[] = [
133
+ // Group A: 2 TP, 1 FP = 66.7% precision
134
+ { prediction: 1, label: 1, protectedGroup: "A" },
135
+ { prediction: 1, label: 1, protectedGroup: "A" },
136
+ { prediction: 1, label: 0, protectedGroup: "A" },
137
+ // Group B: 2 TP, 1 FP = 66.7% precision
138
+ { prediction: 1, label: 1, protectedGroup: "B" },
139
+ { prediction: 1, label: 1, protectedGroup: "B" },
140
+ { prediction: 1, label: 0, protectedGroup: "B" },
141
+ ];
142
+
143
+ const result = predictiveParity(data);
144
+ expect(result.value).toBeCloseTo(0, 2);
145
+ expect(result.passed).toBe(true);
146
+ });
147
+ });
148
+
149
+ describe("runFairnessAnalysis", () => {
150
+ it("should return all metrics", () => {
151
+ const data: PredictionData[] = [
152
+ { prediction: 1, label: 1, protectedGroup: "A" },
153
+ { prediction: 0, label: 0, protectedGroup: "B" },
154
+ ];
155
+
156
+ const result = runFairnessAnalysis(data, "test-model", "gender");
157
+
158
+ expect(result.modelId).toBe("test-model");
159
+ expect(result.protectedAttribute).toBe("gender");
160
+ expect(result.metrics).toHaveLength(4);
161
+ expect(result.samplesAnalyzed).toBe(2);
162
+ expect(result.timestamp).toBeDefined();
163
+ });
164
+ });
165
+
166
+ describe("generateLoanDataset", () => {
167
+ it("should generate specified number of samples", () => {
168
+ const data = generateLoanDataset(100);
169
+ expect(data).toHaveLength(100);
170
+ });
171
+
172
+ it("should include all required fields", () => {
173
+ const data = generateLoanDataset(10);
174
+ for (const item of data) {
175
+ expect(item.prediction).toBeDefined();
176
+ expect(item.label).toBeDefined();
177
+ expect(item.protectedGroup).toBeDefined();
178
+ }
179
+ });
180
+
181
+ it("should have multiple protected groups", () => {
182
+ const data = generateLoanDataset(1000);
183
+ const groups = new Set(data.map((d) => d.protectedGroup));
184
+ expect(groups.size).toBeGreaterThan(1);
185
+ });
186
+ });
187
+ });