@euconform/core 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +34 -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/types.d.ts +175 -1
  28. package/dist/evidence/types.d.ts.map +1 -1
  29. package/dist/evidence/validate-bias-v2.d.ts +31 -0
  30. package/dist/evidence/validate-bias-v2.d.ts.map +1 -0
  31. package/dist/evidence/validate-bias-v2.js +40 -0
  32. package/dist/evidence/validate.d.ts +29 -2
  33. package/dist/evidence/validate.d.ts.map +1 -1
  34. package/dist/evidence/validate.js +90 -8
  35. package/dist/fairness-metrics.d.ts +15 -11
  36. package/dist/fairness-metrics.d.ts.map +1 -1
  37. package/dist/fairness-metrics.js +59 -183
  38. package/dist/index.d.ts +2 -2
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/inference/browser-inference.d.ts +6 -57
  42. package/dist/inference/browser-inference.d.ts.map +1 -1
  43. package/dist/inference/browser-inference.js +6 -219
  44. package/dist/inference/capability-detection.d.ts +9 -2
  45. package/dist/inference/capability-detection.d.ts.map +1 -1
  46. package/dist/inference/capability-detection.js +18 -16
  47. package/dist/inference/index.d.ts +15 -2
  48. package/dist/inference/index.d.ts.map +1 -1
  49. package/dist/inference/index.js +12 -3
  50. package/dist/inference/inference-factory.d.ts +6 -3
  51. package/dist/inference/inference-factory.d.ts.map +1 -1
  52. package/dist/inference/inference-factory.js +7 -26
  53. package/dist/inference/ollama-client.d.ts +29 -0
  54. package/dist/inference/ollama-client.d.ts.map +1 -1
  55. package/dist/inference/ollama-client.js +29 -0
  56. package/dist/inference/scored-log-prob-types.d.ts +64 -0
  57. package/dist/inference/scored-log-prob-types.d.ts.map +1 -0
  58. package/dist/inference/scored-log-prob-types.js +12 -0
  59. package/dist/inference/sequence-logprob.d.ts +34 -0
  60. package/dist/inference/sequence-logprob.d.ts.map +1 -0
  61. package/dist/inference/sequence-logprob.js +87 -0
  62. package/dist/inference/template-scoring.d.ts +124 -0
  63. package/dist/inference/template-scoring.d.ts.map +1 -0
  64. package/dist/inference/template-scoring.js +164 -0
  65. package/dist/inference/transformers-inference.d.ts +124 -0
  66. package/dist/inference/transformers-inference.d.ts.map +1 -0
  67. package/dist/inference/transformers-inference.js +392 -0
  68. package/dist/legal-checks/bias-metrics.d.ts +5 -7
  69. package/dist/legal-checks/bias-metrics.d.ts.map +1 -1
  70. package/dist/legal-checks/bias-metrics.js +8 -101
  71. package/dist/metrics/crows-pairs-bias.d.ts +17 -9
  72. package/dist/metrics/crows-pairs-bias.d.ts.map +1 -1
  73. package/dist/metrics/crows-pairs-bias.js +147 -59
  74. package/dist/metrics/stereotype-bias.d.ts +13 -5
  75. package/dist/metrics/stereotype-bias.d.ts.map +1 -1
  76. package/dist/metrics/stereotype-bias.js +16 -99
  77. package/dist/scanner/detectors/ai-framework-detector.js +1 -1
  78. package/dist/scanner/detectors/local-inference-detector.d.ts.map +1 -1
  79. package/dist/scanner/detectors/local-inference-detector.js +1 -1
  80. package/dist/scanner/detectors/package-json-detector.d.ts.map +1 -1
  81. package/dist/scanner/detectors/package-json-detector.js +1 -0
  82. package/dist/types.d.ts +103 -2
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/validation/schema-validator.d.ts +1 -1
  85. package/dist/validation/schema-validator.d.ts.map +1 -1
  86. package/dist/validation/schema-validator.js +6 -0
  87. package/package.json +4 -2
  88. package/scripts/seed-logprob-fixture.mjs +179 -0
  89. package/scripts/ts-extensionless-resolve-hook.mjs +26 -0
  90. package/src/datasets/crows-pairs.ts +74 -0
  91. package/src/evidence/bias-comparison-markdown.ts +162 -0
  92. package/src/evidence/bias-comparison.ts +432 -0
  93. package/src/evidence/bias-statistics.ts +180 -0
  94. package/src/evidence/bias-v2-semantics.ts +214 -0
  95. package/src/evidence/browser.ts +35 -0
  96. package/src/evidence/bundle.ts +2 -0
  97. package/src/evidence/index.ts +31 -0
  98. package/src/evidence/types.ts +204 -1
  99. package/src/evidence/validate-bias-v2.ts +47 -0
  100. package/src/evidence/validate.ts +140 -13
  101. package/src/fairness-metrics.ts +79 -222
  102. package/src/index.ts +7 -0
  103. package/src/inference/browser-inference.ts +14 -256
  104. package/src/inference/capability-detection.ts +32 -18
  105. package/src/inference/index.ts +33 -5
  106. package/src/inference/inference-factory.ts +9 -30
  107. package/src/inference/ollama-client.ts +29 -0
  108. package/src/inference/scored-log-prob-types.ts +66 -0
  109. package/src/inference/sequence-logprob.ts +136 -0
  110. package/src/inference/template-scoring.ts +248 -0
  111. package/src/inference/transformers-inference.ts +494 -0
  112. package/src/legal-checks/bias-metrics.ts +20 -118
  113. package/src/metrics/crows-pairs-bias.ts +180 -83
  114. package/src/metrics/stereotype-bias.ts +27 -119
  115. package/src/scanner/detectors/ai-framework-detector.ts +1 -1
  116. package/src/scanner/detectors/local-inference-detector.ts +2 -1
  117. package/src/scanner/detectors/package-json-detector.ts +1 -0
  118. package/src/types.ts +108 -2
  119. package/src/validation/schema-validator.ts +15 -1
  120. package/tests/annex-iv-report.test.ts +1 -1
  121. package/tests/capability-detection.test.ts +57 -0
  122. package/tests/crows-pairs-bias.test.ts +286 -186
  123. package/tests/custom-dataset.test.ts +80 -0
  124. package/tests/evidence/bias-artifact-v2.test.ts +135 -0
  125. package/tests/evidence/bias-artifact.test.ts +78 -0
  126. package/tests/evidence/bias-comparison.test.ts +397 -0
  127. package/tests/evidence/bias-statistics.test.ts +191 -0
  128. package/tests/evidence/external-observation.test.ts +167 -0
  129. package/tests/evidence/spec-schemas.test.ts +35 -2
  130. package/tests/evidence/validate-bias-v2.test.ts +180 -0
  131. package/tests/fairness-metrics.test.ts +77 -1
  132. package/tests/inference/browser-inference-scoring.test.ts +159 -0
  133. package/tests/inference/fixtures/logprob-golden.json +69 -0
  134. package/tests/inference/gemma-template.integration.test.ts +81 -0
  135. package/tests/inference/logprob-golden.integration.test.ts +128 -0
  136. package/tests/inference/synthetic-logits.test.ts +188 -0
  137. package/tests/inference/template-scoring.test.ts +249 -0
  138. package/tests/inference/transformers-inference-template-mode.test.ts +214 -0
  139. package/tests/integration/bias-calculation.integration.test.ts +119 -599
  140. package/tests/stilled-ollama-paths.test.ts +76 -0
  141. package/src/inference/transformers.d.ts +0 -62
