@euconform/core 1.0.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 (89) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/LICENSE +21 -0
  3. package/dist/fairness-metrics.d.ts +65 -0
  4. package/dist/fairness-metrics.d.ts.map +1 -0
  5. package/dist/fairness-metrics.js +394 -0
  6. package/dist/index.d.ts +14 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +17 -0
  9. package/dist/inference/browser-inference.d.ts +60 -0
  10. package/dist/inference/browser-inference.d.ts.map +1 -0
  11. package/dist/inference/browser-inference.js +221 -0
  12. package/dist/inference/capability-detection.d.ts +183 -0
  13. package/dist/inference/capability-detection.d.ts.map +1 -0
  14. package/dist/inference/capability-detection.js +552 -0
  15. package/dist/inference/index.d.ts +43 -0
  16. package/dist/inference/index.d.ts.map +1 -0
  17. package/dist/inference/index.js +97 -0
  18. package/dist/inference/inference-factory.d.ts +73 -0
  19. package/dist/inference/inference-factory.d.ts.map +1 -0
  20. package/dist/inference/inference-factory.js +175 -0
  21. package/dist/inference/ollama-client.d.ts +114 -0
  22. package/dist/inference/ollama-client.d.ts.map +1 -0
  23. package/dist/inference/ollama-client.js +386 -0
  24. package/dist/legal-checks/annex-iv-report.d.ts +131 -0
  25. package/dist/legal-checks/annex-iv-report.d.ts.map +1 -0
  26. package/dist/legal-checks/annex-iv-report.js +62 -0
  27. package/dist/legal-checks/bias-metrics.d.ts +162 -0
  28. package/dist/legal-checks/bias-metrics.d.ts.map +1 -0
  29. package/dist/legal-checks/bias-metrics.js +408 -0
  30. package/dist/legal-checks/data-governance.d.ts +30 -0
  31. package/dist/legal-checks/data-governance.d.ts.map +1 -0
  32. package/dist/legal-checks/data-governance.js +99 -0
  33. package/dist/legal-checks/human-oversight-logging.d.ts +27 -0
  34. package/dist/legal-checks/human-oversight-logging.d.ts.map +1 -0
  35. package/dist/legal-checks/human-oversight-logging.js +106 -0
  36. package/dist/legal-checks/index.d.ts +13 -0
  37. package/dist/legal-checks/index.d.ts.map +1 -0
  38. package/dist/legal-checks/index.js +6 -0
  39. package/dist/legal-checks/risk-classifier.d.ts +35 -0
  40. package/dist/legal-checks/risk-classifier.d.ts.map +1 -0
  41. package/dist/legal-checks/risk-classifier.js +234 -0
  42. package/dist/legal-checks/robustness-transparency.d.ts +45 -0
  43. package/dist/legal-checks/robustness-transparency.d.ts.map +1 -0
  44. package/dist/legal-checks/robustness-transparency.js +111 -0
  45. package/dist/metrics/crows-pairs-bias.d.ts +41 -0
  46. package/dist/metrics/crows-pairs-bias.d.ts.map +1 -0
  47. package/dist/metrics/crows-pairs-bias.js +172 -0
  48. package/dist/metrics/index.d.ts +6 -0
  49. package/dist/metrics/index.d.ts.map +1 -0
  50. package/dist/metrics/index.js +5 -0
  51. package/dist/metrics/stereotype-bias.d.ts +89 -0
  52. package/dist/metrics/stereotype-bias.d.ts.map +1 -0
  53. package/dist/metrics/stereotype-bias.js +157 -0
  54. package/dist/risk-engine.d.ts +23 -0
  55. package/dist/risk-engine.d.ts.map +1 -0
  56. package/dist/risk-engine.js +451 -0
  57. package/dist/types.d.ts +131 -0
  58. package/dist/types.d.ts.map +1 -0
  59. package/dist/types.js +5 -0
  60. package/package.json +28 -0
  61. package/src/fairness-metrics.ts +498 -0
  62. package/src/index.ts +150 -0
  63. package/src/inference/browser-inference.ts +258 -0
  64. package/src/inference/capability-detection.ts +714 -0
  65. package/src/inference/index.ts +156 -0
  66. package/src/inference/inference-factory.ts +222 -0
  67. package/src/inference/ollama-client.ts +514 -0
  68. package/src/inference/transformers.d.ts +62 -0
  69. package/src/legal-checks/annex-iv-report.ts +157 -0
  70. package/src/legal-checks/bias-metrics.ts +590 -0
  71. package/src/legal-checks/data-governance.ts +148 -0
  72. package/src/legal-checks/human-oversight-logging.ts +135 -0
  73. package/src/legal-checks/index.ts +55 -0
  74. package/src/legal-checks/risk-classifier.ts +285 -0
  75. package/src/legal-checks/robustness-transparency.ts +162 -0
  76. package/src/metrics/crows-pairs-bias.ts +245 -0
  77. package/src/metrics/index.ts +22 -0
  78. package/src/metrics/stereotype-bias.ts +267 -0
  79. package/src/risk-engine.ts +525 -0
  80. package/src/types.ts +154 -0
  81. package/tests/annex-iv-report.test.ts +323 -0
  82. package/tests/crows-pairs-bias.test.ts +342 -0
  83. package/tests/fairness-metrics.test.ts +187 -0
  84. package/tests/integration/bias-calculation.integration.test.ts +688 -0
  85. package/tests/legal-checks.test.ts +53 -0
  86. package/tests/ollama-client.test.ts +427 -0
  87. package/tests/risk-engine.test.ts +149 -0
  88. package/tsconfig.json +11 -0
  89. package/vitest.config.ts +17 -0
