@retrivora-ai/rag-engine 1.9.6 → 1.9.8
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/README.md +130 -113
- package/dist/{ILLMProvider-DNhyOYoK.d.mts → ILLMProvider-B8ROITYK.d.mts} +59 -8
- package/dist/{ILLMProvider-DNhyOYoK.d.ts → ILLMProvider-B8ROITYK.d.ts} +59 -8
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +2376 -489
- package/dist/handlers/index.mjs +2372 -489
- package/dist/{index-CjQdL0cX.d.ts → index-B8PbEFSY.d.mts} +17 -3
- package/dist/{index-CHL1jdYm.d.mts → index-BCPKUAVL.d.ts} +33 -41
- package/dist/{index-Hgbwl9X4.d.ts → index-CrxCy36A.d.mts} +33 -41
- package/dist/{index-C9v7-tWd.d.mts → index-DNvoi-sV.d.ts} +17 -3
- package/dist/index.css +695 -203
- package/dist/index.d.mts +37 -7
- package/dist/index.d.ts +37 -7
- package/dist/index.js +1197 -286
- package/dist/index.mjs +1221 -297
- package/dist/server.d.mts +62 -6
- package/dist/server.d.ts +62 -6
- package/dist/server.js +2526 -574
- package/dist/server.mjs +2517 -573
- package/package.json +12 -10
- package/src/app/constants.tsx +207 -212
- package/src/app/layout.tsx +4 -28
- package/src/app/types.ts +17 -17
- package/src/components/AmbientBackground.tsx +10 -10
- package/src/components/ArchitectureCard.tsx +43 -7
- package/src/components/ArchitectureCardsSection.tsx +37 -4
- package/src/components/ChatWidget.tsx +4 -1
- package/src/components/ChatWindow.tsx +9 -2
- package/src/components/CodeViewer.tsx +19 -14
- package/src/components/DocViewer.tsx +75 -15
- package/src/components/Documentation.tsx +111 -28
- package/src/components/Hero.tsx +103 -20
- package/src/components/Lifecycle.tsx +65 -25
- package/src/components/MarkdownComponents.tsx +44 -1
- package/src/components/MessageBubble.tsx +162 -50
- package/src/components/constants.tsx +279 -0
- package/src/config/RagConfig.ts +56 -10
- package/src/config/constants.ts +5 -0
- package/src/config/serverConfig.ts +15 -0
- package/src/core/ConfigResolver.ts +30 -25
- package/src/core/DatabaseStorage.ts +469 -0
- package/src/core/LicenseVerifier.ts +154 -0
- package/src/core/MultiAgentCoordinator.ts +239 -0
- package/src/core/Pipeline.ts +148 -16
- package/src/core/ProviderRegistry.ts +5 -5
- package/src/core/Retrivora.ts +52 -6
- package/src/core/VectorPlugin.ts +74 -11
- package/src/core/mcp.ts +261 -0
- package/src/exceptions/index.ts +52 -0
- package/src/handlers/index.ts +504 -47
- package/src/hooks/useRagChat.ts +100 -43
- package/src/hooks/useStoredMessages.ts +15 -4
- package/src/index.ts +7 -0
- package/src/llm/LLMFactory.ts +15 -6
- package/src/llm/providers/GroqProvider.ts +176 -0
- package/src/llm/providers/QwenProvider.ts +191 -0
- package/src/server.ts +23 -13
- package/src/types/chat.ts +16 -0
- package/src/types/props.ts +50 -1
- package/.env.example +0 -80
- package/LICENSE.txt +0 -21
package/dist/handlers/index.mjs
CHANGED
|
@@ -6,9 +6,6 @@ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
7
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
8
|
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
9
|
-
var __typeError = (msg) => {
|
|
10
|
-
throw TypeError(msg);
|
|
11
|
-
};
|
|
12
9
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
10
|
var __spreadValues = (a, b) => {
|
|
14
11
|
for (var prop in b || (b = {}))
|
|
@@ -56,43 +53,15 @@ var __asyncGenerator = (__this, __arguments, generator) => {
|
|
|
56
53
|
}, method = (k, call, wait, clear) => it[k] = (x) => (call = new Promise((yes, no, run) => (run = () => resume(k, x, yes, no), q ? q.then(run) : run())), clear = () => q === wait && (q = 0), q = wait = call.then(clear, clear), call), q, it = {};
|
|
57
54
|
return generator = generator.apply(__this, __arguments), it[__knownSymbol("asyncIterator")] = () => it, method("next"), method("throw"), method("return"), it;
|
|
58
55
|
};
|
|
59
|
-
var __yieldStar = (value) => {
|
|
60
|
-
var obj = value[__knownSymbol("asyncIterator")], isAwait = false, method, it = {};
|
|
61
|
-
if (obj == null) {
|
|
62
|
-
obj = value[__knownSymbol("iterator")]();
|
|
63
|
-
method = (k) => it[k] = (x) => obj[k](x);
|
|
64
|
-
} else {
|
|
65
|
-
obj = obj.call(value);
|
|
66
|
-
method = (k) => it[k] = (v) => {
|
|
67
|
-
if (isAwait) {
|
|
68
|
-
isAwait = false;
|
|
69
|
-
if (k === "throw") throw v;
|
|
70
|
-
return v;
|
|
71
|
-
}
|
|
72
|
-
isAwait = true;
|
|
73
|
-
return {
|
|
74
|
-
done: false,
|
|
75
|
-
value: new __await(new Promise((resolve) => {
|
|
76
|
-
var x = obj[k](v);
|
|
77
|
-
if (!(x instanceof Object)) __typeError("Object expected");
|
|
78
|
-
resolve(x);
|
|
79
|
-
}), 1)
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
return it[__knownSymbol("iterator")] = () => it, method("next"), "throw" in obj ? method("throw") : it.throw = (x) => {
|
|
84
|
-
throw x;
|
|
85
|
-
}, "return" in obj && method("return"), it;
|
|
86
|
-
};
|
|
87
56
|
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
|
|
88
57
|
|
|
89
58
|
// src/utils/templateUtils.ts
|
|
90
59
|
function isRecord(value) {
|
|
91
60
|
return typeof value === "object" && value !== null;
|
|
92
61
|
}
|
|
93
|
-
function resolvePath(obj,
|
|
94
|
-
if (!
|
|
95
|
-
return
|
|
62
|
+
function resolvePath(obj, path2) {
|
|
63
|
+
if (!path2 || !obj) return void 0;
|
|
64
|
+
return path2.replace(/\[(\w+)\]/g, ".$1").replace(/^\./, "").split(".").reduce((current, segment) => {
|
|
96
65
|
if (!isRecord(current) && !Array.isArray(current)) {
|
|
97
66
|
return void 0;
|
|
98
67
|
}
|
|
@@ -202,7 +171,7 @@ var init_PineconeProvider = __esm({
|
|
|
202
171
|
static getHealthChecker() {
|
|
203
172
|
return {
|
|
204
173
|
async check(config) {
|
|
205
|
-
var
|
|
174
|
+
var _a2, _b;
|
|
206
175
|
const opts = config.options || {};
|
|
207
176
|
const indexName = config.indexName;
|
|
208
177
|
const timestamp = Date.now();
|
|
@@ -210,7 +179,7 @@ var init_PineconeProvider = __esm({
|
|
|
210
179
|
const { Pinecone: Pinecone2 } = await import("@pinecone-database/pinecone");
|
|
211
180
|
const client = new Pinecone2({ apiKey: opts.apiKey });
|
|
212
181
|
const indexes = await client.listIndexes();
|
|
213
|
-
const indexNames = (_b = (
|
|
182
|
+
const indexNames = (_b = (_a2 = indexes.indexes) == null ? void 0 : _a2.map((i) => i.name)) != null ? _b : [];
|
|
214
183
|
if (!indexNames.includes(indexName)) {
|
|
215
184
|
return {
|
|
216
185
|
healthy: false,
|
|
@@ -237,10 +206,10 @@ var init_PineconeProvider = __esm({
|
|
|
237
206
|
};
|
|
238
207
|
}
|
|
239
208
|
async initialize() {
|
|
240
|
-
var
|
|
209
|
+
var _a2, _b;
|
|
241
210
|
this.client = new Pinecone({ apiKey: this.apiKey });
|
|
242
211
|
const indexes = await this.client.listIndexes();
|
|
243
|
-
const names = (_b = (
|
|
212
|
+
const names = (_b = (_a2 = indexes.indexes) == null ? void 0 : _a2.map((i) => i.name)) != null ? _b : [];
|
|
244
213
|
if (!names.includes(this.indexName)) {
|
|
245
214
|
throw new Error(
|
|
246
215
|
`[PineconeProvider] Index "${this.indexName}" not found. Available: ${names.join(", ")}`
|
|
@@ -252,12 +221,12 @@ var init_PineconeProvider = __esm({
|
|
|
252
221
|
return namespace ? idx.namespace(namespace) : idx.namespace("");
|
|
253
222
|
}
|
|
254
223
|
async upsert(doc, namespace) {
|
|
255
|
-
var
|
|
224
|
+
var _a2;
|
|
256
225
|
await this.index(namespace).upsert({
|
|
257
226
|
records: [{
|
|
258
227
|
id: String(doc.id),
|
|
259
228
|
values: doc.vector,
|
|
260
|
-
metadata: __spreadValues({ content: doc.content }, (
|
|
229
|
+
metadata: __spreadValues({ content: doc.content }, (_a2 = doc.metadata) != null ? _a2 : {})
|
|
261
230
|
}]
|
|
262
231
|
});
|
|
263
232
|
}
|
|
@@ -265,29 +234,29 @@ var init_PineconeProvider = __esm({
|
|
|
265
234
|
const BATCH = 100;
|
|
266
235
|
for (let i = 0; i < docs.length; i += BATCH) {
|
|
267
236
|
const records = docs.slice(i, i + BATCH).map((d) => {
|
|
268
|
-
var
|
|
237
|
+
var _a2;
|
|
269
238
|
return {
|
|
270
239
|
id: String(d.id),
|
|
271
240
|
values: d.vector,
|
|
272
|
-
metadata: __spreadValues({ content: d.content }, (
|
|
241
|
+
metadata: __spreadValues({ content: d.content }, (_a2 = d.metadata) != null ? _a2 : {})
|
|
273
242
|
};
|
|
274
243
|
});
|
|
275
244
|
await this.index(namespace).upsert({ records });
|
|
276
245
|
}
|
|
277
246
|
}
|
|
278
247
|
async query(vector, topK, namespace, filter) {
|
|
279
|
-
var
|
|
248
|
+
var _a2;
|
|
280
249
|
const pineconeFilter = this.sanitizeFilter(filter);
|
|
281
250
|
const result = await this.index(namespace).query(__spreadValues({
|
|
282
251
|
vector,
|
|
283
252
|
topK,
|
|
284
253
|
includeMetadata: true
|
|
285
254
|
}, Object.keys(pineconeFilter).length > 0 ? { filter: pineconeFilter } : {}));
|
|
286
|
-
return ((
|
|
287
|
-
var
|
|
255
|
+
return ((_a2 = result.matches) != null ? _a2 : []).map((m) => {
|
|
256
|
+
var _a3, _b;
|
|
288
257
|
return {
|
|
289
258
|
id: m.id,
|
|
290
|
-
score: (
|
|
259
|
+
score: (_a3 = m.score) != null ? _a3 : 0,
|
|
291
260
|
content: ((_b = m.metadata) == null ? void 0 : _b.content) || "",
|
|
292
261
|
metadata: m.metadata
|
|
293
262
|
};
|
|
@@ -326,13 +295,13 @@ var init_PostgreSQLProvider = __esm({
|
|
|
326
295
|
init_BaseVectorProvider();
|
|
327
296
|
PostgreSQLProvider = class extends BaseVectorProvider {
|
|
328
297
|
constructor(config) {
|
|
329
|
-
var
|
|
298
|
+
var _a2;
|
|
330
299
|
super(config);
|
|
331
300
|
this.tableName = this.indexName.replace(/[^a-z0-9_]/gi, "_");
|
|
332
301
|
const opts = config.options;
|
|
333
302
|
if (!opts.connectionString) throw new Error("[PostgreSQLProvider] options.connectionString is required");
|
|
334
303
|
this.connectionString = opts.connectionString;
|
|
335
|
-
this.dimensions = (
|
|
304
|
+
this.dimensions = (_a2 = opts.dimensions) != null ? _a2 : 1536;
|
|
336
305
|
}
|
|
337
306
|
static getValidator() {
|
|
338
307
|
return {
|
|
@@ -413,7 +382,7 @@ var init_PostgreSQLProvider = __esm({
|
|
|
413
382
|
}
|
|
414
383
|
}
|
|
415
384
|
async upsert(doc, namespace = "") {
|
|
416
|
-
var
|
|
385
|
+
var _a2;
|
|
417
386
|
const vectorLiteral = `[${doc.vector.join(",")}]`;
|
|
418
387
|
await this.pool.query(
|
|
419
388
|
`INSERT INTO ${this.tableName} (id, namespace, content, metadata, embedding)
|
|
@@ -423,7 +392,7 @@ var init_PostgreSQLProvider = __esm({
|
|
|
423
392
|
content = EXCLUDED.content,
|
|
424
393
|
metadata = EXCLUDED.metadata,
|
|
425
394
|
embedding = EXCLUDED.embedding`,
|
|
426
|
-
[doc.id, namespace, doc.content, JSON.stringify((
|
|
395
|
+
[doc.id, namespace, doc.content, JSON.stringify((_a2 = doc.metadata) != null ? _a2 : {}), vectorLiteral]
|
|
427
396
|
);
|
|
428
397
|
}
|
|
429
398
|
async batchUpsert(docs, namespace = "") {
|
|
@@ -436,9 +405,9 @@ var init_PostgreSQLProvider = __esm({
|
|
|
436
405
|
const batch = docs.slice(i, i + BATCH_SIZE);
|
|
437
406
|
const values = [];
|
|
438
407
|
const valuePlaceholders = batch.map((doc, idx) => {
|
|
439
|
-
var
|
|
408
|
+
var _a2;
|
|
440
409
|
const offset = idx * 5;
|
|
441
|
-
values.push(doc.id, namespace, doc.content, JSON.stringify((
|
|
410
|
+
values.push(doc.id, namespace, doc.content, JSON.stringify((_a2 = doc.metadata) != null ? _a2 : {}), `[${doc.vector.join(",")}]`);
|
|
442
411
|
return `($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}::vector)`;
|
|
443
412
|
}).join(", ");
|
|
444
413
|
const query = `
|
|
@@ -521,8 +490,8 @@ var init_PostgreSQLProvider = __esm({
|
|
|
521
490
|
|
|
522
491
|
// src/utils/synonyms.ts
|
|
523
492
|
function resolveMetadataValue(meta, uiKey) {
|
|
524
|
-
var
|
|
525
|
-
const synonyms = (
|
|
493
|
+
var _a2;
|
|
494
|
+
const synonyms = (_a2 = FIELD_SYNONYMS[uiKey]) != null ? _a2 : [];
|
|
526
495
|
const keys = Object.keys(meta);
|
|
527
496
|
const systemKeys = ["namespace", "filename", "filesize", "filetype", "docid", "uploadedat", "dimension", "chunkindex"];
|
|
528
497
|
let match = keys.find((k) => k.toLowerCase() === uiKey.toLowerCase());
|
|
@@ -609,14 +578,14 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
609
578
|
init_synonyms();
|
|
610
579
|
MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
611
580
|
constructor(config) {
|
|
612
|
-
var
|
|
581
|
+
var _a2, _b, _c;
|
|
613
582
|
super(config);
|
|
614
583
|
const opts = config.options || {};
|
|
615
584
|
if (!opts.connectionString) {
|
|
616
585
|
throw new Error("[MultiTablePostgresProvider] options.connectionString is required");
|
|
617
586
|
}
|
|
618
587
|
this.connectionString = opts.connectionString;
|
|
619
|
-
this.dimensions = (
|
|
588
|
+
this.dimensions = (_a2 = opts.dimensions) != null ? _a2 : 768;
|
|
620
589
|
this.tables = [];
|
|
621
590
|
const rawSearchFields = (_c = (_b = opts.searchFields) != null ? _b : process.env.VECTOR_DB_SEARCH_FIELDS) != null ? _c : ["name", "product_name", "productname", "title"];
|
|
622
591
|
this.searchFields = typeof rawSearchFields === "string" ? rawSearchFields.split(",").map((f) => f.trim()).filter(Boolean) : rawSearchFields;
|
|
@@ -674,11 +643,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
674
643
|
* Batch upsert documents by dynamically provisioning tables and columns based on CSV headers.
|
|
675
644
|
*/
|
|
676
645
|
async batchUpsert(docs, namespace = "") {
|
|
677
|
-
var
|
|
646
|
+
var _a2;
|
|
678
647
|
if (docs.length === 0) return;
|
|
679
648
|
const docsByFile = {};
|
|
680
649
|
for (const doc of docs) {
|
|
681
|
-
const fileName = ((
|
|
650
|
+
const fileName = ((_a2 = doc.metadata) == null ? void 0 : _a2.fileName) || this.uploadTable;
|
|
682
651
|
if (!docsByFile[fileName]) docsByFile[fileName] = [];
|
|
683
652
|
docsByFile[fileName].push(doc);
|
|
684
653
|
}
|
|
@@ -731,11 +700,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
731
700
|
const headerColumns = csvHeaders.map((h) => `"${h}"`).join(", ");
|
|
732
701
|
const allColumns = `id, ${headerColumns ? headerColumns + ", " : ""}namespace, content, metadata, embedding`;
|
|
733
702
|
const valuePlaceholders = batch.map((doc, idx) => {
|
|
734
|
-
var
|
|
703
|
+
var _a3, _b;
|
|
735
704
|
const offset = idx * (5 + csvHeaders.length);
|
|
736
705
|
values.push(doc.id);
|
|
737
706
|
for (const h of csvHeaders) {
|
|
738
|
-
values.push(String(((
|
|
707
|
+
values.push(String(((_a3 = doc.metadata) == null ? void 0 : _a3[h]) || ""));
|
|
739
708
|
}
|
|
740
709
|
values.push(namespace, doc.content, JSON.stringify((_b = doc.metadata) != null ? _b : {}), `[${doc.vector.join(",")}]`);
|
|
741
710
|
const docPlaceholders = [];
|
|
@@ -774,7 +743,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
774
743
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
775
744
|
*/
|
|
776
745
|
async query(vector, topK, _namespace, _filter) {
|
|
777
|
-
var
|
|
746
|
+
var _a2;
|
|
778
747
|
if (!this.pool) {
|
|
779
748
|
throw new Error("[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.");
|
|
780
749
|
}
|
|
@@ -805,11 +774,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
805
774
|
const filterParams = [];
|
|
806
775
|
if (metadataFilters && Object.keys(metadataFilters).length > 0) {
|
|
807
776
|
const conditions = Object.entries(metadataFilters).map(([key, val]) => {
|
|
808
|
-
var
|
|
777
|
+
var _a4;
|
|
809
778
|
filterParams.push(String(val));
|
|
810
779
|
const baseOffset = queryText && dynamicKeywordQuery ? 2 : 1;
|
|
811
780
|
const paramIdx = baseOffset + filterParams.length;
|
|
812
|
-
const synonyms = (
|
|
781
|
+
const synonyms = (_a4 = FIELD_SYNONYMS[key]) != null ? _a4 : [];
|
|
813
782
|
const keysToCheck = [key, ...synonyms];
|
|
814
783
|
const coalesceExprs = keysToCheck.flatMap((k) => [
|
|
815
784
|
`metadata->>'${k}'`,
|
|
@@ -878,7 +847,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
878
847
|
}
|
|
879
848
|
const tableResults = [];
|
|
880
849
|
for (const row of result.rows) {
|
|
881
|
-
const
|
|
850
|
+
const _a3 = row, { hybrid_score, id } = _a3, rest = __objRest(_a3, ["hybrid_score", "id"]);
|
|
882
851
|
delete rest.embedding;
|
|
883
852
|
delete rest.vector_score;
|
|
884
853
|
delete rest.keyword_score;
|
|
@@ -907,10 +876,10 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
907
876
|
}
|
|
908
877
|
allResults.sort((a, b) => b.score - a.score);
|
|
909
878
|
if (allResults.length === 0) return [];
|
|
910
|
-
const bestMatchTable = (
|
|
879
|
+
const bestMatchTable = (_a2 = allResults[0].metadata) == null ? void 0 : _a2.source_table;
|
|
911
880
|
const finalSorted = allResults.filter((res) => {
|
|
912
|
-
var
|
|
913
|
-
return ((
|
|
881
|
+
var _a3;
|
|
882
|
+
return ((_a3 = res.metadata) == null ? void 0 : _a3.source_table) === bestMatchTable;
|
|
914
883
|
});
|
|
915
884
|
console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
|
|
916
885
|
console.log(`[MultiTablePostgresProvider] Final top match from "${bestMatchTable}" with score ${finalSorted[0].score.toFixed(4)}`);
|
|
@@ -1315,14 +1284,14 @@ var init_QdrantProvider = __esm({
|
|
|
1315
1284
|
* Samples points from the collection to discover available payload fields.
|
|
1316
1285
|
*/
|
|
1317
1286
|
async discoverSchema() {
|
|
1318
|
-
var
|
|
1287
|
+
var _a2;
|
|
1319
1288
|
try {
|
|
1320
1289
|
const { data } = await this.http.post(`/collections/${this.indexName}/points/scroll`, {
|
|
1321
1290
|
limit: 20,
|
|
1322
1291
|
with_payload: true,
|
|
1323
1292
|
with_vector: false
|
|
1324
1293
|
});
|
|
1325
|
-
const points = ((
|
|
1294
|
+
const points = ((_a2 = data.result) == null ? void 0 : _a2.points) || [];
|
|
1326
1295
|
const keys = /* @__PURE__ */ new Set();
|
|
1327
1296
|
for (const point of points) {
|
|
1328
1297
|
const payload = point.payload || {};
|
|
@@ -1348,12 +1317,12 @@ var init_QdrantProvider = __esm({
|
|
|
1348
1317
|
* Ensures the collection exists. Creates it if missing.
|
|
1349
1318
|
*/
|
|
1350
1319
|
async ensureCollection() {
|
|
1351
|
-
var
|
|
1320
|
+
var _a2;
|
|
1352
1321
|
try {
|
|
1353
1322
|
await this.http.get(`/collections/${this.indexName}`);
|
|
1354
1323
|
console.log(`[QdrantProvider] \u2705 Collection "${this.indexName}" already exists.`);
|
|
1355
1324
|
} catch (err) {
|
|
1356
|
-
if (axios4.isAxiosError(err) && ((
|
|
1325
|
+
if (axios4.isAxiosError(err) && ((_a2 = err.response) == null ? void 0 : _a2.status) === 404) {
|
|
1357
1326
|
const opts = this.config.options;
|
|
1358
1327
|
const dimensionsForCreate = opts.dimensions || 1536;
|
|
1359
1328
|
console.log(`[QdrantProvider] \u23F3 Creating collection "${this.indexName}" (dimensions: ${dimensionsForCreate})...`);
|
|
@@ -1373,7 +1342,7 @@ var init_QdrantProvider = __esm({
|
|
|
1373
1342
|
* Ensures that a payload field has an index.
|
|
1374
1343
|
*/
|
|
1375
1344
|
async ensureIndex(fieldName, schema = "keyword") {
|
|
1376
|
-
var
|
|
1345
|
+
var _a2;
|
|
1377
1346
|
let fullPath = fieldName;
|
|
1378
1347
|
if (!this.isFlatPayload && !fieldName.includes(".") && fieldName !== "namespace" && fieldName !== this.contentField) {
|
|
1379
1348
|
fullPath = `${this.metadataField}.${fieldName}`;
|
|
@@ -1386,7 +1355,7 @@ var init_QdrantProvider = __esm({
|
|
|
1386
1355
|
console.log(`[QdrantProvider] \u2705 Ensured ${schema} index for "${fullPath}"`);
|
|
1387
1356
|
} catch (err) {
|
|
1388
1357
|
let status;
|
|
1389
|
-
if (axios4.isAxiosError(err)) status = (
|
|
1358
|
+
if (axios4.isAxiosError(err)) status = (_a2 = err.response) == null ? void 0 : _a2.status;
|
|
1390
1359
|
if (status === 409) return;
|
|
1391
1360
|
console.warn(`[QdrantProvider] \u26A0\uFE0F Could not ensure index for "${fullPath}":`, err instanceof Error ? err.message : String(err));
|
|
1392
1361
|
}
|
|
@@ -1415,7 +1384,7 @@ var init_QdrantProvider = __esm({
|
|
|
1415
1384
|
await this.http.put(`/collections/${this.indexName}/points`, payload);
|
|
1416
1385
|
}
|
|
1417
1386
|
async query(vector, topK, namespace, _filter) {
|
|
1418
|
-
var
|
|
1387
|
+
var _a2;
|
|
1419
1388
|
const must = [];
|
|
1420
1389
|
if (namespace) {
|
|
1421
1390
|
must.push({ key: "namespace", match: { value: namespace } });
|
|
@@ -1437,7 +1406,7 @@ var init_QdrantProvider = __esm({
|
|
|
1437
1406
|
limit: topK,
|
|
1438
1407
|
with_payload: true,
|
|
1439
1408
|
params: {
|
|
1440
|
-
hnsw_ef: ((
|
|
1409
|
+
hnsw_ef: ((_a2 = this.config.options) == null ? void 0 : _a2.efSearch) || Math.max(topK * 20, 128),
|
|
1441
1410
|
exact: false
|
|
1442
1411
|
},
|
|
1443
1412
|
filter: must.length > 0 ? { must } : void 0
|
|
@@ -1532,13 +1501,13 @@ var init_ChromaDBProvider = __esm({
|
|
|
1532
1501
|
* Get or create the ChromaDB collection.
|
|
1533
1502
|
*/
|
|
1534
1503
|
async initialize() {
|
|
1535
|
-
var
|
|
1504
|
+
var _a2;
|
|
1536
1505
|
try {
|
|
1537
1506
|
const { data } = await this.http.get(`/api/v1/collections/${this.indexName}`);
|
|
1538
1507
|
this.collectionId = data.id;
|
|
1539
1508
|
console.log(`[ChromaDBProvider] \u2705 Collection "${this.indexName}" found (id: ${this.collectionId})`);
|
|
1540
1509
|
} catch (err) {
|
|
1541
|
-
if (axios5.isAxiosError(err) && ((
|
|
1510
|
+
if (axios5.isAxiosError(err) && ((_a2 = err.response) == null ? void 0 : _a2.status) === 404) {
|
|
1542
1511
|
console.log(`[ChromaDBProvider] \u23F3 Collection "${this.indexName}" not found. Creating...`);
|
|
1543
1512
|
const { data } = await this.http.post("/api/v1/collections", {
|
|
1544
1513
|
name: this.indexName
|
|
@@ -1759,7 +1728,7 @@ var init_WeaviateProvider = __esm({
|
|
|
1759
1728
|
await this.http.post("/v1/batch/objects", payload);
|
|
1760
1729
|
}
|
|
1761
1730
|
async query(vector, topK, namespace, _filter) {
|
|
1762
|
-
var
|
|
1731
|
+
var _a2, _b;
|
|
1763
1732
|
const queryText = _filter == null ? void 0 : _filter.queryText;
|
|
1764
1733
|
const sanitizedFilter = this.sanitizeFilter(_filter);
|
|
1765
1734
|
const searchParams = queryText ? `hybrid: { query: ${JSON.stringify(queryText)}, alpha: 0.5 }` : `nearVector: { vector: ${JSON.stringify(vector)} }`;
|
|
@@ -1785,7 +1754,7 @@ var init_WeaviateProvider = __esm({
|
|
|
1785
1754
|
`
|
|
1786
1755
|
};
|
|
1787
1756
|
const { data } = await this.http.post("/v1/graphql", graphqlQuery);
|
|
1788
|
-
const results = ((_b = (
|
|
1757
|
+
const results = ((_b = (_a2 = data.data) == null ? void 0 : _a2.Get) == null ? void 0 : _b[this.indexName]) || [];
|
|
1789
1758
|
return results.map((res) => ({
|
|
1790
1759
|
id: res["_additional"].id,
|
|
1791
1760
|
score: 1 - res["_additional"].distance,
|
|
@@ -1838,10 +1807,10 @@ var init_WeaviateProvider = __esm({
|
|
|
1838
1807
|
return `{ operator: And, operands: [${operands.join(", ")}] }`;
|
|
1839
1808
|
}
|
|
1840
1809
|
weaviateOperand(key, value) {
|
|
1841
|
-
const
|
|
1842
|
-
if (typeof value === "number") return `{ ${
|
|
1843
|
-
if (typeof value === "boolean") return `{ ${
|
|
1844
|
-
return `{ ${
|
|
1810
|
+
const path2 = `path: [${JSON.stringify(key)}], operator: Equal`;
|
|
1811
|
+
if (typeof value === "number") return `{ ${path2}, valueNumber: ${value} }`;
|
|
1812
|
+
if (typeof value === "boolean") return `{ ${path2}, valueBoolean: ${value} }`;
|
|
1813
|
+
return `{ ${path2}, valueString: ${JSON.stringify(value)} }`;
|
|
1845
1814
|
}
|
|
1846
1815
|
};
|
|
1847
1816
|
}
|
|
@@ -1868,21 +1837,21 @@ var init_UniversalVectorProvider = __esm({
|
|
|
1868
1837
|
}
|
|
1869
1838
|
}
|
|
1870
1839
|
async initialize() {
|
|
1871
|
-
var
|
|
1840
|
+
var _a2;
|
|
1872
1841
|
this.http = axios8.create({
|
|
1873
1842
|
baseURL: this.opts.baseUrl,
|
|
1874
1843
|
headers: __spreadValues({
|
|
1875
1844
|
"Content-Type": "application/json"
|
|
1876
1845
|
}, this.opts.headers),
|
|
1877
|
-
timeout: (
|
|
1846
|
+
timeout: (_a2 = this.opts.timeout) != null ? _a2 : 3e4
|
|
1878
1847
|
});
|
|
1879
1848
|
if (!await this.ping()) {
|
|
1880
1849
|
throw new Error(`[UniversalVectorProvider] Failed to connect to ${this.opts.baseUrl}`);
|
|
1881
1850
|
}
|
|
1882
1851
|
}
|
|
1883
1852
|
async upsert(doc, namespace) {
|
|
1884
|
-
var
|
|
1885
|
-
const endpoint = (
|
|
1853
|
+
var _a2, _b, _c;
|
|
1854
|
+
const endpoint = (_a2 = this.opts.upsertEndpoint) != null ? _a2 : "/upsert";
|
|
1886
1855
|
const template = (_b = this.opts.upsertTemplate) != null ? _b : JSON.stringify({
|
|
1887
1856
|
id: "{{id}}",
|
|
1888
1857
|
vector: "{{vector}}",
|
|
@@ -1915,8 +1884,8 @@ var init_UniversalVectorProvider = __esm({
|
|
|
1915
1884
|
}
|
|
1916
1885
|
}
|
|
1917
1886
|
async query(vector, topK, namespace, filter) {
|
|
1918
|
-
var
|
|
1919
|
-
const endpoint = (
|
|
1887
|
+
var _a2, _b;
|
|
1888
|
+
const endpoint = (_a2 = this.opts.queryEndpoint) != null ? _a2 : "/query";
|
|
1920
1889
|
const template = (_b = this.opts.queryTemplate) != null ? _b : JSON.stringify({
|
|
1921
1890
|
vector: "{{vector}}",
|
|
1922
1891
|
limit: "{{topK}}",
|
|
@@ -1942,12 +1911,12 @@ var init_UniversalVectorProvider = __esm({
|
|
|
1942
1911
|
);
|
|
1943
1912
|
}
|
|
1944
1913
|
return results.map((item) => {
|
|
1945
|
-
var
|
|
1914
|
+
var _a3, _b2, _c, _d, _e, _f, _g2;
|
|
1946
1915
|
return {
|
|
1947
|
-
id: item[(
|
|
1916
|
+
id: item[(_a3 = this.opts.queryIdField) != null ? _a3 : "id"],
|
|
1948
1917
|
score: (_c = item[(_b2 = this.opts.queryScoreField) != null ? _b2 : "score"]) != null ? _c : 0,
|
|
1949
1918
|
content: (_e = item[(_d = this.opts.queryContentField) != null ? _d : "content"]) != null ? _e : "",
|
|
1950
|
-
metadata: (
|
|
1919
|
+
metadata: (_g2 = item[(_f = this.opts.queryMetadataField) != null ? _f : "metadata"]) != null ? _g2 : {}
|
|
1951
1920
|
};
|
|
1952
1921
|
});
|
|
1953
1922
|
} catch (error) {
|
|
@@ -1957,8 +1926,8 @@ var init_UniversalVectorProvider = __esm({
|
|
|
1957
1926
|
}
|
|
1958
1927
|
}
|
|
1959
1928
|
async delete(id, namespace) {
|
|
1960
|
-
var
|
|
1961
|
-
const endpoint = (
|
|
1929
|
+
var _a2, _b;
|
|
1930
|
+
const endpoint = (_a2 = this.opts.deleteEndpoint) != null ? _a2 : "/delete";
|
|
1962
1931
|
const template = (_b = this.opts.deleteTemplate) != null ? _b : JSON.stringify({
|
|
1963
1932
|
id: "{{id}}",
|
|
1964
1933
|
namespace: "{{namespace}}"
|
|
@@ -1976,8 +1945,8 @@ var init_UniversalVectorProvider = __esm({
|
|
|
1976
1945
|
}
|
|
1977
1946
|
}
|
|
1978
1947
|
async deleteNamespace(namespace) {
|
|
1979
|
-
var
|
|
1980
|
-
const endpoint = (
|
|
1948
|
+
var _a2;
|
|
1949
|
+
const endpoint = (_a2 = this.opts.deleteNamespaceEndpoint) != null ? _a2 : "/delete-namespace";
|
|
1981
1950
|
try {
|
|
1982
1951
|
await this.http.post(endpoint, { namespace });
|
|
1983
1952
|
} catch (error) {
|
|
@@ -1987,9 +1956,9 @@ var init_UniversalVectorProvider = __esm({
|
|
|
1987
1956
|
}
|
|
1988
1957
|
}
|
|
1989
1958
|
async ping() {
|
|
1990
|
-
var
|
|
1959
|
+
var _a2;
|
|
1991
1960
|
try {
|
|
1992
|
-
const endpoint = (
|
|
1961
|
+
const endpoint = (_a2 = this.opts.pingEndpoint) != null ? _a2 : "/health";
|
|
1993
1962
|
const response = await this.http.get(endpoint, { timeout: 5e3 });
|
|
1994
1963
|
return response.status >= 200 && response.status < 300;
|
|
1995
1964
|
} catch (e) {
|
|
@@ -2103,6 +2072,8 @@ var LLM_PROVIDERS = [
|
|
|
2103
2072
|
"anthropic",
|
|
2104
2073
|
"ollama",
|
|
2105
2074
|
"gemini",
|
|
2075
|
+
"groq",
|
|
2076
|
+
"qwen",
|
|
2106
2077
|
"rest",
|
|
2107
2078
|
"universal_rest",
|
|
2108
2079
|
"custom"
|
|
@@ -2111,6 +2082,7 @@ var EMBEDDING_PROVIDERS = [
|
|
|
2111
2082
|
"openai",
|
|
2112
2083
|
"ollama",
|
|
2113
2084
|
"gemini",
|
|
2085
|
+
"qwen",
|
|
2114
2086
|
"rest",
|
|
2115
2087
|
"universal_rest",
|
|
2116
2088
|
"custom"
|
|
@@ -2119,6 +2091,7 @@ var PROVIDERS_WITH_EMBEDDINGS = [
|
|
|
2119
2091
|
"openai",
|
|
2120
2092
|
"ollama",
|
|
2121
2093
|
"gemini",
|
|
2094
|
+
"qwen",
|
|
2122
2095
|
"rest",
|
|
2123
2096
|
"universal_rest"
|
|
2124
2097
|
];
|
|
@@ -2126,8 +2099,8 @@ var UI_BORDER_RADIUS_OPTIONS = ["none", "sm", "md", "lg", "xl", "full"];
|
|
|
2126
2099
|
|
|
2127
2100
|
// src/config/serverConfig.ts
|
|
2128
2101
|
function readString(env, name) {
|
|
2129
|
-
var
|
|
2130
|
-
const value = (
|
|
2102
|
+
var _a2;
|
|
2103
|
+
const value = (_a2 = env[name]) == null ? void 0 : _a2.trim();
|
|
2131
2104
|
return value ? value : void 0;
|
|
2132
2105
|
}
|
|
2133
2106
|
function readNumber(env, name, fallback) {
|
|
@@ -2140,23 +2113,23 @@ function readNumber(env, name, fallback) {
|
|
|
2140
2113
|
return parsed;
|
|
2141
2114
|
}
|
|
2142
2115
|
function readEnum(env, name, fallback, allowed) {
|
|
2143
|
-
var
|
|
2144
|
-
const value = (
|
|
2116
|
+
var _a2;
|
|
2117
|
+
const value = (_a2 = readString(env, name)) != null ? _a2 : fallback;
|
|
2145
2118
|
if (allowed.includes(value)) {
|
|
2146
2119
|
return value;
|
|
2147
2120
|
}
|
|
2148
2121
|
throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
|
|
2149
2122
|
}
|
|
2150
2123
|
function getEnvConfig(env = process.env, base) {
|
|
2151
|
-
var
|
|
2152
|
-
const projectId = (_c = (_b = (
|
|
2124
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa;
|
|
2125
|
+
const projectId = (_c = (_b = (_a2 = readString(env, "RAG_PROJECT_ID")) != null ? _a2 : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : base == null ? void 0 : base.projectId) != null ? _c : "__default__";
|
|
2153
2126
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
2154
2127
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
2155
2128
|
const embeddingProvider = readEnum(env, "EMBEDDING_PROVIDER", "openai", EMBEDDING_PROVIDERS);
|
|
2156
2129
|
const embeddingDimensions = readNumber(env, "EMBEDDING_DIMENSIONS", 1536);
|
|
2157
2130
|
const vectorDbOptions = {};
|
|
2158
2131
|
if (vectorProvider === "pinecone") {
|
|
2159
|
-
vectorDbOptions.apiKey = (
|
|
2132
|
+
vectorDbOptions.apiKey = (_g2 = (_f = readString(env, "PINECONE_API_KEY")) != null ? _f : (_e = (_d = base == null ? void 0 : base.vectorDb) == null ? void 0 : _d.options) == null ? void 0 : _e.apiKey) != null ? _g2 : "";
|
|
2160
2133
|
vectorDbOptions.indexName = (_l = (_i = readString(env, "PINECONE_INDEX")) != null ? _i : (_h = base == null ? void 0 : base.vectorDb) == null ? void 0 : _h.indexName) != null ? _l : (_k = (_j = base == null ? void 0 : base.vectorDb) == null ? void 0 : _j.options) == null ? void 0 : _k.indexName;
|
|
2161
2134
|
} else if (vectorProvider === "pgvector" || vectorProvider === "postgresql") {
|
|
2162
2135
|
vectorDbOptions.connectionString = (_q = (_p = (_m = readString(env, "PGVECTOR_CONNECTION_STRING")) != null ? _m : readString(env, "POSTGRES_URL")) != null ? _p : (_o = (_n = base == null ? void 0 : base.vectorDb) == null ? void 0 : _n.options) == null ? void 0 : _o.connectionString) != null ? _q : "";
|
|
@@ -2218,17 +2191,18 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2218
2191
|
rest: "default",
|
|
2219
2192
|
custom: "default"
|
|
2220
2193
|
};
|
|
2221
|
-
return __spreadValues({
|
|
2194
|
+
return __spreadProps(__spreadValues({
|
|
2222
2195
|
projectId,
|
|
2196
|
+
licenseKey: (_ha = (_ga = readString(env, "RETRIVORA_LICENSE_KEY")) != null ? _ga : readString(env, "LICENSE_KEY")) != null ? _ha : base == null ? void 0 : base.licenseKey,
|
|
2223
2197
|
vectorDb: {
|
|
2224
2198
|
provider: vectorProvider,
|
|
2225
|
-
indexName: (
|
|
2199
|
+
indexName: (_ja = (_ia = readString(env, "VECTOR_DB_INDEX")) != null ? _ia : vectorDbOptions.indexName) != null ? _ja : "rag-index",
|
|
2226
2200
|
options: vectorDbOptions
|
|
2227
2201
|
},
|
|
2228
2202
|
llm: {
|
|
2229
2203
|
provider: llmProvider,
|
|
2230
|
-
model: (
|
|
2231
|
-
apiKey: (
|
|
2204
|
+
model: (_la = (_ka = readString(env, "LLM_MODEL")) != null ? _ka : DEFAULT_MODEL_BY_PROVIDER[llmProvider]) != null ? _la : "gpt-4o",
|
|
2205
|
+
apiKey: (_ma = llmApiKeyByProvider[llmProvider]) != null ? _ma : "",
|
|
2232
2206
|
baseUrl: readString(env, "LLM_BASE_URL"),
|
|
2233
2207
|
systemPrompt: readString(env, "LLM_SYSTEM_PROMPT"),
|
|
2234
2208
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
|
|
@@ -2241,7 +2215,7 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2241
2215
|
},
|
|
2242
2216
|
embedding: {
|
|
2243
2217
|
provider: embeddingProvider,
|
|
2244
|
-
model: (
|
|
2218
|
+
model: (_na = readString(env, "EMBEDDING_MODEL")) != null ? _na : "text-embedding-3-small",
|
|
2245
2219
|
apiKey: embeddingApiKeyByProvider[embeddingProvider],
|
|
2246
2220
|
baseUrl: readString(env, "EMBEDDING_BASE_URL"),
|
|
2247
2221
|
dimensions: embeddingDimensions,
|
|
@@ -2252,30 +2226,30 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2252
2226
|
}
|
|
2253
2227
|
},
|
|
2254
2228
|
ui: {
|
|
2255
|
-
title: (
|
|
2256
|
-
subtitle: (
|
|
2257
|
-
primaryColor: (
|
|
2258
|
-
accentColor: (
|
|
2259
|
-
logoUrl: (
|
|
2260
|
-
placeholder: (
|
|
2261
|
-
showSources: ((
|
|
2262
|
-
welcomeMessage: (
|
|
2263
|
-
visualStyle: (
|
|
2264
|
-
borderRadius: (
|
|
2265
|
-
allowUpload: ((
|
|
2229
|
+
title: (_pa = (_oa = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _oa : readString(env, "UI_TITLE")) != null ? _pa : "AI Assistant",
|
|
2230
|
+
subtitle: (_ra = (_qa = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _qa : readString(env, "UI_SUBTITLE")) != null ? _ra : "Powered by RAG",
|
|
2231
|
+
primaryColor: (_ta = (_sa = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _sa : readString(env, "UI_PRIMARY_COLOR")) != null ? _ta : "#10b981",
|
|
2232
|
+
accentColor: (_va = (_ua = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _ua : readString(env, "UI_ACCENT_COLOR")) != null ? _va : "#3b82f6",
|
|
2233
|
+
logoUrl: (_wa = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _wa : readString(env, "UI_LOGO_URL"),
|
|
2234
|
+
placeholder: (_ya = (_xa = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _xa : readString(env, "UI_PLACEHOLDER")) != null ? _ya : "Ask me anything\u2026",
|
|
2235
|
+
showSources: ((_Aa = (_za = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _za : readString(env, "UI_SHOW_SOURCES")) != null ? _Aa : "true") !== "false",
|
|
2236
|
+
welcomeMessage: (_Ca = (_Ba = readString(env, "NEXT_PUBLIC_WELCOME_MESSAGE")) != null ? _Ba : readString(env, "UI_WELCOME_MESSAGE")) != null ? _Ca : "Hello! I'm your AI assistant. Ask me anything about your documents.",
|
|
2237
|
+
visualStyle: (_Ea = (_Da = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _Da : readString(env, "UI_VISUAL_STYLE")) != null ? _Ea : "glass",
|
|
2238
|
+
borderRadius: (_Ga = (_Fa = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _Fa : readString(env, "UI_BORDER_RADIUS")) != null ? _Ga : "xl",
|
|
2239
|
+
allowUpload: ((_Ia = (_Ha = readString(env, "NEXT_PUBLIC_ALLOW_UPLOAD")) != null ? _Ha : readString(env, "UI_ALLOW_UPLOAD")) != null ? _Ia : "false") === "true"
|
|
2266
2240
|
},
|
|
2267
2241
|
rag: {
|
|
2268
2242
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
2269
2243
|
scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
|
|
2270
2244
|
chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
|
|
2271
2245
|
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
|
|
2272
|
-
filterableFields: (
|
|
2246
|
+
filterableFields: (_Ja = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Ja.split(",").map((f) => f.trim()),
|
|
2273
2247
|
// Query pipeline toggles — read from .env.local
|
|
2274
2248
|
useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
|
|
2275
2249
|
useReranking: readString(env, "RAG_USE_RERANKING") === "true",
|
|
2276
2250
|
useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
|
|
2277
|
-
architecture: (
|
|
2278
|
-
chunkingStrategy: (
|
|
2251
|
+
architecture: (_Ka = readString(env, "RAG_ARCHITECTURE")) != null ? _Ka : "simple",
|
|
2252
|
+
chunkingStrategy: (_La = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _La : "recursive",
|
|
2279
2253
|
uiMapping: (() => {
|
|
2280
2254
|
const raw = readString(env, "RAG_UI_MAPPING");
|
|
2281
2255
|
if (!raw) return void 0;
|
|
@@ -2285,6 +2259,10 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2285
2259
|
return void 0;
|
|
2286
2260
|
}
|
|
2287
2261
|
})()
|
|
2262
|
+
},
|
|
2263
|
+
telemetry: {
|
|
2264
|
+
enabled: readString(env, "TELEMETRY_ENABLED") === "true" || readString(env, "NEXT_PUBLIC_TELEMETRY_ENABLED") === "true" || ((_Ma = base == null ? void 0 : base.telemetry) == null ? void 0 : _Ma.enabled) || false,
|
|
2265
|
+
url: (_Qa = (_Pa = (_Na = readString(env, "TELEMETRY_URL")) != null ? _Na : readString(env, "NEXT_PUBLIC_TELEMETRY_URL")) != null ? _Pa : (_Oa = base == null ? void 0 : base.telemetry) == null ? void 0 : _Oa.url) != null ? _Qa : "/api/telemetry"
|
|
2288
2266
|
}
|
|
2289
2267
|
}, readString(env, "GRAPH_DB_PROVIDER") ? {
|
|
2290
2268
|
graphDb: {
|
|
@@ -2295,29 +2273,21 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2295
2273
|
password: readString(env, "GRAPH_DB_PASSWORD")
|
|
2296
2274
|
}
|
|
2297
2275
|
}
|
|
2298
|
-
} : {})
|
|
2276
|
+
} : {}), {
|
|
2277
|
+
mcpServers: (() => {
|
|
2278
|
+
var _a3;
|
|
2279
|
+
const raw = (_a3 = readString(env, "MCP_SERVERS")) != null ? _a3 : readString(env, "NEXT_PUBLIC_MCP_SERVERS");
|
|
2280
|
+
if (!raw) return void 0;
|
|
2281
|
+
try {
|
|
2282
|
+
return JSON.parse(raw);
|
|
2283
|
+
} catch (e) {
|
|
2284
|
+
console.warn("[serverConfig] Failed to parse MCP_SERVERS env:", e);
|
|
2285
|
+
return void 0;
|
|
2286
|
+
}
|
|
2287
|
+
})()
|
|
2288
|
+
});
|
|
2299
2289
|
}
|
|
2300
2290
|
|
|
2301
|
-
// src/exceptions/index.ts
|
|
2302
|
-
var RetrivoraError = class extends Error {
|
|
2303
|
-
constructor(message, code, details) {
|
|
2304
|
-
super(message);
|
|
2305
|
-
this.name = new.target.name;
|
|
2306
|
-
this.code = code;
|
|
2307
|
-
this.details = details;
|
|
2308
|
-
}
|
|
2309
|
-
};
|
|
2310
|
-
var ProviderNotFoundException = class extends RetrivoraError {
|
|
2311
|
-
constructor(providerType, provider, details) {
|
|
2312
|
-
super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
|
|
2313
|
-
}
|
|
2314
|
-
};
|
|
2315
|
-
var ConfigurationException = class extends RetrivoraError {
|
|
2316
|
-
constructor(message, details) {
|
|
2317
|
-
super(message, "CONFIGURATION_ERROR", details);
|
|
2318
|
-
}
|
|
2319
|
-
};
|
|
2320
|
-
|
|
2321
2291
|
// src/core/ConfigResolver.ts
|
|
2322
2292
|
var ConfigResolver = class {
|
|
2323
2293
|
/**
|
|
@@ -2341,7 +2311,8 @@ var ConfigResolver = class {
|
|
|
2341
2311
|
options: __spreadValues(__spreadValues({}, envConfig.embedding.options || {}), hostConfig.embedding.options || {})
|
|
2342
2312
|
}) : envConfig.embedding,
|
|
2343
2313
|
ui: hostConfig.ui ? mergeDefined(envConfig.ui, hostConfig.ui) : envConfig.ui,
|
|
2344
|
-
rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
|
|
2314
|
+
rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag,
|
|
2315
|
+
telemetry: hostConfig.telemetry ? __spreadValues(__spreadValues({}, envConfig.telemetry), hostConfig.telemetry) : envConfig.telemetry
|
|
2345
2316
|
});
|
|
2346
2317
|
}
|
|
2347
2318
|
/**
|
|
@@ -2350,10 +2321,10 @@ var ConfigResolver = class {
|
|
|
2350
2321
|
* fallback behavior.
|
|
2351
2322
|
*/
|
|
2352
2323
|
static resolveUniversal(hostConfig, env = process.env) {
|
|
2353
|
-
var
|
|
2324
|
+
var _a2;
|
|
2354
2325
|
if (!hostConfig) return this.resolve(void 0, env);
|
|
2355
2326
|
const normalized = __spreadProps(__spreadValues({}, hostConfig), {
|
|
2356
|
-
vectorDb: (
|
|
2327
|
+
vectorDb: (_a2 = hostConfig.vectorDb) != null ? _a2 : hostConfig.vectorDatabase,
|
|
2357
2328
|
rag: this.mergeRetrievalWorkflow(hostConfig.rag, hostConfig.retrieval, hostConfig.workflow)
|
|
2358
2329
|
});
|
|
2359
2330
|
return this.resolve(normalized, env);
|
|
@@ -2363,21 +2334,21 @@ var ConfigResolver = class {
|
|
|
2363
2334
|
*/
|
|
2364
2335
|
static validate(config) {
|
|
2365
2336
|
if (!config.projectId) {
|
|
2366
|
-
throw new
|
|
2337
|
+
throw new Error("[ConfigResolver] projectId is required");
|
|
2367
2338
|
}
|
|
2368
2339
|
if (!config.vectorDb.provider) {
|
|
2369
|
-
throw new
|
|
2340
|
+
throw new Error("[ConfigResolver] vectorDb.provider is required");
|
|
2370
2341
|
}
|
|
2371
2342
|
if (!config.llm.provider) {
|
|
2372
|
-
throw new
|
|
2343
|
+
throw new Error("[ConfigResolver] llm.provider is required");
|
|
2373
2344
|
}
|
|
2374
2345
|
}
|
|
2375
2346
|
static mergeRetrievalWorkflow(rag, retrieval, workflow) {
|
|
2376
|
-
var
|
|
2347
|
+
var _a2, _b, _c, _d, _e;
|
|
2377
2348
|
if (!rag && !retrieval && !workflow) return void 0;
|
|
2378
2349
|
const normalized = __spreadValues({}, rag != null ? rag : {});
|
|
2379
2350
|
if (retrieval) {
|
|
2380
|
-
normalized.topK = (
|
|
2351
|
+
normalized.topK = (_a2 = retrieval.topK) != null ? _a2 : normalized.topK;
|
|
2381
2352
|
normalized.scoreThreshold = (_b = retrieval.scoreThreshold) != null ? _b : normalized.scoreThreshold;
|
|
2382
2353
|
normalized.useReranking = (_c = retrieval.useReranking) != null ? _c : normalized.useReranking;
|
|
2383
2354
|
if (retrieval.strategy === "hybrid") normalized.architecture = (_d = normalized.architecture) != null ? _d : "hybrid";
|
|
@@ -2459,8 +2430,8 @@ var OpenAIProvider = class {
|
|
|
2459
2430
|
const apiKey = config.apiKey;
|
|
2460
2431
|
const modelName = config.model;
|
|
2461
2432
|
try {
|
|
2462
|
-
const
|
|
2463
|
-
const client = new
|
|
2433
|
+
const OpenAI4 = await import("openai");
|
|
2434
|
+
const client = new OpenAI4.default({ apiKey });
|
|
2464
2435
|
const models = await client.models.list();
|
|
2465
2436
|
const hasModel = models.data.some((m) => m.id === modelName);
|
|
2466
2437
|
return {
|
|
@@ -2481,8 +2452,8 @@ var OpenAIProvider = class {
|
|
|
2481
2452
|
};
|
|
2482
2453
|
}
|
|
2483
2454
|
async chat(messages, context, options) {
|
|
2484
|
-
var
|
|
2485
|
-
const basePrompt = (_b = (
|
|
2455
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i;
|
|
2456
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2486
2457
|
const systemMessage = {
|
|
2487
2458
|
role: "system",
|
|
2488
2459
|
content: buildSystemContent(basePrompt, context)
|
|
@@ -2501,12 +2472,12 @@ var OpenAIProvider = class {
|
|
|
2501
2472
|
temperature: (_f = (_e = options == null ? void 0 : options.temperature) != null ? _e : this.llmConfig.temperature) != null ? _f : 0.7,
|
|
2502
2473
|
stop: options == null ? void 0 : options.stop
|
|
2503
2474
|
});
|
|
2504
|
-
return (_i = (_h = (
|
|
2475
|
+
return (_i = (_h = (_g2 = completion.choices[0]) == null ? void 0 : _g2.message) == null ? void 0 : _h.content) != null ? _i : "";
|
|
2505
2476
|
}
|
|
2506
2477
|
chatStream(messages, context, options) {
|
|
2507
2478
|
return __asyncGenerator(this, null, function* () {
|
|
2508
|
-
var
|
|
2509
|
-
const basePrompt = (_b = (
|
|
2479
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h;
|
|
2480
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2510
2481
|
const systemMessage = {
|
|
2511
2482
|
role: "system",
|
|
2512
2483
|
content: buildSystemContent(basePrompt, context)
|
|
@@ -2532,7 +2503,7 @@ var OpenAIProvider = class {
|
|
|
2532
2503
|
try {
|
|
2533
2504
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
2534
2505
|
const chunk = temp.value;
|
|
2535
|
-
const content = ((_h = (
|
|
2506
|
+
const content = ((_h = (_g2 = chunk.choices[0]) == null ? void 0 : _g2.delta) == null ? void 0 : _h.content) || "";
|
|
2536
2507
|
if (content) yield content;
|
|
2537
2508
|
}
|
|
2538
2509
|
} catch (temp) {
|
|
@@ -2552,8 +2523,8 @@ var OpenAIProvider = class {
|
|
|
2552
2523
|
return results[0];
|
|
2553
2524
|
}
|
|
2554
2525
|
async batchEmbed(texts, options) {
|
|
2555
|
-
var
|
|
2556
|
-
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (
|
|
2526
|
+
var _a2, _b, _c, _d, _e;
|
|
2527
|
+
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (_a2 = this.embeddingConfig) == null ? void 0 : _a2.model) != null ? _c : "text-embedding-3-small";
|
|
2557
2528
|
const apiKey = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.apiKey) != null ? _e : this.llmConfig.apiKey;
|
|
2558
2529
|
const client = apiKey !== this.llmConfig.apiKey ? new OpenAI({ apiKey }) : this.client;
|
|
2559
2530
|
const response = await client.embeddings.create({ model, input: texts });
|
|
@@ -2630,8 +2601,8 @@ var AnthropicProvider = class {
|
|
|
2630
2601
|
};
|
|
2631
2602
|
}
|
|
2632
2603
|
async chat(messages, context, options) {
|
|
2633
|
-
var
|
|
2634
|
-
const basePrompt = (_b = (
|
|
2604
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k;
|
|
2605
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the context below to answer the user's question accurately.";
|
|
2635
2606
|
const system = buildSystemContent(basePrompt, context);
|
|
2636
2607
|
const anthropicMessages = messages.map((m) => ({
|
|
2637
2608
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
@@ -2642,7 +2613,7 @@ var AnthropicProvider = class {
|
|
|
2642
2613
|
const extraParams = {};
|
|
2643
2614
|
if (isThinkingEnabled && isClaude37) {
|
|
2644
2615
|
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2645
|
-
const maxTokens = (
|
|
2616
|
+
const maxTokens = (_g2 = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g2 : 4096;
|
|
2646
2617
|
const budget = Math.min(
|
|
2647
2618
|
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2648
2619
|
maxTokens - 1
|
|
@@ -2669,8 +2640,8 @@ var AnthropicProvider = class {
|
|
|
2669
2640
|
}
|
|
2670
2641
|
chatStream(messages, context, options) {
|
|
2671
2642
|
return __asyncGenerator(this, null, function* () {
|
|
2672
|
-
var
|
|
2673
|
-
const basePrompt = (_b = (
|
|
2643
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k;
|
|
2644
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the context below to answer the user's question accurately.";
|
|
2674
2645
|
const system = buildSystemContent(basePrompt, context);
|
|
2675
2646
|
const anthropicMessages = messages.map((m) => ({
|
|
2676
2647
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
@@ -2681,7 +2652,7 @@ var AnthropicProvider = class {
|
|
|
2681
2652
|
const extraParams = {};
|
|
2682
2653
|
if (isThinkingEnabled && isClaude37) {
|
|
2683
2654
|
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2684
|
-
const maxTokens = (
|
|
2655
|
+
const maxTokens = (_g2 = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g2 : 4096;
|
|
2685
2656
|
const budget = Math.min(
|
|
2686
2657
|
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2687
2658
|
maxTokens - 1
|
|
@@ -2754,8 +2725,8 @@ var AnthropicProvider = class {
|
|
|
2754
2725
|
import axios from "axios";
|
|
2755
2726
|
var OllamaProvider = class {
|
|
2756
2727
|
constructor(llmConfig, embeddingConfig) {
|
|
2757
|
-
var
|
|
2758
|
-
const baseURL = (
|
|
2728
|
+
var _a2, _b;
|
|
2729
|
+
const baseURL = (_a2 = llmConfig.baseUrl) != null ? _a2 : "http://localhost:11434";
|
|
2759
2730
|
const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
|
|
2760
2731
|
this.http = axios.create({ baseURL, timeout });
|
|
2761
2732
|
this.llmConfig = llmConfig;
|
|
@@ -2813,8 +2784,8 @@ var OllamaProvider = class {
|
|
|
2813
2784
|
};
|
|
2814
2785
|
}
|
|
2815
2786
|
async chat(messages, context, options) {
|
|
2816
|
-
var
|
|
2817
|
-
const basePrompt = (_b = (
|
|
2787
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
2788
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the provided context to answer the user's question.";
|
|
2818
2789
|
const system = buildSystemContent(basePrompt, context);
|
|
2819
2790
|
const { data } = await this.http.post("/api/chat", {
|
|
2820
2791
|
model: this.llmConfig.model,
|
|
@@ -2832,8 +2803,8 @@ var OllamaProvider = class {
|
|
|
2832
2803
|
}
|
|
2833
2804
|
chatStream(messages, context, options) {
|
|
2834
2805
|
return __asyncGenerator(this, null, function* () {
|
|
2835
|
-
var
|
|
2836
|
-
const basePrompt = (_b = (
|
|
2806
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h;
|
|
2807
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the provided context to answer the user's question.";
|
|
2837
2808
|
const system = buildSystemContent(basePrompt, context);
|
|
2838
2809
|
const response = yield new __await(this.http.post("/api/chat", {
|
|
2839
2810
|
model: this.llmConfig.model,
|
|
@@ -2862,7 +2833,7 @@ var OllamaProvider = class {
|
|
|
2862
2833
|
if (!line.trim()) continue;
|
|
2863
2834
|
try {
|
|
2864
2835
|
const json = JSON.parse(line);
|
|
2865
|
-
if ((
|
|
2836
|
+
if ((_g2 = json.message) == null ? void 0 : _g2.content) {
|
|
2866
2837
|
yield json.message.content;
|
|
2867
2838
|
}
|
|
2868
2839
|
if (json.done) return;
|
|
@@ -2891,10 +2862,10 @@ var OllamaProvider = class {
|
|
|
2891
2862
|
});
|
|
2892
2863
|
}
|
|
2893
2864
|
async embed(text, options) {
|
|
2894
|
-
var
|
|
2895
|
-
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (
|
|
2865
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k;
|
|
2866
|
+
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (_a2 = this.embeddingConfig) == null ? void 0 : _a2.model) != null ? _c : "nomic-embed-text";
|
|
2896
2867
|
const baseURL = (_f = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.baseUrl) != null ? _e : this.llmConfig.baseUrl) != null ? _f : "http://localhost:11434";
|
|
2897
|
-
const client = baseURL !== ((
|
|
2868
|
+
const client = baseURL !== ((_g2 = this.llmConfig.baseUrl) != null ? _g2 : "http://localhost:11434") ? axios.create({ baseURL, timeout: 6e4 }) : this.http;
|
|
2898
2869
|
let prompt = text;
|
|
2899
2870
|
const queryPrefix = (_i = (_h = this.embeddingConfig) == null ? void 0 : _h.queryPrefix) != null ? _i : model.includes("nomic") ? "search_query: " : "";
|
|
2900
2871
|
const docPrefix = (_k = (_j = this.embeddingConfig) == null ? void 0 : _j.documentPrefix) != null ? _k : model.includes("nomic") ? "search_document: " : "";
|
|
@@ -2993,9 +2964,9 @@ var GeminiProvider = class {
|
|
|
2993
2964
|
static getHealthChecker() {
|
|
2994
2965
|
return {
|
|
2995
2966
|
async check(config) {
|
|
2996
|
-
var
|
|
2967
|
+
var _a2, _b;
|
|
2997
2968
|
const timestamp = Date.now();
|
|
2998
|
-
const apiKey = (_b = (
|
|
2969
|
+
const apiKey = (_b = (_a2 = config.apiKey) != null ? _a2 : process.env.GOOGLE_GENAI_API_KEY) != null ? _b : "";
|
|
2999
2970
|
const modelName = sanitizeModel(config.model);
|
|
3000
2971
|
try {
|
|
3001
2972
|
const { GoogleGenerativeAI: GoogleGenerativeAI2 } = await import("@google/generative-ai");
|
|
@@ -3025,16 +2996,16 @@ var GeminiProvider = class {
|
|
|
3025
2996
|
/** Resolve the embedding client — uses a separate client when the embedding
|
|
3026
2997
|
* API key differs from the LLM API key. */
|
|
3027
2998
|
get embeddingClient() {
|
|
3028
|
-
var
|
|
3029
|
-
if (((
|
|
2999
|
+
var _a2;
|
|
3000
|
+
if (((_a2 = this.embeddingConfig) == null ? void 0 : _a2.apiKey) && this.embeddingConfig.apiKey !== this.llmConfig.apiKey) {
|
|
3030
3001
|
return buildClient(this.embeddingConfig.apiKey);
|
|
3031
3002
|
}
|
|
3032
3003
|
return this.client;
|
|
3033
3004
|
}
|
|
3034
3005
|
/** Resolve the embedding model to use, in order of specificity. */
|
|
3035
3006
|
resolveEmbeddingModel(optionsModel) {
|
|
3036
|
-
var
|
|
3037
|
-
return sanitizeModel((_b = optionsModel != null ? optionsModel : (
|
|
3007
|
+
var _a2, _b;
|
|
3008
|
+
return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a2 = this.embeddingConfig) == null ? void 0 : _a2.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
|
|
3038
3009
|
}
|
|
3039
3010
|
/**
|
|
3040
3011
|
* Convert ChatMessage[] to the Gemini contents format.
|
|
@@ -3054,8 +3025,8 @@ var GeminiProvider = class {
|
|
|
3054
3025
|
// ILLMProvider — chat
|
|
3055
3026
|
// -------------------------------------------------------------------------
|
|
3056
3027
|
async chat(messages, context, options) {
|
|
3057
|
-
var
|
|
3058
|
-
const basePrompt = (_b = (
|
|
3028
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
3029
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
3059
3030
|
const model = this.client.getGenerativeModel({
|
|
3060
3031
|
model: this.llmConfig.model,
|
|
3061
3032
|
systemInstruction: buildSystemContent(basePrompt, context)
|
|
@@ -3072,8 +3043,8 @@ var GeminiProvider = class {
|
|
|
3072
3043
|
}
|
|
3073
3044
|
chatStream(messages, context, options) {
|
|
3074
3045
|
return __asyncGenerator(this, null, function* () {
|
|
3075
|
-
var
|
|
3076
|
-
const basePrompt = (_b = (
|
|
3046
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
3047
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
3077
3048
|
const model = this.client.getGenerativeModel({
|
|
3078
3049
|
model: this.llmConfig.model,
|
|
3079
3050
|
systemInstruction: buildSystemContent(basePrompt, context)
|
|
@@ -3108,11 +3079,11 @@ var GeminiProvider = class {
|
|
|
3108
3079
|
// ILLMProvider — embeddings
|
|
3109
3080
|
// -------------------------------------------------------------------------
|
|
3110
3081
|
async embed(text, options) {
|
|
3111
|
-
var
|
|
3082
|
+
var _a2, _b;
|
|
3112
3083
|
const content = applyPrefix(
|
|
3113
3084
|
text,
|
|
3114
3085
|
options == null ? void 0 : options.taskType,
|
|
3115
|
-
(
|
|
3086
|
+
(_a2 = this.embeddingConfig) == null ? void 0 : _a2.queryPrefix,
|
|
3116
3087
|
(_b = this.embeddingConfig) == null ? void 0 : _b.documentPrefix
|
|
3117
3088
|
);
|
|
3118
3089
|
const modelName = this.resolveEmbeddingModel(options == null ? void 0 : options.model);
|
|
@@ -3129,7 +3100,7 @@ var GeminiProvider = class {
|
|
|
3129
3100
|
const modelName = this.resolveEmbeddingModel(options == null ? void 0 : options.model);
|
|
3130
3101
|
const model = this.embeddingClient.getGenerativeModel({ model: modelName });
|
|
3131
3102
|
const requests = texts.map((text) => {
|
|
3132
|
-
var
|
|
3103
|
+
var _a2, _b;
|
|
3133
3104
|
return {
|
|
3134
3105
|
content: {
|
|
3135
3106
|
role: "user",
|
|
@@ -3137,7 +3108,7 @@ var GeminiProvider = class {
|
|
|
3137
3108
|
text: applyPrefix(
|
|
3138
3109
|
text,
|
|
3139
3110
|
options == null ? void 0 : options.taskType,
|
|
3140
|
-
(
|
|
3111
|
+
(_a2 = this.embeddingConfig) == null ? void 0 : _a2.queryPrefix,
|
|
3141
3112
|
(_b = this.embeddingConfig) == null ? void 0 : _b.documentPrefix
|
|
3142
3113
|
)
|
|
3143
3114
|
}]
|
|
@@ -3154,8 +3125,8 @@ var GeminiProvider = class {
|
|
|
3154
3125
|
throw err;
|
|
3155
3126
|
});
|
|
3156
3127
|
return response.embeddings.map((e) => {
|
|
3157
|
-
var
|
|
3158
|
-
return (
|
|
3128
|
+
var _a2;
|
|
3129
|
+
return (_a2 = e.values) != null ? _a2 : [];
|
|
3159
3130
|
});
|
|
3160
3131
|
}
|
|
3161
3132
|
// -------------------------------------------------------------------------
|
|
@@ -3175,169 +3146,488 @@ var GeminiProvider = class {
|
|
|
3175
3146
|
}
|
|
3176
3147
|
};
|
|
3177
3148
|
|
|
3178
|
-
// src/llm/providers/
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}}}'
|
|
3189
|
-
};
|
|
3190
|
-
var LLM_PROFILES = {
|
|
3191
|
-
"openai-compatible": OPENAI_BASE,
|
|
3192
|
-
"litellm": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
3193
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}},"stream":false}'
|
|
3194
|
-
}),
|
|
3195
|
-
"anthropic-claude": {
|
|
3196
|
-
chatPath: "/v1/messages",
|
|
3197
|
-
responseExtractPath: "content[0].text",
|
|
3198
|
-
headers: { "anthropic-version": "2023-06-01" },
|
|
3199
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}}}'
|
|
3200
|
-
},
|
|
3201
|
-
"google-gemini": {
|
|
3202
|
-
chatPath: "/v1beta/models/{{model}}:generateContent",
|
|
3203
|
-
responseExtractPath: "candidates[0].content.parts[0].text",
|
|
3204
|
-
chatPayloadTemplate: '{"contents":[{"parts":[{"text":"{{messages}}"}]}]}'
|
|
3205
|
-
},
|
|
3206
|
-
"github-copilot": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
3207
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"temperature":{{temperature}}}'
|
|
3208
|
-
}),
|
|
3209
|
-
"ollama-standard": {
|
|
3210
|
-
chatPath: "/api/chat",
|
|
3211
|
-
embedPath: "/api/embeddings",
|
|
3212
|
-
responseExtractPath: "message.content",
|
|
3213
|
-
embedExtractPath: "embedding",
|
|
3214
|
-
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"stream":false}',
|
|
3215
|
-
embedPayloadTemplate: '{"model":"{{model}}","prompt":"{{input}}"}'
|
|
3216
|
-
}
|
|
3217
|
-
};
|
|
3218
|
-
|
|
3219
|
-
// src/llm/providers/UniversalLLMAdapter.ts
|
|
3220
|
-
var UniversalLLMAdapter = class {
|
|
3221
|
-
constructor(config) {
|
|
3222
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
3223
|
-
this.model = config.model;
|
|
3224
|
-
const llmConfig = config;
|
|
3225
|
-
const options = (_a = llmConfig.options) != null ? _a : {};
|
|
3226
|
-
let profile = {};
|
|
3227
|
-
if (typeof options.profile === "string") {
|
|
3228
|
-
profile = LLM_PROFILES[options.profile] || {};
|
|
3229
|
-
} else if (typeof options.profile === "object" && options.profile !== null) {
|
|
3230
|
-
profile = options.profile;
|
|
3231
|
-
}
|
|
3232
|
-
this.opts = __spreadValues(__spreadValues({}, profile), (_b = llmConfig.options) != null ? _b : {});
|
|
3233
|
-
this.systemPrompt = (_c = llmConfig.systemPrompt) != null ? _c : "You are a helpful AI assistant. Use the provided context to answer the user.";
|
|
3234
|
-
this.maxTokens = (_d = llmConfig.maxTokens) != null ? _d : 1024;
|
|
3235
|
-
this.temperature = (_e = llmConfig.temperature) != null ? _e : 0;
|
|
3236
|
-
this.apiKey = config.apiKey;
|
|
3237
|
-
this.baseUrl = (_g = (_f = llmConfig.baseUrl) != null ? _f : this.opts.baseUrl) != null ? _g : "";
|
|
3238
|
-
if (!this.baseUrl) {
|
|
3239
|
-
throw new Error("[UniversalLLMAdapter] baseUrl is required in config or config.options");
|
|
3240
|
-
}
|
|
3241
|
-
this.resolvedHeaders = __spreadValues(__spreadValues({
|
|
3242
|
-
"Content-Type": "application/json"
|
|
3243
|
-
}, config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}), this.opts.headers || {});
|
|
3244
|
-
this.http = axios2.create({
|
|
3245
|
-
baseURL: this.baseUrl,
|
|
3246
|
-
headers: this.resolvedHeaders,
|
|
3247
|
-
timeout: (_h = this.opts.timeout) != null ? _h : 6e4
|
|
3149
|
+
// src/llm/providers/GroqProvider.ts
|
|
3150
|
+
import OpenAI2 from "openai";
|
|
3151
|
+
var GroqProvider = class {
|
|
3152
|
+
constructor(llmConfig, embeddingConfig) {
|
|
3153
|
+
void embeddingConfig;
|
|
3154
|
+
const apiKey = llmConfig.apiKey || process.env.GROQ_API_KEY;
|
|
3155
|
+
if (!apiKey) throw new Error("[GroqProvider] llmConfig.apiKey or GROQ_API_KEY environment variable is required");
|
|
3156
|
+
this.client = new OpenAI2({
|
|
3157
|
+
apiKey,
|
|
3158
|
+
baseURL: llmConfig.baseUrl || "https://api.groq.com/openai/v1"
|
|
3248
3159
|
});
|
|
3160
|
+
this.llmConfig = llmConfig;
|
|
3249
3161
|
}
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3162
|
+
static getValidator() {
|
|
3163
|
+
return {
|
|
3164
|
+
validate(config) {
|
|
3165
|
+
const errors = [];
|
|
3166
|
+
if (!config.apiKey && !process.env.GROQ_API_KEY) {
|
|
3167
|
+
errors.push({
|
|
3168
|
+
field: "llm.apiKey",
|
|
3169
|
+
message: "Groq API key is required",
|
|
3170
|
+
suggestion: "Set GROQ_API_KEY environment variable",
|
|
3171
|
+
severity: "error"
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
if (!config.model) {
|
|
3175
|
+
errors.push({
|
|
3176
|
+
field: "llm.model",
|
|
3177
|
+
message: "Groq model name is required",
|
|
3178
|
+
suggestion: 'e.g., "llama-3.3-70b-versatile"',
|
|
3179
|
+
severity: "error"
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
return errors;
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
static getHealthChecker() {
|
|
3187
|
+
return {
|
|
3188
|
+
async check(config) {
|
|
3189
|
+
const timestamp = Date.now();
|
|
3190
|
+
const apiKey = config.apiKey || process.env.GROQ_API_KEY || "";
|
|
3191
|
+
const modelName = config.model;
|
|
3192
|
+
try {
|
|
3193
|
+
const OpenAI4 = await import("openai");
|
|
3194
|
+
const client = new OpenAI4.default({
|
|
3195
|
+
apiKey,
|
|
3196
|
+
baseURL: config.baseUrl || "https://api.groq.com/openai/v1"
|
|
3197
|
+
});
|
|
3198
|
+
const models = await client.models.list();
|
|
3199
|
+
const hasModel = models.data.some((m) => m.id === modelName);
|
|
3200
|
+
return {
|
|
3201
|
+
healthy: true,
|
|
3202
|
+
provider: "groq",
|
|
3203
|
+
capabilities: { model: modelName, available: hasModel, totalModels: models.data.length },
|
|
3204
|
+
timestamp
|
|
3205
|
+
};
|
|
3206
|
+
} catch (error) {
|
|
3207
|
+
return {
|
|
3208
|
+
healthy: false,
|
|
3209
|
+
provider: "groq",
|
|
3210
|
+
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
3211
|
+
timestamp
|
|
3212
|
+
};
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
async chat(messages, context, options) {
|
|
3218
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i;
|
|
3219
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
3220
|
+
const systemMessage = {
|
|
3221
|
+
role: "system",
|
|
3222
|
+
content: buildSystemContent(basePrompt, context)
|
|
3223
|
+
};
|
|
3253
3224
|
const formattedMessages = [
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3225
|
+
systemMessage,
|
|
3226
|
+
...messages.map((m) => ({
|
|
3227
|
+
role: m.role,
|
|
3228
|
+
content: m.content
|
|
3229
|
+
}))
|
|
3259
3230
|
];
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
} else {
|
|
3269
|
-
payload = {
|
|
3270
|
-
model: this.model,
|
|
3271
|
-
messages: formattedMessages,
|
|
3272
|
-
max_tokens: this.maxTokens,
|
|
3273
|
-
temperature: this.temperature
|
|
3274
|
-
};
|
|
3275
|
-
}
|
|
3276
|
-
const { data } = await this.http.post(path, payload);
|
|
3277
|
-
const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
|
|
3278
|
-
const result = resolvePath(data, extractPath);
|
|
3279
|
-
if (result === void 0) {
|
|
3280
|
-
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
3281
|
-
}
|
|
3282
|
-
return String(result);
|
|
3231
|
+
const completion = await this.client.chat.completions.create({
|
|
3232
|
+
model: this.llmConfig.model,
|
|
3233
|
+
messages: formattedMessages,
|
|
3234
|
+
max_tokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
|
|
3235
|
+
temperature: (_f = (_e = options == null ? void 0 : options.temperature) != null ? _e : this.llmConfig.temperature) != null ? _f : 0.7,
|
|
3236
|
+
stop: options == null ? void 0 : options.stop
|
|
3237
|
+
});
|
|
3238
|
+
return (_i = (_h = (_g2 = completion.choices[0]) == null ? void 0 : _g2.message) == null ? void 0 : _h.content) != null ? _i : "";
|
|
3283
3239
|
}
|
|
3284
|
-
|
|
3285
|
-
* Streaming chat using native fetch + ReadableStream.
|
|
3286
|
-
* Parses OpenAI-compatible SSE frames: `data: {...}\n\n`
|
|
3287
|
-
* Works with vLLM, LMStudio, Together AI, Fireworks, and any OpenAI-compatible API.
|
|
3288
|
-
*/
|
|
3289
|
-
chatStream(messages, context) {
|
|
3240
|
+
chatStream(messages, context, options) {
|
|
3290
3241
|
return __asyncGenerator(this, null, function* () {
|
|
3291
|
-
var
|
|
3292
|
-
const
|
|
3293
|
-
const
|
|
3294
|
-
|
|
3242
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h;
|
|
3243
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
3244
|
+
const systemMessage = {
|
|
3245
|
+
role: "system",
|
|
3246
|
+
content: buildSystemContent(basePrompt, context)
|
|
3247
|
+
};
|
|
3295
3248
|
const formattedMessages = [
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3249
|
+
systemMessage,
|
|
3250
|
+
...messages.map((m) => ({
|
|
3251
|
+
role: m.role,
|
|
3252
|
+
content: m.content
|
|
3253
|
+
}))
|
|
3301
3254
|
];
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
});
|
|
3310
|
-
if (typeof payload === "object" && payload !== null) {
|
|
3311
|
-
payload.stream = true;
|
|
3312
|
-
}
|
|
3313
|
-
} else {
|
|
3314
|
-
payload = {
|
|
3315
|
-
model: this.model,
|
|
3316
|
-
messages: formattedMessages,
|
|
3317
|
-
max_tokens: this.maxTokens,
|
|
3318
|
-
temperature: this.temperature,
|
|
3319
|
-
stream: true
|
|
3320
|
-
};
|
|
3321
|
-
}
|
|
3322
|
-
const response = yield new __await(fetch(url, {
|
|
3323
|
-
method: "POST",
|
|
3324
|
-
headers: this.resolvedHeaders,
|
|
3325
|
-
body: JSON.stringify(payload)
|
|
3255
|
+
const stream = yield new __await(this.client.chat.completions.create({
|
|
3256
|
+
model: this.llmConfig.model,
|
|
3257
|
+
messages: formattedMessages,
|
|
3258
|
+
max_tokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
|
|
3259
|
+
temperature: (_f = (_e = options == null ? void 0 : options.temperature) != null ? _e : this.llmConfig.temperature) != null ? _f : 0.7,
|
|
3260
|
+
stop: options == null ? void 0 : options.stop,
|
|
3261
|
+
stream: true
|
|
3326
3262
|
}));
|
|
3327
|
-
if (!
|
|
3328
|
-
|
|
3329
|
-
throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
|
|
3330
|
-
}
|
|
3331
|
-
if (!response.body) {
|
|
3332
|
-
throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
|
|
3263
|
+
if (!stream) {
|
|
3264
|
+
throw new Error("[GroqProvider] completions.create stream is undefined");
|
|
3333
3265
|
}
|
|
3334
|
-
const reader = response.body.getReader();
|
|
3335
|
-
const decoder = new TextDecoder("utf-8");
|
|
3336
|
-
let buffer = "";
|
|
3337
3266
|
try {
|
|
3338
|
-
|
|
3339
|
-
const
|
|
3340
|
-
|
|
3267
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
3268
|
+
const chunk = temp.value;
|
|
3269
|
+
const content = ((_h = (_g2 = chunk.choices[0]) == null ? void 0 : _g2.delta) == null ? void 0 : _h.content) || "";
|
|
3270
|
+
if (content) yield content;
|
|
3271
|
+
}
|
|
3272
|
+
} catch (temp) {
|
|
3273
|
+
error = [temp];
|
|
3274
|
+
} finally {
|
|
3275
|
+
try {
|
|
3276
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
3277
|
+
} finally {
|
|
3278
|
+
if (error)
|
|
3279
|
+
throw error[0];
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
});
|
|
3283
|
+
}
|
|
3284
|
+
async embed(text, options) {
|
|
3285
|
+
void text;
|
|
3286
|
+
void options;
|
|
3287
|
+
throw new Error('[GroqProvider] Groq does not provide a native embedding API. Please configure "openai" or "gemini" for embeddings in RagConfig.');
|
|
3288
|
+
}
|
|
3289
|
+
async batchEmbed(texts, options) {
|
|
3290
|
+
void texts;
|
|
3291
|
+
void options;
|
|
3292
|
+
throw new Error('[GroqProvider] Groq does not provide a native embedding API. Please configure "openai" or "gemini" for embeddings in RagConfig.');
|
|
3293
|
+
}
|
|
3294
|
+
async ping() {
|
|
3295
|
+
try {
|
|
3296
|
+
await this.client.models.list();
|
|
3297
|
+
return true;
|
|
3298
|
+
} catch (err) {
|
|
3299
|
+
console.error("[GroqProvider] Ping failed:", err);
|
|
3300
|
+
return false;
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
|
|
3305
|
+
// src/llm/providers/QwenProvider.ts
|
|
3306
|
+
import OpenAI3 from "openai";
|
|
3307
|
+
var QwenProvider = class {
|
|
3308
|
+
constructor(llmConfig, embeddingConfig) {
|
|
3309
|
+
const apiKey = llmConfig.apiKey || process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY;
|
|
3310
|
+
if (!apiKey) throw new Error("[QwenProvider] llmConfig.apiKey, DASHSCOPE_API_KEY, or QWEN_API_KEY environment variable is required");
|
|
3311
|
+
this.client = new OpenAI3({
|
|
3312
|
+
apiKey,
|
|
3313
|
+
baseURL: llmConfig.baseUrl || "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
3314
|
+
});
|
|
3315
|
+
this.llmConfig = llmConfig;
|
|
3316
|
+
this.embeddingConfig = embeddingConfig;
|
|
3317
|
+
}
|
|
3318
|
+
static getValidator() {
|
|
3319
|
+
return {
|
|
3320
|
+
validate(config) {
|
|
3321
|
+
const errors = [];
|
|
3322
|
+
const isEmbedding = config.provider === "qwen" && "dimensions" in config;
|
|
3323
|
+
const prefix = isEmbedding ? "embedding" : "llm";
|
|
3324
|
+
if (!config.apiKey && !process.env.DASHSCOPE_API_KEY && !process.env.QWEN_API_KEY) {
|
|
3325
|
+
errors.push({
|
|
3326
|
+
field: `${prefix}.apiKey`,
|
|
3327
|
+
message: "Qwen API key is required",
|
|
3328
|
+
suggestion: "Set DASHSCOPE_API_KEY or QWEN_API_KEY environment variable",
|
|
3329
|
+
severity: "error"
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
3332
|
+
if (!config.model) {
|
|
3333
|
+
errors.push({
|
|
3334
|
+
field: `${prefix}.model`,
|
|
3335
|
+
message: "Qwen model name is required",
|
|
3336
|
+
suggestion: isEmbedding ? 'e.g., "text-embedding-v3"' : 'e.g., "qwen-plus" or "qwen-max"',
|
|
3337
|
+
severity: "error"
|
|
3338
|
+
});
|
|
3339
|
+
}
|
|
3340
|
+
return errors;
|
|
3341
|
+
}
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
static getHealthChecker() {
|
|
3345
|
+
return {
|
|
3346
|
+
async check(config) {
|
|
3347
|
+
const timestamp = Date.now();
|
|
3348
|
+
const apiKey = config.apiKey || process.env.DASHSCOPE_API_KEY || process.env.QWEN_API_KEY || "";
|
|
3349
|
+
const modelName = config.model;
|
|
3350
|
+
try {
|
|
3351
|
+
const OpenAI4 = await import("openai");
|
|
3352
|
+
const client = new OpenAI4.default({
|
|
3353
|
+
apiKey,
|
|
3354
|
+
baseURL: config.baseUrl || "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
3355
|
+
});
|
|
3356
|
+
const models = await client.models.list();
|
|
3357
|
+
const hasModel = models.data.some((m) => m.id === modelName);
|
|
3358
|
+
return {
|
|
3359
|
+
healthy: true,
|
|
3360
|
+
provider: "qwen",
|
|
3361
|
+
capabilities: { model: modelName, available: hasModel, totalModels: models.data.length },
|
|
3362
|
+
timestamp
|
|
3363
|
+
};
|
|
3364
|
+
} catch (error) {
|
|
3365
|
+
return {
|
|
3366
|
+
healthy: false,
|
|
3367
|
+
provider: "qwen",
|
|
3368
|
+
error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
3369
|
+
timestamp
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
};
|
|
3374
|
+
}
|
|
3375
|
+
async chat(messages, context, options) {
|
|
3376
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i;
|
|
3377
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
3378
|
+
const systemMessage = {
|
|
3379
|
+
role: "system",
|
|
3380
|
+
content: buildSystemContent(basePrompt, context)
|
|
3381
|
+
};
|
|
3382
|
+
const formattedMessages = [
|
|
3383
|
+
systemMessage,
|
|
3384
|
+
...messages.map((m) => ({
|
|
3385
|
+
role: m.role,
|
|
3386
|
+
content: m.content
|
|
3387
|
+
}))
|
|
3388
|
+
];
|
|
3389
|
+
const completion = await this.client.chat.completions.create({
|
|
3390
|
+
model: this.llmConfig.model,
|
|
3391
|
+
messages: formattedMessages,
|
|
3392
|
+
max_tokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
|
|
3393
|
+
temperature: (_f = (_e = options == null ? void 0 : options.temperature) != null ? _e : this.llmConfig.temperature) != null ? _f : 0.7,
|
|
3394
|
+
stop: options == null ? void 0 : options.stop
|
|
3395
|
+
});
|
|
3396
|
+
return (_i = (_h = (_g2 = completion.choices[0]) == null ? void 0 : _g2.message) == null ? void 0 : _h.content) != null ? _i : "";
|
|
3397
|
+
}
|
|
3398
|
+
chatStream(messages, context, options) {
|
|
3399
|
+
return __asyncGenerator(this, null, function* () {
|
|
3400
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h;
|
|
3401
|
+
const basePrompt = (_b = (_a2 = options == null ? void 0 : options.systemPrompt) != null ? _a2 : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
3402
|
+
const systemMessage = {
|
|
3403
|
+
role: "system",
|
|
3404
|
+
content: buildSystemContent(basePrompt, context)
|
|
3405
|
+
};
|
|
3406
|
+
const formattedMessages = [
|
|
3407
|
+
systemMessage,
|
|
3408
|
+
...messages.map((m) => ({
|
|
3409
|
+
role: m.role,
|
|
3410
|
+
content: m.content
|
|
3411
|
+
}))
|
|
3412
|
+
];
|
|
3413
|
+
const stream = yield new __await(this.client.chat.completions.create({
|
|
3414
|
+
model: this.llmConfig.model,
|
|
3415
|
+
messages: formattedMessages,
|
|
3416
|
+
max_tokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
|
|
3417
|
+
temperature: (_f = (_e = options == null ? void 0 : options.temperature) != null ? _e : this.llmConfig.temperature) != null ? _f : 0.7,
|
|
3418
|
+
stop: options == null ? void 0 : options.stop,
|
|
3419
|
+
stream: true
|
|
3420
|
+
}));
|
|
3421
|
+
if (!stream) {
|
|
3422
|
+
throw new Error("[QwenProvider] completions.create stream is undefined");
|
|
3423
|
+
}
|
|
3424
|
+
try {
|
|
3425
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
3426
|
+
const chunk = temp.value;
|
|
3427
|
+
const content = ((_h = (_g2 = chunk.choices[0]) == null ? void 0 : _g2.delta) == null ? void 0 : _h.content) || "";
|
|
3428
|
+
if (content) yield content;
|
|
3429
|
+
}
|
|
3430
|
+
} catch (temp) {
|
|
3431
|
+
error = [temp];
|
|
3432
|
+
} finally {
|
|
3433
|
+
try {
|
|
3434
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
3435
|
+
} finally {
|
|
3436
|
+
if (error)
|
|
3437
|
+
throw error[0];
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
});
|
|
3441
|
+
}
|
|
3442
|
+
async embed(text, options) {
|
|
3443
|
+
const results = await this.batchEmbed([text], options);
|
|
3444
|
+
return results[0];
|
|
3445
|
+
}
|
|
3446
|
+
async batchEmbed(texts, options) {
|
|
3447
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
3448
|
+
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (_a2 = this.embeddingConfig) == null ? void 0 : _a2.model) != null ? _c : "text-embedding-v1";
|
|
3449
|
+
const apiKey = (_e = (_d = this.embeddingConfig) == null ? void 0 : _d.apiKey) != null ? _e : this.llmConfig.apiKey;
|
|
3450
|
+
const client = apiKey !== this.llmConfig.apiKey ? new OpenAI3({
|
|
3451
|
+
apiKey,
|
|
3452
|
+
baseURL: ((_f = this.embeddingConfig) == null ? void 0 : _f.baseUrl) || "https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
3453
|
+
}) : this.client;
|
|
3454
|
+
const response = await client.embeddings.create({ model, input: texts });
|
|
3455
|
+
return response.data.map((d) => d.embedding);
|
|
3456
|
+
}
|
|
3457
|
+
async ping() {
|
|
3458
|
+
try {
|
|
3459
|
+
await this.client.models.list();
|
|
3460
|
+
return true;
|
|
3461
|
+
} catch (err) {
|
|
3462
|
+
console.error("[QwenProvider] Ping failed:", err);
|
|
3463
|
+
return false;
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
};
|
|
3467
|
+
|
|
3468
|
+
// src/llm/providers/UniversalLLMAdapter.ts
|
|
3469
|
+
init_templateUtils();
|
|
3470
|
+
import axios2 from "axios";
|
|
3471
|
+
|
|
3472
|
+
// src/config/UniversalProfiles.ts
|
|
3473
|
+
var OPENAI_BASE = {
|
|
3474
|
+
chatPath: "/chat/completions",
|
|
3475
|
+
embedPath: "/embeddings",
|
|
3476
|
+
responseExtractPath: "choices[0].message.content",
|
|
3477
|
+
embedExtractPath: "data[0].embedding",
|
|
3478
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}}}'
|
|
3479
|
+
};
|
|
3480
|
+
var LLM_PROFILES = {
|
|
3481
|
+
"openai-compatible": OPENAI_BASE,
|
|
3482
|
+
"litellm": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
3483
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}},"temperature":{{temperature}},"stream":false}'
|
|
3484
|
+
}),
|
|
3485
|
+
"anthropic-claude": {
|
|
3486
|
+
chatPath: "/v1/messages",
|
|
3487
|
+
responseExtractPath: "content[0].text",
|
|
3488
|
+
headers: { "anthropic-version": "2023-06-01" },
|
|
3489
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"max_tokens":{{maxTokens}}}'
|
|
3490
|
+
},
|
|
3491
|
+
"google-gemini": {
|
|
3492
|
+
chatPath: "/v1beta/models/{{model}}:generateContent",
|
|
3493
|
+
responseExtractPath: "candidates[0].content.parts[0].text",
|
|
3494
|
+
chatPayloadTemplate: '{"contents":[{"parts":[{"text":"{{messages}}"}]}]}'
|
|
3495
|
+
},
|
|
3496
|
+
"github-copilot": __spreadProps(__spreadValues({}, OPENAI_BASE), {
|
|
3497
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"temperature":{{temperature}}}'
|
|
3498
|
+
}),
|
|
3499
|
+
"ollama-standard": {
|
|
3500
|
+
chatPath: "/api/chat",
|
|
3501
|
+
embedPath: "/api/embeddings",
|
|
3502
|
+
responseExtractPath: "message.content",
|
|
3503
|
+
embedExtractPath: "embedding",
|
|
3504
|
+
chatPayloadTemplate: '{"model":"{{model}}","messages":{{messages}},"stream":false}',
|
|
3505
|
+
embedPayloadTemplate: '{"model":"{{model}}","prompt":"{{input}}"}'
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
|
|
3509
|
+
// src/llm/providers/UniversalLLMAdapter.ts
|
|
3510
|
+
var UniversalLLMAdapter = class {
|
|
3511
|
+
constructor(config) {
|
|
3512
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h;
|
|
3513
|
+
this.model = config.model;
|
|
3514
|
+
const llmConfig = config;
|
|
3515
|
+
const options = (_a2 = llmConfig.options) != null ? _a2 : {};
|
|
3516
|
+
let profile = {};
|
|
3517
|
+
if (typeof options.profile === "string") {
|
|
3518
|
+
profile = LLM_PROFILES[options.profile] || {};
|
|
3519
|
+
} else if (typeof options.profile === "object" && options.profile !== null) {
|
|
3520
|
+
profile = options.profile;
|
|
3521
|
+
}
|
|
3522
|
+
this.opts = __spreadValues(__spreadValues({}, profile), (_b = llmConfig.options) != null ? _b : {});
|
|
3523
|
+
this.systemPrompt = (_c = llmConfig.systemPrompt) != null ? _c : "You are a helpful AI assistant. Use the provided context to answer the user.";
|
|
3524
|
+
this.maxTokens = (_d = llmConfig.maxTokens) != null ? _d : 1024;
|
|
3525
|
+
this.temperature = (_e = llmConfig.temperature) != null ? _e : 0;
|
|
3526
|
+
this.apiKey = config.apiKey;
|
|
3527
|
+
this.baseUrl = (_g2 = (_f = llmConfig.baseUrl) != null ? _f : this.opts.baseUrl) != null ? _g2 : "";
|
|
3528
|
+
if (!this.baseUrl) {
|
|
3529
|
+
throw new Error("[UniversalLLMAdapter] baseUrl is required in config or config.options");
|
|
3530
|
+
}
|
|
3531
|
+
this.resolvedHeaders = __spreadValues(__spreadValues({
|
|
3532
|
+
"Content-Type": "application/json"
|
|
3533
|
+
}, config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}), this.opts.headers || {});
|
|
3534
|
+
this.http = axios2.create({
|
|
3535
|
+
baseURL: this.baseUrl,
|
|
3536
|
+
headers: this.resolvedHeaders,
|
|
3537
|
+
timeout: (_h = this.opts.timeout) != null ? _h : 6e4
|
|
3538
|
+
});
|
|
3539
|
+
}
|
|
3540
|
+
async chat(messages, context) {
|
|
3541
|
+
var _a2, _b;
|
|
3542
|
+
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3543
|
+
const formattedMessages = [
|
|
3544
|
+
{ role: "system", content: `${this.systemPrompt}
|
|
3545
|
+
|
|
3546
|
+
Context:
|
|
3547
|
+
${context != null ? context : "None"}` },
|
|
3548
|
+
...messages
|
|
3549
|
+
];
|
|
3550
|
+
let payload;
|
|
3551
|
+
if (this.opts.chatPayloadTemplate) {
|
|
3552
|
+
payload = buildPayload(this.opts.chatPayloadTemplate, {
|
|
3553
|
+
model: this.model,
|
|
3554
|
+
messages: formattedMessages,
|
|
3555
|
+
maxTokens: this.maxTokens,
|
|
3556
|
+
temperature: this.temperature
|
|
3557
|
+
});
|
|
3558
|
+
} else {
|
|
3559
|
+
payload = {
|
|
3560
|
+
model: this.model,
|
|
3561
|
+
messages: formattedMessages,
|
|
3562
|
+
max_tokens: this.maxTokens,
|
|
3563
|
+
temperature: this.temperature
|
|
3564
|
+
};
|
|
3565
|
+
}
|
|
3566
|
+
const { data } = await this.http.post(path2, payload);
|
|
3567
|
+
const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
|
|
3568
|
+
const result = resolvePath(data, extractPath);
|
|
3569
|
+
if (result === void 0) {
|
|
3570
|
+
throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
|
|
3571
|
+
}
|
|
3572
|
+
return String(result);
|
|
3573
|
+
}
|
|
3574
|
+
/**
|
|
3575
|
+
* Streaming chat using native fetch + ReadableStream.
|
|
3576
|
+
* Parses OpenAI-compatible SSE frames: `data: {...}\n\n`
|
|
3577
|
+
* Works with vLLM, LMStudio, Together AI, Fireworks, and any OpenAI-compatible API.
|
|
3578
|
+
*/
|
|
3579
|
+
chatStream(messages, context) {
|
|
3580
|
+
return __asyncGenerator(this, null, function* () {
|
|
3581
|
+
var _a2, _b, _c;
|
|
3582
|
+
const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
|
|
3583
|
+
const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
|
|
3584
|
+
const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
|
|
3585
|
+
const formattedMessages = [
|
|
3586
|
+
{ role: "system", content: `${this.systemPrompt}
|
|
3587
|
+
|
|
3588
|
+
Context:
|
|
3589
|
+
${context != null ? context : "None"}` },
|
|
3590
|
+
...messages
|
|
3591
|
+
];
|
|
3592
|
+
let payload;
|
|
3593
|
+
if (this.opts.chatPayloadTemplate) {
|
|
3594
|
+
payload = buildPayload(this.opts.chatPayloadTemplate, {
|
|
3595
|
+
model: this.model,
|
|
3596
|
+
messages: formattedMessages,
|
|
3597
|
+
maxTokens: this.maxTokens,
|
|
3598
|
+
temperature: this.temperature
|
|
3599
|
+
});
|
|
3600
|
+
if (typeof payload === "object" && payload !== null) {
|
|
3601
|
+
payload.stream = true;
|
|
3602
|
+
}
|
|
3603
|
+
} else {
|
|
3604
|
+
payload = {
|
|
3605
|
+
model: this.model,
|
|
3606
|
+
messages: formattedMessages,
|
|
3607
|
+
max_tokens: this.maxTokens,
|
|
3608
|
+
temperature: this.temperature,
|
|
3609
|
+
stream: true
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
const response = yield new __await(fetch(url, {
|
|
3613
|
+
method: "POST",
|
|
3614
|
+
headers: this.resolvedHeaders,
|
|
3615
|
+
body: JSON.stringify(payload)
|
|
3616
|
+
}));
|
|
3617
|
+
if (!response.ok) {
|
|
3618
|
+
const errorText = yield new __await(response.text().catch(() => response.statusText));
|
|
3619
|
+
throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
|
|
3620
|
+
}
|
|
3621
|
+
if (!response.body) {
|
|
3622
|
+
throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
|
|
3623
|
+
}
|
|
3624
|
+
const reader = response.body.getReader();
|
|
3625
|
+
const decoder = new TextDecoder("utf-8");
|
|
3626
|
+
let buffer = "";
|
|
3627
|
+
try {
|
|
3628
|
+
while (true) {
|
|
3629
|
+
const { done, value } = yield new __await(reader.read());
|
|
3630
|
+
if (done) break;
|
|
3341
3631
|
buffer += decoder.decode(value, { stream: true });
|
|
3342
3632
|
const lines = buffer.split("\n");
|
|
3343
3633
|
buffer = (_c = lines.pop()) != null ? _c : "";
|
|
@@ -3368,8 +3658,8 @@ ${context != null ? context : "None"}` },
|
|
|
3368
3658
|
});
|
|
3369
3659
|
}
|
|
3370
3660
|
async embed(text) {
|
|
3371
|
-
var
|
|
3372
|
-
const
|
|
3661
|
+
var _a2, _b;
|
|
3662
|
+
const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
|
|
3373
3663
|
let payload;
|
|
3374
3664
|
if (this.opts.embedPayloadTemplate) {
|
|
3375
3665
|
payload = buildPayload(this.opts.embedPayloadTemplate, {
|
|
@@ -3382,7 +3672,7 @@ ${context != null ? context : "None"}` },
|
|
|
3382
3672
|
input: text
|
|
3383
3673
|
};
|
|
3384
3674
|
}
|
|
3385
|
-
const { data } = await this.http.post(
|
|
3675
|
+
const { data } = await this.http.post(path2, payload);
|
|
3386
3676
|
const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
|
|
3387
3677
|
const vector = resolvePath(data, extractPath);
|
|
3388
3678
|
if (!Array.isArray(vector)) {
|
|
@@ -3410,6 +3700,78 @@ ${context != null ? context : "None"}` },
|
|
|
3410
3700
|
}
|
|
3411
3701
|
};
|
|
3412
3702
|
|
|
3703
|
+
// src/exceptions/index.ts
|
|
3704
|
+
var RetrivoraError = class extends Error {
|
|
3705
|
+
constructor(message, code, details) {
|
|
3706
|
+
super(message);
|
|
3707
|
+
this.name = new.target.name;
|
|
3708
|
+
this.code = code;
|
|
3709
|
+
this.details = details;
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3712
|
+
var ProviderNotFoundException = class extends RetrivoraError {
|
|
3713
|
+
constructor(providerType, provider, details) {
|
|
3714
|
+
super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
|
|
3715
|
+
}
|
|
3716
|
+
};
|
|
3717
|
+
var EmbeddingFailedException = class extends RetrivoraError {
|
|
3718
|
+
constructor(message = "Embedding generation failed", details) {
|
|
3719
|
+
super(message, "EMBEDDING_FAILED", details);
|
|
3720
|
+
}
|
|
3721
|
+
};
|
|
3722
|
+
var RetrievalException = class extends RetrivoraError {
|
|
3723
|
+
constructor(message = "Retrieval failed", details) {
|
|
3724
|
+
super(message, "RETRIEVAL_FAILED", details);
|
|
3725
|
+
}
|
|
3726
|
+
};
|
|
3727
|
+
var RateLimitException = class extends RetrivoraError {
|
|
3728
|
+
constructor(message = "Provider rate limit exceeded", details) {
|
|
3729
|
+
super(message, "RATE_LIMITED", details);
|
|
3730
|
+
}
|
|
3731
|
+
};
|
|
3732
|
+
var ConfigurationException = class extends RetrivoraError {
|
|
3733
|
+
constructor(message, details) {
|
|
3734
|
+
super(message, "CONFIGURATION_ERROR", details);
|
|
3735
|
+
}
|
|
3736
|
+
};
|
|
3737
|
+
var AuthenticationException = class extends RetrivoraError {
|
|
3738
|
+
constructor(message = "Provider authentication failed", details) {
|
|
3739
|
+
super(message, "AUTHENTICATION_ERROR", details);
|
|
3740
|
+
}
|
|
3741
|
+
};
|
|
3742
|
+
function wrapError(err, defaultCode, defaultMessage) {
|
|
3743
|
+
var _a2;
|
|
3744
|
+
if (err instanceof RetrivoraError) {
|
|
3745
|
+
return err;
|
|
3746
|
+
}
|
|
3747
|
+
const error = err;
|
|
3748
|
+
const message = (error == null ? void 0 : error.message) || defaultMessage || String(err);
|
|
3749
|
+
const status = (error == null ? void 0 : error.status) || (error == null ? void 0 : error.statusCode) || ((_a2 = error == null ? void 0 : error.response) == null ? void 0 : _a2.status);
|
|
3750
|
+
const code = error == null ? void 0 : error.code;
|
|
3751
|
+
if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
|
|
3752
|
+
return new RateLimitException(message, err);
|
|
3753
|
+
}
|
|
3754
|
+
if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
|
|
3755
|
+
return new AuthenticationException(message, err);
|
|
3756
|
+
}
|
|
3757
|
+
switch (defaultCode) {
|
|
3758
|
+
case "PROVIDER_NOT_FOUND":
|
|
3759
|
+
return new ProviderNotFoundException("provider", message, err);
|
|
3760
|
+
case "EMBEDDING_FAILED":
|
|
3761
|
+
return new EmbeddingFailedException(message, err);
|
|
3762
|
+
case "RETRIEVAL_FAILED":
|
|
3763
|
+
return new RetrievalException(message, err);
|
|
3764
|
+
case "RATE_LIMITED":
|
|
3765
|
+
return new RateLimitException(message, err);
|
|
3766
|
+
case "CONFIGURATION_ERROR":
|
|
3767
|
+
return new ConfigurationException(message, err);
|
|
3768
|
+
case "AUTHENTICATION_ERROR":
|
|
3769
|
+
return new AuthenticationException(message, err);
|
|
3770
|
+
default:
|
|
3771
|
+
return new RetrivoraError(message, defaultCode, err);
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3413
3775
|
// src/llm/LLMFactory.ts
|
|
3414
3776
|
var customProviders = /* @__PURE__ */ new Map();
|
|
3415
3777
|
var LLMFactory = class _LLMFactory {
|
|
@@ -3448,6 +3810,8 @@ var LLMFactory = class _LLMFactory {
|
|
|
3448
3810
|
"anthropic",
|
|
3449
3811
|
"ollama",
|
|
3450
3812
|
"gemini",
|
|
3813
|
+
"groq",
|
|
3814
|
+
"qwen",
|
|
3451
3815
|
"rest",
|
|
3452
3816
|
"universal_rest",
|
|
3453
3817
|
"custom",
|
|
@@ -3455,7 +3819,7 @@ var LLMFactory = class _LLMFactory {
|
|
|
3455
3819
|
];
|
|
3456
3820
|
}
|
|
3457
3821
|
static create(llmConfig, embeddingConfig) {
|
|
3458
|
-
var
|
|
3822
|
+
var _a2, _b, _c;
|
|
3459
3823
|
switch (llmConfig.provider) {
|
|
3460
3824
|
case "openai":
|
|
3461
3825
|
return new OpenAIProvider(llmConfig, embeddingConfig);
|
|
@@ -3465,12 +3829,16 @@ var LLMFactory = class _LLMFactory {
|
|
|
3465
3829
|
return new OllamaProvider(llmConfig, embeddingConfig);
|
|
3466
3830
|
case "gemini":
|
|
3467
3831
|
return new GeminiProvider(llmConfig, embeddingConfig);
|
|
3832
|
+
case "groq":
|
|
3833
|
+
return new GroqProvider(llmConfig, embeddingConfig);
|
|
3834
|
+
case "qwen":
|
|
3835
|
+
return new QwenProvider(llmConfig, embeddingConfig);
|
|
3468
3836
|
case "rest":
|
|
3469
3837
|
case "universal_rest":
|
|
3470
3838
|
case "custom":
|
|
3471
3839
|
return new UniversalLLMAdapter(llmConfig);
|
|
3472
3840
|
default: {
|
|
3473
|
-
const providerName = String((
|
|
3841
|
+
const providerName = String((_a2 = llmConfig.provider) != null ? _a2 : "").toLowerCase();
|
|
3474
3842
|
const customFactory = customProviders.get(providerName);
|
|
3475
3843
|
if (customFactory) {
|
|
3476
3844
|
return customFactory(llmConfig);
|
|
@@ -3479,9 +3847,12 @@ var LLMFactory = class _LLMFactory {
|
|
|
3479
3847
|
return new UniversalLLMAdapter(llmConfig);
|
|
3480
3848
|
}
|
|
3481
3849
|
throw new ProviderNotFoundException(
|
|
3482
|
-
"
|
|
3483
|
-
|
|
3484
|
-
|
|
3850
|
+
"llm",
|
|
3851
|
+
(_c = llmConfig.provider) != null ? _c : "undefined",
|
|
3852
|
+
{
|
|
3853
|
+
message: `Unknown provider "${llmConfig.provider}". Register a custom provider with LLMFactory.register().`,
|
|
3854
|
+
available: _LLMFactory.listProviders()
|
|
3855
|
+
}
|
|
3485
3856
|
);
|
|
3486
3857
|
}
|
|
3487
3858
|
}
|
|
@@ -3504,6 +3875,10 @@ var LLMFactory = class _LLMFactory {
|
|
|
3504
3875
|
return OllamaProvider;
|
|
3505
3876
|
case "gemini":
|
|
3506
3877
|
return GeminiProvider;
|
|
3878
|
+
case "groq":
|
|
3879
|
+
return GroqProvider;
|
|
3880
|
+
case "qwen":
|
|
3881
|
+
return QwenProvider;
|
|
3507
3882
|
case "rest":
|
|
3508
3883
|
case "universal_rest":
|
|
3509
3884
|
case "custom":
|
|
@@ -3565,7 +3940,7 @@ var ProviderRegistry = class {
|
|
|
3565
3940
|
return null;
|
|
3566
3941
|
}
|
|
3567
3942
|
static async loadVectorProviderClass(provider) {
|
|
3568
|
-
var
|
|
3943
|
+
var _a2;
|
|
3569
3944
|
if (this.vectorProviders[provider]) return this.vectorProviders[provider];
|
|
3570
3945
|
switch (provider) {
|
|
3571
3946
|
case "pinecone": {
|
|
@@ -3574,7 +3949,7 @@ var ProviderRegistry = class {
|
|
|
3574
3949
|
}
|
|
3575
3950
|
case "pgvector":
|
|
3576
3951
|
case "postgresql": {
|
|
3577
|
-
const postgresMode = ((
|
|
3952
|
+
const postgresMode = ((_a2 = process.env.POSTGRES_MODE) != null ? _a2 : "multi").toLowerCase();
|
|
3578
3953
|
if (postgresMode === "single") {
|
|
3579
3954
|
const { PostgreSQLProvider: PostgreSQLProvider2 } = await Promise.resolve().then(() => (init_PostgreSQLProvider(), PostgreSQLProvider_exports));
|
|
3580
3955
|
return PostgreSQLProvider2;
|
|
@@ -4023,11 +4398,11 @@ var LlamaIndexIngestor = class {
|
|
|
4023
4398
|
* than standard character-count splitting.
|
|
4024
4399
|
*/
|
|
4025
4400
|
async chunk(text, options = {}) {
|
|
4026
|
-
var
|
|
4401
|
+
var _a2, _b;
|
|
4027
4402
|
try {
|
|
4028
4403
|
const { Document, SentenceSplitter, MetadataMode } = await import(`${"llamaindex"}`);
|
|
4029
4404
|
const splitter = new SentenceSplitter({
|
|
4030
|
-
chunkSize: (
|
|
4405
|
+
chunkSize: (_a2 = options.chunkSize) != null ? _a2 : 1e3,
|
|
4031
4406
|
chunkOverlap: (_b = options.chunkOverlap) != null ? _b : 200
|
|
4032
4407
|
});
|
|
4033
4408
|
const doc = new Document({ text, metadata: options.metadata || {} });
|
|
@@ -4136,7 +4511,7 @@ ${error instanceof Error ? error.message : String(error)}`
|
|
|
4136
4511
|
* The agent returns `{ messages: [...] }` — the last message is the final answer.
|
|
4137
4512
|
*/
|
|
4138
4513
|
async run(input, chatHistory = []) {
|
|
4139
|
-
var
|
|
4514
|
+
var _a2, _b, _c;
|
|
4140
4515
|
if (!this.agent) {
|
|
4141
4516
|
throw new Error("[LangChainAgent] Agent not initialized. Call initialize() first.");
|
|
4142
4517
|
}
|
|
@@ -4147,7 +4522,7 @@ ${error instanceof Error ? error.message : String(error)}`
|
|
|
4147
4522
|
const response = await this.agent.invoke({
|
|
4148
4523
|
messages: [...historyMessages, new HumanMessage(input)]
|
|
4149
4524
|
});
|
|
4150
|
-
const lastMessage = (
|
|
4525
|
+
const lastMessage = (_a2 = response == null ? void 0 : response.messages) == null ? void 0 : _a2.at(-1);
|
|
4151
4526
|
if (lastMessage && typeof lastMessage.content === "string") {
|
|
4152
4527
|
return lastMessage.content;
|
|
4153
4528
|
}
|
|
@@ -4158,6 +4533,437 @@ ${error instanceof Error ? error.message : String(error)}`
|
|
|
4158
4533
|
}
|
|
4159
4534
|
};
|
|
4160
4535
|
|
|
4536
|
+
// src/core/mcp.ts
|
|
4537
|
+
import { spawn } from "child_process";
|
|
4538
|
+
var MCPClient = class {
|
|
4539
|
+
constructor(config) {
|
|
4540
|
+
this.config = config;
|
|
4541
|
+
this.messageIdCounter = 0;
|
|
4542
|
+
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
4543
|
+
this.stdioBuffer = "";
|
|
4544
|
+
this.initialized = false;
|
|
4545
|
+
}
|
|
4546
|
+
getNextMessageId() {
|
|
4547
|
+
return ++this.messageIdCounter;
|
|
4548
|
+
}
|
|
4549
|
+
/**
|
|
4550
|
+
* Connect and initialize the MCP server connection.
|
|
4551
|
+
*/
|
|
4552
|
+
async connect() {
|
|
4553
|
+
if (this.initialized) return;
|
|
4554
|
+
if (this.config.transport === "stdio") {
|
|
4555
|
+
await this.connectStdio();
|
|
4556
|
+
} else {
|
|
4557
|
+
await this.connectSSE();
|
|
4558
|
+
}
|
|
4559
|
+
try {
|
|
4560
|
+
const response = await this.sendJsonRpc("initialize", {
|
|
4561
|
+
protocolVersion: "2024-11-05",
|
|
4562
|
+
capabilities: {},
|
|
4563
|
+
clientInfo: {
|
|
4564
|
+
name: "retrivora-sdk",
|
|
4565
|
+
version: "1.0.0"
|
|
4566
|
+
}
|
|
4567
|
+
});
|
|
4568
|
+
await this.sendNotification("notifications/initialized");
|
|
4569
|
+
this.initialized = true;
|
|
4570
|
+
console.log(`[MCPClient] Connected and initialized server "${this.config.name}" successfully.`);
|
|
4571
|
+
} catch (err) {
|
|
4572
|
+
this.disconnect();
|
|
4573
|
+
throw new Error(`[MCPClient] Failed to initialize server "${this.config.name}": ${err.message}`);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
async connectStdio() {
|
|
4577
|
+
var _a2;
|
|
4578
|
+
const cmd = this.config.command;
|
|
4579
|
+
if (!cmd) {
|
|
4580
|
+
throw new Error(`[MCPClient] Command option is required for stdio transport on "${this.config.name}"`);
|
|
4581
|
+
}
|
|
4582
|
+
const args = this.config.args || [];
|
|
4583
|
+
const env = __spreadValues(__spreadValues({}, process.env), this.config.env || {});
|
|
4584
|
+
this.childProcess = spawn(cmd, args, { env, shell: true });
|
|
4585
|
+
if (!this.childProcess || !this.childProcess.stdout || !this.childProcess.stdin) {
|
|
4586
|
+
throw new Error(`[MCPClient] Failed to spawn stdio child process for "${this.config.name}"`);
|
|
4587
|
+
}
|
|
4588
|
+
this.childProcess.stdout.on("data", (data) => {
|
|
4589
|
+
this.stdioBuffer += data.toString("utf-8");
|
|
4590
|
+
this.processStdioLines();
|
|
4591
|
+
});
|
|
4592
|
+
(_a2 = this.childProcess.stderr) == null ? void 0 : _a2.on("data", (data) => {
|
|
4593
|
+
console.warn(`[MCPClient] [stderr] [${this.config.name}]:`, data.toString("utf-8"));
|
|
4594
|
+
});
|
|
4595
|
+
this.childProcess.on("close", (code) => {
|
|
4596
|
+
console.log(`[MCPClient] Connection closed for "${this.config.name}" with exit code: ${code}`);
|
|
4597
|
+
this.disconnect();
|
|
4598
|
+
});
|
|
4599
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4600
|
+
}
|
|
4601
|
+
processStdioLines() {
|
|
4602
|
+
let newlineIndex;
|
|
4603
|
+
while ((newlineIndex = this.stdioBuffer.indexOf("\n")) !== -1) {
|
|
4604
|
+
const line = this.stdioBuffer.substring(0, newlineIndex).trim();
|
|
4605
|
+
this.stdioBuffer = this.stdioBuffer.substring(newlineIndex + 1);
|
|
4606
|
+
if (!line) continue;
|
|
4607
|
+
try {
|
|
4608
|
+
const payload = JSON.parse(line);
|
|
4609
|
+
if (payload.id !== void 0) {
|
|
4610
|
+
const req = this.pendingRequests.get(Number(payload.id));
|
|
4611
|
+
if (req) {
|
|
4612
|
+
this.pendingRequests.delete(Number(payload.id));
|
|
4613
|
+
if (payload.error) {
|
|
4614
|
+
req.reject(new Error(payload.error.message || JSON.stringify(payload.error)));
|
|
4615
|
+
} else {
|
|
4616
|
+
req.resolve(payload.result);
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
} catch (e) {
|
|
4621
|
+
console.warn(`[MCPClient] Error parsing stdio JSON-RPC line from "${this.config.name}":`, e, "Line content:", line);
|
|
4622
|
+
}
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
async connectSSE() {
|
|
4626
|
+
if (!this.config.url) {
|
|
4627
|
+
throw new Error(`[MCPClient] URL is required for sse transport on "${this.config.name}"`);
|
|
4628
|
+
}
|
|
4629
|
+
this.sseUrl = this.config.url;
|
|
4630
|
+
}
|
|
4631
|
+
async sendJsonRpc(method, params) {
|
|
4632
|
+
const id = this.getNextMessageId();
|
|
4633
|
+
const request = {
|
|
4634
|
+
jsonrpc: "2.0",
|
|
4635
|
+
id,
|
|
4636
|
+
method,
|
|
4637
|
+
params
|
|
4638
|
+
};
|
|
4639
|
+
if (this.config.transport === "stdio") {
|
|
4640
|
+
if (!this.childProcess || !this.childProcess.stdin) {
|
|
4641
|
+
throw new Error(`[MCPClient] Server "${this.config.name}" is not connected (stdio process missing)`);
|
|
4642
|
+
}
|
|
4643
|
+
return new Promise((resolve, reject) => {
|
|
4644
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
4645
|
+
this.childProcess.stdin.write(JSON.stringify(request) + "\n", "utf-8");
|
|
4646
|
+
});
|
|
4647
|
+
} else {
|
|
4648
|
+
if (!this.sseUrl) {
|
|
4649
|
+
throw new Error(`[MCPClient] Server "${this.config.name}" SSE URL is not initialized`);
|
|
4650
|
+
}
|
|
4651
|
+
const response = await fetch(this.sseUrl, {
|
|
4652
|
+
method: "POST",
|
|
4653
|
+
headers: { "Content-Type": "application/json" },
|
|
4654
|
+
body: JSON.stringify(request)
|
|
4655
|
+
});
|
|
4656
|
+
if (!response.ok) {
|
|
4657
|
+
throw new Error(`SSE JSON-RPC POST failed with status ${response.status}`);
|
|
4658
|
+
}
|
|
4659
|
+
const payload = await response.json();
|
|
4660
|
+
if (payload.error) {
|
|
4661
|
+
throw new Error(payload.error.message || JSON.stringify(payload.error));
|
|
4662
|
+
}
|
|
4663
|
+
return payload.result;
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
async sendNotification(method, params) {
|
|
4667
|
+
var _a2;
|
|
4668
|
+
const notification = {
|
|
4669
|
+
jsonrpc: "2.0",
|
|
4670
|
+
method,
|
|
4671
|
+
params
|
|
4672
|
+
};
|
|
4673
|
+
if (this.config.transport === "stdio") {
|
|
4674
|
+
if ((_a2 = this.childProcess) == null ? void 0 : _a2.stdin) {
|
|
4675
|
+
this.childProcess.stdin.write(JSON.stringify(notification) + "\n", "utf-8");
|
|
4676
|
+
}
|
|
4677
|
+
} else if (this.sseUrl) {
|
|
4678
|
+
await fetch(this.sseUrl, {
|
|
4679
|
+
method: "POST",
|
|
4680
|
+
headers: { "Content-Type": "application/json" },
|
|
4681
|
+
body: JSON.stringify(notification)
|
|
4682
|
+
}).catch((err) => console.warn("[MCPClient] Failed to send notification over SSE:", err));
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
/**
|
|
4686
|
+
* List all tools offered by the MCP server.
|
|
4687
|
+
*/
|
|
4688
|
+
async listTools() {
|
|
4689
|
+
await this.connect();
|
|
4690
|
+
const result = await this.sendJsonRpc("tools/list", {});
|
|
4691
|
+
return (result == null ? void 0 : result.tools) || [];
|
|
4692
|
+
}
|
|
4693
|
+
/**
|
|
4694
|
+
* Execute a tool on the MCP server.
|
|
4695
|
+
*/
|
|
4696
|
+
async callTool(name, args = {}) {
|
|
4697
|
+
await this.connect();
|
|
4698
|
+
return await this.sendJsonRpc("tools/call", { name, arguments: args });
|
|
4699
|
+
}
|
|
4700
|
+
disconnect() {
|
|
4701
|
+
this.initialized = false;
|
|
4702
|
+
this.pendingRequests.forEach((req) => req.reject(new Error("MCPClient disconnected")));
|
|
4703
|
+
this.pendingRequests.clear();
|
|
4704
|
+
if (this.childProcess) {
|
|
4705
|
+
try {
|
|
4706
|
+
this.childProcess.kill();
|
|
4707
|
+
} catch (e) {
|
|
4708
|
+
}
|
|
4709
|
+
this.childProcess = void 0;
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
};
|
|
4713
|
+
var MCPRegistry = class {
|
|
4714
|
+
constructor(servers = []) {
|
|
4715
|
+
this.clients = [];
|
|
4716
|
+
this.clients = servers.map((cfg) => new MCPClient(cfg));
|
|
4717
|
+
}
|
|
4718
|
+
async getAllTools() {
|
|
4719
|
+
const list = [];
|
|
4720
|
+
await Promise.all(
|
|
4721
|
+
this.clients.map(async (client) => {
|
|
4722
|
+
try {
|
|
4723
|
+
const tools = await client.listTools();
|
|
4724
|
+
tools.forEach((t) => list.push({ serverName: client["config"].name, tool: t }));
|
|
4725
|
+
} catch (e) {
|
|
4726
|
+
console.warn(`[MCPRegistry] Failed to fetch tools from server "${client["config"].name}":`, e);
|
|
4727
|
+
}
|
|
4728
|
+
})
|
|
4729
|
+
);
|
|
4730
|
+
return list;
|
|
4731
|
+
}
|
|
4732
|
+
async callTool(toolName, args = {}) {
|
|
4733
|
+
for (const client of this.clients) {
|
|
4734
|
+
try {
|
|
4735
|
+
const tools = await client.listTools();
|
|
4736
|
+
if (tools.some((t) => t.name === toolName)) {
|
|
4737
|
+
return await client.callTool(toolName, args);
|
|
4738
|
+
}
|
|
4739
|
+
} catch (e) {
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
throw new Error(`[MCPRegistry] Tool "${toolName}" not found on any registered MCP server.`);
|
|
4743
|
+
}
|
|
4744
|
+
disconnectAll() {
|
|
4745
|
+
this.clients.forEach((c) => c.disconnect());
|
|
4746
|
+
}
|
|
4747
|
+
};
|
|
4748
|
+
|
|
4749
|
+
// src/core/MultiAgentCoordinator.ts
|
|
4750
|
+
var MultiAgentCoordinator = class {
|
|
4751
|
+
constructor(options) {
|
|
4752
|
+
var _a2;
|
|
4753
|
+
this.llmProvider = options.llmProvider;
|
|
4754
|
+
this.mcpRegistry = options.mcpRegistry;
|
|
4755
|
+
this.documentSearch = options.documentSearch;
|
|
4756
|
+
this.maxIterations = (_a2 = options.maxIterations) != null ? _a2 : 5;
|
|
4757
|
+
}
|
|
4758
|
+
/**
|
|
4759
|
+
* Run the multi-agent coordination loop synchronously and return the final response.
|
|
4760
|
+
*/
|
|
4761
|
+
async run(question, history = []) {
|
|
4762
|
+
const generator = this.runStream(question, history);
|
|
4763
|
+
let finalReply = "";
|
|
4764
|
+
const sources = [];
|
|
4765
|
+
try {
|
|
4766
|
+
for (var iter = __forAwait(generator), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
4767
|
+
const chunk = temp.value;
|
|
4768
|
+
if (typeof chunk === "string") {
|
|
4769
|
+
finalReply += chunk;
|
|
4770
|
+
} else if (chunk && typeof chunk === "object") {
|
|
4771
|
+
if ("reply" in chunk && chunk.reply) {
|
|
4772
|
+
finalReply += chunk.reply;
|
|
4773
|
+
}
|
|
4774
|
+
if ("sources" in chunk && chunk.sources) {
|
|
4775
|
+
sources.push(...chunk.sources);
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
} catch (temp) {
|
|
4780
|
+
error = [temp];
|
|
4781
|
+
} finally {
|
|
4782
|
+
try {
|
|
4783
|
+
more && (temp = iter.return) && await temp.call(iter);
|
|
4784
|
+
} finally {
|
|
4785
|
+
if (error)
|
|
4786
|
+
throw error[0];
|
|
4787
|
+
}
|
|
4788
|
+
}
|
|
4789
|
+
return {
|
|
4790
|
+
reply: finalReply,
|
|
4791
|
+
sources
|
|
4792
|
+
};
|
|
4793
|
+
}
|
|
4794
|
+
/**
|
|
4795
|
+
* Run the multi-agent coordination loop as an async stream, yielding intermediate thought blocks and tool execution events.
|
|
4796
|
+
*/
|
|
4797
|
+
runStream(_0) {
|
|
4798
|
+
return __asyncGenerator(this, arguments, function* (question, history = []) {
|
|
4799
|
+
const mcpTools = yield new __await(this.mcpRegistry.getAllTools());
|
|
4800
|
+
let systemPrompt = `You are a goal-driven supervisor agent coordinating a RAG search engine and external MCP servers to solve the user's task.
|
|
4801
|
+
You must think step-by-step before acting. Write your internal thinking process inside a \`<think>...</think>\` block first, then act or answer.
|
|
4802
|
+
|
|
4803
|
+
Available Tools:
|
|
4804
|
+
- document_search(query: string): Search the vector database and knowledge base for documents.
|
|
4805
|
+
|
|
4806
|
+
${mcpTools.length > 0 ? "MCP Server Tools:" : ""}
|
|
4807
|
+
${mcpTools.map((mt) => {
|
|
4808
|
+
var _a2;
|
|
4809
|
+
return `- ${mt.tool.name}(${JSON.stringify(((_a2 = mt.tool.inputSchema) == null ? void 0 : _a2.properties) || {})}): ${mt.tool.description || "No description provided."}`;
|
|
4810
|
+
}).join("\n")}
|
|
4811
|
+
|
|
4812
|
+
Tool Calling Protocol:
|
|
4813
|
+
If you need to call a tool, write:
|
|
4814
|
+
TOOL_CALL: <toolName>({ "argName": "value" })
|
|
4815
|
+
And then STOP generating immediately.
|
|
4816
|
+
|
|
4817
|
+
Once you have gathered enough information to fully satisfy the user's query, write your final response directly after the closing \`</think>\` block.
|
|
4818
|
+
`;
|
|
4819
|
+
const supervisorHistory = [
|
|
4820
|
+
{ role: "system", content: systemPrompt },
|
|
4821
|
+
...history,
|
|
4822
|
+
{ role: "user", content: question }
|
|
4823
|
+
];
|
|
4824
|
+
let iteration = 0;
|
|
4825
|
+
const allSources = [];
|
|
4826
|
+
while (iteration < this.maxIterations) {
|
|
4827
|
+
iteration++;
|
|
4828
|
+
const context = "";
|
|
4829
|
+
let fullModelResponse = "";
|
|
4830
|
+
let textBuffer = "";
|
|
4831
|
+
let inThinking = false;
|
|
4832
|
+
let toolCallDetected = null;
|
|
4833
|
+
if (!this.llmProvider.chatStream) {
|
|
4834
|
+
const reply = yield new __await(this.llmProvider.chat(supervisorHistory, context));
|
|
4835
|
+
fullModelResponse = reply;
|
|
4836
|
+
const thinkIndex = reply.indexOf("<think>");
|
|
4837
|
+
const endThinkIndex = reply.indexOf("</think>");
|
|
4838
|
+
let thinkingText = "";
|
|
4839
|
+
let answerText = reply;
|
|
4840
|
+
if (thinkIndex !== -1 && endThinkIndex !== -1) {
|
|
4841
|
+
thinkingText = reply.substring(thinkIndex + 7, endThinkIndex);
|
|
4842
|
+
answerText = reply.substring(endThinkIndex + 8);
|
|
4843
|
+
yield { type: "thinking", text: thinkingText };
|
|
4844
|
+
}
|
|
4845
|
+
const toolMatch = answerText.match(/TOOL_CALL:\s*(\w+)\s*\(([\s\S]*)\)/);
|
|
4846
|
+
if (toolMatch) {
|
|
4847
|
+
const toolName = toolMatch[1];
|
|
4848
|
+
const toolArgsStr = toolMatch[2];
|
|
4849
|
+
try {
|
|
4850
|
+
const toolArgs = JSON.parse(toolArgsStr.trim());
|
|
4851
|
+
toolCallDetected = { name: toolName, args: toolArgs };
|
|
4852
|
+
} catch (e) {
|
|
4853
|
+
console.error("[MultiAgentCoordinator] Failed to parse tool args:", toolArgsStr, e);
|
|
4854
|
+
}
|
|
4855
|
+
} else {
|
|
4856
|
+
yield answerText;
|
|
4857
|
+
}
|
|
4858
|
+
} else {
|
|
4859
|
+
const stream = this.llmProvider.chatStream(supervisorHistory, context);
|
|
4860
|
+
try {
|
|
4861
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
4862
|
+
const chunk = temp.value;
|
|
4863
|
+
fullModelResponse += chunk;
|
|
4864
|
+
textBuffer += chunk;
|
|
4865
|
+
if (!inThinking && textBuffer.includes("<think>")) {
|
|
4866
|
+
inThinking = true;
|
|
4867
|
+
const idx = textBuffer.indexOf("<think>");
|
|
4868
|
+
textBuffer = textBuffer.slice(idx + 7);
|
|
4869
|
+
}
|
|
4870
|
+
if (inThinking && textBuffer.includes("</think>")) {
|
|
4871
|
+
inThinking = false;
|
|
4872
|
+
const idx = textBuffer.indexOf("</think>");
|
|
4873
|
+
const thinkingDelta = textBuffer.slice(0, idx);
|
|
4874
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
4875
|
+
textBuffer = textBuffer.slice(idx + 8);
|
|
4876
|
+
}
|
|
4877
|
+
if (inThinking && textBuffer.length > 0) {
|
|
4878
|
+
yield { type: "thinking", text: textBuffer };
|
|
4879
|
+
textBuffer = "";
|
|
4880
|
+
}
|
|
4881
|
+
if (!inThinking && textBuffer.includes("TOOL_CALL:")) {
|
|
4882
|
+
const idx = textBuffer.indexOf("TOOL_CALL:");
|
|
4883
|
+
const precedingText = textBuffer.slice(0, idx);
|
|
4884
|
+
if (precedingText.trim()) {
|
|
4885
|
+
yield precedingText;
|
|
4886
|
+
}
|
|
4887
|
+
textBuffer = textBuffer.slice(idx);
|
|
4888
|
+
}
|
|
4889
|
+
if (!inThinking && !textBuffer.includes("TOOL_CALL:") && textBuffer.length > 0) {
|
|
4890
|
+
yield textBuffer;
|
|
4891
|
+
textBuffer = "";
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
} catch (temp) {
|
|
4895
|
+
error = [temp];
|
|
4896
|
+
} finally {
|
|
4897
|
+
try {
|
|
4898
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
4899
|
+
} finally {
|
|
4900
|
+
if (error)
|
|
4901
|
+
throw error[0];
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4904
|
+
if (textBuffer.trim()) {
|
|
4905
|
+
const toolMatch = textBuffer.match(/TOOL_CALL:\s*(\w+)\s*\(([\s\S]*)\)/);
|
|
4906
|
+
if (toolMatch) {
|
|
4907
|
+
const toolName = toolMatch[1];
|
|
4908
|
+
const toolArgsStr = toolMatch[2];
|
|
4909
|
+
try {
|
|
4910
|
+
const toolArgs = JSON.parse(toolArgsStr.trim());
|
|
4911
|
+
toolCallDetected = { name: toolName, args: toolArgs };
|
|
4912
|
+
} catch (e) {
|
|
4913
|
+
console.error("[MultiAgentCoordinator] Failed to parse tool args:", toolArgsStr, e);
|
|
4914
|
+
}
|
|
4915
|
+
} else {
|
|
4916
|
+
yield textBuffer;
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
}
|
|
4920
|
+
supervisorHistory.push({ role: "assistant", content: fullModelResponse });
|
|
4921
|
+
if (toolCallDetected) {
|
|
4922
|
+
const { name: toolName, args: toolArgs } = toolCallDetected;
|
|
4923
|
+
yield {
|
|
4924
|
+
type: "thinking",
|
|
4925
|
+
text: `
|
|
4926
|
+
[Agent Call] Executing tool "${toolName}" with parameters ${JSON.stringify(toolArgs)}...
|
|
4927
|
+
`
|
|
4928
|
+
};
|
|
4929
|
+
let toolResultText = "";
|
|
4930
|
+
try {
|
|
4931
|
+
if (toolName === "document_search") {
|
|
4932
|
+
const searchRes = yield new __await(this.documentSearch(toolArgs.query || ""));
|
|
4933
|
+
toolResultText = `document_search returned:
|
|
4934
|
+
${searchRes.reply}
|
|
4935
|
+
|
|
4936
|
+
Sources count: ${searchRes.sources.length}`;
|
|
4937
|
+
if (searchRes.sources && searchRes.sources.length > 0) {
|
|
4938
|
+
allSources.push(...searchRes.sources);
|
|
4939
|
+
yield { reply: "", sources: searchRes.sources };
|
|
4940
|
+
}
|
|
4941
|
+
} else {
|
|
4942
|
+
const mcpRes = yield new __await(this.mcpRegistry.callTool(toolName, toolArgs));
|
|
4943
|
+
toolResultText = `MCP Tool "${toolName}" returned:
|
|
4944
|
+
${JSON.stringify(mcpRes.content)}`;
|
|
4945
|
+
}
|
|
4946
|
+
} catch (err) {
|
|
4947
|
+
toolResultText = `Error calling tool "${toolName}": ${err.message}`;
|
|
4948
|
+
}
|
|
4949
|
+
supervisorHistory.push({
|
|
4950
|
+
role: "user",
|
|
4951
|
+
content: `TOOL_RESULT for ${toolName}:
|
|
4952
|
+
${toolResultText}`
|
|
4953
|
+
});
|
|
4954
|
+
yield {
|
|
4955
|
+
type: "thinking",
|
|
4956
|
+
text: `[Agent Output] Tool "${toolName}" executed. Continuing reasoning...
|
|
4957
|
+
`
|
|
4958
|
+
};
|
|
4959
|
+
} else {
|
|
4960
|
+
break;
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
});
|
|
4964
|
+
}
|
|
4965
|
+
};
|
|
4966
|
+
|
|
4161
4967
|
// src/core/BatchProcessor.ts
|
|
4162
4968
|
function isTransientError(error) {
|
|
4163
4969
|
if (!(error instanceof Error)) return false;
|
|
@@ -4478,7 +5284,7 @@ var QueryProcessor = class {
|
|
|
4478
5284
|
* @param validFields Optional list of known filterable fields to look for
|
|
4479
5285
|
*/
|
|
4480
5286
|
static extractQueryFieldHints(question, validFields = []) {
|
|
4481
|
-
var
|
|
5287
|
+
var _a2, _b, _c, _d;
|
|
4482
5288
|
if (!question.trim()) return [];
|
|
4483
5289
|
const hints = /* @__PURE__ */ new Map();
|
|
4484
5290
|
const addHint = (value, field) => {
|
|
@@ -4558,7 +5364,7 @@ var QueryProcessor = class {
|
|
|
4558
5364
|
];
|
|
4559
5365
|
for (const p of universalPatterns) {
|
|
4560
5366
|
for (const match of question.matchAll(p.regex)) {
|
|
4561
|
-
const val = p.group ? (
|
|
5367
|
+
const val = p.group ? (_a2 = match[p.group]) != null ? _a2 : match[0] : match[0];
|
|
4562
5368
|
if (!val) continue;
|
|
4563
5369
|
if (p.field) addHint(val, p.field);
|
|
4564
5370
|
else addHint(val);
|
|
@@ -4782,11 +5588,11 @@ var LLMRouter = class {
|
|
|
4782
5588
|
* When provided it is used directly as the 'default' role without re-constructing.
|
|
4783
5589
|
*/
|
|
4784
5590
|
async initialize(prebuiltDefault) {
|
|
4785
|
-
var
|
|
5591
|
+
var _a2;
|
|
4786
5592
|
const defaultModel = prebuiltDefault != null ? prebuiltDefault : LLMFactory.create(this.config.llm, this.config.embedding);
|
|
4787
5593
|
this.models.set("default", defaultModel);
|
|
4788
5594
|
const envFastModel = process.env.FAST_LLM_MODEL;
|
|
4789
|
-
const providerFastDefault = (
|
|
5595
|
+
const providerFastDefault = (_a2 = FAST_MODEL_DEFAULTS[this.config.llm.provider]) != null ? _a2 : "";
|
|
4790
5596
|
const fastModelName = envFastModel || providerFastDefault;
|
|
4791
5597
|
if (fastModelName && fastModelName !== this.config.llm.model) {
|
|
4792
5598
|
console.log(`[LLMRouter] Fast role \u2192 provider="${this.config.llm.provider}" model="${fastModelName}"`);
|
|
@@ -4805,8 +5611,8 @@ var LLMRouter = class {
|
|
|
4805
5611
|
* Falls back to 'default' if the requested role is not registered.
|
|
4806
5612
|
*/
|
|
4807
5613
|
get(role) {
|
|
4808
|
-
var
|
|
4809
|
-
const provider = (
|
|
5614
|
+
var _a2;
|
|
5615
|
+
const provider = (_a2 = this.models.get(role)) != null ? _a2 : this.models.get("default");
|
|
4810
5616
|
if (!provider) {
|
|
4811
5617
|
throw new Error(`[LLMRouter] No provider registered for role: "${role}". Did you call initialize()?`);
|
|
4812
5618
|
}
|
|
@@ -4824,7 +5630,7 @@ var UITransformer = class _UITransformer {
|
|
|
4824
5630
|
* Prefer `analyzeAndDecide()` in production.
|
|
4825
5631
|
*/
|
|
4826
5632
|
static transform(userQuery, retrievedData, config, trainedSchema, intent) {
|
|
4827
|
-
var
|
|
5633
|
+
var _a2, _b, _c;
|
|
4828
5634
|
if (!retrievedData || retrievedData.length === 0) {
|
|
4829
5635
|
return this.createTextResponse("No data available", "No relevant data found for your query.");
|
|
4830
5636
|
}
|
|
@@ -4844,7 +5650,7 @@ var UITransformer = class _UITransformer {
|
|
|
4844
5650
|
return this.transformToBarChart(filteredData, profile, userQuery, resolvedIntent.visualizationHint === "ranking");
|
|
4845
5651
|
}
|
|
4846
5652
|
if (resolvedIntent.visualizationHint === "distribution") {
|
|
4847
|
-
return (
|
|
5653
|
+
return (_a2 = this.transformToHistogram(profile, userQuery)) != null ? _a2 : this.transformToBarChart(filteredData, profile, userQuery);
|
|
4848
5654
|
}
|
|
4849
5655
|
if (resolvedIntent.visualizationHint === "correlation") {
|
|
4850
5656
|
return (_b = this.transformToScatterPlot(profile, userQuery)) != null ? _b : this.transformToBarChart(filteredData, profile, userQuery);
|
|
@@ -5169,11 +5975,11 @@ ${schemaProfileText}` : ""}`;
|
|
|
5169
5975
|
};
|
|
5170
5976
|
}
|
|
5171
5977
|
static transformToPieChart(data, profile, query = "") {
|
|
5172
|
-
var
|
|
5978
|
+
var _a2;
|
|
5173
5979
|
const dimension = profile ? this.selectDimensionField(profile, query) : void 0;
|
|
5174
5980
|
const categories = dimension ? Array.from(new Set(profile.records.map((record) => {
|
|
5175
|
-
var
|
|
5176
|
-
return String((
|
|
5981
|
+
var _a3;
|
|
5982
|
+
return String((_a3 = record.fields[dimension.key]) != null ? _a3 : "");
|
|
5177
5983
|
}).filter(Boolean))) : this.detectCategories(data);
|
|
5178
5984
|
if (categories.length === 0) return null;
|
|
5179
5985
|
const categoryData = dimension && profile ? this.aggregateProfileByDimension(profile, dimension.key) : this.aggregateByCategory(data, categories);
|
|
@@ -5183,7 +5989,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
5183
5989
|
});
|
|
5184
5990
|
return {
|
|
5185
5991
|
type: "pie_chart",
|
|
5186
|
-
title: `Distribution by ${(
|
|
5992
|
+
title: `Distribution by ${(_a2 = dimension == null ? void 0 : dimension.label) != null ? _a2 : "Category"}`,
|
|
5187
5993
|
description: `Showing breakdown across ${pieData.length} categories`,
|
|
5188
5994
|
data: pieData
|
|
5189
5995
|
};
|
|
@@ -5193,8 +5999,8 @@ ${schemaProfileText}` : ""}`;
|
|
|
5193
5999
|
const valueField = profile.numericFields[0];
|
|
5194
6000
|
const buckets = /* @__PURE__ */ new Map();
|
|
5195
6001
|
profile.records.forEach((record) => {
|
|
5196
|
-
var
|
|
5197
|
-
const timestamp = String((
|
|
6002
|
+
var _a2, _b, _c;
|
|
6003
|
+
const timestamp = String((_a2 = record.fields[dateField.key]) != null ? _a2 : "");
|
|
5198
6004
|
const value = (_b = this.toFiniteNumber(record.fields[valueField.key])) != null ? _b : 0;
|
|
5199
6005
|
buckets.set(timestamp, ((_c = buckets.get(timestamp)) != null ? _c : 0) + value);
|
|
5200
6006
|
});
|
|
@@ -5207,23 +6013,23 @@ ${schemaProfileText}` : ""}`;
|
|
|
5207
6013
|
};
|
|
5208
6014
|
}
|
|
5209
6015
|
static transformToBarChart(data, profile, query = "", horizontal = false) {
|
|
5210
|
-
var
|
|
6016
|
+
var _a2;
|
|
5211
6017
|
const dimension = profile ? this.selectDimensionField(profile, query) : void 0;
|
|
5212
6018
|
const measure = profile ? this.selectNumericField(profile, query) : void 0;
|
|
5213
6019
|
const aggregate = dimension && profile ? this.aggregateProfileByDimension(profile, dimension.key, measure == null ? void 0 : measure.key) : this.aggregateByCategory(data, this.detectCategories(data));
|
|
5214
6020
|
const barData = Object.entries(aggregate).map(([category, value]) => ({ category, value: Number(value) })).sort((a, b) => horizontal ? b.value - a.value : 0).slice(0, 12);
|
|
5215
6021
|
const fallbackData = barData.length > 0 ? barData : data.slice(0, 12).map((item, index) => {
|
|
5216
|
-
var
|
|
6022
|
+
var _a3, _b, _c, _d, _e;
|
|
5217
6023
|
const meta = item.metadata || {};
|
|
5218
6024
|
const label = String(
|
|
5219
|
-
(_c = (_b = (
|
|
6025
|
+
(_c = (_b = (_a3 = this.getDynamicVal(meta, "name")) != null ? _a3 : meta.label) != null ? _b : meta.title) != null ? _c : `Result ${index + 1}`
|
|
5220
6026
|
);
|
|
5221
6027
|
const value = (_e = (_d = this.extractNumericValue(meta)) != null ? _d : item.score) != null ? _e : 0;
|
|
5222
6028
|
return { category: label, value: Number(value) };
|
|
5223
6029
|
});
|
|
5224
6030
|
return {
|
|
5225
6031
|
type: horizontal ? "horizontal_bar" : "bar_chart",
|
|
5226
|
-
title: dimension ? `${(
|
|
6032
|
+
title: dimension ? `${(_a2 = measure == null ? void 0 : measure.label) != null ? _a2 : "Count"} by ${dimension.label}` : "Comparison",
|
|
5227
6033
|
description: `Showing ${fallbackData.length} comparable values`,
|
|
5228
6034
|
data: fallbackData
|
|
5229
6035
|
};
|
|
@@ -5258,11 +6064,11 @@ ${schemaProfileText}` : ""}`;
|
|
|
5258
6064
|
if (fields.length < 2) return null;
|
|
5259
6065
|
const [xField, yField] = fields;
|
|
5260
6066
|
const points = profile.records.map((record) => {
|
|
5261
|
-
var
|
|
6067
|
+
var _a2;
|
|
5262
6068
|
const x = this.toFiniteNumber(record.fields[xField.key]);
|
|
5263
6069
|
const y = this.toFiniteNumber(record.fields[yField.key]);
|
|
5264
6070
|
if (x === null || y === null) return null;
|
|
5265
|
-
return { x, y, label: String((
|
|
6071
|
+
return { x, y, label: String((_a2 = this.getRecordLabel(record)) != null ? _a2 : record.id) };
|
|
5266
6072
|
}).filter((point) => point !== null).slice(0, 100);
|
|
5267
6073
|
if (points.length === 0) return null;
|
|
5268
6074
|
return {
|
|
@@ -5292,9 +6098,9 @@ ${schemaProfileText}` : ""}`;
|
|
|
5292
6098
|
static transformToRadarChart(data) {
|
|
5293
6099
|
const attributeMap = {};
|
|
5294
6100
|
data.forEach((item) => {
|
|
5295
|
-
var
|
|
6101
|
+
var _a2, _b, _c;
|
|
5296
6102
|
const meta = item.metadata || {};
|
|
5297
|
-
const seriesName = String((_c = (_b = (
|
|
6103
|
+
const seriesName = String((_c = (_b = (_a2 = meta.name) != null ? _a2 : meta.product) != null ? _b : item.id) != null ? _c : "Item");
|
|
5298
6104
|
Object.entries(meta).forEach(([key, val]) => {
|
|
5299
6105
|
if (typeof val === "number" && !["price", "id"].includes(key.toLowerCase())) {
|
|
5300
6106
|
if (!attributeMap[key]) attributeMap[key] = {};
|
|
@@ -5376,22 +6182,22 @@ ${schemaProfileText}` : ""}`;
|
|
|
5376
6182
|
return null;
|
|
5377
6183
|
}
|
|
5378
6184
|
static normalizeTransformation(payload) {
|
|
5379
|
-
var
|
|
6185
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j;
|
|
5380
6186
|
if (!payload || typeof payload !== "object") return null;
|
|
5381
6187
|
const p = payload;
|
|
5382
6188
|
const type = this.normalizeVisualizationType(
|
|
5383
|
-
String((_c = (_b = (
|
|
6189
|
+
String((_c = (_b = (_a2 = p.type) != null ? _a2 : p.view) != null ? _b : p.chartType) != null ? _c : "")
|
|
5384
6190
|
);
|
|
5385
6191
|
if (!type) return null;
|
|
5386
6192
|
const title = String((_e = (_d = p.title) != null ? _d : p.heading) != null ? _e : "Visualization");
|
|
5387
6193
|
const description = p.description ? String(p.description) : void 0;
|
|
5388
|
-
const rawData = (_j = (_i = (_h = (
|
|
6194
|
+
const rawData = (_j = (_i = (_h = (_g2 = (_f = p.data) != null ? _f : p.table) != null ? _g2 : p.rows) != null ? _h : p.items) != null ? _i : p.content) != null ? _j : null;
|
|
5389
6195
|
const data = type === "text" && typeof rawData === "string" ? { content: rawData } : rawData;
|
|
5390
6196
|
const transformation = { type, title, description, data };
|
|
5391
6197
|
return this.validateTransformation(transformation) ? transformation : null;
|
|
5392
6198
|
}
|
|
5393
6199
|
static normalizeVisualizationType(type) {
|
|
5394
|
-
var
|
|
6200
|
+
var _a2;
|
|
5395
6201
|
const mapping = {
|
|
5396
6202
|
pie: "pie_chart",
|
|
5397
6203
|
pie_chart: "pie_chart",
|
|
@@ -5419,7 +6225,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
5419
6225
|
product_carousel: "product_carousel",
|
|
5420
6226
|
carousel: "carousel"
|
|
5421
6227
|
};
|
|
5422
|
-
return (
|
|
6228
|
+
return (_a2 = mapping[type.toLowerCase()]) != null ? _a2 : null;
|
|
5423
6229
|
}
|
|
5424
6230
|
static validateTransformation(t) {
|
|
5425
6231
|
const { type, data } = t;
|
|
@@ -5621,16 +6427,16 @@ ${schemaProfileText}` : ""}`;
|
|
|
5621
6427
|
return null;
|
|
5622
6428
|
}
|
|
5623
6429
|
static selectDimensionField(profile, query) {
|
|
5624
|
-
var
|
|
6430
|
+
var _a2, _b;
|
|
5625
6431
|
const productCategory = profile.categoricalFields.find(
|
|
5626
6432
|
(field) => /category|department|collection|type|group|segment|region|country|state|city/i.test(field.key)
|
|
5627
6433
|
);
|
|
5628
6434
|
const ranked = this.rankFieldsByQuery(profile.categoricalFields, query);
|
|
5629
|
-
return (_b = (
|
|
6435
|
+
return (_b = (_a2 = ranked[0]) != null ? _a2 : productCategory) != null ? _b : profile.categoricalFields[0];
|
|
5630
6436
|
}
|
|
5631
6437
|
static selectNumericField(profile, query) {
|
|
5632
|
-
var
|
|
5633
|
-
return (
|
|
6438
|
+
var _a2;
|
|
6439
|
+
return (_a2 = this.rankFieldsByQuery(profile.numericFields, query)[0]) != null ? _a2 : profile.numericFields[0];
|
|
5634
6440
|
}
|
|
5635
6441
|
static rankFieldsByQuery(fields, query) {
|
|
5636
6442
|
const q = query.toLowerCase();
|
|
@@ -5659,8 +6465,8 @@ ${schemaProfileText}` : ""}`;
|
|
|
5659
6465
|
static aggregateProfileByDimension(profile, dimensionKey, measureKey) {
|
|
5660
6466
|
const result = {};
|
|
5661
6467
|
profile.records.forEach((record) => {
|
|
5662
|
-
var
|
|
5663
|
-
const category = String((
|
|
6468
|
+
var _a2, _b, _c;
|
|
6469
|
+
const category = String((_a2 = record.fields[dimensionKey]) != null ? _a2 : "Other").trim() || "Other";
|
|
5664
6470
|
const value = measureKey ? (_b = this.toFiniteNumber(record.fields[measureKey])) != null ? _b : 0 : 1;
|
|
5665
6471
|
result[category] = ((_c = result[category]) != null ? _c : 0) + value;
|
|
5666
6472
|
});
|
|
@@ -5739,8 +6545,8 @@ ${schemaProfileText}` : ""}`;
|
|
|
5739
6545
|
static aggregateByCategory(data, categories) {
|
|
5740
6546
|
const result = Object.fromEntries(categories.map((c) => [c, 0]));
|
|
5741
6547
|
data.forEach((item) => {
|
|
5742
|
-
var
|
|
5743
|
-
const cat = (
|
|
6548
|
+
var _a2;
|
|
6549
|
+
const cat = (_a2 = this.getProductCategory(item)) != null ? _a2 : "Other";
|
|
5744
6550
|
if (Object.prototype.hasOwnProperty.call(result, cat)) result[cat]++;
|
|
5745
6551
|
else result["Other"] = (result["Other"] || 0) + 1;
|
|
5746
6552
|
});
|
|
@@ -5748,17 +6554,17 @@ ${schemaProfileText}` : ""}`;
|
|
|
5748
6554
|
}
|
|
5749
6555
|
static extractTimeSeriesData(data) {
|
|
5750
6556
|
return data.map((item) => {
|
|
5751
|
-
var
|
|
6557
|
+
var _a2, _b, _c, _d, _e;
|
|
5752
6558
|
const meta = item.metadata || {};
|
|
5753
6559
|
return {
|
|
5754
|
-
timestamp: (_b = (
|
|
6560
|
+
timestamp: (_b = (_a2 = meta.timestamp) != null ? _a2 : meta.date) != null ? _b : (/* @__PURE__ */ new Date()).toISOString(),
|
|
5755
6561
|
value: (_d = (_c = meta.value) != null ? _c : item.score) != null ? _d : 0,
|
|
5756
6562
|
label: (_e = meta.label) != null ? _e : item.content.substring(0, 50)
|
|
5757
6563
|
};
|
|
5758
6564
|
});
|
|
5759
6565
|
}
|
|
5760
6566
|
static extractNumericValue(meta) {
|
|
5761
|
-
var
|
|
6567
|
+
var _a2;
|
|
5762
6568
|
const preferredKeys = [
|
|
5763
6569
|
"value",
|
|
5764
6570
|
"count",
|
|
@@ -5773,7 +6579,7 @@ ${schemaProfileText}` : ""}`;
|
|
|
5773
6579
|
"price"
|
|
5774
6580
|
];
|
|
5775
6581
|
for (const key of preferredKeys) {
|
|
5776
|
-
const raw = (
|
|
6582
|
+
const raw = (_a2 = resolveMetadataValue(meta, key)) != null ? _a2 : meta[key];
|
|
5777
6583
|
const value = typeof raw === "number" ? raw : Number(String(raw != null ? raw : "").replace(/[$,% ,]/g, ""));
|
|
5778
6584
|
if (Number.isFinite(value)) return value;
|
|
5779
6585
|
}
|
|
@@ -5919,8 +6725,8 @@ ${schemaProfileText}` : ""}`;
|
|
|
5919
6725
|
let inStock = 0;
|
|
5920
6726
|
let outOfStock = 0;
|
|
5921
6727
|
data.forEach((d) => {
|
|
5922
|
-
var
|
|
5923
|
-
const cat = (
|
|
6728
|
+
var _a2, _b;
|
|
6729
|
+
const cat = (_a2 = this.getProductCategory(d)) != null ? _a2 : "Other";
|
|
5924
6730
|
if (cat === category) {
|
|
5925
6731
|
const quantity = (_b = this.extractStockQuantity(d)) != null ? _b : 1;
|
|
5926
6732
|
if (this.determineStockStatus(d)) inStock += quantity;
|
|
@@ -5964,9 +6770,9 @@ ${schemaProfileText}` : ""}`;
|
|
|
5964
6770
|
}
|
|
5965
6771
|
// ─── Product Extraction ───────────────────────────────────────────────────
|
|
5966
6772
|
static getDynamicVal(meta, uiKey, config, trainedSchema) {
|
|
5967
|
-
var
|
|
6773
|
+
var _a2;
|
|
5968
6774
|
if (!meta) return void 0;
|
|
5969
|
-
const mapping = (
|
|
6775
|
+
const mapping = (_a2 = config == null ? void 0 : config.rag) == null ? void 0 : _a2.uiMapping;
|
|
5970
6776
|
if ((mapping == null ? void 0 : mapping[uiKey]) && meta[mapping[uiKey]] !== void 0) return meta[mapping[uiKey]];
|
|
5971
6777
|
if (trainedSchema && typeof trainedSchema === "object") {
|
|
5972
6778
|
const trainedKey = trainedSchema[uiKey];
|
|
@@ -5975,13 +6781,13 @@ ${schemaProfileText}` : ""}`;
|
|
|
5975
6781
|
return resolveMetadataValue(meta, uiKey);
|
|
5976
6782
|
}
|
|
5977
6783
|
static extractProductInfo(item, config, trainedSchema) {
|
|
5978
|
-
var
|
|
6784
|
+
var _a2;
|
|
5979
6785
|
const meta = item.metadata || {};
|
|
5980
6786
|
const name = this.getDynamicVal(meta, "name", config, trainedSchema);
|
|
5981
6787
|
const price = this.getDynamicVal(meta, "price", config, trainedSchema);
|
|
5982
6788
|
const brand = this.getDynamicVal(meta, "brand", config, trainedSchema);
|
|
5983
6789
|
const description = this.cleanProductDescription(
|
|
5984
|
-
(
|
|
6790
|
+
(_a2 = this.extractProductDescriptionFromContent(item.content)) != null ? _a2 : this.getProductDescriptionValue(meta, config, trainedSchema)
|
|
5985
6791
|
);
|
|
5986
6792
|
if (name || this.isProductData(item)) {
|
|
5987
6793
|
let finalName = name ? String(name) : void 0;
|
|
@@ -6089,10 +6895,10 @@ RULES:
|
|
|
6089
6895
|
}
|
|
6090
6896
|
static buildContextSummary(sources, maxChars = 6e3) {
|
|
6091
6897
|
const items = sources.map((s, i) => {
|
|
6092
|
-
var
|
|
6898
|
+
var _a2, _b, _c, _d;
|
|
6093
6899
|
return {
|
|
6094
6900
|
index: i + 1,
|
|
6095
|
-
content: (_b = (
|
|
6901
|
+
content: (_b = (_a2 = s.content) == null ? void 0 : _a2.substring(0, 400)) != null ? _b : "",
|
|
6096
6902
|
metadata: (_c = s.metadata) != null ? _c : {},
|
|
6097
6903
|
score: (_d = s.score) != null ? _d : 0
|
|
6098
6904
|
};
|
|
@@ -6285,9 +7091,11 @@ var Pipeline = class {
|
|
|
6285
7091
|
/** LRU-bounded cache: avoids re-embedding identical queries within the same process. */
|
|
6286
7092
|
this.embeddingCache = new LRUEmbeddingCache(500);
|
|
6287
7093
|
this.initialised = false;
|
|
6288
|
-
|
|
7094
|
+
/** Namespace-specific static cold context cache for CAG */
|
|
7095
|
+
this.coldContexts = /* @__PURE__ */ new Map();
|
|
7096
|
+
var _a2, _b, _c, _d, _e;
|
|
6289
7097
|
this.chunker = new DocumentChunker(
|
|
6290
|
-
(_b = (
|
|
7098
|
+
(_b = (_a2 = config.rag) == null ? void 0 : _a2.chunkSize) != null ? _b : 1e3,
|
|
6291
7099
|
(_d = (_c = config.rag) == null ? void 0 : _c.chunkOverlap) != null ? _d : 200
|
|
6292
7100
|
);
|
|
6293
7101
|
if (((_e = config.rag) == null ? void 0 : _e.chunkingStrategy) === "llamaindex") {
|
|
@@ -6303,7 +7111,7 @@ var Pipeline = class {
|
|
|
6303
7111
|
return this.initialised ? this.llmProvider : void 0;
|
|
6304
7112
|
}
|
|
6305
7113
|
async initialize() {
|
|
6306
|
-
var
|
|
7114
|
+
var _a2, _b, _c;
|
|
6307
7115
|
if (this.initialised) return;
|
|
6308
7116
|
const chartInstruction = `You are a helpful assistant. Use the provided context to answer questions accurately.
|
|
6309
7117
|
|
|
@@ -6346,12 +7154,47 @@ var Pipeline = class {
|
|
|
6346
7154
|
this.entityExtractor = new EntityExtractor(this.llmProvider);
|
|
6347
7155
|
}
|
|
6348
7156
|
await this.vectorDB.initialize();
|
|
6349
|
-
if (((
|
|
7157
|
+
if (((_a2 = this.config.rag) == null ? void 0 : _a2.architecture) === "agentic" || this.config.mcpServers && this.config.mcpServers.length > 0) {
|
|
7158
|
+
this.mcpRegistry = new MCPRegistry(this.config.mcpServers || []);
|
|
7159
|
+
this.multiAgentCoordinator = new MultiAgentCoordinator({
|
|
7160
|
+
llmProvider: this.llmProvider,
|
|
7161
|
+
mcpRegistry: this.mcpRegistry,
|
|
7162
|
+
documentSearch: async (query) => {
|
|
7163
|
+
return this.runNormalQuery(query);
|
|
7164
|
+
}
|
|
7165
|
+
});
|
|
6350
7166
|
this.agent = new LangChainAgent(this, this.config);
|
|
6351
7167
|
await this.agent.initialize(this.llmProvider);
|
|
6352
7168
|
}
|
|
7169
|
+
if ((_c = (_b = this.config.rag) == null ? void 0 : _b.cag) == null ? void 0 : _c.enabled) {
|
|
7170
|
+
await this.loadColdContext(this.config.projectId);
|
|
7171
|
+
}
|
|
6353
7172
|
this.initialised = true;
|
|
6354
7173
|
}
|
|
7174
|
+
async loadColdContext(ns) {
|
|
7175
|
+
var _a2, _b;
|
|
7176
|
+
const cagConfig = (_a2 = this.config.rag) == null ? void 0 : _a2.cag;
|
|
7177
|
+
if (!cagConfig || !cagConfig.enabled) return;
|
|
7178
|
+
try {
|
|
7179
|
+
const coldNs = cagConfig.coldNamespace || ns;
|
|
7180
|
+
const limit = (_b = cagConfig.coldLimit) != null ? _b : 20;
|
|
7181
|
+
const queryText = cagConfig.coldQuery || "documentation";
|
|
7182
|
+
const queryVector = await this.embeddingProvider.embed(queryText, { taskType: "query" });
|
|
7183
|
+
const coldMatches = await this.vectorDB.query(queryVector, limit, coldNs);
|
|
7184
|
+
if (coldMatches.length > 0) {
|
|
7185
|
+
const formatted = coldMatches.map((m, i) => `[Cold Source ${i + 1}]
|
|
7186
|
+
${m.content}`).join("\n\n---\n\n");
|
|
7187
|
+
this.coldContexts.set(ns, formatted);
|
|
7188
|
+
console.log(`[Pipeline] Cold RAG (CAG) loaded ${coldMatches.length} documents into cached context for namespace: ${ns}`);
|
|
7189
|
+
} else {
|
|
7190
|
+
this.coldContexts.set(ns, "No cold context found.");
|
|
7191
|
+
console.warn(`[Pipeline] Cold RAG (CAG) initialized but no documents were found in namespace: ${coldNs}`);
|
|
7192
|
+
}
|
|
7193
|
+
} catch (err) {
|
|
7194
|
+
console.error(`[Pipeline] Failed to load Cold RAG (CAG) context for namespace ${ns}:`, err);
|
|
7195
|
+
this.coldContexts.set(ns, "Failed to load cold context.");
|
|
7196
|
+
}
|
|
7197
|
+
}
|
|
6355
7198
|
/**
|
|
6356
7199
|
* Ingest documents with automatic chunking, embedding, and batch upsert.
|
|
6357
7200
|
*/
|
|
@@ -6383,12 +7226,12 @@ var Pipeline = class {
|
|
|
6383
7226
|
}
|
|
6384
7227
|
/** Step 1: Chunk the document content. */
|
|
6385
7228
|
async prepareChunks(doc) {
|
|
6386
|
-
var
|
|
7229
|
+
var _a2, _b;
|
|
6387
7230
|
if (this.llamaIngestor) {
|
|
6388
7231
|
return this.llamaIngestor.chunk(doc.content, {
|
|
6389
7232
|
docId: doc.docId,
|
|
6390
7233
|
metadata: doc.metadata,
|
|
6391
|
-
chunkSize: (
|
|
7234
|
+
chunkSize: (_a2 = this.config.rag) == null ? void 0 : _a2.chunkSize,
|
|
6392
7235
|
chunkOverlap: (_b = this.config.rag) == null ? void 0 : _b.chunkOverlap
|
|
6393
7236
|
});
|
|
6394
7237
|
}
|
|
@@ -6410,7 +7253,7 @@ var Pipeline = class {
|
|
|
6410
7253
|
embedBatchOptions
|
|
6411
7254
|
);
|
|
6412
7255
|
if (vectors.length !== chunks.length) {
|
|
6413
|
-
throw new
|
|
7256
|
+
throw new EmbeddingFailedException(
|
|
6414
7257
|
`[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
|
|
6415
7258
|
);
|
|
6416
7259
|
}
|
|
@@ -6459,12 +7302,37 @@ var Pipeline = class {
|
|
|
6459
7302
|
extractionOptions
|
|
6460
7303
|
);
|
|
6461
7304
|
}
|
|
7305
|
+
async runNormalQuery(question, history = [], namespace) {
|
|
7306
|
+
const stream = this.askStreamInternal(question, history, namespace);
|
|
7307
|
+
let reply = "";
|
|
7308
|
+
let sources = [];
|
|
7309
|
+
try {
|
|
7310
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
7311
|
+
const chunk = temp.value;
|
|
7312
|
+
if (typeof chunk === "string") {
|
|
7313
|
+
reply += chunk;
|
|
7314
|
+
} else if (typeof chunk === "object" && chunk !== null) {
|
|
7315
|
+
if ("reply" in chunk && chunk.reply) reply += chunk.reply;
|
|
7316
|
+
if ("sources" in chunk && chunk.sources) sources = chunk.sources;
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
} catch (temp) {
|
|
7320
|
+
error = [temp];
|
|
7321
|
+
} finally {
|
|
7322
|
+
try {
|
|
7323
|
+
more && (temp = iter.return) && await temp.call(iter);
|
|
7324
|
+
} finally {
|
|
7325
|
+
if (error)
|
|
7326
|
+
throw error[0];
|
|
7327
|
+
}
|
|
7328
|
+
}
|
|
7329
|
+
return { reply, sources };
|
|
7330
|
+
}
|
|
6462
7331
|
async ask(question, history = [], namespace) {
|
|
6463
|
-
var
|
|
7332
|
+
var _a2;
|
|
6464
7333
|
await this.initialize();
|
|
6465
|
-
if (((
|
|
6466
|
-
|
|
6467
|
-
return { reply: agentReply, sources: [] };
|
|
7334
|
+
if ((((_a2 = this.config.rag) == null ? void 0 : _a2.architecture) === "agentic" || this.config.mcpServers && this.config.mcpServers.length > 0) && this.multiAgentCoordinator) {
|
|
7335
|
+
return await this.multiAgentCoordinator.run(question, history);
|
|
6468
7336
|
}
|
|
6469
7337
|
const stream = this.askStream(question, history, namespace);
|
|
6470
7338
|
let reply = "";
|
|
@@ -6488,13 +7356,54 @@ var Pipeline = class {
|
|
|
6488
7356
|
error = [temp];
|
|
6489
7357
|
} finally {
|
|
6490
7358
|
try {
|
|
6491
|
-
more && (temp = iter.return) && await temp.call(iter);
|
|
7359
|
+
more && (temp = iter.return) && await temp.call(iter);
|
|
7360
|
+
} finally {
|
|
7361
|
+
if (error)
|
|
7362
|
+
throw error[0];
|
|
7363
|
+
}
|
|
7364
|
+
}
|
|
7365
|
+
return { reply, sources, graphData, ui_transformation: uiTransformation, trace };
|
|
7366
|
+
}
|
|
7367
|
+
askStream(_0) {
|
|
7368
|
+
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
7369
|
+
var _a2;
|
|
7370
|
+
yield new __await(this.initialize());
|
|
7371
|
+
if ((((_a2 = this.config.rag) == null ? void 0 : _a2.architecture) === "agentic" || this.config.mcpServers && this.config.mcpServers.length > 0) && this.multiAgentCoordinator) {
|
|
7372
|
+
const stream2 = this.multiAgentCoordinator.runStream(question, history);
|
|
7373
|
+
try {
|
|
7374
|
+
for (var iter = __forAwait(stream2), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
7375
|
+
const chunk = temp.value;
|
|
7376
|
+
yield chunk;
|
|
7377
|
+
}
|
|
7378
|
+
} catch (temp) {
|
|
7379
|
+
error = [temp];
|
|
7380
|
+
} finally {
|
|
7381
|
+
try {
|
|
7382
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
7383
|
+
} finally {
|
|
7384
|
+
if (error)
|
|
7385
|
+
throw error[0];
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7388
|
+
return;
|
|
7389
|
+
}
|
|
7390
|
+
const stream = this.askStreamInternal(question, history, namespace);
|
|
7391
|
+
try {
|
|
7392
|
+
for (var iter2 = __forAwait(stream), more2, temp2, error2; more2 = !(temp2 = yield new __await(iter2.next())).done; more2 = false) {
|
|
7393
|
+
const chunk = temp2.value;
|
|
7394
|
+
yield chunk;
|
|
7395
|
+
}
|
|
7396
|
+
} catch (temp2) {
|
|
7397
|
+
error2 = [temp2];
|
|
6492
7398
|
} finally {
|
|
6493
|
-
|
|
6494
|
-
|
|
7399
|
+
try {
|
|
7400
|
+
more2 && (temp2 = iter2.return) && (yield new __await(temp2.call(iter2)));
|
|
7401
|
+
} finally {
|
|
7402
|
+
if (error2)
|
|
7403
|
+
throw error2[0];
|
|
7404
|
+
}
|
|
6495
7405
|
}
|
|
6496
|
-
}
|
|
6497
|
-
return { reply, sources, graphData, ui_transformation: uiTransformation, trace };
|
|
7406
|
+
});
|
|
6498
7407
|
}
|
|
6499
7408
|
/**
|
|
6500
7409
|
* High-performance streaming RAG flow.
|
|
@@ -6506,12 +7415,12 @@ var Pipeline = class {
|
|
|
6506
7415
|
* - UITransformation is computed after text streaming and emitted with metadata
|
|
6507
7416
|
* - SchemaMapper.train runs while answer generation streams
|
|
6508
7417
|
*/
|
|
6509
|
-
|
|
7418
|
+
askStreamInternal(_0) {
|
|
6510
7419
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
6511
|
-
var
|
|
7420
|
+
var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
|
6512
7421
|
yield new __await(this.initialize());
|
|
6513
7422
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
6514
|
-
const topK = (_b = (
|
|
7423
|
+
const topK = (_b = (_a2 = this.config.rag) == null ? void 0 : _a2.topK) != null ? _b : 5;
|
|
6515
7424
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
6516
7425
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6517
7426
|
const requestStart = performance.now();
|
|
@@ -6524,7 +7433,7 @@ var Pipeline = class {
|
|
|
6524
7433
|
}
|
|
6525
7434
|
const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
6526
7435
|
const filter = QueryProcessor.buildQueryFilter(question, hints);
|
|
6527
|
-
const numericPredicates = QueryProcessor.extractNumericPredicates(question, (
|
|
7436
|
+
const numericPredicates = QueryProcessor.extractNumericPredicates(question, (_g2 = this.config.rag) == null ? void 0 : _g2.filterableFields);
|
|
6528
7437
|
if (numericPredicates.length > 0) {
|
|
6529
7438
|
filter.__numericPredicates = numericPredicates;
|
|
6530
7439
|
}
|
|
@@ -6590,6 +7499,32 @@ ${graphContext}
|
|
|
6590
7499
|
VECTOR CONTEXT:
|
|
6591
7500
|
${context}`;
|
|
6592
7501
|
}
|
|
7502
|
+
if ((_n = (_m = this.config.rag) == null ? void 0 : _m.cag) == null ? void 0 : _n.enabled) {
|
|
7503
|
+
if (!this.coldContexts.has(ns)) {
|
|
7504
|
+
yield new __await(this.loadColdContext(ns));
|
|
7505
|
+
}
|
|
7506
|
+
const coldContext = this.coldContexts.get(ns);
|
|
7507
|
+
if (coldContext && coldContext !== "No cold context found." && coldContext !== "Failed to load cold context.") {
|
|
7508
|
+
context = `COLD CONTEXT (STATIC KNOWLEDGE):
|
|
7509
|
+
${coldContext}
|
|
7510
|
+
|
|
7511
|
+
RETRIEVED HOT CONTEXT (REAL-TIME KNOWLEDGE):
|
|
7512
|
+
${context}`;
|
|
7513
|
+
}
|
|
7514
|
+
}
|
|
7515
|
+
yield {
|
|
7516
|
+
reply: "",
|
|
7517
|
+
sources: sources.map((s) => {
|
|
7518
|
+
var _a3;
|
|
7519
|
+
return {
|
|
7520
|
+
id: s.id,
|
|
7521
|
+
score: s.score,
|
|
7522
|
+
content: s.content,
|
|
7523
|
+
metadata: (_a3 = s.metadata) != null ? _a3 : {},
|
|
7524
|
+
namespace: ns
|
|
7525
|
+
};
|
|
7526
|
+
})
|
|
7527
|
+
};
|
|
6593
7528
|
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
6594
7529
|
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
6595
7530
|
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
@@ -6617,10 +7552,10 @@ ${context}`;
|
|
|
6617
7552
|
let hallucinationScoringPromise = Promise.resolve(void 0);
|
|
6618
7553
|
const restrictionSuffix = "\n\n(IMPORTANT: Format your response beautifully using rich Markdown! Use bolding for emphasis, headings (##) for structure, bullet points for lists, and proper line breaks between paragraphs to make it highly readable. However, NEVER generate Markdown tables, HTML figures, or text charts (the UI renders requested charts separately, so NEVER say you cannot create, display, draw, render, or provide a chart/visualization). If listing products, use simple markdown bullet points or comma-separated names. NEVER use plus signs (+) to separate product names, category names, or list items. For product description/detail questions, provide customer-facing prose only and do NOT list internal catalog fields such as Handle, Body (HTML), Vendor, Type, Tags, Published, Option, Variant, SKU, or raw metadata labels. You CAN use numbers for years/counts like 2006 or 5800, but NEVER put a dollar sign ($) before them.)";
|
|
6619
7554
|
const isClaude37 = this.config.llm.model.includes("claude-3-7-sonnet");
|
|
6620
|
-
const isNativeThinking = isClaude37 && (((
|
|
7555
|
+
const isNativeThinking = isClaude37 && (((_o = this.config.llm.options) == null ? void 0 : _o.thinking) === true || ((_p = this.config.llm.options) == null ? void 0 : _p.thinking) === "enabled" || ((_q = this.config.llm.options) == null ? void 0 : _q.thinking) !== false);
|
|
6621
7556
|
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
6622
7557
|
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
6623
|
-
const isSimulatedThinking = !isNativeThinking && (((
|
|
7558
|
+
const isSimulatedThinking = !isNativeThinking && (((_r = this.config.llm.options) == null ? void 0 : _r.thinking) === true || ((_s = this.config.llm.options) == null ? void 0 : _s.thinking) === "enabled" || isReasoningModel && ((_t = this.config.llm.options) == null ? void 0 : _t.thinking) !== false);
|
|
6624
7559
|
let finalRestrictionSuffix = restrictionSuffix;
|
|
6625
7560
|
if (isSimulatedThinking) {
|
|
6626
7561
|
finalRestrictionSuffix += "\n\n(IMPORTANT: You must think step-by-step before answering. Write your thinking process inside a `<think>...</think>` block. Write the `<think>` block first, then follow it with your final response. Keep the thinking process thorough and detailed. Example: `<think>Thinking details here...</think>Final answer text here.`)";
|
|
@@ -6628,7 +7563,7 @@ ${context}`;
|
|
|
6628
7563
|
const hardenedHistory = [...history];
|
|
6629
7564
|
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
6630
7565
|
const messages = [...hardenedHistory, userQuestion];
|
|
6631
|
-
const systemPrompt = (
|
|
7566
|
+
const systemPrompt = (_u = this.config.llm.systemPrompt) != null ? _u : "";
|
|
6632
7567
|
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
6633
7568
|
let fullReply = "";
|
|
6634
7569
|
let thinkingText = "";
|
|
@@ -6739,7 +7674,7 @@ ${context}`;
|
|
|
6739
7674
|
}
|
|
6740
7675
|
yield fullReply;
|
|
6741
7676
|
}
|
|
6742
|
-
const runHallucination = ((
|
|
7677
|
+
const runHallucination = ((_v = this.config.llm.options) == null ? void 0 : _v.hallucinationScoring) === true || this.config.llm.provider !== "ollama" && ((_w = this.config.llm.options) == null ? void 0 : _w.hallucinationScoring) !== false;
|
|
6743
7678
|
if (runHallucination) {
|
|
6744
7679
|
hallucinationScoringPromise = scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0);
|
|
6745
7680
|
}
|
|
@@ -6748,7 +7683,7 @@ ${context}`;
|
|
|
6748
7683
|
const latency = {
|
|
6749
7684
|
embedMs: Math.round(embedMs),
|
|
6750
7685
|
retrieveMs: Math.round(retrieveMs),
|
|
6751
|
-
rerankMs: ((
|
|
7686
|
+
rerankMs: ((_x = this.config.rag) == null ? void 0 : _x.useReranking) ? Math.round(rerankMs) : void 0,
|
|
6752
7687
|
generateMs: Math.round(generateMs),
|
|
6753
7688
|
totalMs: Math.round(totalMs)
|
|
6754
7689
|
};
|
|
@@ -6761,33 +7696,63 @@ ${context}`;
|
|
|
6761
7696
|
totalTokens: promptTokens + completionTokens,
|
|
6762
7697
|
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
6763
7698
|
};
|
|
7699
|
+
const awaitHallucination = ((_y = this.config.llm.options) == null ? void 0 : _y.awaitHallucination) === true;
|
|
6764
7700
|
const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
|
|
6765
7701
|
uiTransformationPromise,
|
|
6766
|
-
hallucinationScoringPromise
|
|
7702
|
+
awaitHallucination ? hallucinationScoringPromise : Promise.resolve(void 0)
|
|
6767
7703
|
]));
|
|
6768
|
-
const
|
|
7704
|
+
const buildTrace = (hScore) => __spreadValues({
|
|
6769
7705
|
requestId,
|
|
6770
7706
|
query: question,
|
|
6771
7707
|
rewrittenQuery,
|
|
6772
7708
|
systemPrompt,
|
|
6773
7709
|
userPrompt: question + finalRestrictionSuffix,
|
|
6774
7710
|
chunks: sources.map((s) => {
|
|
6775
|
-
var
|
|
7711
|
+
var _a3;
|
|
6776
7712
|
return {
|
|
6777
7713
|
id: s.id,
|
|
6778
7714
|
score: s.score,
|
|
6779
7715
|
content: s.content,
|
|
6780
|
-
metadata: (
|
|
7716
|
+
metadata: (_a3 = s.metadata) != null ? _a3 : {},
|
|
6781
7717
|
namespace: ns
|
|
6782
7718
|
};
|
|
6783
7719
|
}),
|
|
6784
7720
|
latency,
|
|
6785
7721
|
tokens,
|
|
6786
7722
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6787
|
-
},
|
|
6788
|
-
hallucinationScore:
|
|
6789
|
-
hallucinationReason:
|
|
7723
|
+
}, hScore ? {
|
|
7724
|
+
hallucinationScore: hScore.score,
|
|
7725
|
+
hallucinationReason: hScore.reason
|
|
6790
7726
|
} : {});
|
|
7727
|
+
const trace = buildTrace(hallucinationResult);
|
|
7728
|
+
if ((_z = this.config.telemetry) == null ? void 0 : _z.enabled) {
|
|
7729
|
+
const telemetryUrl = this.config.telemetry.url || "/api/telemetry";
|
|
7730
|
+
const absoluteUrl = telemetryUrl.startsWith("http") ? telemetryUrl : (process.env.NEXT_PUBLIC_APP_URL || `http://localhost:${process.env.PORT || 3e3}`) + telemetryUrl;
|
|
7731
|
+
(async () => {
|
|
7732
|
+
try {
|
|
7733
|
+
let finalTrace = trace;
|
|
7734
|
+
if (!awaitHallucination && runHallucination) {
|
|
7735
|
+
const backgroundScoreResult = await hallucinationScoringPromise;
|
|
7736
|
+
if (backgroundScoreResult) {
|
|
7737
|
+
finalTrace = buildTrace(backgroundScoreResult);
|
|
7738
|
+
}
|
|
7739
|
+
}
|
|
7740
|
+
await fetch(absoluteUrl, {
|
|
7741
|
+
method: "POST",
|
|
7742
|
+
headers: {
|
|
7743
|
+
"Content-Type": "application/json"
|
|
7744
|
+
},
|
|
7745
|
+
body: JSON.stringify({
|
|
7746
|
+
trace: finalTrace,
|
|
7747
|
+
licenseKey: this.config.licenseKey,
|
|
7748
|
+
projectId: ns
|
|
7749
|
+
})
|
|
7750
|
+
});
|
|
7751
|
+
} catch (err) {
|
|
7752
|
+
console.warn(`[Pipeline Telemetry] Failed to send trace async to ${absoluteUrl}:`, err.message);
|
|
7753
|
+
}
|
|
7754
|
+
})();
|
|
7755
|
+
}
|
|
6791
7756
|
yield {
|
|
6792
7757
|
reply: "",
|
|
6793
7758
|
sources,
|
|
@@ -6807,7 +7772,7 @@ ${context}`;
|
|
|
6807
7772
|
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
6808
7773
|
*/
|
|
6809
7774
|
async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
|
|
6810
|
-
var
|
|
7775
|
+
var _a2;
|
|
6811
7776
|
if (!sources || sources.length === 0) {
|
|
6812
7777
|
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6813
7778
|
}
|
|
@@ -6821,7 +7786,7 @@ ${context}`;
|
|
|
6821
7786
|
);
|
|
6822
7787
|
}
|
|
6823
7788
|
const isLocalProvider = this.config.llm.provider === "ollama";
|
|
6824
|
-
const disableLlmUiTransform = ((
|
|
7789
|
+
const disableLlmUiTransform = ((_a2 = this.config.llm.options) == null ? void 0 : _a2.disableLlmUiTransform) === true;
|
|
6825
7790
|
if (forceDeterministic || isLocalProvider || disableLlmUiTransform) {
|
|
6826
7791
|
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6827
7792
|
}
|
|
@@ -6839,9 +7804,9 @@ ${context}`;
|
|
|
6839
7804
|
const value = this.resolveNumericPredicateValue(source, predicate);
|
|
6840
7805
|
return value !== null && this.matchesNumericPredicate(value, predicate);
|
|
6841
7806
|
})).sort((a, b) => {
|
|
6842
|
-
var
|
|
7807
|
+
var _a2, _b;
|
|
6843
7808
|
const primary = predicates[0];
|
|
6844
|
-
const aValue = (
|
|
7809
|
+
const aValue = (_a2 = this.resolveNumericPredicateValue(a, primary)) != null ? _a2 : 0;
|
|
6845
7810
|
const bValue = (_b = this.resolveNumericPredicateValue(b, primary)) != null ? _b : 0;
|
|
6846
7811
|
return primary.operator === "lt" || primary.operator === "lte" ? aValue - bValue : bValue - aValue;
|
|
6847
7812
|
});
|
|
@@ -6913,8 +7878,8 @@ ${context}`;
|
|
|
6913
7878
|
return Number.isFinite(numeric) ? numeric : null;
|
|
6914
7879
|
}
|
|
6915
7880
|
async retrieve(query, options) {
|
|
6916
|
-
var
|
|
6917
|
-
const ns = (
|
|
7881
|
+
var _a2, _b, _c, _d, _e, _f, _g2;
|
|
7882
|
+
const ns = (_a2 = options.namespace) != null ? _a2 : this.config.projectId;
|
|
6918
7883
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
6919
7884
|
const cacheKey = `${ns}::${query}`;
|
|
6920
7885
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
@@ -6946,7 +7911,7 @@ ${context}`;
|
|
|
6946
7911
|
) : [];
|
|
6947
7912
|
const resolvedSources = [];
|
|
6948
7913
|
for (const source of sources) {
|
|
6949
|
-
const parentId = (
|
|
7914
|
+
const parentId = (_g2 = source.metadata) == null ? void 0 : _g2.parent_id;
|
|
6950
7915
|
if (parentId) {
|
|
6951
7916
|
console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
|
|
6952
7917
|
resolvedSources.push(source);
|
|
@@ -7124,12 +8089,142 @@ var ProviderHealthCheck = class {
|
|
|
7124
8089
|
}
|
|
7125
8090
|
};
|
|
7126
8091
|
|
|
8092
|
+
// src/core/LicenseVerifier.ts
|
|
8093
|
+
import * as crypto2 from "crypto";
|
|
8094
|
+
var LicenseVerifier = class {
|
|
8095
|
+
/**
|
|
8096
|
+
* Decodes, verifies signature, and checks metadata of a license key.
|
|
8097
|
+
*
|
|
8098
|
+
* @param licenseKey - Base64url signed JWT license key.
|
|
8099
|
+
* @param currentProjectId - Project namespace ID.
|
|
8100
|
+
* @param publicKeyOverride - Optional override for unit/integration tests.
|
|
8101
|
+
*/
|
|
8102
|
+
static verify(licenseKey, currentProjectId, provider, publicKeyOverride) {
|
|
8103
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
8104
|
+
if (!licenseKey) {
|
|
8105
|
+
if (isProduction) {
|
|
8106
|
+
throw new ConfigurationException(
|
|
8107
|
+
"[Retrivora SDK] License key (licenseKey) is required in production environments."
|
|
8108
|
+
);
|
|
8109
|
+
}
|
|
8110
|
+
console.warn(
|
|
8111
|
+
`\x1B[33m[Retrivora SDK] WARNING: Running without a license key. This namespace (${currentProjectId}) is permitted for local development only.\x1B[0m`
|
|
8112
|
+
);
|
|
8113
|
+
return {
|
|
8114
|
+
projectId: currentProjectId,
|
|
8115
|
+
expiresAt: Math.floor(Date.now() / 1e3) + 86400,
|
|
8116
|
+
// Valid for 24h
|
|
8117
|
+
tier: "hobby"
|
|
8118
|
+
};
|
|
8119
|
+
}
|
|
8120
|
+
try {
|
|
8121
|
+
const parts = licenseKey.split(".");
|
|
8122
|
+
if (parts.length !== 3) {
|
|
8123
|
+
throw new Error("Malformed token structure (expected 3 parts).");
|
|
8124
|
+
}
|
|
8125
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
8126
|
+
const dataToVerify = `${headerB64}.${payloadB64}`;
|
|
8127
|
+
const publicKey = publicKeyOverride != null ? publicKeyOverride : this.PUBLIC_KEY;
|
|
8128
|
+
const signature = Buffer.from(signatureB64, "base64url");
|
|
8129
|
+
const data = Buffer.from(dataToVerify);
|
|
8130
|
+
const isValid = crypto2.verify(
|
|
8131
|
+
"sha256",
|
|
8132
|
+
data,
|
|
8133
|
+
publicKey,
|
|
8134
|
+
signature
|
|
8135
|
+
);
|
|
8136
|
+
if (!isValid) {
|
|
8137
|
+
throw new Error("Signature verification failed.");
|
|
8138
|
+
}
|
|
8139
|
+
const payload = JSON.parse(
|
|
8140
|
+
Buffer.from(payloadB64, "base64url").toString("utf8")
|
|
8141
|
+
);
|
|
8142
|
+
if (!payload.projectId) {
|
|
8143
|
+
throw new Error('License payload is missing "projectId".');
|
|
8144
|
+
}
|
|
8145
|
+
if (payload.projectId !== currentProjectId) {
|
|
8146
|
+
throw new Error(
|
|
8147
|
+
`Project ID mismatch. License is bound to project namespace "${payload.projectId}" but configuration has "${currentProjectId}".`
|
|
8148
|
+
);
|
|
8149
|
+
}
|
|
8150
|
+
if (!payload.expiresAt) {
|
|
8151
|
+
throw new Error('License payload is missing expiration ("expiresAt").');
|
|
8152
|
+
}
|
|
8153
|
+
const currentTimestampSec = Math.floor(Date.now() / 1e3);
|
|
8154
|
+
if (currentTimestampSec > payload.expiresAt) {
|
|
8155
|
+
throw new Error(
|
|
8156
|
+
`License key has expired. Expiration: ${new Date(
|
|
8157
|
+
payload.expiresAt * 1e3
|
|
8158
|
+
).toDateString()}`
|
|
8159
|
+
);
|
|
8160
|
+
}
|
|
8161
|
+
if (isProduction && provider) {
|
|
8162
|
+
const tier = (payload.tier || "").toLowerCase();
|
|
8163
|
+
const normalizedProvider = provider.toLowerCase();
|
|
8164
|
+
if (tier === "hobby") {
|
|
8165
|
+
const allowedHobby = ["postgresql", "pgvector", "supabase"];
|
|
8166
|
+
if (!allowedHobby.includes(normalizedProvider)) {
|
|
8167
|
+
throw new Error(
|
|
8168
|
+
`The database provider "${provider}" is not allowed on the Hobby tier. Hobby tier is restricted to PostgreSQL and Supabase. Please upgrade to a Developer Pro or Enterprise plan.`
|
|
8169
|
+
);
|
|
8170
|
+
}
|
|
8171
|
+
} else if (tier === "pro" || tier === "developer_pro" || tier === "premium" || tier === "growth") {
|
|
8172
|
+
const allowedPro = [
|
|
8173
|
+
"postgresql",
|
|
8174
|
+
"pgvector",
|
|
8175
|
+
"supabase",
|
|
8176
|
+
"pinecone",
|
|
8177
|
+
"qdrant",
|
|
8178
|
+
"mongodb",
|
|
8179
|
+
"milvus",
|
|
8180
|
+
"chroma",
|
|
8181
|
+
"chromadb"
|
|
8182
|
+
];
|
|
8183
|
+
if (!allowedPro.includes(normalizedProvider)) {
|
|
8184
|
+
throw new Error(
|
|
8185
|
+
`The database provider "${provider}" is not allowed on the Developer Pro tier. Please upgrade to an Enterprise plan.`
|
|
8186
|
+
);
|
|
8187
|
+
}
|
|
8188
|
+
}
|
|
8189
|
+
}
|
|
8190
|
+
return payload;
|
|
8191
|
+
} catch (err) {
|
|
8192
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8193
|
+
throw new ConfigurationException(
|
|
8194
|
+
`[Retrivora SDK] License key validation failed: ${message}`
|
|
8195
|
+
);
|
|
8196
|
+
}
|
|
8197
|
+
}
|
|
8198
|
+
};
|
|
8199
|
+
// Retrivora's Public Key used to verify the license key signature.
|
|
8200
|
+
// In production, this matches the private key held by Retrivora SaaS.
|
|
8201
|
+
LicenseVerifier.PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
8202
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArMieCkCBtTfByRNOserm
|
|
8203
|
+
XM4T0Am29JyNzB4XQPe+WJJ56CN73H1HLXx9n1ByFcxkEJ9po+SURj5DnvUXwEy+
|
|
8204
|
+
xstx33wDPpVmcMQe33j5CRYS0S4gICiNqIRN7XkTlJtrcXqrmh1/hdOAwfRbjO1T
|
|
8205
|
+
NVtORHwUwWfI/lBLyxUPGtKPed+0fQ7NtcW4o00JXxs3EjCB5BV9CbnXoTjQb/4h
|
|
8206
|
+
iwZof4l8rb7oaNzOsVg0RSyxsETX7Ex5sI0CjBz1p2mYp4oVhw/A/h8Ls7H0XzjC
|
|
8207
|
+
+Eb6BLtsytt5JHoSp0jExLlCpDmpys2L+kETcBVx+IqiXtdN1n6KbkksvEDUVzLI
|
|
8208
|
+
MwIDAQAB
|
|
8209
|
+
-----END PUBLIC KEY-----`;
|
|
8210
|
+
|
|
7127
8211
|
// src/core/VectorPlugin.ts
|
|
7128
8212
|
var VectorPlugin = class {
|
|
7129
8213
|
constructor(hostConfig) {
|
|
7130
|
-
|
|
7131
|
-
this.
|
|
7132
|
-
this.
|
|
8214
|
+
const resolvedConfig = ConfigResolver.resolve(hostConfig);
|
|
8215
|
+
this.config = resolvedConfig;
|
|
8216
|
+
this.validationPromise = (async () => {
|
|
8217
|
+
var _a2;
|
|
8218
|
+
await ConfigValidator.validateAndThrow(resolvedConfig);
|
|
8219
|
+
LicenseVerifier.verify(
|
|
8220
|
+
resolvedConfig.licenseKey,
|
|
8221
|
+
resolvedConfig.projectId,
|
|
8222
|
+
(_a2 = resolvedConfig.vectorDb) == null ? void 0 : _a2.provider
|
|
8223
|
+
);
|
|
8224
|
+
})();
|
|
8225
|
+
this.validationPromise.catch(() => {
|
|
8226
|
+
});
|
|
8227
|
+
this.pipeline = new Pipeline(resolvedConfig);
|
|
7133
8228
|
}
|
|
7134
8229
|
/**
|
|
7135
8230
|
* Get the current resolved configuration.
|
|
@@ -7162,31 +8257,93 @@ var VectorPlugin = class {
|
|
|
7162
8257
|
* Run a chat query.
|
|
7163
8258
|
*/
|
|
7164
8259
|
async chat(message, history = [], namespace) {
|
|
7165
|
-
|
|
7166
|
-
|
|
8260
|
+
try {
|
|
8261
|
+
await this.validationPromise;
|
|
8262
|
+
} catch (err) {
|
|
8263
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
8264
|
+
}
|
|
8265
|
+
try {
|
|
8266
|
+
return await this.pipeline.ask(message, history, namespace);
|
|
8267
|
+
} catch (err) {
|
|
8268
|
+
const msg = String(err);
|
|
8269
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
8270
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
8271
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
8272
|
+
}
|
|
8273
|
+
throw wrapError(err, defaultCode);
|
|
8274
|
+
}
|
|
7167
8275
|
}
|
|
7168
8276
|
/**
|
|
7169
8277
|
* Run a streaming chat query.
|
|
7170
8278
|
*/
|
|
7171
8279
|
chatStream(_0) {
|
|
7172
8280
|
return __asyncGenerator(this, arguments, function* (message, history = [], namespace) {
|
|
7173
|
-
|
|
7174
|
-
|
|
8281
|
+
try {
|
|
8282
|
+
yield new __await(this.validationPromise);
|
|
8283
|
+
} catch (err) {
|
|
8284
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
8285
|
+
}
|
|
8286
|
+
try {
|
|
8287
|
+
const stream = this.pipeline.askStream(message, history, namespace);
|
|
8288
|
+
try {
|
|
8289
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
8290
|
+
const chunk = temp.value;
|
|
8291
|
+
yield chunk;
|
|
8292
|
+
}
|
|
8293
|
+
} catch (temp) {
|
|
8294
|
+
error = [temp];
|
|
8295
|
+
} finally {
|
|
8296
|
+
try {
|
|
8297
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
8298
|
+
} finally {
|
|
8299
|
+
if (error)
|
|
8300
|
+
throw error[0];
|
|
8301
|
+
}
|
|
8302
|
+
}
|
|
8303
|
+
} catch (err) {
|
|
8304
|
+
const msg = String(err);
|
|
8305
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
8306
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
8307
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
8308
|
+
}
|
|
8309
|
+
throw wrapError(err, defaultCode);
|
|
8310
|
+
}
|
|
7175
8311
|
});
|
|
7176
8312
|
}
|
|
7177
8313
|
/**
|
|
7178
8314
|
* Ingest documents into the vector database.
|
|
7179
8315
|
*/
|
|
7180
8316
|
async ingest(documents, namespace) {
|
|
7181
|
-
|
|
7182
|
-
|
|
8317
|
+
try {
|
|
8318
|
+
await this.validationPromise;
|
|
8319
|
+
} catch (err) {
|
|
8320
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
8321
|
+
}
|
|
8322
|
+
try {
|
|
8323
|
+
return await this.pipeline.ingest(documents, namespace);
|
|
8324
|
+
} catch (err) {
|
|
8325
|
+
const msg = String(err);
|
|
8326
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
8327
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
8328
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
8329
|
+
}
|
|
8330
|
+
throw wrapError(err, defaultCode);
|
|
8331
|
+
}
|
|
7183
8332
|
}
|
|
7184
8333
|
/**
|
|
7185
8334
|
* Get auto-suggestions based on a query prefix.
|
|
7186
8335
|
*/
|
|
7187
8336
|
async getSuggestions(query, namespace) {
|
|
7188
|
-
|
|
7189
|
-
|
|
8337
|
+
try {
|
|
8338
|
+
await this.validationPromise;
|
|
8339
|
+
} catch (err) {
|
|
8340
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
8341
|
+
}
|
|
8342
|
+
try {
|
|
8343
|
+
return await this.pipeline.getSuggestions(query, namespace);
|
|
8344
|
+
} catch (err) {
|
|
8345
|
+
throw wrapError(err, "RETRIEVAL_FAILED");
|
|
8346
|
+
}
|
|
7190
8347
|
}
|
|
7191
8348
|
};
|
|
7192
8349
|
|
|
@@ -7196,8 +8353,8 @@ var DocumentParser = class {
|
|
|
7196
8353
|
* Extract text from a File or Buffer based on its type.
|
|
7197
8354
|
*/
|
|
7198
8355
|
static async parse(file, fileName, mimeType) {
|
|
7199
|
-
var
|
|
7200
|
-
const extension = (
|
|
8356
|
+
var _a2;
|
|
8357
|
+
const extension = (_a2 = fileName.split(".").pop()) == null ? void 0 : _a2.toLowerCase();
|
|
7201
8358
|
if (extension === "txt" || extension === "md" || mimeType === "text/plain" || mimeType === "text/markdown") {
|
|
7202
8359
|
return this.readAsText(file);
|
|
7203
8360
|
}
|
|
@@ -7249,7 +8406,496 @@ var DocumentParser = class {
|
|
|
7249
8406
|
}
|
|
7250
8407
|
};
|
|
7251
8408
|
|
|
8409
|
+
// src/core/DatabaseStorage.ts
|
|
8410
|
+
import * as fs from "fs";
|
|
8411
|
+
import * as path from "path";
|
|
8412
|
+
var DatabaseStorage = class {
|
|
8413
|
+
constructor(config) {
|
|
8414
|
+
// Dynamic PG Pool
|
|
8415
|
+
this.pgPool = null;
|
|
8416
|
+
// Dynamic MongoDB Client
|
|
8417
|
+
this.mongoClient = null;
|
|
8418
|
+
this.mongoDbName = "";
|
|
8419
|
+
// Filesystem fallback configuration
|
|
8420
|
+
this.fallbackDir = path.join(process.cwd(), ".retrivora");
|
|
8421
|
+
this.historyFile = path.join(this.fallbackDir, "history.json");
|
|
8422
|
+
this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
|
|
8423
|
+
var _a2, _b, _c, _d, _e;
|
|
8424
|
+
this.config = config;
|
|
8425
|
+
this.provider = ((_a2 = config.vectorDb) == null ? void 0 : _a2.provider) || "fs";
|
|
8426
|
+
const rawHistoryTable = ((_c = (_b = config.rag) == null ? void 0 : _b.history) == null ? void 0 : _c.tableName) || "retrivora_history";
|
|
8427
|
+
const rawFeedbackTable = ((_e = (_d = config.rag) == null ? void 0 : _d.feedback) == null ? void 0 : _e.tableName) || "retrivora_feedback";
|
|
8428
|
+
this.historyTableName = rawHistoryTable.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
8429
|
+
this.feedbackTableName = rawFeedbackTable.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
8430
|
+
}
|
|
8431
|
+
/**
|
|
8432
|
+
* Asserts the user is running a valid Premium/Enterprise license in production.
|
|
8433
|
+
* In non-production (development / test) environments this is a complete no-op —
|
|
8434
|
+
* all History and Feedback features are freely accessible for local development.
|
|
8435
|
+
*/
|
|
8436
|
+
checkPremiumSubscription() {
|
|
8437
|
+
if (process.env.NODE_ENV !== "production") {
|
|
8438
|
+
return;
|
|
8439
|
+
}
|
|
8440
|
+
if (!this.config.licenseKey) {
|
|
8441
|
+
throw new Error(
|
|
8442
|
+
"[Retrivora SDK] Chat History and Feedback features require a Premium or Enterprise license key in production."
|
|
8443
|
+
);
|
|
8444
|
+
}
|
|
8445
|
+
try {
|
|
8446
|
+
const payload = LicenseVerifier.verify(this.config.licenseKey, this.config.projectId);
|
|
8447
|
+
const tier = (payload.tier || "").toLowerCase();
|
|
8448
|
+
if (tier !== "premium" && tier !== "enterprise" && tier !== "growth" && tier !== "pro" && tier !== "developer_pro") {
|
|
8449
|
+
throw new Error(
|
|
8450
|
+
`[Retrivora SDK] Chat History and Feedback features are not available on the "${tier}" tier. Please upgrade to a Developer Pro or Enterprise plan.`
|
|
8451
|
+
);
|
|
8452
|
+
}
|
|
8453
|
+
} catch (err) {
|
|
8454
|
+
throw new Error(
|
|
8455
|
+
`[Retrivora SDK] Subscription check failed: ${err instanceof Error ? err.message : String(err)}`
|
|
8456
|
+
);
|
|
8457
|
+
}
|
|
8458
|
+
}
|
|
8459
|
+
checkHistoryEnabled() {
|
|
8460
|
+
var _a2, _b;
|
|
8461
|
+
this.checkPremiumSubscription();
|
|
8462
|
+
if (((_b = (_a2 = this.config.rag) == null ? void 0 : _a2.history) == null ? void 0 : _b.enabled) === false) {
|
|
8463
|
+
throw new Error("[Retrivora SDK] Chat History is disabled in RagConfig.");
|
|
8464
|
+
}
|
|
8465
|
+
}
|
|
8466
|
+
checkFeedbackEnabled() {
|
|
8467
|
+
var _a2, _b;
|
|
8468
|
+
this.checkPremiumSubscription();
|
|
8469
|
+
if (((_b = (_a2 = this.config.rag) == null ? void 0 : _a2.feedback) == null ? void 0 : _b.enabled) === false) {
|
|
8470
|
+
throw new Error("[Retrivora SDK] User Feedback is disabled in RagConfig.");
|
|
8471
|
+
}
|
|
8472
|
+
}
|
|
8473
|
+
async getPGPool() {
|
|
8474
|
+
const globalKey = `__retrivora_pg_pool_${this.config.projectId}`;
|
|
8475
|
+
const _g2 = global;
|
|
8476
|
+
if (_g2[globalKey]) {
|
|
8477
|
+
this.pgPool = _g2[globalKey];
|
|
8478
|
+
return this.pgPool;
|
|
8479
|
+
}
|
|
8480
|
+
const opts = this.config.vectorDb.options;
|
|
8481
|
+
if (!opts.connectionString) {
|
|
8482
|
+
throw new Error("[DatabaseStorage] pg connectionString option is missing.");
|
|
8483
|
+
}
|
|
8484
|
+
const { Pool: Pool3 } = await import("pg");
|
|
8485
|
+
this.pgPool = new Pool3({ connectionString: opts.connectionString });
|
|
8486
|
+
_g2[globalKey] = this.pgPool;
|
|
8487
|
+
const client = await this.pgPool.connect();
|
|
8488
|
+
try {
|
|
8489
|
+
await client.query(`
|
|
8490
|
+
CREATE TABLE IF NOT EXISTS ${this.historyTableName} (
|
|
8491
|
+
id TEXT PRIMARY KEY,
|
|
8492
|
+
session_id TEXT NOT NULL,
|
|
8493
|
+
role TEXT NOT NULL,
|
|
8494
|
+
content TEXT NOT NULL,
|
|
8495
|
+
sources JSONB,
|
|
8496
|
+
ui_transformation JSONB,
|
|
8497
|
+
trace JSONB,
|
|
8498
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
8499
|
+
)
|
|
8500
|
+
`);
|
|
8501
|
+
await client.query(`
|
|
8502
|
+
CREATE TABLE IF NOT EXISTS ${this.feedbackTableName} (
|
|
8503
|
+
id TEXT PRIMARY KEY,
|
|
8504
|
+
message_id TEXT NOT NULL UNIQUE,
|
|
8505
|
+
session_id TEXT NOT NULL,
|
|
8506
|
+
rating TEXT NOT NULL,
|
|
8507
|
+
comment TEXT,
|
|
8508
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
8509
|
+
)
|
|
8510
|
+
`);
|
|
8511
|
+
} finally {
|
|
8512
|
+
client.release();
|
|
8513
|
+
}
|
|
8514
|
+
return this.pgPool;
|
|
8515
|
+
}
|
|
8516
|
+
async getMongoClient() {
|
|
8517
|
+
const globalKey = `__retrivora_mongo_client_${this.config.projectId}`;
|
|
8518
|
+
const _g2 = global;
|
|
8519
|
+
if (_g2[globalKey]) {
|
|
8520
|
+
this.mongoClient = _g2[globalKey];
|
|
8521
|
+
this.mongoDbName = this.config.vectorDb.options.database;
|
|
8522
|
+
return this.mongoClient;
|
|
8523
|
+
}
|
|
8524
|
+
const opts = this.config.vectorDb.options;
|
|
8525
|
+
if (!opts.uri || !opts.database) {
|
|
8526
|
+
throw new Error("[DatabaseStorage] mongo uri and database options are missing.");
|
|
8527
|
+
}
|
|
8528
|
+
const { MongoClient: MongoClient2 } = await import("mongodb");
|
|
8529
|
+
this.mongoClient = new MongoClient2(opts.uri);
|
|
8530
|
+
await this.mongoClient.connect();
|
|
8531
|
+
_g2[globalKey] = this.mongoClient;
|
|
8532
|
+
this.mongoDbName = opts.database;
|
|
8533
|
+
return this.mongoClient;
|
|
8534
|
+
}
|
|
8535
|
+
getMongoDb() {
|
|
8536
|
+
return this.mongoClient.db(this.mongoDbName);
|
|
8537
|
+
}
|
|
8538
|
+
// Helper for FS fallback reads
|
|
8539
|
+
readLocalFile(filePath) {
|
|
8540
|
+
if (!fs.existsSync(filePath)) return [];
|
|
8541
|
+
try {
|
|
8542
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
8543
|
+
return JSON.parse(raw) || [];
|
|
8544
|
+
} catch (e) {
|
|
8545
|
+
return [];
|
|
8546
|
+
}
|
|
8547
|
+
}
|
|
8548
|
+
// Helper for FS fallback writes
|
|
8549
|
+
writeLocalFile(filePath, data) {
|
|
8550
|
+
if (!fs.existsSync(this.fallbackDir)) {
|
|
8551
|
+
fs.mkdirSync(this.fallbackDir, { recursive: true });
|
|
8552
|
+
}
|
|
8553
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
8554
|
+
}
|
|
8555
|
+
// ─── Message History Operations ──────────────────────────────────────────
|
|
8556
|
+
async saveMessage(sessionId, message) {
|
|
8557
|
+
this.checkHistoryEnabled();
|
|
8558
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8559
|
+
const msgId = message.id || `msg_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
8560
|
+
if (this.provider === "postgresql" || this.provider === "pgvector") {
|
|
8561
|
+
const pool = await this.getPGPool();
|
|
8562
|
+
await pool.query(
|
|
8563
|
+
`INSERT INTO ${this.historyTableName} (id, session_id, role, content, sources, ui_transformation, trace, created_at)
|
|
8564
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
8565
|
+
ON CONFLICT (id) DO UPDATE
|
|
8566
|
+
SET content = EXCLUDED.content, sources = EXCLUDED.sources, ui_transformation = EXCLUDED.ui_transformation, trace = EXCLUDED.trace`,
|
|
8567
|
+
[
|
|
8568
|
+
msgId,
|
|
8569
|
+
sessionId,
|
|
8570
|
+
message.role,
|
|
8571
|
+
message.content,
|
|
8572
|
+
message.sources ? JSON.stringify(message.sources) : null,
|
|
8573
|
+
message.uiTransformation ? JSON.stringify(message.uiTransformation) : null,
|
|
8574
|
+
message.trace ? JSON.stringify(message.trace) : null,
|
|
8575
|
+
createdAt
|
|
8576
|
+
]
|
|
8577
|
+
);
|
|
8578
|
+
} else if (this.provider === "mongodb") {
|
|
8579
|
+
await this.getMongoClient();
|
|
8580
|
+
const db = this.getMongoDb();
|
|
8581
|
+
const col = db.collection(this.historyTableName);
|
|
8582
|
+
await col.updateOne(
|
|
8583
|
+
{ id: msgId },
|
|
8584
|
+
{
|
|
8585
|
+
$set: {
|
|
8586
|
+
id: msgId,
|
|
8587
|
+
session_id: sessionId,
|
|
8588
|
+
role: message.role,
|
|
8589
|
+
content: message.content,
|
|
8590
|
+
sources: message.sources || null,
|
|
8591
|
+
ui_transformation: message.uiTransformation || null,
|
|
8592
|
+
trace: message.trace || null,
|
|
8593
|
+
created_at: createdAt
|
|
8594
|
+
}
|
|
8595
|
+
},
|
|
8596
|
+
{ upsert: true }
|
|
8597
|
+
);
|
|
8598
|
+
} else {
|
|
8599
|
+
const history = this.readLocalFile(this.historyFile);
|
|
8600
|
+
const index = history.findIndex((h) => h.id === msgId);
|
|
8601
|
+
const item = {
|
|
8602
|
+
id: msgId,
|
|
8603
|
+
session_id: sessionId,
|
|
8604
|
+
role: message.role,
|
|
8605
|
+
content: message.content,
|
|
8606
|
+
sources: message.sources || null,
|
|
8607
|
+
ui_transformation: message.uiTransformation || null,
|
|
8608
|
+
trace: message.trace || null,
|
|
8609
|
+
created_at: createdAt
|
|
8610
|
+
};
|
|
8611
|
+
if (index !== -1) {
|
|
8612
|
+
history[index] = item;
|
|
8613
|
+
} else {
|
|
8614
|
+
history.push(item);
|
|
8615
|
+
}
|
|
8616
|
+
this.writeLocalFile(this.historyFile, history);
|
|
8617
|
+
}
|
|
8618
|
+
}
|
|
8619
|
+
async getHistory(sessionId) {
|
|
8620
|
+
this.checkHistoryEnabled();
|
|
8621
|
+
if (this.provider === "postgresql" || this.provider === "pgvector") {
|
|
8622
|
+
const pool = await this.getPGPool();
|
|
8623
|
+
const res = await pool.query(
|
|
8624
|
+
`SELECT id, role, content, sources, ui_transformation as "uiTransformation", trace, created_at as "createdAt"
|
|
8625
|
+
FROM ${this.historyTableName}
|
|
8626
|
+
WHERE session_id = $1
|
|
8627
|
+
ORDER BY created_at ASC`,
|
|
8628
|
+
[sessionId]
|
|
8629
|
+
);
|
|
8630
|
+
return res.rows;
|
|
8631
|
+
} else if (this.provider === "mongodb") {
|
|
8632
|
+
await this.getMongoClient();
|
|
8633
|
+
const db = this.getMongoDb();
|
|
8634
|
+
const col = db.collection(this.historyTableName);
|
|
8635
|
+
const items = await col.find({ session_id: sessionId }).sort({ created_at: 1 }).toArray();
|
|
8636
|
+
return items.map((item) => ({
|
|
8637
|
+
id: item.id,
|
|
8638
|
+
role: item.role,
|
|
8639
|
+
content: item.content,
|
|
8640
|
+
sources: item.sources,
|
|
8641
|
+
uiTransformation: item.ui_transformation,
|
|
8642
|
+
trace: item.trace,
|
|
8643
|
+
createdAt: item.created_at
|
|
8644
|
+
}));
|
|
8645
|
+
} else {
|
|
8646
|
+
const history = this.readLocalFile(this.historyFile);
|
|
8647
|
+
return history.filter((h) => h.session_id === sessionId).sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime());
|
|
8648
|
+
}
|
|
8649
|
+
}
|
|
8650
|
+
async clearHistory(sessionId) {
|
|
8651
|
+
this.checkHistoryEnabled();
|
|
8652
|
+
if (this.provider === "postgresql" || this.provider === "pgvector") {
|
|
8653
|
+
const pool = await this.getPGPool();
|
|
8654
|
+
await pool.query(`DELETE FROM ${this.historyTableName} WHERE session_id = $1`, [sessionId]);
|
|
8655
|
+
await pool.query(`DELETE FROM ${this.feedbackTableName} WHERE session_id = $1`, [sessionId]);
|
|
8656
|
+
} else if (this.provider === "mongodb") {
|
|
8657
|
+
await this.getMongoClient();
|
|
8658
|
+
const db = this.getMongoDb();
|
|
8659
|
+
await db.collection(this.historyTableName).deleteMany({ session_id: sessionId });
|
|
8660
|
+
await db.collection(this.feedbackTableName).deleteMany({ session_id: sessionId });
|
|
8661
|
+
} else {
|
|
8662
|
+
const history = this.readLocalFile(this.historyFile);
|
|
8663
|
+
const filteredHistory = history.filter((h) => h.session_id !== sessionId);
|
|
8664
|
+
this.writeLocalFile(this.historyFile, filteredHistory);
|
|
8665
|
+
const feedback = this.readLocalFile(this.feedbackFile);
|
|
8666
|
+
const filteredFeedback = feedback.filter((f) => f.session_id !== sessionId);
|
|
8667
|
+
this.writeLocalFile(this.feedbackFile, filteredFeedback);
|
|
8668
|
+
}
|
|
8669
|
+
}
|
|
8670
|
+
async listSessions() {
|
|
8671
|
+
this.checkHistoryEnabled();
|
|
8672
|
+
if (this.provider === "postgresql" || this.provider === "pgvector") {
|
|
8673
|
+
const pool = await this.getPGPool();
|
|
8674
|
+
const res = await pool.query(`SELECT DISTINCT session_id FROM ${this.historyTableName}`);
|
|
8675
|
+
return res.rows.map((r) => r.session_id);
|
|
8676
|
+
} else if (this.provider === "mongodb") {
|
|
8677
|
+
await this.getMongoClient();
|
|
8678
|
+
const db = this.getMongoDb();
|
|
8679
|
+
return db.collection(this.historyTableName).distinct("session_id");
|
|
8680
|
+
} else {
|
|
8681
|
+
const history = this.readLocalFile(this.historyFile);
|
|
8682
|
+
const sessions = new Set(history.map((h) => h.session_id));
|
|
8683
|
+
return Array.from(sessions);
|
|
8684
|
+
}
|
|
8685
|
+
}
|
|
8686
|
+
// ─── Feedback Operations ──────────────────────────────────────────────────
|
|
8687
|
+
async saveFeedback(feedback) {
|
|
8688
|
+
this.checkFeedbackEnabled();
|
|
8689
|
+
const id = `fb_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
8690
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8691
|
+
if (this.provider === "postgresql" || this.provider === "pgvector") {
|
|
8692
|
+
const pool = await this.getPGPool();
|
|
8693
|
+
await pool.query(
|
|
8694
|
+
`INSERT INTO ${this.feedbackTableName} (id, message_id, session_id, rating, comment, created_at)
|
|
8695
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
8696
|
+
ON CONFLICT (message_id) DO UPDATE
|
|
8697
|
+
SET rating = EXCLUDED.rating, comment = EXCLUDED.comment`,
|
|
8698
|
+
[id, feedback.messageId, feedback.sessionId, feedback.rating, feedback.comment || null, createdAt]
|
|
8699
|
+
);
|
|
8700
|
+
} else if (this.provider === "mongodb") {
|
|
8701
|
+
await this.getMongoClient();
|
|
8702
|
+
const db = this.getMongoDb();
|
|
8703
|
+
const col = db.collection(this.feedbackTableName);
|
|
8704
|
+
await col.updateOne(
|
|
8705
|
+
{ message_id: feedback.messageId },
|
|
8706
|
+
{
|
|
8707
|
+
$set: {
|
|
8708
|
+
id,
|
|
8709
|
+
message_id: feedback.messageId,
|
|
8710
|
+
session_id: feedback.sessionId,
|
|
8711
|
+
rating: feedback.rating,
|
|
8712
|
+
comment: feedback.comment || null,
|
|
8713
|
+
created_at: createdAt
|
|
8714
|
+
}
|
|
8715
|
+
},
|
|
8716
|
+
{ upsert: true }
|
|
8717
|
+
);
|
|
8718
|
+
} else {
|
|
8719
|
+
const list = this.readLocalFile(this.feedbackFile);
|
|
8720
|
+
const index = list.findIndex((f) => f.message_id === feedback.messageId);
|
|
8721
|
+
const item = {
|
|
8722
|
+
id,
|
|
8723
|
+
message_id: feedback.messageId,
|
|
8724
|
+
session_id: feedback.sessionId,
|
|
8725
|
+
rating: feedback.rating,
|
|
8726
|
+
comment: feedback.comment || null,
|
|
8727
|
+
created_at: createdAt
|
|
8728
|
+
};
|
|
8729
|
+
if (index !== -1) {
|
|
8730
|
+
list[index] = item;
|
|
8731
|
+
} else {
|
|
8732
|
+
list.push(item);
|
|
8733
|
+
}
|
|
8734
|
+
this.writeLocalFile(this.feedbackFile, list);
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
async getFeedback(messageId) {
|
|
8738
|
+
this.checkFeedbackEnabled();
|
|
8739
|
+
if (this.provider === "postgresql" || this.provider === "pgvector") {
|
|
8740
|
+
const pool = await this.getPGPool();
|
|
8741
|
+
const res = await pool.query(
|
|
8742
|
+
`SELECT id, message_id as "messageId", session_id as "sessionId", rating, comment, created_at as "createdAt"
|
|
8743
|
+
FROM ${this.feedbackTableName}
|
|
8744
|
+
WHERE message_id = $1`,
|
|
8745
|
+
[messageId]
|
|
8746
|
+
);
|
|
8747
|
+
return res.rows[0] || null;
|
|
8748
|
+
} else if (this.provider === "mongodb") {
|
|
8749
|
+
await this.getMongoClient();
|
|
8750
|
+
const db = this.getMongoDb();
|
|
8751
|
+
const col = db.collection(this.feedbackTableName);
|
|
8752
|
+
const item = await col.findOne({ message_id: messageId });
|
|
8753
|
+
if (!item) return null;
|
|
8754
|
+
return {
|
|
8755
|
+
id: item.id,
|
|
8756
|
+
messageId: item.message_id,
|
|
8757
|
+
sessionId: item.session_id,
|
|
8758
|
+
rating: item.rating,
|
|
8759
|
+
comment: item.comment,
|
|
8760
|
+
createdAt: item.created_at
|
|
8761
|
+
};
|
|
8762
|
+
} else {
|
|
8763
|
+
const list = this.readLocalFile(this.feedbackFile);
|
|
8764
|
+
return list.find((f) => f.message_id === messageId) || null;
|
|
8765
|
+
}
|
|
8766
|
+
}
|
|
8767
|
+
async listFeedback() {
|
|
8768
|
+
this.checkFeedbackEnabled();
|
|
8769
|
+
if (this.provider === "postgresql" || this.provider === "pgvector") {
|
|
8770
|
+
const pool = await this.getPGPool();
|
|
8771
|
+
const res = await pool.query(
|
|
8772
|
+
`SELECT id, message_id as "messageId", session_id as "sessionId", rating, comment, created_at as "createdAt"
|
|
8773
|
+
FROM ${this.feedbackTableName}
|
|
8774
|
+
ORDER BY created_at DESC`
|
|
8775
|
+
);
|
|
8776
|
+
return res.rows;
|
|
8777
|
+
} else if (this.provider === "mongodb") {
|
|
8778
|
+
await this.getMongoClient();
|
|
8779
|
+
const db = this.getMongoDb();
|
|
8780
|
+
const col = db.collection(this.feedbackTableName);
|
|
8781
|
+
const items = await col.find({}).sort({ created_at: -1 }).toArray();
|
|
8782
|
+
return items.map((item) => ({
|
|
8783
|
+
id: item.id,
|
|
8784
|
+
messageId: item.message_id,
|
|
8785
|
+
sessionId: item.session_id,
|
|
8786
|
+
rating: item.rating,
|
|
8787
|
+
comment: item.comment,
|
|
8788
|
+
createdAt: item.created_at
|
|
8789
|
+
}));
|
|
8790
|
+
} else {
|
|
8791
|
+
const list = this.readLocalFile(this.feedbackFile);
|
|
8792
|
+
return [...list].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
|
8793
|
+
}
|
|
8794
|
+
}
|
|
8795
|
+
async disconnect() {
|
|
8796
|
+
if (this.pgPool) {
|
|
8797
|
+
await this.pgPool.end();
|
|
8798
|
+
this.pgPool = null;
|
|
8799
|
+
}
|
|
8800
|
+
if (this.mongoClient) {
|
|
8801
|
+
await this.mongoClient.close();
|
|
8802
|
+
this.mongoClient = null;
|
|
8803
|
+
}
|
|
8804
|
+
}
|
|
8805
|
+
};
|
|
8806
|
+
|
|
7252
8807
|
// src/handlers/index.ts
|
|
8808
|
+
async function checkAuth(req, onAuthorize) {
|
|
8809
|
+
if (!onAuthorize) return null;
|
|
8810
|
+
try {
|
|
8811
|
+
const res = await onAuthorize(req);
|
|
8812
|
+
if (res === false) {
|
|
8813
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
8814
|
+
}
|
|
8815
|
+
if (res instanceof Response) {
|
|
8816
|
+
return res;
|
|
8817
|
+
}
|
|
8818
|
+
return null;
|
|
8819
|
+
} catch (err) {
|
|
8820
|
+
const msg = err instanceof Error ? err.message : "Authorization check failed";
|
|
8821
|
+
return NextResponse.json({ error: msg }, { status: 401 });
|
|
8822
|
+
}
|
|
8823
|
+
}
|
|
8824
|
+
var RateLimiter = class {
|
|
8825
|
+
constructor(windowMs = 6e4, max = 30) {
|
|
8826
|
+
this.hits = /* @__PURE__ */ new Map();
|
|
8827
|
+
this.windowMs = windowMs;
|
|
8828
|
+
this.max = max;
|
|
8829
|
+
}
|
|
8830
|
+
/** Returns true if the request should be allowed, false if rate-limited. */
|
|
8831
|
+
allow(key) {
|
|
8832
|
+
const now = Date.now();
|
|
8833
|
+
const cutoff = now - this.windowMs;
|
|
8834
|
+
const existing = (this.hits.get(key) || []).filter((t) => t > cutoff);
|
|
8835
|
+
existing.push(now);
|
|
8836
|
+
this.hits.set(key, existing);
|
|
8837
|
+
if (this.hits.size > 5e3) {
|
|
8838
|
+
for (const [k, timestamps] of this.hits.entries()) {
|
|
8839
|
+
if (timestamps.every((t) => t <= cutoff)) this.hits.delete(k);
|
|
8840
|
+
}
|
|
8841
|
+
}
|
|
8842
|
+
return existing.length <= this.max;
|
|
8843
|
+
}
|
|
8844
|
+
retryAfterSec(key) {
|
|
8845
|
+
const now = Date.now();
|
|
8846
|
+
const cutoff = now - this.windowMs;
|
|
8847
|
+
const existing = (this.hits.get(key) || []).filter((t) => t > cutoff);
|
|
8848
|
+
if (existing.length === 0) return 0;
|
|
8849
|
+
return Math.ceil((existing[0] + this.windowMs - now) / 1e3);
|
|
8850
|
+
}
|
|
8851
|
+
};
|
|
8852
|
+
var _g = global;
|
|
8853
|
+
var _a;
|
|
8854
|
+
var rateLimiter = (_a = _g.__retrivoraRateLimiter) != null ? _a : _g.__retrivoraRateLimiter = new RateLimiter(6e4, 30);
|
|
8855
|
+
function getRateLimitKey(req) {
|
|
8856
|
+
var _a2, _b;
|
|
8857
|
+
const ip = ((_b = (_a2 = req.headers.get("x-forwarded-for")) == null ? void 0 : _a2.split(",")[0]) == null ? void 0 : _b.trim()) || req.headers.get("x-real-ip") || "127.0.0.1";
|
|
8858
|
+
return `ip:${ip}`;
|
|
8859
|
+
}
|
|
8860
|
+
function checkRateLimit(req) {
|
|
8861
|
+
const key = getRateLimitKey(req);
|
|
8862
|
+
if (!rateLimiter.allow(key)) {
|
|
8863
|
+
const retryAfter = rateLimiter.retryAfterSec(key);
|
|
8864
|
+
return new Response(
|
|
8865
|
+
JSON.stringify({ error: { code: "RATE_LIMITED", message: "Too many requests. Please slow down." } }),
|
|
8866
|
+
{
|
|
8867
|
+
status: 429,
|
|
8868
|
+
headers: {
|
|
8869
|
+
"Content-Type": "application/json",
|
|
8870
|
+
"Retry-After": String(retryAfter),
|
|
8871
|
+
"X-RateLimit-Limit": "30",
|
|
8872
|
+
"X-RateLimit-Window": "60"
|
|
8873
|
+
}
|
|
8874
|
+
}
|
|
8875
|
+
);
|
|
8876
|
+
}
|
|
8877
|
+
return null;
|
|
8878
|
+
}
|
|
8879
|
+
var MAX_MESSAGE_LENGTH = 8e3;
|
|
8880
|
+
function sanitizeInput(raw) {
|
|
8881
|
+
const stripped = raw.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, "");
|
|
8882
|
+
const trimmed = stripped.trim();
|
|
8883
|
+
if (!trimmed) {
|
|
8884
|
+
return { ok: false, error: "message must not be empty" };
|
|
8885
|
+
}
|
|
8886
|
+
if (trimmed.length > MAX_MESSAGE_LENGTH) {
|
|
8887
|
+
return { ok: false, error: `message must be \u2264 ${MAX_MESSAGE_LENGTH} characters` };
|
|
8888
|
+
}
|
|
8889
|
+
return { ok: true, value: trimmed };
|
|
8890
|
+
}
|
|
8891
|
+
function getOrCreatePlugin(configOrPlugin) {
|
|
8892
|
+
if (configOrPlugin instanceof VectorPlugin) return configOrPlugin;
|
|
8893
|
+
const cacheKey = "__retrivoraPlugin_" + JSON.stringify(configOrPlugin != null ? configOrPlugin : {}).slice(0, 64);
|
|
8894
|
+
if (!_g[cacheKey]) {
|
|
8895
|
+
_g[cacheKey] = new VectorPlugin(configOrPlugin);
|
|
8896
|
+
}
|
|
8897
|
+
return _g[cacheKey];
|
|
8898
|
+
}
|
|
7253
8899
|
function sseFrame(payload) {
|
|
7254
8900
|
return `data: ${JSON.stringify(payload)}
|
|
7255
8901
|
|
|
@@ -7287,47 +8933,88 @@ var SSE_HEADERS = {
|
|
|
7287
8933
|
"X-Accel-Buffering": "no"
|
|
7288
8934
|
// Disable Nginx buffering for streaming
|
|
7289
8935
|
};
|
|
7290
|
-
function createChatHandler(configOrPlugin) {
|
|
7291
|
-
const plugin =
|
|
8936
|
+
function createChatHandler(configOrPlugin, options) {
|
|
8937
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
8938
|
+
const storage = new DatabaseStorage(plugin.getConfig());
|
|
8939
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
7292
8940
|
return async function POST(req) {
|
|
8941
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
8942
|
+
if (authResult) return authResult;
|
|
8943
|
+
const rateLimited = checkRateLimit(req);
|
|
8944
|
+
if (rateLimited) return rateLimited;
|
|
7293
8945
|
try {
|
|
7294
8946
|
const body = await req.json();
|
|
7295
|
-
const { message, history = [], namespace } = body;
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
8947
|
+
const { message: rawMessage, history = [], namespace, sessionId = "default", messageId, userMessageId } = body;
|
|
8948
|
+
const sanitized = sanitizeInput(rawMessage != null ? rawMessage : "");
|
|
8949
|
+
if (!sanitized.ok) {
|
|
8950
|
+
return NextResponse.json({ error: { code: "INVALID_INPUT", message: sanitized.error } }, { status: 400 });
|
|
8951
|
+
}
|
|
8952
|
+
const message = sanitized.value;
|
|
8953
|
+
const userMsgId = userMessageId || `user_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
8954
|
+
await storage.saveMessage(sessionId, {
|
|
8955
|
+
id: userMsgId,
|
|
8956
|
+
role: "user",
|
|
8957
|
+
content: message
|
|
8958
|
+
}).catch((err) => console.warn("[createChatHandler] Failed to save user message:", err));
|
|
7299
8959
|
const result = await plugin.chat(message, history, namespace);
|
|
7300
|
-
|
|
8960
|
+
const assistantMsgId = messageId || `assistant_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
8961
|
+
await storage.saveMessage(sessionId, {
|
|
8962
|
+
id: assistantMsgId,
|
|
8963
|
+
role: "assistant",
|
|
8964
|
+
content: result.reply,
|
|
8965
|
+
sources: result.sources,
|
|
8966
|
+
uiTransformation: result.ui_transformation,
|
|
8967
|
+
trace: result.trace
|
|
8968
|
+
}).catch((err) => console.warn("[createChatHandler] Failed to save assistant message:", err));
|
|
8969
|
+
return NextResponse.json(__spreadProps(__spreadValues({}, result), {
|
|
8970
|
+
messageId: assistantMsgId,
|
|
8971
|
+
userMessageId: userMsgId
|
|
8972
|
+
}));
|
|
7301
8973
|
} catch (err) {
|
|
7302
8974
|
const message = err instanceof Error ? err.message : "Internal server error";
|
|
7303
8975
|
return NextResponse.json({ error: message }, { status: 500 });
|
|
7304
8976
|
}
|
|
7305
8977
|
};
|
|
7306
8978
|
}
|
|
7307
|
-
function createStreamHandler(configOrPlugin) {
|
|
7308
|
-
const plugin =
|
|
8979
|
+
function createStreamHandler(configOrPlugin, options) {
|
|
8980
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
8981
|
+
const storage = new DatabaseStorage(plugin.getConfig());
|
|
8982
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
7309
8983
|
return async function POST(req) {
|
|
8984
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
8985
|
+
if (authResult) return authResult;
|
|
8986
|
+
const rateLimited = checkRateLimit(req);
|
|
8987
|
+
if (rateLimited) return rateLimited;
|
|
7310
8988
|
let body;
|
|
7311
8989
|
try {
|
|
7312
8990
|
body = await req.json();
|
|
7313
8991
|
} catch (e) {
|
|
7314
|
-
return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
|
|
8992
|
+
return new Response(JSON.stringify({ error: { code: "INVALID_JSON", message: "Invalid JSON body" } }), {
|
|
7315
8993
|
status: 400,
|
|
7316
8994
|
headers: { "Content-Type": "application/json" }
|
|
7317
8995
|
});
|
|
7318
8996
|
}
|
|
7319
|
-
const { message, history = [], namespace } = body;
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
8997
|
+
const { message: rawMessage, history = [], namespace, sessionId = "default", messageId, userMessageId } = body;
|
|
8998
|
+
const sanitized = sanitizeInput(rawMessage != null ? rawMessage : "");
|
|
8999
|
+
if (!sanitized.ok) {
|
|
9000
|
+
return new Response(
|
|
9001
|
+
JSON.stringify({ error: { code: "INVALID_INPUT", message: sanitized.error } }),
|
|
9002
|
+
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
9003
|
+
);
|
|
7325
9004
|
}
|
|
9005
|
+
const message = sanitized.value;
|
|
9006
|
+
const userMsgId = userMessageId || `user_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
9007
|
+
await storage.saveMessage(sessionId, {
|
|
9008
|
+
id: userMsgId,
|
|
9009
|
+
role: "user",
|
|
9010
|
+
content: message
|
|
9011
|
+
}).catch((err) => console.warn("[createStreamHandler] Failed to save user message:", err));
|
|
9012
|
+
const assistantMsgId = messageId || `assistant_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
7326
9013
|
const encoder = new TextEncoder();
|
|
7327
9014
|
let isActive = true;
|
|
7328
9015
|
const stream = new ReadableStream({
|
|
7329
9016
|
async start(controller) {
|
|
7330
|
-
var
|
|
9017
|
+
var _a2;
|
|
7331
9018
|
const enqueue = (text) => {
|
|
7332
9019
|
if (!isActive) return;
|
|
7333
9020
|
try {
|
|
@@ -7338,11 +9025,13 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7338
9025
|
};
|
|
7339
9026
|
try {
|
|
7340
9027
|
const pipelineStream = plugin.chatStream(message, history, namespace);
|
|
9028
|
+
let fullReply = "";
|
|
7341
9029
|
try {
|
|
7342
9030
|
for (var iter = __forAwait(pipelineStream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
7343
9031
|
const chunk = temp.value;
|
|
7344
9032
|
if (!isActive) break;
|
|
7345
9033
|
if (typeof chunk === "string") {
|
|
9034
|
+
fullReply += chunk;
|
|
7346
9035
|
enqueue(sseTextFrame(chunk));
|
|
7347
9036
|
} else if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "thinking") {
|
|
7348
9037
|
enqueue(`data: ${JSON.stringify(chunk)}
|
|
@@ -7355,9 +9044,10 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7355
9044
|
if (responseChunk == null ? void 0 : responseChunk.trace) {
|
|
7356
9045
|
enqueue(sseObservabilityFrame(responseChunk.trace));
|
|
7357
9046
|
}
|
|
9047
|
+
let uiTransformation = responseChunk == null ? void 0 : responseChunk.ui_transformation;
|
|
7358
9048
|
if (sources.length > 0) {
|
|
7359
9049
|
try {
|
|
7360
|
-
|
|
9050
|
+
uiTransformation = (_a2 = responseChunk == null ? void 0 : responseChunk.ui_transformation) != null ? _a2 : UITransformer.transform(message, sources, plugin.getConfig());
|
|
7361
9051
|
if (uiTransformation) {
|
|
7362
9052
|
enqueue(sseUIFrame(uiTransformation));
|
|
7363
9053
|
}
|
|
@@ -7365,11 +9055,22 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7365
9055
|
console.warn("[createStreamHandler] UI transformation warning:", transformError);
|
|
7366
9056
|
try {
|
|
7367
9057
|
const fallback = UITransformer.transform(message, sources, plugin.getConfig());
|
|
7368
|
-
if (fallback)
|
|
9058
|
+
if (fallback) {
|
|
9059
|
+
uiTransformation = fallback;
|
|
9060
|
+
enqueue(sseUIFrame(fallback));
|
|
9061
|
+
}
|
|
7369
9062
|
} catch (e) {
|
|
7370
9063
|
}
|
|
7371
9064
|
}
|
|
7372
9065
|
}
|
|
9066
|
+
await storage.saveMessage(sessionId, {
|
|
9067
|
+
id: assistantMsgId,
|
|
9068
|
+
role: "assistant",
|
|
9069
|
+
content: fullReply,
|
|
9070
|
+
sources,
|
|
9071
|
+
uiTransformation,
|
|
9072
|
+
trace: responseChunk == null ? void 0 : responseChunk.trace
|
|
9073
|
+
}).catch((err) => console.warn("[createStreamHandler] Failed to save assistant message:", err));
|
|
7373
9074
|
}
|
|
7374
9075
|
}
|
|
7375
9076
|
} catch (temp) {
|
|
@@ -7406,12 +9107,15 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7406
9107
|
console.log("[createStreamHandler] Stream connection closed by client:", reason);
|
|
7407
9108
|
}
|
|
7408
9109
|
});
|
|
7409
|
-
return new Response(stream, { headers: SSE_HEADERS });
|
|
9110
|
+
return new Response(stream, { headers: __spreadProps(__spreadValues({}, SSE_HEADERS), { "x-message-id": assistantMsgId, "x-user-message-id": userMsgId }) });
|
|
7410
9111
|
};
|
|
7411
9112
|
}
|
|
7412
|
-
function createIngestHandler(configOrPlugin) {
|
|
7413
|
-
const plugin =
|
|
9113
|
+
function createIngestHandler(configOrPlugin, options) {
|
|
9114
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9115
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
7414
9116
|
return async function POST(req) {
|
|
9117
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
9118
|
+
if (authResult) return authResult;
|
|
7415
9119
|
try {
|
|
7416
9120
|
const body = await req.json();
|
|
7417
9121
|
const { documents, namespace } = body;
|
|
@@ -7426,9 +9130,14 @@ function createIngestHandler(configOrPlugin) {
|
|
|
7426
9130
|
}
|
|
7427
9131
|
};
|
|
7428
9132
|
}
|
|
7429
|
-
function createHealthHandler(configOrPlugin) {
|
|
7430
|
-
const plugin =
|
|
7431
|
-
|
|
9133
|
+
function createHealthHandler(configOrPlugin, options) {
|
|
9134
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9135
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
9136
|
+
return async function GET(req) {
|
|
9137
|
+
if (req) {
|
|
9138
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
9139
|
+
if (authResult) return authResult;
|
|
9140
|
+
}
|
|
7432
9141
|
try {
|
|
7433
9142
|
const health = await plugin.checkHealth();
|
|
7434
9143
|
const status = health.allHealthy ? "ok" : "degraded";
|
|
@@ -7443,9 +9152,12 @@ function createHealthHandler(configOrPlugin) {
|
|
|
7443
9152
|
}
|
|
7444
9153
|
};
|
|
7445
9154
|
}
|
|
7446
|
-
function createUploadHandler(configOrPlugin) {
|
|
7447
|
-
const plugin =
|
|
9155
|
+
function createUploadHandler(configOrPlugin, options) {
|
|
9156
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9157
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
7448
9158
|
return async function POST(req) {
|
|
9159
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
9160
|
+
if (authResult) return authResult;
|
|
7449
9161
|
try {
|
|
7450
9162
|
const formData = await req.formData();
|
|
7451
9163
|
const files = formData.getAll("files");
|
|
@@ -7519,9 +9231,12 @@ function createUploadHandler(configOrPlugin) {
|
|
|
7519
9231
|
}
|
|
7520
9232
|
};
|
|
7521
9233
|
}
|
|
7522
|
-
function createSuggestionsHandler(configOrPlugin) {
|
|
7523
|
-
const plugin =
|
|
9234
|
+
function createSuggestionsHandler(configOrPlugin, options) {
|
|
9235
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9236
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
7524
9237
|
return async function POST(req) {
|
|
9238
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
9239
|
+
if (authResult) return authResult;
|
|
7525
9240
|
try {
|
|
7526
9241
|
const body = await req.json();
|
|
7527
9242
|
const { query, namespace } = body;
|
|
@@ -7536,10 +9251,178 @@ function createSuggestionsHandler(configOrPlugin) {
|
|
|
7536
9251
|
}
|
|
7537
9252
|
};
|
|
7538
9253
|
}
|
|
9254
|
+
function createHistoryHandler(configOrPlugin, options) {
|
|
9255
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9256
|
+
const storage = new DatabaseStorage(plugin.getConfig());
|
|
9257
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
9258
|
+
return async function handler(req) {
|
|
9259
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
9260
|
+
if (authResult) return authResult;
|
|
9261
|
+
const url = new URL(req.url);
|
|
9262
|
+
const sessionId = url.searchParams.get("sessionId") || "default";
|
|
9263
|
+
if (req.method === "GET") {
|
|
9264
|
+
try {
|
|
9265
|
+
const history = await storage.getHistory(sessionId);
|
|
9266
|
+
return NextResponse.json({ history });
|
|
9267
|
+
} catch (err) {
|
|
9268
|
+
const message = err instanceof Error ? err.message : "Failed to fetch history";
|
|
9269
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
9270
|
+
}
|
|
9271
|
+
} else if (req.method === "POST") {
|
|
9272
|
+
try {
|
|
9273
|
+
const body = await req.json().catch(() => ({}));
|
|
9274
|
+
const sid = body.sessionId || sessionId;
|
|
9275
|
+
await storage.clearHistory(sid);
|
|
9276
|
+
return NextResponse.json({ success: true });
|
|
9277
|
+
} catch (err) {
|
|
9278
|
+
const message = err instanceof Error ? err.message : "Failed to clear history";
|
|
9279
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
9280
|
+
}
|
|
9281
|
+
}
|
|
9282
|
+
return NextResponse.json({ error: "Method Not Allowed" }, { status: 405 });
|
|
9283
|
+
};
|
|
9284
|
+
}
|
|
9285
|
+
function createFeedbackHandler(configOrPlugin, options) {
|
|
9286
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9287
|
+
const storage = new DatabaseStorage(plugin.getConfig());
|
|
9288
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
9289
|
+
return async function handler(req) {
|
|
9290
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
9291
|
+
if (authResult) return authResult;
|
|
9292
|
+
if (req.method === "GET") {
|
|
9293
|
+
try {
|
|
9294
|
+
const url = new URL(req.url);
|
|
9295
|
+
const messageId = url.searchParams.get("messageId");
|
|
9296
|
+
if (!messageId) {
|
|
9297
|
+
const feedbackList = await storage.listFeedback();
|
|
9298
|
+
return NextResponse.json({ feedback: feedbackList });
|
|
9299
|
+
}
|
|
9300
|
+
const feedback = await storage.getFeedback(messageId);
|
|
9301
|
+
return NextResponse.json({ feedback });
|
|
9302
|
+
} catch (err) {
|
|
9303
|
+
const message = err instanceof Error ? err.message : "Failed to fetch feedback";
|
|
9304
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
9305
|
+
}
|
|
9306
|
+
} else if (req.method === "POST") {
|
|
9307
|
+
try {
|
|
9308
|
+
const body = await req.json();
|
|
9309
|
+
const { messageId, sessionId = "default", rating, comment } = body;
|
|
9310
|
+
if (!messageId) {
|
|
9311
|
+
return NextResponse.json({ error: "messageId is required" }, { status: 400 });
|
|
9312
|
+
}
|
|
9313
|
+
if (!rating) {
|
|
9314
|
+
return NextResponse.json({ error: "rating is required" }, { status: 400 });
|
|
9315
|
+
}
|
|
9316
|
+
await storage.saveFeedback({ messageId, sessionId, rating, comment });
|
|
9317
|
+
return NextResponse.json({ success: true });
|
|
9318
|
+
} catch (err) {
|
|
9319
|
+
const message = err instanceof Error ? err.message : "Failed to save feedback";
|
|
9320
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
9321
|
+
}
|
|
9322
|
+
}
|
|
9323
|
+
return NextResponse.json({ error: "Method Not Allowed" }, { status: 405 });
|
|
9324
|
+
};
|
|
9325
|
+
}
|
|
9326
|
+
function createSessionsHandler(configOrPlugin, options) {
|
|
9327
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9328
|
+
const storage = new DatabaseStorage(plugin.getConfig());
|
|
9329
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
9330
|
+
return async function GET(req) {
|
|
9331
|
+
if (req) {
|
|
9332
|
+
const authResult = await checkAuth(req, onAuthorize);
|
|
9333
|
+
if (authResult) return authResult;
|
|
9334
|
+
}
|
|
9335
|
+
void req;
|
|
9336
|
+
try {
|
|
9337
|
+
const sessions = await storage.listSessions();
|
|
9338
|
+
return NextResponse.json({ sessions });
|
|
9339
|
+
} catch (err) {
|
|
9340
|
+
const message = err instanceof Error ? err.message : "Failed to list sessions";
|
|
9341
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
9342
|
+
}
|
|
9343
|
+
};
|
|
9344
|
+
}
|
|
9345
|
+
function createRagHandler(configOrPlugin, options) {
|
|
9346
|
+
const plugin = getOrCreatePlugin(configOrPlugin);
|
|
9347
|
+
const onAuthorize = options == null ? void 0 : options.onAuthorize;
|
|
9348
|
+
const chatHandler = createChatHandler(plugin, { onAuthorize });
|
|
9349
|
+
const streamHandler = createStreamHandler(plugin, { onAuthorize });
|
|
9350
|
+
const uploadHandler = createUploadHandler(plugin, { onAuthorize });
|
|
9351
|
+
const healthHandler = createHealthHandler(plugin, { onAuthorize });
|
|
9352
|
+
const suggestionsHandler = createSuggestionsHandler(plugin, { onAuthorize });
|
|
9353
|
+
const historyHandler = createHistoryHandler(plugin, { onAuthorize });
|
|
9354
|
+
const feedbackHandler = createFeedbackHandler(plugin, { onAuthorize });
|
|
9355
|
+
const sessionsHandler = createSessionsHandler(plugin, { onAuthorize });
|
|
9356
|
+
async function routePostRequest(req, segment) {
|
|
9357
|
+
switch (segment) {
|
|
9358
|
+
case "chat":
|
|
9359
|
+
return streamHandler(req);
|
|
9360
|
+
case "chat-sync":
|
|
9361
|
+
return chatHandler(req);
|
|
9362
|
+
case "upload":
|
|
9363
|
+
return uploadHandler(req);
|
|
9364
|
+
case "suggestions":
|
|
9365
|
+
return suggestionsHandler(req);
|
|
9366
|
+
case "health":
|
|
9367
|
+
return healthHandler(req);
|
|
9368
|
+
case "history":
|
|
9369
|
+
case "history/clear":
|
|
9370
|
+
return historyHandler(req);
|
|
9371
|
+
case "feedback":
|
|
9372
|
+
return feedbackHandler(req);
|
|
9373
|
+
default:
|
|
9374
|
+
return NextResponse.json({ error: `Not Found: POST segment "${segment}" not supported.` }, { status: 404 });
|
|
9375
|
+
}
|
|
9376
|
+
}
|
|
9377
|
+
async function routeGetRequest(req, segment) {
|
|
9378
|
+
switch (segment) {
|
|
9379
|
+
case "health":
|
|
9380
|
+
return healthHandler(req);
|
|
9381
|
+
case "history":
|
|
9382
|
+
return historyHandler(req);
|
|
9383
|
+
case "feedback":
|
|
9384
|
+
return feedbackHandler(req);
|
|
9385
|
+
case "sessions":
|
|
9386
|
+
return sessionsHandler(req);
|
|
9387
|
+
default:
|
|
9388
|
+
return NextResponse.json({ error: `Method Not Allowed: GET segment "${segment}" not supported.` }, { status: 405 });
|
|
9389
|
+
}
|
|
9390
|
+
}
|
|
9391
|
+
async function getSegment(context) {
|
|
9392
|
+
var _a2;
|
|
9393
|
+
const resolvedParams = typeof ((_a2 = context == null ? void 0 : context.params) == null ? void 0 : _a2.then) === "function" ? await context.params : context == null ? void 0 : context.params;
|
|
9394
|
+
const segments = (resolvedParams == null ? void 0 : resolvedParams.retrivora) || [];
|
|
9395
|
+
return segments.join("/") || "chat";
|
|
9396
|
+
}
|
|
9397
|
+
return {
|
|
9398
|
+
GET: async (req, context) => {
|
|
9399
|
+
try {
|
|
9400
|
+
const segment = await getSegment(context);
|
|
9401
|
+
return await routeGetRequest(req, segment);
|
|
9402
|
+
} catch (err) {
|
|
9403
|
+
const msg = err instanceof Error ? err.message : "GET Routing failed";
|
|
9404
|
+
return NextResponse.json({ error: msg }, { status: 500 });
|
|
9405
|
+
}
|
|
9406
|
+
},
|
|
9407
|
+
POST: async (req, context) => {
|
|
9408
|
+
try {
|
|
9409
|
+
const segment = await getSegment(context);
|
|
9410
|
+
return await routePostRequest(req, segment);
|
|
9411
|
+
} catch (err) {
|
|
9412
|
+
const msg = err instanceof Error ? err.message : "POST Routing failed";
|
|
9413
|
+
return NextResponse.json({ error: msg }, { status: 500 });
|
|
9414
|
+
}
|
|
9415
|
+
}
|
|
9416
|
+
};
|
|
9417
|
+
}
|
|
7539
9418
|
export {
|
|
7540
9419
|
createChatHandler,
|
|
9420
|
+
createFeedbackHandler,
|
|
7541
9421
|
createHealthHandler,
|
|
9422
|
+
createHistoryHandler,
|
|
7542
9423
|
createIngestHandler,
|
|
9424
|
+
createRagHandler,
|
|
9425
|
+
createSessionsHandler,
|
|
7543
9426
|
createStreamHandler,
|
|
7544
9427
|
createSuggestionsHandler,
|
|
7545
9428
|
createUploadHandler,
|