@@ -1,258 +1,16 @@
1
1
  /**
2
- * Browser-based inference using transformers.js
3
- * For small models (≤3B params) that can run via WebGPU
4
- *
5
- * STRICT MODE: Real inference only. No demo data.
2
+ * @deprecated Kept for backward compatibility (apps/web, existing tests import
3
+ * `BrowserInference` from this path). The canonical implementation now lives in
4
+ * `transformers-inference.ts` under the name `TransformersInference` — see
5
+ * Messmotor-Sanierung Task 2. New code should import `TransformersInference`
6
+ * from "./transformers-inference" directly.
6
7
  */
7
-
8
- // We need to use 'any' for transformers objects because the types are not fully exported
9
- /* eslint-disable @typescript-eslint/no-explicit-any */
10
-
11
- export interface BrowserModel {
12
- id: string;
13
- name: string;
14
- size: string;
15
- supported: boolean;
16
- }
17
-
18
- // Supported models for browser inference
19
- export const BROWSER_MODELS: BrowserModel[] = [
20
- {
21
- id: "Xenova/phi-3-mini-4k-instruct",
22
- name: "Phi-3 Mini",
23
- size: "3.8B",
24
- supported: true,
25
- },
26
- {
27
- id: "Xenova/Phi-3.5-mini-instruct",
28
- name: "Phi-3.5 Mini",
29
- size: "3.8B",
30
- supported: true,
31
- },
32
- {
33
- id: "Xenova/gpt2",
34
- name: "GPT-2",
35
- size: "124M",
36
- supported: true,
37
- },
38
- {
39
- id: "Xenova/distilgpt2",
40
- name: "DistilGPT-2",
41
- size: "82M",
42
- supported: true,
43
- },
44
- ];
45
-
46
- /**
47
- * Check if WebGPU is available in the browser
48
- */
49
- export async function checkWebGPU(): Promise<boolean> {
50
- if (typeof navigator === "undefined") return false;
51
- if (typeof window === "undefined") return false;
52
-
53
- try {
54
- // Check for WebGPU support
55
- const gpu = (navigator as Navigator & { gpu?: unknown }).gpu;
56
- if (!gpu) return false;
57
- // Try to get adapter
58
- const adapter = await (gpu as { requestAdapter: () => Promise<unknown> }).requestAdapter();
59
- return adapter !== null;
60
- } catch {
61
- return false;
62
- }
63
- }
64
-
65
- /**
66
- * Check if transformers.js is available
67
- */
68
- export async function checkTransformersAvailable(): Promise<boolean> {
69
- try {
70
- // Try dynamic import - this will fail if package not installed
71
- await import("@xenova/transformers");
72
- return true;
73
- } catch {
74
- return false;
75
- }
76
- }
77
-
78
- /**
79
- * Browser inference engine using transformers.js
80
- * STRICT MODE: Throws error if package not installed.
81
- */
82
- export class BrowserInference {
83
- public readonly modelId: string;
84
- private model: unknown = null;
85
- private tokenizer: unknown = null;
86
- private pipeline: unknown = null; // Keep pipeline for generation
87
- private loadingPromise: Promise<void> | null = null;
88
- private loadError: Error | null = null;
89
- private statusCallback: ((status: string) => void) | null = null;
90
-
91
- constructor(
92
- modelId = "Xenova/distilgpt2",
93
- statusCallback: ((status: string) => void) | null = null
94
- ) {
95
- this.modelId = modelId;
96
- this.statusCallback = statusCallback;
97
- }
98
-
99
- /**
100
- * Check if browser inference is available
101
- */
102
- async isAvailable(): Promise<boolean> {
103
- const hasTransformers = await checkTransformersAvailable();
104
- return hasTransformers;
105
- }
106
-
107
- /**
108
- * Load the model (lazy loading)
109
- * Handles concurrency with loadingPromise
110
- */
111
- async load(): Promise<void> {
112
- if (this.model && this.tokenizer) return;
113
- if (this.loadingPromise) return this.loadingPromise;
114
- if (this.loadError) throw this.loadError;
115
-
116
- this.loadingPromise = this.performLoad().finally(() => {
117
- this.loadingPromise = null;
118
- });
119
-
120
- return this.loadingPromise;
121
- }
122
-
123
- private async performLoad() {
124
- if (this.statusCallback) this.statusCallback("loading");
125
-
126
- try {
127
- const { AutoTokenizer, AutoModelForCausalLM, pipeline } = await import(
128
- "@xenova/transformers"
129
- );
130
-
131
- const hasWebGPU = await checkWebGPU();
132
- const device = hasWebGPU ? "webgpu" : "cpu";
133
-
134
- if (this.statusCallback) this.statusCallback("loading-tokenizer");
135
- this.tokenizer = await AutoTokenizer.from_pretrained(this.modelId);
136
-
137
- if (this.statusCallback) this.statusCallback("loading-model");
138
- // biome-ignore lint/suspicious/noExplicitAny: library internal
139
- this.model = await (AutoModelForCausalLM as any).from_pretrained(this.modelId, {
140
- device,
141
- quantized: true,
142
- });
143
-
144
- if (this.statusCallback) this.statusCallback("loading-pipeline");
145
- // biome-ignore lint/suspicious/noExplicitAny: library internal
146
- this.pipeline = await (pipeline as any)("text-generation", this.modelId, {
147
- device,
148
- model: this.model,
149
- tokenizer: this.tokenizer,
150
- });
151
-
152
- if (this.statusCallback) this.statusCallback("ready");
153
- } catch (err) {
154
- this.loadError = err instanceof Error ? err : new Error(String(err));
155
- if (this.statusCallback) this.statusCallback("error");
156
- throw this.loadError;
157
- }
158
- }
159
-
160
- /**
161
- * Generate text
162
- */
163
- async generate(prompt: string, maxTokens = 50): Promise<string> {
164
- await this.load();
165
-
166
- if (!this.pipeline) {
167
- throw new Error("Model not loaded");
168
- }
169
-
170
- // biome-ignore lint/suspicious/noExplicitAny: library internal
171
- const result = await (this.pipeline as any)(prompt, {
172
- max_new_tokens: maxTokens,
173
- temperature: 0.7,
174
- do_sample: true,
175
- });
176
-
177
- return result[0]?.generated_text || "";
178
- }
179
-
180
- /**
181
- * Calculate Log-Probability for a given text
182
- * This is the "Gold Standard" for bias testing.
183
- * We calculate the loss (negative log likelihood) of the text.
184
- */
185
- async getLogProb(text: string): Promise<number> {
186
- await this.load();
187
-
188
- if (!this.tokenizer || !this.model) {
189
- throw new Error("Model not loaded");
190
- }
191
-
192
- // biome-ignore lint/suspicious/noExplicitAny: internal transformers.js types
193
- const inputs = await (this.tokenizer as any)(text, { return_tensors: "pt" });
194
- const inputIds = inputs.input_ids;
195
-
196
- // biome-ignore lint/suspicious/noExplicitAny: internal transformers.js types
197
- const outputs = await (this.model as any)(inputs);
198
- const logits = outputs.logits;
199
-
200
- // 3. Compute Log Likelihood
201
- // We want to calculate P(text) = P(t1) * P(t2|t1) * ...
202
- // LogProb = sum( log( P(ti | t_<i) ) )
203
-
204
- // logits shape: [batch_size, seq_len, vocab_size]
205
- // inputIds shape: [batch_size, seq_len]
206
- // We are interested in the probability of the *next* token given previous tokens.
207
- // So we look at logits[0, i, :] to predict inputIds[0, i+1]
208
-
209
- const data = logits.data; // Float32Array
210
- const dims = logits.dims; // [batch, seq, vocab]
211
- const vocabSize = dims[2];
212
- const seqLen = dims[1];
213
-
214
- let totalLogProb = 0;
215
-
216
- // Helper to get logit value
217
- const getLogit = (seqIdx: number, vocabIdx: number) => {
218
- return data[seqIdx * vocabSize + vocabIdx];
219
- };
220
-
221
- // Helper for softmax/logsoftmax
222
- // We only need the log_prob of the target token
223
- // log_softmax(x_i) = x_i - log(sum(exp(x_j)))
224
- // To avoid overflow: x_i - max(x) - log(sum(exp(x_j - max(x))))
225
-
226
- for (let i = 0; i < seqLen - 1; i++) {
227
- const targetTokenId = Number(inputIds.data[i + 1]);
228
-
229
- // Calculate max logit for numerical stability
230
- let maxLogit = Number.NEGATIVE_INFINITY;
231
- for (let j = 0; j < vocabSize; j++) {
232
- const val = getLogit(i, j);
233
- if (val > maxLogit) maxLogit = val;
234
- }
235
-
236
- // Calculate sum of exp
237
- let sumExp = 0;
238
- for (let j = 0; j < vocabSize; j++) {
239
- sumExp += Math.exp(getLogit(i, j) - maxLogit);
240
- }
241
-
242
- const logSumExp = maxLogit + Math.log(sumExp);
243
- const targetLogit = getLogit(i, targetTokenId);
244
-
245
- const logProb = targetLogit - logSumExp;
246
- totalLogProb += logProb;
247
- }
248
-
249
- return totalLogProb;
250
- }
251
-
252
- /**
253
- * Get list of supported browser models
254
- */
255
- static getSupportedModels(): BrowserModel[] {
256
- return BROWSER_MODELS.filter((m) => m.supported);
257
- }
258
- }
8
+ export {
9
+ TransformersInference as BrowserInference,
10
+ type BrowserModel,
11
+ BROWSER_MODELS,
12
+ checkWebGPU,
13
+ checkTransformersAvailable,
14
+ type ScoredLogProb,
15
+ type ScoredLogProbProvenance,
16
+ } from "./transformers-inference";
@@ -6,9 +6,21 @@
6
6
  import type { BiasCalculationMethod } from "../types";
