@retrivora-ai/rag-engine 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ILLMProvider-BfRgI1Xh.d.mts → ILLMProvider-BOJFz3Na.d.mts} +47 -1
- package/dist/{ILLMProvider-BfRgI1Xh.d.ts → ILLMProvider-BOJFz3Na.d.ts} +47 -1
- package/dist/{MultiTablePostgresProvider-YY7LPNJK.mjs → MultiTablePostgresProvider-ZLGSKTJR.mjs} +1 -1
- package/dist/chunk-ICKRMZQK.mjs +76 -0
- package/dist/{chunk-BFYLQYQU.mjs → chunk-LZVVLSDN.mjs} +192 -100
- package/dist/{chunk-R3RGUMHE.mjs → chunk-OZFBG4BA.mjs} +121 -48
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +368 -147
- package/dist/handlers/index.mjs +4 -1
- package/dist/{index-BV0z5mb6.d.mts → index-BwpcaziY.d.ts} +4 -2
- package/dist/{index-1Z4GuYBi.d.ts → index-D3V9Et2M.d.mts} +4 -2
- package/dist/index.d.mts +23 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +1143 -790
- package/dist/index.mjs +1065 -770
- package/dist/server.d.mts +15 -25
- package/dist/server.d.ts +15 -25
- package/dist/server.js +366 -147
- package/dist/server.mjs +3 -2
- package/package.json +4 -2
- package/src/app/api/upload/route.ts +4 -0
- package/src/app/constants.tsx +2 -2
- package/src/app/page.tsx +12 -321
- package/src/components/AmbientBackground.tsx +29 -0
- package/src/components/ArchitectureCard.tsx +17 -0
- package/src/components/ArchitectureCardsSection.tsx +15 -0
- package/src/components/ChatWindow.tsx +32 -0
- package/src/components/CodeViewer.tsx +51 -0
- package/src/components/ConfigProvider.tsx +1 -0
- package/src/components/DocViewer.tsx +37 -0
- package/src/components/DocumentUpload.tsx +44 -1
- package/src/components/Documentation.tsx +58 -0
- package/src/components/DynamicChart.tsx +27 -2
- package/src/components/Hero.tsx +59 -0
- package/src/components/HourglassLoader.tsx +87 -0
- package/src/components/Lifecycle.tsx +37 -0
- package/src/components/MarkdownComponents.tsx +140 -0
- package/src/components/MessageBubble.tsx +88 -1010
- package/src/components/Navbar.tsx +55 -0
- package/src/components/ObservabilityPanel.tsx +374 -0
- package/src/components/ProductCard.tsx +3 -1
- package/src/components/UIDispatcher.tsx +344 -0
- package/src/components/VisualizationRenderer.tsx +48 -26
- package/src/core/Pipeline.ts +186 -76
- package/src/handlers/index.ts +72 -12
- package/src/hooks/useRagChat.ts +19 -9
- package/src/index.ts +9 -1
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
- package/src/types/chat.ts +2 -0
- package/src/types/index.ts +52 -0
- package/src/types/props.ts +9 -1
- package/src/utils/ProductExtractor.ts +347 -0
- package/src/utils/UITransformer.ts +4 -53
- package/src/utils/synonyms.ts +78 -0
package/dist/handlers/index.js
CHANGED
|
@@ -347,7 +347,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
347
347
|
init_BaseVectorProvider();
|
|
348
348
|
MultiTablePostgresProvider = class extends BaseVectorProvider {
|
|
349
349
|
constructor(config) {
|
|
350
|
-
var _a, _b, _c
|
|
350
|
+
var _a, _b, _c;
|
|
351
351
|
super(config);
|
|
352
352
|
const opts = config.options || {};
|
|
353
353
|
if (!opts.connectionString) {
|
|
@@ -355,37 +355,37 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
355
355
|
}
|
|
356
356
|
this.connectionString = opts.connectionString;
|
|
357
357
|
this.dimensions = (_a = opts.dimensions) != null ? _a : 768;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (this.tables.length === 0) {
|
|
361
|
-
console.warn(
|
|
362
|
-
"[MultiTablePostgresProvider] No tables configured. Set VECTOR_DB_TABLES as a comma-separated list of table names or pass options.tables."
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
const rawSearchFields = (_e = (_d = opts.searchFields) != null ? _d : process.env.VECTOR_DB_SEARCH_FIELDS) != null ? _e : ["name", "product_name", "productname", "title"];
|
|
358
|
+
this.tables = [];
|
|
359
|
+
const rawSearchFields = (_c = (_b = opts.searchFields) != null ? _b : process.env.VECTOR_DB_SEARCH_FIELDS) != null ? _c : ["name", "product_name", "productname", "title"];
|
|
366
360
|
this.searchFields = typeof rawSearchFields === "string" ? rawSearchFields.split(",").map((f) => f.trim()).filter(Boolean) : rawSearchFields;
|
|
361
|
+
this.uploadTable = opts.uploadTable || "document_chunks";
|
|
367
362
|
}
|
|
368
363
|
async initialize() {
|
|
369
364
|
this.pool = new import_pg.Pool({ connectionString: this.connectionString });
|
|
370
365
|
const client = await this.pool.connect();
|
|
371
366
|
try {
|
|
367
|
+
await client.query("CREATE EXTENSION IF NOT EXISTS vector");
|
|
368
|
+
await client.query(`
|
|
369
|
+
CREATE TABLE IF NOT EXISTS ${this.uploadTable} (
|
|
370
|
+
id TEXT PRIMARY KEY,
|
|
371
|
+
namespace TEXT NOT NULL DEFAULT '',
|
|
372
|
+
content TEXT NOT NULL,
|
|
373
|
+
metadata JSONB,
|
|
374
|
+
embedding VECTOR(${this.dimensions})
|
|
375
|
+
)
|
|
376
|
+
`);
|
|
377
|
+
await client.query(`
|
|
378
|
+
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
379
|
+
ON ${this.uploadTable}
|
|
380
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
381
|
+
`);
|
|
372
382
|
const res = await client.query(`
|
|
373
383
|
SELECT table_name
|
|
374
384
|
FROM information_schema.columns
|
|
375
385
|
WHERE column_name = 'embedding'
|
|
376
386
|
AND table_schema = 'public'
|
|
377
387
|
`);
|
|
378
|
-
|
|
379
|
-
if (this.tables.length === 0) {
|
|
380
|
-
this.tables = discoveredTables;
|
|
381
|
-
} else {
|
|
382
|
-
const staticTables = [...this.tables];
|
|
383
|
-
this.tables = staticTables.filter((t) => discoveredTables.includes(t));
|
|
384
|
-
if (this.tables.length < staticTables.length) {
|
|
385
|
-
const missing = staticTables.filter((t) => !discoveredTables.includes(t));
|
|
386
|
-
console.warn(`[MultiTablePostgresProvider] Some configured tables were skipped (missing 'embedding' column): ${missing.join(", ")}`);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
388
|
+
this.tables = res.rows.map((r) => r.table_name);
|
|
389
389
|
if (this.tables.length === 0) {
|
|
390
390
|
console.warn('[MultiTablePostgresProvider] No tables with "embedding" columns found in the database.');
|
|
391
391
|
} else {
|
|
@@ -398,27 +398,106 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
400
|
/**
|
|
401
|
-
* Upsert
|
|
402
|
-
* searching across pre-existing tables with varying schemas.
|
|
401
|
+
* Upsert a document by dynamically provisioning a table and columns.
|
|
403
402
|
*/
|
|
404
|
-
async upsert(
|
|
405
|
-
|
|
406
|
-
"[MultiTablePostgresProvider] upsert() is not supported in multi-table mode. Please use the standard PostgreSQLProvider for single-table managed indices."
|
|
407
|
-
);
|
|
403
|
+
async upsert(doc, namespace = "") {
|
|
404
|
+
await this.batchUpsert([doc], namespace);
|
|
408
405
|
}
|
|
409
406
|
/**
|
|
410
|
-
* Batch upsert
|
|
407
|
+
* Batch upsert documents by dynamically provisioning tables and columns based on CSV headers.
|
|
411
408
|
*/
|
|
412
|
-
async batchUpsert(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
409
|
+
async batchUpsert(docs, namespace = "") {
|
|
410
|
+
var _a;
|
|
411
|
+
if (docs.length === 0) return;
|
|
412
|
+
const docsByFile = {};
|
|
413
|
+
for (const doc of docs) {
|
|
414
|
+
const fileName = ((_a = doc.metadata) == null ? void 0 : _a.fileName) || this.uploadTable;
|
|
415
|
+
if (!docsByFile[fileName]) docsByFile[fileName] = [];
|
|
416
|
+
docsByFile[fileName].push(doc);
|
|
417
|
+
}
|
|
418
|
+
const client = await this.pool.connect();
|
|
419
|
+
try {
|
|
420
|
+
await client.query("BEGIN");
|
|
421
|
+
for (const [fileName, fileDocs] of Object.entries(docsByFile)) {
|
|
422
|
+
let tableName = fileName;
|
|
423
|
+
if (tableName.toLowerCase().endsWith(".csv")) {
|
|
424
|
+
tableName = tableName.slice(0, -4);
|
|
425
|
+
}
|
|
426
|
+
tableName = tableName.replace(/[^a-z0-9_]/gi, "_").toLowerCase() || this.uploadTable;
|
|
427
|
+
const firstMeta = fileDocs[0].metadata || {};
|
|
428
|
+
const systemKeys = ["fileName", "fileSize", "fileType", "uploadedAt", "dimension", "chunkIndex", "id", "namespace", "content", "metadata", "embedding"];
|
|
429
|
+
const csvHeaders = Object.keys(firstMeta).filter((k) => !systemKeys.includes(k) && !systemKeys.includes(k.toLowerCase()));
|
|
430
|
+
const columnDefs = csvHeaders.map((h) => `"${h}" TEXT`).join(",\n ");
|
|
431
|
+
const createTableSql = `
|
|
432
|
+
CREATE TABLE IF NOT EXISTS "${tableName}" (
|
|
433
|
+
id TEXT PRIMARY KEY,
|
|
434
|
+
${columnDefs ? columnDefs + "," : ""}
|
|
435
|
+
namespace TEXT NOT NULL DEFAULT '',
|
|
436
|
+
content TEXT NOT NULL,
|
|
437
|
+
metadata JSONB,
|
|
438
|
+
embedding VECTOR(${this.dimensions})
|
|
439
|
+
)
|
|
440
|
+
`;
|
|
441
|
+
await client.query(createTableSql);
|
|
442
|
+
await client.query(`
|
|
443
|
+
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
444
|
+
ON "${tableName}"
|
|
445
|
+
USING hnsw (embedding vector_cosine_ops)
|
|
446
|
+
`);
|
|
447
|
+
if (!this.tables.includes(tableName)) {
|
|
448
|
+
this.tables.push(tableName);
|
|
449
|
+
}
|
|
450
|
+
const BATCH_SIZE = 50;
|
|
451
|
+
for (let i = 0; i < fileDocs.length; i += BATCH_SIZE) {
|
|
452
|
+
const batch = fileDocs.slice(i, i + BATCH_SIZE);
|
|
453
|
+
const values = [];
|
|
454
|
+
const headerColumns = csvHeaders.map((h) => `"${h}"`).join(", ");
|
|
455
|
+
const allColumns = `id, ${headerColumns ? headerColumns + ", " : ""}namespace, content, metadata, embedding`;
|
|
456
|
+
const valuePlaceholders = batch.map((doc, idx) => {
|
|
457
|
+
var _a2, _b;
|
|
458
|
+
const offset = idx * (5 + csvHeaders.length);
|
|
459
|
+
values.push(doc.id);
|
|
460
|
+
for (const h of csvHeaders) {
|
|
461
|
+
values.push(String(((_a2 = doc.metadata) == null ? void 0 : _a2[h]) || ""));
|
|
462
|
+
}
|
|
463
|
+
values.push(namespace, doc.content, JSON.stringify((_b = doc.metadata) != null ? _b : {}), `[${doc.vector.join(",")}]`);
|
|
464
|
+
const docPlaceholders = [];
|
|
465
|
+
for (let j = 1; j <= 5 + csvHeaders.length; j++) {
|
|
466
|
+
if (j === 5 + csvHeaders.length) {
|
|
467
|
+
docPlaceholders.push(`$${offset + j}::vector`);
|
|
468
|
+
} else {
|
|
469
|
+
docPlaceholders.push(`$${offset + j}`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return `(${docPlaceholders.join(", ")})`;
|
|
473
|
+
}).join(", ");
|
|
474
|
+
const conflictUpdates = csvHeaders.map((h) => `"${h}" = EXCLUDED."${h}"`).join(",\n ");
|
|
475
|
+
const query = `
|
|
476
|
+
INSERT INTO "${tableName}" (${allColumns})
|
|
477
|
+
VALUES ${valuePlaceholders}
|
|
478
|
+
ON CONFLICT (id) DO UPDATE
|
|
479
|
+
SET ${conflictUpdates ? conflictUpdates + "," : ""}
|
|
480
|
+
namespace = EXCLUDED.namespace,
|
|
481
|
+
content = EXCLUDED.content,
|
|
482
|
+
metadata = EXCLUDED.metadata,
|
|
483
|
+
embedding = EXCLUDED.embedding
|
|
484
|
+
`;
|
|
485
|
+
await client.query(query, values);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
await client.query("COMMIT");
|
|
489
|
+
} catch (error) {
|
|
490
|
+
await client.query("ROLLBACK");
|
|
491
|
+
throw error;
|
|
492
|
+
} finally {
|
|
493
|
+
client.release();
|
|
494
|
+
}
|
|
416
495
|
}
|
|
417
496
|
/**
|
|
418
497
|
* Query all configured tables and merge results, sorted by cosine similarity score.
|
|
419
498
|
*/
|
|
420
499
|
async query(vector, topK, _namespace, _filter) {
|
|
421
|
-
var _a
|
|
500
|
+
var _a;
|
|
422
501
|
if (!this.pool) {
|
|
423
502
|
throw new Error("[MultiTablePostgresProvider] Provider not initialized. Call initialize() first.");
|
|
424
503
|
}
|
|
@@ -510,22 +589,16 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
510
589
|
for (const tableResults of resultsArray) {
|
|
511
590
|
allResults.push(...tableResults);
|
|
512
591
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
const finalSorted = balancedResults.sort((a, b) => b.score - a.score);
|
|
525
|
-
if (finalSorted.length > 0) {
|
|
526
|
-
console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
|
|
527
|
-
console.log(`[MultiTablePostgresProvider] Final top match from "${(_c = (_b = finalSorted[0].metadata) == null ? void 0 : _b.source_table) != null ? _c : "unknown"}" with score ${finalSorted[0].score.toFixed(4)}`);
|
|
528
|
-
}
|
|
592
|
+
allResults.sort((a, b) => b.score - a.score);
|
|
593
|
+
if (allResults.length === 0) return [];
|
|
594
|
+
const bestMatchTable = (_a = allResults[0].metadata) == null ? void 0 : _a.source_table;
|
|
595
|
+
const finalSorted = allResults.filter((res) => {
|
|
596
|
+
var _a2;
|
|
597
|
+
return ((_a2 = res.metadata) == null ? void 0 : _a2.source_table) === bestMatchTable;
|
|
598
|
+
});
|
|
599
|
+
console.log(`[MultiTablePostgresProvider] --- Search Complete ---`);
|
|
600
|
+
console.log(`[MultiTablePostgresProvider] Final top match from "${bestMatchTable}" with score ${finalSorted[0].score.toFixed(4)}`);
|
|
601
|
+
console.log(`[MultiTablePostgresProvider] Restricting recommendations strictly to table: "${bestMatchTable}"`);
|
|
529
602
|
return finalSorted.slice(0, Math.max(topK, 15));
|
|
530
603
|
}
|
|
531
604
|
async delete(_id, _namespace) {
|
|
@@ -1640,6 +1713,7 @@ __export(handlers_exports, {
|
|
|
1640
1713
|
sseErrorFrame: () => sseErrorFrame,
|
|
1641
1714
|
sseFrame: () => sseFrame,
|
|
1642
1715
|
sseMetaFrame: () => sseMetaFrame,
|
|
1716
|
+
sseObservabilityFrame: () => sseObservabilityFrame,
|
|
1643
1717
|
sseTextFrame: () => sseTextFrame,
|
|
1644
1718
|
sseUIFrame: () => sseUIFrame
|
|
1645
1719
|
});
|
|
@@ -3900,6 +3974,64 @@ var QueryProcessor = class {
|
|
|
3900
3974
|
}
|
|
3901
3975
|
};
|
|
3902
3976
|
|
|
3977
|
+
// src/utils/synonyms.ts
|
|
3978
|
+
var FIELD_SYNONYMS = {
|
|
3979
|
+
name: ["product", "item", "title", "label", "heading", "subject", "product name", "item name"],
|
|
3980
|
+
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
3981
|
+
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
3982
|
+
image: [
|
|
3983
|
+
"imageUrl",
|
|
3984
|
+
"thumbnail",
|
|
3985
|
+
"img",
|
|
3986
|
+
"url",
|
|
3987
|
+
"photo",
|
|
3988
|
+
"picture",
|
|
3989
|
+
"media",
|
|
3990
|
+
"image_url",
|
|
3991
|
+
"main_image",
|
|
3992
|
+
"product_image",
|
|
3993
|
+
"thumb"
|
|
3994
|
+
],
|
|
3995
|
+
stock: [
|
|
3996
|
+
"inventory",
|
|
3997
|
+
"quantity",
|
|
3998
|
+
"count",
|
|
3999
|
+
"availability",
|
|
4000
|
+
"stock_level",
|
|
4001
|
+
"inStock",
|
|
4002
|
+
"is_available",
|
|
4003
|
+
"in stock",
|
|
4004
|
+
"status"
|
|
4005
|
+
],
|
|
4006
|
+
description: ["summary", "content", "body", "text", "info", "details"],
|
|
4007
|
+
link: ["url", "href", "product_url", "page_url", "link"]
|
|
4008
|
+
};
|
|
4009
|
+
function resolveMetadataValue(meta, uiKey) {
|
|
4010
|
+
var _a;
|
|
4011
|
+
const synonyms = (_a = FIELD_SYNONYMS[uiKey]) != null ? _a : [];
|
|
4012
|
+
const keys = Object.keys(meta);
|
|
4013
|
+
const systemKeys = ["namespace", "filename", "filesize", "filetype", "docid", "uploadedat", "dimension", "chunkindex"];
|
|
4014
|
+
let match = keys.find((k) => k.toLowerCase() === uiKey.toLowerCase());
|
|
4015
|
+
if (match !== void 0) return meta[match];
|
|
4016
|
+
match = keys.find((k) => synonyms.map((s) => s.toLowerCase()).includes(k.toLowerCase()));
|
|
4017
|
+
if (match !== void 0) return meta[match];
|
|
4018
|
+
const isBlacklisted = (kl) => {
|
|
4019
|
+
return systemKeys.includes(kl) || kl.startsWith("option1") || kl.startsWith("option2") || kl.startsWith("option3");
|
|
4020
|
+
};
|
|
4021
|
+
match = keys.find((k) => {
|
|
4022
|
+
const kl = k.toLowerCase();
|
|
4023
|
+
if (isBlacklisted(kl)) return false;
|
|
4024
|
+
return kl.includes(uiKey.toLowerCase());
|
|
4025
|
+
});
|
|
4026
|
+
if (match !== void 0) return meta[match];
|
|
4027
|
+
match = keys.find((k) => {
|
|
4028
|
+
const kl = k.toLowerCase();
|
|
4029
|
+
if (isBlacklisted(kl)) return false;
|
|
4030
|
+
return synonyms.some((sk) => kl.includes(sk.toLowerCase()) || sk.toLowerCase().includes(kl));
|
|
4031
|
+
});
|
|
4032
|
+
return match !== void 0 ? meta[match] : void 0;
|
|
4033
|
+
}
|
|
4034
|
+
|
|
3903
4035
|
// src/utils/UITransformer.ts
|
|
3904
4036
|
var UITransformer = class {
|
|
3905
4037
|
/**
|
|
@@ -3967,28 +4099,6 @@ var UITransformer = class {
|
|
|
3967
4099
|
data: pieData
|
|
3968
4100
|
};
|
|
3969
4101
|
}
|
|
3970
|
-
/**
|
|
3971
|
-
* Transform data to bar chart format
|
|
3972
|
-
*/
|
|
3973
|
-
static transformToBarChart(data) {
|
|
3974
|
-
const categories = this.detectCategories(data);
|
|
3975
|
-
const categoryData = this.aggregateByCategory(data, categories);
|
|
3976
|
-
const barData = Object.entries(categoryData).map(([category, value]) => {
|
|
3977
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(category, data);
|
|
3978
|
-
return {
|
|
3979
|
-
category,
|
|
3980
|
-
value,
|
|
3981
|
-
inStockCount,
|
|
3982
|
-
outOfStockCount
|
|
3983
|
-
};
|
|
3984
|
-
});
|
|
3985
|
-
return {
|
|
3986
|
-
type: "bar_chart",
|
|
3987
|
-
title: "Comparison by Category",
|
|
3988
|
-
description: `Comparing ${categories.length} categories`,
|
|
3989
|
-
data: barData
|
|
3990
|
-
};
|
|
3991
|
-
}
|
|
3992
4102
|
/**
|
|
3993
4103
|
* Transform data to line chart format
|
|
3994
4104
|
*/
|
|
@@ -4267,15 +4377,7 @@ var UITransformer = class {
|
|
|
4267
4377
|
const trainedKey = trainedSchema[uiKey];
|
|
4268
4378
|
if (trainedKey && meta[trainedKey] !== void 0) return meta[trainedKey];
|
|
4269
4379
|
}
|
|
4270
|
-
|
|
4271
|
-
const searchKeys = [uiKey, ...synonyms].map((k) => k.toLowerCase());
|
|
4272
|
-
const foundDirectKey = Object.keys(meta).find((k) => searchKeys.includes(k.toLowerCase()));
|
|
4273
|
-
if (foundDirectKey) return meta[foundDirectKey];
|
|
4274
|
-
const foundPartialKey = Object.keys(meta).find((k) => {
|
|
4275
|
-
const keyLow = k.toLowerCase();
|
|
4276
|
-
return searchKeys.some((sk) => keyLow.includes(sk) || sk.includes(keyLow));
|
|
4277
|
-
});
|
|
4278
|
-
return foundPartialKey ? meta[foundPartialKey] : void 0;
|
|
4380
|
+
return resolveMetadataValue(meta, uiKey);
|
|
4279
4381
|
}
|
|
4280
4382
|
static extractProductInfo(item, config, trainedSchema) {
|
|
4281
4383
|
const meta = item.metadata || {};
|
|
@@ -4345,7 +4447,7 @@ var UITransformer = class {
|
|
|
4345
4447
|
data.forEach((item) => {
|
|
4346
4448
|
const meta = item.metadata || {};
|
|
4347
4449
|
const itemCategory = meta.category || meta.type || "Other";
|
|
4348
|
-
if (
|
|
4450
|
+
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
4349
4451
|
result[itemCategory]++;
|
|
4350
4452
|
} else {
|
|
4351
4453
|
result["Other"] = (result["Other"] || 0) + 1;
|
|
@@ -4566,18 +4668,6 @@ IMPORTANT:
|
|
|
4566
4668
|
return JSON.stringify(partial, null, 2) + "\n// ... truncated";
|
|
4567
4669
|
}
|
|
4568
4670
|
};
|
|
4569
|
-
/**
|
|
4570
|
-
* Central dictionary of common synonyms for UI properties.
|
|
4571
|
-
* This allows the system to be schema-agnostic by guessing field names.
|
|
4572
|
-
*/
|
|
4573
|
-
UITransformer.SYNONYMS = {
|
|
4574
|
-
name: ["product", "item", "title", "label", "heading", "subject"],
|
|
4575
|
-
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
4576
|
-
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
4577
|
-
image: ["imageUrl", "thumbnail", "img", "url", "photo", "picture", "media", "image_url", "main_image", "product_image", "thumb"],
|
|
4578
|
-
stock: ["inventory", "quantity", "count", "availability", "stock_level", "inStock", "is_available"],
|
|
4579
|
-
description: ["summary", "content", "body", "text", "info", "details"]
|
|
4580
|
-
};
|
|
4581
4671
|
|
|
4582
4672
|
// src/utils/SchemaMapper.ts
|
|
4583
4673
|
var SchemaMapper = class {
|
|
@@ -4698,6 +4788,57 @@ var LRUEmbeddingCache = class {
|
|
|
4698
4788
|
return this.cache.size;
|
|
4699
4789
|
}
|
|
4700
4790
|
};
|
|
4791
|
+
function estimateTokens(text) {
|
|
4792
|
+
return Math.ceil(text.length / 4);
|
|
4793
|
+
}
|
|
4794
|
+
var MODEL_COST_PER_1K = {
|
|
4795
|
+
"gpt-4o": { input: 25e-4, output: 0.01 },
|
|
4796
|
+
"gpt-4o-mini": { input: 15e-5, output: 6e-4 },
|
|
4797
|
+
"gpt-4-turbo": { input: 0.01, output: 0.03 },
|
|
4798
|
+
"gpt-3.5-turbo": { input: 5e-4, output: 15e-4 },
|
|
4799
|
+
"claude-3-5-sonnet": { input: 3e-3, output: 0.015 },
|
|
4800
|
+
"claude-3-haiku": { input: 25e-5, output: 125e-5 },
|
|
4801
|
+
"gemini-1.5-flash": { input: 75e-6, output: 3e-4 },
|
|
4802
|
+
"gemini-1.5-pro": { input: 125e-5, output: 5e-3 }
|
|
4803
|
+
};
|
|
4804
|
+
function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
4805
|
+
if (!model) return void 0;
|
|
4806
|
+
const key = Object.keys(MODEL_COST_PER_1K).find((k) => model.toLowerCase().includes(k));
|
|
4807
|
+
if (!key) return void 0;
|
|
4808
|
+
const prices = MODEL_COST_PER_1K[key];
|
|
4809
|
+
return promptTokens / 1e3 * prices.input + completionTokens / 1e3 * prices.output;
|
|
4810
|
+
}
|
|
4811
|
+
async function scoreHallucination(llm, answer, context) {
|
|
4812
|
+
const maxContextChars = 3e3;
|
|
4813
|
+
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
4814
|
+
const prompt = `You are an AI quality checker. Given the CONTEXT retrieved from a knowledge base and the ANSWER generated from it, rate how well-grounded the answer is.
|
|
4815
|
+
|
|
4816
|
+
CONTEXT:
|
|
4817
|
+
${truncatedContext}
|
|
4818
|
+
|
|
4819
|
+
ANSWER:
|
|
4820
|
+
${answer}
|
|
4821
|
+
|
|
4822
|
+
Return ONLY a valid JSON object with no markdown fences:
|
|
4823
|
+
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
4824
|
+
|
|
4825
|
+
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
4826
|
+
try {
|
|
4827
|
+
const raw = await llm.chat(
|
|
4828
|
+
[{ role: "user", content: prompt }],
|
|
4829
|
+
"",
|
|
4830
|
+
{ temperature: 0, maxTokens: 120 }
|
|
4831
|
+
);
|
|
4832
|
+
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
4833
|
+
if (!jsonMatch) return void 0;
|
|
4834
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
4835
|
+
if (typeof parsed.score === "number" && typeof parsed.reason === "string") {
|
|
4836
|
+
return { score: Math.min(1, Math.max(0, parsed.score)), reason: parsed.reason };
|
|
4837
|
+
}
|
|
4838
|
+
} catch (e) {
|
|
4839
|
+
}
|
|
4840
|
+
return void 0;
|
|
4841
|
+
}
|
|
4701
4842
|
var Pipeline = class {
|
|
4702
4843
|
constructor(config) {
|
|
4703
4844
|
this.config = config;
|
|
@@ -4724,8 +4865,7 @@ var Pipeline = class {
|
|
|
4724
4865
|
async initialize() {
|
|
4725
4866
|
var _a;
|
|
4726
4867
|
if (this.initialised) return;
|
|
4727
|
-
const chartInstruction = `
|
|
4728
|
-
You are a helpful product assistant. Use the provided context to answer questions accurately.
|
|
4868
|
+
const chartInstruction = `You are a helpful product assistant. Use the provided context to answer questions accurately.
|
|
4729
4869
|
|
|
4730
4870
|
### UI STYLE RULES (CRITICAL):
|
|
4731
4871
|
- NEVER generate markdown tables. If you do, the UI will break.
|
|
@@ -4739,7 +4879,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4739
4879
|
- Do NOT try to format product lists as tables.
|
|
4740
4880
|
`;
|
|
4741
4881
|
this.config.llm.systemPrompt = chartInstruction;
|
|
4742
|
-
console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
|
|
4743
4882
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
4744
4883
|
const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
4745
4884
|
this.config.llm,
|
|
@@ -4761,7 +4900,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4761
4900
|
}
|
|
4762
4901
|
/**
|
|
4763
4902
|
* Ingest documents with automatic chunking, embedding, and batch upsert.
|
|
4764
|
-
* Handles retries for transient failures.
|
|
4765
4903
|
*/
|
|
4766
4904
|
async ingest(documents, namespace) {
|
|
4767
4905
|
await this.initialize();
|
|
@@ -4778,10 +4916,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4778
4916
|
metadata: chunk.metadata
|
|
4779
4917
|
}));
|
|
4780
4918
|
const totalProcessed = await this.processUpserts(upsertDocs, ns);
|
|
4781
|
-
results.push({
|
|
4782
|
-
docId: doc.docId,
|
|
4783
|
-
chunksIngested: totalProcessed
|
|
4784
|
-
});
|
|
4919
|
+
results.push({ docId: doc.docId, chunksIngested: totalProcessed });
|
|
4785
4920
|
if (this.graphDB && this.entityExtractor) {
|
|
4786
4921
|
await this.processGraphIngestion(doc.docId, chunks);
|
|
4787
4922
|
}
|
|
@@ -4792,23 +4927,18 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4792
4927
|
}
|
|
4793
4928
|
return results;
|
|
4794
4929
|
}
|
|
4795
|
-
/**
|
|
4796
|
-
* Step 1: Chunk the document content.
|
|
4797
|
-
*/
|
|
4930
|
+
/** Step 1: Chunk the document content. */
|
|
4798
4931
|
async prepareChunks(doc) {
|
|
4799
4932
|
var _a, _b;
|
|
4800
4933
|
if (this.llamaIngestor) {
|
|
4801
|
-
return
|
|
4934
|
+
return this.llamaIngestor.chunk(doc.content, {
|
|
4802
4935
|
docId: doc.docId,
|
|
4803
4936
|
metadata: doc.metadata,
|
|
4804
4937
|
chunkSize: (_a = this.config.rag) == null ? void 0 : _a.chunkSize,
|
|
4805
4938
|
chunkOverlap: (_b = this.config.rag) == null ? void 0 : _b.chunkOverlap
|
|
4806
4939
|
});
|
|
4807
4940
|
}
|
|
4808
|
-
return this.chunker.chunk(doc.content, {
|
|
4809
|
-
docId: doc.docId,
|
|
4810
|
-
metadata: doc.metadata
|
|
4811
|
-
});
|
|
4941
|
+
return this.chunker.chunk(doc.content, { docId: doc.docId, metadata: doc.metadata });
|
|
4812
4942
|
}
|
|
4813
4943
|
/**
|
|
4814
4944
|
* Step 2: Generate embeddings for chunks with retry logic.
|
|
@@ -4832,9 +4962,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4832
4962
|
}
|
|
4833
4963
|
return vectors;
|
|
4834
4964
|
}
|
|
4835
|
-
/**
|
|
4836
|
-
* Step 3: Upsert chunks to vector database with retry logic.
|
|
4837
|
-
*/
|
|
4965
|
+
/** Step 3: Upsert chunks to vector database with retry logic. */
|
|
4838
4966
|
async processUpserts(upsertDocs, namespace) {
|
|
4839
4967
|
const upsertBatchOptions = {
|
|
4840
4968
|
batchSize: 100,
|
|
@@ -4851,14 +4979,10 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4851
4979
|
}
|
|
4852
4980
|
return upsertResult.totalProcessed;
|
|
4853
4981
|
}
|
|
4854
|
-
/**
|
|
4855
|
-
* Step 4: Optional graph-based entity extraction and ingestion.
|
|
4856
|
-
*/
|
|
4982
|
+
/** Step 4: Optional graph-based entity extraction and ingestion. */
|
|
4857
4983
|
async processGraphIngestion(docId, chunks) {
|
|
4858
|
-
console.log(`[Pipeline] Extracting entities for doc ${docId} (${chunks.length} chunks)...`);
|
|
4859
4984
|
const extractionOptions = {
|
|
4860
4985
|
batchSize: 2,
|
|
4861
|
-
// Low concurrency for LLM extraction
|
|
4862
4986
|
maxRetries: 1,
|
|
4863
4987
|
initialDelayMs: 500
|
|
4864
4988
|
};
|
|
@@ -4871,7 +4995,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4871
4995
|
if (nodes.length > 0) await this.graphDB.addNodes(nodes);
|
|
4872
4996
|
if (edges.length > 0) await this.graphDB.addEdges(edges);
|
|
4873
4997
|
} catch (err) {
|
|
4874
|
-
console.warn(`[Pipeline] Entity extraction failed for chunk:`, err);
|
|
4998
|
+
console.warn(`[Pipeline] Entity extraction failed for chunk in doc ${docId}:`, err);
|
|
4875
4999
|
}
|
|
4876
5000
|
}
|
|
4877
5001
|
},
|
|
@@ -4882,7 +5006,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4882
5006
|
var _a;
|
|
4883
5007
|
await this.initialize();
|
|
4884
5008
|
if (((_a = this.config.rag) == null ? void 0 : _a.architecture) === "agentic" && this.agent) {
|
|
4885
|
-
console.log("[Pipeline] \u{1F916} Executing in Agentic Mode...");
|
|
4886
5009
|
const agentReply = await this.agent.run(question, history);
|
|
4887
5010
|
return { reply: agentReply, sources: [] };
|
|
4888
5011
|
}
|
|
@@ -4891,6 +5014,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4891
5014
|
let sources = [];
|
|
4892
5015
|
let graphData;
|
|
4893
5016
|
let uiTransformation;
|
|
5017
|
+
let trace;
|
|
4894
5018
|
try {
|
|
4895
5019
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
4896
5020
|
const chunk = temp.value;
|
|
@@ -4900,6 +5024,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4900
5024
|
if ("sources" in chunk) sources = chunk.sources;
|
|
4901
5025
|
if ("graphData" in chunk) graphData = chunk.graphData;
|
|
4902
5026
|
if ("ui_transformation" in chunk) uiTransformation = chunk.ui_transformation;
|
|
5027
|
+
if ("trace" in chunk) trace = chunk.trace;
|
|
4903
5028
|
}
|
|
4904
5029
|
}
|
|
4905
5030
|
} catch (temp) {
|
|
@@ -4912,38 +5037,47 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4912
5037
|
throw error[0];
|
|
4913
5038
|
}
|
|
4914
5039
|
}
|
|
4915
|
-
return { reply, sources, graphData, ui_transformation: uiTransformation };
|
|
5040
|
+
return { reply, sources, graphData, ui_transformation: uiTransformation, trace };
|
|
4916
5041
|
}
|
|
4917
5042
|
/**
|
|
4918
5043
|
* High-performance streaming RAG flow.
|
|
4919
|
-
* Yields text chunks first, then the retrieval metadata at the end.
|
|
5044
|
+
* Yields text chunks first, then the retrieval metadata + observability trace at the end.
|
|
4920
5045
|
*/
|
|
4921
5046
|
askStream(_0) {
|
|
4922
5047
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
4923
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
5048
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
4924
5049
|
yield new __await(this.initialize());
|
|
4925
5050
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
4926
5051
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
4927
5052
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
5053
|
+
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
5054
|
+
const requestStart = performance.now();
|
|
4928
5055
|
try {
|
|
4929
5056
|
let searchQuery = question;
|
|
5057
|
+
let rewrittenQuery;
|
|
4930
5058
|
if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
|
|
4931
5059
|
searchQuery = yield new __await(this.rewriteQuery(question, history));
|
|
5060
|
+
rewrittenQuery = searchQuery !== question ? searchQuery : void 0;
|
|
4932
5061
|
}
|
|
4933
5062
|
const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
4934
5063
|
const filter = QueryProcessor.buildQueryFilter(question, hints);
|
|
4935
|
-
|
|
5064
|
+
const embedStart = performance.now();
|
|
4936
5065
|
const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
|
|
4937
5066
|
namespace: ns,
|
|
4938
5067
|
topK: topK * 2,
|
|
4939
5068
|
filter
|
|
4940
5069
|
}));
|
|
5070
|
+
const retrieveEnd = performance.now();
|
|
5071
|
+
const embedMs = retrieveEnd - embedStart;
|
|
5072
|
+
const retrieveMs = retrieveEnd - embedStart;
|
|
5073
|
+
const rerankStart = performance.now();
|
|
4941
5074
|
let sources = rawSources.filter((m) => m.score >= scoreThreshold);
|
|
4942
5075
|
if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
|
|
4943
5076
|
sources = yield new __await(this.reranker.rerank(sources, question, topK));
|
|
4944
5077
|
} else {
|
|
4945
5078
|
sources = sources.slice(0, topK);
|
|
4946
5079
|
}
|
|
5080
|
+
const rerankMs = performance.now() - rerankStart;
|
|
4947
5081
|
let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
4948
5082
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
4949
5083
|
if (graphData && graphData.nodes.length > 0) {
|
|
@@ -4962,6 +5096,10 @@ ${context}`;
|
|
|
4962
5096
|
const hardenedHistory = [...history];
|
|
4963
5097
|
const userQuestion = { role: "user", content: question + restrictionSuffix };
|
|
4964
5098
|
const messages = [...hardenedHistory, userQuestion];
|
|
5099
|
+
const systemPrompt = (_h = this.config.llm.systemPrompt) != null ? _h : "";
|
|
5100
|
+
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
5101
|
+
let fullReply = "";
|
|
5102
|
+
const generateStart = performance.now();
|
|
4965
5103
|
if (this.llmProvider.chatStream) {
|
|
4966
5104
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
4967
5105
|
if (!stream) {
|
|
@@ -4970,6 +5108,7 @@ ${context}`;
|
|
|
4970
5108
|
try {
|
|
4971
5109
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
4972
5110
|
const chunk = temp.value;
|
|
5111
|
+
fullReply += chunk;
|
|
4973
5112
|
yield chunk;
|
|
4974
5113
|
}
|
|
4975
5114
|
} catch (temp) {
|
|
@@ -4984,15 +5123,58 @@ ${context}`;
|
|
|
4984
5123
|
}
|
|
4985
5124
|
} else {
|
|
4986
5125
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
5126
|
+
fullReply = reply;
|
|
4987
5127
|
yield reply;
|
|
4988
5128
|
}
|
|
5129
|
+
const generateMs = performance.now() - generateStart;
|
|
5130
|
+
const totalMs = performance.now() - requestStart;
|
|
5131
|
+
const latency = {
|
|
5132
|
+
embedMs: Math.round(embedMs),
|
|
5133
|
+
retrieveMs: Math.round(retrieveMs),
|
|
5134
|
+
rerankMs: ((_i = this.config.rag) == null ? void 0 : _i.useReranking) ? Math.round(rerankMs) : void 0,
|
|
5135
|
+
generateMs: Math.round(generateMs),
|
|
5136
|
+
totalMs: Math.round(totalMs)
|
|
5137
|
+
};
|
|
5138
|
+
const promptText = systemPrompt + "\n" + context + "\n" + userPrompt;
|
|
5139
|
+
const promptTokens = estimateTokens(promptText);
|
|
5140
|
+
const completionTokens = estimateTokens(fullReply);
|
|
5141
|
+
const tokens = {
|
|
5142
|
+
promptTokens,
|
|
5143
|
+
completionTokens,
|
|
5144
|
+
totalTokens: promptTokens + completionTokens,
|
|
5145
|
+
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
5146
|
+
};
|
|
5147
|
+
const hallucinationResult = yield new __await(scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0));
|
|
4989
5148
|
const trainedSchema = yield new __await(trainingPromise);
|
|
4990
5149
|
const uiTransformation = yield new __await(this.generateUiTransformation(question, sources, context, trainedSchema));
|
|
5150
|
+
const trace = {
|
|
5151
|
+
requestId,
|
|
5152
|
+
query: question,
|
|
5153
|
+
rewrittenQuery,
|
|
5154
|
+
systemPrompt,
|
|
5155
|
+
userPrompt: question + restrictionSuffix,
|
|
5156
|
+
chunks: sources.map((s) => {
|
|
5157
|
+
var _a2;
|
|
5158
|
+
return {
|
|
5159
|
+
id: s.id,
|
|
5160
|
+
score: s.score,
|
|
5161
|
+
content: s.content,
|
|
5162
|
+
metadata: (_a2 = s.metadata) != null ? _a2 : {},
|
|
5163
|
+
namespace: ns
|
|
5164
|
+
};
|
|
5165
|
+
}),
|
|
5166
|
+
latency,
|
|
5167
|
+
tokens,
|
|
5168
|
+
hallucinationScore: hallucinationResult == null ? void 0 : hallucinationResult.score,
|
|
5169
|
+
hallucinationReason: hallucinationResult == null ? void 0 : hallucinationResult.reason,
|
|
5170
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
5171
|
+
};
|
|
4991
5172
|
yield {
|
|
4992
5173
|
reply: "",
|
|
4993
5174
|
sources,
|
|
4994
5175
|
graphData,
|
|
4995
|
-
ui_transformation: uiTransformation
|
|
5176
|
+
ui_transformation: uiTransformation,
|
|
5177
|
+
trace
|
|
4996
5178
|
};
|
|
4997
5179
|
} catch (error2) {
|
|
4998
5180
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
@@ -5031,12 +5213,9 @@ ${context}`;
|
|
|
5031
5213
|
const sources = await this.vectorDB.query(queryVector, topK, ns, options.filter);
|
|
5032
5214
|
return { sources, graphData };
|
|
5033
5215
|
}
|
|
5034
|
-
/**
|
|
5035
|
-
* Rewrite the user query for better retrieval performance.
|
|
5036
|
-
*/
|
|
5216
|
+
/** Rewrite the user query for better retrieval performance. */
|
|
5037
5217
|
async rewriteQuery(question, history) {
|
|
5038
|
-
const prompt = `
|
|
5039
|
-
Given the following conversation history and a new question, rewrite the question to be a better search query for a vector database.
|
|
5218
|
+
const prompt = `Given the following conversation history and a new question, rewrite the question to be a better search query for a vector database.
|
|
5040
5219
|
Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
|
|
5041
5220
|
|
|
5042
5221
|
History:
|
|
@@ -5054,23 +5233,16 @@ Optimized Search Query:`;
|
|
|
5054
5233
|
);
|
|
5055
5234
|
return rewrite.trim() || question;
|
|
5056
5235
|
}
|
|
5057
|
-
/**
|
|
5058
|
-
* Generate 3-5 short, relevant questions based on the vector database content.
|
|
5059
|
-
*/
|
|
5236
|
+
/** Generate 3-5 short, relevant questions based on the vector database content. */
|
|
5060
5237
|
async getSuggestions(query, namespace) {
|
|
5061
|
-
if (!query || query.trim().length < 3)
|
|
5062
|
-
return [];
|
|
5063
|
-
}
|
|
5238
|
+
if (!query || query.trim().length < 3) return [];
|
|
5064
5239
|
await this.initialize();
|
|
5065
5240
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
5066
5241
|
try {
|
|
5067
5242
|
const { sources } = await this.retrieve(query, { namespace: ns, topK: 3 });
|
|
5068
|
-
if (sources.length === 0)
|
|
5069
|
-
return [];
|
|
5070
|
-
}
|
|
5243
|
+
if (sources.length === 0) return [];
|
|
5071
5244
|
const context = sources.map((s) => s.content).join("\n\n---\n\n");
|
|
5072
|
-
const prompt = `
|
|
5073
|
-
Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
|
|
5245
|
+
const prompt = `Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
|
|
5074
5246
|
Focus on questions that can be answered by the context.
|
|
5075
5247
|
Keep each question under 10 words and make them very specific to the content.
|
|
5076
5248
|
Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3"].
|
|
@@ -5347,6 +5519,11 @@ function sseMetaFrame(meta) {
|
|
|
5347
5519
|
function sseUIFrame(uiTransformation) {
|
|
5348
5520
|
return `data: ${JSON.stringify({ type: "ui_transformation", data: uiTransformation })}
|
|
5349
5521
|
|
|
5522
|
+
`;
|
|
5523
|
+
}
|
|
5524
|
+
function sseObservabilityFrame(trace) {
|
|
5525
|
+
return `data: ${JSON.stringify({ type: "observability", data: trace })}
|
|
5526
|
+
|
|
5350
5527
|
`;
|
|
5351
5528
|
}
|
|
5352
5529
|
function sseErrorFrame(message) {
|
|
@@ -5413,6 +5590,9 @@ function createStreamHandler(configOrPlugin) {
|
|
|
5413
5590
|
enqueue(sseMetaFrame(chunk));
|
|
5414
5591
|
const responseChunk = chunk;
|
|
5415
5592
|
const sources = (responseChunk == null ? void 0 : responseChunk.sources) || [];
|
|
5593
|
+
if (responseChunk == null ? void 0 : responseChunk.trace) {
|
|
5594
|
+
enqueue(sseObservabilityFrame(responseChunk.trace));
|
|
5595
|
+
}
|
|
5416
5596
|
if (sources.length > 0) {
|
|
5417
5597
|
try {
|
|
5418
5598
|
const llmProvider = (_a = plugin.getLLMProvider) == null ? void 0 : _a.call(plugin);
|
|
@@ -5494,24 +5674,64 @@ function createUploadHandler(configOrPlugin) {
|
|
|
5494
5674
|
const formData = await req.formData();
|
|
5495
5675
|
const files = formData.getAll("files");
|
|
5496
5676
|
const namespace = formData.get("namespace") || void 0;
|
|
5677
|
+
const dimensionRaw = formData.get("dimension");
|
|
5678
|
+
const dimension = dimensionRaw ? parseInt(dimensionRaw, 10) : void 0;
|
|
5497
5679
|
if (!files || files.length === 0) {
|
|
5498
5680
|
return import_server.NextResponse.json({ error: "No files provided" }, { status: 400 });
|
|
5499
5681
|
}
|
|
5500
|
-
const documents =
|
|
5501
|
-
|
|
5682
|
+
const documents = [];
|
|
5683
|
+
for (const file of files) {
|
|
5684
|
+
if (file.name.toLowerCase().endsWith(".csv") || file.type === "text/csv") {
|
|
5685
|
+
const text = await file.text();
|
|
5686
|
+
const Papa = await import("papaparse");
|
|
5687
|
+
const parsed = Papa.default.parse(text, { header: true, skipEmptyLines: true });
|
|
5688
|
+
if (parsed.data && parsed.data.length > 0) {
|
|
5689
|
+
let i = 0;
|
|
5690
|
+
let lastRowData = null;
|
|
5691
|
+
for (const row of parsed.data) {
|
|
5692
|
+
i++;
|
|
5693
|
+
const rowData = row;
|
|
5694
|
+
const groupingKey = Object.keys(rowData)[0];
|
|
5695
|
+
if (lastRowData && groupingKey && rowData[groupingKey] && rowData[groupingKey] === lastRowData[groupingKey]) {
|
|
5696
|
+
for (const key of Object.keys(rowData)) {
|
|
5697
|
+
if (!rowData[key] && lastRowData[key]) {
|
|
5698
|
+
rowData[key] = lastRowData[key];
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5702
|
+
lastRowData = __spreadValues({}, rowData);
|
|
5703
|
+
const contentParts = [];
|
|
5704
|
+
for (const [key, val] of Object.entries(rowData)) {
|
|
5705
|
+
if (key && val) {
|
|
5706
|
+
contentParts.push(`${key}: ${val}`);
|
|
5707
|
+
}
|
|
5708
|
+
}
|
|
5709
|
+
documents.push({
|
|
5710
|
+
docId: `${file.name}-row-${i}`,
|
|
5711
|
+
content: contentParts.join(", "),
|
|
5712
|
+
metadata: __spreadValues(__spreadValues({
|
|
5713
|
+
fileName: file.name,
|
|
5714
|
+
fileSize: file.size,
|
|
5715
|
+
fileType: file.type,
|
|
5716
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5717
|
+
}, dimension ? { dimension } : {}), rowData)
|
|
5718
|
+
});
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
} else {
|
|
5502
5722
|
const content = await DocumentParser.parse(file, file.name, file.type);
|
|
5503
|
-
|
|
5723
|
+
documents.push({
|
|
5504
5724
|
docId: file.name,
|
|
5505
5725
|
content,
|
|
5506
|
-
metadata: {
|
|
5726
|
+
metadata: __spreadValues({
|
|
5507
5727
|
fileName: file.name,
|
|
5508
5728
|
fileSize: file.size,
|
|
5509
5729
|
fileType: file.type,
|
|
5510
5730
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5511
|
-
}
|
|
5512
|
-
};
|
|
5513
|
-
}
|
|
5514
|
-
|
|
5731
|
+
}, dimension ? { dimension } : {})
|
|
5732
|
+
});
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5515
5735
|
const results = await plugin.ingest(documents, namespace);
|
|
5516
5736
|
return import_server.NextResponse.json({ message: "Upload successful", results });
|
|
5517
5737
|
} catch (err) {
|
|
@@ -5548,6 +5768,7 @@ function createSuggestionsHandler(configOrPlugin) {
|
|
|
5548
5768
|
sseErrorFrame,
|
|
5549
5769
|
sseFrame,
|
|
5550
5770
|
sseMetaFrame,
|
|
5771
|
+
sseObservabilityFrame,
|
|
5551
5772
|
sseTextFrame,
|
|
5552
5773
|
sseUIFrame
|
|
5553
5774
|
});
|