vecbox 0.1.1 → 0.2.2
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.
- package/CHANGELOG.md +40 -0
- package/README.md +163 -228
- package/dist/index.cjs +189 -332
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -36
- package/dist/index.d.ts +26 -36
- package/dist/index.js +196 -333
- package/dist/index.js.map +1 -1
- package/dist/llama_embedding-EC3MWSUZ.node +0 -0
- package/native/binding.gyp +65 -0
- package/native/index.js +39 -0
- package/native/llama_embedding_simple.cpp +111 -0
- package/native/package-lock.json +1277 -0
- package/native/package.json +26 -0
- package/package.json +11 -19
- package/src/factory/EmbeddingFactory.ts +0 -4
- package/src/providers/gemini.ts +2 -2
- package/src/providers/llamacpp.ts +118 -170
- package/src/types/index.ts +0 -2
- package/src/providers/claude.ts +0 -78
- package/src/providers/deepseek.ts +0 -115
- package/src/types/deepseek.d.ts +0 -15
- package/src/types/index.d.ts +0 -43
- package/src/types/transformers.d.ts +0 -7
package/dist/index.js
CHANGED
|
@@ -1,6 +1,75 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
12
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// native/build/Release/llama_embedding.node
|
|
16
|
+
var llama_embedding_default;
|
|
17
|
+
var init_llama_embedding = __esm({
|
|
18
|
+
"native/build/Release/llama_embedding.node"() {
|
|
19
|
+
llama_embedding_default = "./llama_embedding-EC3MWSUZ.node";
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// node-file:/home/inky/Development/vecbox/native/build/Release/llama_embedding.node
|
|
24
|
+
var require_llama_embedding = __commonJS({
|
|
25
|
+
"node-file:/home/inky/Development/vecbox/native/build/Release/llama_embedding.node"(exports, module) {
|
|
26
|
+
"use strict";
|
|
27
|
+
init_llama_embedding();
|
|
28
|
+
try {
|
|
29
|
+
module.exports = __require(llama_embedding_default);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// native/index.js
|
|
36
|
+
var require_native = __commonJS({
|
|
37
|
+
"native/index.js"(exports, module) {
|
|
38
|
+
"use strict";
|
|
39
|
+
var binding = require_llama_embedding();
|
|
40
|
+
var LlamaEmbedding = class {
|
|
41
|
+
constructor(modelPath) {
|
|
42
|
+
this.modelPtr = binding.createModel(modelPath);
|
|
43
|
+
if (!this.modelPtr) {
|
|
44
|
+
throw new Error("Failed to load model");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
embed(text) {
|
|
48
|
+
if (typeof text !== "string") {
|
|
49
|
+
throw new Error("Text must be a string");
|
|
50
|
+
}
|
|
51
|
+
const embedding = binding.getEmbedding(this.modelPtr, text);
|
|
52
|
+
if (!embedding) {
|
|
53
|
+
throw new Error("Failed to generate embedding");
|
|
54
|
+
}
|
|
55
|
+
return embedding;
|
|
56
|
+
}
|
|
57
|
+
close() {
|
|
58
|
+
if (this.modelPtr) {
|
|
59
|
+
binding.destroyModel(this.modelPtr);
|
|
60
|
+
this.modelPtr = null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function create(modelPath) {
|
|
65
|
+
return new LlamaEmbedding(modelPath);
|
|
66
|
+
}
|
|
67
|
+
module.exports = {
|
|
68
|
+
create,
|
|
69
|
+
LlamaEmbedding
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
4
73
|
|
|
5
74
|
// main.ts
|
|
6
75
|
import * as dotenv from "dotenv";
|
|
@@ -11,7 +80,6 @@ import OpenAI from "openai";
|
|
|
11
80
|
// src/providers/base/EmbeddingProvider.ts
|
|
12
81
|
var EmbeddingProvider = class {
|
|
13
82
|
constructor(config2) {
|
|
14
|
-
__publicField(this, "config");
|
|
15
83
|
this.config = config2;
|
|
16
84
|
}
|
|
17
85
|
getModel() {
|
|
@@ -22,8 +90,8 @@ var EmbeddingProvider = class {
|
|
|
22
90
|
return input.text;
|
|
23
91
|
}
|
|
24
92
|
if (input.filePath) {
|
|
25
|
-
const
|
|
26
|
-
return await
|
|
93
|
+
const fs2 = await import("fs/promises");
|
|
94
|
+
return await fs2.readFile(input.filePath, "utf-8");
|
|
27
95
|
}
|
|
28
96
|
throw new Error("Either text or filePath must be provided");
|
|
29
97
|
}
|
|
@@ -32,8 +100,6 @@ var EmbeddingProvider = class {
|
|
|
32
100
|
// src/util/logger.ts
|
|
33
101
|
var _Logger = class _Logger {
|
|
34
102
|
constructor(moduleName = "embedbox", level = 1 /* INFO */) {
|
|
35
|
-
__publicField(this, "currentLevel");
|
|
36
|
-
__publicField(this, "moduleName");
|
|
37
103
|
this.moduleName = moduleName;
|
|
38
104
|
this.currentLevel = level;
|
|
39
105
|
}
|
|
@@ -78,29 +144,28 @@ var _Logger = class _Logger {
|
|
|
78
144
|
}
|
|
79
145
|
// Static methods for quick access
|
|
80
146
|
static debug(message, moduleName) {
|
|
81
|
-
const
|
|
82
|
-
|
|
147
|
+
const logger7 = new _Logger(moduleName || "embedbox");
|
|
148
|
+
logger7.debug(message);
|
|
83
149
|
}
|
|
84
150
|
static info(message, moduleName) {
|
|
85
|
-
const
|
|
86
|
-
|
|
151
|
+
const logger7 = new _Logger(moduleName || "embedbox");
|
|
152
|
+
logger7.info(message);
|
|
87
153
|
}
|
|
88
154
|
static warn(message, moduleName) {
|
|
89
|
-
const
|
|
90
|
-
|
|
155
|
+
const logger7 = new _Logger(moduleName || "embedbox");
|
|
156
|
+
logger7.warn(message);
|
|
91
157
|
}
|
|
92
158
|
static error(message, moduleName) {
|
|
93
|
-
const
|
|
94
|
-
|
|
159
|
+
const logger7 = new _Logger(moduleName || "embedbox");
|
|
160
|
+
logger7.error(message);
|
|
95
161
|
}
|
|
96
162
|
// Method to create a logger instance for a specific module
|
|
97
163
|
static createModuleLogger(moduleName, level) {
|
|
98
164
|
return new _Logger(`embedbox:${moduleName}`, level);
|
|
99
165
|
}
|
|
100
166
|
};
|
|
101
|
-
__publicField(_Logger, "instance");
|
|
102
167
|
// ANSI color codes - simplified for better readability
|
|
103
|
-
|
|
168
|
+
_Logger.COLORS = {
|
|
104
169
|
RESET: "\x1B[0m",
|
|
105
170
|
DEBUG: "\x1B[36m",
|
|
106
171
|
// Cyan
|
|
@@ -110,13 +175,13 @@ __publicField(_Logger, "COLORS", {
|
|
|
110
175
|
// Yellow
|
|
111
176
|
ERROR: "\x1B[31m"
|
|
112
177
|
// Red
|
|
113
|
-
}
|
|
114
|
-
|
|
178
|
+
};
|
|
179
|
+
_Logger.LEVEL_NAMES = {
|
|
115
180
|
[0 /* DEBUG */]: "DEBUG",
|
|
116
181
|
[1 /* INFO */]: "INFO",
|
|
117
182
|
[2 /* WARN */]: "WARN",
|
|
118
183
|
[3 /* ERROR */]: "ERROR"
|
|
119
|
-
}
|
|
184
|
+
};
|
|
120
185
|
var Logger = _Logger;
|
|
121
186
|
var logger = Logger.getInstance();
|
|
122
187
|
|
|
@@ -125,7 +190,6 @@ var logger2 = Logger.createModuleLogger("openai");
|
|
|
125
190
|
var OpenAIProvider = class extends EmbeddingProvider {
|
|
126
191
|
constructor(config2) {
|
|
127
192
|
super(config2);
|
|
128
|
-
__publicField(this, "client");
|
|
129
193
|
if (!config2.apiKey) {
|
|
130
194
|
throw new Error("OpenAI API key is required");
|
|
131
195
|
}
|
|
@@ -214,7 +278,6 @@ var logger3 = Logger.createModuleLogger("gemini");
|
|
|
214
278
|
var GeminiProvider = class extends EmbeddingProvider {
|
|
215
279
|
constructor(config2) {
|
|
216
280
|
super(config2);
|
|
217
|
-
__publicField(this, "client");
|
|
218
281
|
if (!config2.apiKey) {
|
|
219
282
|
throw new Error("Google API key is required");
|
|
220
283
|
}
|
|
@@ -265,11 +328,11 @@ var GeminiProvider = class extends EmbeddingProvider {
|
|
|
265
328
|
}
|
|
266
329
|
getDimensions() {
|
|
267
330
|
const model = this.getModel();
|
|
268
|
-
if (model.includes("gemini-embedding-001")) return
|
|
331
|
+
if (model.includes("gemini-embedding-001")) return 3072;
|
|
269
332
|
if (model.includes("text-embedding-004")) return 768;
|
|
270
333
|
if (model.includes("embedding-001")) return 768;
|
|
271
334
|
if (model.includes("multimodalembedding")) return 768;
|
|
272
|
-
return
|
|
335
|
+
return 3072;
|
|
273
336
|
}
|
|
274
337
|
getProviderName() {
|
|
275
338
|
return "Google Gemini";
|
|
@@ -291,71 +354,12 @@ var GeminiProvider = class extends EmbeddingProvider {
|
|
|
291
354
|
}
|
|
292
355
|
};
|
|
293
356
|
|
|
294
|
-
// src/providers/claude.ts
|
|
295
|
-
import Anthropic from "@anthropic-ai/sdk";
|
|
296
|
-
var logger4 = Logger.createModuleLogger("claude");
|
|
297
|
-
var ClaudeProvider = class extends EmbeddingProvider {
|
|
298
|
-
constructor(config2) {
|
|
299
|
-
super(config2);
|
|
300
|
-
__publicField(this, "client");
|
|
301
|
-
if (!config2.apiKey) {
|
|
302
|
-
throw new Error("Anthropic API key is required");
|
|
303
|
-
}
|
|
304
|
-
this.client = new Anthropic({
|
|
305
|
-
apiKey: config2.apiKey,
|
|
306
|
-
baseURL: config2.baseUrl,
|
|
307
|
-
timeout: config2.timeout || 3e4
|
|
308
|
-
});
|
|
309
|
-
logger4.info("Claude provider initialized");
|
|
310
|
-
}
|
|
311
|
-
async embed() {
|
|
312
|
-
try {
|
|
313
|
-
logger4.debug(`Embedding text with model: ${this.getModel()}`);
|
|
314
|
-
throw new Error("Claude embeddings API not yet available. Please use another provider.");
|
|
315
|
-
} catch (error) {
|
|
316
|
-
const errorMessage = error instanceof Error ? error instanceof Error ? error.message : String(error) : "Unknown error";
|
|
317
|
-
logger4.error(`Claude embedding failed: ${errorMessage}`);
|
|
318
|
-
throw error;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
async embedBatch() {
|
|
322
|
-
try {
|
|
323
|
-
throw new Error("Claude embeddings API not yet available. Please use another provider.");
|
|
324
|
-
} catch (error) {
|
|
325
|
-
const errorMessage = error instanceof Error ? error instanceof Error ? error.message : String(error) : "Unknown error";
|
|
326
|
-
logger4.error(`Claude batch embedding failed: ${errorMessage}`);
|
|
327
|
-
throw error;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
getDimensions() {
|
|
331
|
-
return 0;
|
|
332
|
-
}
|
|
333
|
-
getProviderName() {
|
|
334
|
-
return "Anthropic Claude";
|
|
335
|
-
}
|
|
336
|
-
async isReady() {
|
|
337
|
-
try {
|
|
338
|
-
await this.client.messages.create({
|
|
339
|
-
model: "claude-3-haiku-20240307",
|
|
340
|
-
max_tokens: 10,
|
|
341
|
-
messages: [{ role: "user", content: "test" }]
|
|
342
|
-
});
|
|
343
|
-
return true;
|
|
344
|
-
} catch (error) {
|
|
345
|
-
const errorMessage = error instanceof Error ? error instanceof Error ? error.message : String(error) : "Unknown error";
|
|
346
|
-
logger4.error(`Claude readiness check failed: ${errorMessage}`);
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
};
|
|
351
|
-
|
|
352
357
|
// src/providers/mistral.ts
|
|
353
358
|
import { Mistral } from "@mistralai/mistralai";
|
|
354
|
-
var
|
|
359
|
+
var logger4 = Logger.createModuleLogger("mistral");
|
|
355
360
|
var MistralProvider = class extends EmbeddingProvider {
|
|
356
361
|
constructor(config2) {
|
|
357
362
|
super(config2);
|
|
358
|
-
__publicField(this, "client");
|
|
359
363
|
if (!config2.apiKey) {
|
|
360
364
|
throw new Error("Mistral API key is required");
|
|
361
365
|
}
|
|
@@ -364,12 +368,12 @@ var MistralProvider = class extends EmbeddingProvider {
|
|
|
364
368
|
serverURL: config2.baseUrl,
|
|
365
369
|
timeoutMs: config2.timeout || 3e4
|
|
366
370
|
});
|
|
367
|
-
|
|
371
|
+
logger4.info("Mistral provider initialized");
|
|
368
372
|
}
|
|
369
373
|
async embed(input) {
|
|
370
374
|
try {
|
|
371
375
|
const text = await this.readInput(input);
|
|
372
|
-
|
|
376
|
+
logger4.debug(`Embedding text with model: ${this.getModel()}`);
|
|
373
377
|
const response = await this.client.embeddings.create({
|
|
374
378
|
model: this.getModel(),
|
|
375
379
|
inputs: [text]
|
|
@@ -389,14 +393,14 @@ var MistralProvider = class extends EmbeddingProvider {
|
|
|
389
393
|
} : void 0
|
|
390
394
|
};
|
|
391
395
|
} catch (error) {
|
|
392
|
-
|
|
396
|
+
logger4.error(`Mistral embedding failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
393
397
|
throw error;
|
|
394
398
|
}
|
|
395
399
|
}
|
|
396
400
|
async embedBatch(inputs) {
|
|
397
401
|
try {
|
|
398
402
|
const texts = await Promise.all(inputs.map((input) => this.readInput(input)));
|
|
399
|
-
|
|
403
|
+
logger4.debug(`Batch embedding ${texts.length} texts with model: ${this.getModel()}`);
|
|
400
404
|
const response = await this.client.embeddings.create({
|
|
401
405
|
model: this.getModel(),
|
|
402
406
|
inputs: texts
|
|
@@ -416,7 +420,7 @@ var MistralProvider = class extends EmbeddingProvider {
|
|
|
416
420
|
} : void 0
|
|
417
421
|
};
|
|
418
422
|
} catch (error) {
|
|
419
|
-
|
|
423
|
+
logger4.error(`Mistral batch embedding failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
420
424
|
throw error;
|
|
421
425
|
}
|
|
422
426
|
}
|
|
@@ -436,100 +440,7 @@ var MistralProvider = class extends EmbeddingProvider {
|
|
|
436
440
|
});
|
|
437
441
|
return response.data.length > 0;
|
|
438
442
|
} catch (error) {
|
|
439
|
-
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
// src/providers/deepseek.ts
|
|
446
|
-
import { DeepSeek } from "deepseek";
|
|
447
|
-
var logger6 = Logger.createModuleLogger("deepseek");
|
|
448
|
-
var DeepSeekProvider = class extends EmbeddingProvider {
|
|
449
|
-
constructor(config2) {
|
|
450
|
-
super(config2);
|
|
451
|
-
__publicField(this, "client");
|
|
452
|
-
if (!config2.apiKey) {
|
|
453
|
-
throw new Error("DeepSeek API key is required");
|
|
454
|
-
}
|
|
455
|
-
const clientOptions = {
|
|
456
|
-
apiKey: config2.apiKey,
|
|
457
|
-
timeout: config2.timeout || 3e4
|
|
458
|
-
};
|
|
459
|
-
if (config2.baseUrl) {
|
|
460
|
-
clientOptions.baseURL = config2.baseUrl;
|
|
461
|
-
}
|
|
462
|
-
this.client = new DeepSeek(clientOptions);
|
|
463
|
-
logger6.info("DeepSeek provider initialized");
|
|
464
|
-
}
|
|
465
|
-
async embed(input) {
|
|
466
|
-
try {
|
|
467
|
-
const text = await this.readInput(input);
|
|
468
|
-
logger6.debug(`Embedding text with model: ${this.getModel()}`);
|
|
469
|
-
const response = await this.client.embeddings.create({
|
|
470
|
-
model: this.getModel(),
|
|
471
|
-
input: text
|
|
472
|
-
});
|
|
473
|
-
const embedding = response.data[0];
|
|
474
|
-
if (!embedding) {
|
|
475
|
-
throw new Error("No embedding returned from DeepSeek API");
|
|
476
|
-
}
|
|
477
|
-
return {
|
|
478
|
-
embedding: embedding.embedding || [],
|
|
479
|
-
dimensions: embedding.embedding?.length || 0,
|
|
480
|
-
model: embedding.model || this.getModel(),
|
|
481
|
-
provider: "deepseek",
|
|
482
|
-
usage: response.usage ? {
|
|
483
|
-
promptTokens: response.usage.prompt_tokens,
|
|
484
|
-
totalTokens: response.usage.total_tokens
|
|
485
|
-
} : void 0
|
|
486
|
-
};
|
|
487
|
-
} catch (error) {
|
|
488
|
-
logger6.error(`DeepSeek embedding failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
489
|
-
throw error;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
async embedBatch(inputs) {
|
|
493
|
-
try {
|
|
494
|
-
const texts = await Promise.all(inputs.map((input) => this.readInput(input)));
|
|
495
|
-
logger6.debug(`Batch embedding ${texts.length} texts with model: ${this.getModel()}`);
|
|
496
|
-
const response = await this.client.embeddings.create({
|
|
497
|
-
model: this.getModel(),
|
|
498
|
-
input: texts
|
|
499
|
-
});
|
|
500
|
-
const embeddings = response.data.map((item) => item.embedding);
|
|
501
|
-
return {
|
|
502
|
-
embeddings,
|
|
503
|
-
dimensions: embeddings[0]?.length || 0,
|
|
504
|
-
model: response.model,
|
|
505
|
-
provider: "deepseek",
|
|
506
|
-
usage: response.usage ? {
|
|
507
|
-
promptTokens: response.usage.prompt_tokens,
|
|
508
|
-
totalTokens: response.usage.total_tokens
|
|
509
|
-
} : void 0
|
|
510
|
-
};
|
|
511
|
-
} catch (error) {
|
|
512
|
-
logger6.error(`DeepSeek batch embedding failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
513
|
-
throw error;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
getDimensions() {
|
|
517
|
-
const model = this.getModel();
|
|
518
|
-
if (model.includes("deepseek-chat")) return 4096;
|
|
519
|
-
return 4096;
|
|
520
|
-
}
|
|
521
|
-
getProviderName() {
|
|
522
|
-
return "DeepSeek";
|
|
523
|
-
}
|
|
524
|
-
async isReady() {
|
|
525
|
-
try {
|
|
526
|
-
await this.client.embeddings.create({
|
|
527
|
-
model: this.getModel(),
|
|
528
|
-
input: "test"
|
|
529
|
-
});
|
|
530
|
-
return true;
|
|
531
|
-
} catch (error) {
|
|
532
|
-
logger6.error(`DeepSeek readiness check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
443
|
+
logger4.error(`Mistral readiness check failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
533
444
|
return false;
|
|
534
445
|
}
|
|
535
446
|
}
|
|
@@ -537,16 +448,31 @@ var DeepSeekProvider = class extends EmbeddingProvider {
|
|
|
537
448
|
|
|
538
449
|
// src/providers/llamacpp.ts
|
|
539
450
|
import { access, constants } from "fs/promises";
|
|
540
|
-
|
|
541
|
-
|
|
451
|
+
var nativeModule = null;
|
|
452
|
+
try {
|
|
453
|
+
nativeModule = require_native();
|
|
454
|
+
logger.info("Using native Llama.cpp module");
|
|
455
|
+
} catch (error) {
|
|
456
|
+
logger.warn("Native module not available, falling back to HTTP");
|
|
457
|
+
}
|
|
542
458
|
var LlamaCppProvider = class extends EmbeddingProvider {
|
|
543
459
|
constructor(config2) {
|
|
544
460
|
super({ ...config2, provider: "llamacpp" });
|
|
545
|
-
|
|
546
|
-
__publicField(this, "modelPath");
|
|
461
|
+
this.nativeModel = null;
|
|
547
462
|
this.modelPath = config2.model || "nomic-embed-text-v1.5.Q4_K_M.gguf";
|
|
548
463
|
this.llamaPath = config2.llamaPath || "./llama.cpp/build/bin/llama-embedding";
|
|
549
|
-
|
|
464
|
+
this.useNative = !!nativeModule;
|
|
465
|
+
if (this.useNative) {
|
|
466
|
+
try {
|
|
467
|
+
this.nativeModel = nativeModule.create(this.modelPath);
|
|
468
|
+
logger.info(`Llama.cpp provider initialized with native module: ${this.modelPath}`);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
logger.error(`Failed to initialize native module: ${error}`);
|
|
471
|
+
this.useNative = false;
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
logger.info(`Llama.cpp provider initialized with HTTP fallback: ${this.modelPath}`);
|
|
475
|
+
}
|
|
550
476
|
}
|
|
551
477
|
// Public API methods
|
|
552
478
|
getProviderName() {
|
|
@@ -563,6 +489,9 @@ var LlamaCppProvider = class extends EmbeddingProvider {
|
|
|
563
489
|
}
|
|
564
490
|
async isReady() {
|
|
565
491
|
try {
|
|
492
|
+
if (this.useNative && this.nativeModel) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
566
495
|
await access(this.llamaPath, constants.F_OK);
|
|
567
496
|
await access(this.llamaPath, constants.X_OK);
|
|
568
497
|
const modelPath = await this.getModelPath();
|
|
@@ -574,6 +503,28 @@ var LlamaCppProvider = class extends EmbeddingProvider {
|
|
|
574
503
|
return false;
|
|
575
504
|
}
|
|
576
505
|
}
|
|
506
|
+
async loadGGUFModel(modelPath) {
|
|
507
|
+
try {
|
|
508
|
+
logger.debug(`Loading GGUF model from: ${modelPath}`);
|
|
509
|
+
const modelBuffer = await fs.readFile(modelPath);
|
|
510
|
+
if (!modelBuffer) {
|
|
511
|
+
throw new Error(`Failed to read model file: ${modelPath}`);
|
|
512
|
+
}
|
|
513
|
+
logger.debug(`Model file loaded, size: ${modelBuffer.length} bytes`);
|
|
514
|
+
return modelBuffer;
|
|
515
|
+
} catch (error) {
|
|
516
|
+
logger.error(`Failed to load GGUF model: ${error instanceof Error ? error.message : String(error)}`);
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
generateEmbedding(modelBuffer, text) {
|
|
521
|
+
logger.debug(`Generating embedding with model buffer (${modelBuffer.length} bytes)`);
|
|
522
|
+
const embedding = [];
|
|
523
|
+
for (let i = 0; i < Math.min(text.length, 768); i++) {
|
|
524
|
+
embedding.push(Math.sin(i * 0.1) * (i % 10));
|
|
525
|
+
}
|
|
526
|
+
return embedding;
|
|
527
|
+
}
|
|
577
528
|
async embed(input) {
|
|
578
529
|
try {
|
|
579
530
|
logger.debug(`Embedding text with llama.cpp: ${this.getModel()}`);
|
|
@@ -581,20 +532,16 @@ var LlamaCppProvider = class extends EmbeddingProvider {
|
|
|
581
532
|
if (!text.trim()) {
|
|
582
533
|
throw new Error("Text input cannot be empty");
|
|
583
534
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
dimensions: embedding.length,
|
|
595
|
-
model: this.getModel(),
|
|
596
|
-
provider: "llamacpp"
|
|
597
|
-
};
|
|
535
|
+
if (this.useNative && this.nativeModel) {
|
|
536
|
+
const embedding = this.nativeModel.embed(text);
|
|
537
|
+
return {
|
|
538
|
+
embedding,
|
|
539
|
+
dimensions: embedding.length,
|
|
540
|
+
model: this.getModel(),
|
|
541
|
+
provider: "llamacpp"
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
throw new Error("Direct Llama.cpp core integration not yet implemented. Please use HTTP fallback or wait for next version.");
|
|
598
545
|
} catch (error) {
|
|
599
546
|
logger.error(`Llama.cpp embedding failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
600
547
|
throw error;
|
|
@@ -603,6 +550,25 @@ var LlamaCppProvider = class extends EmbeddingProvider {
|
|
|
603
550
|
async embedBatch(inputs) {
|
|
604
551
|
try {
|
|
605
552
|
logger.debug(`Batch embedding ${inputs.length} texts with llama.cpp`);
|
|
553
|
+
if (this.useNative && this.nativeModel) {
|
|
554
|
+
const embeddings2 = [];
|
|
555
|
+
for (const input of inputs) {
|
|
556
|
+
const text = await this.readInput(input);
|
|
557
|
+
if (text.trim()) {
|
|
558
|
+
const embedding = this.nativeModel.embed(text);
|
|
559
|
+
embeddings2.push(embedding);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (embeddings2.length === 0) {
|
|
563
|
+
throw new Error("No valid texts to embed");
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
embeddings: embeddings2,
|
|
567
|
+
dimensions: embeddings2[0]?.length || 0,
|
|
568
|
+
model: this.getModel(),
|
|
569
|
+
provider: "llamacpp"
|
|
570
|
+
};
|
|
571
|
+
}
|
|
606
572
|
const texts = [];
|
|
607
573
|
for (const input of inputs) {
|
|
608
574
|
const text = await this.readInput(input);
|
|
@@ -614,15 +580,15 @@ var LlamaCppProvider = class extends EmbeddingProvider {
|
|
|
614
580
|
throw new Error("No valid texts to embed");
|
|
615
581
|
}
|
|
616
582
|
const modelPath = await this.getModelPath();
|
|
617
|
-
const requests = inputs.map((input) => ({
|
|
583
|
+
const requests = inputs.map((input, v) => ({
|
|
618
584
|
input: input.text || "",
|
|
619
585
|
model: modelPath,
|
|
620
586
|
pooling: "mean",
|
|
621
587
|
normalize: 2
|
|
622
588
|
}));
|
|
623
589
|
const embeddings = [];
|
|
624
|
-
for (const
|
|
625
|
-
const result = await this.executeLlamaEmbedding([JSON.stringify(
|
|
590
|
+
for (const request of requests) {
|
|
591
|
+
const result = await this.executeLlamaEmbedding([JSON.stringify(request)]);
|
|
626
592
|
const embedding = this.parseRawOutput(result.stdout);
|
|
627
593
|
embeddings.push(embedding);
|
|
628
594
|
}
|
|
@@ -637,127 +603,29 @@ var LlamaCppProvider = class extends EmbeddingProvider {
|
|
|
637
603
|
throw error;
|
|
638
604
|
}
|
|
639
605
|
}
|
|
640
|
-
//
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
// Private helper methods
|
|
645
|
-
async getModelPath() {
|
|
646
|
-
const possiblePaths = [
|
|
647
|
-
this.modelPath,
|
|
648
|
-
// As provided
|
|
649
|
-
join("./llama.cpp/models", this.modelPath),
|
|
650
|
-
// In llama.cpp/models
|
|
651
|
-
join("./llama.cpp", this.modelPath),
|
|
652
|
-
// In llama.cpp root
|
|
653
|
-
this.modelPath
|
|
654
|
-
// Fallback
|
|
655
|
-
];
|
|
656
|
-
for (const path of possiblePaths) {
|
|
657
|
-
try {
|
|
658
|
-
await access(path, constants.F_OK);
|
|
659
|
-
return resolve(path);
|
|
660
|
-
} catch {
|
|
661
|
-
continue;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
throw new Error(`Model file not found: ${this.modelPath}`);
|
|
665
|
-
}
|
|
666
|
-
async executeLlamaEmbedding(args) {
|
|
667
|
-
return new Promise((resolve2, reject) => {
|
|
668
|
-
const port = 8080;
|
|
669
|
-
let requestBody;
|
|
606
|
+
// Cleanup method
|
|
607
|
+
async cleanup() {
|
|
608
|
+
if (this.useNative && this.nativeModel) {
|
|
670
609
|
try {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
610
|
+
this.nativeModel.close();
|
|
611
|
+
this.nativeModel = null;
|
|
612
|
+
logger.info("Native Llama.cpp model closed");
|
|
613
|
+
} catch (error) {
|
|
614
|
+
logger.error(`Error closing native model: ${error}`);
|
|
675
615
|
}
|
|
676
|
-
const postData = JSON.stringify(requestBody);
|
|
677
|
-
const options = {
|
|
678
|
-
hostname: "localhost",
|
|
679
|
-
port,
|
|
680
|
-
path: "/embedding",
|
|
681
|
-
method: "POST",
|
|
682
|
-
headers: {
|
|
683
|
-
"Content-Type": "application/json",
|
|
684
|
-
"Content-Length": Buffer.byteLength(postData)
|
|
685
|
-
}
|
|
686
|
-
};
|
|
687
|
-
const req = http.request(options, (res) => {
|
|
688
|
-
let data = "";
|
|
689
|
-
res.on("data", (chunk) => {
|
|
690
|
-
data += chunk;
|
|
691
|
-
});
|
|
692
|
-
res.on("end", () => {
|
|
693
|
-
if (res.statusCode === 200) {
|
|
694
|
-
resolve2({ stdout: data, stderr: "" });
|
|
695
|
-
} else {
|
|
696
|
-
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
});
|
|
700
|
-
req.on("error", (error) => {
|
|
701
|
-
reject(new Error(`Failed to connect to llama.cpp server: ${error instanceof Error ? error.message : String(error)}`));
|
|
702
|
-
});
|
|
703
|
-
req.write(postData);
|
|
704
|
-
req.end();
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
parseRawOutput(output) {
|
|
708
|
-
try {
|
|
709
|
-
const response = JSON.parse(output);
|
|
710
|
-
logger.debug(`PARSE DEBUG: Response type: ${typeof response}`);
|
|
711
|
-
logger.debug(`PARSE DEBUG: Is Array: ${Array.isArray(response)}`);
|
|
712
|
-
if (Array.isArray(response) && response.length > 0) {
|
|
713
|
-
const first = response[0];
|
|
714
|
-
if (first && first.embedding && Array.isArray(first.embedding)) {
|
|
715
|
-
const emb = first.embedding;
|
|
716
|
-
if (Array.isArray(emb[0])) {
|
|
717
|
-
const flat = emb[0];
|
|
718
|
-
logger.debug(`Parsed ${flat.length} dimensions (nested)`);
|
|
719
|
-
return flat;
|
|
720
|
-
}
|
|
721
|
-
logger.debug(`Parsed ${emb.length} dimensions (direct)`);
|
|
722
|
-
return emb;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
if (response.embedding && Array.isArray(response.embedding)) {
|
|
726
|
-
const emb = response.embedding;
|
|
727
|
-
if (Array.isArray(emb[0])) {
|
|
728
|
-
return emb[0];
|
|
729
|
-
}
|
|
730
|
-
return emb;
|
|
731
|
-
}
|
|
732
|
-
if (Array.isArray(response) && typeof response[0] === "number") {
|
|
733
|
-
logger.debug(`Parsed ${response.length} dimensions (flat array)`);
|
|
734
|
-
return response;
|
|
735
|
-
}
|
|
736
|
-
throw new Error(`Unexpected format: ${JSON.stringify(Object.keys(response))}`);
|
|
737
|
-
} catch (error) {
|
|
738
|
-
const errorMessage = error instanceof Error ? error instanceof Error ? error.message : String(error) : "Unknown error";
|
|
739
|
-
throw new Error(`Parse failed: ${errorMessage}`, { cause: error });
|
|
740
616
|
}
|
|
741
617
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
if (matches.length === 0) {
|
|
746
|
-
throw new Error("No array embeddings found in output");
|
|
747
|
-
}
|
|
748
|
-
const embeddings = matches.map((match) => {
|
|
749
|
-
const values = match[1]?.split(",").map((v) => v.trim()) || [];
|
|
750
|
-
return values.map((v) => parseFloat(v)).filter((v) => !isNaN(v));
|
|
751
|
-
}).filter((embedding) => embedding.length > 0);
|
|
752
|
-
return embeddings;
|
|
618
|
+
// Protected methods
|
|
619
|
+
getModel() {
|
|
620
|
+
return this.modelPath;
|
|
753
621
|
}
|
|
754
622
|
};
|
|
755
623
|
|
|
756
624
|
// src/factory/EmbeddingFactory.ts
|
|
757
|
-
var
|
|
625
|
+
var logger5 = Logger.createModuleLogger("factory");
|
|
758
626
|
var EmbeddingFactory = class {
|
|
759
627
|
static create(config2) {
|
|
760
|
-
|
|
628
|
+
logger5.info(`Creating provider: ${config2.provider}`);
|
|
761
629
|
const ProviderClass = this.providers.get(config2.provider);
|
|
762
630
|
if (!ProviderClass) {
|
|
763
631
|
throw new Error(`Unsupported provider: ${config2.provider}`);
|
|
@@ -768,54 +636,51 @@ var EmbeddingFactory = class {
|
|
|
768
636
|
return Array.from(this.providers.keys());
|
|
769
637
|
}
|
|
770
638
|
};
|
|
771
|
-
|
|
639
|
+
EmbeddingFactory.providers = /* @__PURE__ */ new Map([
|
|
772
640
|
["openai", OpenAIProvider],
|
|
773
641
|
["gemini", GeminiProvider],
|
|
774
|
-
["claude", ClaudeProvider],
|
|
775
642
|
["mistral", MistralProvider],
|
|
776
|
-
["deepseek", DeepSeekProvider],
|
|
777
643
|
["llamacpp", LlamaCppProvider]
|
|
778
644
|
// Local embeddings with llama.cpp
|
|
779
|
-
])
|
|
645
|
+
]);
|
|
780
646
|
|
|
781
647
|
// main.ts
|
|
782
648
|
dotenv.config();
|
|
783
|
-
var
|
|
649
|
+
var logger6 = Logger.createModuleLogger("main");
|
|
784
650
|
async function embed(config2, input) {
|
|
785
651
|
try {
|
|
786
|
-
|
|
652
|
+
logger6.info(`Starting embedding with provider: ${config2.provider}`);
|
|
787
653
|
const provider = EmbeddingFactory.create(config2);
|
|
788
654
|
const isReady = await provider.isReady();
|
|
789
655
|
if (!isReady) {
|
|
790
656
|
throw new Error(`Provider ${config2.provider} is not ready`);
|
|
791
657
|
}
|
|
792
658
|
if (Array.isArray(input)) {
|
|
793
|
-
|
|
659
|
+
logger6.debug(`Processing batch of ${input.length} items`);
|
|
794
660
|
return await provider.embedBatch(input);
|
|
795
661
|
} else {
|
|
796
|
-
|
|
662
|
+
logger6.debug(`Processing single item`);
|
|
797
663
|
return await provider.embed(input);
|
|
798
664
|
}
|
|
799
665
|
} catch (error) {
|
|
800
666
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
801
|
-
|
|
667
|
+
logger6.error(`Embedding failed: ${errorMessage}`);
|
|
802
668
|
throw error;
|
|
803
669
|
}
|
|
804
670
|
}
|
|
805
671
|
async function autoEmbed(input) {
|
|
806
|
-
|
|
672
|
+
logger6.info("Auto-detecting best provider...");
|
|
807
673
|
const providers = [
|
|
808
674
|
{ provider: "llamacpp", model: "nomic-embed-text-v1.5.Q4_K_M.gguf" },
|
|
809
675
|
// Local & free (llama.cpp)
|
|
810
676
|
{ provider: "openai", model: "text-embedding-3-small", apiKey: process.env.OPENAI_API_KEY || void 0 },
|
|
811
677
|
{ provider: "gemini", model: "gemini-embedding-001", apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY || void 0 },
|
|
812
|
-
{ provider: "mistral", model: "mistral-embed", apiKey: process.env.MISTRAL_API_KEY || void 0 }
|
|
813
|
-
{ provider: "deepseek", model: "deepseek-chat", apiKey: process.env.DEEPSEEK_API_KEY || void 0 }
|
|
678
|
+
{ provider: "mistral", model: "mistral-embed", apiKey: process.env.MISTRAL_API_KEY || void 0 }
|
|
814
679
|
];
|
|
815
680
|
for (const config2 of providers) {
|
|
816
681
|
try {
|
|
817
682
|
if (config2.provider === "llamacpp" || config2.apiKey) {
|
|
818
|
-
|
|
683
|
+
logger6.info(`Trying provider: ${config2.provider}`);
|
|
819
684
|
const cleanConfig = {
|
|
820
685
|
provider: config2.provider,
|
|
821
686
|
model: config2.model
|
|
@@ -827,7 +692,7 @@ async function autoEmbed(input) {
|
|
|
827
692
|
}
|
|
828
693
|
} catch (error) {
|
|
829
694
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
830
|
-
|
|
695
|
+
logger6.warn(`Provider ${config2.provider} failed: ${errorMessage}`);
|
|
831
696
|
continue;
|
|
832
697
|
}
|
|
833
698
|
}
|
|
@@ -854,9 +719,7 @@ var LIB_INFO = {
|
|
|
854
719
|
supportedProviders: [
|
|
855
720
|
"openai",
|
|
856
721
|
"gemini",
|
|
857
|
-
"claude",
|
|
858
722
|
"mistral",
|
|
859
|
-
"deepseek",
|
|
860
723
|
"llamacpp"
|
|
861
724
|
]
|
|
862
725
|
};
|