7
7
 
8
8
  /**
9
- * Detection status for model capabilities
9
+ * Detection status for model capabilities.
10
+ *
11
+ * "unverified": the model is declared supported (e.g. it's in the static
12
+ * BROWSER_MODELS list) but has NOT actually been loaded/tested — no request
13
+ * was made, no capability was measured. Distinct from "available", which
14
+ * means a real detection/measurement round confirmed it. Do not report
15
+ * "available" for a model that was never tested (see Messmotor-Sanierung
16
+ * Task 2): the runtime status is what a consumer reads, not source comments.
10
17
  */
11
- export type CapabilityDetectionStatus = "detecting" | "available" | "unavailable" | "error";
18
+ export type CapabilityDetectionStatus =
19
+ | "detecting"
20
+ | "available"
21
+ | "unverified"
22
+ | "unavailable"
23
+ | "error";
12
24
 
13
25
  /**
14
26
  * Model capability information
@@ -105,15 +117,19 @@ export class DefaultCapabilityDetectionService implements CapabilityDetectionSer
105
117
  // Import browser models dynamically
106
118
  const { BROWSER_MODELS } = await import("./browser-inference");
107
119
 
120
+ // These entries are declared from the static BROWSER_MODELS list, not
121
+ // measured — no model has actually been loaded/tested at this point.
122
+ // status: "unverified" (not "available") reflects that honestly; no
123
+ // lastTested since no test ran. A real per-model verification step is
124
+ // future work, not this task's scope.
108
125
  for (const model of BROWSER_MODELS) {
109
126
  if (!model.supported) continue;
110
127
  capabilities.push({
111
128
  modelId: model.id,
112
129
  engine: "browser",
113
130
  method: "logprobs_exact",
114
- status: "available",
131
+ status: "unverified",
115
132
  recommended: true,
116
- lastTested: new Date().toISOString(),
117
133
  });
118
134
  }
119
135
  } catch (error) {
@@ -123,9 +139,8 @@ export class DefaultCapabilityDetectionService implements CapabilityDetectionSer
123
139
  modelId: "Xenova/distilgpt2",
124
140
  engine: "browser",
125
141
  method: "logprobs_exact",
126
- status: "available",
142
+ status: "unverified",
127
143
  recommended: true,
128
- lastTested: new Date().toISOString(),
129
144
  });
130
145
  }
131
146
 
@@ -189,15 +204,14 @@ export class DefaultCapabilityDetectionService implements CapabilityDetectionSer
189
204
  return cached.capability;
190
205
  }
191
206
 
192
- // Browser inference always supports exact log-probabilities
207
+ // Declared, not measured — no model has been loaded/tested here.
193
208
  if (engine === "browser") {
194
209
  const capability: ModelCapability = {
195
210
  modelId,
196
211
  engine: "browser",
197
212
  method: "logprobs_exact",
198
- status: "available",
213
+ status: "unverified",
199
214
  recommended: true,
200
- lastTested: new Date().toISOString(),
201
215
  };
202
216
 
203
217
  this.setCachedCapability(capability);
@@ -360,7 +374,9 @@ export class DefaultCapabilityDetectionService implements CapabilityDetectionSer
360
374
  * Calculate recommendation score for a capability
361
375
  */
