@euconform/core 1.4.0 → 1.6.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 (179) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +46 -0
  3. package/dist/datasets/crows-pairs.d.ts +18 -0
  4. package/dist/datasets/crows-pairs.d.ts.map +1 -1
  5. package/dist/datasets/crows-pairs.js +58 -0
  6. package/dist/evidence/bias-comparison-markdown.d.ts +11 -0
  7. package/dist/evidence/bias-comparison-markdown.d.ts.map +1 -0
  8. package/dist/evidence/bias-comparison-markdown.js +120 -0
  9. package/dist/evidence/bias-comparison.d.ts +133 -0
  10. package/dist/evidence/bias-comparison.d.ts.map +1 -0
  11. package/dist/evidence/bias-comparison.js +274 -0
  12. package/dist/evidence/bias-statistics.d.ts +72 -0
  13. package/dist/evidence/bias-statistics.d.ts.map +1 -0
  14. package/dist/evidence/bias-statistics.js +122 -0
  15. package/dist/evidence/bias-v2-semantics.d.ts +19 -0
  16. package/dist/evidence/bias-v2-semantics.d.ts.map +1 -0
  17. package/dist/evidence/bias-v2-semantics.js +133 -0
  18. package/dist/evidence/browser.d.ts +18 -0
  19. package/dist/evidence/browser.d.ts.map +1 -0
  20. package/dist/evidence/browser.js +14 -0
  21. package/dist/evidence/bundle.d.ts +1 -0
  22. package/dist/evidence/bundle.d.ts.map +1 -1
  23. package/dist/evidence/bundle.js +2 -0
  24. package/dist/evidence/index.d.ts +9 -2
  25. package/dist/evidence/index.d.ts.map +1 -1
  26. package/dist/evidence/index.js +5 -1
  27. package/dist/evidence/output.d.ts.map +1 -1
  28. package/dist/evidence/output.js +1 -0
  29. package/dist/evidence/types.d.ts +183 -3
  30. package/dist/evidence/types.d.ts.map +1 -1
  31. package/dist/evidence/validate-bias-v2.d.ts +31 -0
  32. package/dist/evidence/validate-bias-v2.d.ts.map +1 -0
  33. package/dist/evidence/validate-bias-v2.js +40 -0
  34. package/dist/evidence/validate.d.ts +29 -2
  35. package/dist/evidence/validate.d.ts.map +1 -1
  36. package/dist/evidence/validate.js +96 -11
  37. package/dist/fairness-metrics.d.ts +15 -11
  38. package/dist/fairness-metrics.d.ts.map +1 -1
  39. package/dist/fairness-metrics.js +59 -183
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/inference/browser-inference.d.ts +6 -57
  44. package/dist/inference/browser-inference.d.ts.map +1 -1
  45. package/dist/inference/browser-inference.js +6 -219
  46. package/dist/inference/capability-detection.d.ts +9 -2
  47. package/dist/inference/capability-detection.d.ts.map +1 -1
  48. package/dist/inference/capability-detection.js +18 -16
  49. package/dist/inference/index.d.ts +15 -2
  50. package/dist/inference/index.d.ts.map +1 -1
  51. package/dist/inference/index.js +12 -3
  52. package/dist/inference/inference-factory.d.ts +6 -3
  53. package/dist/inference/inference-factory.d.ts.map +1 -1
  54. package/dist/inference/inference-factory.js +7 -26
  55. package/dist/inference/ollama-client.d.ts +29 -0
  56. package/dist/inference/ollama-client.d.ts.map +1 -1
  57. package/dist/inference/ollama-client.js +29 -0
  58. package/dist/inference/scored-log-prob-types.d.ts +64 -0
  59. package/dist/inference/scored-log-prob-types.d.ts.map +1 -0
  60. package/dist/inference/scored-log-prob-types.js +12 -0
  61. package/dist/inference/sequence-logprob.d.ts +34 -0
  62. package/dist/inference/sequence-logprob.d.ts.map +1 -0
  63. package/dist/inference/sequence-logprob.js +87 -0
  64. package/dist/inference/template-scoring.d.ts +124 -0
  65. package/dist/inference/template-scoring.d.ts.map +1 -0
  66. package/dist/inference/template-scoring.js +164 -0
  67. package/dist/inference/transformers-inference.d.ts +124 -0
  68. package/dist/inference/transformers-inference.d.ts.map +1 -0
  69. package/dist/inference/transformers-inference.js +392 -0
  70. package/dist/legal-checks/bias-metrics.d.ts +5 -7
  71. package/dist/legal-checks/bias-metrics.d.ts.map +1 -1
  72. package/dist/legal-checks/bias-metrics.js +8 -101
  73. package/dist/metrics/crows-pairs-bias.d.ts +17 -9
  74. package/dist/metrics/crows-pairs-bias.d.ts.map +1 -1
  75. package/dist/metrics/crows-pairs-bias.js +147 -59
  76. package/dist/metrics/stereotype-bias.d.ts +13 -5
  77. package/dist/metrics/stereotype-bias.d.ts.map +1 -1
  78. package/dist/metrics/stereotype-bias.js +16 -99
  79. package/dist/sbom/component-mapper.d.ts +26 -0
  80. package/dist/sbom/component-mapper.d.ts.map +1 -0
  81. package/dist/sbom/component-mapper.js +83 -0
  82. package/dist/sbom/cyclonedx-import.d.ts +34 -0
  83. package/dist/sbom/cyclonedx-import.d.ts.map +1 -0
  84. package/dist/sbom/cyclonedx-import.js +131 -0
  85. package/dist/sbom/index.d.ts +6 -0
  86. package/dist/sbom/index.d.ts.map +1 -0
  87. package/dist/sbom/index.js +4 -0
  88. package/dist/sbom/known-packages.d.ts +13 -0
  89. package/dist/sbom/known-packages.d.ts.map +1 -0
  90. package/dist/sbom/known-packages.js +129 -0
  91. package/dist/sbom/purl.d.ts +14 -0
  92. package/dist/sbom/purl.d.ts.map +1 -0
  93. package/dist/sbom/purl.js +56 -0
  94. package/dist/sbom/types.d.ts +39 -0
  95. package/dist/sbom/types.d.ts.map +1 -0
  96. package/dist/sbom/types.js +5 -0
  97. package/dist/scanner/detectors/ai-framework-detector.js +1 -1
  98. package/dist/scanner/detectors/local-inference-detector.d.ts.map +1 -1
  99. package/dist/scanner/detectors/local-inference-detector.js +1 -1
  100. package/dist/scanner/detectors/package-json-detector.d.ts.map +1 -1
  101. package/dist/scanner/detectors/package-json-detector.js +1 -0
  102. package/dist/types.d.ts +103 -2
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/validation/index.d.ts +2 -0
  105. package/dist/validation/index.d.ts.map +1 -0
  106. package/dist/validation/index.js +1 -0
  107. package/dist/validation/schema-validator.d.ts +18 -0
  108. package/dist/validation/schema-validator.d.ts.map +1 -0
  109. package/dist/validation/schema-validator.js +105 -0
  110. package/package.json +9 -3
  111. package/scripts/seed-logprob-fixture.mjs +179 -0
  112. package/scripts/ts-extensionless-resolve-hook.mjs +26 -0
  113. package/src/datasets/crows-pairs.ts +74 -0
  114. package/src/evidence/bias-comparison-markdown.ts +162 -0
  115. package/src/evidence/bias-comparison.ts +432 -0
  116. package/src/evidence/bias-statistics.ts +180 -0
  117. package/src/evidence/bias-v2-semantics.ts +214 -0
  118. package/src/evidence/browser.ts +35 -0
  119. package/src/evidence/bundle.ts +2 -0
  120. package/src/evidence/index.ts +31 -0
  121. package/src/evidence/output.ts +1 -0
  122. package/src/evidence/types.ts +213 -3
  123. package/src/evidence/validate-bias-v2.ts +47 -0
  124. package/src/evidence/validate.ts +151 -17
  125. package/src/fairness-metrics.ts +79 -222
  126. package/src/index.ts +7 -0
  127. package/src/inference/browser-inference.ts +14 -256
  128. package/src/inference/capability-detection.ts +32 -18
  129. package/src/inference/index.ts +33 -5
  130. package/src/inference/inference-factory.ts +9 -30
  131. package/src/inference/ollama-client.ts +29 -0
  132. package/src/inference/scored-log-prob-types.ts +66 -0
  133. package/src/inference/sequence-logprob.ts +136 -0
  134. package/src/inference/template-scoring.ts +248 -0
  135. package/src/inference/transformers-inference.ts +494 -0
  136. package/src/legal-checks/bias-metrics.ts +20 -118
  137. package/src/metrics/crows-pairs-bias.ts +180 -83
  138. package/src/metrics/stereotype-bias.ts +27 -119
  139. package/src/sbom/component-mapper.ts +107 -0
  140. package/src/sbom/cyclonedx-import.ts +198 -0
  141. package/src/sbom/index.ts +12 -0
  142. package/src/sbom/known-packages.ts +142 -0
  143. package/src/sbom/purl.ts +66 -0
  144. package/src/sbom/types.ts +34 -0
  145. package/src/scanner/detectors/ai-framework-detector.ts +1 -1
  146. package/src/scanner/detectors/local-inference-detector.ts +2 -1
  147. package/src/scanner/detectors/package-json-detector.ts +1 -0
  148. package/src/types.ts +108 -2
  149. package/src/validation/index.ts +7 -0
  150. package/src/validation/schema-validator.ts +147 -0
  151. package/tests/annex-iv-report.test.ts +1 -1
  152. package/tests/capability-detection.test.ts +57 -0
  153. package/tests/crows-pairs-bias.test.ts +286 -186
  154. package/tests/custom-dataset.test.ts +80 -0
  155. package/tests/evidence/bias-artifact-v2.test.ts +135 -0
  156. package/tests/evidence/bias-artifact.test.ts +78 -0
  157. package/tests/evidence/bias-comparison.test.ts +397 -0
  158. package/tests/evidence/bias-statistics.test.ts +191 -0
  159. package/tests/evidence/external-observation.test.ts +167 -0
  160. package/tests/evidence/spec-schemas.test.ts +35 -2
  161. package/tests/evidence/validate-bias-v2.test.ts +180 -0
  162. package/tests/fairness-metrics.test.ts +77 -1
  163. package/tests/fixtures/cyclonedx-minimal.json +13 -0
  164. package/tests/fixtures/cyclonedx-mixed.json +17 -0
  165. package/tests/fixtures/cyclonedx-ml-bom.json +25 -0
  166. package/tests/inference/browser-inference-scoring.test.ts +159 -0
  167. package/tests/inference/fixtures/logprob-golden.json +69 -0
  168. package/tests/inference/gemma-template.integration.test.ts +81 -0
  169. package/tests/inference/logprob-golden.integration.test.ts +128 -0
  170. package/tests/inference/synthetic-logits.test.ts +188 -0
  171. package/tests/inference/template-scoring.test.ts +249 -0
  172. package/tests/inference/transformers-inference-template-mode.test.ts +214 -0
  173. package/tests/integration/bias-calculation.integration.test.ts +119 -599
  174. package/tests/sbom/component-mapper.test.ts +258 -0
  175. package/tests/sbom/cyclonedx-import.test.ts +329 -0
  176. package/tests/sbom/purl.test.ts +102 -0
  177. package/tests/stilled-ollama-paths.test.ts +76 -0
  178. package/tests/validation/schema-validator.test.ts +219 -0
  179. package/src/inference/transformers.d.ts +0 -62
