@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
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveMetadataValue
|
|
3
|
+
} from "./chunk-ICKRMZQK.mjs";
|
|
1
4
|
import {
|
|
2
5
|
buildPayload,
|
|
3
6
|
mergeDefined,
|
|
@@ -1348,7 +1351,7 @@ var ProviderRegistry = class {
|
|
|
1348
1351
|
}
|
|
1349
1352
|
case "pgvector":
|
|
1350
1353
|
case "postgresql": {
|
|
1351
|
-
const { MultiTablePostgresProvider } = await import("./MultiTablePostgresProvider-
|
|
1354
|
+
const { MultiTablePostgresProvider } = await import("./MultiTablePostgresProvider-ZLGSKTJR.mjs");
|
|
1352
1355
|
return MultiTablePostgresProvider;
|
|
1353
1356
|
}
|
|
1354
1357
|
case "mongodb": {
|
|
@@ -2380,28 +2383,6 @@ var UITransformer = class {
|
|
|
2380
2383
|
data: pieData
|
|
2381
2384
|
};
|
|
2382
2385
|
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Transform data to bar chart format
|
|
2385
|
-
*/
|
|
2386
|
-
static transformToBarChart(data) {
|
|
2387
|
-
const categories = this.detectCategories(data);
|
|
2388
|
-
const categoryData = this.aggregateByCategory(data, categories);
|
|
2389
|
-
const barData = Object.entries(categoryData).map(([category, value]) => {
|
|
2390
|
-
const { inStockCount, outOfStockCount } = this.calculateStockCounts(category, data);
|
|
2391
|
-
return {
|
|
2392
|
-
category,
|
|
2393
|
-
value,
|
|
2394
|
-
inStockCount,
|
|
2395
|
-
outOfStockCount
|
|
2396
|
-
};
|
|
2397
|
-
});
|
|
2398
|
-
return {
|
|
2399
|
-
type: "bar_chart",
|
|
2400
|
-
title: "Comparison by Category",
|
|
2401
|
-
description: `Comparing ${categories.length} categories`,
|
|
2402
|
-
data: barData
|
|
2403
|
-
};
|
|
2404
|
-
}
|
|
2405
2386
|
/**
|
|
2406
2387
|
* Transform data to line chart format
|
|
2407
2388
|
*/
|
|
@@ -2680,15 +2661,7 @@ var UITransformer = class {
|
|
|
2680
2661
|
const trainedKey = trainedSchema[uiKey];
|
|
2681
2662
|
if (trainedKey && meta[trainedKey] !== void 0) return meta[trainedKey];
|
|
2682
2663
|
}
|
|
2683
|
-
|
|
2684
|
-
const searchKeys = [uiKey, ...synonyms].map((k) => k.toLowerCase());
|
|
2685
|
-
const foundDirectKey = Object.keys(meta).find((k) => searchKeys.includes(k.toLowerCase()));
|
|
2686
|
-
if (foundDirectKey) return meta[foundDirectKey];
|
|
2687
|
-
const foundPartialKey = Object.keys(meta).find((k) => {
|
|
2688
|
-
const keyLow = k.toLowerCase();
|
|
2689
|
-
return searchKeys.some((sk) => keyLow.includes(sk) || sk.includes(keyLow));
|
|
2690
|
-
});
|
|
2691
|
-
return foundPartialKey ? meta[foundPartialKey] : void 0;
|
|
2664
|
+
return resolveMetadataValue(meta, uiKey);
|
|
2692
2665
|
}
|
|
2693
2666
|
static extractProductInfo(item, config, trainedSchema) {
|
|
2694
2667
|
const meta = item.metadata || {};
|
|
@@ -2758,7 +2731,7 @@ var UITransformer = class {
|
|
|
2758
2731
|
data.forEach((item) => {
|
|
2759
2732
|
const meta = item.metadata || {};
|
|
2760
2733
|
const itemCategory = meta.category || meta.type || "Other";
|
|
2761
|
-
if (
|
|
2734
|
+
if (Object.prototype.hasOwnProperty.call(result, itemCategory)) {
|
|
2762
2735
|
result[itemCategory]++;
|
|
2763
2736
|
} else {
|
|
2764
2737
|
result["Other"] = (result["Other"] || 0) + 1;
|
|
@@ -2979,18 +2952,6 @@ IMPORTANT:
|
|
|
2979
2952
|
return JSON.stringify(partial, null, 2) + "\n// ... truncated";
|
|
2980
2953
|
}
|
|
2981
2954
|
};
|
|
2982
|
-
/**
|
|
2983
|
-
* Central dictionary of common synonyms for UI properties.
|
|
2984
|
-
* This allows the system to be schema-agnostic by guessing field names.
|
|
2985
|
-
*/
|
|
2986
|
-
UITransformer.SYNONYMS = {
|
|
2987
|
-
name: ["product", "item", "title", "label", "heading", "subject"],
|
|
2988
|
-
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
2989
|
-
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
2990
|
-
image: ["imageUrl", "thumbnail", "img", "url", "photo", "picture", "media", "image_url", "main_image", "product_image", "thumb"],
|
|
2991
|
-
stock: ["inventory", "quantity", "count", "availability", "stock_level", "inStock", "is_available"],
|
|
2992
|
-
description: ["summary", "content", "body", "text", "info", "details"]
|
|
2993
|
-
};
|
|
2994
2955
|
|
|
2995
2956
|
// src/utils/SchemaMapper.ts
|
|
2996
2957
|
var SchemaMapper = class {
|
|
@@ -3111,6 +3072,57 @@ var LRUEmbeddingCache = class {
|
|
|
3111
3072
|
return this.cache.size;
|
|
3112
3073
|
}
|
|
3113
3074
|
};
|
|
3075
|
+
function estimateTokens(text) {
|
|
3076
|
+
return Math.ceil(text.length / 4);
|
|
3077
|
+
}
|
|
3078
|
+
var MODEL_COST_PER_1K = {
|
|
3079
|
+
"gpt-4o": { input: 25e-4, output: 0.01 },
|
|
3080
|
+
"gpt-4o-mini": { input: 15e-5, output: 6e-4 },
|
|
3081
|
+
"gpt-4-turbo": { input: 0.01, output: 0.03 },
|
|
3082
|
+
"gpt-3.5-turbo": { input: 5e-4, output: 15e-4 },
|
|
3083
|
+
"claude-3-5-sonnet": { input: 3e-3, output: 0.015 },
|
|
3084
|
+
"claude-3-haiku": { input: 25e-5, output: 125e-5 },
|
|
3085
|
+
"gemini-1.5-flash": { input: 75e-6, output: 3e-4 },
|
|
3086
|
+
"gemini-1.5-pro": { input: 125e-5, output: 5e-3 }
|
|
3087
|
+
};
|
|
3088
|
+
function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
3089
|
+
if (!model) return void 0;
|
|
3090
|
+
const key = Object.keys(MODEL_COST_PER_1K).find((k) => model.toLowerCase().includes(k));
|
|
3091
|
+
if (!key) return void 0;
|
|
3092
|
+
const prices = MODEL_COST_PER_1K[key];
|
|
3093
|
+
return promptTokens / 1e3 * prices.input + completionTokens / 1e3 * prices.output;
|
|
3094
|
+
}
|
|
3095
|
+
async function scoreHallucination(llm, answer, context) {
|
|
3096
|
+
const maxContextChars = 3e3;
|
|
3097
|
+
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
3098
|
+
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.
|
|
3099
|
+
|
|
3100
|
+
CONTEXT:
|
|
3101
|
+
${truncatedContext}
|
|
3102
|
+
|
|
3103
|
+
ANSWER:
|
|
3104
|
+
${answer}
|
|
3105
|
+
|
|
3106
|
+
Return ONLY a valid JSON object with no markdown fences:
|
|
3107
|
+
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
3108
|
+
|
|
3109
|
+
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
3110
|
+
try {
|
|
3111
|
+
const raw = await llm.chat(
|
|
3112
|
+
[{ role: "user", content: prompt }],
|
|
3113
|
+
"",
|
|
3114
|
+
{ temperature: 0, maxTokens: 120 }
|
|
3115
|
+
);
|
|
3116
|
+
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
3117
|
+
if (!jsonMatch) return void 0;
|
|
3118
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
3119
|
+
if (typeof parsed.score === "number" && typeof parsed.reason === "string") {
|
|
3120
|
+
return { score: Math.min(1, Math.max(0, parsed.score)), reason: parsed.reason };
|
|
3121
|
+
}
|
|
3122
|
+
} catch (e) {
|
|
3123
|
+
}
|
|
3124
|
+
return void 0;
|
|
3125
|
+
}
|
|
3114
3126
|
var Pipeline = class {
|
|
3115
3127
|
constructor(config) {
|
|
3116
3128
|
this.config = config;
|
|
@@ -3137,8 +3149,7 @@ var Pipeline = class {
|
|
|
3137
3149
|
async initialize() {
|
|
3138
3150
|
var _a;
|
|
3139
3151
|
if (this.initialised) return;
|
|
3140
|
-
const chartInstruction = `
|
|
3141
|
-
You are a helpful product assistant. Use the provided context to answer questions accurately.
|
|
3152
|
+
const chartInstruction = `You are a helpful product assistant. Use the provided context to answer questions accurately.
|
|
3142
3153
|
|
|
3143
3154
|
### UI STYLE RULES (CRITICAL):
|
|
3144
3155
|
- NEVER generate markdown tables. If you do, the UI will break.
|
|
@@ -3152,7 +3163,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3152
3163
|
- Do NOT try to format product lists as tables.
|
|
3153
3164
|
`;
|
|
3154
3165
|
this.config.llm.systemPrompt = chartInstruction;
|
|
3155
|
-
console.log(`[Pipeline] Initializing with provider: ${this.config.vectorDb.provider}...`);
|
|
3156
3166
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
3157
3167
|
const { llmProvider, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
3158
3168
|
this.config.llm,
|
|
@@ -3174,7 +3184,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3174
3184
|
}
|
|
3175
3185
|
/**
|
|
3176
3186
|
* Ingest documents with automatic chunking, embedding, and batch upsert.
|
|
3177
|
-
* Handles retries for transient failures.
|
|
3178
3187
|
*/
|
|
3179
3188
|
async ingest(documents, namespace) {
|
|
3180
3189
|
await this.initialize();
|
|
@@ -3191,10 +3200,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3191
3200
|
metadata: chunk.metadata
|
|
3192
3201
|
}));
|
|
3193
3202
|
const totalProcessed = await this.processUpserts(upsertDocs, ns);
|
|
3194
|
-
results.push({
|
|
3195
|
-
docId: doc.docId,
|
|
3196
|
-
chunksIngested: totalProcessed
|
|
3197
|
-
});
|
|
3203
|
+
results.push({ docId: doc.docId, chunksIngested: totalProcessed });
|
|
3198
3204
|
if (this.graphDB && this.entityExtractor) {
|
|
3199
3205
|
await this.processGraphIngestion(doc.docId, chunks);
|
|
3200
3206
|
}
|
|
@@ -3205,23 +3211,18 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3205
3211
|
}
|
|
3206
3212
|
return results;
|
|
3207
3213
|
}
|
|
3208
|
-
/**
|
|
3209
|
-
* Step 1: Chunk the document content.
|
|
3210
|
-
*/
|
|
3214
|
+
/** Step 1: Chunk the document content. */
|
|
3211
3215
|
async prepareChunks(doc) {
|
|
3212
3216
|
var _a, _b;
|
|
3213
3217
|
if (this.llamaIngestor) {
|
|
3214
|
-
return
|
|
3218
|
+
return this.llamaIngestor.chunk(doc.content, {
|
|
3215
3219
|
docId: doc.docId,
|
|
3216
3220
|
metadata: doc.metadata,
|
|
3217
3221
|
chunkSize: (_a = this.config.rag) == null ? void 0 : _a.chunkSize,
|
|
3218
3222
|
chunkOverlap: (_b = this.config.rag) == null ? void 0 : _b.chunkOverlap
|
|
3219
3223
|
});
|
|
3220
3224
|
}
|
|
3221
|
-
return this.chunker.chunk(doc.content, {
|
|
3222
|
-
docId: doc.docId,
|
|
3223
|
-
metadata: doc.metadata
|
|
3224
|
-
});
|
|
3225
|
+
return this.chunker.chunk(doc.content, { docId: doc.docId, metadata: doc.metadata });
|
|
3225
3226
|
}
|
|
3226
3227
|
/**
|
|
3227
3228
|
* Step 2: Generate embeddings for chunks with retry logic.
|
|
@@ -3245,9 +3246,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3245
3246
|
}
|
|
3246
3247
|
return vectors;
|
|
3247
3248
|
}
|
|
3248
|
-
/**
|
|
3249
|
-
* Step 3: Upsert chunks to vector database with retry logic.
|
|
3250
|
-
*/
|
|
3249
|
+
/** Step 3: Upsert chunks to vector database with retry logic. */
|
|
3251
3250
|
async processUpserts(upsertDocs, namespace) {
|
|
3252
3251
|
const upsertBatchOptions = {
|
|
3253
3252
|
batchSize: 100,
|
|
@@ -3264,14 +3263,10 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3264
3263
|
}
|
|
3265
3264
|
return upsertResult.totalProcessed;
|
|
3266
3265
|
}
|
|
3267
|
-
/**
|
|
3268
|
-
* Step 4: Optional graph-based entity extraction and ingestion.
|
|
3269
|
-
*/
|
|
3266
|
+
/** Step 4: Optional graph-based entity extraction and ingestion. */
|
|
3270
3267
|
async processGraphIngestion(docId, chunks) {
|
|
3271
|
-
console.log(`[Pipeline] Extracting entities for doc ${docId} (${chunks.length} chunks)...`);
|
|
3272
3268
|
const extractionOptions = {
|
|
3273
3269
|
batchSize: 2,
|
|
3274
|
-
// Low concurrency for LLM extraction
|
|
3275
3270
|
maxRetries: 1,
|
|
3276
3271
|
initialDelayMs: 500
|
|
3277
3272
|
};
|
|
@@ -3284,7 +3279,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3284
3279
|
if (nodes.length > 0) await this.graphDB.addNodes(nodes);
|
|
3285
3280
|
if (edges.length > 0) await this.graphDB.addEdges(edges);
|
|
3286
3281
|
} catch (err) {
|
|
3287
|
-
console.warn(`[Pipeline] Entity extraction failed for chunk:`, err);
|
|
3282
|
+
console.warn(`[Pipeline] Entity extraction failed for chunk in doc ${docId}:`, err);
|
|
3288
3283
|
}
|
|
3289
3284
|
}
|
|
3290
3285
|
},
|
|
@@ -3295,7 +3290,6 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3295
3290
|
var _a;
|
|
3296
3291
|
await this.initialize();
|
|
3297
3292
|
if (((_a = this.config.rag) == null ? void 0 : _a.architecture) === "agentic" && this.agent) {
|
|
3298
|
-
console.log("[Pipeline] \u{1F916} Executing in Agentic Mode...");
|
|
3299
3293
|
const agentReply = await this.agent.run(question, history);
|
|
3300
3294
|
return { reply: agentReply, sources: [] };
|
|
3301
3295
|
}
|
|
@@ -3304,6 +3298,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3304
3298
|
let sources = [];
|
|
3305
3299
|
let graphData;
|
|
3306
3300
|
let uiTransformation;
|
|
3301
|
+
let trace;
|
|
3307
3302
|
try {
|
|
3308
3303
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
|
|
3309
3304
|
const chunk = temp.value;
|
|
@@ -3313,6 +3308,7 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3313
3308
|
if ("sources" in chunk) sources = chunk.sources;
|
|
3314
3309
|
if ("graphData" in chunk) graphData = chunk.graphData;
|
|
3315
3310
|
if ("ui_transformation" in chunk) uiTransformation = chunk.ui_transformation;
|
|
3311
|
+
if ("trace" in chunk) trace = chunk.trace;
|
|
3316
3312
|
}
|
|
3317
3313
|
}
|
|
3318
3314
|
} catch (temp) {
|
|
@@ -3325,38 +3321,47 @@ You are a helpful product assistant. Use the provided context to answer question
|
|
|
3325
3321
|
throw error[0];
|
|
3326
3322
|
}
|
|
3327
3323
|
}
|
|
3328
|
-
return { reply, sources, graphData, ui_transformation: uiTransformation };
|
|
3324
|
+
return { reply, sources, graphData, ui_transformation: uiTransformation, trace };
|
|
3329
3325
|
}
|
|
3330
3326
|
/**
|
|
3331
3327
|
* High-performance streaming RAG flow.
|
|
3332
|
-
* Yields text chunks first, then the retrieval metadata at the end.
|
|
3328
|
+
* Yields text chunks first, then the retrieval metadata + observability trace at the end.
|
|
3333
3329
|
*/
|
|
3334
3330
|
askStream(_0) {
|
|
3335
3331
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
3336
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
3332
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
3337
3333
|
yield new __await(this.initialize());
|
|
3338
3334
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
3339
3335
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
3340
3336
|
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
3337
|
+
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
3338
|
+
const requestStart = performance.now();
|
|
3341
3339
|
try {
|
|
3342
3340
|
let searchQuery = question;
|
|
3341
|
+
let rewrittenQuery;
|
|
3343
3342
|
if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
|
|
3344
3343
|
searchQuery = yield new __await(this.rewriteQuery(question, history));
|
|
3344
|
+
rewrittenQuery = searchQuery !== question ? searchQuery : void 0;
|
|
3345
3345
|
}
|
|
3346
3346
|
const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
3347
3347
|
const filter = QueryProcessor.buildQueryFilter(question, hints);
|
|
3348
|
-
|
|
3348
|
+
const embedStart = performance.now();
|
|
3349
3349
|
const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
|
|
3350
3350
|
namespace: ns,
|
|
3351
3351
|
topK: topK * 2,
|
|
3352
3352
|
filter
|
|
3353
3353
|
}));
|
|
3354
|
+
const retrieveEnd = performance.now();
|
|
3355
|
+
const embedMs = retrieveEnd - embedStart;
|
|
3356
|
+
const retrieveMs = retrieveEnd - embedStart;
|
|
3357
|
+
const rerankStart = performance.now();
|
|
3354
3358
|
let sources = rawSources.filter((m) => m.score >= scoreThreshold);
|
|
3355
3359
|
if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
|
|
3356
3360
|
sources = yield new __await(this.reranker.rerank(sources, question, topK));
|
|
3357
3361
|
} else {
|
|
3358
3362
|
sources = sources.slice(0, topK);
|
|
3359
3363
|
}
|
|
3364
|
+
const rerankMs = performance.now() - rerankStart;
|
|
3360
3365
|
let context = sources.length ? sources.map((m, i) => `[Source ${i + 1}]
|
|
3361
3366
|
${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
3362
3367
|
if (graphData && graphData.nodes.length > 0) {
|
|
@@ -3375,6 +3380,10 @@ ${context}`;
|
|
|
3375
3380
|
const hardenedHistory = [...history];
|
|
3376
3381
|
const userQuestion = { role: "user", content: question + restrictionSuffix };
|
|
3377
3382
|
const messages = [...hardenedHistory, userQuestion];
|
|
3383
|
+
const systemPrompt = (_h = this.config.llm.systemPrompt) != null ? _h : "";
|
|
3384
|
+
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
3385
|
+
let fullReply = "";
|
|
3386
|
+
const generateStart = performance.now();
|
|
3378
3387
|
if (this.llmProvider.chatStream) {
|
|
3379
3388
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
3380
3389
|
if (!stream) {
|
|
@@ -3383,6 +3392,7 @@ ${context}`;
|
|
|
3383
3392
|
try {
|
|
3384
3393
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
3385
3394
|
const chunk = temp.value;
|
|
3395
|
+
fullReply += chunk;
|
|
3386
3396
|
yield chunk;
|
|
3387
3397
|
}
|
|
3388
3398
|
} catch (temp) {
|
|
@@ -3397,15 +3407,58 @@ ${context}`;
|
|
|
3397
3407
|
}
|
|
3398
3408
|
} else {
|
|
3399
3409
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
3410
|
+
fullReply = reply;
|
|
3400
3411
|
yield reply;
|
|
3401
3412
|
}
|
|
3413
|
+
const generateMs = performance.now() - generateStart;
|
|
3414
|
+
const totalMs = performance.now() - requestStart;
|
|
3415
|
+
const latency = {
|
|
3416
|
+
embedMs: Math.round(embedMs),
|
|
3417
|
+
retrieveMs: Math.round(retrieveMs),
|
|
3418
|
+
rerankMs: ((_i = this.config.rag) == null ? void 0 : _i.useReranking) ? Math.round(rerankMs) : void 0,
|
|
3419
|
+
generateMs: Math.round(generateMs),
|
|
3420
|
+
totalMs: Math.round(totalMs)
|
|
3421
|
+
};
|
|
3422
|
+
const promptText = systemPrompt + "\n" + context + "\n" + userPrompt;
|
|
3423
|
+
const promptTokens = estimateTokens(promptText);
|
|
3424
|
+
const completionTokens = estimateTokens(fullReply);
|
|
3425
|
+
const tokens = {
|
|
3426
|
+
promptTokens,
|
|
3427
|
+
completionTokens,
|
|
3428
|
+
totalTokens: promptTokens + completionTokens,
|
|
3429
|
+
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
3430
|
+
};
|
|
3431
|
+
const hallucinationResult = yield new __await(scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0));
|
|
3402
3432
|
const trainedSchema = yield new __await(trainingPromise);
|
|
3403
3433
|
const uiTransformation = yield new __await(this.generateUiTransformation(question, sources, context, trainedSchema));
|
|
3434
|
+
const trace = {
|
|
3435
|
+
requestId,
|
|
3436
|
+
query: question,
|
|
3437
|
+
rewrittenQuery,
|
|
3438
|
+
systemPrompt,
|
|
3439
|
+
userPrompt: question + restrictionSuffix,
|
|
3440
|
+
chunks: sources.map((s) => {
|
|
3441
|
+
var _a2;
|
|
3442
|
+
return {
|
|
3443
|
+
id: s.id,
|
|
3444
|
+
score: s.score,
|
|
3445
|
+
content: s.content,
|
|
3446
|
+
metadata: (_a2 = s.metadata) != null ? _a2 : {},
|
|
3447
|
+
namespace: ns
|
|
3448
|
+
};
|
|
3449
|
+
}),
|
|
3450
|
+
latency,
|
|
3451
|
+
tokens,
|
|
3452
|
+
hallucinationScore: hallucinationResult == null ? void 0 : hallucinationResult.score,
|
|
3453
|
+
hallucinationReason: hallucinationResult == null ? void 0 : hallucinationResult.reason,
|
|
3454
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3455
|
+
};
|
|
3404
3456
|
yield {
|
|
3405
3457
|
reply: "",
|
|
3406
3458
|
sources,
|
|
3407
3459
|
graphData,
|
|
3408
|
-
ui_transformation: uiTransformation
|
|
3460
|
+
ui_transformation: uiTransformation,
|
|
3461
|
+
trace
|
|
3409
3462
|
};
|
|
3410
3463
|
} catch (error2) {
|
|
3411
3464
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
@@ -3444,12 +3497,9 @@ ${context}`;
|
|
|
3444
3497
|
const sources = await this.vectorDB.query(queryVector, topK, ns, options.filter);
|
|
3445
3498
|
return { sources, graphData };
|
|
3446
3499
|
}
|
|
3447
|
-
/**
|
|
3448
|
-
* Rewrite the user query for better retrieval performance.
|
|
3449
|
-
*/
|
|
3500
|
+
/** Rewrite the user query for better retrieval performance. */
|
|
3450
3501
|
async rewriteQuery(question, history) {
|
|
3451
|
-
const prompt = `
|
|
3452
|
-
Given the following conversation history and a new question, rewrite the question to be a better search query for a vector database.
|
|
3502
|
+
const prompt = `Given the following conversation history and a new question, rewrite the question to be a better search query for a vector database.
|
|
3453
3503
|
Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.
|
|
3454
3504
|
|
|
3455
3505
|
History:
|
|
@@ -3467,23 +3517,16 @@ Optimized Search Query:`;
|
|
|
3467
3517
|
);
|
|
3468
3518
|
return rewrite.trim() || question;
|
|
3469
3519
|
}
|
|
3470
|
-
/**
|
|
3471
|
-
* Generate 3-5 short, relevant questions based on the vector database content.
|
|
3472
|
-
*/
|
|
3520
|
+
/** Generate 3-5 short, relevant questions based on the vector database content. */
|
|
3473
3521
|
async getSuggestions(query, namespace) {
|
|
3474
|
-
if (!query || query.trim().length < 3)
|
|
3475
|
-
return [];
|
|
3476
|
-
}
|
|
3522
|
+
if (!query || query.trim().length < 3) return [];
|
|
3477
3523
|
await this.initialize();
|
|
3478
3524
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
3479
3525
|
try {
|
|
3480
3526
|
const { sources } = await this.retrieve(query, { namespace: ns, topK: 3 });
|
|
3481
|
-
if (sources.length === 0)
|
|
3482
|
-
return [];
|
|
3483
|
-
}
|
|
3527
|
+
if (sources.length === 0) return [];
|
|
3484
3528
|
const context = sources.map((s) => s.content).join("\n\n---\n\n");
|
|
3485
|
-
const prompt = `
|
|
3486
|
-
Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
|
|
3529
|
+
const prompt = `Based on the following snippets from a document, what are 3 short, relevant questions a user might ask?
|
|
3487
3530
|
Focus on questions that can be answered by the context.
|
|
3488
3531
|
Keep each question under 10 words and make them very specific to the content.
|
|
3489
3532
|
Return ONLY a JSON array of strings like ["Question 1", "Question 2", "Question 3"].
|
|
@@ -3760,6 +3803,11 @@ function sseMetaFrame(meta) {
|
|
|
3760
3803
|
function sseUIFrame(uiTransformation) {
|
|
3761
3804
|
return `data: ${JSON.stringify({ type: "ui_transformation", data: uiTransformation })}
|
|
3762
3805
|
|
|
3806
|
+
`;
|
|
3807
|
+
}
|
|
3808
|
+
function sseObservabilityFrame(trace) {
|
|
3809
|
+
return `data: ${JSON.stringify({ type: "observability", data: trace })}
|
|
3810
|
+
|
|
3763
3811
|
`;
|
|
3764
3812
|
}
|
|
3765
3813
|
function sseErrorFrame(message) {
|
|
@@ -3826,6 +3874,9 @@ function createStreamHandler(configOrPlugin) {
|
|
|
3826
3874
|
enqueue(sseMetaFrame(chunk));
|
|
3827
3875
|
const responseChunk = chunk;
|
|
3828
3876
|
const sources = (responseChunk == null ? void 0 : responseChunk.sources) || [];
|
|
3877
|
+
if (responseChunk == null ? void 0 : responseChunk.trace) {
|
|
3878
|
+
enqueue(sseObservabilityFrame(responseChunk.trace));
|
|
3879
|
+
}
|
|
3829
3880
|
if (sources.length > 0) {
|
|
3830
3881
|
try {
|
|
3831
3882
|
const llmProvider = (_a = plugin.getLLMProvider) == null ? void 0 : _a.call(plugin);
|
|
@@ -3907,24 +3958,64 @@ function createUploadHandler(configOrPlugin) {
|
|
|
3907
3958
|
const formData = await req.formData();
|
|
3908
3959
|
const files = formData.getAll("files");
|
|
3909
3960
|
const namespace = formData.get("namespace") || void 0;
|
|
3961
|
+
const dimensionRaw = formData.get("dimension");
|
|
3962
|
+
const dimension = dimensionRaw ? parseInt(dimensionRaw, 10) : void 0;
|
|
3910
3963
|
if (!files || files.length === 0) {
|
|
3911
3964
|
return NextResponse.json({ error: "No files provided" }, { status: 400 });
|
|
3912
3965
|
}
|
|
3913
|
-
const documents =
|
|
3914
|
-
|
|
3966
|
+
const documents = [];
|
|
3967
|
+
for (const file of files) {
|
|
3968
|
+
if (file.name.toLowerCase().endsWith(".csv") || file.type === "text/csv") {
|
|
3969
|
+
const text = await file.text();
|
|
3970
|
+
const Papa = await import("papaparse");
|
|
3971
|
+
const parsed = Papa.default.parse(text, { header: true, skipEmptyLines: true });
|
|
3972
|
+
if (parsed.data && parsed.data.length > 0) {
|
|
3973
|
+
let i = 0;
|
|
3974
|
+
let lastRowData = null;
|
|
3975
|
+
for (const row of parsed.data) {
|
|
3976
|
+
i++;
|
|
3977
|
+
const rowData = row;
|
|
3978
|
+
const groupingKey = Object.keys(rowData)[0];
|
|
3979
|
+
if (lastRowData && groupingKey && rowData[groupingKey] && rowData[groupingKey] === lastRowData[groupingKey]) {
|
|
3980
|
+
for (const key of Object.keys(rowData)) {
|
|
3981
|
+
if (!rowData[key] && lastRowData[key]) {
|
|
3982
|
+
rowData[key] = lastRowData[key];
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
lastRowData = __spreadValues({}, rowData);
|
|
3987
|
+
const contentParts = [];
|
|
3988
|
+
for (const [key, val] of Object.entries(rowData)) {
|
|
3989
|
+
if (key && val) {
|
|
3990
|
+
contentParts.push(`${key}: ${val}`);
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
documents.push({
|
|
3994
|
+
docId: `${file.name}-row-${i}`,
|
|
3995
|
+
content: contentParts.join(", "),
|
|
3996
|
+
metadata: __spreadValues(__spreadValues({
|
|
3997
|
+
fileName: file.name,
|
|
3998
|
+
fileSize: file.size,
|
|
3999
|
+
fileType: file.type,
|
|
4000
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4001
|
+
}, dimension ? { dimension } : {}), rowData)
|
|
4002
|
+
});
|
|
4003
|
+
}
|
|
4004
|
+
}
|
|
4005
|
+
} else {
|
|
3915
4006
|
const content = await DocumentParser.parse(file, file.name, file.type);
|
|
3916
|
-
|
|
4007
|
+
documents.push({
|
|
3917
4008
|
docId: file.name,
|
|
3918
4009
|
content,
|
|
3919
|
-
metadata: {
|
|
4010
|
+
metadata: __spreadValues({
|
|
3920
4011
|
fileName: file.name,
|
|
3921
4012
|
fileSize: file.size,
|
|
3922
4013
|
fileType: file.type,
|
|
3923
4014
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3924
|
-
}
|
|
3925
|
-
};
|
|
3926
|
-
}
|
|
3927
|
-
|
|
4015
|
+
}, dimension ? { dimension } : {})
|
|
4016
|
+
});
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
3928
4019
|
const results = await plugin.ingest(documents, namespace);
|
|
3929
4020
|
return NextResponse.json({ message: "Upload successful", results });
|
|
3930
4021
|
} catch (err) {
|
|
@@ -3975,6 +4066,7 @@ export {
|
|
|
3975
4066
|
sseTextFrame,
|
|
3976
4067
|
sseMetaFrame,
|
|
3977
4068
|
sseUIFrame,
|
|
4069
|
+
sseObservabilityFrame,
|
|
3978
4070
|
sseErrorFrame,
|
|
3979
4071
|
createChatHandler,
|
|
3980
4072
|
createStreamHandler,
|