362
376
  protected getRecommendationScore(capability: ModelCapability): number {
363
- if (capability.status !== "available") return 0;
377
+ // "unverified" is a usable, declared-supported model (see the type doc) —
378
+ // only truly broken/unknown states (unavailable, error, detecting) score 0.
379
+ if (capability.status !== "available" && capability.status !== "unverified") return 0;
364
380
 
365
381
  let score = 0;
366
382
 
@@ -398,15 +414,14 @@ export class EnhancedCapabilityDetectionService extends DefaultCapabilityDetecti
398
414
  return cached.capability;
399
415
  }
400
416
 
401
- // Browser inference always supports exact log-probabilities
417
+ // Declared, not measured — no model has been loaded/tested here.
402
418
  if (engine === "browser") {
403
419
  const capability: ModelCapability = {
404
420
  modelId,
405
421
  engine: "browser",
406
422
  method: "logprobs_exact",
407
- status: "available",
423
+ status: "unverified",
408
424
  recommended: true,
409
- lastTested: new Date().toISOString(),
410
425
  };
411
426
 
412
427
  this.setCachedCapability(capability);
@@ -509,7 +524,8 @@ export class EnhancedCapabilityDetectionService extends DefaultCapabilityDetecti
509
524
  async detectAllCapabilities(): Promise<ModelCapability[]> {
510
525
  const capabilities: ModelCapability[] = [];
511
526
 
512
- // Add all supported browser models dynamically
527
+ // Add all supported browser models dynamically. Declared, not measured —
528
+ // no model has actually been loaded/tested at this point.
513
529
  try {
514
530
  const { BROWSER_MODELS } = await import("./browser-inference");
515
531
 
@@ -519,9 +535,8 @@ export class EnhancedCapabilityDetectionService extends DefaultCapabilityDetecti
519
535
  modelId: model.id,
520
536
  engine: "browser",
521
537
  method: "logprobs_exact",
522
- status: "available",
538
+ status: "unverified",
523
539
  recommended: true,
524
- lastTested: new Date().toISOString(),
525
540
  });
526
541
  }
