@euconform/core 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +46 -0
  3. package/dist/datasets/crows-pairs.d.ts +18 -0
  4. package/dist/datasets/crows-pairs.d.ts.map +1 -1
  5. package/dist/datasets/crows-pairs.js +58 -0
  6. package/dist/evidence/bias-comparison-markdown.d.ts +11 -0
  7. package/dist/evidence/bias-comparison-markdown.d.ts.map +1 -0
  8. package/dist/evidence/bias-comparison-markdown.js +120 -0
  9. package/dist/evidence/bias-comparison.d.ts +133 -0
  10. package/dist/evidence/bias-comparison.d.ts.map +1 -0
  11. package/dist/evidence/bias-comparison.js +274 -0
  12. package/dist/evidence/bias-statistics.d.ts +72 -0
  13. package/dist/evidence/bias-statistics.d.ts.map +1 -0
  14. package/dist/evidence/bias-statistics.js +122 -0
  15. package/dist/evidence/bias-v2-semantics.d.ts +19 -0
  16. package/dist/evidence/bias-v2-semantics.d.ts.map +1 -0
  17. package/dist/evidence/bias-v2-semantics.js +133 -0
  18. package/dist/evidence/browser.d.ts +18 -0
  19. package/dist/evidence/browser.d.ts.map +1 -0
  20. package/dist/evidence/browser.js +14 -0
  21. package/dist/evidence/bundle.d.ts +1 -0
  22. package/dist/evidence/bundle.d.ts.map +1 -1
  23. package/dist/evidence/bundle.js +2 -0
  24. package/dist/evidence/index.d.ts +9 -2
  25. package/dist/evidence/index.d.ts.map +1 -1
  26. package/dist/evidence/index.js +5 -1
  27. package/dist/evidence/output.d.ts.map +1 -1
  28. package/dist/evidence/output.js +1 -0
  29. package/dist/evidence/types.d.ts +183 -3
  30. package/dist/evidence/types.d.ts.map +1 -1
  31. package/dist/evidence/validate-bias-v2.d.ts +31 -0
  32. package/dist/evidence/validate-bias-v2.d.ts.map +1 -0
  33. package/dist/evidence/validate-bias-v2.js +40 -0
  34. package/dist/evidence/validate.d.ts +29 -2
  35. package/dist/evidence/validate.d.ts.map +1 -1
  36. package/dist/evidence/validate.js +96 -11
  37. package/dist/fairness-metrics.d.ts +15 -11
  38. package/dist/fairness-metrics.d.ts.map +1 -1
  39. package/dist/fairness-metrics.js +59 -183
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/inference/browser-inference.d.ts +6 -57
  44. package/dist/inference/browser-inference.d.ts.map +1 -1
  45. package/dist/inference/browser-inference.js +6 -219
  46. package/dist/inference/capability-detection.d.ts +9 -2
  47. package/dist/inference/capability-detection.d.ts.map +1 -1
  48. package/dist/inference/capability-detection.js +18 -16
  49. package/dist/inference/index.d.ts +15 -2
  50. package/dist/inference/index.d.ts.map +1 -1
  51. package/dist/inference/index.js +12 -3
  52. package/dist/inference/inference-factory.d.ts +6 -3
  53. package/dist/inference/inference-factory.d.ts.map +1 -1
  54. package/dist/inference/inference-factory.js +7 -26
  55. package/dist/inference/ollama-client.d.ts +29 -0
  56. package/dist/inference/ollama-client.d.ts.map +1 -1
  57. package/dist/inference/ollama-client.js +29 -0
  58. package/dist/inference/scored-log-prob-types.d.ts +64 -0
  59. package/dist/inference/scored-log-prob-types.d.ts.map +1 -0
  60. package/dist/inference/scored-log-prob-types.js +12 -0
  61. package/dist/inference/sequence-logprob.d.ts +34 -0
  62. package/dist/inference/sequence-logprob.d.ts.map +1 -0
  63. package/dist/inference/sequence-logprob.js +87 -0
  64. package/dist/inference/template-scoring.d.ts +124 -0
  65. package/dist/inference/template-scoring.d.ts.map +1 -0
  66. package/dist/inference/template-scoring.js +164 -0
  67. package/dist/inference/transformers-inference.d.ts +124 -0
  68. package/dist/inference/transformers-inference.d.ts.map +1 -0
  69. package/dist/inference/transformers-inference.js +392 -0
  70. package/dist/legal-checks/bias-metrics.d.ts +5 -7
  71. package/dist/legal-checks/bias-metrics.d.ts.map +1 -1
  72. package/dist/legal-checks/bias-metrics.js +8 -101
  73. package/dist/metrics/crows-pairs-bias.d.ts +17 -9
  74. package/dist/metrics/crows-pairs-bias.d.ts.map +1 -1
  75. package/dist/metrics/crows-pairs-bias.js +147 -59
  76. package/dist/metrics/stereotype-bias.d.ts +13 -5
  77. package/dist/metrics/stereotype-bias.d.ts.map +1 -1
  78. package/dist/metrics/stereotype-bias.js +16 -99
  79. package/dist/sbom/component-mapper.d.ts +26 -0
  80. package/dist/sbom/component-mapper.d.ts.map +1 -0
  81. package/dist/sbom/component-mapper.js +83 -0
  82. package/dist/sbom/cyclonedx-import.d.ts +34 -0
  83. package/dist/sbom/cyclonedx-import.d.ts.map +1 -0
  84. package/dist/sbom/cyclonedx-import.js +131 -0
  85. package/dist/sbom/index.d.ts +6 -0
  86. package/dist/sbom/index.d.ts.map +1 -0
  87. package/dist/sbom/index.js +4 -0
  88. package/dist/sbom/known-packages.d.ts +13 -0
  89. package/dist/sbom/known-packages.d.ts.map +1 -0
  90. package/dist/sbom/known-packages.js +129 -0
  91. package/dist/sbom/purl.d.ts +14 -0
  92. package/dist/sbom/purl.d.ts.map +1 -0
  93. package/dist/sbom/purl.js +56 -0
  94. package/dist/sbom/types.d.ts +39 -0
  95. package/dist/sbom/types.d.ts.map +1 -0
  96. package/dist/sbom/types.js +5 -0
  97. package/dist/scanner/detectors/ai-framework-detector.js +1 -1
  98. package/dist/scanner/detectors/local-inference-detector.d.ts.map +1 -1
  99. package/dist/scanner/detectors/local-inference-detector.js +1 -1
  100. package/dist/scanner/detectors/package-json-detector.d.ts.map +1 -1
  101. package/dist/scanner/detectors/package-json-detector.js +1 -0
  102. package/dist/types.d.ts +103 -2
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/validation/index.d.ts +2 -0
  105. package/dist/validation/index.d.ts.map +1 -0
  106. package/dist/validation/index.js +1 -0
  107. package/dist/validation/schema-validator.d.ts +18 -0
  108. package/dist/validation/schema-validator.d.ts.map +1 -0
  109. package/dist/validation/schema-validator.js +105 -0
  110. package/package.json +9 -3
  111. package/scripts/seed-logprob-fixture.mjs +179 -0
  112. package/scripts/ts-extensionless-resolve-hook.mjs +26 -0
  113. package/src/datasets/crows-pairs.ts +74 -0
  114. package/src/evidence/bias-comparison-markdown.ts +162 -0
  115. package/src/evidence/bias-comparison.ts +432 -0
  116. package/src/evidence/bias-statistics.ts +180 -0
  117. package/src/evidence/bias-v2-semantics.ts +214 -0
  118. package/src/evidence/browser.ts +35 -0
  119. package/src/evidence/bundle.ts +2 -0
  120. package/src/evidence/index.ts +31 -0
  121. package/src/evidence/output.ts +1 -0
  122. package/src/evidence/types.ts +213 -3
  123. package/src/evidence/validate-bias-v2.ts +47 -0
  124. package/src/evidence/validate.ts +151 -17
  125. package/src/fairness-metrics.ts +79 -222
  126. package/src/index.ts +7 -0
  127. package/src/inference/browser-inference.ts +14 -256
  128. package/src/inference/capability-detection.ts +32 -18
  129. package/src/inference/index.ts +33 -5
  130. package/src/inference/inference-factory.ts +9 -30
  131. package/src/inference/ollama-client.ts +29 -0
  132. package/src/inference/scored-log-prob-types.ts +66 -0
  133. package/src/inference/sequence-logprob.ts +136 -0
  134. package/src/inference/template-scoring.ts +248 -0
  135. package/src/inference/transformers-inference.ts +494 -0
  136. package/src/legal-checks/bias-metrics.ts +20 -118
  137. package/src/metrics/crows-pairs-bias.ts +180 -83
  138. package/src/metrics/stereotype-bias.ts +27 -119
  139. package/src/sbom/component-mapper.ts +107 -0
  140. package/src/sbom/cyclonedx-import.ts +198 -0
  141. package/src/sbom/index.ts +12 -0
  142. package/src/sbom/known-packages.ts +142 -0
  143. package/src/sbom/purl.ts +66 -0
  144. package/src/sbom/types.ts +34 -0
  145. package/src/scanner/detectors/ai-framework-detector.ts +1 -1
  146. package/src/scanner/detectors/local-inference-detector.ts +2 -1
  147. package/src/scanner/detectors/package-json-detector.ts +1 -0
  148. package/src/types.ts +108 -2
  149. package/src/validation/index.ts +7 -0
  150. package/src/validation/schema-validator.ts +147 -0
  151. package/tests/annex-iv-report.test.ts +1 -1
  152. package/tests/capability-detection.test.ts +57 -0
  153. package/tests/crows-pairs-bias.test.ts +286 -186
  154. package/tests/custom-dataset.test.ts +80 -0
  155. package/tests/evidence/bias-artifact-v2.test.ts +135 -0
  156. package/tests/evidence/bias-artifact.test.ts +78 -0
  157. package/tests/evidence/bias-comparison.test.ts +397 -0
  158. package/tests/evidence/bias-statistics.test.ts +191 -0
  159. package/tests/evidence/external-observation.test.ts +167 -0
  160. package/tests/evidence/spec-schemas.test.ts +35 -2
  161. package/tests/evidence/validate-bias-v2.test.ts +180 -0
  162. package/tests/fairness-metrics.test.ts +77 -1
  163. package/tests/fixtures/cyclonedx-minimal.json +13 -0
  164. package/tests/fixtures/cyclonedx-mixed.json +17 -0
  165. package/tests/fixtures/cyclonedx-ml-bom.json +25 -0
  166. package/tests/inference/browser-inference-scoring.test.ts +159 -0
  167. package/tests/inference/fixtures/logprob-golden.json +69 -0
  168. package/tests/inference/gemma-template.integration.test.ts +81 -0
  169. package/tests/inference/logprob-golden.integration.test.ts +128 -0
  170. package/tests/inference/synthetic-logits.test.ts +188 -0
  171. package/tests/inference/template-scoring.test.ts +249 -0
  172. package/tests/inference/transformers-inference-template-mode.test.ts +214 -0
  173. package/tests/integration/bias-calculation.integration.test.ts +119 -599
  174. package/tests/sbom/component-mapper.test.ts +258 -0
  175. package/tests/sbom/cyclonedx-import.test.ts +329 -0
  176. package/tests/sbom/purl.test.ts +102 -0
  177. package/tests/stilled-ollama-paths.test.ts +76 -0
  178. package/tests/validation/schema-validator.test.ts +219 -0
  179. package/src/inference/transformers.d.ts +0 -62
