@euconform/cli 1.2.0 → 1.3.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.
@@ -1,186 +0,0 @@
1
- import {
2
- BrowserInference
3
- } from "./chunk-FEBJZMWY.js";
4
- import {
5
- OllamaClient
6
- } from "./chunk-JQTB43B2.js";
7
- import {
8
- formatBiasSeverity
9
- } from "./chunk-AYXSWY3I.js";
10
-
11
- // ../core/src/metrics/crows-pairs-bias.ts
12
- async function calculateCrowsPairsBias(config, inferenceClient) {
13
- const { dataset, model, engine } = config;
14
- if (dataset.length === 0) {
15
- throw new Error("Dataset cannot be empty");
16
- }
17
- const method = await determineCalculationMethod(engine, inferenceClient);
18
- const { biasScores, stereotypicalCount, finalMethod } = await processDataset(
19
- dataset,
20
- engine,
21
- inferenceClient,
22
- method
23
- );
24
- if (biasScores.length === 0) {
25
- throw new Error("No valid bias scores calculated from dataset");
26
- }
27
- const overallBiasScore = biasScores.reduce((sum, score) => sum + score, 0) / biasScores.length;
28
- const stereotypicalPreference = stereotypicalCount / biasScores.length * 100;
29
- return {
30
- score: Number(overallBiasScore.toFixed(6)),
31
- method: finalMethod,
32
- pairsAnalyzed: biasScores.length,
33
- stereotypicalPreference: Number(stereotypicalPreference.toFixed(2)),
34
- metadata: {
35
- engine,
36
- model,
37
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
38
- }
39
- };
40
- }
41
- async function determineCalculationMethod(engine, inferenceClient) {
42
- if (engine === "browser") return "logprobs_exact";
43
- if (engine === "ollama" && inferenceClient instanceof OllamaClient) {
44
- const supportsLogProbs = await inferenceClient.detectLogProbSupport();
45
- return supportsLogProbs ? "logprobs_exact" : "logprobs_fallback_latency";
46
- }
47
- throw new Error(`Unsupported engine: ${engine}`);
48
- }
49
- async function processDataset(dataset, engine, inferenceClient, initialMethod) {
50
- const biasScores = [];
51
- let stereotypicalCount = 0;
52
- let finalMethod = initialMethod;
53
- for (const entry of dataset) {
54
- try {
55
- const pair = extractSentences(entry);
56
- if (!pair) continue;
57
- const { logprobStereo, logprobAnti, actualMethod } = await getLogProbsForPair(
58
- pair,
59
- engine,
60
- inferenceClient
61
- );
62
- if (actualMethod) finalMethod = actualMethod;
63
- const pairBias = logprobStereo - logprobAnti;
64
- biasScores.push(pairBias);
65
- if (pairBias > 0) stereotypicalCount++;
66
- } catch (error) {
67
- console.warn("Failed to process entry:", error);
68
- }
69
- }
70
- return { biasScores, stereotypicalCount, finalMethod };
71
- }
72
- function extractSentences(entry) {
73
- const stereo = entry.sent_more || entry.stereotype;
74
- const anti = entry.sent_less || entry.antiStereotype;
75
- if (!stereo || !anti) return null;
76
- return { stereo, anti };
77
- }
78
- async function getLogProbsForPair(pair, engine, inferenceClient) {
79
- if (engine === "browser" && inferenceClient instanceof BrowserInference) {
80
- const logprobStereo = await inferenceClient.getLogProb(pair.stereo);
81
- const logprobAnti = await inferenceClient.getLogProb(pair.anti);
82
- return { logprobStereo, logprobAnti };
83
- }
84
- if (engine === "ollama" && inferenceClient instanceof OllamaClient) {
85
- const stereoResult = await inferenceClient.getLogProbWithFallback(pair.stereo);
86
- const antiResult = await inferenceClient.getLogProbWithFallback(pair.anti);
87
- return {
88
- logprobStereo: stereoResult.logprob,
89
- logprobAnti: antiResult.logprob,
90
- actualMethod: stereoResult.method
91
- };
92
- }
93
- throw new Error("Invalid inference client for engine");
94
- }
95
-
96
- // ../core/src/datasets/crows-pairs.ts
97
- import { existsSync, readFileSync } from "fs";
98
- import { dirname, resolve } from "path";
99
- import { fileURLToPath } from "url";
100
- function findDatasetPath(language) {
101
- const fileName = `crows-pairs-${language}.json`;
102
- const currentDir = dirname(fileURLToPath(import.meta.url));
103
- const fromSource = resolve(currentDir, `../../assets/datasets/${fileName}`);
104
- if (existsSync(fromSource)) return fromSource;
105
- const fromCwd = resolve(process.cwd(), `packages/core/assets/datasets/${fileName}`);
106
- if (existsSync(fromCwd)) return fromCwd;
107
- if (process.argv[1]) {
108
- let dir = dirname(resolve(process.argv[1]));
109
- for (let i = 0; i < 5; i++) {
110
- const candidate = resolve(dir, `packages/core/assets/datasets/${fileName}`);
111
- if (existsSync(candidate)) return candidate;
112
- dir = dirname(dir);
113
- }
114
- }
115
- throw new Error(
116
- `CrowS-Pairs dataset (${language}) not found. Ensure packages/core/assets/datasets/ exists.`
117
- );
118
- }
119
- function loadCrowsPairsDataset(language) {
120
- const filePath = findDatasetPath(language);
121
- const raw = readFileSync(filePath, "utf-8");
122
- const dataset = JSON.parse(raw);
123
- return dataset.pairs.map((pair) => ({
124
- id: pair.id,
125
- sent_more: pair.stereotype,
126
- sent_less: pair.antiStereotype,
127
- bias_type: pair.biasType
128
- }));
129
- }
130
-
131
- // src/bias/run-bias-test.ts
132
- import consola from "consola";
133
-
134
- // src/bias/cache.ts
135
- import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
136
- import { homedir } from "os";
137
- import { join } from "path";
138
- var CACHE_DIR = join(homedir(), ".euconform", "cache");
139
- var CACHE_FILE = join(CACHE_DIR, "ollama-capabilities.json");
140
- var _cache = null;
141
- function loadCache() {
142
- if (_cache !== null) return _cache;
143
- try {
144
- _cache = JSON.parse(readFileSync2(CACHE_FILE, "utf-8")) ?? {};
145
- } catch {
146
- _cache = {};
147
- }
148
- return _cache;
149
- }
150
- function persistCache() {
151
- mkdirSync(CACHE_DIR, { recursive: true });
152
- writeFileSync(CACHE_FILE, JSON.stringify(_cache, null, 2));
153
- }
154
- var fileCache = {
155
- get(key) {
156
- return loadCache()[key] ?? null;
157
- },
158
- set(key, value) {
159
- loadCache()[key] = value;
160
- persistCache();
161
- }
162
- };
163
-
164
- // src/bias/run-bias-test.ts
165
- async function runBiasTest(options) {
166
- const { model, lang, url } = options;
167
- const baseUrl = url ?? "http://localhost:11434";
168
- const client = new OllamaClient(model, baseUrl, fileCache);
169
- consola.start(`Verifying model '${model}' is available on ${baseUrl}...`);
170
- await client.ensureModelLoaded();
171
- consola.success(`Model '${model}' is loaded`);
172
- consola.start(`Loading CrowS-Pairs dataset (${lang})...`);
173
- const dataset = loadCrowsPairsDataset(lang);
174
- consola.success(`Loaded ${dataset.length} sentence pairs`);
175
- consola.start(`Running CrowS-Pairs bias test on '${model}'...`);
176
- const result = await calculateCrowsPairsBias({ dataset, model, engine: "ollama" }, client);
177
- const severity = formatBiasSeverity(result.score);
178
- consola.success(
179
- `Bias test complete: score=${result.score.toFixed(4)} (${severity}), method=${result.method}, pairs=${result.pairsAnalyzed}`
180
- );
181
- return result;
182
- }
183
-
184
- export {
185
- runBiasTest
186
- };
@@ -1,175 +0,0 @@
1
- // ../core/src/inference/browser-inference.ts
2
- var BROWSER_MODELS = [
3
- {
4
- id: "Xenova/phi-3-mini-4k-instruct",
5
- name: "Phi-3 Mini",
6
- size: "3.8B",
7
- supported: true
8
- },
9
- {
10
- id: "Xenova/Phi-3.5-mini-instruct",
11
- name: "Phi-3.5 Mini",
12
- size: "3.8B",
13
- supported: true
14
- },
15
- {
16
- id: "Xenova/gpt2",
17
- name: "GPT-2",
18
- size: "124M",
19
- supported: true
20
- },
21
- {
22
- id: "Xenova/distilgpt2",
23
- name: "DistilGPT-2",
24
- size: "82M",
25
- supported: true
26
- }
27
- ];
28
- async function checkWebGPU() {
29
- if (typeof navigator === "undefined") return false;
30
- if (typeof window === "undefined") return false;
31
- try {
32
- const gpu = navigator.gpu;
33
- if (!gpu) return false;
34
- const adapter = await gpu.requestAdapter();
35
- return adapter !== null;
36
- } catch {
37
- return false;
38
- }
39
- }
40
- async function checkTransformersAvailable() {
41
- try {
42
- await import("@xenova/transformers");
43
- return true;
44
- } catch {
45
- return false;
46
- }
47
- }
48
- var BrowserInference = class {
49
- modelId;
50
- model = null;
51
- tokenizer = null;
52
- pipeline = null;
53
- // Keep pipeline for generation
54
- loadingPromise = null;
55
- loadError = null;
56
- statusCallback = null;
57
- constructor(modelId = "Xenova/distilgpt2", statusCallback = null) {
58
- this.modelId = modelId;
59
- this.statusCallback = statusCallback;
60
- }
61
- /**
62
- * Check if browser inference is available
63
- */
64
- async isAvailable() {
65
- const hasTransformers = await checkTransformersAvailable();
66
- return hasTransformers;
67
- }
68
- /**
69
- * Load the model (lazy loading)
70
- * Handles concurrency with loadingPromise
71
- */
72
- async load() {
73
- if (this.model && this.tokenizer) return;
74
- if (this.loadingPromise) return this.loadingPromise;
75
- if (this.loadError) throw this.loadError;
76
- this.loadingPromise = this.performLoad().finally(() => {
77
- this.loadingPromise = null;
78
- });
79
- return this.loadingPromise;
80
- }
81
- async performLoad() {
82
- if (this.statusCallback) this.statusCallback("loading");
83
- try {
84
- const { AutoTokenizer, AutoModelForCausalLM, pipeline } = await import("@xenova/transformers");
85
- const hasWebGPU = await checkWebGPU();
86
- const device = hasWebGPU ? "webgpu" : "cpu";
87
- if (this.statusCallback) this.statusCallback("loading-tokenizer");
88
- this.tokenizer = await AutoTokenizer.from_pretrained(this.modelId);
89
- if (this.statusCallback) this.statusCallback("loading-model");
90
- this.model = await AutoModelForCausalLM.from_pretrained(this.modelId, {
91
- device,
92
- quantized: true
93
- });
94
- if (this.statusCallback) this.statusCallback("loading-pipeline");
95
- this.pipeline = await pipeline("text-generation", this.modelId, {
96
- device,
97
- model: this.model,
98
- tokenizer: this.tokenizer
99
- });
100
- if (this.statusCallback) this.statusCallback("ready");
101
- } catch (err) {
102
- this.loadError = err instanceof Error ? err : new Error(String(err));
103
- if (this.statusCallback) this.statusCallback("error");
104
- throw this.loadError;
105
- }
106
- }
107
- /**
108
- * Generate text
109
- */
110
- async generate(prompt, maxTokens = 50) {
111
- await this.load();
112
- if (!this.pipeline) {
113
- throw new Error("Model not loaded");
114
- }
115
- const result = await this.pipeline(prompt, {
116
- max_new_tokens: maxTokens,
117
- temperature: 0.7,
118
- do_sample: true
119
- });
120
- return result[0]?.generated_text || "";
121
- }
122
- /**
123
- * Calculate Log-Probability for a given text
124
- * This is the "Gold Standard" for bias testing.
125
- * We calculate the loss (negative log likelihood) of the text.
126
- */
127
- async getLogProb(text) {
128
- await this.load();
129
- if (!this.tokenizer || !this.model) {
130
- throw new Error("Model not loaded");
131
- }
132
- const inputs = await this.tokenizer(text, { return_tensors: "pt" });
133
- const inputIds = inputs.input_ids;
134
- const outputs = await this.model(inputs);
135
- const logits = outputs.logits;
136
- const data = logits.data;
137
- const dims = logits.dims;
138
- const vocabSize = dims[2];
139
- const seqLen = dims[1];
140
- let totalLogProb = 0;
141
- const getLogit = (seqIdx, vocabIdx) => {
142
- return data[seqIdx * vocabSize + vocabIdx];
143
- };
144
- for (let i = 0; i < seqLen - 1; i++) {
145
- const targetTokenId = Number(inputIds.data[i + 1]);
146
- let maxLogit = Number.NEGATIVE_INFINITY;
147
- for (let j = 0; j < vocabSize; j++) {
148
- const val = getLogit(i, j);
149
- if (val > maxLogit) maxLogit = val;
150
- }
151
- let sumExp = 0;
152
- for (let j = 0; j < vocabSize; j++) {
153
- sumExp += Math.exp(getLogit(i, j) - maxLogit);
154
- }
155
- const logSumExp = maxLogit + Math.log(sumExp);
156
- const targetLogit = getLogit(i, targetTokenId);
157
- const logProb = targetLogit - logSumExp;
158
- totalLogProb += logProb;
159
- }
160
- return totalLogProb;
161
- }
162
- /**
163
- * Get list of supported browser models
164
- */
165
- static getSupportedModels() {
166
- return BROWSER_MODELS.filter((m) => m.supported);
167
- }
168
- };
169
-
170
- export {
171
- BROWSER_MODELS,
172
- checkWebGPU,
173
- checkTransformersAvailable,
174
- BrowserInference
175
- };
@@ -1,10 +0,0 @@
1
- import {
2
- runBiasTest
3
- } from "./chunk-F6VBKHY4.js";
4
- import "./chunk-FEBJZMWY.js";
5
- import "./chunk-JQTB43B2.js";
6
- import "./chunk-AYXSWY3I.js";
7
- import "./chunk-JSBRDJBE.js";
8
- export {
9
- runBiasTest
10
- };
@@ -1,7 +0,0 @@
1
- import {
2
- formatBiasSeverity
3
- } from "./chunk-AYXSWY3I.js";
4
- import "./chunk-JSBRDJBE.js";
5
- export {
6
- formatBiasSeverity
7
- };