@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +46 -0
- package/dist/datasets/crows-pairs.d.ts +18 -0
- package/dist/datasets/crows-pairs.d.ts.map +1 -1
- package/dist/datasets/crows-pairs.js +58 -0
- package/dist/evidence/bias-comparison-markdown.d.ts +11 -0
- package/dist/evidence/bias-comparison-markdown.d.ts.map +1 -0
- package/dist/evidence/bias-comparison-markdown.js +120 -0
- package/dist/evidence/bias-comparison.d.ts +133 -0
- package/dist/evidence/bias-comparison.d.ts.map +1 -0
- package/dist/evidence/bias-comparison.js +274 -0
- package/dist/evidence/bias-statistics.d.ts +72 -0
- package/dist/evidence/bias-statistics.d.ts.map +1 -0
- package/dist/evidence/bias-statistics.js +122 -0
- package/dist/evidence/bias-v2-semantics.d.ts +19 -0
- package/dist/evidence/bias-v2-semantics.d.ts.map +1 -0
- package/dist/evidence/bias-v2-semantics.js +133 -0
- package/dist/evidence/browser.d.ts +18 -0
- package/dist/evidence/browser.d.ts.map +1 -0
- package/dist/evidence/browser.js +14 -0
- package/dist/evidence/bundle.d.ts +1 -0
- package/dist/evidence/bundle.d.ts.map +1 -1
- package/dist/evidence/bundle.js +2 -0
- package/dist/evidence/index.d.ts +9 -2
- package/dist/evidence/index.d.ts.map +1 -1
- package/dist/evidence/index.js +5 -1
- package/dist/evidence/output.d.ts.map +1 -1
- package/dist/evidence/output.js +1 -0
- package/dist/evidence/types.d.ts +183 -3
- package/dist/evidence/types.d.ts.map +1 -1
- package/dist/evidence/validate-bias-v2.d.ts +31 -0
- package/dist/evidence/validate-bias-v2.d.ts.map +1 -0
- package/dist/evidence/validate-bias-v2.js +40 -0
- package/dist/evidence/validate.d.ts +29 -2
- package/dist/evidence/validate.d.ts.map +1 -1
- package/dist/evidence/validate.js +96 -11
- package/dist/fairness-metrics.d.ts +15 -11
- package/dist/fairness-metrics.d.ts.map +1 -1
- package/dist/fairness-metrics.js +59 -183
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/inference/browser-inference.d.ts +6 -57
- package/dist/inference/browser-inference.d.ts.map +1 -1
- package/dist/inference/browser-inference.js +6 -219
- package/dist/inference/capability-detection.d.ts +9 -2
- package/dist/inference/capability-detection.d.ts.map +1 -1
- package/dist/inference/capability-detection.js +18 -16
- package/dist/inference/index.d.ts +15 -2
- package/dist/inference/index.d.ts.map +1 -1
- package/dist/inference/index.js +12 -3
- package/dist/inference/inference-factory.d.ts +6 -3
- package/dist/inference/inference-factory.d.ts.map +1 -1
- package/dist/inference/inference-factory.js +7 -26
- package/dist/inference/ollama-client.d.ts +29 -0
- package/dist/inference/ollama-client.d.ts.map +1 -1
- package/dist/inference/ollama-client.js +29 -0
- package/dist/inference/scored-log-prob-types.d.ts +64 -0
- package/dist/inference/scored-log-prob-types.d.ts.map +1 -0
- package/dist/inference/scored-log-prob-types.js +12 -0
- package/dist/inference/sequence-logprob.d.ts +34 -0
- package/dist/inference/sequence-logprob.d.ts.map +1 -0
- package/dist/inference/sequence-logprob.js +87 -0
- package/dist/inference/template-scoring.d.ts +124 -0
- package/dist/inference/template-scoring.d.ts.map +1 -0
- package/dist/inference/template-scoring.js +164 -0
- package/dist/inference/transformers-inference.d.ts +124 -0
- package/dist/inference/transformers-inference.d.ts.map +1 -0
- package/dist/inference/transformers-inference.js +392 -0
- package/dist/legal-checks/bias-metrics.d.ts +5 -7
- package/dist/legal-checks/bias-metrics.d.ts.map +1 -1
- package/dist/legal-checks/bias-metrics.js +8 -101
- package/dist/metrics/crows-pairs-bias.d.ts +17 -9
- package/dist/metrics/crows-pairs-bias.d.ts.map +1 -1
- package/dist/metrics/crows-pairs-bias.js +147 -59
- package/dist/metrics/stereotype-bias.d.ts +13 -5
- package/dist/metrics/stereotype-bias.d.ts.map +1 -1
- package/dist/metrics/stereotype-bias.js +16 -99
- package/dist/sbom/component-mapper.d.ts +26 -0
- package/dist/sbom/component-mapper.d.ts.map +1 -0
- package/dist/sbom/component-mapper.js +83 -0
- package/dist/sbom/cyclonedx-import.d.ts +34 -0
- package/dist/sbom/cyclonedx-import.d.ts.map +1 -0
- package/dist/sbom/cyclonedx-import.js +131 -0
- package/dist/sbom/index.d.ts +6 -0
- package/dist/sbom/index.d.ts.map +1 -0
- package/dist/sbom/index.js +4 -0
- package/dist/sbom/known-packages.d.ts +13 -0
- package/dist/sbom/known-packages.d.ts.map +1 -0
- package/dist/sbom/known-packages.js +129 -0
- package/dist/sbom/purl.d.ts +14 -0
- package/dist/sbom/purl.d.ts.map +1 -0
- package/dist/sbom/purl.js +56 -0
- package/dist/sbom/types.d.ts +39 -0
- package/dist/sbom/types.d.ts.map +1 -0
- package/dist/sbom/types.js +5 -0
- package/dist/scanner/detectors/ai-framework-detector.js +1 -1
- package/dist/scanner/detectors/local-inference-detector.d.ts.map +1 -1
- package/dist/scanner/detectors/local-inference-detector.js +1 -1
- package/dist/scanner/detectors/package-json-detector.d.ts.map +1 -1
- package/dist/scanner/detectors/package-json-detector.js +1 -0
- package/dist/types.d.ts +103 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +1 -0
- package/dist/validation/schema-validator.d.ts +18 -0
- package/dist/validation/schema-validator.d.ts.map +1 -0
- package/dist/validation/schema-validator.js +105 -0
- package/package.json +9 -3
- package/scripts/seed-logprob-fixture.mjs +179 -0
- package/scripts/ts-extensionless-resolve-hook.mjs +26 -0
- package/src/datasets/crows-pairs.ts +74 -0
- package/src/evidence/bias-comparison-markdown.ts +162 -0
- package/src/evidence/bias-comparison.ts +432 -0
- package/src/evidence/bias-statistics.ts +180 -0
- package/src/evidence/bias-v2-semantics.ts +214 -0
- package/src/evidence/browser.ts +35 -0
- package/src/evidence/bundle.ts +2 -0
- package/src/evidence/index.ts +31 -0
- package/src/evidence/output.ts +1 -0
- package/src/evidence/types.ts +213 -3
- package/src/evidence/validate-bias-v2.ts +47 -0
- package/src/evidence/validate.ts +151 -17
- package/src/fairness-metrics.ts +79 -222
- package/src/index.ts +7 -0
- package/src/inference/browser-inference.ts +14 -256
- package/src/inference/capability-detection.ts +32 -18
- package/src/inference/index.ts +33 -5
- package/src/inference/inference-factory.ts +9 -30
- package/src/inference/ollama-client.ts +29 -0
- package/src/inference/scored-log-prob-types.ts +66 -0
- package/src/inference/sequence-logprob.ts +136 -0
- package/src/inference/template-scoring.ts +248 -0
- package/src/inference/transformers-inference.ts +494 -0
- package/src/legal-checks/bias-metrics.ts +20 -118
- package/src/metrics/crows-pairs-bias.ts +180 -83
- package/src/metrics/stereotype-bias.ts +27 -119
- package/src/sbom/component-mapper.ts +107 -0
- package/src/sbom/cyclonedx-import.ts +198 -0
- package/src/sbom/index.ts +12 -0
- package/src/sbom/known-packages.ts +142 -0
- package/src/sbom/purl.ts +66 -0
- package/src/sbom/types.ts +34 -0
- package/src/scanner/detectors/ai-framework-detector.ts +1 -1
- package/src/scanner/detectors/local-inference-detector.ts +2 -1
- package/src/scanner/detectors/package-json-detector.ts +1 -0
- package/src/types.ts +108 -2
- package/src/validation/index.ts +7 -0
- package/src/validation/schema-validator.ts +147 -0
- package/tests/annex-iv-report.test.ts +1 -1
- package/tests/capability-detection.test.ts +57 -0
- package/tests/crows-pairs-bias.test.ts +286 -186
- package/tests/custom-dataset.test.ts +80 -0
- package/tests/evidence/bias-artifact-v2.test.ts +135 -0
- package/tests/evidence/bias-artifact.test.ts +78 -0
- package/tests/evidence/bias-comparison.test.ts +397 -0
- package/tests/evidence/bias-statistics.test.ts +191 -0
- package/tests/evidence/external-observation.test.ts +167 -0
- package/tests/evidence/spec-schemas.test.ts +35 -2
- package/tests/evidence/validate-bias-v2.test.ts +180 -0
- package/tests/fairness-metrics.test.ts +77 -1
- package/tests/fixtures/cyclonedx-minimal.json +13 -0
- package/tests/fixtures/cyclonedx-mixed.json +17 -0
- package/tests/fixtures/cyclonedx-ml-bom.json +25 -0
- package/tests/inference/browser-inference-scoring.test.ts +159 -0
- package/tests/inference/fixtures/logprob-golden.json +69 -0
- package/tests/inference/gemma-template.integration.test.ts +81 -0
- package/tests/inference/logprob-golden.integration.test.ts +128 -0
- package/tests/inference/synthetic-logits.test.ts +188 -0
- package/tests/inference/template-scoring.test.ts +249 -0
- package/tests/inference/transformers-inference-template-mode.test.ts +214 -0
- package/tests/integration/bias-calculation.integration.test.ts +119 -599
- package/tests/sbom/component-mapper.test.ts +258 -0
- package/tests/sbom/cyclonedx-import.test.ts +329 -0
- package/tests/sbom/purl.test.ts +102 -0
- package/tests/stilled-ollama-paths.test.ts +76 -0
- package/tests/validation/schema-validator.test.ts +219 -0
- package/src/inference/transformers.d.ts +0 -62
package/src/fairness-metrics.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Based on EU AI Act requirements for non-discrimination
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
import {
|
|
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:
|
|
211
|
+
inferenceClient: SequenceLogProbProvider
|
|
213
212
|
): Promise<BiasTestResult> {
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
*
|
|
248
|
-
*
|
|
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
|
-
|
|
252
|
-
|
|
244
|
+
_config: BiasTestConfig,
|
|
245
|
+
_statusCallback?: (status: string) => void
|
|
253
246
|
): Promise<InferenceResult<BiasTestResult>> {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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:
|
|
259
|
+
inferenceClient: SequenceLogProbProvider,
|
|
311
260
|
protectedAttribute: string
|
|
312
261
|
): Promise<BiasTestResult> {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
*
|
|
369
|
-
*
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
307
|
+
_predictionData: PredictionData[],
|
|
308
|
+
_crowsPairsConfig: BiasTestConfig,
|
|
309
|
+
_protectedAttribute: string,
|
|
310
|
+
_statusCallback?: (status: string) => void
|
|
376
311
|
): Promise<InferenceResult<BiasTestResult>> {
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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";
|