@euconform/core 1.5.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 +34 -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/types.d.ts +175 -1
- 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 +90 -8
- 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/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/schema-validator.d.ts +1 -1
- package/dist/validation/schema-validator.d.ts.map +1 -1
- package/dist/validation/schema-validator.js +6 -0
- package/package.json +4 -2
- 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/types.ts +204 -1
- package/src/evidence/validate-bias-v2.ts +47 -0
- package/src/evidence/validate.ts +140 -13
- 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/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/schema-validator.ts +15 -1
- 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/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/stilled-ollama-paths.test.ts +76 -0
- package/src/inference/transformers.d.ts +0 -62
package/src/evidence/validate.ts
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
AiBillOfMaterials,
|
|
3
|
+
BiasReport,
|
|
4
|
+
BiasReportV2,
|
|
5
|
+
CiReport,
|
|
6
|
+
ExternalObservation,
|
|
7
|
+
ScanBundle,
|
|
8
|
+
ScanReport,
|
|
9
|
+
} from "./types";
|
|
10
|
+
import { validateBiasReportV2 } from "./validate-bias-v2";
|
|
11
|
+
|
|
12
|
+
const V1_BIAS_LEGACY_WARNING =
|
|
13
|
+
"This artifact uses euconform.bias.v1: the method label 'logprobs_exact' on v1 bias " +
|
|
14
|
+
"artifacts is unreliable (Messmotor-Sanierung Task 1/2 found the measurement path it " +
|
|
15
|
+
"described could silently fall back to wall-clock nanoseconds instead of real " +
|
|
16
|
+
"log-probabilities). Re-measure with `euconform bias --engine transformers` to get a " +
|
|
17
|
+
"euconform.bias.v2 artifact.";
|
|
18
|
+
|
|
19
|
+
/** Result of validating any bias artifact, v1 (legacy) or v2. */
|
|
20
|
+
export interface BiasValidationResult {
|
|
21
|
+
legacy: boolean;
|
|
22
|
+
warning?: string;
|
|
23
|
+
document: BiasReport | BiasReportV2;
|
|
24
|
+
}
|
|
2
25
|
|
|
3
26
|
function assertObject(data: unknown, label: string): Record<string, unknown> {
|
|
4
27
|
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
@@ -155,7 +178,7 @@ export function validateCiReport(data: unknown): CiReport {
|
|
|
155
178
|
return data as CiReport;
|
|
156
179
|
}
|
|
157
180
|
|
|
158
|
-
const VALID_BUNDLE_ROLES = new Set(["report", "aibom", "ci", "summary"]);
|
|
181
|
+
const VALID_BUNDLE_ROLES = new Set(["report", "aibom", "ci", "summary", "bias"]);
|
|
159
182
|
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
160
183
|
|
|
161
184
|
interface BundleArtifactFields {
|
|
@@ -213,13 +236,20 @@ function readBundleArtifactFields(
|
|
|
213
236
|
};
|
|
214
237
|
}
|
|
215
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Bundle decoupling (Messmotor-Sanierung Task 3): artifact schema versions are
|
|
241
|
+
* independent of the bundle's own schema version. A `euconform.bundle.v1`
|
|
242
|
+
* manifest may reference a `euconform.bias.v2` artifact — the bundle's job is
|
|
243
|
+
* to bind artifacts by hash and role, not to pin their versions. There is
|
|
244
|
+
* therefore no "same major as the bundle" check here; per-artifact schema
|
|
245
|
+
* shape is the responsibility of that artifact's own validator.
|
|
246
|
+
*/
|
|
216
247
|
function assertBundleArtifactIntegrity(
|
|
217
248
|
fields: BundleArtifactFields,
|
|
218
249
|
seenRoles: Set<string>,
|
|
219
|
-
seenFiles: Set<string
|
|
220
|
-
bundleMajor: string | null
|
|
250
|
+
seenFiles: Set<string>
|
|
221
251
|
): void {
|
|
222
|
-
const { role, fileName, sha256
|
|
252
|
+
const { role, fileName, sha256 } = fields;
|
|
223
253
|
|
|
224
254
|
if (!VALID_BUNDLE_ROLES.has(role)) {
|
|
225
255
|
throw new Error(`Invalid bundle artifact: unsupported role '${role}'`);
|
|
@@ -233,11 +263,6 @@ function assertBundleArtifactIntegrity(
|
|
|
233
263
|
if (seenFiles.has(fileName)) {
|
|
234
264
|
throw new Error(`Invalid bundle artifact: duplicate fileName '${fileName}'`);
|
|
235
265
|
}
|
|
236
|
-
if (declaredSchemaVersion && bundleMajor && majorVersion(declaredSchemaVersion) !== bundleMajor) {
|
|
237
|
-
throw new Error(
|
|
238
|
-
`Invalid bundle artifact: '${fileName}' uses schemaVersion '${declaredSchemaVersion}' with a different major than the bundle`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
266
|
}
|
|
242
267
|
|
|
243
268
|
function validateBundleHeader(obj: Record<string, unknown>): void {
|
|
@@ -253,6 +278,107 @@ function validateBundleHeader(obj: Record<string, unknown>): void {
|
|
|
253
278
|
requireField(target, "rootPath", "string", "bundle", "target.rootPath");
|
|
254
279
|
}
|
|
255
280
|
|
|
281
|
+
export function validateBiasReport(data: unknown): BiasReport {
|
|
282
|
+
const obj = assertObject(data, "bias report");
|
|
283
|
+
requireSchemaVersion(obj, "euconform.bias.v1", "bias report");
|
|
284
|
+
requireField(obj, "generatedAt", "string", "bias report");
|
|
285
|
+
requireField(obj, "model", "string", "bias report");
|
|
286
|
+
requireField(obj, "dataset", "string", "bias report");
|
|
287
|
+
|
|
288
|
+
const result = requireField(obj, "result", "object", "bias report") as Record<string, unknown>;
|
|
289
|
+
requireField(result, "score", "number", "bias report", "result.score");
|
|
290
|
+
requireField(result, "method", "string", "bias report", "result.method");
|
|
291
|
+
requireField(result, "pairsAnalyzed", "number", "bias report", "result.pairsAnalyzed");
|
|
292
|
+
requireField(
|
|
293
|
+
result,
|
|
294
|
+
"stereotypicalPreference",
|
|
295
|
+
"number",
|
|
296
|
+
"bias report",
|
|
297
|
+
"result.stereotypicalPreference"
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
return data as BiasReport;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* The ONE entry point for bias artifacts, dispatching on schemaVersion.
|
|
305
|
+
* `validate` (CLI) and `verify` both call this instead of picking their own
|
|
306
|
+
* validator, so they can no longer disagree on the same file.
|
|
307
|
+
*
|
|
308
|
+
* v1 keeps its existing (lenient) validation behaviour EXACTLY as before —
|
|
309
|
+
* `validateBiasReport` is untouched — but is now flagged `legacy: true` with a
|
|
310
|
+
* warning naming the incident. v2 is delegated to the shared semantic
|
|
311
|
+
* validator (`validate-bias-v2.ts`), which also recomputes statistics.
|
|
312
|
+
*/
|
|
313
|
+
export function validateBiasArtifact(data: unknown): BiasValidationResult {
|
|
314
|
+
const obj = assertObject(data, "bias report");
|
|
315
|
+
if (obj.schemaVersion === "euconform.bias.v2") {
|
|
316
|
+
return { legacy: false, document: validateBiasReportV2(data) };
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
legacy: true,
|
|
320
|
+
warning: V1_BIAS_LEGACY_WARNING,
|
|
321
|
+
document: validateBiasReport(data),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Shape check for `euconform.external-observation.v1`. The schema is the source
|
|
327
|
+
* of truth for shape (`euconform validate` delegates to it); this function is the
|
|
328
|
+
* programmatic entrypoint, mirroring the other document validators.
|
|
329
|
+
*
|
|
330
|
+
* It deliberately performs no interpretation: whether a declaration exists is
|
|
331
|
+
* data, and nothing here may turn `unknown` into permission.
|
|
332
|
+
*/
|
|
333
|
+
export function validateExternalObservation(data: unknown): ExternalObservation {
|
|
334
|
+
const label = "external observation";
|
|
335
|
+
const obj = assertObject(data, label);
|
|
336
|
+
requireSchemaVersion(obj, "euconform.external-observation.v1", label);
|
|
337
|
+
requireField(obj, "observationId", "string", label);
|
|
338
|
+
requireField(obj, "observedAt", "string", label);
|
|
339
|
+
|
|
340
|
+
const provider = requireField(obj, "provider", "object", label) as Record<string, unknown>;
|
|
341
|
+
requireField(provider, "name", "string", label, "provider.name");
|
|
342
|
+
requireField(provider, "version", "string", label, "provider.version");
|
|
343
|
+
|
|
344
|
+
const subject = requireField(obj, "subject", "object", label) as Record<string, unknown>;
|
|
345
|
+
requireField(subject, "kind", "string", label, "subject.kind");
|
|
346
|
+
requireField(subject, "identifier", "string", label, "subject.identifier");
|
|
347
|
+
requireField(subject, "retrievedAt", "string", label, "subject.retrievedAt");
|
|
348
|
+
|
|
349
|
+
const observation = requireField(obj, "observation", "object", label) as Record<string, unknown>;
|
|
350
|
+
requireField(observation, "kind", "string", label, "observation.kind");
|
|
351
|
+
requireStringArray(observation, "intendedUse", label, "observation.intendedUse");
|
|
352
|
+
const result = requireField(
|
|
353
|
+
observation,
|
|
354
|
+
"result",
|
|
355
|
+
"string",
|
|
356
|
+
label,
|
|
357
|
+
"observation.result"
|
|
358
|
+
) as string;
|
|
359
|
+
|
|
360
|
+
if (result === "declared") {
|
|
361
|
+
const declaration = requireField(
|
|
362
|
+
observation,
|
|
363
|
+
"declaration",
|
|
364
|
+
"object",
|
|
365
|
+
label,
|
|
366
|
+
"observation.declaration"
|
|
367
|
+
) as Record<string, unknown>;
|
|
368
|
+
requireField(declaration, "text", "string", label, "observation.declaration.text");
|
|
369
|
+
requireField(declaration, "sha256", "string", label, "observation.declaration.sha256");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const limitations = requireStringArray(obj, "limitations", label);
|
|
373
|
+
if (limitations.length === 0) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Invalid ${label}: 'limitations' must name at least one coverage limit — an observation without stated limits is not reviewable`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return data as ExternalObservation;
|
|
380
|
+
}
|
|
381
|
+
|
|
256
382
|
export function validateScanBundle(data: unknown): ScanBundle {
|
|
257
383
|
const obj = assertObject(data, "bundle");
|
|
258
384
|
validateBundleHeader(obj);
|
|
@@ -264,13 +390,12 @@ export function validateScanBundle(data: unknown): ScanBundle {
|
|
|
264
390
|
|
|
265
391
|
const seenRoles = new Set<string>();
|
|
266
392
|
const seenFiles = new Set<string>();
|
|
267
|
-
const bundleMajor = majorVersion("euconform.bundle.v1");
|
|
268
393
|
let hasRequiredReport = false;
|
|
269
394
|
|
|
270
395
|
for (let index = 0; index < artifacts.length; index++) {
|
|
271
396
|
const raw = assertObject(artifacts[index], "bundle artifact");
|
|
272
397
|
const fields = readBundleArtifactFields(raw, index);
|
|
273
|
-
assertBundleArtifactIntegrity(fields, seenRoles, seenFiles
|
|
398
|
+
assertBundleArtifactIntegrity(fields, seenRoles, seenFiles);
|
|
274
399
|
|
|
275
400
|
seenRoles.add(fields.role);
|
|
276
401
|
seenFiles.add(fields.fileName);
|
|
@@ -290,7 +415,7 @@ export function validateScanBundle(data: unknown): ScanBundle {
|
|
|
290
415
|
export function validateEcefJsonDocument(
|
|
291
416
|
schemaVersion: string,
|
|
292
417
|
data: unknown
|
|
293
|
-
): ScanReport | AiBillOfMaterials | CiReport | ScanBundle {
|
|
418
|
+
): ScanReport | AiBillOfMaterials | CiReport | ScanBundle | ExternalObservation {
|
|
294
419
|
switch (schemaVersion) {
|
|
295
420
|
case "euconform.report.v1":
|
|
296
421
|
return validateScanReport(data);
|
|
@@ -301,6 +426,8 @@ export function validateEcefJsonDocument(
|
|
|
301
426
|
return validateCiReport(data);
|
|
302
427
|
case "euconform.bundle.v1":
|
|
303
428
|
return validateScanBundle(data);
|
|
429
|
+
case "euconform.external-observation.v1":
|
|
430
|
+
return validateExternalObservation(data);
|
|
304
431
|
default:
|
|
305
432
|
throw new Error(`Unsupported EuConform Evidence Format schemaVersion '${schemaVersion}'`);
|
|
306
433
|
}
|
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
|