@@ -0,0 +1,147 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import addFormats from "ajv-formats";
5
+ import Ajv2020 from "ajv/dist/2020";
6
+
7
+ export interface ValidationError {
8
+ path: string;
9
+ message: string;
10
+ keyword: string;
11
+ expected?: unknown;
12
+ received?: unknown;
13
+ }
14
+
15
+ export type SchemaType =
16
+ | "report.v1"
17
+ | "aibom.v1"
18
+ | "aibom.v1.1"
19
+ | "ci.v1"
20
+ | "bundle.v1"
21
+ | "bias.v1"
22
+ | "bias.v2"
23
+ | "external-observation.v1";
24
+
25
+ export interface ValidationResult {
26
+ valid: boolean;
27
+ schemaType: SchemaType;
28
+ errors: ValidationError[];
29
+ }
30
+
31
+ export interface ValidateOptions {
32
+ strict?: boolean;
33
+ }
34
+
35
+ // Resolve schema directory relative to this file at runtime.
36
+ // When bundled into packages/cli/dist, schemas are copied to dist/schemas/.
37
+ // When running from source at packages/core/src/validation/, schemas are at ../../../../docs/spec/schemas.
38
+ const __filename = fileURLToPath(import.meta.url);
39
+ const __dirname = dirname(__filename);
40
+ const _bundledSchemasDir = resolve(__dirname, "schemas");
41
+ const schemasDir = existsSync(_bundledSchemasDir)
42
+ ? _bundledSchemasDir
43
+ : resolve(__dirname, "../../../../docs/spec/schemas");
44
+
45
+ function loadSchema(filename: string): object {
46
+ const content = readFileSync(resolve(schemasDir, filename), "utf-8");
47
+ return JSON.parse(content) as object;
48
+ }
49
+
50
+ const SCHEMA_MAP: Record<string, { schemaFile: string; type: SchemaType }> = {
51
+ "euconform.report.v1": { schemaFile: "report-v1.schema.json", type: "report.v1" },
52
+ "euconform.aibom.v1": { schemaFile: "aibom-v1.schema.json", type: "aibom.v1" },
53
+ "euconform.aibom.v1.1": { schemaFile: "aibom-v1.1.schema.json", type: "aibom.v1.1" },
54
+ "euconform.ci.v1": { schemaFile: "ci-v1.schema.json", type: "ci.v1" },
55
+ "euconform.bundle.v1": { schemaFile: "bundle-v1.schema.json", type: "bundle.v1" },
56
+ "euconform.bias.v1": { schemaFile: "bias-v1.schema.json", type: "bias.v1" },
57
+ "euconform.bias.v2": { schemaFile: "bias-v2.schema.json", type: "bias.v2" },
58
+ "euconform.external-observation.v1": {
59
+ schemaFile: "external-observation-v1.schema.json",
60
+ type: "external-observation.v1",
61
+ },
62
+ };
63
+
64
+ let ajvInstance: Ajv2020 | null = null;
65
+
66
+ function getAjv(): Ajv2020 {
67
+ if (!ajvInstance) {
68
+ ajvInstance = new Ajv2020({ allErrors: true, strict: false });
69
+ addFormats(ajvInstance);
70
+ }
71
+ return ajvInstance;
72
+ }
73
+
74
+ export function validate(data: unknown, _options?: ValidateOptions): ValidationResult {
75
+ if (typeof data !== "object" || data === null) {
76
+ return {
77
+ valid: false,
78
+ schemaType: "report.v1",
79
+ errors: [
80
+ {
81
+ path: "",
82
+ message: "Input must be a JSON object",
83
+ keyword: "type",
84
+ expected: "object",
85
+ received: typeof data,
86
+ },
87
+ ],
88
+ };
89
+ }
90
+
91
+ const record = data as Record<string, unknown>;
92
+ const schemaVersion = record.schemaVersion;
93
+
94
+ if (typeof schemaVersion !== "string") {
95
+ return {
96
+ valid: false,
97
+ schemaType: "report.v1",
98
+ errors: [
99
+ {
100
+ path: "",
101
+ message: `Missing or invalid schemaVersion field. Expected one of: ${Object.keys(SCHEMA_MAP).join(", ")}`,
102
+ keyword: "required",
103
+ },
104
+ ],
105
+ };
106
+ }
107
+
108
+ const entry = SCHEMA_MAP[schemaVersion];
109
+ if (!entry) {
110
+ return {
111
+ valid: false,
112
+ schemaType: "report.v1",
113
+ errors: [
114
+ {
115
+ path: "/schemaVersion",
116
+ message: `Unknown schemaVersion "${schemaVersion}". Expected one of: ${Object.keys(SCHEMA_MAP).join(", ")}`,
117
+ keyword: "const",
118
+ expected: Object.keys(SCHEMA_MAP),
119
+ received: schemaVersion,
120
+ },
121
+ ],
122
+ };
123
+ }
124
+
125
+ const ajv = getAjv();
126
+ let validateFn = ajv.getSchema(schemaVersion);
127
+ if (!validateFn) {
128
+ const schema = loadSchema(entry.schemaFile);
129
+ validateFn = ajv.compile({ ...schema, $id: schemaVersion });
130
+ }
131
+
132
+ const valid = validateFn(data) as boolean;
133
+
134
+ if (valid) {
135
+ return { valid: true, schemaType: entry.type, errors: [] };
136
+ }
137
+
138
+ const errors: ValidationError[] = (validateFn.errors ?? []).map((err) => ({
139
+ path: err.instancePath ?? "",
140
+ message: err.message ?? "Validation error",
141
+ keyword: err.keyword ?? "unknown",
142
+ expected: err.params,
143
+ received: undefined,
144
+ }));
145
+
146
+ return { valid: false, schemaType: entry.type, errors };
147
+ }
@@ -78,7 +78,7 @@ describe("Annex IV Report Generation", () => {
78
78
  engine: "browser",
79
79
  crowsPairsResult: {
80
80
  score: 0.15,
81
- method: "logprobs_exact",
81
+ method: "sequence_logprob_sum",
82
82
  pairsAnalyzed: 100,
83
83
  stereotypicalPreference: 65.5,
84
84
  metadata: {
@@ -0,0 +1,57 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ DefaultCapabilityDetectionService,
4
+ EnhancedCapabilityDetectionService,
5
+ } from "../src/inference/capability-detection";
6
+
7
+ /**
8
+ * Browser/transformers.js capability entries are declared from a static model
9
+ * list — no model is actually loaded or tested to produce them. Reporting
10
+ * `status: "available"` with a fresh `lastTested` for that would claim a test
11
+ * ran when it didn't (see Messmotor-Sanierung Task 2). This pins the honest
12
+ * runtime values: `status: "unverified"` and no `lastTested`.
13
+ */
14
+ describe("capability detection: browser/transformers entries are not overclaimed", () => {
15
+ it("DefaultCapabilityDetectionService.detectModelCapability reports 'unverified', not 'available', for browser", async () => {
16
+ const service = new DefaultCapabilityDetectionService();
17
+ const capability = await service.detectModelCapability("Xenova/distilgpt2", "browser");
18
+
19
+ expect(capability.status).toBe("unverified");
20
+ expect(capability.lastTested).toBeUndefined();
21
+ // The declared method may still be reported (renaming is Task 3's job) —
22
+ // it just must not be dressed up as measured.
23
+ expect(capability.method).toBe("logprobs_exact");
24
+ });
25
+
26
+ it("EnhancedCapabilityDetectionService.detectModelCapability reports 'unverified', not 'available', for browser", async () => {
27
+ const service = new EnhancedCapabilityDetectionService();
28
+ const capability = await service.detectModelCapability("Xenova/distilgpt2", "browser");
29
+
30
+ expect(capability.status).toBe("unverified");
31
+ expect(capability.lastTested).toBeUndefined();
32
+ });
33
+
34
+ it("'unverified' is not scored as 0 by the recommendation ranking (regression guard)", async () => {
35
+ // Exercise the same code path detectAllCapabilities uses, without touching
36
+ // the network (no live Ollama in CI): detect a single browser capability
37
+ // and confirm it sorts ahead of an explicitly zero-scored "error" entry.
38
+ const service = new DefaultCapabilityDetectionService();
39
+ const browserCapability = await service.detectModelCapability("Xenova/distilgpt2", "browser");
40
+ const errorCapability = {
41
+ modelId: "broken-model",
42
+ engine: "ollama" as const,
43
+ method: "logprobs_fallback_latency" as const,
44
+ status: "error" as const,
45
+ lastTested: new Date().toISOString(),
46
+ };
47
+
48
+ // sortByRecommendation is protected; reach it through the public
49
+ // detectAllCapabilities-equivalent ordering contract by calling the same
50
+ // scoring indirectly via Array#sort with the class's own comparator is not
51
+ // exposed, so assert the documented invariant directly instead: an
52
+ // "unverified" capability must score above a definitionally-zero one.
53
+ // biome-ignore lint/suspicious/noExplicitAny: reaching a protected method for a focused unit check
54
+ const score = (service as any).getRecommendationScore.bind(service);
55
+ expect(score(browserCapability)).toBeGreaterThan(score(errorCapability));
56
+ });
57
+ });