@lov3kaizen/agentsea-embeddings 0.5.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,1235 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/providers/index.ts
21
+ var providers_exports = {};
22
+ __export(providers_exports, {
23
+ BaseProvider: () => BaseProvider,
24
+ CohereProvider: () => CohereProvider,
25
+ HuggingFaceProvider: () => HuggingFaceProvider,
26
+ LocalProvider: () => LocalProvider,
27
+ OpenAIProvider: () => OpenAIProvider,
28
+ VoyageProvider: () => VoyageProvider,
29
+ createCohereProvider: () => createCohereProvider,
30
+ createHuggingFaceProvider: () => createHuggingFaceProvider,
31
+ createLocalProvider: () => createLocalProvider,
32
+ createMockProvider: () => createMockProvider,
33
+ createOpenAIProvider: () => createOpenAIProvider,
34
+ createRandomProvider: () => createRandomProvider,
35
+ createVoyageProvider: () => createVoyageProvider
36
+ });
37
+ module.exports = __toCommonJS(providers_exports);
38
+
39
+ // src/core/EmbeddingModel.ts
40
+ var EmbeddingModel = class {
41
+ /**
42
+ * Get model dimensions
43
+ */
44
+ get dimensions() {
45
+ return this.info.dimensions;
46
+ }
47
+ /**
48
+ * Get max tokens
49
+ */
50
+ get maxTokens() {
51
+ return this.info.maxTokens;
52
+ }
53
+ /**
54
+ * Get max batch size
55
+ */
56
+ get maxBatchSize() {
57
+ return this.info.maxBatchSize;
58
+ }
59
+ /**
60
+ * Get model name
61
+ */
62
+ get name() {
63
+ return this.info.name;
64
+ }
65
+ /**
66
+ * Get provider name
67
+ */
68
+ get provider() {
69
+ return this.info.provider;
70
+ }
71
+ /**
72
+ * Count tokens in text (default implementation)
73
+ * Subclasses should override for accurate counting
74
+ */
75
+ countTokens(text) {
76
+ return Math.ceil(text.length / 4);
77
+ }
78
+ /**
79
+ * Check if text exceeds max tokens
80
+ */
81
+ exceedsMaxTokens(text) {
82
+ return this.countTokens(text) > this.maxTokens;
83
+ }
84
+ /**
85
+ * Truncate text to max tokens
86
+ */
87
+ truncateToMaxTokens(text) {
88
+ const tokens = this.countTokens(text);
89
+ if (tokens <= this.maxTokens) {
90
+ return text;
91
+ }
92
+ const ratio = this.maxTokens / tokens;
93
+ const targetLength = Math.floor(text.length * ratio * 0.95);
94
+ return text.slice(0, targetLength);
95
+ }
96
+ /**
97
+ * Calculate similarity between two vectors
98
+ */
99
+ static cosineSimilarity(a, b) {
100
+ if (a.length !== b.length) {
101
+ throw new Error(`Vector dimensions mismatch: ${a.length} vs ${b.length}`);
102
+ }
103
+ let dotProduct = 0;
104
+ let normA = 0;
105
+ let normB = 0;
106
+ for (let i = 0; i < a.length; i++) {
107
+ dotProduct += a[i] * b[i];
108
+ normA += a[i] * a[i];
109
+ normB += b[i] * b[i];
110
+ }
111
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
112
+ if (magnitude === 0) {
113
+ return 0;
114
+ }
115
+ return dotProduct / magnitude;
116
+ }
117
+ /**
118
+ * Calculate Euclidean distance between two vectors
119
+ */
120
+ static euclideanDistance(a, b) {
121
+ if (a.length !== b.length) {
122
+ throw new Error(`Vector dimensions mismatch: ${a.length} vs ${b.length}`);
123
+ }
124
+ let sum = 0;
125
+ for (let i = 0; i < a.length; i++) {
126
+ const diff = a[i] - b[i];
127
+ sum += diff * diff;
128
+ }
129
+ return Math.sqrt(sum);
130
+ }
131
+ /**
132
+ * Calculate dot product of two vectors
133
+ */
134
+ static dotProduct(a, b) {
135
+ if (a.length !== b.length) {
136
+ throw new Error(`Vector dimensions mismatch: ${a.length} vs ${b.length}`);
137
+ }
138
+ let result = 0;
139
+ for (let i = 0; i < a.length; i++) {
140
+ result += a[i] * b[i];
141
+ }
142
+ return result;
143
+ }
144
+ /**
145
+ * Normalize a vector to unit length
146
+ */
147
+ static normalize(vector) {
148
+ let norm = 0;
149
+ for (let i = 0; i < vector.length; i++) {
150
+ norm += vector[i] * vector[i];
151
+ }
152
+ norm = Math.sqrt(norm);
153
+ if (norm === 0) {
154
+ return vector.slice();
155
+ }
156
+ return vector.map((v) => v / norm);
157
+ }
158
+ /**
159
+ * Average multiple vectors
160
+ */
161
+ static average(vectors) {
162
+ if (vectors.length === 0) {
163
+ throw new Error("Cannot average empty array of vectors");
164
+ }
165
+ const dimensions = vectors[0].length;
166
+ const result = new Array(dimensions).fill(0);
167
+ for (const vector of vectors) {
168
+ if (vector.length !== dimensions) {
169
+ throw new Error(
170
+ `Vector dimensions mismatch: expected ${dimensions}, got ${vector.length}`
171
+ );
172
+ }
173
+ for (let i = 0; i < dimensions; i++) {
174
+ result[i] += vector[i];
175
+ }
176
+ }
177
+ for (let i = 0; i < dimensions; i++) {
178
+ result[i] /= vectors.length;
179
+ }
180
+ return result;
181
+ }
182
+ /**
183
+ * Weighted average of vectors
184
+ */
185
+ static weightedAverage(vectors, weights) {
186
+ if (vectors.length === 0) {
187
+ throw new Error("Cannot average empty array of vectors");
188
+ }
189
+ if (vectors.length !== weights.length) {
190
+ throw new Error("Vectors and weights arrays must have same length");
191
+ }
192
+ const dimensions = vectors[0].length;
193
+ const result = new Array(dimensions).fill(0);
194
+ let totalWeight = 0;
195
+ for (let j = 0; j < vectors.length; j++) {
196
+ const vector = vectors[j];
197
+ const weight = weights[j];
198
+ totalWeight += weight;
199
+ if (vector.length !== dimensions) {
200
+ throw new Error(
201
+ `Vector dimensions mismatch: expected ${dimensions}, got ${vector.length}`
202
+ );
203
+ }
204
+ for (let i = 0; i < dimensions; i++) {
205
+ result[i] += vector[i] * weight;
206
+ }
207
+ }
208
+ if (totalWeight === 0) {
209
+ throw new Error("Total weight cannot be zero");
210
+ }
211
+ for (let i = 0; i < dimensions; i++) {
212
+ result[i] /= totalWeight;
213
+ }
214
+ return result;
215
+ }
216
+ };
217
+ var ModelRegistry = class {
218
+ models = /* @__PURE__ */ new Map();
219
+ defaultModel = null;
220
+ /**
221
+ * Register a model
222
+ */
223
+ register(model, isDefault = false) {
224
+ const key = `${model.provider}:${model.name}`;
225
+ this.models.set(key, model);
226
+ if (isDefault || this.defaultModel === null) {
227
+ this.defaultModel = key;
228
+ }
229
+ }
230
+ /**
231
+ * Get a model by provider and name
232
+ */
233
+ get(provider, name) {
234
+ return this.models.get(`${provider}:${name}`);
235
+ }
236
+ /**
237
+ * Get model by key
238
+ */
239
+ getByKey(key) {
240
+ return this.models.get(key);
241
+ }
242
+ /**
243
+ * Get the default model
244
+ */
245
+ getDefault() {
246
+ if (this.defaultModel === null) {
247
+ return void 0;
248
+ }
249
+ return this.models.get(this.defaultModel);
250
+ }
251
+ /**
252
+ * Set default model
253
+ */
254
+ setDefault(provider, name) {
255
+ const key = `${provider}:${name}`;
256
+ if (!this.models.has(key)) {
257
+ throw new Error(`Model ${key} not found in registry`);
258
+ }
259
+ this.defaultModel = key;
260
+ }
261
+ /**
262
+ * List all registered models
263
+ */
264
+ list() {
265
+ return Array.from(this.models.values()).map((m) => m.info);
266
+ }
267
+ /**
268
+ * Check if a model is registered
269
+ */
270
+ has(provider, name) {
271
+ return this.models.has(`${provider}:${name}`);
272
+ }
273
+ /**
274
+ * Remove a model
275
+ */
276
+ remove(provider, name) {
277
+ const key = `${provider}:${name}`;
278
+ if (this.defaultModel === key) {
279
+ this.defaultModel = null;
280
+ }
281
+ return this.models.delete(key);
282
+ }
283
+ /**
284
+ * Clear all models
285
+ */
286
+ clear() {
287
+ this.models.clear();
288
+ this.defaultModel = null;
289
+ }
290
+ };
291
+ var modelRegistry = new ModelRegistry();
292
+
293
+ // src/core/utils.ts
294
+ function batch(items, batchSize) {
295
+ const batches = [];
296
+ for (let i = 0; i < items.length; i += batchSize) {
297
+ batches.push(items.slice(i, i + batchSize));
298
+ }
299
+ return batches;
300
+ }
301
+ async function withConcurrency(items, fn, concurrency) {
302
+ const results = new Array(items.length);
303
+ let currentIndex = 0;
304
+ async function worker() {
305
+ while (currentIndex < items.length) {
306
+ const index = currentIndex++;
307
+ results[index] = await fn(items[index], index);
308
+ }
309
+ }
310
+ const workers = Array.from(
311
+ { length: Math.min(concurrency, items.length) },
312
+ () => worker()
313
+ );
314
+ await Promise.all(workers);
315
+ return results;
316
+ }
317
+ async function retry(fn, options = {}) {
318
+ const {
319
+ maxRetries = 3,
320
+ initialDelay = 1e3,
321
+ maxDelay = 3e4,
322
+ backoffMultiplier = 2,
323
+ retryCondition = () => true
324
+ } = options;
325
+ let lastError;
326
+ let delay = initialDelay;
327
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
328
+ try {
329
+ return await fn();
330
+ } catch (error) {
331
+ lastError = error;
332
+ if (attempt === maxRetries || !retryCondition(lastError)) {
333
+ throw lastError;
334
+ }
335
+ await sleep(delay);
336
+ delay = Math.min(delay * backoffMultiplier, maxDelay);
337
+ }
338
+ }
339
+ throw lastError;
340
+ }
341
+ function sleep(ms) {
342
+ return new Promise((resolve) => setTimeout(resolve, ms));
343
+ }
344
+ async function measureTime(fn) {
345
+ const start = performance.now();
346
+ const result = await fn();
347
+ const durationMs = performance.now() - start;
348
+ return { result, durationMs };
349
+ }
350
+
351
+ // src/providers/BaseProvider.ts
352
+ var BaseProvider = class extends EmbeddingModel {
353
+ config;
354
+ metrics;
355
+ health;
356
+ latencies = [];
357
+ maxLatencySamples = 1e3;
358
+ constructor(config) {
359
+ super();
360
+ this.config = {
361
+ timeout: 3e4,
362
+ maxRetries: 3,
363
+ retryDelay: 1e3,
364
+ ...config
365
+ };
366
+ this.metrics = this.createInitialMetrics();
367
+ this.health = {
368
+ healthy: true,
369
+ latencyMs: 0,
370
+ lastCheck: Date.now()
371
+ };
372
+ }
373
+ createInitialMetrics() {
374
+ return {
375
+ provider: this.config.type,
376
+ totalRequests: 0,
377
+ successfulRequests: 0,
378
+ failedRequests: 0,
379
+ totalTokens: 0,
380
+ avgLatencyMs: 0,
381
+ p50LatencyMs: 0,
382
+ p95LatencyMs: 0,
383
+ p99LatencyMs: 0,
384
+ errorRate: 0,
385
+ rateLimitHits: 0,
386
+ estimatedCostUSD: 0
387
+ };
388
+ }
389
+ /**
390
+ * Generate embedding for a single text
391
+ */
392
+ async embed(text, options) {
393
+ const result = await this.embedBatch([text], options);
394
+ return result.results[0];
395
+ }
396
+ /**
397
+ * Generate embeddings for multiple texts
398
+ */
399
+ async embedBatch(texts, options) {
400
+ const startTime = performance.now();
401
+ const maxBatchSize = this.info.maxBatchSize;
402
+ const concurrency = options?.concurrency ?? 5;
403
+ const results = [];
404
+ let totalTokens = 0;
405
+ let failures = 0;
406
+ const batches = batch(texts, maxBatchSize);
407
+ const processBatch = async (batchTexts) => {
408
+ this.metrics.totalRequests++;
409
+ try {
410
+ const { result, durationMs } = await measureTime(
411
+ () => retry(() => this.doEmbed(batchTexts, options), {
412
+ maxRetries: this.config.maxRetries,
413
+ initialDelay: this.config.retryDelay,
414
+ retryCondition: (error) => this.isRetryable(error)
415
+ })
416
+ );
417
+ this.recordLatency(durationMs);
418
+ this.metrics.successfulRequests++;
419
+ this.metrics.totalTokens += result.tokenCount;
420
+ totalTokens += result.tokenCount;
421
+ return batchTexts.map((text, i) => ({
422
+ vector: result.vectors[i],
423
+ text,
424
+ tokenCount: Math.ceil(result.tokenCount / batchTexts.length),
425
+ cached: false,
426
+ model: this.info.name,
427
+ dimensions: this.info.dimensions,
428
+ latencyMs: durationMs / batchTexts.length
429
+ }));
430
+ } catch (error) {
431
+ this.metrics.failedRequests++;
432
+ this.health.healthy = false;
433
+ this.health.error = error.message;
434
+ if (options?.continueOnError) {
435
+ failures += batchTexts.length;
436
+ return [];
437
+ }
438
+ throw error;
439
+ }
440
+ };
441
+ const batchResults = await withConcurrency(
442
+ batches,
443
+ processBatch,
444
+ concurrency
445
+ );
446
+ for (const batchResult of batchResults) {
447
+ results.push(...batchResult);
448
+ }
449
+ const totalLatencyMs = performance.now() - startTime;
450
+ this.updateMetrics();
451
+ return {
452
+ results,
453
+ totalTokens,
454
+ totalLatencyMs,
455
+ cacheHits: 0,
456
+ cacheMisses: texts.length,
457
+ failures
458
+ };
459
+ }
460
+ /**
461
+ * Check if error is retryable
462
+ */
463
+ isRetryable(error) {
464
+ const message = error.message.toLowerCase();
465
+ return message.includes("rate limit") || message.includes("timeout") || message.includes("network") || message.includes("econnreset") || message.includes("502") || message.includes("503") || message.includes("504");
466
+ }
467
+ /**
468
+ * Record latency sample
469
+ */
470
+ recordLatency(latencyMs) {
471
+ this.latencies.push(latencyMs);
472
+ if (this.latencies.length > this.maxLatencySamples) {
473
+ this.latencies.shift();
474
+ }
475
+ }
476
+ /**
477
+ * Calculate percentile from latencies
478
+ */
479
+ calculatePercentile(p) {
480
+ if (this.latencies.length === 0) return 0;
481
+ const sorted = [...this.latencies].sort((a, b) => a - b);
482
+ const index = Math.ceil(p / 100 * sorted.length) - 1;
483
+ return sorted[Math.max(0, index)];
484
+ }
485
+ /**
486
+ * Update metrics
487
+ */
488
+ updateMetrics() {
489
+ const total = this.metrics.totalRequests;
490
+ if (total > 0) {
491
+ this.metrics.errorRate = this.metrics.failedRequests / total;
492
+ this.metrics.avgLatencyMs = this.latencies.reduce((a, b) => a + b, 0) / this.latencies.length || 0;
493
+ this.metrics.p50LatencyMs = this.calculatePercentile(50);
494
+ this.metrics.p95LatencyMs = this.calculatePercentile(95);
495
+ this.metrics.p99LatencyMs = this.calculatePercentile(99);
496
+ }
497
+ }
498
+ /**
499
+ * Get provider metrics
500
+ */
501
+ getMetrics() {
502
+ return { ...this.metrics };
503
+ }
504
+ /**
505
+ * Get provider health
506
+ */
507
+ getHealth() {
508
+ return { ...this.health };
509
+ }
510
+ /**
511
+ * Check provider health
512
+ */
513
+ async checkHealth() {
514
+ try {
515
+ const { durationMs } = await measureTime(
516
+ () => this.doEmbed(["health check"])
517
+ );
518
+ this.health = {
519
+ healthy: true,
520
+ latencyMs: durationMs,
521
+ lastCheck: Date.now()
522
+ };
523
+ } catch (error) {
524
+ this.health = {
525
+ healthy: false,
526
+ latencyMs: 0,
527
+ lastCheck: Date.now(),
528
+ error: error.message
529
+ };
530
+ }
531
+ return this.health;
532
+ }
533
+ /**
534
+ * Reset metrics
535
+ */
536
+ resetMetrics() {
537
+ this.metrics = this.createInitialMetrics();
538
+ this.latencies = [];
539
+ }
540
+ };
541
+
542
+ // src/providers/OpenAIProvider.ts
543
+ var OPENAI_MODELS = {
544
+ "text-embedding-3-small": {
545
+ name: "text-embedding-3-small",
546
+ provider: "openai",
547
+ dimensions: 1536,
548
+ maxTokens: 8191,
549
+ maxBatchSize: 2048,
550
+ costPer1K: 2e-5,
551
+ description: "Smaller, faster, cheaper embedding model"
552
+ },
553
+ "text-embedding-3-large": {
554
+ name: "text-embedding-3-large",
555
+ provider: "openai",
556
+ dimensions: 3072,
557
+ maxTokens: 8191,
558
+ maxBatchSize: 2048,
559
+ costPer1K: 13e-5,
560
+ description: "Larger, more powerful embedding model"
561
+ },
562
+ "text-embedding-ada-002": {
563
+ name: "text-embedding-ada-002",
564
+ provider: "openai",
565
+ dimensions: 1536,
566
+ maxTokens: 8191,
567
+ maxBatchSize: 2048,
568
+ costPer1K: 1e-4,
569
+ description: "Legacy embedding model"
570
+ }
571
+ };
572
+ var OpenAIProvider = class extends BaseProvider {
573
+ modelInfo;
574
+ apiKey;
575
+ baseUrl;
576
+ organization;
577
+ constructor(config) {
578
+ super({ ...config, type: "openai" });
579
+ if (!config.apiKey) {
580
+ throw new Error("OpenAI API key is required");
581
+ }
582
+ this.apiKey = config.apiKey;
583
+ this.baseUrl = config.baseUrl ?? "https://api.openai.com/v1";
584
+ this.organization = config.organization;
585
+ const modelName = config.model ?? "text-embedding-3-small";
586
+ const modelConfig = OPENAI_MODELS[modelName];
587
+ if (!modelConfig) {
588
+ this.modelInfo = {
589
+ name: modelName,
590
+ provider: "openai",
591
+ dimensions: config.dimensions ?? 1536,
592
+ maxTokens: 8191,
593
+ maxBatchSize: 2048,
594
+ costPer1K: 1e-4
595
+ };
596
+ } else {
597
+ this.modelInfo = {
598
+ ...modelConfig,
599
+ // Allow dimension override for text-embedding-3 models
600
+ dimensions: config.dimensions ?? modelConfig.dimensions
601
+ };
602
+ }
603
+ }
604
+ get info() {
605
+ return this.modelInfo;
606
+ }
607
+ async doEmbed(texts, options) {
608
+ const headers = {
609
+ "Content-Type": "application/json",
610
+ Authorization: `Bearer ${this.apiKey}`
611
+ };
612
+ if (this.organization) {
613
+ headers["OpenAI-Organization"] = this.organization;
614
+ }
615
+ const body = {
616
+ model: options?.model ?? this.modelInfo.name,
617
+ input: texts
618
+ };
619
+ if (this.modelInfo.name.startsWith("text-embedding-3")) {
620
+ const config2 = this.config;
621
+ if (config2.dimensions) {
622
+ body.dimensions = config2.dimensions;
623
+ }
624
+ }
625
+ const config = this.config;
626
+ if (config.encodingFormat) {
627
+ body.encoding_format = config.encodingFormat;
628
+ }
629
+ if (options?.user) {
630
+ body.user = options.user;
631
+ }
632
+ const response = await fetch(`${this.baseUrl}/embeddings`, {
633
+ method: "POST",
634
+ headers,
635
+ body: JSON.stringify(body),
636
+ signal: this.config.timeout ? AbortSignal.timeout(this.config.timeout) : void 0
637
+ });
638
+ if (!response.ok) {
639
+ const error = await response.json().catch(() => ({ error: { message: response.statusText } }));
640
+ const errorMessage = error.error?.message ?? response.statusText;
641
+ if (response.status === 429) {
642
+ this.metrics.rateLimitHits++;
643
+ }
644
+ throw new Error(`OpenAI API error: ${errorMessage} (${response.status})`);
645
+ }
646
+ const data = await response.json();
647
+ const embeddings = data.data.sort((a, b) => a.index - b.index);
648
+ const vectors = embeddings.map((e) => e.embedding);
649
+ const tokenCount = data.usage?.total_tokens ?? 0;
650
+ this.metrics.estimatedCostUSD += tokenCount / 1e3 * (this.modelInfo.costPer1K ?? 0);
651
+ return { vectors, tokenCount };
652
+ }
653
+ /**
654
+ * Count tokens using tiktoken approximation
655
+ */
656
+ countTokens(text) {
657
+ const words = text.split(/\s+/);
658
+ let tokens = 0;
659
+ for (const word of words) {
660
+ tokens += Math.ceil(word.length / 4) + 1;
661
+ }
662
+ return Math.max(1, tokens);
663
+ }
664
+ };
665
+ function createOpenAIProvider(config) {
666
+ return new OpenAIProvider(config);
667
+ }
668
+
669
+ // src/providers/CohereProvider.ts
670
+ var COHERE_MODELS = {
671
+ "embed-english-v3.0": {
672
+ name: "embed-english-v3.0",
673
+ provider: "cohere",
674
+ dimensions: 1024,
675
+ maxTokens: 512,
676
+ maxBatchSize: 96,
677
+ costPer1K: 1e-4,
678
+ description: "English embedding model v3"
679
+ },
680
+ "embed-multilingual-v3.0": {
681
+ name: "embed-multilingual-v3.0",
682
+ provider: "cohere",
683
+ dimensions: 1024,
684
+ maxTokens: 512,
685
+ maxBatchSize: 96,
686
+ costPer1K: 1e-4,
687
+ description: "Multilingual embedding model v3"
688
+ },
689
+ "embed-english-light-v3.0": {
690
+ name: "embed-english-light-v3.0",
691
+ provider: "cohere",
692
+ dimensions: 384,
693
+ maxTokens: 512,
694
+ maxBatchSize: 96,
695
+ costPer1K: 1e-4,
696
+ description: "Lightweight English embedding model v3"
697
+ },
698
+ "embed-multilingual-light-v3.0": {
699
+ name: "embed-multilingual-light-v3.0",
700
+ provider: "cohere",
701
+ dimensions: 384,
702
+ maxTokens: 512,
703
+ maxBatchSize: 96,
704
+ costPer1K: 1e-4,
705
+ description: "Lightweight multilingual embedding model v3"
706
+ },
707
+ "embed-english-v2.0": {
708
+ name: "embed-english-v2.0",
709
+ provider: "cohere",
710
+ dimensions: 4096,
711
+ maxTokens: 512,
712
+ maxBatchSize: 96,
713
+ costPer1K: 1e-4,
714
+ description: "Legacy English embedding model v2"
715
+ }
716
+ };
717
+ var CohereProvider = class extends BaseProvider {
718
+ modelInfo;
719
+ apiKey;
720
+ baseUrl;
721
+ inputType;
722
+ truncate;
723
+ constructor(config) {
724
+ super({ ...config, type: "cohere" });
725
+ if (!config.apiKey) {
726
+ throw new Error("Cohere API key is required");
727
+ }
728
+ this.apiKey = config.apiKey;
729
+ this.baseUrl = config.baseUrl ?? "https://api.cohere.ai/v1";
730
+ this.inputType = config.inputType ?? "search_document";
731
+ this.truncate = config.truncate ?? "END";
732
+ const modelName = config.model ?? "embed-english-v3.0";
733
+ const modelConfig = COHERE_MODELS[modelName];
734
+ if (!modelConfig) {
735
+ this.modelInfo = {
736
+ name: modelName,
737
+ provider: "cohere",
738
+ dimensions: 1024,
739
+ maxTokens: 512,
740
+ maxBatchSize: 96,
741
+ costPer1K: 1e-4
742
+ };
743
+ } else {
744
+ this.modelInfo = { ...modelConfig };
745
+ }
746
+ }
747
+ get info() {
748
+ return this.modelInfo;
749
+ }
750
+ async doEmbed(texts, options) {
751
+ const headers = {
752
+ "Content-Type": "application/json",
753
+ Authorization: `Bearer ${this.apiKey}`,
754
+ "Request-Source": "agentsea-embeddings"
755
+ };
756
+ const body = {
757
+ model: options?.model ?? this.modelInfo.name,
758
+ texts,
759
+ input_type: this.inputType,
760
+ truncate: this.truncate
761
+ };
762
+ const response = await fetch(`${this.baseUrl}/embed`, {
763
+ method: "POST",
764
+ headers,
765
+ body: JSON.stringify(body),
766
+ signal: this.config.timeout ? AbortSignal.timeout(this.config.timeout) : void 0
767
+ });
768
+ if (!response.ok) {
769
+ const error = await response.json().catch(() => ({ message: response.statusText }));
770
+ const errorMessage = error.message ?? response.statusText;
771
+ if (response.status === 429) {
772
+ this.metrics.rateLimitHits++;
773
+ }
774
+ throw new Error(`Cohere API error: ${errorMessage} (${response.status})`);
775
+ }
776
+ const data = await response.json();
777
+ const vectors = data.embeddings;
778
+ const tokenCount = texts.reduce(
779
+ (sum, text) => sum + this.countTokens(text),
780
+ 0
781
+ );
782
+ this.metrics.estimatedCostUSD += tokenCount / 1e3 * (this.modelInfo.costPer1K ?? 0);
783
+ return { vectors, tokenCount };
784
+ }
785
+ /**
786
+ * Set input type for embeddings
787
+ */
788
+ setInputType(inputType) {
789
+ this.inputType = inputType;
790
+ return this;
791
+ }
792
+ /**
793
+ * Count tokens (approximation)
794
+ */
795
+ countTokens(text) {
796
+ return Math.ceil(text.length / 4);
797
+ }
798
+ };
799
+ function createCohereProvider(config) {
800
+ return new CohereProvider(config);
801
+ }
802
+
803
+ // src/providers/VoyageProvider.ts
804
+ var VOYAGE_MODELS = {
805
+ "voyage-3": {
806
+ name: "voyage-3",
807
+ provider: "voyage",
808
+ dimensions: 1024,
809
+ maxTokens: 32e3,
810
+ maxBatchSize: 128,
811
+ costPer1K: 6e-5,
812
+ description: "Latest general-purpose embedding model"
813
+ },
814
+ "voyage-3-lite": {
815
+ name: "voyage-3-lite",
816
+ provider: "voyage",
817
+ dimensions: 512,
818
+ maxTokens: 32e3,
819
+ maxBatchSize: 128,
820
+ costPer1K: 2e-5,
821
+ description: "Lightweight general-purpose model"
822
+ },
823
+ "voyage-code-3": {
824
+ name: "voyage-code-3",
825
+ provider: "voyage",
826
+ dimensions: 1024,
827
+ maxTokens: 32e3,
828
+ maxBatchSize: 128,
829
+ costPer1K: 6e-5,
830
+ description: "Optimized for code retrieval"
831
+ },
832
+ "voyage-finance-2": {
833
+ name: "voyage-finance-2",
834
+ provider: "voyage",
835
+ dimensions: 1024,
836
+ maxTokens: 32e3,
837
+ maxBatchSize: 128,
838
+ costPer1K: 12e-5,
839
+ description: "Optimized for finance domain"
840
+ },
841
+ "voyage-law-2": {
842
+ name: "voyage-law-2",
843
+ provider: "voyage",
844
+ dimensions: 1024,
845
+ maxTokens: 32e3,
846
+ maxBatchSize: 128,
847
+ costPer1K: 12e-5,
848
+ description: "Optimized for legal domain"
849
+ },
850
+ "voyage-multilingual-2": {
851
+ name: "voyage-multilingual-2",
852
+ provider: "voyage",
853
+ dimensions: 1024,
854
+ maxTokens: 32e3,
855
+ maxBatchSize: 128,
856
+ costPer1K: 12e-5,
857
+ description: "Multilingual embedding model"
858
+ },
859
+ "voyage-2": {
860
+ name: "voyage-2",
861
+ provider: "voyage",
862
+ dimensions: 1024,
863
+ maxTokens: 4e3,
864
+ maxBatchSize: 128,
865
+ costPer1K: 1e-4,
866
+ description: "Previous generation model"
867
+ }
868
+ };
869
+ var VoyageProvider = class extends BaseProvider {
870
+ modelInfo;
871
+ apiKey;
872
+ baseUrl;
873
+ inputType;
874
+ truncation;
875
+ constructor(config) {
876
+ super({ ...config, type: "voyage" });
877
+ if (!config.apiKey) {
878
+ throw new Error("Voyage AI API key is required");
879
+ }
880
+ this.apiKey = config.apiKey;
881
+ this.baseUrl = config.baseUrl ?? "https://api.voyageai.com/v1";
882
+ this.inputType = config.inputType ?? "document";
883
+ this.truncation = config.truncation ?? true;
884
+ const modelName = config.model ?? "voyage-3";
885
+ const modelConfig = VOYAGE_MODELS[modelName];
886
+ if (!modelConfig) {
887
+ this.modelInfo = {
888
+ name: modelName,
889
+ provider: "voyage",
890
+ dimensions: 1024,
891
+ maxTokens: 32e3,
892
+ maxBatchSize: 128,
893
+ costPer1K: 1e-4
894
+ };
895
+ } else {
896
+ this.modelInfo = { ...modelConfig };
897
+ }
898
+ }
899
+ get info() {
900
+ return this.modelInfo;
901
+ }
902
+ async doEmbed(texts, options) {
903
+ const headers = {
904
+ "Content-Type": "application/json",
905
+ Authorization: `Bearer ${this.apiKey}`
906
+ };
907
+ const body = {
908
+ model: options?.model ?? this.modelInfo.name,
909
+ input: texts,
910
+ input_type: this.inputType,
911
+ truncation: this.truncation
912
+ };
913
+ const response = await fetch(`${this.baseUrl}/embeddings`, {
914
+ method: "POST",
915
+ headers,
916
+ body: JSON.stringify(body),
917
+ signal: this.config.timeout ? AbortSignal.timeout(this.config.timeout) : void 0
918
+ });
919
+ if (!response.ok) {
920
+ const error = await response.json().catch(() => ({ detail: response.statusText }));
921
+ const errorMessage = error.detail ?? response.statusText;
922
+ if (response.status === 429) {
923
+ this.metrics.rateLimitHits++;
924
+ }
925
+ throw new Error(
926
+ `Voyage AI API error: ${errorMessage} (${response.status})`
927
+ );
928
+ }
929
+ const data = await response.json();
930
+ const vectors = data.data.map((d) => d.embedding);
931
+ const tokenCount = data.usage?.total_tokens ?? texts.reduce((sum, text) => sum + this.countTokens(text), 0);
932
+ this.metrics.estimatedCostUSD += tokenCount / 1e3 * (this.modelInfo.costPer1K ?? 0);
933
+ return { vectors, tokenCount };
934
+ }
935
+ /**
936
+ * Set input type for embeddings
937
+ */
938
+ setInputType(inputType) {
939
+ this.inputType = inputType;
940
+ return this;
941
+ }
942
+ /**
943
+ * Count tokens (approximation)
944
+ */
945
+ countTokens(text) {
946
+ return Math.ceil(text.length / 4);
947
+ }
948
+ };
949
+ function createVoyageProvider(config) {
950
+ return new VoyageProvider(config);
951
+ }
952
+
953
+ // src/providers/LocalProvider.ts
954
+ var LocalProvider = class extends BaseProvider {
955
+ modelInfo;
956
+ embedFn = null;
957
+ normalize;
958
+ batchSize;
959
+ constructor(config) {
960
+ super({ ...config, type: "local" });
961
+ if (!config.embedFn && !config.modelPath) {
962
+ throw new Error(
963
+ "Either embedFn or modelPath is required for local provider"
964
+ );
965
+ }
966
+ this.embedFn = config.embedFn ?? null;
967
+ this.normalize = config.normalize ?? true;
968
+ this.batchSize = config.batchSize ?? 32;
969
+ this.modelInfo = {
970
+ name: config.name ?? config.modelPath ?? "local-model",
971
+ provider: "local",
972
+ dimensions: config.dimensions,
973
+ maxTokens: config.maxTokens ?? 512,
974
+ maxBatchSize: config.maxBatchSize ?? 32,
975
+ costPer1K: 0,
976
+ // Local models have no API cost
977
+ description: "Local embedding model"
978
+ };
979
+ }
980
+ get info() {
981
+ return this.modelInfo;
982
+ }
983
+ async doEmbed(texts, options) {
984
+ if (!this.embedFn) {
985
+ throw new Error("No embedding function configured");
986
+ }
987
+ let vectors = await this.embedFn(texts, options);
988
+ if (this.normalize) {
989
+ vectors = vectors.map((v) => EmbeddingModel.normalize(v));
990
+ }
991
+ const tokenCount = texts.reduce(
992
+ (sum, text) => sum + this.countTokens(text),
993
+ 0
994
+ );
995
+ return { vectors, tokenCount };
996
+ }
997
+ /**
998
+ * Set the embedding function
999
+ */
1000
+ setEmbedFunction(fn) {
1001
+ this.embedFn = fn;
1002
+ return this;
1003
+ }
1004
+ /**
1005
+ * Count tokens (simple approximation for local models)
1006
+ */
1007
+ countTokens(text) {
1008
+ return text.split(/\s+/).length;
1009
+ }
1010
+ };
1011
+ function createLocalProvider(config) {
1012
+ return new LocalProvider(config);
1013
+ }
1014
+ function createMockProvider(config) {
1015
+ const delay = config.delay ?? 10;
1016
+ return new LocalProvider({
1017
+ type: "local",
1018
+ dimensions: config.dimensions,
1019
+ name: config.name ?? "mock-model",
1020
+ embedFn: (texts) => {
1021
+ return new Promise((resolve) => setTimeout(resolve, delay)).then(() => {
1022
+ return texts.map((text) => {
1023
+ const hash = text.split("").reduce((acc, char) => {
1024
+ return (acc << 5) - acc + char.charCodeAt(0);
1025
+ }, 0);
1026
+ const vector = [];
1027
+ let seed = Math.abs(hash);
1028
+ for (let i = 0; i < config.dimensions; i++) {
1029
+ seed = seed * 1103515245 + 12345 & 2147483647;
1030
+ vector.push(seed / 2147483647 * 2 - 1);
1031
+ }
1032
+ const norm = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
1033
+ return vector.map((v) => v / norm);
1034
+ });
1035
+ });
1036
+ }
1037
+ });
1038
+ }
1039
+ function createRandomProvider(config) {
1040
+ return new LocalProvider({
1041
+ type: "local",
1042
+ dimensions: config.dimensions,
1043
+ name: config.name ?? "random-model",
1044
+ embedFn: (texts) => {
1045
+ return Promise.resolve(
1046
+ texts.map(() => {
1047
+ const vector = [];
1048
+ for (let i = 0; i < config.dimensions; i++) {
1049
+ vector.push(Math.random() * 2 - 1);
1050
+ }
1051
+ const norm = Math.sqrt(vector.reduce((sum, v) => sum + v * v, 0));
1052
+ return vector.map((v) => v / norm);
1053
+ })
1054
+ );
1055
+ }
1056
+ });
1057
+ }
1058
+
1059
+ // src/providers/HuggingFaceProvider.ts
1060
+ var HUGGINGFACE_MODELS = {
1061
+ "sentence-transformers/all-MiniLM-L6-v2": {
1062
+ dimensions: 384,
1063
+ maxTokens: 256,
1064
+ description: "Lightweight sentence transformer"
1065
+ },
1066
+ "sentence-transformers/all-mpnet-base-v2": {
1067
+ dimensions: 768,
1068
+ maxTokens: 384,
1069
+ description: "High quality sentence transformer"
1070
+ },
1071
+ "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2": {
1072
+ dimensions: 384,
1073
+ maxTokens: 128,
1074
+ description: "Multilingual sentence transformer"
1075
+ },
1076
+ "BAAI/bge-small-en-v1.5": {
1077
+ dimensions: 384,
1078
+ maxTokens: 512,
1079
+ description: "BGE small English model"
1080
+ },
1081
+ "BAAI/bge-base-en-v1.5": {
1082
+ dimensions: 768,
1083
+ maxTokens: 512,
1084
+ description: "BGE base English model"
1085
+ },
1086
+ "BAAI/bge-large-en-v1.5": {
1087
+ dimensions: 1024,
1088
+ maxTokens: 512,
1089
+ description: "BGE large English model"
1090
+ },
1091
+ "thenlper/gte-small": {
1092
+ dimensions: 384,
1093
+ maxTokens: 512,
1094
+ description: "GTE small model"
1095
+ },
1096
+ "thenlper/gte-base": {
1097
+ dimensions: 768,
1098
+ maxTokens: 512,
1099
+ description: "GTE base model"
1100
+ },
1101
+ "thenlper/gte-large": {
1102
+ dimensions: 1024,
1103
+ maxTokens: 512,
1104
+ description: "GTE large model"
1105
+ },
1106
+ "intfloat/e5-small-v2": {
1107
+ dimensions: 384,
1108
+ maxTokens: 512,
1109
+ description: "E5 small v2 model"
1110
+ },
1111
+ "intfloat/e5-base-v2": {
1112
+ dimensions: 768,
1113
+ maxTokens: 512,
1114
+ description: "E5 base v2 model"
1115
+ },
1116
+ "intfloat/e5-large-v2": {
1117
+ dimensions: 1024,
1118
+ maxTokens: 512,
1119
+ description: "E5 large v2 model"
1120
+ }
1121
+ };
1122
+ var HuggingFaceProvider = class extends BaseProvider {
1123
+ modelInfo;
1124
+ apiKey;
1125
+ baseUrl;
1126
+ waitForModel;
1127
+ constructor(config) {
1128
+ super({ ...config, type: "huggingface" });
1129
+ if (!config.apiKey) {
1130
+ throw new Error("HuggingFace API key is required");
1131
+ }
1132
+ this.apiKey = config.apiKey;
1133
+ this.waitForModel = config.waitForModel ?? true;
1134
+ const modelName = config.model ?? "sentence-transformers/all-MiniLM-L6-v2";
1135
+ const knownConfig = HUGGINGFACE_MODELS[modelName];
1136
+ this.baseUrl = config.baseUrl ?? `https://api-inference.huggingface.co/pipeline/feature-extraction/${modelName}`;
1137
+ this.modelInfo = {
1138
+ name: modelName,
1139
+ provider: "huggingface",
1140
+ dimensions: knownConfig?.dimensions ?? 768,
1141
+ maxTokens: knownConfig?.maxTokens ?? 512,
1142
+ maxBatchSize: 32,
1143
+ // HF inference API handles batching
1144
+ costPer1K: 0,
1145
+ // Free tier available
1146
+ description: knownConfig?.description ?? "HuggingFace model"
1147
+ };
1148
+ }
1149
+ get info() {
1150
+ return this.modelInfo;
1151
+ }
1152
+ async doEmbed(texts, _options) {
1153
+ const headers = {
1154
+ "Content-Type": "application/json",
1155
+ Authorization: `Bearer ${this.apiKey}`
1156
+ };
1157
+ const body = {
1158
+ inputs: texts,
1159
+ options: {
1160
+ wait_for_model: this.waitForModel
1161
+ }
1162
+ };
1163
+ const response = await fetch(this.baseUrl, {
1164
+ method: "POST",
1165
+ headers,
1166
+ body: JSON.stringify(body),
1167
+ signal: this.config.timeout ? AbortSignal.timeout(this.config.timeout) : void 0
1168
+ });
1169
+ if (!response.ok) {
1170
+ const error = await response.json().catch(() => ({ error: response.statusText }));
1171
+ const errorMessage = error.error ?? response.statusText;
1172
+ if (response.status === 429) {
1173
+ this.metrics.rateLimitHits++;
1174
+ }
1175
+ throw new Error(
1176
+ `HuggingFace API error: ${errorMessage} (${response.status})`
1177
+ );
1178
+ }
1179
+ const data = await response.json();
1180
+ let vectors;
1181
+ if (Array.isArray(data) && Array.isArray(data[0])) {
1182
+ if (typeof data[0][0] === "number") {
1183
+ vectors = data;
1184
+ } else {
1185
+ vectors = data.map((tokenEmbeddings) => {
1186
+ const dims = tokenEmbeddings[0]?.length ?? this.modelInfo.dimensions;
1187
+ const mean = new Array(dims).fill(0);
1188
+ for (const embedding of tokenEmbeddings) {
1189
+ for (let i = 0; i < dims; i++) {
1190
+ mean[i] += embedding[i];
1191
+ }
1192
+ }
1193
+ return mean.map((v) => v / tokenEmbeddings.length);
1194
+ });
1195
+ }
1196
+ } else {
1197
+ vectors = [data];
1198
+ }
1199
+ const tokenCount = texts.reduce(
1200
+ (sum, text) => sum + this.countTokens(text),
1201
+ 0
1202
+ );
1203
+ return { vectors, tokenCount };
1204
+ }
1205
+ /**
1206
+ * Count tokens (approximation based on wordpiece)
1207
+ */
1208
+ countTokens(text) {
1209
+ const words = text.split(/\s+/);
1210
+ let tokens = 0;
1211
+ for (const word of words) {
1212
+ tokens += Math.ceil(word.length / 5) + 1;
1213
+ }
1214
+ return Math.max(1, tokens);
1215
+ }
1216
+ };
1217
+ function createHuggingFaceProvider(config) {
1218
+ return new HuggingFaceProvider(config);
1219
+ }
1220
+ // Annotate the CommonJS export names for ESM import in node:
1221
+ 0 && (module.exports = {
1222
+ BaseProvider,
1223
+ CohereProvider,
1224
+ HuggingFaceProvider,
1225
+ LocalProvider,
1226
+ OpenAIProvider,
1227
+ VoyageProvider,
1228
+ createCohereProvider,
1229
+ createHuggingFaceProvider,
1230
+ createLocalProvider,
1231
+ createMockProvider,
1232
+ createOpenAIProvider,
1233
+ createRandomProvider,
1234
+ createVoyageProvider
1235
+ });