527
542
  } catch (error) {
@@ -531,9 +546,8 @@ export class EnhancedCapabilityDetectionService extends DefaultCapabilityDetecti
531
546
  modelId: "Xenova/distilgpt2",
532
547
  engine: "browser",
533
548
  method: "logprobs_exact",
534
- status: "available",
549
+ status: "unverified",
535
550
  recommended: true,
536
- lastTested: new Date().toISOString(),
537
551
  });
538
552
  }
539
553
 
@@ -3,13 +3,26 @@
3
3
  * Auto-detects available inference engines and provides unified interface
4
4
  */
5
5
 
6
+ import { OllamaClient, type OllamaModel, detectOllama, listOllamaModels } from "./ollama-client";
6
7
  import {
7
8
  BROWSER_MODELS,
8
- BrowserInference,
9
9
  type BrowserModel,
10
+ type ScoredLogProb,
11
+ TransformersInference,
10
12
  checkWebGPU,
11
- } from "./browser-inference";
12
- import { OllamaClient, type OllamaModel, detectOllama, listOllamaModels } from "./ollama-client";
13
+ } from "./transformers-inference";
14
+
15
+ /**
16
+ * The one contract a bias-measurement engine must satisfy: a real, cross-checked
17
+ * sentence log-probability (see Task 1 / Messmotor-Sanierung). `OllamaClient`
18
+ * deliberately does NOT implement this — Ollama's logprobs only cover generated
19
+ * tokens, never prompt/sentence log-probability, so it cannot honestly produce a
20
+ * `ScoredLogProb`. TypeScript's structural typing then locks it out of any API
21
+ * that requires this interface (e.g. `calculateCrowsPairsBias`).
22
+ */
23
+ export interface SequenceLogProbProvider {
24
+ getScoredLogProb(text: string): Promise<ScoredLogProb>;
25
+ }
13
26
 
