@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.
@@ -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;AAKD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAiG5D;AAED,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,OAAO,GACZ,UAAU,GAAG,iBAAiB,GAAG,QAAQ,GAAG,UAAU,CAaxD"}
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
- export function validateScanBundle(data) {
104
- const obj = assertObject(data, "bundle");
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 artifact = assertObject(artifacts[index], "bundle artifact");
123
- const role = requireField(artifact, "role", "string", "bundle artifact", `artifacts[${index}].role`);
124
- const fileName = requireField(artifact, "fileName", "string", "bundle artifact", `artifacts[${index}].fileName`);
125
- const sha256 = requireField(artifact, "sha256", "string", "bundle artifact", `artifacts[${index}].sha256`);
126
- const required = requireField(artifact, "required", "boolean", "bundle artifact", `artifacts[${index}].required`);
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
- 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 ECEF schemaVersion '${schemaVersion}'`);
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
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,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/E,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"}
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"}
@@ -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
- constructor(model: string, baseUrl?: string);
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":"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"}
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
- constructor(model, baseUrl = OLLAMA_BASE_URL) {
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
- const cached = localStorage.getItem(cacheKey);
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 maxAge = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
254
- if (cacheAge < maxAge) {
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 (error) {
260
- console.warn("Failed to read capability cache:", error);
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
- localStorage.setItem(cacheKey, JSON.stringify(capability));
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.0",
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
+ }
@@ -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
- export function validateScanBundle(data: unknown): ScanBundle {
156
- const obj = assertObject(data, "bundle");
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 artifact = assertObject(artifacts[index], "bundle artifact");
180
- const role = requireField(
181
- artifact,
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
- const declaredSchemaVersion = artifact.schemaVersion;
225
- if (declaredSchemaVersion !== undefined && typeof declaredSchemaVersion !== "string") {
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 ECEF schemaVersion '${schemaVersion}'`);
298
+ throw new Error(`Unsupported EuConform Evidence Format schemaVersion '${schemaVersion}'`);
269
299
  }
270
300
  }
package/src/index.ts CHANGED
@@ -147,6 +147,7 @@ export {
147
147
  type UserPreferences,
148
148
  type EnhancedModelSelectionState,
149
149
  type CapabilityDetectionStatus,
150
+ type CapabilityCache,
150
151
  } from "./inference";
151
152
 
152
153
  // Inference Factory