@@ -0,0 +1,552 @@
1
+ /**
2
+ * Model Capability Detection Service
3
+ * Real-time detection of model capabilities with smart caching
4
+ */
5
+ /**
6
+ * Implementation of capability detection service
7
+ */
8
+ export class DefaultCapabilityDetectionService {
9
+ cache = new Map();
10
+ CACHE_DURATION_MS = 24 * 60 * 60 * 1000; // 24 hours
11
+ CACHE_KEY_PREFIX = "euconform_capability_";
12
+ constructor() {
13
+ this.loadCacheFromStorage();
14
+ }
15
+ /**
16
+ * Detect capabilities for all available models
17
+ * Includes browser inference and parallel Ollama model testing
18
+ */
19
+ async detectAllCapabilities() {
20
+ const capabilities = [];
21
+ try {
22
+ // Import browser models dynamically
23
+ const { BROWSER_MODELS } = await import("./browser-inference");
24
+ for (const model of BROWSER_MODELS) {
25
+ if (!model.supported)
26
+ continue;
27
+ capabilities.push({
28
+ modelId: model.id,
29
+ engine: "browser",
30
+ method: "logprobs_exact",
31
+ status: "available",
32
+ recommended: true,
33
+ lastTested: new Date().toISOString(),
34
+ });
35
+ }
36
+ }
37
+ catch (error) {
38
+ console.warn("Failed to load browser models:", error);
39
+ // Fallback if import fails
40
+ capabilities.push({
41
+ modelId: "Xenova/distilgpt2",
42
+ engine: "browser",
43
+ method: "logprobs_exact",
44
+ status: "available",
45
+ recommended: true,
46
+ lastTested: new Date().toISOString(),
47
+ });
48
+ }
49
+ try {
50
+ // Import Ollama functions dynamically to avoid issues if not available
51
+ const { detectOllama, listOllamaModels } = await import("./ollama-client");
52
+ // Check if Ollama is available
53
+ const ollamaAvailable = await detectOllama();
54
+ if (ollamaAvailable) {
55
+ // Get list of available Ollama models
56
+ const ollamaModels = await listOllamaModels();
57
+ // Detect capabilities for all Ollama models in parallel
58
+ const detectionPromises = ollamaModels.map((model) => this.detectModelCapability(model.name, "ollama"));
59
+ const ollamaCapabilities = await Promise.allSettled(detectionPromises);
60
+ // Process results and add successful detections
61
+ ollamaCapabilities.forEach((result, index) => {
62
+ if (result.status === "fulfilled") {
63
+ capabilities.push(result.value);
64
+ }
65
+ else {
66
+ // Add error capability for failed detections
67
+ const model = ollamaModels[index];
68
+ if (model) {
69
+ capabilities.push({
70
+ modelId: model.name,
71
+ engine: "ollama",
72
+ method: "logprobs_fallback_latency",
73
+ status: "error",
74
+ error: result.reason?.message || "Detection failed",
75
+ lastTested: new Date().toISOString(),
76
+ });
77
+ }
78
+ }
79
+ });
80
+ }
81
+ }
82
+ catch (error) {
83
+ console.warn("Failed to detect Ollama capabilities:", error);
84
+ }
85
+ // Sort capabilities by recommendation score
86
+ return this.sortByRecommendation(capabilities);
87
+ }
88
+ /**
89
+ * Detect capability for a specific model
90
+ * Uses cache-first strategy with live fallback
91
+ */
92
+ async detectModelCapability(modelId, engine) {
93
+ // Check cache first
94
+ const cached = this.getCachedCapability(modelId);
95
+ if (cached && this.isCacheValid(cached)) {
96
+ return cached.capability;
97
+ }
98
+ // Browser inference always supports exact log-probabilities
99
+ if (engine === "browser") {
100
+ const capability = {
101
+ modelId,
102
+ engine: "browser",
103
+ method: "logprobs_exact",
104
+ status: "available",
105
+ recommended: true,
106
+ lastTested: new Date().toISOString(),
107
+ };
108
+ this.setCachedCapability(capability);
109
+ return capability;
110
+ }
111
+ // Detect Ollama model capability
112
+ try {
113
+ const { OllamaClient } = await import("./ollama-client");
114
+ const client = new OllamaClient(modelId);
115
+ // Test if model supports log-probabilities
116
+ const supportsLogProbs = await client.detectLogProbSupport();
117
+ const capability = {
118
+ modelId,
119
+ engine: "ollama",
120
+ method: supportsLogProbs ? "logprobs_exact" : "logprobs_fallback_latency",
121
+ status: "available",
122
+ lastTested: new Date().toISOString(),
123
+ };
124
+ this.setCachedCapability(capability);
125
+ return capability;
126
+ }
127
+ catch (error) {
128
+ const capability = {
129
+ modelId,
130
+ engine: "ollama",
131
+ method: "logprobs_fallback_latency",
132
+ status: "error",
133
+ error: error instanceof Error ? error.message : "Unknown error",
134
+ lastTested: new Date().toISOString(),
135
+ };
136
+ // Cache error results for shorter duration to allow retry
137
+ this.setCachedCapability(capability, 5 * 60 * 1000); // 5 minutes for errors
138
+ return capability;
139
+ }
140
+ }
141
+ /**
142
+ * Get cached capability result
143
+ */
144
+ getCachedCapability(modelId) {
145
+ const cached = this.cache.get(modelId);
146
+ if (cached && this.isCacheValid(cached)) {
147
+ return cached;
148
+ }
149
+ return null;
150
+ }
151
+ /**
152
+ * Cache capability result with optional custom duration
153
+ */
154
+ setCachedCapability(capability, durationMs) {
155
+ const duration = durationMs || this.CACHE_DURATION_MS;
156
+ const now = new Date();
157
+ const expiresAt = new Date(now.getTime() + duration);
158
+ const cacheEntry = {
159
+ modelId: capability.modelId,
160
+ capability,
161
+ cachedAt: now.toISOString(),
162
+ expiresAt: expiresAt.toISOString(),
163
+ };
164
+ // Store in memory cache
165
+ this.cache.set(capability.modelId, cacheEntry);
166
+ // Persist to localStorage
167
+ this.saveCacheToStorage(capability.modelId, cacheEntry);
168
+ }
169
+ /**
170
+ * Clear all cached capabilities
171
+ */
172
+ clearCache() {
173
+ this.cache.clear();
174
+ // Clear from localStorage
175
+ try {
176
+ const keys = Object.keys(localStorage);
177
+ for (const key of keys) {
178
+ if (key.startsWith(this.CACHE_KEY_PREFIX)) {
179
+ localStorage.removeItem(key);
180
+ }
181
+ }
182
+ }
183
+ catch (error) {
184
+ console.warn("Failed to clear capability cache from localStorage:", error);
185
+ }
186
+ }
187
+ /**
188
+ * Refresh all capabilities by clearing cache and re-detecting
189
+ */
190
+ async refreshCapabilities() {
191
+ this.clearCache();
192
+ return this.detectAllCapabilities();
193
+ }
194
+ /**
195
+ * Check if cache entry is still valid
196
+ */
197
+ isCacheValid(cacheEntry) {
198
+ const now = new Date();
199
+ const expiresAt = new Date(cacheEntry.expiresAt);
200
+ return now < expiresAt;
201
+ }
202
+ /**
203
+ * Load cache from localStorage
204
+ */
205
+ loadCacheFromStorage() {
206
+ try {
207
+ const keys = Object.keys(localStorage);
208
+ for (const key of keys) {
209
+ if (key.startsWith(this.CACHE_KEY_PREFIX)) {
210
+ const cached = localStorage.getItem(key);
211
+ if (cached) {
212
+ const cacheEntry = JSON.parse(cached);
213
+ if (this.isCacheValid(cacheEntry)) {
214
+ const modelId = key.replace(this.CACHE_KEY_PREFIX, "");
215
+ this.cache.set(modelId, cacheEntry);
216
+ }
217
+ else {
218
+ // Remove expired cache entries
219
+ localStorage.removeItem(key);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ catch (error) {
226
+ console.warn("Failed to load capability cache from localStorage:", error);
227
+ }
228
+ }
229
+ /**
230
+ * Save cache entry to localStorage
231
+ */
232
+ saveCacheToStorage(modelId, cacheEntry) {
233
+ try {
234
+ const key = `${this.CACHE_KEY_PREFIX}${modelId}`;
235
+ localStorage.setItem(key, JSON.stringify(cacheEntry));
236
+ }
237
+ catch (error) {
238
+ console.warn("Failed to save capability cache to localStorage:", error);
239
+ }
240
+ }
241
+ /**
242
+ * Sort capabilities by recommendation score
243
+ */
244
+ sortByRecommendation(capabilities) {
245
+ return capabilities.sort((a, b) => {
246
+ const scoreA = this.getRecommendationScore(a);
247
+ const scoreB = this.getRecommendationScore(b);
248
+ return scoreB - scoreA;
249
+ });
250
+ }
251
+ /**
252
+ * Calculate recommendation score for a capability
253
+ */
254
+ getRecommendationScore(capability) {
255
+ if (capability.status !== "available")
256
+ return 0;
257
+ let score = 0;
258
+ // Browser inference gets highest score
259
+ if (capability.engine === "browser")
260
+ score += 100;
261
+ // Exact methods get bonus
262
+ if (capability.method === "logprobs_exact")
263
+ score += 50;
264
+ // Recommended models get bonus
265
+ if (capability.recommended)
266
+ score += 25;
267
+ return score;
268
+ }
269
+ }
270
+ /**
271
+ * Enhanced capability detection service with timeout and retry logic
272
+ */
273
+ export class EnhancedCapabilityDetectionService extends DefaultCapabilityDetectionService {
274
+ DETECTION_TIMEOUT_MS = 5000; // 5 seconds
275
+ MAX_RETRIES = 3;
276
+ RETRY_DELAY_BASE_MS = 1000; // Base delay for exponential backoff
277
+ /**
278
+ * Detect model capability with timeout and retry logic
279
+ */
280
+ async detectModelCapability(modelId, engine) {
281
+ // Check cache first
282
+ const cached = this.getCachedCapability(modelId);
283
+ if (cached && this.isCacheValid(cached)) {
284
+ return cached.capability;
285
+ }
286
+ // Browser inference always supports exact log-probabilities
287
+ if (engine === "browser") {
288
+ const capability = {
289
+ modelId,
290
+ engine: "browser",
291
+ method: "logprobs_exact",
292
+ status: "available",
293
+ recommended: true,
294
+ lastTested: new Date().toISOString(),
295
+ };
296
+ this.setCachedCapability(capability);
297
+ return capability;
298
+ }
299
+ // Detect Ollama model capability with timeout and retry
300
+ return this.detectOllamaCapabilityWithRetry(modelId);
301
+ }
302
+ /**
303
+ * Detect Ollama capability with retry logic and exponential backoff
304
+ */
305
+ async detectOllamaCapabilityWithRetry(modelId) {
306
+ let lastError = null;
307
+ for (let attempt = 0; attempt < this.MAX_RETRIES; attempt++) {
308
+ try {
309
+ return await this.detectOllamaCapabilityWithTimeout(modelId);
310
+ }
311
+ catch (error) {
312
+ lastError = error instanceof Error ? error : new Error("Unknown error");
313
+ // Don't retry on certain errors
314
+ if (this.isNonRetryableError(lastError)) {
315
+ break;
316
+ }
317
+ // Wait before retry with exponential backoff
318
+ if (attempt < this.MAX_RETRIES - 1) {
319
+ const delay = this.RETRY_DELAY_BASE_MS * 2 ** attempt;
320
+ await this.sleep(delay);
321
+ }
322
+ }
323
+ }
324
+ // All retries failed, return error capability
325
+ const errorMessage = this.getErrorMessage(lastError);
326
+ const capability = {
327
+ modelId,
328
+ engine: "ollama",
329
+ method: "logprobs_fallback_latency",
330
+ status: this.isOllamaUnavailableError(lastError) ? "unavailable" : "error",
331
+ error: errorMessage,
332
+ lastTested: new Date().toISOString(),
333
+ };
334
+ // Cache error results for shorter duration
335
+ this.setCachedCapability(capability, 5 * 60 * 1000); // 5 minutes for errors
336
+ return capability;
337
+ }
338
+ /**
339
+ * Detect Ollama capability with timeout
340
+ */
341
+ async detectOllamaCapabilityWithTimeout(modelId) {
342
+ let timeoutId;
343
+ const timeoutPromise = new Promise((_resolve, reject) => {
344
+ timeoutId = setTimeout(() => {
345
+ reject(new Error(`Detection timeout after ${this.DETECTION_TIMEOUT_MS}ms`));
346
+ }, this.DETECTION_TIMEOUT_MS);
347
+ });
348
+ const detectionPromise = (async () => {
349
+ const { OllamaClient, detectOllama } = await import("./ollama-client");
350
+ // First check if Ollama is available
351
+ const ollamaAvailable = await detectOllama();
352
+ if (!ollamaAvailable) {
353
+ throw new Error("Ollama service is not available");
354
+ }
355
+ const client = new OllamaClient(modelId);
356
+ // Test if model supports log-probabilities
357
+ const supportsLogProbs = await client.detectLogProbSupport();
358
+ const capability = {
359
+ modelId,
360
+ engine: "ollama",
361
+ method: supportsLogProbs ? "logprobs_exact" : "logprobs_fallback_latency",
362
+ status: "available",
363
+ lastTested: new Date().toISOString(),
364
+ };
365
+ this.setCachedCapability(capability);
366
+ return capability;
367
+ })();
368
+ try {
369
+ return await Promise.race([detectionPromise, timeoutPromise]);
370
+ }
371
+ finally {
372
+ if (timeoutId)
373
+ clearTimeout(timeoutId);
374
+ }
375
+ }
376
+ /**
377
+ * Enhanced detectAllCapabilities with better error handling
378
+ */
379
+ async detectAllCapabilities() {
380
+ const capabilities = [];
381
+ // Add all supported browser models dynamically
382
+ try {
383
+ const { BROWSER_MODELS } = await import("./browser-inference");
384
+ for (const model of BROWSER_MODELS) {
385
+ if (!model.supported)
386
+ continue;
387
+ capabilities.push({
388
+ modelId: model.id,
389
+ engine: "browser",
390
+ method: "logprobs_exact",
391
+ status: "available",
392
+ recommended: true,
393
+ lastTested: new Date().toISOString(),
394
+ });
395
+ }
396
+ }
397
+ catch (error) {
398
+ console.warn("Failed to load browser models:", error);
399
+ // Fallback if import fails
400
+ capabilities.push({
401
+ modelId: "Xenova/distilgpt2",
402
+ engine: "browser",
403
+ method: "logprobs_exact",
404
+ status: "available",
405
+ recommended: true,
406
+ lastTested: new Date().toISOString(),
407
+ });
408
+ }
409
+ try {
410
+ // Import Ollama functions dynamically
411
+ const { detectOllama, listOllamaModels } = await import("./ollama-client");
412
+ // Check if Ollama is available with timeout
413
+ const ollamaAvailable = await this.withTimeout(detectOllama(), 2000, // 2 second timeout for Ollama availability check
414
+ "Ollama availability check timeout");
415
+ if (ollamaAvailable) {
416
+ // Get list of available Ollama models with timeout
417
+ const ollamaModels = await this.withTimeout(listOllamaModels(), 5000, // 5 second timeout for model listing
418
+ "Ollama model listing timeout");
419
+ if (ollamaModels.length === 0) {
420
+ console.warn("No Ollama models found");
421
+ return capabilities;
422
+ }
423
+ // Detect capabilities for all Ollama models in parallel
424
+ const detectionPromises = ollamaModels.map((model) => this.detectModelCapability(model.name, "ollama"));
425
+ const ollamaCapabilities = await Promise.allSettled(detectionPromises);
426
+ // Process results and add all capabilities (including errors)
427
+ ollamaCapabilities.forEach((result, index) => {
428
+ if (result.status === "fulfilled") {
429
+ capabilities.push(result.value);
430
+ }
431
+ else {
432
+ // Add error capability for failed detections
433
+ const model = ollamaModels[index];
434
+ if (model) {
435
+ const errorMessage = result.reason?.message || "Detection failed";
436
+ capabilities.push({
437
+ modelId: model.name,
438
+ engine: "ollama",
439
+ method: "logprobs_fallback_latency",
440
+ status: "error",
441
+ error: errorMessage,
442
+ lastTested: new Date().toISOString(),
443
+ });
444
+ }
445
+ }
446
+ });
447
+ }
448
+ else {
449
+ console.info("Ollama is not available, only browser inference will be offered");
450
+ }
451
+ }
452
+ catch (error) {
453
+ console.warn("Failed to detect Ollama capabilities:", error);
454
+ // Don't throw - we still have browser inference available
455
+ }
456
+ // Sort capabilities by recommendation score
457
+ return this.sortByRecommendation(capabilities);
458
+ }
459
+ /**
460
+ * Utility function to add timeout to any promise
461
+ */
462
+ withTimeout(promise, timeoutMs, errorMessage) {
463
+ return new Promise((resolve, reject) => {
464
+ const timeoutId = setTimeout(() => {
465
+ reject(new Error(errorMessage));
466
+ }, timeoutMs);
467
+ promise
468
+ .then((result) => {
469
+ clearTimeout(timeoutId);
470
+ resolve(result);
471
+ })
472
+ .catch((error) => {
473
+ clearTimeout(timeoutId);
474
+ reject(error);
475
+ });
476
+ });
477
+ }
478
+ /**
479
+ * Sleep utility for retry delays
480
+ */
481
+ sleep(ms) {
482
+ return new Promise((resolve) => setTimeout(resolve, ms));
483
+ }
484
+ /**
485
+ * Check if error should not be retried
486
+ */
487
+ isNonRetryableError(error) {
488
+ const message = error.message.toLowerCase();
489
+ return (message.includes("not found") ||
490
+ (message.includes("model") && message.includes("not available")) ||
491
+ message.includes("permission denied") ||
492
+ message.includes("unauthorized"));
493
+ }
494
+ /**
495
+ * Check if error indicates Ollama is unavailable
496
+ */
497
+ isOllamaUnavailableError(error) {
498
+ if (!error)
499
+ return false;
500
+ const message = error.message.toLowerCase();
501
+ return (message.includes("ollama service is not available") ||
502
+ message.includes("connection refused") ||
503
+ message.includes("network error") ||
504
+ message.includes("fetch failed"));
505
+ }
506
+ /**
507
+ * Get user-friendly error message
508
+ */
509
+ getErrorMessage(error) {
510
+ if (!error)
511
+ return "Unknown error";
512
+ const message = error.message;
513
+ // Timeout errors
514
+ if (message.includes("timeout")) {
515
+ return "Model detection timed out. The model may be slow to respond.";
516
+ }
517
+ // Ollama unavailable
518
+ if (this.isOllamaUnavailableError(error)) {
519
+ return "Ollama service is not running. Please start Ollama to use local models.";
520
+ }
521
+ // Model not found
522
+ if (message.includes("not found")) {
523
+ return "Model not found. Please ensure the model is installed in Ollama.";
524
+ }
525
+ // Network errors
526
+ if (message.includes("network") || message.includes("fetch")) {
527
+ return "Network error. Please check your connection and try again.";
528
+ }
529
+ // Generic error
530
+ return `Detection failed: ${message}`;
531
+ }
532
+ /**
533
+ * Check if cache entry is still valid (override to handle version tracking)
534
+ */
535
+ isCacheValid(cacheEntry) {
536
+ const now = new Date();
537
+ const expiresAt = new Date(cacheEntry.expiresAt);
538
+ // Check expiration
539
+ if (now >= expiresAt) {
540
+ return false;
541
+ }
542
+ // TODO: Add Ollama version checking when available
543
+ // For now, we rely on time-based expiration
544
+ return true;
545
+ }
546
+ }
547
+ /**
548
+ * Create a capability detection service instance
549
+ */
550
+ export function createCapabilityDetectionService() {
551
+ return new EnhancedCapabilityDetectionService();
552
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Inference Factory
3
+ * Auto-detects available inference engines and provides unified interface
4
+ */
5
+ import { type BrowserModel } from "./browser-inference";
6
+ import { type OllamaModel } from "./ollama-client";
7
+ export type InferenceEngine = "ollama" | "browser" | "demo";
8
+ export interface InferenceCapabilities {
9
+ ollama: {
10
+ available: boolean;
11
+ models: OllamaModel[];
12
+ };
13
+ browser: {
14
+ available: boolean;
15
+ webgpu: boolean;
16
+ models: BrowserModel[];
17
+ };
18
+ recommended: InferenceEngine;
19
+ }
20
+ export interface InferenceClient {
21
+ engine: InferenceEngine;
22
+ modelId: string;
23
+ generate(prompt: string): Promise<string>;
24
+ getLogProb(prompt: string): Promise<number>;
25
+ }
26
+ /**
27
+ * Detect all available inference capabilities
28
+ */
29
+ export declare function detectCapabilities(): Promise<InferenceCapabilities>;
30
+ /**
31
+ * Create an inference client for the specified engine
32
+ */
33
+ export declare function createInferenceClient(engine: InferenceEngine, modelId: string, statusCallback?: (status: string) => void): InferenceClient;
34
+ /**
35
+ * Auto-select the best available inference client
36
+ */
37
+ export declare function createAutoInferenceClient(preferredModel?: string): Promise<InferenceClient>;
38
+ export { detectOllama, listOllamaModels, OllamaClient } from "./ollama-client";
39
+ export { BrowserInference, checkWebGPU, BROWSER_MODELS } from "./browser-inference";
40
+ export { InferenceFactory, createInferenceFactory, detectBestMethodForConfig, type InferenceResult, } from "./inference-factory";
41
+ export { createCapabilityDetectionService, DefaultCapabilityDetectionService, EnhancedCapabilityDetectionService, type CapabilityDetectionService, type ModelCapability, type ModelSelectionState, type CapabilityCacheEntry, type DetectionProgress, type UserPreferences, type EnhancedModelSelectionState, type CapabilityDetectionStatus, } from "./capability-detection";
42
+ export type { OllamaModel, BrowserModel };
43
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Inference Factory
3
+ * Auto-detects available inference engines and provides unified interface
4
+ */
5
+ import { BROWSER_MODELS, BrowserInference, checkWebGPU, } from "./browser-inference";
6
+ import { OllamaClient, detectOllama, listOllamaModels } from "./ollama-client";
7
+ /**
8
+ * Detect all available inference capabilities
9
+ */
10
+ export async function detectCapabilities() {
11
+ const [ollamaAvailable, webgpuAvailable, ollamaModels] = await Promise.all([
12
+ detectOllama(),
13
+ checkWebGPU(),
14
+ detectOllama().then((ok) => (ok ? listOllamaModels() : [])),
15
+ ]);
16
+ // Determine recommended engine
17
+ let recommended = "demo";
18
+ if (ollamaAvailable && ollamaModels.length > 0) {
19
+ recommended = "ollama";
20
+ }
21
+ else if (webgpuAvailable) {
22
+ recommended = "browser";
23
+ }
24
+ return {
25
+ ollama: {
26
+ available: ollamaAvailable,
27
+ models: ollamaModels,
28
+ },
29
+ browser: {
30
+ available: webgpuAvailable || true, // CPU fallback always available
31
+ webgpu: webgpuAvailable,
32
+ models: BROWSER_MODELS,
33
+ },
34
+ recommended,
35
+ };
36
+ }
37
+ /**
38
+ * Create an inference client for the specified engine
39
+ */
40
+ export function createInferenceClient(engine, modelId, statusCallback) {
41
+ switch (engine) {
42
+ case "ollama": {
43
+ const ollamaClient = new OllamaClient(modelId);
44
+ return {
45
+ engine: "ollama",
46
+ modelId,
47
+ generate: (prompt) => ollamaClient.generate(prompt),
48
+ getLogProb: (prompt) => ollamaClient.getLogProb(prompt),
49
+ };
50
+ }
51
+ case "browser": {
52
+ const browserClient = new BrowserInference(modelId, statusCallback);
53
+ return {
54
+ engine: "browser",
55
+ modelId,
56
+ generate: (prompt) => browserClient.generate(prompt),
57
+ getLogProb: (prompt) => browserClient.getLogProb(prompt),
58
+ };
59
+ }
60
+ default:
61
+ // Demo mode returns simulated results
62
+ return {
63
+ engine: "demo",
64
+ modelId: "demo-model",
65
+ generate: async (prompt) => {
66
+ await new Promise((r) => setTimeout(r, 500)); // Simulate latency
67
+ return `[Demo] Response to: ${prompt.slice(0, 50)}...`;
68
+ },
69
+ getLogProb: async () => {
70
+ await new Promise((r) => setTimeout(r, 100));
71
+ // Return random value between -5 and 0 for demo
72
+ return -Math.random() * 5;
73
+ },
74
+ };
75
+ }
76
+ }
77
+ /**
78
+ * Auto-select the best available inference client
79
+ */
80
+ export async function createAutoInferenceClient(preferredModel) {
81
+ const capabilities = await detectCapabilities();
82
+ if (capabilities.recommended === "ollama") {
83
+ // Use preferred model if available, otherwise first model
84
+ const model = preferredModel || capabilities.ollama.models[0]?.name || "llama3.2:3b";
85
+ return createInferenceClient("ollama", model);
86
+ }
87
+ if (capabilities.recommended === "browser") {
88
+ const model = preferredModel || BROWSER_MODELS[0]?.id || "Xenova/distilgpt2";
89
+ return createInferenceClient("browser", model);
90
+ }
91
+ return createInferenceClient("demo", "demo-model");
92
+ }
93
+ // Re-export types and utilities
94
+ export { detectOllama, listOllamaModels, OllamaClient } from "./ollama-client";
95
+ export { BrowserInference, checkWebGPU, BROWSER_MODELS } from "./browser-inference";
96
+ export { InferenceFactory, createInferenceFactory, detectBestMethodForConfig, } from "./inference-factory";
97
+ export { createCapabilityDetectionService, DefaultCapabilityDetectionService, EnhancedCapabilityDetectionService, } from "./capability-detection";