@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
@@ -3,9 +3,8 @@
3
3
  * Based on EU AI Act requirements for non-discrimination
4
4
  */
5
5
 
6
- import type { BrowserInference } from "./inference/browser-inference";
7
- import { InferenceFactory, type InferenceResult } from "./inference/inference-factory";
8
- import type { OllamaClient } from "./inference/ollama-client";
6
+ import type { SequenceLogProbProvider } from "./inference";
7
+ import type { InferenceResult } from "./inference/inference-factory";
9
8
  import { calculateCrowsPairsBias } from "./metrics/crows-pairs-bias";
10
9
  import type { BiasTestConfig, BiasTestResult, FairnessMetric, PredictionData } from "./types";
11
10
 
@@ -209,95 +208,45 @@ export function runFairnessAnalysis(
209
208
  */
210
209
  export async function runCrowsPairsBiasAnalysis(
211
210
  config: BiasTestConfig,
212
- inferenceClient: BrowserInference | OllamaClient
211
+ inferenceClient: SequenceLogProbProvider
213
212
  ): Promise<BiasTestResult> {
214
- try {
215
- const crowsPairsResult = await calculateCrowsPairsBias(config, inferenceClient);
213
+ // A measurement failure (including the core's intentional fail-closed abort)
214
+ // must propagate, not be replaced by a fabricated "failed" result — a
215
+ // synthesized `BiasTestResult` is indistinguishable from a real one to any
216
+ // caller that doesn't inspect stack traces, which is exactly the
217
+ // silent-fallback pattern this remediation removes.
218
+ const crowsPairsResult = await calculateCrowsPairsBias(config, inferenceClient);
219
+
220
+ // Determine if bias test passed based on score threshold
221
+ // A score close to 0 indicates less bias, positive scores indicate stereotypical bias
222
+ const biasThreshold = 0.1; // Configurable threshold for acceptable bias
223
+ const overallPassed = Math.abs(crowsPairsResult.score) < biasThreshold;
216
224
 
217
- // Determine if bias test passed based on score threshold
218
- // A score close to 0 indicates less bias, positive scores indicate stereotypical bias
219
- const biasThreshold = 0.1; // Configurable threshold for acceptable bias
220
- const overallPassed = Math.abs(crowsPairsResult.score) < biasThreshold;
221
-
222
- return {
223
- modelId: config.model,
224
- timestamp: new Date().toISOString(),
225
- method: crowsPairsResult.method,
226
- engine: config.engine,
227
- crowsPairsResult,
228
- overallPassed,
229
- samplesAnalyzed: crowsPairsResult.pairsAnalyzed,
230
- };
231
- } catch (error) {
232
- console.error("CrowS-Pairs bias analysis failed:", error);
233
-
234
- // Return failed result with error information
235
- return {
236
- modelId: config.model,
237
- timestamp: new Date().toISOString(),
238
- method: "logprobs_fallback_latency", // Default fallback
239
- engine: config.engine,
240
- overallPassed: false,
241
- samplesAnalyzed: 0,
242
- };
243
- }
225
+ return {
226
+ modelId: config.model,
227
+ timestamp: new Date().toISOString(),
228
+ method: crowsPairsResult.method,
229
+ engine: config.engine,
230
+ crowsPairsResult,
231
+ overallPassed,
232
+ samplesAnalyzed: crowsPairsResult.pairsAnalyzed,
233
+ };
244
234
  }
245
235
 
246
236
  /**
247
- * Run CrowS-Pairs bias analysis using InferenceFactory with automatic method detection
248
- * This version provides method metadata and automatic best method selection
237
+ * @deprecated Uses the removed Ollama logprob path; see bias --engine transformers.
238
+ * `InferenceFactory.createBiasCalculation` auto-selected between browser and
239
+ * Ollama engines, which no longer measures anything honest for Ollama — that
240
+ * auto-selection logic needs a real redesign (Task 3+), not a throw-or-convert
241
+ * fix, so this entry point is stilled rather than converted.
249
242
  */
250
243
  export async function runCrowsPairsBiasAnalysisWithFactory(
251
- config: BiasTestConfig,
252
- statusCallback?: (status: string) => void
244
+ _config: BiasTestConfig,
245
+ _statusCallback?: (status: string) => void
253
246
  ): Promise<InferenceResult<BiasTestResult>> {
254
- try {
255
- const factory = new InferenceFactory(statusCallback);
256
- const result = await factory.createBiasCalculation(config);
257
-
258
- // Determine if bias test passed based on score threshold
259
- const biasThreshold = 0.1; // Configurable threshold for acceptable bias
260
- const overallPassed = Math.abs(result.data.score) < biasThreshold;
261
-
262
- const biasTestResult: BiasTestResult = {
263
- modelId: config.model,
264
- timestamp: new Date().toISOString(),
265
- method: result.method,
266
- engine: result.engine,
267
- crowsPairsResult: result.data,
268
- overallPassed,
269
- samplesAnalyzed: result.data.pairsAnalyzed,
270
- };
271
-
272
- // Clean up factory resources
273
- factory.dispose();
274
-
275
- return {
276
- data: biasTestResult,
277
- method: result.method,
278
- engine: result.engine,
279
- model: result.model,
280
- };
281
- } catch (error) {
282
- console.error("CrowS-Pairs bias analysis with factory failed:", error);
283
-
284
- // Return failed result with error information
285
- const failedResult: BiasTestResult = {
286
- modelId: config.model,
287
- timestamp: new Date().toISOString(),
288
- method: "logprobs_fallback_latency", // Default fallback
289
- engine: config.engine,
290
- overallPassed: false,
291
- samplesAnalyzed: 0,
292
- };
293
-
294
- return {
295
- data: failedResult,
296
- method: "logprobs_fallback_latency",
297
- engine: config.engine,
298
- model: config.model,
299
- };
300
- }
247
+ throw new Error(
248
+ "runCrowsPairsBiasAnalysisWithFactory verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers."
249
+ );
301
250
  }
302
251
 
303
252
  /**
@@ -307,154 +256,62 @@ export async function runCrowsPairsBiasAnalysisWithFactory(
307
256
  export async function runComprehensiveBiasAnalysis(
308
257
  predictionData: PredictionData[],
309
258
  crowsPairsConfig: BiasTestConfig,
310
- inferenceClient: BrowserInference | OllamaClient,
259
+ inferenceClient: SequenceLogProbProvider,
311
260
  protectedAttribute: string
312
261
  ): Promise<BiasTestResult> {
313
- try {
314
- // Run CrowS-Pairs analysis (primary method)
315
- const crowsPairsResult = await calculateCrowsPairsBias(crowsPairsConfig, inferenceClient);
316
-
317
- // Run traditional fairness metrics if prediction data is available
318
- let traditionalMetrics: FairnessMetric[] = [];
319
- if (predictionData && predictionData.length > 0) {
320
- const traditionalResult = runFairnessAnalysis(
321
- predictionData,
322
- crowsPairsConfig.model,
323
- protectedAttribute
324
- );
325
- traditionalMetrics = traditionalResult.metrics || [];
326
- }
327
-
328
- // Determine overall pass/fail based on CrowS-Pairs result and traditional metrics
329
- const biasThreshold = 0.1;
330
- const crowsPairsPassed = Math.abs(crowsPairsResult.score) < biasThreshold;
331
- const traditionalPassed =
332
- traditionalMetrics.length === 0 || traditionalMetrics.every((m) => m.passed);
333
- const overallPassed = crowsPairsPassed && traditionalPassed;
334
-
335
- return {
336
- modelId: crowsPairsConfig.model,
337
- timestamp: new Date().toISOString(),
338
- method: crowsPairsResult.method,
339
- engine: crowsPairsConfig.engine,
340
- protectedAttribute,
341
- crowsPairsResult,
342
- metrics: traditionalMetrics.length > 0 ? traditionalMetrics : undefined,
343
- overallPassed,
344
- samplesAnalyzed: crowsPairsResult.pairsAnalyzed + (predictionData?.length || 0),
345
- };
346
- } catch (error) {
347
- console.error("Comprehensive bias analysis failed:", error);
262
+ // Run CrowS-Pairs analysis (primary method). A failure here — including the
263
+ // core's intentional fail-closed abort — must propagate. It must NOT be
264
+ // caught and replaced by a traditional-metrics-only result or a fabricated
265
+ // "failed" result: either would silently present a degraded or synthesized
266
+ // outcome as if it were the real, complete measurement.
267
+ const crowsPairsResult = await calculateCrowsPairsBias(crowsPairsConfig, inferenceClient);
268
+
269
+ // Run traditional fairness metrics if prediction data is available
270
+ let traditionalMetrics: FairnessMetric[] = [];
271
+ if (predictionData && predictionData.length > 0) {
272
+ const traditionalResult = runFairnessAnalysis(
273
+ predictionData,
274
+ crowsPairsConfig.model,
275
+ protectedAttribute
276
+ );
277
+ traditionalMetrics = traditionalResult.metrics || [];
278
+ }
348
279
 
349
- // Fallback to traditional metrics only if CrowS-Pairs fails
350
- if (predictionData && predictionData.length > 0) {
351
- return runFairnessAnalysis(predictionData, crowsPairsConfig.model, protectedAttribute);
352
- }
280
+ // Determine overall pass/fail based on CrowS-Pairs result and traditional metrics
281
+ const biasThreshold = 0.1;
282
+ const crowsPairsPassed = Math.abs(crowsPairsResult.score) < biasThreshold;
283
+ const traditionalPassed =
284
+ traditionalMetrics.length === 0 || traditionalMetrics.every((m) => m.passed);
285
+ const overallPassed = crowsPairsPassed && traditionalPassed;
353
286
 
354
- // Return failed result if both methods fail
355
- return {
356
- modelId: crowsPairsConfig.model,
357
- timestamp: new Date().toISOString(),
358
- method: "logprobs_fallback_latency",
359
- engine: crowsPairsConfig.engine,
360
- protectedAttribute,
361
- overallPassed: false,
362
- samplesAnalyzed: 0,
363
- };
364
- }
287
+ return {
288
+ modelId: crowsPairsConfig.model,
289
+ timestamp: new Date().toISOString(),
290
+ method: crowsPairsResult.method,
291
+ engine: crowsPairsConfig.engine,
292
+ protectedAttribute,
293
+ crowsPairsResult,
294
+ metrics: traditionalMetrics.length > 0 ? traditionalMetrics : undefined,
295
+ overallPassed,
296
+ samplesAnalyzed: crowsPairsResult.pairsAnalyzed + (predictionData?.length || 0),
297
+ };
365
298
  }
366
299
 
367
300
  /**
368
- * Run comprehensive bias analysis using InferenceFactory with automatic method detection
369
- * Combines prediction-based fairness metrics with CrowS-Pairs stereotypical bias detection
301
+ * @deprecated Uses the removed Ollama logprob path via InferenceFactory's
302
+ * auto-selection; see bias --engine transformers. Stilled for the same reason
303
+ * as `runCrowsPairsBiasAnalysisWithFactory` — the factory's engine
304
+ * auto-selection needs a real redesign, not a throw-or-convert fix.
370
305
  */
371
306
  export async function runComprehensiveBiasAnalysisWithFactory(
372
- predictionData: PredictionData[],
373
- crowsPairsConfig: BiasTestConfig,
374
- protectedAttribute: string,
375
- statusCallback?: (status: string) => void
307
+ _predictionData: PredictionData[],
308
+ _crowsPairsConfig: BiasTestConfig,
309
+ _protectedAttribute: string,
310
+ _statusCallback?: (status: string) => void
376
311
  ): Promise<InferenceResult<BiasTestResult>> {
377
- try {
378
- const factory = new InferenceFactory(statusCallback);
379
-
380
- // Run CrowS-Pairs analysis (primary method) with automatic method detection
381
- const crowsPairsResult = await factory.createBiasCalculation(crowsPairsConfig);
382
-
383
- // Run traditional fairness metrics if prediction data is available
384
- let traditionalMetrics: FairnessMetric[] = [];
385
- if (predictionData && predictionData.length > 0) {
386
- const traditionalResult = runFairnessAnalysis(
387
- predictionData,
388
- crowsPairsConfig.model,
389
- protectedAttribute
390
- );
391
- traditionalMetrics = traditionalResult.metrics || [];
392
- }
393
-
394
- // Determine overall pass/fail based on CrowS-Pairs result and traditional metrics
395
- const biasThreshold = 0.1;
396
- const crowsPairsPassed = Math.abs(crowsPairsResult.data.score) < biasThreshold;
397
- const traditionalPassed =
398
- traditionalMetrics.length === 0 || traditionalMetrics.every((m) => m.passed);
399
- const overallPassed = crowsPairsPassed && traditionalPassed;
400
-
401
- const biasTestResult: BiasTestResult = {
402
- modelId: crowsPairsConfig.model,
403
- timestamp: new Date().toISOString(),
404
- method: crowsPairsResult.method,
405
- engine: crowsPairsResult.engine,
406
- protectedAttribute,
407
- crowsPairsResult: crowsPairsResult.data,
408
- metrics: traditionalMetrics.length > 0 ? traditionalMetrics : undefined,
409
- overallPassed,
410
- samplesAnalyzed: crowsPairsResult.data.pairsAnalyzed + (predictionData?.length || 0),
411
- };
412
-
413
- // Clean up factory resources
414
- factory.dispose();
415
-
416
- return {
417
- data: biasTestResult,
418
- method: crowsPairsResult.method,
419
- engine: crowsPairsResult.engine,
420
- model: crowsPairsResult.model,
421
- };
422
- } catch (error) {
423
- console.error("Comprehensive bias analysis with factory failed:", error);
424
-
425
- // Fallback to traditional metrics only if CrowS-Pairs fails
426
- if (predictionData && predictionData.length > 0) {
427
- const traditionalResult = runFairnessAnalysis(
428
- predictionData,
429
- crowsPairsConfig.model,
430
- protectedAttribute
431
- );
432
- return {
433
- data: traditionalResult,
434
- method: "logprobs_fallback_latency",
435
- engine: crowsPairsConfig.engine,
436
- model: crowsPairsConfig.model,
437
- };
438
- }
439
-
440
- // Return failed result if both methods fail
441
- const failedResult: BiasTestResult = {
442
- modelId: crowsPairsConfig.model,
443
- timestamp: new Date().toISOString(),
444
- method: "logprobs_fallback_latency",
445
- engine: crowsPairsConfig.engine,
446
- protectedAttribute,
447
- overallPassed: false,
448
- samplesAnalyzed: 0,
449
- };
450
-
451
- return {
452
- data: failedResult,
453
- method: "logprobs_fallback_latency",
454
- engine: crowsPairsConfig.engine,
455
- model: crowsPairsConfig.model,
456
- };
457
- }
312
+ throw new Error(
313
+ "runComprehensiveBiasAnalysisWithFactory verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers."
314
+ );
458
315
  }
459
316
 
460
317
  /**
package/src/index.ts CHANGED
@@ -21,6 +21,7 @@ export type {
21
21
  BiasCalculationMethod,
22
22
  BiasTestConfig,
23
23
  CrowsPairsBiasResult,
24
+ CrowsPairsFailedPair,
24
25
  ModelCapabilityCache,
25
26
  } from "./types";
26
27
 
@@ -126,6 +127,7 @@ export {
126
127
  listOllamaModels,
127
128
  OllamaClient,
128
129
  BrowserInference,
130
+ TransformersInference,
129
131
  checkWebGPU,
130
132
  BROWSER_MODELS,
131
133
  detectCapabilities,
@@ -139,6 +141,9 @@ export {
139
141
  type InferenceEngine,
140
142
  type InferenceCapabilities,
141
143
  type InferenceClient,
144
+ type SequenceLogProbProvider,
145
+ type ScoredLogProb,
146
+ type ScoredLogProbProvenance,
142
147
  type CapabilityDetectionService,
143
148
  type ModelCapability,
144
149
  type ModelSelectionState,
@@ -148,6 +153,8 @@ export {
148
153
  type EnhancedModelSelectionState,
149
154
  type CapabilityDetectionStatus,
150
155
  type CapabilityCache,
156
+ isConditionalGenerationConfig,
157
+ type MinimalModelConfig,
151
158
  } from "./inference";
152
159
 
153
160
  // Inference Factory
@@ -1,258 +1,16 @@
1
1
  /**
2
- * Browser-based inference using transformers.js
3
- * For small models (≤3B params) that can run via WebGPU
4
- *
5
- * STRICT MODE: Real inference only. No demo data.
2
+ * @deprecated Kept for backward compatibility (apps/web, existing tests import
3
+ * `BrowserInference` from this path). The canonical implementation now lives in
4
+ * `transformers-inference.ts` under the name `TransformersInference` — see
5
+ * Messmotor-Sanierung Task 2. New code should import `TransformersInference`
6
+ * from "./transformers-inference" directly.
6
7
  */
7
-
8
- // We need to use 'any' for transformers objects because the types are not fully exported
9
- /* eslint-disable @typescript-eslint/no-explicit-any */
10
-
11
- export interface BrowserModel {
12
- id: string;
13
- name: string;
14
- size: string;
15
- supported: boolean;
16
- }
17
-
18
- // Supported models for browser inference
19
- export const BROWSER_MODELS: BrowserModel[] = [
20
- {
21
- id: "Xenova/phi-3-mini-4k-instruct",
22
- name: "Phi-3 Mini",
23
- size: "3.8B",
24
- supported: true,
25
- },
26
- {
27
- id: "Xenova/Phi-3.5-mini-instruct",
28
- name: "Phi-3.5 Mini",
29
- size: "3.8B",
30
- supported: true,
31
- },
32
- {
33
- id: "Xenova/gpt2",
34
- name: "GPT-2",
35
- size: "124M",
36
- supported: true,
37
- },
38
- {
39
- id: "Xenova/distilgpt2",
40
- name: "DistilGPT-2",
41
- size: "82M",
42
- supported: true,
43
- },
44
- ];
45
-
46
- /**
47
- * Check if WebGPU is available in the browser
48
- */
49
- export async function checkWebGPU(): Promise<boolean> {
50
- if (typeof navigator === "undefined") return false;
51
- if (typeof window === "undefined") return false;
52
-
53
- try {
54
- // Check for WebGPU support
55
- const gpu = (navigator as Navigator & { gpu?: unknown }).gpu;
56
- if (!gpu) return false;
57
- // Try to get adapter
58
- const adapter = await (gpu as { requestAdapter: () => Promise<unknown> }).requestAdapter();
59
- return adapter !== null;
60
- } catch {
61
- return false;
62
- }
63
- }
64
-
65
- /**
66
- * Check if transformers.js is available
67
- */
68
- export async function checkTransformersAvailable(): Promise<boolean> {
69
- try {
70
- // Try dynamic import - this will fail if package not installed
71
- await import("@xenova/transformers");
72
- return true;
73
- } catch {
74
- return false;
75
- }
76
- }
77
-
78
- /**
79
- * Browser inference engine using transformers.js
80
- * STRICT MODE: Throws error if package not installed.
81
- */
82
- export class BrowserInference {
83
- public readonly modelId: string;
84
- private model: unknown = null;
85
- private tokenizer: unknown = null;
86
- private pipeline: unknown = null; // Keep pipeline for generation
87
- private loadingPromise: Promise<void> | null = null;
88
- private loadError: Error | null = null;
89
- private statusCallback: ((status: string) => void) | null = null;
90
-
91
- constructor(
92
- modelId = "Xenova/distilgpt2",
93
- statusCallback: ((status: string) => void) | null = null
94
- ) {
95
- this.modelId = modelId;
96
- this.statusCallback = statusCallback;
97
- }
98
-
99
- /**
100
- * Check if browser inference is available
101
- */
102
- async isAvailable(): Promise<boolean> {
103
- const hasTransformers = await checkTransformersAvailable();
104
- return hasTransformers;
105
- }
106
-
107
- /**
108
- * Load the model (lazy loading)
109
- * Handles concurrency with loadingPromise
110
- */
111
- async load(): Promise<void> {
112
- if (this.model && this.tokenizer) return;
113
- if (this.loadingPromise) return this.loadingPromise;
114
- if (this.loadError) throw this.loadError;
115
-
116
- this.loadingPromise = this.performLoad().finally(() => {
117
- this.loadingPromise = null;
118
- });
119
-
120
- return this.loadingPromise;
121
- }
122
-
123
- private async performLoad() {
124
- if (this.statusCallback) this.statusCallback("loading");
125
-
126
- try {
127
- const { AutoTokenizer, AutoModelForCausalLM, pipeline } = await import(
128
- "@xenova/transformers"
129
- );
130
-
131
- const hasWebGPU = await checkWebGPU();
132
- const device = hasWebGPU ? "webgpu" : "cpu";
133
-
134
- if (this.statusCallback) this.statusCallback("loading-tokenizer");
135
- this.tokenizer = await AutoTokenizer.from_pretrained(this.modelId);
136
-
137
- if (this.statusCallback) this.statusCallback("loading-model");
138
- // biome-ignore lint/suspicious/noExplicitAny: library internal
139
- this.model = await (AutoModelForCausalLM as any).from_pretrained(this.modelId, {
140
- device,
141
- quantized: true,
142
- });
143
-
144
- if (this.statusCallback) this.statusCallback("loading-pipeline");
145
- // biome-ignore lint/suspicious/noExplicitAny: library internal
146
- this.pipeline = await (pipeline as any)("text-generation", this.modelId, {
147
- device,
148
- model: this.model,
149
- tokenizer: this.tokenizer,
150
- });
151
-
152
- if (this.statusCallback) this.statusCallback("ready");
153
- } catch (err) {
154
- this.loadError = err instanceof Error ? err : new Error(String(err));
155
- if (this.statusCallback) this.statusCallback("error");
156
- throw this.loadError;
157
- }
158
- }
159
-
160
- /**
161
- * Generate text
162
- */
163
- async generate(prompt: string, maxTokens = 50): Promise<string> {
164
- await this.load();
165
-
166
- if (!this.pipeline) {
167
- throw new Error("Model not loaded");
168
- }
169
-
170
- // biome-ignore lint/suspicious/noExplicitAny: library internal
171
- const result = await (this.pipeline as any)(prompt, {
172
- max_new_tokens: maxTokens,
173
- temperature: 0.7,
174
- do_sample: true,
175
- });
176
-
177
- return result[0]?.generated_text || "";
178
- }
179
-
180
- /**
181
- * Calculate Log-Probability for a given text
182
- * This is the "Gold Standard" for bias testing.
183
- * We calculate the loss (negative log likelihood) of the text.
184
- */
185
- async getLogProb(text: string): Promise<number> {
186
- await this.load();
187
-
188
- if (!this.tokenizer || !this.model) {
189
- throw new Error("Model not loaded");
190
- }
191
-
192
- // biome-ignore lint/suspicious/noExplicitAny: internal transformers.js types
193
- const inputs = await (this.tokenizer as any)(text, { return_tensors: "pt" });
194
- const inputIds = inputs.input_ids;
195
-
196
- // biome-ignore lint/suspicious/noExplicitAny: internal transformers.js types
197
- const outputs = await (this.model as any)(inputs);
198
- const logits = outputs.logits;
199
-
200
- // 3. Compute Log Likelihood
201
- // We want to calculate P(text) = P(t1) * P(t2|t1) * ...
202
- // LogProb = sum( log( P(ti | t_<i) ) )
203
-
204
- // logits shape: [batch_size, seq_len, vocab_size]
205
- // inputIds shape: [batch_size, seq_len]
206
- // We are interested in the probability of the *next* token given previous tokens.
207
- // So we look at logits[0, i, :] to predict inputIds[0, i+1]
208
-
209
- const data = logits.data; // Float32Array
210
- const dims = logits.dims; // [batch, seq, vocab]
211
- const vocabSize = dims[2];
212
- const seqLen = dims[1];
213
-
214
- let totalLogProb = 0;
215
-
216
- // Helper to get logit value
217
- const getLogit = (seqIdx: number, vocabIdx: number) => {
218
- return data[seqIdx * vocabSize + vocabIdx];
219
- };
220
-
221
- // Helper for softmax/logsoftmax
222
- // We only need the log_prob of the target token
223
- // log_softmax(x_i) = x_i - log(sum(exp(x_j)))
224
- // To avoid overflow: x_i - max(x) - log(sum(exp(x_j - max(x))))
225
-
226
- for (let i = 0; i < seqLen - 1; i++) {
227
- const targetTokenId = Number(inputIds.data[i + 1]);
228
-
229
- // Calculate max logit for numerical stability
230
- let maxLogit = Number.NEGATIVE_INFINITY;
231
- for (let j = 0; j < vocabSize; j++) {
232
- const val = getLogit(i, j);
233
- if (val > maxLogit) maxLogit = val;
234
- }
235
-
236
- // Calculate sum of exp
237
- let sumExp = 0;
238
- for (let j = 0; j < vocabSize; j++) {
239
- sumExp += Math.exp(getLogit(i, j) - maxLogit);
240
- }
241
-
242
- const logSumExp = maxLogit + Math.log(sumExp);
243
- const targetLogit = getLogit(i, targetTokenId);
244
-
245
- const logProb = targetLogit - logSumExp;
246
- totalLogProb += logProb;
247
- }
248
-
249
- return totalLogProb;
250
- }
251
-
252
- /**
253
- * Get list of supported browser models
254
- */
255
- static getSupportedModels(): BrowserModel[] {
256
- return BROWSER_MODELS.filter((m) => m.supported);
257
- }
258
- }
8
+ export {
9
+ TransformersInference as BrowserInference,
10
+ type BrowserModel,
11
+ BROWSER_MODELS,
12
+ checkWebGPU,
13
+ checkTransformersAvailable,
14
+ type ScoredLogProb,
15
+ type ScoredLogProbProvenance,
16
+ } from "./transformers-inference";