14
27
  export type InferenceEngine = "ollama" | "browser" | "demo";
15
28
 
@@ -85,7 +98,7 @@ export function createInferenceClient(
85
98
  }
86
99
 
87
100
  case "browser": {
88
- const browserClient = new BrowserInference(modelId, statusCallback);
101
+ const browserClient = new TransformersInference(modelId, statusCallback);
89
102
  return {
90
103
  engine: "browser",
91
104
  modelId,
@@ -138,7 +151,22 @@ export {
138
151
  OllamaClient,
139
152
  type CapabilityCache,
140
153
  } from "./ollama-client";
141
- export { BrowserInference, checkWebGPU, BROWSER_MODELS } from "./browser-inference";
154
+ export {
155
+ TransformersInference,
156
+ checkWebGPU,
157
+ BROWSER_MODELS,
158
+ type ScoredLogProb,
159
+ type ScoredLogProbProvenance,
160
+ } from "./transformers-inference";
161
+ // Architecture detection for the Gemma-4 template-mode scorer, re-exported so callers that
162
+ // must pick a dtype BEFORE constructing `TransformersInference` (e.g. the CLI's
163
+ // `run-bias-test.ts`, which needs to choose "q4" over "fp32" for conditional-generation
164
+ // models — fp32 Gemma-4 weights were measured and rejected as impractically large in
165
+ // Messmotor-Sanierung Task 5) can reuse the same detection `TransformersInference` uses
166
+ // internally, instead of duplicating the architecture check.
167
+ export { isConditionalGenerationConfig, type MinimalModelConfig } from "./template-scoring";
168
+ // Backward-compat alias: existing consumers (apps/web, tests) import `BrowserInference`.
169
+ export { BrowserInference } from "./browser-inference";
142
170
  export {
143
171
  InferenceFactory,
144
172
  createInferenceFactory,
@@ -3,7 +3,6 @@
3
3
  * Provides unified interface for bias calculations across engines with automatic method detection
4
4
  */
5
5
 
6
- import { calculateCrowsPairsBias } from "../metrics/crows-pairs-bias";
7
6
  import type { BiasCalculationMethod, BiasTestConfig, CrowsPairsBiasResult } from "../types";
8
7
  import { BrowserInference } from "./browser-inference";
9
8
  import type { ModelCapability } from "./capability-detection";
@@ -42,38 +41,18 @@ export class InferenceFactory {
42
41
  }
43
42
 
44
43
  /**
45
- * Create a bias calculation with automatic method detection
46
- * Returns InferenceResult with both data and method metadata
44
+ * @deprecated Uses the removed Ollama logprob path: this method auto-selected
45
+ * between browser and Ollama engines via `getInferenceClient`, which no longer
46
+ * measures anything honest for Ollama (see Messmotor-Sanierung Task 2). The
47
+ * auto-selection logic itself needs a real redesign, not a throw-or-convert
48
+ * fix — see bias --engine transformers for the supported path.
47
49
  */
48
50
  async createBiasCalculation(
49
- config: BiasTestConfig
51
+ _config: BiasTestConfig
50
52
  ): Promise<InferenceResult<CrowsPairsBiasResult>> {
51
- const { engine, model } = config;
52
-
53
- this.validateConfiguration(config);
54
-
55
- const inferenceClient = await this.getInferenceClient(engine, model);
56
- let _detectedMethod: BiasCalculationMethod;
57
-
58
- if (engine === "browser") {
59
- _detectedMethod = "logprobs_exact";
60
- } else {
61
- // Use capability method if available, otherwise detect
62
- _detectedMethod =
63
- this.selectedCapability?.method ?? (await this.detectBestMethod(inferenceClient));
64
- }
65
-
66
- // Perform bias calculation
67
- const biasResult = await calculateCrowsPairsBias(config, inferenceClient);
68
-
69
- // Return result with metadata including capability information
70
- return {
71
- data: biasResult,
72
- method: biasResult.method, // Use the method determined during calculation
73
- engine,
74
- model,
75
- capability: this.selectedCapability || undefined,
76
- };
53
+ throw new Error(
54
+ "InferenceFactory.createBiasCalculation verwendet den entfernten Ollama-Logprob-Pfad; siehe bias --engine transformers."
55
+ );
77
56
  }
78
57
 
79
58
  /**
@@ -270,6 +270,9 @@ export class OllamaClient {
270
270
  return response.response;
271
271
  }
272
272
 
273
+ /**
274
+ * @deprecated Not a sentence log-probability — see `getLogProbWithFallback`.
275
+ */
273
276
  async getLogProb(prompt: string): Promise<number> {
274
277
  // Use the enhanced getLogProbWithFallback method that supports both exact and fallback methods
275
278
  const result = await this.getLogProbWithFallback(prompt);
@@ -281,6 +284,12 @@ export class OllamaClient {
281
284
  }
282
285
 
283
286
  /**
287
+ * @deprecated Part of the removed Ollama bias-measurement path (Messmotor-Sanierung
288
+ * Task 2): Ollama's logprobs cover only generated tokens, never the prompt/sentence,
289
+ * so this "capability" check never verified a real measurement. Kept only because
290
+ * `scan`'s non-bias paths and the capability-cache tests still reference it — not
291
+ * a foundation to build on. See bias --engine transformers for the real path.
292
+ *
284
293
  * Test if the current model supports log-probabilities
285
294
  * Sends a test request with "Der" prompt to detect capability
286
295
  */
@@ -405,6 +414,9 @@ export class OllamaClient {
405
414
  }
406
415
 
407
416
  /**
417
+ * @deprecated Part of the removed Ollama bias-measurement path (see
418
+ * `testLogProbCapability`); no longer used by `calculateCrowsPairsBias`.
419
+ *
408
420
  * Detect if the current model supports log-probabilities
409
421
  * Checks cache first, then runs test if needed
410
422
  */
@@ -429,6 +441,10 @@ export class OllamaClient {
429
441
  return supports;
430
442
  }
431
443
 
444
+ /**
445
+ * @deprecated Part of the removed Ollama bias-measurement path; see
446
+ * `testLogProbCapability`. Not a real sentence log-probability.
447
+ */
432
448
  private async tryExactLogProbs(
433
449
  prompt: string
434
450
  ): Promise<{ logprob: number; method: BiasCalculationMethod } | null> {
@@ -477,6 +493,11 @@ export class OllamaClient {
477
493
  }
478
494
  }
479
495
 
496
+ /**
497
+ * @deprecated Part of the removed Ollama bias-measurement path; this is a
498
+ * latency proxy, not a probability — the exact thing Messmotor-Sanierung
499
+ * removes from the bias measurement path. Do not build on this.
500
+ */
480
501
  private async latencyFallback(
481
502
  prompt: string
482
503
  ): Promise<{ logprob: number; method: BiasCalculationMethod }> {
@@ -519,6 +540,14 @@ export class OllamaClient {
519
540
  }
520
541
 
521
542
  /**
543
+ * @deprecated Removed from the bias-measurement path (Messmotor-Sanierung Task 2):
544
+ * Ollama returns logprobs only for generated tokens, never for the prompt/sentence
545
+ * being scored, so this never measured a sentence log-probability — even the
546
+ * "exact" branch (`tryExactLogProbs`) is not what its name implies, and the
547
+ * fallback is a latency proxy. Verified: r(sentence length, logP)=0.019 where a
548
+ * real sentence-probability measure shows −0.96. Use `TransformersInference.
549
+ * getScoredLogProb` (via `bias --engine transformers`) instead.
550
+ *
522
551
  * Get log-probability with automatic fallback to latency method
523
552
  * Returns both the calculated value and the method used
524
553
  */
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Shared result types for `SequenceLogProbProvider.getScoredLogProb`.
3
+ *
4
+ * Pulled out of `transformers-inference.ts` (Messmotor-Sanierung Task 5) so that
5
+ * `template-scoring.ts` (the Gemma-4 template-mode scorer) and `transformers-inference.ts`
6
+ * (which loads models and picks which mode to use) can both depend on these types without
7
+ * either module importing from the other — avoids a circular import between the two.
8
+ *
9
+ * `transformers-inference.ts` re-exports both types from here, so existing imports of
10
+ * `ScoredLogProb`/`ScoredLogProbProvenance` from `"./transformers-inference"` keep working.
11
+ */
12
+
13
+ import type { SequenceLogProbMethod } from "../types";
14
+ import type { Span } from "./sequence-logprob";
15
+
16
+ /** Provenance metadata attached to a scored log-probability result. */
17
+ export interface ScoredLogProbProvenance {
18
+ modelId: string;
19
+ /**
20
+ * The CONFIGURED weight precision passed to `from_pretrained` (e.g. "q4", "fp32", "q8") —
21
+ * NOT the output tensor's reported dtype (see `logitsDtype` for that; the two are
22
+ * different facts and must not be conflated). Task 1 measured 1.3-11.9% score deviation
23
+ * between quantized and fp32 weights on the same model/sentences, so this field is
24
+ * ergebnisrelevant: an artifact that cannot say which weight precision produced a score
25
+ * claims a precision it does not have (Messmotor-Sanierung Task 5 review finding).
26
+ */
27
+ dtype: string;
28
+ /**
29
+ * The OUTPUT LOGITS TENSOR's reported `.type` (e.g. "float32"). The Task 0 spike found
30
+ * this stays "float32" even for q4-quantized weights ("logits.data ist Float32Array trotz
31
+ * q4-Gewichten") — so this field does NOT tell you the weight precision; use `dtype` for
32
+ * that. Kept because it is still a real, independently useful fact (confirms no silent
33
+ * downcast in the output itself).
34
+ */
35
+ logitsDtype: string;
36
+ backend: string;
37
+ libraryVersion: string;
38
+ /**
39
+ * "none" for the base autoregressive mode (Task 1); "fixed_template_span" for the
40
+ * instruction-tuned template mode (Task 5), which conditions scoring on a fixed
41
+ * user-turn prompt. Optional so pre-Task-5 test doubles that predate this field
42
+ * (e.g. hand-built `ScoredLogProb` literals in other packages' tests) stay valid;
43
+ * every real measurement path sets it explicitly.
44
+ */
45
+ templateStrategy?: "none" | "fixed_template_span";
46
+ /** Present only when templateStrategy is "fixed_template_span": the verbatim fixed
47
+ * context string fed before the sentence — recomputable by any third party. */
48
+ promptText?: string;
49
+ /** SHA-256 of `promptText`, present only when templateStrategy is "fixed_template_span". */
50
+ promptSha256?: string;
51
+ }
52
+
53
+ /** Full result of scoring a sentence with `getScoredLogProb`. */
54
+ export interface ScoredLogProb {
55
+ score: number;
56
+ scoredTokenIds: number[];
57
+ span: Span;
58
+ contextTokenHash: string;
59
+ provenance: ScoredLogProbProvenance;
60
+ /**
61
+ * How the score was accumulated (Task 3's `SequenceLogProbMethod`). Optional for the
62
+ * same backward-compatibility reason as `provenance.templateStrategy`; every real
63
+ * measurement path sets it explicitly.
64
+ */
65
+ method?: SequenceLogProbMethod;
66
+ }