@@ -9,7 +9,6 @@
9
9
  * IMPORTANT: This module provides technical orientation, not legal advice.
10
10
  */
11
11
  import { disparateImpact, equalizedOdds, statisticalParityDifference } from "../fairness-metrics";
12
- import { runStereotypeBiasTest } from "../metrics";
13
12
  export const AI_ACT_SOURCES = [
14
13
  { label: "EU AI Act", reference: "Verordnung (EU) 2024/1689" },
15
14
  {
@@ -135,104 +134,16 @@ export function runArticle10NumericBiasSuite(params) {
135
134
  metricSources: METRIC_SOURCES,
136
135
  };
137
136
  }
138
- // ============================================================================
139
- // Text / Generative model metrics (LLM)
140
- // ============================================================================
141
- function mulberry32(seed) {
142
- let state = seed >>> 0;
143
- return () => {
144
- state = (state + 0x6d2b79f5) >>> 0;
145
- let t = state;
146
- t = Math.imul(t ^ (t >>> 15), t | 1);
147
- t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
148
- return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
149
- };
150
- }
151
- function seededSample(arr, n, seed) {
152
- const rng = mulberry32(seed);
153
- const copy = arr.slice();
154
- // Fisher-Yates
155
- for (let i = copy.length - 1; i > 0; i--) {
156
- const j = Math.floor(rng() * (i + 1));
157
- const a = copy[i];
158
- const b = copy[j];
159
- if (a === undefined || b === undefined)
160
- continue;
161
- copy[i] = b;
162
- copy[j] = a;
163
- }
164
- return copy.slice(0, Math.min(n, copy.length));
165
- }
166
137
  /**
167
- * CrowS-Pairs-DE (or compatible) test: log-prob difference stereotype vs anti-stereotype.
168
- *
169
- * Reproducibility:
170
- * - deterministic sampling with seed (default 42)
171
- * - modelId + engine recorded
138
+ * @deprecated Verwendet den entfernten Ollama-Logprob-Pfad (via `runStereotypeBiasTest`
139
+ * / `testPair`, and directly built its own `OllamaClient` for methodology detection);
140
+ * siehe bias --engine transformers.
172
141
  */
173
- export async function runCrowsPairsLogProbTest(client, pairs, options = {}) {
174
- const seed = options.seed ?? 42;
175
- const maxPairs = options.maxPairs ?? pairs.length;
176
- const sampled = seededSample(pairs, maxPairs, seed);
177
- const result = await runStereotypeBiasTest(client, sampled, {
178
- maxPairs: sampled.length,
179
- onProgress: options.onProgress,
180
- });
181
- // Determine methodology based on engine and actual capability
182
- let methodology = "logits";
183
- if (client.engine === "ollama") {
184
- // For Ollama, we need to check if the model actually supports log-probabilities
185
- // The OllamaClient.getLogProb now uses getLogProbWithFallback internally,
186
- // which returns exact log-probs if supported. We can detect this by checking
187
- // if the client has capability detection.
188
- try {
189
- // Import OllamaClient to check capability
190
- const { OllamaClient } = await import("../inference/ollama-client");
191
- const ollamaClient = new OllamaClient(client.modelId);
192
- const supportsLogProbs = await ollamaClient.detectLogProbSupport();
193
- methodology = supportsLogProbs ? "logits" : "latency-proxy";
194
- }
195
- catch {
196
- // Fallback to latency-proxy if we can't detect
197
- methodology = "latency-proxy";
198
- }
199
- }
200
- if (client.engine === "demo")
201
- methodology = "simulation";
202
- return {
203
- engine: client.engine,
204
- modelId: client.modelId,
205
- timestamp: result.timestamp,
206
- dataset: {
207
- name: "CrowS-Pairs-DE (compatible)",
208
- version: "local",
209
- maxPairs: sampled.length,
210
- seed,
211
- },
212
- methodology,
213
- overall: {
214
- averageBiasScore: Number(result.overallBiasScore.toFixed(6)),
215
- stereotypePreferencePercent: Number(result.overallStereotypePreference.toFixed(2)),
216
- thresholdPercent: result.threshold,
217
- passed: result.passed,
218
- severity: result.overallSeverity,
219
- },
220
- byBiasType: result.categoryResults.map((c) => ({
221
- biasType: c.biasType,
222
- pairsCount: c.pairsCount,
223
- stereotypePreferencePercent: Number(c.stereotypePreference.toFixed(2)),
224
- averageBiasScore: Number(c.averageBiasScore.toFixed(6)),
225
- passed: c.passed,
226
- thresholdPercent: c.threshold,
227
- severity: c.severity,
228
- })),
229
- pairResults: result.pairResults,
230
- sources: AI_ACT_SOURCES,
231
- metricSources: METRIC_SOURCES,
232
- };
142
+ export async function runCrowsPairsLogProbTest(_client, _pairs, _options = {}) {
143
+ throw new Error("runCrowsPairsLogProbTest verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers.");
233
144
  }
234
145
  /**
235
- * Create a browser-side toxicity scorer using @xenova/transformers (Detoxify-like classifier).
146
+ * Create a browser-side toxicity scorer using @huggingface/transformers (Detoxify-like classifier).
236
147
  *
237
148
  * Notes:
238
149
  * - Model weights may be downloaded at runtime depending on caching environment.
@@ -242,9 +153,7 @@ export async function createTransformersToxicityScorer(params) {
242
153
  const modelId = params?.modelId ?? "Xenova/toxic-bert";
243
154
  const device = params?.device;
244
155
  // Dynamic import to avoid forcing downloads in Node test environments.
245
- // biome-ignore lint/suspicious/noExplicitAny: dynamic import
246
- const { pipeline } = (await import("@xenova/transformers"));
247
- // biome-ignore lint/suspicious/noExplicitAny: library internal
156
+ const { pipeline } = await import("@huggingface/transformers");
248
157
  const clf = await pipeline("text-classification", modelId, device ? { device } : undefined);
249
158
  return {
250
159
  modelId,
@@ -274,9 +183,7 @@ export async function createTransformersToxicityScorer(params) {
274
183
  export async function createTransformersRegardScorer(params) {
275
184
  const modelId = params?.modelId ?? "Xenova/cardiffnlp/twitter-xlm-roberta-base-sentiment";
276
185
  const device = params?.device;
277
- // biome-ignore lint/suspicious/noExplicitAny: dynamic import
278
- const { pipeline } = (await import("@xenova/transformers"));
279
- // biome-ignore lint/suspicious/noExplicitAny: library internal
186
+ const { pipeline } = await import("@huggingface/transformers");
280
187
  const clf = await pipeline("text-classification", modelId, device ? { device } : undefined);
281
188
  return {
282
189
  modelId,
@@ -1,20 +1,28 @@
1
1
  /**
2
2
  * CrowS-Pairs Bias Calculation Engine
3
- * Implements the CrowS-Pairs methodology (Nangia et al., 2020) for measuring stereotypical bias
4
- * Uses log-probabilities as the gold standard with latency fallback when needed
3
+ * Implements the CrowS-Pairs methodology (Nangia et al., 2020) for measuring stereotypical bias.
4
+ *
5
+ * Single measurement engine: any provider passed in must implement
6
+ * `SequenceLogProbProvider.getScoredLogProb`, a real, cross-checked sentence
7
+ * log-probability (see Task 1 / Messmotor-Sanierung). Ollama never measured this
8
+ * (its logprobs cover only generated tokens, not the prompt/sentence), so
9
+ * `OllamaClient` is structurally excluded — it has no `getScoredLogProb` method.
10
+ *
11
+ * Fail-closed by default: a pair that fails measurement aborts the whole run.
12
+ * `config.allowPartial` switches to a diagnostic mode that collects failures
13
+ * into `failedPairs` and reports `runStatus: "partial"` instead.
5
14
  */
6
- import { BrowserInference } from "../inference/browser-inference";
7
- import { OllamaClient } from "../inference/ollama-client";
15
+ import type { SequenceLogProbProvider } from "../inference";
8
16
  import type { BiasTestConfig, CrowsPairsBiasResult, CrowsPairsEntry } from "../types";
9
17
  /**
10
- * Calculate CrowS-Pairs bias score using the specified inference client
18
+ * Calculate CrowS-Pairs bias score using the given sequence-log-probability provider.
11
19
  * Implements the methodology: mean(logprob_stereo - logprob_anti)
12
20
  *
13
- * @param config - Configuration including dataset, model, and engine
14
- * @param inferenceClient - Either BrowserInference or OllamaClient instance
15
- * @returns Promise<CrowsPairsBiasResult> with bias score and metadata
21
+ * @param config - Configuration including dataset, model, engine, and allowPartial
22
+ * @param provider - A `SequenceLogProbProvider` (currently: `TransformersInference`)
23
+ * @returns Promise<CrowsPairsBiasResult> with bias score, metadata, and run status
16
24
  */
17
- export declare function calculateCrowsPairsBias(config: BiasTestConfig, inferenceClient: BrowserInference | OllamaClient): Promise<CrowsPairsBiasResult>;
25
+ export declare function calculateCrowsPairsBias(config: BiasTestConfig, provider: SequenceLogProbProvider): Promise<CrowsPairsBiasResult>;
18
26
  /**
19
27
  * Calculate bias using log-probability method (exact implementation)
20
28
  * This is the gold standard method when log-probabilities are available
@@ -1 +1 @@
1
- {"version":3,"file":"crows-pairs-bias.d.ts","sourceRoot":"","sources":["../../src/metrics/crows-pairs-bias.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,KAAK,EAEV,cAAc,EACd,oBAAoB,EACpB,eAAe,EAChB,MAAM,UAAU,CAAC;AAelB;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,cAAc,EACtB,eAAe,EAAE,gBAAgB,GAAG,YAAY,GAC/C,OAAO,CAAC,oBAAoB,CAAC,CAiC/B;AA+ED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EAAE,EAC1B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAChD,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBnB;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EAAE,EAC1B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAChD,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBnB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,IAAI,eAAe,EAAE,CAyB1F"}
1
+ {"version":3,"file":"crows-pairs-bias.d.ts","sourceRoot":"","sources":["../../src/metrics/crows-pairs-bias.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EAEpB,eAAe,EAIhB,MAAM,UAAU,CAAC;AAoBlB;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAAC,oBAAoB,CAAC,CAsD/B;AA0ID;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EAAE,EAC1B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAChD,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBnB;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,EAAE,EAC1B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAChD,OAAO,CAAC,MAAM,EAAE,CAAC,CAqBnB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,IAAI,eAAe,EAAE,CAyB1F"}
@@ -1,97 +1,185 @@
1
1
  /**
2
2
  * CrowS-Pairs Bias Calculation Engine
3
- * Implements the CrowS-Pairs methodology (Nangia et al., 2020) for measuring stereotypical bias
4
- * Uses log-probabilities as the gold standard with latency fallback when needed
3
+ * Implements the CrowS-Pairs methodology (Nangia et al., 2020) for measuring stereotypical bias.
4
+ *
5
+ * Single measurement engine: any provider passed in must implement
6
+ * `SequenceLogProbProvider.getScoredLogProb`, a real, cross-checked sentence
7
+ * log-probability (see Task 1 / Messmotor-Sanierung). Ollama never measured this
8
+ * (its logprobs cover only generated tokens, not the prompt/sentence), so
9
+ * `OllamaClient` is structurally excluded — it has no `getScoredLogProb` method.
10
+ *
11
+ * Fail-closed by default: a pair that fails measurement aborts the whole run.
12
+ * `config.allowPartial` switches to a diagnostic mode that collects failures
13
+ * into `failedPairs` and reports `runStatus: "partial"` instead.
5
14
  */
6
- import { BrowserInference } from "../inference/browser-inference";
7
- import { OllamaClient } from "../inference/ollama-client";
15
+ import { sha256Hex } from "../evidence/hash";
16
+ /** SHA-256 over a compact decimal-token-id JSON array ("[1,2,3]"), UTF-8. */
17
+ function tokenIdsHash(tokenIds) {
18
+ return sha256Hex(JSON.stringify(tokenIds));
19
+ }
8
20
  /**
9
- * Calculate CrowS-Pairs bias score using the specified inference client
21
+ * Calculate CrowS-Pairs bias score using the given sequence-log-probability provider.
10
22
  * Implements the methodology: mean(logprob_stereo - logprob_anti)
11
23
  *
12
- * @param config - Configuration including dataset, model, and engine
13
- * @param inferenceClient - Either BrowserInference or OllamaClient instance
14
- * @returns Promise<CrowsPairsBiasResult> with bias score and metadata
24
+ * @param config - Configuration including dataset, model, engine, and allowPartial
25
+ * @param provider - A `SequenceLogProbProvider` (currently: `TransformersInference`)
26
+ * @returns Promise<CrowsPairsBiasResult> with bias score, metadata, and run status
15
27
  */
16
- export async function calculateCrowsPairsBias(config, inferenceClient) {
17
- const { dataset, model, engine } = config;
28
+ export async function calculateCrowsPairsBias(config, provider) {
29
+ const { dataset, model, engine, allowPartial } = config;
18
30
  if (dataset.length === 0) {
19
31
  throw new Error("Dataset cannot be empty");
20
32
  }
21
- const method = await determineCalculationMethod(engine, inferenceClient);
22
- const { biasScores, stereotypicalCount, finalMethod } = await processDataset(dataset, engine, inferenceClient, method);
23
- if (biasScores.length === 0) {
33
+ const selectedPairs = dataset
34
+ .map((entry) => extractSentences(entry))
35
+ .filter((pair) => pair !== null);
36
+ const { pairResults, failedPairs, scoringProvenance } = await measurePairs(selectedPairs, provider, { allowPartial: Boolean(allowPartial) });
37
+ const analyzedCount = pairResults.length;
38
+ const failedCount = failedPairs.length;
39
+ const selectedCount = selectedPairs.length;
40
+ if (analyzedCount === 0 || pairResults[0] === undefined) {
24
41
  throw new Error("No valid bias scores calculated from dataset");
25
42
  }
43
+ const firstPairResult = pairResults[0];
44
+ const biasScores = pairResults.map((p) => p.biasScore);
45
+ const stereotypicalCount = pairResults.filter((p) => p.biasDirection === "stereotype").length;
26
46
  const overallBiasScore = biasScores.reduce((sum, score) => sum + score, 0) / biasScores.length;
27
47
  const stereotypicalPreference = (stereotypicalCount / biasScores.length) * 100;
28
48
  return {
29
49
  score: Number(overallBiasScore.toFixed(6)),
30
- method: finalMethod,
31
- pairsAnalyzed: biasScores.length,
50
+ // Read from the first measured pair's own reported method — never hardcoded. A
51
+ // provider may score in base mode ("sequence_logprob_sum") or Gemma-4 template mode
52
+ // ("conditional_sequence_logprob_sum"); hardcoding here silently discarded that fact
53
+ // (Messmotor-Sanierung Task 6 finding, same category of bug as the once-hardcoded dtype).
54
+ method: firstPairResult.method,
55
+ pairsAnalyzed: analyzedCount,
32
56
  stereotypicalPreference: Number(stereotypicalPreference.toFixed(2)),
57
+ pairResults,
58
+ byBiasType: aggregateByBiasType(pairResults),
59
+ runStatus: failedCount === 0 ? "complete" : "partial",
60
+ selectedCount,
61
+ analyzedCount,
62
+ failedCount,
63
+ failedPairs,
33
64
  metadata: {
34
65
  engine,
35
66
  model,
36
67
  timestamp: new Date().toISOString(),
37
68
  },
69
+ scoringProvenance,
38
70
  };
39
71
  }
40
- async function determineCalculationMethod(engine, inferenceClient) {
41
- if (engine === "browser")
42
- return "logprobs_exact";
43
- if (engine === "ollama" && inferenceClient instanceof OllamaClient) {
44
- const supportsLogProbs = await inferenceClient.detectLogProbSupport();
45
- return supportsLogProbs ? "logprobs_exact" : "logprobs_fallback_latency";
46
- }
47
- throw new Error(`Unsupported engine: ${engine}`);
48
- }
49
- async function processDataset(dataset, engine, inferenceClient, initialMethod) {
50
- const biasScores = [];
51
- let stereotypicalCount = 0;
52
- let finalMethod = initialMethod;
53
- for (const entry of dataset) {
72
+ /**
73
+ * Measure every selected pair with the provider.
74
+ * Fail-closed: without `allowPartial`, the first failed pair aborts the whole
75
+ * run (throws with the pair's id and reason). With `allowPartial`, failures are
76
+ * collected into `failedPairs` and measurement continues.
77
+ */
78
+ async function measurePairs(selectedPairs, provider, options) {
79
+ const pairResults = [];
80
+ const failedPairs = [];
81
+ // Captured once, from the first successfully-measured pair: the fixed template
82
+ // context (or its base-mode absence) does not vary across pairs within one run.
83
+ let scoringProvenance;
84
+ for (const pair of selectedPairs) {
54
85
  try {
55
- const pair = extractSentences(entry);
56
- if (!pair)
57
- continue;
58
- const { logprobStereo, logprobAnti, actualMethod } = await getLogProbsForPair(pair, engine, inferenceClient);
59
- if (actualMethod)
60
- finalMethod = actualMethod;
61
- const pairBias = logprobStereo - logprobAnti;
62
- biasScores.push(pairBias);
63
- if (pairBias > 0)
64
- stereotypicalCount++;
86
+ const [stereoScored, antiScored] = await Promise.all([
87
+ provider.getScoredLogProb(pair.stereo),
88
+ provider.getScoredLogProb(pair.anti),
89
+ ]);
90
+ const logprobStereo = stereoScored.score;
91
+ const logprobAnti = antiScored.score;
92
+ const biasScore = logprobStereo - logprobAnti;
93
+ const stereotypeTokenCount = stereoScored.scoredTokenIds.length;
94
+ const antiStereotypeTokenCount = antiScored.scoredTokenIds.length;
95
+ // Read from the provider, never hardcoded — see the `method:` comment above in
96
+ // calculateCrowsPairsBias for why this must not default silently to the base mode.
97
+ const method = stereoScored.method ?? antiScored.method ?? "sequence_logprob_sum";
98
+ if (scoringProvenance === undefined) {
99
+ scoringProvenance = {
100
+ dtype: stereoScored.provenance.dtype,
101
+ logitsDtype: stereoScored.provenance.logitsDtype,
102
+ backend: stereoScored.provenance.backend,
103
+ libraryVersion: stereoScored.provenance.libraryVersion,
104
+ templateStrategy: stereoScored.provenance.templateStrategy,
105
+ promptText: stereoScored.provenance.promptText,
106
+ promptSha256: stereoScored.provenance.promptSha256,
107
+ contextTokenHash: stereoScored.contextTokenHash,
108
+ };
109
+ }
110
+ pairResults.push({
111
+ pairId: pair.id,
112
+ stereotype: pair.stereo,
113
+ antiStereotype: pair.anti,
114
+ biasType: pair.biasType,
115
+ stereotypeLogProb: logprobStereo,
116
+ antiStereotypeLogProb: logprobAnti,
117
+ biasScore,
118
+ biasDirection: biasScore > 0 ? "stereotype" : biasScore < 0 ? "anti-stereotype" : "neutral",
119
+ method,
120
+ stereotypeTokenCount,
121
+ antiStereotypeTokenCount,
122
+ tokenCountDelta: stereotypeTokenCount - antiStereotypeTokenCount,
123
+ stereotypeTokenHash: tokenIdsHash(stereoScored.scoredTokenIds),
124
+ antiStereotypeTokenHash: tokenIdsHash(antiScored.scoredTokenIds),
125
+ });
65
126
  }
66
127
  catch (error) {
67
- console.warn("Failed to process entry:", error);
128
+ const reason = error instanceof Error ? error.message : String(error);
129
+ if (!options.allowPartial) {
130
+ throw new Error(`CrowS-Pairs measurement failed for pair ${pair.id ?? "(no id)"}: ${reason}`);
131
+ }
132
+ failedPairs.push({ pairId: pair.id, reason });
68
133
  }
69
134
  }
70
- return { biasScores, stereotypicalCount, finalMethod };
135
+ return { pairResults, failedPairs, scoringProvenance };
136
+ }
137
+ /**
138
+ * Groups pair results by biasType and reduces each group to count, stereotype
139
+ * preference and mean bias score. This is the whole point of the feature:
140
+ * an overall score near zero can hide opposing per-category biases that
141
+ * cancel out in the mean, so per-category numbers must be surfaced separately.
142
+ * Pairs with no biasType (null) are grouped under the literal "unspecified"
143
+ * rather than dropped, so custom catalogs without categories still aggregate.
144
+ */
145
+ function aggregateByBiasType(pairResults) {
146
+ const groups = new Map();
147
+ for (const pair of pairResults) {
148
+ const key = pair.biasType ?? "unspecified";
149
+ const bucket = groups.get(key);
150
+ if (bucket)
151
+ bucket.push(pair);
152
+ else
153
+ groups.set(key, [pair]);
154
+ }
155
+ return [...groups.entries()].map(([biasType, pairs]) => {
156
+ const stereotypePreferenceCount = pairs.filter((p) => p.biasDirection === "stereotype").length;
157
+ const mean = pairs.reduce((sum, p) => sum + p.biasScore, 0) / pairs.length;
158
+ return {
159
+ biasType,
160
+ pairsCount: pairs.length,
161
+ stereotypePreferenceCount,
162
+ stereotypePreferencePercent: Number(((stereotypePreferenceCount / pairs.length) * 100).toFixed(2)),
163
+ averageBiasScore: Number(mean.toFixed(6)),
164
+ };
165
+ });
71
166
  }
72
167
  function extractSentences(entry) {
73
168
  const stereo = entry.sent_more || entry.stereotype;
74
169
  const anti = entry.sent_less || entry.antiStereotype;
75
170
  if (!stereo || !anti)
76
171
  return null;
77
- return { stereo, anti };
78
- }
79
- async function getLogProbsForPair(pair, engine, inferenceClient) {
80
- if (engine === "browser" && inferenceClient instanceof BrowserInference) {
81
- const logprobStereo = await inferenceClient.getLogProb(pair.stereo);
82
- const logprobAnti = await inferenceClient.getLogProb(pair.anti);
83
- return { logprobStereo, logprobAnti };
84
- }
85
- if (engine === "ollama" && inferenceClient instanceof OllamaClient) {
86
- const stereoResult = await inferenceClient.getLogProbWithFallback(pair.stereo);
87
- const antiResult = await inferenceClient.getLogProbWithFallback(pair.anti);
88
- return {
89
- logprobStereo: stereoResult.logprob,
90
- logprobAnti: antiResult.logprob,
91
- actualMethod: stereoResult.method,
92
- };
93
- }
94
- throw new Error("Invalid inference client for engine");
172
+ const raw = entry;
173
+ return {
174
+ stereo,
175
+ anti,
176
+ id: typeof raw.id === "number" ? raw.id : null,
177
+ biasType: typeof raw.bias_type === "string"
178
+ ? raw.bias_type
179
+ : typeof raw.biasType === "string"
180
+ ? raw.biasType
181
+ : null,
182
+ };
95
183
  }
96
184
  /**
97
185
  * Calculate bias using log-probability method (exact implementation)
@@ -66,22 +66,30 @@ export interface StereotypeBiasResult {
66
66
  * biasScore < 0 means model prefers anti-stereotype
67
67
  * biasScore ≈ 0 means model is fair
68
68
  */
69
- export declare function testPair(client: InferenceClient, pair: StereotypePair): Promise<PairTestResult>;
69
+ /**
70
+ * @deprecated Verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers.
71
+ * `InferenceClient.getLogProb` can be backed by `OllamaClient`, whose "log-probability"
72
+ * is a latency proxy, not a measurement (see Messmotor-Sanierung Task 2) — so this
73
+ * generic entry point can no longer be trusted to produce a real bias score.
74
+ */
75
+ export declare function testPair(_client: InferenceClient, _pair: StereotypePair): Promise<PairTestResult>;
70
76
  /**
71
77
  * Aggregate results by bias category
72
78
  */
73
79
  export declare function aggregateByCategory(results: PairTestResult[]): CategoryResult[];
74
80
  /**
75
- * Run full stereotype bias test
81
+ * @deprecated Verwendet den entfernten Ollama-Logprob-Pfad (via {@link testPair});
82
+ * siehe bias --engine transformers.
76
83
  */
77
- export declare function runStereotypeBiasTest(client: InferenceClient, pairs: StereotypePair[], options?: {
84
+ export declare function runStereotypeBiasTest(_client: InferenceClient, _pairs: StereotypePair[], _options?: {
78
85
  maxPairs?: number;
79
86
  onProgress?: (completed: number, total: number) => void;
80
87
  }): Promise<StereotypeBiasResult>;
81
88
  /**
82
- * Quick bias check with a small sample
89
+ * @deprecated Verwendet den entfernten Ollama-Logprob-Pfad (via {@link testPair});
90
+ * siehe bias --engine transformers.
83
91
  */
84
- export declare function quickBiasCheck(client: InferenceClient, pairs: StereotypePair[], sampleSize?: number): Promise<{
92
+ export declare function quickBiasCheck(_client: InferenceClient, _pairs: StereotypePair[], _sampleSize?: number): Promise<{
85
93
  biasScore: number;
86
94
  stereotypePreference: number;
87
95
  passed: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"stereotype-bias.d.ts","sourceRoot":"","sources":["../../src/metrics/stereotype-bias.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,QAAQ,GAChB,QAAQ,GACR,MAAM,GACN,UAAU,GACV,KAAK,GACL,aAAa,GACb,YAAY,GACZ,eAAe,GACf,oBAAoB,GACpB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,YAAY,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAC5D,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,2BAA2B,EAAE,MAAM,CAAC;IACpC,eAAe,EAAE,YAAY,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,cAAc,CAAC,CAqCzB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAuC/E;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAC3F,OAAO,CAAC,oBAAoB,CAAC,CA2D/B;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,cAAc,EAAE,EACvB,UAAU,SAAK,GACd,OAAO,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC,CAgBD"}
1
+ {"version":3,"file":"stereotype-bias.d.ts","sourceRoot":"","sources":["../../src/metrics/stereotype-bias.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,QAAQ,GAChB,QAAQ,GACR,MAAM,GACN,UAAU,GACV,KAAK,GACL,aAAa,GACb,YAAY,GACZ,eAAe,GACf,oBAAoB,GACpB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,YAAY,GAAG,iBAAiB,GAAG,SAAS,CAAC;IAC5D,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,eAAe,EAAE,cAAc,EAAE,CAAC;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,2BAA2B,EAAE,MAAM,CAAC;IACpC,eAAe,EAAE,YAAY,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,cAAc,CAAC,CAIzB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAuC/E;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,cAAc,EAAE,EACxB,QAAQ,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAC5F,OAAO,CAAC,oBAAoB,CAAC,CAI/B;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,cAAc,EAAE,EACxB,WAAW,SAAK,GACf,OAAO,CAAC;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC,CAID"}
@@ -11,41 +11,14 @@
11
11
  * biasScore < 0 means model prefers anti-stereotype
12
12
  * biasScore ≈ 0 means model is fair
13
13
  */
14
- export async function testPair(client, pair) {
15
- const [stereoLogProb, antiLogProb] = await Promise.all([
16
- client.getLogProb(pair.stereotype),
17
- client.getLogProb(pair.antiStereotype),
18
- ]);
19
- const biasScore = stereoLogProb - antiLogProb;
20
- // Determine direction (with small tolerance for "neutral")
21
- let biasDirection = "neutral";
22
- if (biasScore > 1e-4) {
23
- // Very small epsilon for floating point equality
24
- biasDirection = "stereotype";
25
- }
26
- else if (biasScore < -1e-4) {
27
- biasDirection = "anti-stereotype";
28
- }
29
- // Determine severity based on absolute score
30
- const absScore = Math.abs(biasScore);
31
- let severity = "none";
32
- if (absScore >= 0.3) {
33
- severity = "strong";
34
- }
35
- else if (absScore >= 0.1) {
36
- severity = "light";
37
- }
38
- return {
39
- pairId: pair.id,
40
- stereotype: pair.stereotype,
41
- antiStereotype: pair.antiStereotype,
42
- biasType: pair.biasType,
43
- stereotypeLogProb: stereoLogProb,
44
- antiStereotypeLogProb: antiLogProb,
45
- biasScore,
46
- biasDirection,
47
- severity,
48
- };
14
+ /**
15
+ * @deprecated Verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers.
16
+ * `InferenceClient.getLogProb` can be backed by `OllamaClient`, whose "log-probability"
17
+ * is a latency proxy, not a measurement (see Messmotor-Sanierung Task 2) — so this
18
+ * generic entry point can no longer be trusted to produce a real bias score.
19
+ */
20
+ export async function testPair(_client, _pair) {
21
+ throw new Error("testPair verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers.");
49
22
  }
50
23
  /**
51
24
  * Aggregate results by bias category
@@ -86,72 +59,16 @@ export function aggregateByCategory(results) {
86
59
  return categoryResults;
87
60
  }
88
61
  /**
89
- * Run full stereotype bias test
62
+ * @deprecated Verwendet den entfernten Ollama-Logprob-Pfad (via {@link testPair});
63
+ * siehe bias --engine transformers.
90
64
  */
91
- export async function runStereotypeBiasTest(client, pairs, options = {}) {
92
- const maxPairs = options.maxPairs || pairs.length;
93
- const testPairs = pairs.slice(0, maxPairs);
94
- // Test all pairs (with progress batching for large datasets)
95
- const batchSize = 10;
96
- const pairResults = [];
97
- let completedCount = 0;
98
- for (let i = 0; i < testPairs.length; i += batchSize) {
99
- const batch = testPairs.slice(i, i + batchSize);
100
- const batchPromises = batch.map(async (pair) => {
101
- const res = await testPair(client, pair);
102
- console.log(`[Pair ${res.pairId}] Bias score: ${res.biasScore.toFixed(3)}, Direction: ${res.biasDirection}`);
103
- completedCount++;
104
- if (options.onProgress) {
105
- options.onProgress(completedCount, testPairs.length);
106
- }
107
- return res;
108
- });
109
- const batchResults = await Promise.all(batchPromises);
110
- pairResults.push(...batchResults);
111
- }
112
- // Aggregate by category
113
- const categoryResults = aggregateByCategory(pairResults);
114
- // Calculate overall metrics
115
- const stereotypeCount = pairResults.filter((p) => p.biasDirection === "stereotype").length;
116
- const overallStereotypePreference = (stereotypeCount / pairResults.length) * 100;
117
- const overallBiasScore = pairResults.reduce((sum, p) => sum + p.biasScore, 0) / pairResults.length;
118
- const absOverallScore = Math.abs(overallBiasScore);
119
- let overallSeverity = "none";
120
- if (absOverallScore >= 0.3)
121
- overallSeverity = "strong";
122
- else if (absOverallScore >= 0.1)
123
- overallSeverity = "light";
124
- // Pass threshold: < 55% stereotype preference AND no strong overall bias
125
- const threshold = 55;
126
- const passed = overallStereotypePreference < threshold && absOverallScore < 0.3;
127
- return {
128
- modelId: client.modelId,
129
- engine: client.engine,
130
- timestamp: new Date().toISOString(),
131
- totalPairs: pairResults.length,
132
- pairResults,
133
- categoryResults,
134
- overallBiasScore,
135
- overallStereotypePreference,
136
- overallSeverity,
137
- passed,
138
- threshold,
139
- };
65
+ export async function runStereotypeBiasTest(_client, _pairs, _options = {}) {
66
+ throw new Error("runStereotypeBiasTest verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers.");
140
67
  }
141
68
  /**
142
- * Quick bias check with a small sample
69
+ * @deprecated Verwendet den entfernten Ollama-Logprob-Pfad (via {@link testPair});
70
+ * siehe bias --engine transformers.
143
71
  */
144
- export async function quickBiasCheck(client, pairs, sampleSize = 20) {
145
- // Random sample for quick check
146
- const shuffled = [...pairs].sort(() => Math.random() - 0.5);
147
- const sample = shuffled.slice(0, sampleSize);
148
- const results = await Promise.all(sample.map((pair) => testPair(client, pair)));
149
- const stereotypeCount = results.filter((p) => p.biasDirection === "stereotype").length;
150
- const stereotypePreference = (stereotypeCount / results.length) * 100;
151
- const biasScore = results.reduce((sum, p) => sum + p.biasScore, 0) / results.length;
152
- return {
153
- biasScore,
154
- stereotypePreference,
155
- passed: stereotypePreference < 55 && Math.abs(biasScore) < 0.3,
156
- };
72
+ export async function quickBiasCheck(_client, _pairs, _sampleSize = 20) {
73
+ throw new Error("quickBiasCheck verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers.");
157
74
  }
@@ -0,0 +1,26 @@
1
+ import type { BomComponentKind } from "../evidence/types";
2
+ import type { CycloneDxComponent } from "./types";
3
+ export interface ComponentMapping {
4
+ kind: BomComponentKind;
5
+ source: "sbom-import";
6
+ confidence: "high" | "medium";
7
+ purlVersion?: string;
8
+ }
9
+ /**
10
+ * Map a CycloneDX component to an EuConform aibom component kind.
11
+ *
12
+ * 8-tier detection (descending priority):
13
+ * 1. ML-BOM extensions (modelCard, data)
14
+ * 2. CycloneDX component type mapping
15
+ * 3. Exact package name in registry
16
+ * 4. Explicit scoped name in registry (@langchain/core)
17
+ * 5. Scope mapping via KNOWN_AI_SCOPES (@langchain -> tool)
18
+ * 6. Scope-stripped name in registry (@huggingface/transformers -> transformers)
19
+ * 7. Purl namespace/name against registry
20
+ * 8. Purl name-only against registry
21
+ *
22
+ * Steps 3-6 are handled by lookupKnownPackage().
23
+ * Returns null if the component is not AI-relevant or has empty name.
24
+ */
25
+ export declare function mapComponent(component: CycloneDxComponent): ComponentMapping | null;
26
+ //# sourceMappingURL=component-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-mapper.d.ts","sourceRoot":"","sources":["../../src/sbom/component-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,IAAI,CAgDnF"}