@euconform/core 1.3.0 → 1.4.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 +12 -0
- package/assets/datasets/crows-pairs-de.json +613 -0
- package/assets/datasets/crows-pairs-en.json +9055 -0
- package/dist/datasets/crows-pairs.d.ts +7 -0
- package/dist/datasets/crows-pairs.d.ts.map +1 -0
- package/dist/datasets/crows-pairs.js +41 -0
- package/dist/evidence/validate.d.ts.map +1 -1
- package/dist/evidence/validate.js +53 -36
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/inference/index.d.ts +1 -1
- package/dist/inference/index.d.ts.map +1 -1
- package/dist/inference/index.js +1 -1
- package/dist/inference/ollama-client.d.ts +9 -3
- package/dist/inference/ollama-client.d.ts.map +1 -1
- package/dist/inference/ollama-client.js +23 -11
- package/package.json +4 -2
- package/src/datasets/crows-pairs.ts +64 -0
- package/src/evidence/validate.ts +96 -66
- package/src/index.ts +1 -0
- package/src/inference/index.ts +6 -1
- package/src/inference/ollama-client.ts +30 -14
- package/tests/evidence/{ecef-schemas.test.ts → spec-schemas.test.ts} +3 -3
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CrowsPairsEntry } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Load CrowS-Pairs dataset from bundled assets.
|
|
4
|
+
* Converts the structured JSON format to CrowsPairsEntry[] for the bias engine.
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadCrowsPairsDataset(language: "en" | "de"): CrowsPairsEntry[];
|
|
7
|
+
//# sourceMappingURL=crows-pairs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crows-pairs.d.ts","sourceRoot":"","sources":["../../src/datasets/crows-pairs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AA6ChD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,eAAe,EAAE,CAW9E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
function findDatasetPath(language) {
|
|
5
|
+
const fileName = `crows-pairs-${language}.json`;
|
|
6
|
+
// Strategy 1: Relative to this source file (works in unbundled tsc builds)
|
|
7
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const fromSource = resolve(currentDir, `../../assets/datasets/${fileName}`);
|
|
9
|
+
if (existsSync(fromSource))
|
|
10
|
+
return fromSource;
|
|
11
|
+
// Strategy 2: Relative to process.cwd() (works in monorepo context)
|
|
12
|
+
const fromCwd = resolve(process.cwd(), `packages/core/assets/datasets/${fileName}`);
|
|
13
|
+
if (existsSync(fromCwd))
|
|
14
|
+
return fromCwd;
|
|
15
|
+
// Strategy 3: Walk up from the entry script to find the monorepo root
|
|
16
|
+
if (process.argv[1]) {
|
|
17
|
+
let dir = dirname(resolve(process.argv[1]));
|
|
18
|
+
for (let i = 0; i < 5; i++) {
|
|
19
|
+
const candidate = resolve(dir, `packages/core/assets/datasets/${fileName}`);
|
|
20
|
+
if (existsSync(candidate))
|
|
21
|
+
return candidate;
|
|
22
|
+
dir = dirname(dir);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`CrowS-Pairs dataset (${language}) not found. Ensure packages/core/assets/datasets/ exists.`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Load CrowS-Pairs dataset from bundled assets.
|
|
29
|
+
* Converts the structured JSON format to CrowsPairsEntry[] for the bias engine.
|
|
30
|
+
*/
|
|
31
|
+
export function loadCrowsPairsDataset(language) {
|
|
32
|
+
const filePath = findDatasetPath(language);
|
|
33
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
34
|
+
const dataset = JSON.parse(raw);
|
|
35
|
+
return dataset.pairs.map((pair) => ({
|
|
36
|
+
id: pair.id,
|
|
37
|
+
sent_more: pair.stereotype,
|
|
38
|
+
sent_less: pair.antiStereotype,
|
|
39
|
+
bias_type: pair.biasType,
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/evidence/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA6DnF,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE1E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAmC5D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,CAa1E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAgCxD;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/evidence/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AA6DnF,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE1E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAmC5D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,CAa1E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAgCxD;AAoGD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAgC5D;AAED,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,OAAO,GACZ,UAAU,GAAG,iBAAiB,GAAG,QAAQ,GAAG,UAAU,CAaxD"}
|
|
@@ -100,8 +100,47 @@ export function validateCiReport(data) {
|
|
|
100
100
|
}
|
|
101
101
|
const VALID_BUNDLE_ROLES = new Set(["report", "aibom", "ci", "summary"]);
|
|
102
102
|
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
103
|
-
|
|
104
|
-
const
|
|
103
|
+
function readBundleArtifactFields(raw, index) {
|
|
104
|
+
const scope = "bundle artifact";
|
|
105
|
+
const role = requireField(raw, "role", "string", scope, `artifacts[${index}].role`);
|
|
106
|
+
const fileName = requireField(raw, "fileName", "string", scope, `artifacts[${index}].fileName`);
|
|
107
|
+
const sha256 = requireField(raw, "sha256", "string", scope, `artifacts[${index}].sha256`);
|
|
108
|
+
const required = requireField(raw, "required", "boolean", scope, `artifacts[${index}].required`);
|
|
109
|
+
const declaredSchemaVersion = raw.schemaVersion;
|
|
110
|
+
if (declaredSchemaVersion !== undefined && typeof declaredSchemaVersion !== "string") {
|
|
111
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string schemaVersion`);
|
|
112
|
+
}
|
|
113
|
+
if (raw.mimeType !== undefined && typeof raw.mimeType !== "string") {
|
|
114
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string mimeType`);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
role,
|
|
118
|
+
fileName,
|
|
119
|
+
sha256,
|
|
120
|
+
required,
|
|
121
|
+
declaredSchemaVersion: declaredSchemaVersion,
|
|
122
|
+
mimeType: raw.mimeType,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function assertBundleArtifactIntegrity(fields, seenRoles, seenFiles, bundleMajor) {
|
|
126
|
+
const { role, fileName, sha256, declaredSchemaVersion } = fields;
|
|
127
|
+
if (!VALID_BUNDLE_ROLES.has(role)) {
|
|
128
|
+
throw new Error(`Invalid bundle artifact: unsupported role '${role}'`);
|
|
129
|
+
}
|
|
130
|
+
if (!SHA256_HEX.test(sha256)) {
|
|
131
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has invalid sha256`);
|
|
132
|
+
}
|
|
133
|
+
if (seenRoles.has(role)) {
|
|
134
|
+
throw new Error(`Invalid bundle artifact: duplicate role '${role}'`);
|
|
135
|
+
}
|
|
136
|
+
if (seenFiles.has(fileName)) {
|
|
137
|
+
throw new Error(`Invalid bundle artifact: duplicate fileName '${fileName}'`);
|
|
138
|
+
}
|
|
139
|
+
if (declaredSchemaVersion && bundleMajor && majorVersion(declaredSchemaVersion) !== bundleMajor) {
|
|
140
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' uses schemaVersion '${declaredSchemaVersion}' with a different major than the bundle`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function validateBundleHeader(obj) {
|
|
105
144
|
requireSchemaVersion(obj, "euconform.bundle.v1", "bundle");
|
|
106
145
|
requireField(obj, "generatedAt", "string", "bundle");
|
|
107
146
|
const tool = requireField(obj, "tool", "object", "bundle");
|
|
@@ -110,6 +149,10 @@ export function validateScanBundle(data) {
|
|
|
110
149
|
const target = requireField(obj, "target", "object", "bundle");
|
|
111
150
|
requireField(target, "name", "string", "bundle", "target.name");
|
|
112
151
|
requireField(target, "rootPath", "string", "bundle", "target.rootPath");
|
|
152
|
+
}
|
|
153
|
+
export function validateScanBundle(data) {
|
|
154
|
+
const obj = assertObject(data, "bundle");
|
|
155
|
+
validateBundleHeader(obj);
|
|
113
156
|
const artifacts = requireField(obj, "artifacts", "array", "bundle");
|
|
114
157
|
if (artifacts.length === 0) {
|
|
115
158
|
throw new Error("Invalid bundle: 'artifacts' must contain at least one artifact");
|
|
@@ -119,39 +162,13 @@ export function validateScanBundle(data) {
|
|
|
119
162
|
const bundleMajor = majorVersion("euconform.bundle.v1");
|
|
120
163
|
let hasRequiredReport = false;
|
|
121
164
|
for (let index = 0; index < artifacts.length; index++) {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
if (!SHA256_HEX.test(sha256)) {
|
|
131
|
-
throw new Error(`Invalid bundle artifact: '${fileName}' has invalid sha256`);
|
|
132
|
-
}
|
|
133
|
-
if (seenRoles.has(role)) {
|
|
134
|
-
throw new Error(`Invalid bundle artifact: duplicate role '${role}'`);
|
|
135
|
-
}
|
|
136
|
-
if (seenFiles.has(fileName)) {
|
|
137
|
-
throw new Error(`Invalid bundle artifact: duplicate fileName '${fileName}'`);
|
|
138
|
-
}
|
|
139
|
-
seenRoles.add(role);
|
|
140
|
-
seenFiles.add(fileName);
|
|
141
|
-
const declaredSchemaVersion = artifact.schemaVersion;
|
|
142
|
-
if (declaredSchemaVersion !== undefined && typeof declaredSchemaVersion !== "string") {
|
|
143
|
-
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string schemaVersion`);
|
|
144
|
-
}
|
|
145
|
-
if (artifact.mimeType !== undefined && typeof artifact.mimeType !== "string") {
|
|
146
|
-
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string mimeType`);
|
|
147
|
-
}
|
|
148
|
-
if (declaredSchemaVersion &&
|
|
149
|
-
bundleMajor &&
|
|
150
|
-
majorVersion(declaredSchemaVersion) !== bundleMajor) {
|
|
151
|
-
throw new Error(`Invalid bundle artifact: '${fileName}' uses schemaVersion '${declaredSchemaVersion}' with a different major than the bundle`);
|
|
152
|
-
}
|
|
153
|
-
if (role === "report") {
|
|
154
|
-
hasRequiredReport = required;
|
|
165
|
+
const raw = assertObject(artifacts[index], "bundle artifact");
|
|
166
|
+
const fields = readBundleArtifactFields(raw, index);
|
|
167
|
+
assertBundleArtifactIntegrity(fields, seenRoles, seenFiles, bundleMajor);
|
|
168
|
+
seenRoles.add(fields.role);
|
|
169
|
+
seenFiles.add(fields.fileName);
|
|
170
|
+
if (fields.role === "report") {
|
|
171
|
+
hasRequiredReport = fields.required;
|
|
155
172
|
}
|
|
156
173
|
}
|
|
157
174
|
if (!hasRequiredReport) {
|
|
@@ -170,6 +187,6 @@ export function validateEcefJsonDocument(schemaVersion, data) {
|
|
|
170
187
|
case "euconform.bundle.v1":
|
|
171
188
|
return validateScanBundle(data);
|
|
172
189
|
default:
|
|
173
|
-
throw new Error(`Unsupported
|
|
190
|
+
throw new Error(`Unsupported EuConform Evidence Format schemaVersion '${schemaVersion}'`);
|
|
174
191
|
}
|
|
175
192
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { ANNEX_III_QUESTIONS, PROHIBITED_AI_SCREENING_QUESTIONS, classifyAnnexII
|
|
|
8
8
|
export type { AnnexIIIAnswer, AnnexIIIRiskResult, Citation, CounterfactualPair, CrowsPairsLogProbOptions, CrowsPairsLogProbResult, ChecklistStatus, DataGovernanceChecklistResult, RobustnessResult, RobustnessTestCase, Perturbation, PredictTextFn, HumanOversightRecommendation, LoggingTemplate, AnnexIVStructuredReportV1, GroupTextSample, RegardResult, RegardScorer, SensitiveAttribute, ToxicityResult, ToxicityScorer, DeadlineStatus, AffectedAudience, DeadlineObligation, DeadlinePhase, GPAIObligationType, GPAIComplianceLevel, GPAIComplianceFlag, GPAIComplianceResult, GapPriority, GapAction, GapAnalysisResult, } from "./legal-checks";
|
|
9
9
|
export { classifyRisk, getComplianceGuidance, getQuizQuestions, RISK_QUIZ_QUESTIONS, } from "./risk-engine";
|
|
10
10
|
export { disparateImpact, equalizedOdds, generateLoanDataset, predictiveParity, runFairnessAnalysis, statisticalParityDifference, runCrowsPairsBiasAnalysis, runComprehensiveBiasAnalysis, runCrowsPairsBiasAnalysisWithFactory, runComprehensiveBiasAnalysisWithFactory, } from "./fairness-metrics";
|
|
11
|
-
export { detectOllama, listOllamaModels, OllamaClient, BrowserInference, checkWebGPU, BROWSER_MODELS, detectCapabilities, createInferenceClient, createAutoInferenceClient, createCapabilityDetectionService, DefaultCapabilityDetectionService, EnhancedCapabilityDetectionService, type OllamaModel, type BrowserModel, type InferenceEngine, type InferenceCapabilities, type InferenceClient, type CapabilityDetectionService, type ModelCapability, type ModelSelectionState, type CapabilityCacheEntry, type DetectionProgress, type UserPreferences, type EnhancedModelSelectionState, type CapabilityDetectionStatus, } from "./inference";
|
|
11
|
+
export { detectOllama, listOllamaModels, OllamaClient, BrowserInference, checkWebGPU, BROWSER_MODELS, detectCapabilities, createInferenceClient, createAutoInferenceClient, createCapabilityDetectionService, DefaultCapabilityDetectionService, EnhancedCapabilityDetectionService, type OllamaModel, type BrowserModel, type InferenceEngine, type InferenceCapabilities, type InferenceClient, type CapabilityDetectionService, type ModelCapability, type ModelSelectionState, type CapabilityCacheEntry, type DetectionProgress, type UserPreferences, type EnhancedModelSelectionState, type CapabilityDetectionStatus, type CapabilityCache, } from "./inference";
|
|
12
12
|
export { InferenceFactory, createInferenceFactory, detectBestMethodForConfig, type InferenceResult, } from "./inference/inference-factory";
|
|
13
13
|
export { testPair, aggregateByCategory, runStereotypeBiasTest, quickBiasCheck, type StereotypePair, type BiasType, type PairTestResult, type CategoryResult, type StereotypeBiasResult, calculateCrowsPairsBias, calculateLogProbBias, calculateLatencyBias, validateCrowsPairsDataset, } from "./metrics";
|
|
14
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,SAAS,EACT,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,mBAAmB,EACnB,iCAAiC,EACjC,oBAAoB,EACpB,oBAAoB,EACpB,iCAAiC,EACjC,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,kCAAkC,EAClC,8BAA8B,EAC9B,gCAAgC,EAChC,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,mCAAmC,EACnC,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,wBAAwB,EACxB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,QAAQ,EACR,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,eAAe,EACf,6BAA6B,EAC7B,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,4BAA4B,EAC5B,eAAe,EACf,yBAAyB,EACzB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,yBAAyB,EACzB,4BAA4B,EAC5B,oCAAoC,EACpC,uCAAuC,GACxC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,yBAAyB,EACzB,gCAAgC,EAChC,iCAAiC,EACjC,kCAAkC,EAClC,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,aAAa,EACb,cAAc,EACd,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,UAAU,EACV,YAAY,EACZ,cAAc,EACd,QAAQ,EACR,SAAS,EACT,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,mBAAmB,EACnB,iCAAiC,EACjC,oBAAoB,EACpB,oBAAoB,EACpB,iCAAiC,EACjC,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,kCAAkC,EAClC,8BAA8B,EAC9B,gCAAgC,EAChC,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,mCAAmC,EACnC,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,uBAAuB,EACvB,4BAA4B,EAC5B,wBAAwB,EACxB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,eAAe,EACf,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,QAAQ,EACR,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACvB,eAAe,EACf,6BAA6B,EAC7B,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,4BAA4B,EAC5B,eAAe,EACf,yBAAyB,EACzB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,2BAA2B,EAC3B,yBAAyB,EACzB,4BAA4B,EAC5B,oCAAoC,EACpC,uCAAuC,GACxC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,qBAAqB,EACrB,yBAAyB,EACzB,gCAAgC,EAChC,iCAAiC,EACjC,kCAAkC,EAClC,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,eAAe,GACrB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EACL,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,WAAW,CAAC"}
|
|
@@ -35,7 +35,7 @@ export declare function createInferenceClient(engine: InferenceEngine, modelId:
|
|
|
35
35
|
* Auto-select the best available inference client
|
|
36
36
|
*/
|
|
37
37
|
export declare function createAutoInferenceClient(preferredModel?: string): Promise<InferenceClient>;
|
|
38
|
-
export { detectOllama, listOllamaModels, OllamaClient } from "./ollama-client";
|
|
38
|
+
export { detectOllama, listOllamaModels, OllamaClient, type CapabilityCache, } from "./ollama-client";
|
|
39
39
|
export { BrowserInference, checkWebGPU, BROWSER_MODELS } from "./browser-inference";
|
|
40
40
|
export { InferenceFactory, createInferenceFactory, detectBestMethodForConfig, type InferenceResult, } from "./inference-factory";
|
|
41
41
|
export { createCapabilityDetectionService, DefaultCapabilityDetectionService, EnhancedCapabilityDetectionService, type CapabilityDetectionService, type ModelCapability, type ModelSelectionState, type CapabilityCacheEntry, type DetectionProgress, type UserPreferences, type EnhancedModelSelectionState, type CapabilityDetectionStatus, } from "./capability-detection";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inference/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAgB,KAAK,WAAW,EAAkC,MAAM,iBAAiB,CAAC;AAEjG,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,WAAW,EAAE,CAAC;KACvB,CAAC;IACF,OAAO,EAAE;QACP,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,YAAY,EAAE,CAAC;KACxB,CAAC;IACF,WAAW,EAAE,eAAe,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,qBAAqB,CAAC,CA2BzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GACxC,eAAe,CAqCjB;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAejG;AAGD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inference/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAgB,KAAK,WAAW,EAAkC,MAAM,iBAAiB,CAAC;AAEjG,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,WAAW,EAAE,CAAC;KACvB,CAAC;IACF,OAAO,EAAE;QACP,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,YAAY,EAAE,CAAC;KACxB,CAAC;IACF,WAAW,EAAE,eAAe,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C;AAED;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,qBAAqB,CAAC,CA2BzE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GACxC,eAAe,CAqCjB;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAejG;AAGD,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,eAAe,GACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gCAAgC,EAChC,iCAAiC,EACjC,kCAAkC,EAClC,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,GAC/B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/inference/index.js
CHANGED
|
@@ -91,7 +91,7 @@ export async function createAutoInferenceClient(preferredModel) {
|
|
|
91
91
|
return createInferenceClient("demo", "demo-model");
|
|
92
92
|
}
|
|
93
93
|
// Re-export types and utilities
|
|
94
|
-
export { detectOllama, listOllamaModels, OllamaClient } from "./ollama-client";
|
|
94
|
+
export { detectOllama, listOllamaModels, OllamaClient, } from "./ollama-client";
|
|
95
95
|
export { BrowserInference, checkWebGPU, BROWSER_MODELS } from "./browser-inference";
|
|
96
96
|
export { InferenceFactory, createInferenceFactory, detectBestMethodForConfig, } from "./inference-factory";
|
|
97
97
|
export { createCapabilityDetectionService, DefaultCapabilityDetectionService, EnhancedCapabilityDetectionService, } from "./capability-detection";
|
|
@@ -46,6 +46,11 @@ export interface OllamaLogProbsResponse extends OllamaGenerateResponse {
|
|
|
46
46
|
logprob: number;
|
|
47
47
|
}>;
|
|
48
48
|
}
|
|
49
|
+
/** Abstraction for capability caching — localStorage in browser, file-based in CLI */
|
|
50
|
+
export interface CapabilityCache {
|
|
51
|
+
get(key: string): string | null;
|
|
52
|
+
set(key: string, value: string): void;
|
|
53
|
+
}
|
|
49
54
|
/**
|
|
50
55
|
* Check if Ollama is running and accessible
|
|
51
56
|
*/
|
|
@@ -74,7 +79,8 @@ export declare class OllamaClient {
|
|
|
74
79
|
private baseUrl;
|
|
75
80
|
readonly model: string;
|
|
76
81
|
private capabilityCache;
|
|
77
|
-
|
|
82
|
+
private externalCache;
|
|
83
|
+
constructor(model: string, baseUrl?: string, cache?: CapabilityCache);
|
|
78
84
|
private isCloudModel;
|
|
79
85
|
/**
|
|
80
86
|
* Ensures the model is available in Ollama before proceeding
|
|
@@ -91,11 +97,11 @@ export declare class OllamaClient {
|
|
|
91
97
|
*/
|
|
92
98
|
private testLogProbCapability;
|
|
93
99
|
/**
|
|
94
|
-
* Get cached capability result from localStorage
|
|
100
|
+
* Get cached capability result from localStorage or external cache
|
|
95
101
|
*/
|
|
96
102
|
private getCachedCapability;
|
|
97
103
|
/**
|
|
98
|
-
* Cache capability result in localStorage
|
|
104
|
+
* Cache capability result in localStorage or external cache
|
|
99
105
|
*/
|
|
100
106
|
private setCachedCapability;
|
|
101
107
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ollama-client.d.ts","sourceRoot":"","sources":["../../src/inference/ollama-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAwB,MAAM,UAAU,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC5B,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACpE,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAID;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CAcrD;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAc/D;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,sBAAsB,CAAC,CAkBjC;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA+C/E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiChF;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,eAAe,CAAoC;
|
|
1
|
+
{"version":3,"file":"ollama-client.d.ts","sourceRoot":"","sources":["../../src/inference/ollama-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -142,10 +142,12 @@ export class OllamaClient {
|
|
|
142
142
|
baseUrl;
|
|
143
143
|
model;
|
|
144
144
|
capabilityCache;
|
|
145
|
-
|
|
145
|
+
externalCache;
|
|
146
|
+
constructor(model, baseUrl = OLLAMA_BASE_URL, cache) {
|
|
146
147
|
this.baseUrl = baseUrl;
|
|
147
148
|
this.model = model;
|
|
148
149
|
this.capabilityCache = new Map();
|
|
150
|
+
this.externalCache = cache ?? null;
|
|
149
151
|
}
|
|
150
152
|
isCloudModel() {
|
|
151
153
|
return this.model.includes(":cloud");
|
|
@@ -240,29 +242,34 @@ export class OllamaClient {
|
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
244
|
/**
|
|
243
|
-
* Get cached capability result from localStorage
|
|
245
|
+
* Get cached capability result from localStorage or external cache
|
|
244
246
|
*/
|
|
245
247
|
getCachedCapability() {
|
|
246
248
|
try {
|
|
247
249
|
const cacheKey = `ollama_supports_logprobs_${this.model}`;
|
|
248
|
-
|
|
250
|
+
let cached = null;
|
|
251
|
+
if (this.externalCache) {
|
|
252
|
+
cached = this.externalCache.get(cacheKey);
|
|
253
|
+
}
|
|
254
|
+
else if (typeof localStorage !== "undefined") {
|
|
255
|
+
cached = localStorage.getItem(cacheKey);
|
|
256
|
+
}
|
|
249
257
|
if (cached) {
|
|
250
258
|
const capability = JSON.parse(cached);
|
|
251
|
-
// Check if cache is still valid (24 hours)
|
|
252
259
|
const cacheAge = Date.now() - new Date(capability.testedAt).getTime();
|
|
253
|
-
const
|
|
254
|
-
if (cacheAge <
|
|
260
|
+
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
|
|
261
|
+
if (cacheAge < CACHE_TTL) {
|
|
255
262
|
return capability;
|
|
256
263
|
}
|
|
257
264
|
}
|
|
265
|
+
return null;
|
|
258
266
|
}
|
|
259
|
-
catch
|
|
260
|
-
|
|
267
|
+
catch {
|
|
268
|
+
return null;
|
|
261
269
|
}
|
|
262
|
-
return null;
|
|
263
270
|
}
|
|
264
271
|
/**
|
|
265
|
-
* Cache capability result in localStorage
|
|
272
|
+
* Cache capability result in localStorage or external cache
|
|
266
273
|
*/
|
|
267
274
|
setCachedCapability(supports) {
|
|
268
275
|
try {
|
|
@@ -272,7 +279,12 @@ export class OllamaClient {
|
|
|
272
279
|
supportsLogProbs: supports,
|
|
273
280
|
testedAt: new Date().toISOString(),
|
|
274
281
|
};
|
|
275
|
-
|
|
282
|
+
if (this.externalCache) {
|
|
283
|
+
this.externalCache.set(cacheKey, JSON.stringify(capability));
|
|
284
|
+
}
|
|
285
|
+
else if (typeof localStorage !== "undefined") {
|
|
286
|
+
localStorage.setItem(cacheKey, JSON.stringify(capability));
|
|
287
|
+
}
|
|
276
288
|
this.capabilityCache.set(this.model, capability);
|
|
277
289
|
}
|
|
278
290
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@euconform/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"license": "MIT OR EUPL-1.2",
|
|
5
5
|
"author": "Benedikt Hiepler",
|
|
6
6
|
"description": "Core engine for EU AI Act compliance, risk classification, and fairness metrics.",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
8
9
|
"keywords": [
|
|
9
10
|
"eu-ai-act",
|
|
10
11
|
"compliance",
|
|
@@ -28,7 +29,8 @@
|
|
|
28
29
|
"./fairness-metrics": "./src/fairness-metrics.ts",
|
|
29
30
|
"./types": "./src/types.ts",
|
|
30
31
|
"./scanner": "./src/scanner/index.ts",
|
|
31
|
-
"./evidence": "./src/evidence/index.ts"
|
|
32
|
+
"./evidence": "./src/evidence/index.ts",
|
|
33
|
+
"./datasets": "./src/datasets/crows-pairs.ts"
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
34
36
|
"@types/node": "^22.15.30",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import type { CrowsPairsEntry } from "../types";
|
|
5
|
+
|
|
6
|
+
interface CrowsPairsDataset {
|
|
7
|
+
metadata: {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
version: string;
|
|
11
|
+
license: string;
|
|
12
|
+
source: string;
|
|
13
|
+
};
|
|
14
|
+
pairs: Array<{
|
|
15
|
+
id: number;
|
|
16
|
+
stereotype: string;
|
|
17
|
+
antiStereotype: string;
|
|
18
|
+
biasType: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function findDatasetPath(language: "en" | "de"): string {
|
|
23
|
+
const fileName = `crows-pairs-${language}.json`;
|
|
24
|
+
|
|
25
|
+
// Strategy 1: Relative to this source file (works in unbundled tsc builds)
|
|
26
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const fromSource = resolve(currentDir, `../../assets/datasets/${fileName}`);
|
|
28
|
+
if (existsSync(fromSource)) return fromSource;
|
|
29
|
+
|
|
30
|
+
// Strategy 2: Relative to process.cwd() (works in monorepo context)
|
|
31
|
+
const fromCwd = resolve(process.cwd(), `packages/core/assets/datasets/${fileName}`);
|
|
32
|
+
if (existsSync(fromCwd)) return fromCwd;
|
|
33
|
+
|
|
34
|
+
// Strategy 3: Walk up from the entry script to find the monorepo root
|
|
35
|
+
if (process.argv[1]) {
|
|
36
|
+
let dir = dirname(resolve(process.argv[1]));
|
|
37
|
+
for (let i = 0; i < 5; i++) {
|
|
38
|
+
const candidate = resolve(dir, `packages/core/assets/datasets/${fileName}`);
|
|
39
|
+
if (existsSync(candidate)) return candidate;
|
|
40
|
+
dir = dirname(dir);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new Error(
|
|
45
|
+
`CrowS-Pairs dataset (${language}) not found. Ensure packages/core/assets/datasets/ exists.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Load CrowS-Pairs dataset from bundled assets.
|
|
51
|
+
* Converts the structured JSON format to CrowsPairsEntry[] for the bias engine.
|
|
52
|
+
*/
|
|
53
|
+
export function loadCrowsPairsDataset(language: "en" | "de"): CrowsPairsEntry[] {
|
|
54
|
+
const filePath = findDatasetPath(language);
|
|
55
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
56
|
+
const dataset: CrowsPairsDataset = JSON.parse(raw);
|
|
57
|
+
|
|
58
|
+
return dataset.pairs.map((pair) => ({
|
|
59
|
+
id: pair.id,
|
|
60
|
+
sent_more: pair.stereotype,
|
|
61
|
+
sent_less: pair.antiStereotype,
|
|
62
|
+
bias_type: pair.biasType,
|
|
63
|
+
}));
|
|
64
|
+
}
|
package/src/evidence/validate.ts
CHANGED
|
@@ -152,8 +152,89 @@ export function validateCiReport(data: unknown): CiReport {
|
|
|
152
152
|
const VALID_BUNDLE_ROLES = new Set(["report", "aibom", "ci", "summary"]);
|
|
153
153
|
const SHA256_HEX = /^[a-f0-9]{64}$/;
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
interface BundleArtifactFields {
|
|
156
|
+
role: string;
|
|
157
|
+
fileName: string;
|
|
158
|
+
sha256: string;
|
|
159
|
+
required: boolean;
|
|
160
|
+
declaredSchemaVersion: string | undefined;
|
|
161
|
+
mimeType: string | undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function readBundleArtifactFields(
|
|
165
|
+
raw: Record<string, unknown>,
|
|
166
|
+
index: number
|
|
167
|
+
): BundleArtifactFields {
|
|
168
|
+
const scope = "bundle artifact";
|
|
169
|
+
const role = requireField(raw, "role", "string", scope, `artifacts[${index}].role`) as string;
|
|
170
|
+
const fileName = requireField(
|
|
171
|
+
raw,
|
|
172
|
+
"fileName",
|
|
173
|
+
"string",
|
|
174
|
+
scope,
|
|
175
|
+
`artifacts[${index}].fileName`
|
|
176
|
+
) as string;
|
|
177
|
+
const sha256 = requireField(
|
|
178
|
+
raw,
|
|
179
|
+
"sha256",
|
|
180
|
+
"string",
|
|
181
|
+
scope,
|
|
182
|
+
`artifacts[${index}].sha256`
|
|
183
|
+
) as string;
|
|
184
|
+
const required = requireField(
|
|
185
|
+
raw,
|
|
186
|
+
"required",
|
|
187
|
+
"boolean",
|
|
188
|
+
scope,
|
|
189
|
+
`artifacts[${index}].required`
|
|
190
|
+
) as boolean;
|
|
191
|
+
|
|
192
|
+
const declaredSchemaVersion = raw.schemaVersion;
|
|
193
|
+
if (declaredSchemaVersion !== undefined && typeof declaredSchemaVersion !== "string") {
|
|
194
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string schemaVersion`);
|
|
195
|
+
}
|
|
196
|
+
if (raw.mimeType !== undefined && typeof raw.mimeType !== "string") {
|
|
197
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string mimeType`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
role,
|
|
202
|
+
fileName,
|
|
203
|
+
sha256,
|
|
204
|
+
required,
|
|
205
|
+
declaredSchemaVersion: declaredSchemaVersion as string | undefined,
|
|
206
|
+
mimeType: raw.mimeType as string | undefined,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function assertBundleArtifactIntegrity(
|
|
211
|
+
fields: BundleArtifactFields,
|
|
212
|
+
seenRoles: Set<string>,
|
|
213
|
+
seenFiles: Set<string>,
|
|
214
|
+
bundleMajor: string | null
|
|
215
|
+
): void {
|
|
216
|
+
const { role, fileName, sha256, declaredSchemaVersion } = fields;
|
|
217
|
+
|
|
218
|
+
if (!VALID_BUNDLE_ROLES.has(role)) {
|
|
219
|
+
throw new Error(`Invalid bundle artifact: unsupported role '${role}'`);
|
|
220
|
+
}
|
|
221
|
+
if (!SHA256_HEX.test(sha256)) {
|
|
222
|
+
throw new Error(`Invalid bundle artifact: '${fileName}' has invalid sha256`);
|
|
223
|
+
}
|
|
224
|
+
if (seenRoles.has(role)) {
|
|
225
|
+
throw new Error(`Invalid bundle artifact: duplicate role '${role}'`);
|
|
226
|
+
}
|
|
227
|
+
if (seenFiles.has(fileName)) {
|
|
228
|
+
throw new Error(`Invalid bundle artifact: duplicate fileName '${fileName}'`);
|
|
229
|
+
}
|
|
230
|
+
if (declaredSchemaVersion && bundleMajor && majorVersion(declaredSchemaVersion) !== bundleMajor) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`Invalid bundle artifact: '${fileName}' uses schemaVersion '${declaredSchemaVersion}' with a different major than the bundle`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function validateBundleHeader(obj: Record<string, unknown>): void {
|
|
157
238
|
requireSchemaVersion(obj, "euconform.bundle.v1", "bundle");
|
|
158
239
|
requireField(obj, "generatedAt", "string", "bundle");
|
|
159
240
|
|
|
@@ -164,6 +245,11 @@ export function validateScanBundle(data: unknown): ScanBundle {
|
|
|
164
245
|
const target = requireField(obj, "target", "object", "bundle") as Record<string, unknown>;
|
|
165
246
|
requireField(target, "name", "string", "bundle", "target.name");
|
|
166
247
|
requireField(target, "rootPath", "string", "bundle", "target.rootPath");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function validateScanBundle(data: unknown): ScanBundle {
|
|
251
|
+
const obj = assertObject(data, "bundle");
|
|
252
|
+
validateBundleHeader(obj);
|
|
167
253
|
|
|
168
254
|
const artifacts = requireField(obj, "artifacts", "array", "bundle") as unknown[];
|
|
169
255
|
if (artifacts.length === 0) {
|
|
@@ -176,71 +262,15 @@ export function validateScanBundle(data: unknown): ScanBundle {
|
|
|
176
262
|
let hasRequiredReport = false;
|
|
177
263
|
|
|
178
264
|
for (let index = 0; index < artifacts.length; index++) {
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
"role",
|
|
183
|
-
"string",
|
|
184
|
-
"bundle artifact",
|
|
185
|
-
`artifacts[${index}].role`
|
|
186
|
-
) as string;
|
|
187
|
-
const fileName = requireField(
|
|
188
|
-
artifact,
|
|
189
|
-
"fileName",
|
|
190
|
-
"string",
|
|
191
|
-
"bundle artifact",
|
|
192
|
-
`artifacts[${index}].fileName`
|
|
193
|
-
) as string;
|
|
194
|
-
const sha256 = requireField(
|
|
195
|
-
artifact,
|
|
196
|
-
"sha256",
|
|
197
|
-
"string",
|
|
198
|
-
"bundle artifact",
|
|
199
|
-
`artifacts[${index}].sha256`
|
|
200
|
-
) as string;
|
|
201
|
-
const required = requireField(
|
|
202
|
-
artifact,
|
|
203
|
-
"required",
|
|
204
|
-
"boolean",
|
|
205
|
-
"bundle artifact",
|
|
206
|
-
`artifacts[${index}].required`
|
|
207
|
-
) as boolean;
|
|
208
|
-
|
|
209
|
-
if (!VALID_BUNDLE_ROLES.has(role)) {
|
|
210
|
-
throw new Error(`Invalid bundle artifact: unsupported role '${role}'`);
|
|
211
|
-
}
|
|
212
|
-
if (!SHA256_HEX.test(sha256)) {
|
|
213
|
-
throw new Error(`Invalid bundle artifact: '${fileName}' has invalid sha256`);
|
|
214
|
-
}
|
|
215
|
-
if (seenRoles.has(role)) {
|
|
216
|
-
throw new Error(`Invalid bundle artifact: duplicate role '${role}'`);
|
|
217
|
-
}
|
|
218
|
-
if (seenFiles.has(fileName)) {
|
|
219
|
-
throw new Error(`Invalid bundle artifact: duplicate fileName '${fileName}'`);
|
|
220
|
-
}
|
|
221
|
-
seenRoles.add(role);
|
|
222
|
-
seenFiles.add(fileName);
|
|
265
|
+
const raw = assertObject(artifacts[index], "bundle artifact");
|
|
266
|
+
const fields = readBundleArtifactFields(raw, index);
|
|
267
|
+
assertBundleArtifactIntegrity(fields, seenRoles, seenFiles, bundleMajor);
|
|
223
268
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string schemaVersion`);
|
|
227
|
-
}
|
|
228
|
-
if (artifact.mimeType !== undefined && typeof artifact.mimeType !== "string") {
|
|
229
|
-
throw new Error(`Invalid bundle artifact: '${fileName}' has non-string mimeType`);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
declaredSchemaVersion &&
|
|
234
|
-
bundleMajor &&
|
|
235
|
-
majorVersion(declaredSchemaVersion) !== bundleMajor
|
|
236
|
-
) {
|
|
237
|
-
throw new Error(
|
|
238
|
-
`Invalid bundle artifact: '${fileName}' uses schemaVersion '${declaredSchemaVersion}' with a different major than the bundle`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
269
|
+
seenRoles.add(fields.role);
|
|
270
|
+
seenFiles.add(fields.fileName);
|
|
241
271
|
|
|
242
|
-
if (role === "report") {
|
|
243
|
-
hasRequiredReport = required;
|
|
272
|
+
if (fields.role === "report") {
|
|
273
|
+
hasRequiredReport = fields.required;
|
|
244
274
|
}
|
|
245
275
|
}
|
|
246
276
|
|
|
@@ -265,6 +295,6 @@ export function validateEcefJsonDocument(
|
|
|
265
295
|
case "euconform.bundle.v1":
|
|
266
296
|
return validateScanBundle(data);
|
|
267
297
|
default:
|
|
268
|
-
throw new Error(`Unsupported
|
|
298
|
+
throw new Error(`Unsupported EuConform Evidence Format schemaVersion '${schemaVersion}'`);
|
|
269
299
|
}
|
|
270
300
|
}
|