@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/server.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) {
|
|
@@ -3978,6 +4051,64 @@ var QueryProcessor = class {
|
|
|
3978
4051
|
}
|
|
3979
4052
|
};
|
|
3980
4053
|
|
|
4054
|
+
// src/utils/synonyms.ts
|
|
4055
|
+
var FIELD_SYNONYMS = {
|
|
4056
|
+
name: ["product", "item", "title", "label", "heading", "subject", "product name", "item name"],
|
|
4057
|
+
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
4058
|
+
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
4059
|
+
image: [
|
|
4060
|
+
"imageUrl",
|
|
4061
|
+
"thumbnail",
|
|
4062
|
+
"img",
|
|
4063
|
+
"url",
|
|
4064
|
+
"photo",
|
|
4065
|
+
"picture",
|
|
4066
|
+
"media",
|
|
4067
|
+
"image_url",
|
|
4068
|
+
"main_image",
|
|
4069
|
+
"product_image",
|
|
4070
|
+
"thumb"
|
|
4071
|
+
],
|
|
4072
|
+
stock: [
|
|
4073
|
+
"inventory",
|
|
4074
|
+
"quantity",
|
|
4075
|
+
"count",
|
|
4076
|
+
"availability",
|
|
4077
|
+
"stock_level",
|
|
4078
|
+
"inStock",
|
|
4079
|
+
"is_available",
|
|
4080
|
+
"in stock",
|
|
4081
|
+
"status"
|
|
4082
|
+
],
|
|
4083
|
+
description: ["summary", "content", "body", "text", "info", "details"],
|
|
4084
|
+
link: ["url", "href", "product_url", "page_url", "link"]
|
|
4085
|
+
};
|
|
4086
|
+
function resolveMetadataValue(meta, uiKey) {
|
|
4087
|
+
var _a;
|
|
4088
|
+
const synonyms = (_a = FIELD_SYNONYMS[uiKey]) != null ? _a : [];
|
|
4089
|
+
const keys = Object.keys(meta);
|
|
4090
|
+
const systemKeys = ["namespace", "filename", "filesize", "filetype", "docid", "uploadedat", "dimension", "chunkindex"];
|
|
4091
|
+
let match = keys.find((k) => k.toLowerCase() === uiKey.toLowerCase());
|
|
4092
|
+
if (match !== void 0) return meta[match];
|
|
4093
|
+
match = keys.find((k) => synonyms.map((s) => s.toLowerCase()).includes(k.toLowerCase()));
|
|
4094
|
+
if (match !== void 0) return meta[match];
|
|
4095
|
+
const isBlacklisted = (kl) => {
|
|
4096
|
+
return systemKeys.includes(kl) || kl.startsWith("option1") || kl.startsWith("option2") || kl.startsWith("option3");
|
|
4097
|
+
};
|
|
4098
|
+
match = keys.find((k) => {
|
|
4099
|
+
const kl = k.toLowerCase();
|
|
4100
|
+
if (isBlacklisted(kl)) return false;
|
|
4101
|
+
return kl.includes(uiKey.toLowerCase());
|
|
4102
|
+
});
|
|
4103
|
+
if (match !== void 0) return meta[match];
|
|
4104
|
+
match = keys.find((k) => {
|
|
4105
|
+
const kl = k.toLowerCase();
|
|
4106
|
+
if (isBlacklisted(kl)) return false;
|
|
4107
|
+
return synonyms.some((sk) => kl.includes(sk.toLowerCase()) || sk.toLowerCase().includes(kl));
|
|
4108
|
+
});
|
|
4109
|
+
return match !== void 0 ? meta[match] : void 0;
|
|
4110
|
+
}
|
|
4111
|
+
|
|
3981
4112
|
// src/utils/UITransformer.ts
|
|
3982
4113
|
var UITransformer = class {
|
|
3983
4114
|
/**
|
|
@@ -4045,28 +4176,6 @@ var UITransformer = class {
|
|
|
4045
4176
|
data: pieData
|
|
4046
4177
|
};
|
|
4047
4178
|
}
|
|
4048
|
-
/**
|
|
4049
|
-
* Transform data to bar chart format
|
|
4050
|
-
*/
|
|
4051
|
-
static transformToBarChart(data) {
|
|
4052
|
-
const categories = this.detectCategories(data);
|
|
4053
|
-
const categoryData = this.aggregateByCategory(data, categories);
|
|
4054
|
-
const barData = Object.entries(categoryData).map(([category, value]) => {
|
|
4055
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(category, data);
|
|
4056
|
-
return {
|
|
4057
|
-
category,
|
|
4058
|
-
value,
|
|
4059
|
-
inStockCount,
|
|
4060
|
-
outOfStockCount
|
|
4061
|
-
};
|
|
4062
|
-
});
|
|
4063
|
-
return {
|
|
4064
|
-
type: "bar_chart",
|
|
4065
|
-
title: "Comparison by Category",
|
|
4066
|
-
description: `Comparing ${categories.length} categories`,
|
|
4067
|
-
data: barData
|
|
4068
|
-
};
|
|
4069
|
-
}
|
|
4070
4179
|
/**
|
|
4071
4180
|
* Transform data to line chart format
|
|
4072
4181
|
*/
|
|
@@ -4345,15 +4454,7 @@ var UITransformer = class {
|
|
|
4345
4454
|
const trainedKey = trainedSchema[uiKey];
|
|
4346
4455
|
if (trainedKey && meta[trainedKey] !== void 0) return meta[trainedKey];
|
|
4347
4456
|
}
|
|
4348
|
-
|
|
4349
|
-
const searchKeys = [uiKey, ...synonyms].map((k) => k.toLowerCase());
|
|
4350
|
-
const foundDirectKey = Object.keys(meta).find((k) => searchKeys.includes(k.toLowerCase()));
|
|
4351
|
-
if (foundDirectKey) return meta[foundDirectKey];
|
|
4352
|
-
const foundPartialKey = Object.keys(meta).find((k) => {
|
|
4353
|
-
const keyLow = k.toLowerCase();
|
|
4354
|
-
return searchKeys.some((sk) => keyLow.includes(sk) || sk.includes(keyLow));
|
|
4355
|
-
});
|
|
4356
|
-
return foundPartialKey ? meta[foundPartialKey] : void 0;
|
|
4457
|
+
return resolveMetadataValue(meta, uiKey);
|
|
4357
4458
|
}
|
|
4358
4459
|
static extractProductInfo(item, config, trainedSchema) {
|
|
4359
4460
|
const meta = item.metadata || {};
|
|
@@ -4423,7 +4524,7 @@ var UITransformer = class {
|
|
|
4423
4524
|
data.forEach((item) => {
|
|
4424
4525
|
const meta = item.metadata || {};
|
|
4425
4526
|
const itemCategory = meta.category || meta.type || "Other";
|
|
4426
|
-
if (
|
|
4527
|
+
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
4427
4528
|
result[itemCategory]++;
|
|
4428
4529
|
} else {
|
|
4429
4530
|
result["Other"] = (result["Other"] || 0) + 1;
|
|
@@ -4644,18 +4745,6 @@ IMPORTANT:
|
|
|
4644
4745
|
return JSON.stringify(partial, null, 2) + "\n// ... truncated";
|
|
4645
4746
|
}
|
|
4646
4747
|
};
|
|
4647
|
-
/**
|
|
4648
|
-
* Central dictionary of common synonyms for UI properties.
|
|
4649
|
-
* This allows the system to be schema-agnostic by guessing field names.
|
|
4650
|
-
*/
|
|
4651
|
-
UITransformer.SYNONYMS = {
|
|
4652
|
-
name: ["product", "item", "title", "label", "heading", "subject"],
|
|
4653
|
-
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
4654
|
-
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
4655
|
-
image: ["imageUrl", "thumbnail", "img", "url", "photo", "picture", "media", "image_url", "main_image", "product_image", "thumb"],
|
|
4656
|
-
stock: ["inventory", "quantity", "count", "availability", "stock_level", "inStock", "is_available"],
|
|
4657
|
-
description: ["summary", "content", "body", "text", "info", "details"]
|
|
4658
|
-
};
|
|
4659
4748
|
|
|
4660
4749
|
// src/utils/SchemaMapper.ts
|
|
4661
4750
|
var SchemaMapper = class {
|
|
@@ -4776,6 +4865,57 @@ var LRUEmbeddingCache = class {
|
|
|
4776
4865
|
return this.cache.size;
|
|
4777
4866
|
}
|
|
4778
4867
|
};
|
|
4868
|
+
function estimateTokens(text) {
|
|
4869
|
+
return Math.ceil(text.length / 4);
|
|
4870
|
+
}
|
|
4871
|
+
var MODEL_COST_PER_1K = {
|
|
4872
|
+
"gpt-4o": { input: 25e-4, output: 0.01 },
|
|
4873
|
+
"gpt-4o-mini": { input: 15e-5, output: 6e-4 },
|
|
4874
|
+
"gpt-4-turbo": { input: 0.01, output: 0.03 },
|
|
4875
|
+
"gpt-3.5-turbo": { input: 5e-4, output: 15e-4 },
|
|
4876
|
+
"claude-3-5-sonnet": { input: 3e-3, output: 0.015 },
|
|
4877
|
+
"claude-3-haiku": { input: 25e-5, output: 125e-5 },
|
|
4878
|
+
"gemini-1.5-flash": { input: 75e-6, output: 3e-4 },
|
|
4879
|
+
"gemini-1.5-pro": { input: 125e-5, output: 5e-3 }
|
|
4880
|
+
};
|
|
4881
|
+
function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
4882
|
+
if (!model) return void 0;
|
|
4883
|
+
const key = Object.keys(MODEL_COST_PER_1K).find((k) => model.toLowerCase().includes(k));
|
|
4884
|
+
if (!key) return void 0;
|
|
4885
|
+
const prices = MODEL_COST_PER_1K[key];
|
|
4886
|
+
return promptTokens / 1e3 * prices.input + completionTokens / 1e3 * prices.output;
|
|
4887
|
+
}
|
|
4888
|
+
async function scoreHallucination(llm, answer, context) {
|
|
4889
|
+
const maxContextChars = 3e3;
|
|
4890
|
+
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
4891
|
+
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.
|
|
4892
|
+
|
|
4893
|
+
CONTEXT:
|
|
4894
|
+
${truncatedContext}
|
|
4895
|
+
|
|
4896
|
+
ANSWER:
|
|
4897
|
+
${answer}
|
|
4898
|
+
|
|
4899
|
+
Return ONLY a valid JSON object with no markdown fences:
|
|
4900
|
+
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
4901
|
+
|
|
4902
|
+
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
4903
|
+
try {
|
|
4904
|
+
const raw = await llm.chat(
|
|
4905
|
+
[{ role: "user", content: prompt }],
|
|
4906
|
+
"",
|
|
4907
|
+
{ temperature: 0, maxTokens: 120 }
|
|
4908
|
+
);
|
|
4909
|
+
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
4910
|
+
if (!jsonMatch) return void 0;
|
|
4911
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
4912
|
+
if (typeof parsed.score === "number" && typeof parsed.reason === "string") {
|
|
4913
|
+
return { score: Math.min(1, Math.max(0, parsed.score)), reason: parsed.reason };
|
|
4914
|
+
}
|
|
4915
|
+
} catch (e) {
|
|
4916
|
+
}
|
|
4917
|
+
return void 0;
|
|
4918
|
+
}
|
|
4779
4919
|
var Pipeline = class {
|
|
4780
4920
|
constructor(config) {
|
|
4781
4921
|
this.config = config;
|
|
@@ -4802,8 +4942,7 @@ var Pipeline = class {
|
|
|
4802
4942
|
async initialize() {
|
|
4803
4943
|
var _a;
|
|
4804
4944
|
if (this.initialised) return;
|
|
4805
|
-
const chartInstruction = `
|
|
4806
|
-
You are a helpful product assistant. Use the provided context to answer questions accurately.
|
|
4945
|
+
const chartInstruction = `You are a helpful product assistant. Use the provided context to answer questions accurately.
|
|
4807
4946
|
|
|
4808
4947
|
### UI STYLE RULES (CRITICAL):
|
|
4809
4948
|
- NEVER generate markdown tables. If you do, the UI will break.
|
|
@@ -4817,7 +4956,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4817
4956
|
- Do NOT try to format product lists as tables.
|
|
4818
4957
|
`;
|
|
4819
4958
|
this.config.llm.systemPrompt = chartInstruction;
|
|
4820
|
-
console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
|
|
4821
4959
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
4822
4960
|
const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
4823
4961
|
this.config.llm,
|
|
@@ -4839,7 +4977,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4839
4977
|
}
|
|
4840
4978
|
/**
|
|
4841
4979
|
* Ingest documents with automatic chunking, embedding, and batch upsert.
|
|
4842
|
-
* Handles retries for transient failures.
|
|
4843
4980
|
*/
|
|
4844
4981
|
async ingest(documents, namespace) {
|
|
4845
4982
|
await this.initialize();
|
|
@@ -4856,10 +4993,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4856
4993
|
metadata: chunk.metadata
|
|
4857
4994
|
}));
|
|
4858
4995
|
const totalProcessed = await this.processUpserts(upsertDocs, ns);
|
|
4859
|
-
results.push({
|
|
4860
|
-
docId: doc.docId,
|
|
4861
|
-
chunksIngested: totalProcessed
|
|
4862
|
-
});
|
|
4996
|
+
results.push({ docId: doc.docId, chunksIngested: totalProcessed });
|
|
4863
4997
|
if (this.graphDB && this.entityExtractor) {
|
|
4864
4998
|
await this.processGraphIngestion(doc.docId, chunks);
|
|
4865
4999
|
}
|
|
@@ -4870,23 +5004,18 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4870
5004
|
}
|
|
4871
5005
|
return results;
|
|
4872
5006
|
}
|
|
4873
|
-
/**
|
|
4874
|
-
* Step 1: Chunk the document content.
|
|
4875
|
-
*/
|
|
5007
|
+
/** Step 1: Chunk the document content. */
|
|
4876
5008
|
async prepareChunks(doc) {
|
|
4877
5009
|
var _a, _b;
|
|
4878
5010
|
if (this.llamaIngestor) {
|
|
4879
|
-
return
|
|
5011
|
+
return this.llamaIngestor.chunk(doc.content, {
|
|
4880
5012
|
docId: doc.docId,
|
|
4881
5013
|
metadata: doc.metadata,
|
|
4882
5014
|
chunkSize: (_a = this.config.rag) == null ? void 0 : _a.chunkSize,
|
|
4883
5015
|
chunkOverlap: (_b = this.config.rag) == null ? void 0 : _b.chunkOverlap
|
|
4884
5016
|
});
|
|
4885
5017
|
}
|
|
4886
|
-
return this.chunker.chunk(doc.content, {
|
|
4887
|
-
docId: doc.docId,
|
|
4888
|
-
metadata: doc.metadata
|
|
4889
|
-
});
|
|
5018
|
+
return this.chunker.chunk(doc.content, { docId: doc.docId, metadata: doc.metadata });
|
|
4890
5019
|
}
|
|
4891
5020
|
/**
|
|
4892
5021
|
* Step 2: Generate embeddings for chunks with retry logic.
|
|
@@ -4910,9 +5039,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4910
5039
|
}
|
|
4911
5040
|
return vectors;
|
|
4912
5041
|
}
|
|
4913
|
-
/**
|
|
4914
|
-
* Step 3: Upsert chunks to vector database with retry logic.
|
|
4915
|
-
*/
|
|
5042
|
+
/** Step 3: Upsert chunks to vector database with retry logic. */
|
|
4916
5043
|
async processUpserts(upsertDocs, namespace) {
|
|
4917
5044
|
const upsertBatchOptions = {
|
|
4918
5045
|
batchSize: 100,
|
|
@@ -4929,14 +5056,10 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4929
5056
|
}
|
|
4930
5057
|
return upsertResult.totalProcessed;
|
|
4931
5058
|
}
|
|
4932
|
-
/**
|
|
4933
|
-
* Step 4: Optional graph-based entity extraction and ingestion.
|
|
4934
|
-
*/
|
|
5059
|
+
/** Step 4: Optional graph-based entity extraction and ingestion. */
|
|
4935
5060
|
async processGraphIngestion(docId, chunks) {
|
|
4936
|
-
console.log(`[Pipeline] Extracting entities for doc ${docId} (${chunks.length} chunks)...`);
|
|
4937
5061
|
const extractionOptions = {
|
|
4938
5062
|
batchSize: 2,
|
|
4939
|
-
// Low concurrency for LLM extraction
|
|
4940
5063
|
maxRetries: 1,
|
|
4941
5064
|
initialDelayMs: 500
|
|
4942
5065
|
};
|
|
@@ -4949,7 +5072,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4949
5072
|
if (nodes.length > 0) await this.graphDB.addNodes(nodes);
|
|
4950
5073
|
if (edges.length > 0) await this.graphDB.addEdges(edges);
|
|
4951
5074
|
} catch (err) {
|
|
4952
|
-
console.warn(`[Pipeline] Entity extraction failed for chunk:`, err);
|
|
5075
|
+
console.warn(`[Pipeline] Entity extraction failed for chunk in doc ${docId}:`, err);
|
|
4953
5076
|
}
|
|
4954
5077
|
}
|
|
4955
5078
|
},
|
|
@@ -4960,7 +5083,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4960
5083
|
var _a;
|
|
4961
5084
|
await this.initialize();
|
|
4962
5085
|
if (((_a = this.config.rag) == null ? void 0 : _a.architecture) === "agentic" && this.agent) {
|
|
4963
|
-
console.log("[Pipeline] \u{1F916} Executing in Agentic Mode...");
|
|
4964
5086
|
const agentReply = await this.agent.run(question, history);
|
|
4965
5087
|
return { reply: agentReply, sources: [] };
|
|
4966
5088
|
}
|
|
@@ -4969,6 +5091,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4969
5091
|
let sources = [];
|
|
4970
5092
|
let graphData;
|
|
4971
5093
|
let uiTransformation;
|
|
5094
|
+
let trace;
|
|
4972
5095
|
try {
|
|
4973
5096
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
4974
5097
|
const chunk = temp.value;
|
|
@@ -4978,6 +5101,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4978
5101
|
if ("sources" in chunk) sources = chunk.sources;
|
|
4979
5102
|
if ("graphData" in chunk) graphData = chunk.graphData;
|
|
4980
5103
|
if ("ui_transformation" in chunk) uiTransformation = chunk.ui_transformation;
|
|
5104
|
+
if ("trace" in chunk) trace = chunk.trace;
|
|
4981
5105
|
}
|
|
4982
5106
|
}
|
|
4983
5107
|
} catch (temp) {
|
|
@@ -4990,38 +5114,47 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
4990
5114
|
throw error[0];
|
|
4991
5115
|
}
|
|
4992
5116
|
}
|
|
4993
|
-
return { reply, sources, graphData, ui_transformation: uiTransformation };
|
|
5117
|
+
return { reply, sources, graphData, ui_transformation: uiTransformation, trace };
|
|
4994
5118
|
}
|
|
4995
5119
|
/**
|
|
4996
5120
|
* High-performance streaming RAG flow.
|
|
4997
|
-
* Yields text chunks first, then the retrieval metadata at the end.
|
|
5121
|
+
* Yields text chunks first, then the retrieval metadata + observability trace at the end.
|
|
4998
5122
|
*/
|
|
4999
5123
|
askStream(_0) {
|
|
5000
5124
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
5001
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
5125
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
5002
5126
|
yield new __await(this.initialize());
|
|
5003
5127
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
5004
5128
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
5005
5129
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
5130
|
+
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
5131
|
+
const requestStart = performance.now();
|
|
5006
5132
|
try {
|
|
5007
5133
|
let searchQuery = question;
|
|
5134
|
+
let rewrittenQuery;
|
|
5008
5135
|
if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
|
|
5009
5136
|
searchQuery = yield new __await(this.rewriteQuery(question, history));
|
|
5137
|
+
rewrittenQuery = searchQuery !== question ? searchQuery : void 0;
|
|
5010
5138
|
}
|
|
5011
5139
|
const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
5012
5140
|
const filter = QueryProcessor.buildQueryFilter(question, hints);
|
|
5013
|
-
|
|
5141
|
+
const embedStart = performance.now();
|
|
5014
5142
|
const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
|
|
5015
5143
|
namespace: ns,
|
|
5016
5144
|
topK: topK * 2,
|
|
5017
5145
|
filter
|
|
5018
5146
|
}));
|
|
5147
|
+
const retrieveEnd = performance.now();
|
|
5148
|
+
const embedMs = retrieveEnd - embedStart;
|
|
5149
|
+
const retrieveMs = retrieveEnd - embedStart;
|
|
5150
|
+
const rerankStart = performance.now();
|
|
5019
5151
|
let sources = rawSources.filter((m) => m.score >= scoreThreshold);
|
|
5020
5152
|
if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
|
|
5021
5153
|
sources = yield new __await(this.reranker.rerank(sources, question, topK));
|
|
5022
5154
|
} else {
|
|
5023
5155
|
sources = sources.slice(0, topK);
|
|
5024
5156
|
}
|
|
5157
|
+
const rerankMs = performance.now() - rerankStart;
|
|
5025
5158
|
let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
5026
5159
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
5027
5160
|
if (graphData && graphData.nodes.length > 0) {
|
|
@@ -5040,6 +5173,10 @@ ${context}`;
|
|
|
5040
5173
|
const hardenedHistory = [...history];
|
|
5041
5174
|
const userQuestion = { role: "user", content: question + restrictionSuffix };
|
|
5042
5175
|
const messages = [...hardenedHistory, userQuestion];
|
|
5176
|
+
const systemPrompt = (_h = this.config.llm.systemPrompt) != null ? _h : "";
|
|
5177
|
+
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
5178
|
+
let fullReply = "";
|
|
5179
|
+
const generateStart = performance.now();
|
|
5043
5180
|
if (this.llmProvider.chatStream) {
|
|
5044
5181
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
5045
5182
|
if (!stream) {
|
|
@@ -5048,6 +5185,7 @@ ${context}`;
|
|
|
5048
5185
|
try {
|
|
5049
5186
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
5050
5187
|
const chunk = temp.value;
|
|
5188
|
+
fullReply += chunk;
|
|
5051
5189
|
yield chunk;
|
|
5052
5190
|
}
|
|
5053
5191
|
} catch (temp) {
|
|
@@ -5062,15 +5200,58 @@ ${context}`;
|
|
|
5062
5200
|
}
|
|
5063
5201
|
} else {
|
|
5064
5202
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
5203
|
+
fullReply = reply;
|
|
5065
5204
|
yield reply;
|
|
5066
5205
|
}
|
|
5206
|
+
const generateMs = performance.now() - generateStart;
|
|
5207
|
+
const totalMs = performance.now() - requestStart;
|
|
5208
|
+
const latency = {
|
|
5209
|
+
embedMs: Math.round(embedMs),
|
|
5210
|
+
retrieveMs: Math.round(retrieveMs),
|
|
5211
|
+
rerankMs: ((_i = this.config.rag) == null ? void 0 : _i.useReranking) ? Math.round(rerankMs) : void 0,
|
|
5212
|
+
generateMs: Math.round(generateMs),
|
|
5213
|
+
totalMs: Math.round(totalMs)
|
|
5214
|
+
};
|
|
5215
|
+
const promptText = systemPrompt + "\n" + context + "\n" + userPrompt;
|
|
5216
|
+
const promptTokens = estimateTokens(promptText);
|
|
5217
|
+
const completionTokens = estimateTokens(fullReply);
|
|
5218
|
+
const tokens = {
|
|
5219
|
+
promptTokens,
|
|
5220
|
+
completionTokens,
|
|
5221
|
+
totalTokens: promptTokens + completionTokens,
|
|
5222
|
+
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
5223
|
+
};
|
|
5224
|
+
const hallucinationResult = yield new __await(scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0));
|
|
5067
5225
|
const trainedSchema = yield new __await(trainingPromise);
|
|
5068
5226
|
const uiTransformation = yield new __await(this.generateUiTransformation(question, sources, context, trainedSchema));
|
|
5227
|
+
const trace = {
|
|
5228
|
+
requestId,
|
|
5229
|
+
query: question,
|
|
5230
|
+
rewrittenQuery,
|
|
5231
|
+
systemPrompt,
|
|
5232
|
+
userPrompt: question + restrictionSuffix,
|
|
5233
|
+
chunks: sources.map((s) => {
|
|
5234
|
+
var _a2;
|
|
5235
|
+
return {
|
|
5236
|
+
id: s.id,
|
|
5237
|
+
score: s.score,
|
|
5238
|
+
content: s.content,
|
|
5239
|
+
metadata: (_a2 = s.metadata) != null ? _a2 : {},
|
|
5240
|
+
namespace: ns
|
|
5241
|
+
};
|
|
5242
|
+
}),
|
|
5243
|
+
latency,
|
|
5244
|
+
tokens,
|
|
5245
|
+
hallucinationScore: hallucinationResult == null ? void 0 : hallucinationResult.score,
|
|
5246
|
+
hallucinationReason: hallucinationResult == null ? void 0 : hallucinationResult.reason,
|
|
5247
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
5248
|
+
};
|
|
5069
5249
|
yield {
|
|
5070
5250
|
reply: "",
|
|
5071
5251
|
sources,
|
|
5072
5252
|
graphData,
|
|
5073
|
-
ui_transformation: uiTransformation
|
|
5253
|
+
ui_transformation: uiTransformation,
|
|
5254
|
+
trace
|
|
5074
5255
|
};
|
|
5075
5256
|
} catch (error2) {
|
|
5076
5257
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
@@ -5109,12 +5290,9 @@ ${context}`;
|
|
|
5109
5290
|
const sources = await this.vectorDB.query(queryVector, topK, ns, options.filter);
|
|
5110
5291
|
return { sources, graphData };
|
|
5111
5292
|
}
|
|
5112
|
-
/**
|
|
5113
|
-
* Rewrite the user query for better retrieval performance.
|
|
5114
|
-
*/
|
|
5293
|
+
/** Rewrite the user query for better retrieval performance. */
|
|
5115
5294
|
async rewriteQuery(question, history) {
|
|
5116
|
-
const prompt = `
|
|
5117
|
-
Given the following conversation history and a new question, rewrite the question to be a better search query for a vector database.
|
|
5295
|
+
const prompt = `Given the following conversation history and a new question, rewrite the question to be a better search query for a vector database.
|
|
5118
5296
|
Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
|
|
5119
5297
|
|
|
5120
5298
|
History:
|
|
@@ -5132,23 +5310,16 @@ Optimized Search Query:`;
|
|
|
5132
5310
|
);
|
|
5133
5311
|
return rewrite.trim() || question;
|
|
5134
5312
|
}
|
|
5135
|
-
/**
|
|
5136
|
-
* Generate 3-5 short, relevant questions based on the vector database content.
|
|
5137
|
-
*/
|
|
5313
|
+
/** Generate 3-5 short, relevant questions based on the vector database content. */
|
|
5138
5314
|
async getSuggestions(query, namespace) {
|
|
5139
|
-
if (!query || query.trim().length < 3)
|
|
5140
|
-
return [];
|
|
5141
|
-
}
|
|
5315
|
+
if (!query || query.trim().length < 3) return [];
|
|
5142
5316
|
await this.initialize();
|
|
5143
5317
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
5144
5318
|
try {
|
|
5145
5319
|
const { sources } = await this.retrieve(query, { namespace: ns, topK: 3 });
|
|
5146
|
-
if (sources.length === 0)
|
|
5147
|
-
return [];
|
|
5148
|
-
}
|
|
5320
|
+
if (sources.length === 0) return [];
|
|
5149
5321
|
const context = sources.map((s) => s.content).join("\n\n---\n\n");
|
|
5150
|
-
const prompt = `
|
|
5151
|
-
Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
|
|
5322
|
+
const prompt = `Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
|
|
5152
5323
|
Focus on questions that can be answered by the context.
|
|
5153
5324
|
Keep each question under 10 words and make them very specific to the content.
|
|
5154
5325
|
Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3"].
|
|
@@ -5808,6 +5979,11 @@ function sseMetaFrame(meta) {
|
|
|
5808
5979
|
function sseUIFrame(uiTransformation) {
|
|
5809
5980
|
return `data: ${JSON.stringify({ type: "ui_transformation", data: uiTransformation })}
|
|
5810
5981
|
|
|
5982
|
+
`;
|
|
5983
|
+
}
|
|
5984
|
+
function sseObservabilityFrame(trace) {
|
|
5985
|
+
return `data: ${JSON.stringify({ type: "observability", data: trace })}
|
|
5986
|
+
|
|
5811
5987
|
`;
|
|
5812
5988
|
}
|
|
5813
5989
|
function sseErrorFrame(message) {
|
|
@@ -5874,6 +6050,9 @@ function createStreamHandler(configOrPlugin) {
|
|
|
5874
6050
|
enqueue(sseMetaFrame(chunk));
|
|
5875
6051
|
const responseChunk = chunk;
|
|
5876
6052
|
const sources = (responseChunk == null ? void 0 : responseChunk.sources) || [];
|
|
6053
|
+
if (responseChunk == null ? void 0 : responseChunk.trace) {
|
|
6054
|
+
enqueue(sseObservabilityFrame(responseChunk.trace));
|
|
6055
|
+
}
|
|
5877
6056
|
if (sources.length > 0) {
|
|
5878
6057
|
try {
|
|
5879
6058
|
const llmProvider = (_a = plugin.getLLMProvider) == null ? void 0 : _a.call(plugin);
|
|
@@ -5955,24 +6134,64 @@ function createUploadHandler(configOrPlugin) {
|
|
|
5955
6134
|
const formData = await req.formData();
|
|
5956
6135
|
const files = formData.getAll("files");
|
|
5957
6136
|
const namespace = formData.get("namespace") || void 0;
|
|
6137
|
+
const dimensionRaw = formData.get("dimension");
|
|
6138
|
+
const dimension = dimensionRaw ? parseInt(dimensionRaw, 10) : void 0;
|
|
5958
6139
|
if (!files || files.length === 0) {
|
|
5959
6140
|
return import_server.NextResponse.json({ error: "No files provided" }, { status: 400 });
|
|
5960
6141
|
}
|
|
5961
|
-
const documents =
|
|
5962
|
-
|
|
6142
|
+
const documents = [];
|
|
6143
|
+
for (const file of files) {
|
|
6144
|
+
if (file.name.toLowerCase().endsWith(".csv") || file.type === "text/csv") {
|
|
6145
|
+
const text = await file.text();
|
|
6146
|
+
const Papa = await import("papaparse");
|
|
6147
|
+
const parsed = Papa.default.parse(text, { header: true, skipEmptyLines: true });
|
|
6148
|
+
if (parsed.data && parsed.data.length > 0) {
|
|
6149
|
+
let i = 0;
|
|
6150
|
+
let lastRowData = null;
|
|
6151
|
+
for (const row of parsed.data) {
|
|
6152
|
+
i++;
|
|
6153
|
+
const rowData = row;
|
|
6154
|
+
const groupingKey = Object.keys(rowData)[0];
|
|
6155
|
+
if (lastRowData && groupingKey && rowData[groupingKey] && rowData[groupingKey] === lastRowData[groupingKey]) {
|
|
6156
|
+
for (const key of Object.keys(rowData)) {
|
|
6157
|
+
if (!rowData[key] && lastRowData[key]) {
|
|
6158
|
+
rowData[key] = lastRowData[key];
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
}
|
|
6162
|
+
lastRowData = __spreadValues({}, rowData);
|
|
6163
|
+
const contentParts = [];
|
|
6164
|
+
for (const [key, val] of Object.entries(rowData)) {
|
|
6165
|
+
if (key && val) {
|
|
6166
|
+
contentParts.push(`${key}: ${val}`);
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
documents.push({
|
|
6170
|
+
docId: `${file.name}-row-${i}`,
|
|
6171
|
+
content: contentParts.join(", "),
|
|
6172
|
+
metadata: __spreadValues(__spreadValues({
|
|
6173
|
+
fileName: file.name,
|
|
6174
|
+
fileSize: file.size,
|
|
6175
|
+
fileType: file.type,
|
|
6176
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6177
|
+
}, dimension ? { dimension } : {}), rowData)
|
|
6178
|
+
});
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
} else {
|
|
5963
6182
|
const content = await DocumentParser.parse(file, file.name, file.type);
|
|
5964
|
-
|
|
6183
|
+
documents.push({
|
|
5965
6184
|
docId: file.name,
|
|
5966
6185
|
content,
|
|
5967
|
-
metadata: {
|
|
6186
|
+
metadata: __spreadValues({
|
|
5968
6187
|
fileName: file.name,
|
|
5969
6188
|
fileSize: file.size,
|
|
5970
6189
|
fileType: file.type,
|
|
5971
6190
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5972
|
-
}
|
|
5973
|
-
};
|
|
5974
|
-
}
|
|
5975
|
-
|
|
6191
|
+
}, dimension ? { dimension } : {})
|
|
6192
|
+
});
|
|
6193
|
+
}
|
|
6194
|
+
}
|
|
5976
6195
|
const results = await plugin.ingest(documents, namespace);
|
|
5977
6196
|
return import_server.NextResponse.json({ message: "Upload successful", results });
|
|
5978
6197
|
} catch (err) {
|