@euconform/cli 1.0.0 → 1.1.1

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,373 @@
1
+ // ../core/src/inference/ollama-client.ts
2
+ var OLLAMA_BASE_URL = "http://localhost:11434";
3
+ async function detectOllama() {
4
+ try {
5
+ const controller = new AbortController();
6
+ const timeoutId = setTimeout(() => controller.abort(), 2e3);
7
+ const response = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {
8
+ signal: controller.signal
9
+ });
10
+ clearTimeout(timeoutId);
11
+ return response.ok;
12
+ } catch {
13
+ return false;
14
+ }
15
+ }
16
+ async function listOllamaModels() {
17
+ try {
18
+ const response = await fetch(`${OLLAMA_BASE_URL}/api/tags`);
19
+ if (!response.ok) {
20
+ throw new Error(`Failed to list models: ${response.statusText}`);
21
+ }
22
+ const data = await response.json();
23
+ return data.models || [];
24
+ } catch (error) {
25
+ console.error("Failed to list Ollama models:", error);
26
+ return [];
27
+ }
28
+ }
29
+ async function ollamaGenerate(options) {
30
+ const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {
31
+ method: "POST",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify({
34
+ ...options,
35
+ stream: false
36
+ })
37
+ });
38
+ if (!response.ok) {
39
+ const errorText = await response.text();
40
+ throw new Error(
41
+ `Ollama generate failed (${response.status}): ${errorText || response.statusText}`
42
+ );
43
+ }
44
+ return response.json();
45
+ }
46
+ async function getLogProb(model, prompt) {
47
+ try {
48
+ const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {
49
+ method: "POST",
50
+ headers: { "Content-Type": "application/json" },
51
+ body: JSON.stringify({
52
+ model,
53
+ prompt,
54
+ stream: false,
55
+ options: {
56
+ num_predict: 0,
57
+ // Don't generate, just evaluate prompt
58
+ temperature: 0
59
+ }
60
+ })
61
+ });
62
+ if (!response.ok) {
63
+ const errorText = await response.text();
64
+ throw new Error(
65
+ `Failed to get logprob for model '${model}': ${errorText || response.statusText}`
66
+ );
67
+ }
68
+ const data = await response.json();
69
+ if (data.prompt_eval_duration && data.prompt_eval_count) {
70
+ const timePerToken = data.prompt_eval_duration / data.prompt_eval_count / 1e9;
71
+ return -Math.log(timePerToken + 1e-5);
72
+ }
73
+ if (data.eval_duration && data.eval_count) {
74
+ const timePerToken = data.eval_duration / data.eval_count / 1e9;
75
+ return -Math.log(timePerToken + 1e-3);
76
+ }
77
+ return 0;
78
+ } catch (error) {
79
+ console.error(`Error calculating log probability for model ${model}:`, error);
80
+ throw error;
81
+ }
82
+ }
83
+ async function getPerplexity(model, text) {
84
+ try {
85
+ const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {
86
+ method: "POST",
87
+ headers: { "Content-Type": "application/json" },
88
+ body: JSON.stringify({
89
+ model,
90
+ prompt: text,
91
+ stream: false,
92
+ options: {
93
+ num_predict: 0
94
+ // Don't generate, just evaluate prompt
95
+ }
96
+ })
97
+ });
98
+ if (!response.ok) {
99
+ throw new Error(`Failed to get perplexity: ${response.statusText}`);
100
+ }
101
+ const data = await response.json();
102
+ if (data.prompt_eval_duration && data.prompt_eval_count) {
103
+ const avgTimePerToken = data.prompt_eval_duration / data.prompt_eval_count / 1e9;
104
+ return avgTimePerToken * 1e3;
105
+ }
106
+ return 1;
107
+ } catch (error) {
108
+ console.error("Failed to get perplexity:", error);
109
+ return 1;
110
+ }
111
+ }
112
+ var OllamaClient = class {
113
+ baseUrl;
114
+ model;
115
+ capabilityCache;
116
+ externalCache;
117
+ constructor(model, baseUrl = OLLAMA_BASE_URL, cache) {
118
+ this.baseUrl = baseUrl;
119
+ this.model = model;
120
+ this.capabilityCache = /* @__PURE__ */ new Map();
121
+ this.externalCache = cache ?? null;
122
+ }
123
+ isCloudModel() {
124
+ return this.model.includes(":cloud");
125
+ }
126
+ /**
127
+ * Ensures the model is available in Ollama before proceeding
128
+ * Throws an error if the model is not found
129
+ */
130
+ async ensureModelLoaded() {
131
+ const models = await listOllamaModels();
132
+ const modelExists = models.some(
133
+ (m) => m.name === this.model || m.name.startsWith(`${this.model}:`) || this.model.startsWith(`${m.name}:`)
134
+ );
135
+ if (!modelExists) {
136
+ throw new Error(
137
+ `Model '${this.model}' not found in Ollama. Please run 'ollama pull ${this.model}' first.`
138
+ );
139
+ }
140
+ }
141
+ async isAvailable() {
142
+ return detectOllama();
143
+ }
144
+ async generate(prompt) {
145
+ await this.ensureModelLoaded();
146
+ const response = await ollamaGenerate({
147
+ model: this.model,
148
+ prompt
149
+ });
150
+ return response.response;
151
+ }
152
+ async getLogProb(prompt) {
153
+ const result = await this.getLogProbWithFallback(prompt);
154
+ return result.logprob;
155
+ }
156
+ async getPerplexity(text) {
157
+ return getPerplexity(this.model, text);
158
+ }
159
+ /**
160
+ * Test if the current model supports log-probabilities
161
+ * Sends a test request with "Der" prompt to detect capability
162
+ */
163
+ async testLogProbCapability() {
164
+ if (this.isCloudModel()) {
165
+ console.log(
166
+ `\u{1F50D} ${this.model} is a cloud model - logprobs not supported, using latency method`
167
+ );
168
+ return false;
169
+ }
170
+ try {
171
+ try {
172
+ const versionResponse = await fetch(`${this.baseUrl}/api/version`);
173
+ if (versionResponse.ok) {
174
+ const versionData = await versionResponse.json();
175
+ console.log(`\u{1F50D} Ollama version: ${versionData.version || "unknown"}`);
176
+ }
177
+ } catch {
178
+ }
179
+ const response = await fetch(`${this.baseUrl}/api/generate`, {
180
+ method: "POST",
181
+ headers: { "Content-Type": "application/json" },
182
+ body: JSON.stringify({
183
+ model: this.model,
184
+ prompt: "Der",
185
+ stream: false,
186
+ logprobs: true,
187
+ options: {
188
+ num_predict: 1,
189
+ temperature: 0
190
+ }
191
+ })
192
+ });
193
+ if (!response.ok) {
194
+ console.warn(
195
+ `\u{1F50D} Capability test request failed for ${this.model}: ${response.status} ${response.statusText}`
196
+ );
197
+ return false;
198
+ }
199
+ const data = await response.json();
200
+ const hasLogProbs = !!(data.logprobs && Array.isArray(data.logprobs) && data.logprobs.length > 0);
201
+ console.log(
202
+ `\u{1F50D} Capability test for ${this.model}: ${hasLogProbs ? "\u2705 supports" : "\u274C no support"} log-probabilities`,
203
+ data.logprobs ? `(${data.logprobs.length} tokens)` : "(no logprobs field)"
204
+ );
205
+ if (!hasLogProbs) {
206
+ console.log("\u{1F50D} Full response for debugging:", JSON.stringify(data, null, 2));
207
+ }
208
+ return hasLogProbs;
209
+ } catch (error) {
210
+ console.warn(`Log-probability capability test failed for model ${this.model}:`, error);
211
+ return false;
212
+ }
213
+ }
214
+ /**
215
+ * Get cached capability result from localStorage or external cache
216
+ */
217
+ getCachedCapability() {
218
+ try {
219
+ const cacheKey = `ollama_supports_logprobs_${this.model}`;
220
+ let cached = null;
221
+ if (this.externalCache) {
222
+ cached = this.externalCache.get(cacheKey);
223
+ } else if (typeof localStorage !== "undefined") {
224
+ cached = localStorage.getItem(cacheKey);
225
+ }
226
+ if (cached) {
227
+ const capability = JSON.parse(cached);
228
+ const cacheAge = Date.now() - new Date(capability.testedAt).getTime();
229
+ const CACHE_TTL = 24 * 60 * 60 * 1e3;
230
+ if (cacheAge < CACHE_TTL) {
231
+ return capability;
232
+ }
233
+ }
234
+ return null;
235
+ } catch {
236
+ return null;
237
+ }
238
+ }
239
+ /**
240
+ * Cache capability result in localStorage or external cache
241
+ */
242
+ setCachedCapability(supports) {
243
+ try {
244
+ const cacheKey = `ollama_supports_logprobs_${this.model}`;
245
+ const capability = {
246
+ model: this.model,
247
+ supportsLogProbs: supports,
248
+ testedAt: (/* @__PURE__ */ new Date()).toISOString()
249
+ };
250
+ if (this.externalCache) {
251
+ this.externalCache.set(cacheKey, JSON.stringify(capability));
252
+ } else if (typeof localStorage !== "undefined") {
253
+ localStorage.setItem(cacheKey, JSON.stringify(capability));
254
+ }
255
+ this.capabilityCache.set(this.model, capability);
256
+ } catch (error) {
257
+ console.warn("Failed to cache capability result:", error);
258
+ }
259
+ }
260
+ /**
261
+ * Detect if the current model supports log-probabilities
262
+ * Checks cache first, then runs test if needed
263
+ */
264
+ async detectLogProbSupport() {
265
+ const memoryCache = this.capabilityCache.get(this.model);
266
+ if (memoryCache) {
267
+ return memoryCache.supportsLogProbs;
268
+ }
269
+ const cached = this.getCachedCapability();
270
+ if (cached) {
271
+ this.capabilityCache.set(this.model, cached);
272
+ return cached.supportsLogProbs;
273
+ }
274
+ const supports = await this.testLogProbCapability();
275
+ this.setCachedCapability(supports);
276
+ return supports;
277
+ }
278
+ async tryExactLogProbs(prompt) {
279
+ const supportsLogProbs = await this.detectLogProbSupport();
280
+ if (!supportsLogProbs || this.isCloudModel()) return null;
281
+ try {
282
+ const response = await fetch(`${this.baseUrl}/api/generate`, {
283
+ method: "POST",
284
+ headers: { "Content-Type": "application/json" },
285
+ body: JSON.stringify({
286
+ model: this.model,
287
+ prompt,
288
+ stream: false,
289
+ logprobs: true,
290
+ options: { num_predict: 0, temperature: 0 }
291
+ })
292
+ });
293
+ if (!response.ok) {
294
+ console.warn(
295
+ `\u274C Log-probability request failed for ${this.model}: ${response.status} ${response.statusText}`
296
+ );
297
+ return null;
298
+ }
299
+ const data = await response.json();
300
+ if (!data.logprobs || !Array.isArray(data.logprobs) || data.logprobs.length === 0) {
301
+ console.warn(`\u274C No logprobs in response for ${this.model}, response:`, data);
302
+ return null;
303
+ }
304
+ const avgLogProb = data.logprobs.reduce(
305
+ (sum, item) => sum + item.logprob,
306
+ 0
307
+ ) / data.logprobs.length;
308
+ console.log(`\u2705 Using exact log-probabilities for ${this.model}: ${avgLogProb}`);
309
+ return { logprob: avgLogProb, method: "logprobs_exact" };
310
+ } catch (error) {
311
+ console.warn(
312
+ `\u274C Log-probability request failed for ${this.model}, falling back to latency method:`,
313
+ error
314
+ );
315
+ return null;
316
+ }
317
+ }
318
+ async latencyFallback(prompt) {
319
+ const isCloud = this.isCloudModel();
320
+ console.log(
321
+ `\u26A1 Falling back to latency-based method for ${this.model}${isCloud ? " (cloud model)" : ""}`
322
+ );
323
+ const startTime = performance.now();
324
+ const response = await fetch(`${this.baseUrl}/api/generate`, {
325
+ method: "POST",
326
+ headers: { "Content-Type": "application/json" },
327
+ body: JSON.stringify({
328
+ model: this.model,
329
+ prompt,
330
+ stream: false,
331
+ options: { num_predict: isCloud ? 1 : 0, temperature: 0 }
332
+ })
333
+ });
334
+ const endTime = performance.now();
335
+ if (!response.ok) {
336
+ const errorText = await response.text();
337
+ throw new Error(`Ollama request failed: ${response.statusText}. ${errorText}`);
338
+ }
339
+ const data = await response.json();
340
+ let latency;
341
+ if (data.prompt_eval_duration && data.prompt_eval_count) {
342
+ latency = data.prompt_eval_duration / data.prompt_eval_count / 1e9;
343
+ } else {
344
+ const totalTime = (endTime - startTime) / 1e3;
345
+ const tokenCount = prompt.split(/\s+/).length;
346
+ latency = totalTime / Math.max(tokenCount, 1);
347
+ }
348
+ return { logprob: -Math.log(latency + 1e-5), method: "logprobs_fallback_latency" };
349
+ }
350
+ /**
351
+ * Get log-probability with automatic fallback to latency method
352
+ * Returns both the calculated value and the method used
353
+ */
354
+ async getLogProbWithFallback(prompt) {
355
+ try {
356
+ const exact = await this.tryExactLogProbs(prompt);
357
+ if (exact) return exact;
358
+ return await this.latencyFallback(prompt);
359
+ } catch (error) {
360
+ console.error(`Error in getLogProbWithFallback for model ${this.model}:`, error);
361
+ throw error;
362
+ }
363
+ }
364
+ };
365
+
366
+ export {
367
+ detectOllama,
368
+ listOllamaModels,
369
+ ollamaGenerate,
370
+ getLogProb,
371
+ getPerplexity,
372
+ OllamaClient
373
+ };
@@ -0,0 +1,30 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ export {
28
+ __commonJS,
29
+ __toESM
30
+ };