@retrivora-ai/rag-engine 1.9.3 → 1.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -7
- package/dist/{ILLMProvider-Bw2A28nU.d.mts → ILLMProvider-Bhk6zJOK.d.mts} +43 -7
- package/dist/{ILLMProvider-Bw2A28nU.d.ts → ILLMProvider-Bhk6zJOK.d.ts} +43 -7
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +737 -237
- package/dist/handlers/index.mjs +736 -237
- package/dist/index-B9J_XEh0.d.ts +187 -0
- package/dist/index-BJ4cd-t5.d.mts +187 -0
- package/dist/{index-B70ZLkfG.d.mts → index-Bu7T6xgr.d.ts} +20 -3
- package/dist/{index-DVu-mkAM.d.ts → index-C3SVtPYg.d.mts} +20 -3
- package/dist/index.css +237 -10
- package/dist/index.d.mts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +365 -164
- package/dist/index.mjs +350 -158
- package/dist/server.d.mts +15 -94
- package/dist/server.d.ts +15 -94
- package/dist/server.js +850 -239
- package/dist/server.mjs +839 -238
- package/package.json +2 -4
- package/src/app/api/chat/route.ts +2 -2
- package/src/app/api/health/route.ts +3 -2
- package/src/app/api/ingest/route.ts +3 -2
- package/src/app/api/suggestions/route.ts +3 -2
- package/src/app/api/upload/route.ts +3 -2
- package/src/app/constants.tsx +168 -148
- package/src/app/layout.tsx +5 -18
- package/src/app/types.ts +17 -17
- package/src/components/ChatWidget.tsx +3 -1
- package/src/components/ChatWindow.tsx +5 -1
- package/src/components/DocViewer.tsx +71 -5
- package/src/components/Documentation.tsx +74 -11
- package/src/components/MessageBubble.tsx +39 -2
- package/src/components/ThinkingBlock.tsx +75 -0
- package/src/components/VisualizationRenderer.tsx +27 -1
- package/src/components/constants.tsx +275 -0
- package/src/config/RagConfig.ts +47 -0
- package/src/config/constants.ts +1 -0
- package/src/config/serverConfig.ts +25 -0
- package/src/core/ConfigResolver.ts +73 -22
- package/src/core/ConfigValidator.ts +2 -1
- package/src/core/Pipeline.ts +226 -68
- package/src/core/ProviderRegistry.ts +16 -7
- package/src/core/QueryProcessor.ts +38 -4
- package/src/core/Retrivora.ts +91 -0
- package/src/core/VectorPlugin.ts +62 -8
- package/src/exceptions/index.ts +111 -0
- package/src/handlers/index.ts +73 -0
- package/src/hooks/useRagChat.ts +30 -5
- package/src/index.ts +27 -1
- package/src/lib/plugin.ts +24 -0
- package/src/llm/LLMFactory.ts +8 -4
- package/src/llm/providers/AnthropicProvider.ts +70 -20
- package/src/llm/providers/GeminiProvider.ts +14 -15
- package/src/llm/providers/OllamaProvider.ts +13 -16
- package/src/llm/providers/OpenAIProvider.ts +9 -14
- package/src/llm/providers/UniversalLLMAdapter.ts +5 -5
- package/src/llm/utils.ts +46 -0
- package/src/providers/vectordb/MongoDBProvider.ts +9 -4
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +45 -13
- package/src/rag/EntityExtractor.ts +2 -2
- package/src/rag/Reranker.ts +9 -16
- package/src/server.ts +30 -1
- package/src/types/chat.ts +9 -0
- package/src/types/props.ts +38 -1
- package/src/utils/UITransformer.ts +73 -4
- package/dist/DocumentChunker-Dh9TvmGG.d.mts +0 -45
- package/dist/DocumentChunker-Dh9TvmGG.d.ts +0 -45
package/dist/handlers/index.js
CHANGED
|
@@ -10,9 +10,6 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
10
10
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
11
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
12
12
|
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
13
|
-
var __typeError = (msg) => {
|
|
14
|
-
throw TypeError(msg);
|
|
15
|
-
};
|
|
16
13
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
17
14
|
var __spreadValues = (a, b) => {
|
|
18
15
|
for (var prop in b || (b = {}))
|
|
@@ -77,34 +74,6 @@ var __asyncGenerator = (__this, __arguments, generator) => {
|
|
|
77
74
|
}, method = (k, call, wait, clear) => it[k] = (x) => (call = new Promise((yes, no, run) => (run = () => resume(k, x, yes, no), q ? q.then(run) : run())), clear = () => q === wait && (q = 0), q = wait = call.then(clear, clear), call), q, it = {};
|
|
78
75
|
return generator = generator.apply(__this, __arguments), it[__knownSymbol("asyncIterator")] = () => it, method("next"), method("throw"), method("return"), it;
|
|
79
76
|
};
|
|
80
|
-
var __yieldStar = (value) => {
|
|
81
|
-
var obj = value[__knownSymbol("asyncIterator")], isAwait = false, method, it = {};
|
|
82
|
-
if (obj == null) {
|
|
83
|
-
obj = value[__knownSymbol("iterator")]();
|
|
84
|
-
method = (k) => it[k] = (x) => obj[k](x);
|
|
85
|
-
} else {
|
|
86
|
-
obj = obj.call(value);
|
|
87
|
-
method = (k) => it[k] = (v) => {
|
|
88
|
-
if (isAwait) {
|
|
89
|
-
isAwait = false;
|
|
90
|
-
if (k === "throw") throw v;
|
|
91
|
-
return v;
|
|
92
|
-
}
|
|
93
|
-
isAwait = true;
|
|
94
|
-
return {
|
|
95
|
-
done: false,
|
|
96
|
-
value: new __await(new Promise((resolve) => {
|
|
97
|
-
var x = obj[k](v);
|
|
98
|
-
if (!(x instanceof Object)) __typeError("Object expected");
|
|
99
|
-
resolve(x);
|
|
100
|
-
}), 1)
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
return it[__knownSymbol("iterator")] = () => it, method("next"), "throw" in obj ? method("throw") : it.throw = (x) => {
|
|
105
|
-
throw x;
|
|
106
|
-
}, "return" in obj && method("return"), it;
|
|
107
|
-
};
|
|
108
77
|
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
|
|
109
78
|
|
|
110
79
|
// src/utils/templateUtils.ts
|
|
@@ -661,6 +630,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
661
630
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
662
631
|
ON ${this.uploadTable}
|
|
663
632
|
USING hnsw (embedding vector_cosine_ops)
|
|
633
|
+
`);
|
|
634
|
+
await client.query(`
|
|
635
|
+
CREATE INDEX IF NOT EXISTS "${this.uploadTable}_fts_idx"
|
|
636
|
+
ON "${this.uploadTable}"
|
|
637
|
+
USING gin (to_tsvector('english', content))
|
|
664
638
|
`);
|
|
665
639
|
const res = await client.query(`
|
|
666
640
|
SELECT table_name
|
|
@@ -731,6 +705,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
731
705
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
732
706
|
ON "${tableName}"
|
|
733
707
|
USING hnsw (embedding vector_cosine_ops)
|
|
708
|
+
`);
|
|
709
|
+
await client.query(`
|
|
710
|
+
CREATE INDEX IF NOT EXISTS "${tableName}_fts_idx"
|
|
711
|
+
ON "${tableName}"
|
|
712
|
+
USING gin (to_tsvector('english', content))
|
|
734
713
|
`);
|
|
735
714
|
if (!this.tables.includes(tableName)) {
|
|
736
715
|
this.tables.push(tableName);
|
|
@@ -822,8 +801,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
822
801
|
const paramIdx = baseOffset + filterParams.length;
|
|
823
802
|
const synonyms = (_a3 = FIELD_SYNONYMS[key]) != null ? _a3 : [];
|
|
824
803
|
const keysToCheck = [key, ...synonyms];
|
|
825
|
-
const coalesceExprs = keysToCheck.
|
|
826
|
-
|
|
804
|
+
const coalesceExprs = keysToCheck.flatMap((k) => [
|
|
805
|
+
`metadata->>'${k}'`,
|
|
806
|
+
`to_jsonb(t)->>'${k}'`
|
|
807
|
+
]);
|
|
808
|
+
return `COALESCE(${coalesceExprs.map((expr) => `LOWER(${expr})`).join(", ")}) = LOWER($${paramIdx})`;
|
|
827
809
|
});
|
|
828
810
|
whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
829
811
|
}
|
|
@@ -834,16 +816,35 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
834
816
|
CASE WHEN LOWER(val) IN (${entityHints.map((h) => `'${h.replace(/'/g, "''")}'`).join(", ")})
|
|
835
817
|
THEN 1.0 ELSE 0.0 END
|
|
836
818
|
), 0)
|
|
837
|
-
FROM jsonb_each_text(
|
|
819
|
+
FROM jsonb_each_text(COALESCE(v.metadata, k.metadata)) AS kv(key, val)
|
|
838
820
|
WHERE key IN (${this.searchFields.map((f) => `'${f}'`).join(", ")})
|
|
839
821
|
) * 3.0` : "";
|
|
840
822
|
sqlQuery = `
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
823
|
+
WITH vector_search AS (
|
|
824
|
+
SELECT *, (1 - (embedding <=> $1::vector)) AS vector_score
|
|
825
|
+
FROM "${table}" t
|
|
826
|
+
${whereClause}
|
|
827
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
828
|
+
LIMIT ${tableLimit}
|
|
829
|
+
),
|
|
830
|
+
keyword_search AS (
|
|
831
|
+
SELECT *, COALESCE(ts_rank(to_tsvector('english', content), to_tsquery('english', $2)), 0) AS keyword_score
|
|
832
|
+
FROM "${table}" t
|
|
833
|
+
WHERE ${whereClause ? whereClause.replace("WHERE", "") : "TRUE"}
|
|
834
|
+
AND to_tsvector('english', content) @@ to_tsquery('english', $2)
|
|
835
|
+
ORDER BY keyword_score DESC
|
|
836
|
+
LIMIT ${tableLimit}
|
|
837
|
+
)
|
|
838
|
+
SELECT
|
|
839
|
+
COALESCE(v.id, k.id) AS id,
|
|
840
|
+
COALESCE(v.namespace, k.namespace) AS namespace,
|
|
841
|
+
COALESCE(v.content, k.content) AS content,
|
|
842
|
+
COALESCE(v.metadata, k.metadata) AS metadata,
|
|
843
|
+
COALESCE(v.vector_score, 0) AS vector_score,
|
|
844
|
+
COALESCE(k.keyword_score, 0) AS keyword_score,
|
|
845
|
+
(COALESCE(v.vector_score, 0) + COALESCE(k.keyword_score, 0) * 2.0 ${exactNameScoreExpr}) AS hybrid_score
|
|
846
|
+
FROM vector_search v
|
|
847
|
+
FULL OUTER JOIN keyword_search k ON v.id = k.id
|
|
847
848
|
ORDER BY hybrid_score DESC
|
|
848
849
|
LIMIT ${tableLimit}
|
|
849
850
|
`;
|
|
@@ -854,7 +855,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
854
855
|
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
855
856
|
FROM "${table}" t
|
|
856
857
|
${whereClause}
|
|
857
|
-
ORDER BY
|
|
858
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
858
859
|
LIMIT ${tableLimit}
|
|
859
860
|
`;
|
|
860
861
|
params = [vectorLiteral, ...filterParams];
|
|
@@ -1060,7 +1061,11 @@ var init_MongoDBProvider = __esm({
|
|
|
1060
1061
|
if (key === "namespace") {
|
|
1061
1062
|
vectorSearchFilter.namespace = value;
|
|
1062
1063
|
} else {
|
|
1063
|
-
|
|
1064
|
+
if (typeof value === "string") {
|
|
1065
|
+
matchFilter[key] = { $regex: new RegExp(`^${value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i") };
|
|
1066
|
+
} else {
|
|
1067
|
+
matchFilter[key] = value;
|
|
1068
|
+
}
|
|
1064
1069
|
}
|
|
1065
1070
|
}
|
|
1066
1071
|
const pipeline = [
|
|
@@ -2068,6 +2073,7 @@ __export(handlers_exports, {
|
|
|
2068
2073
|
createChatHandler: () => createChatHandler,
|
|
2069
2074
|
createHealthHandler: () => createHealthHandler,
|
|
2070
2075
|
createIngestHandler: () => createIngestHandler,
|
|
2076
|
+
createRagHandler: () => createRagHandler,
|
|
2071
2077
|
createStreamHandler: () => createStreamHandler,
|
|
2072
2078
|
createSuggestionsHandler: () => createSuggestionsHandler,
|
|
2073
2079
|
createUploadHandler: () => createUploadHandler,
|
|
@@ -2149,7 +2155,7 @@ function readEnum(env, name, fallback, allowed) {
|
|
|
2149
2155
|
throw new Error(`[getRagConfig] ${name} must be one of: ${allowed.join(", ")}`);
|
|
2150
2156
|
}
|
|
2151
2157
|
function getEnvConfig(env = process.env, base) {
|
|
2152
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga;
|
|
2158
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja;
|
|
2153
2159
|
const projectId = (_c = (_b = (_a = readString(env, "RAG_PROJECT_ID")) != null ? _a : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : base == null ? void 0 : base.projectId) != null ? _c : "__default__";
|
|
2154
2160
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
2155
2161
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
@@ -2219,7 +2225,7 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2219
2225
|
rest: "default",
|
|
2220
2226
|
custom: "default"
|
|
2221
2227
|
};
|
|
2222
|
-
return {
|
|
2228
|
+
return __spreadValues({
|
|
2223
2229
|
projectId,
|
|
2224
2230
|
vectorDb: {
|
|
2225
2231
|
provider: vectorProvider,
|
|
@@ -2235,7 +2241,9 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2235
2241
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
|
|
2236
2242
|
temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
|
|
2237
2243
|
options: {
|
|
2238
|
-
profile: readString(env, "LLM_UNIVERSAL_PROFILE")
|
|
2244
|
+
profile: readString(env, "LLM_UNIVERSAL_PROFILE"),
|
|
2245
|
+
thinking: readString(env, "LLM_THINKING") === "true" || readString(env, "LLM_THINKING") === "enabled" ? true : readString(env, "LLM_THINKING") === "false" ? false : void 0,
|
|
2246
|
+
thinkingBudget: env.LLM_THINKING_BUDGET ? parseInt(env.LLM_THINKING_BUDGET, 10) : void 0
|
|
2239
2247
|
}
|
|
2240
2248
|
},
|
|
2241
2249
|
embedding: {
|
|
@@ -2267,9 +2275,34 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2267
2275
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
2268
2276
|
scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
|
|
2269
2277
|
chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
|
|
2270
|
-
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
|
|
2278
|
+
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
|
|
2279
|
+
filterableFields: (_Ha = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Ha.split(",").map((f) => f.trim()),
|
|
2280
|
+
// Query pipeline toggles — read from .env.local
|
|
2281
|
+
useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
|
|
2282
|
+
useReranking: readString(env, "RAG_USE_RERANKING") === "true",
|
|
2283
|
+
useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
|
|
2284
|
+
architecture: (_Ia = readString(env, "RAG_ARCHITECTURE")) != null ? _Ia : "simple",
|
|
2285
|
+
chunkingStrategy: (_Ja = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _Ja : "recursive",
|
|
2286
|
+
uiMapping: (() => {
|
|
2287
|
+
const raw = readString(env, "RAG_UI_MAPPING");
|
|
2288
|
+
if (!raw) return void 0;
|
|
2289
|
+
try {
|
|
2290
|
+
return JSON.parse(raw);
|
|
2291
|
+
} catch (e) {
|
|
2292
|
+
return void 0;
|
|
2293
|
+
}
|
|
2294
|
+
})()
|
|
2271
2295
|
}
|
|
2272
|
-
}
|
|
2296
|
+
}, readString(env, "GRAPH_DB_PROVIDER") ? {
|
|
2297
|
+
graphDb: {
|
|
2298
|
+
provider: readString(env, "GRAPH_DB_PROVIDER"),
|
|
2299
|
+
options: {
|
|
2300
|
+
uri: readString(env, "GRAPH_DB_URI"),
|
|
2301
|
+
username: readString(env, "GRAPH_DB_USERNAME"),
|
|
2302
|
+
password: readString(env, "GRAPH_DB_PASSWORD")
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
} : {});
|
|
2273
2306
|
}
|
|
2274
2307
|
|
|
2275
2308
|
// src/core/ConfigResolver.ts
|
|
@@ -2298,6 +2331,20 @@ var ConfigResolver = class {
|
|
|
2298
2331
|
rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
|
|
2299
2332
|
});
|
|
2300
2333
|
}
|
|
2334
|
+
/**
|
|
2335
|
+
* Resolves the public SDK config shape used by `new Retrivora({...})`.
|
|
2336
|
+
* Supports aliases from the product prompt while preserving existing env
|
|
2337
|
+
* fallback behavior.
|
|
2338
|
+
*/
|
|
2339
|
+
static resolveUniversal(hostConfig, env = process.env) {
|
|
2340
|
+
var _a;
|
|
2341
|
+
if (!hostConfig) return this.resolve(void 0, env);
|
|
2342
|
+
const normalized = __spreadProps(__spreadValues({}, hostConfig), {
|
|
2343
|
+
vectorDb: (_a = hostConfig.vectorDb) != null ? _a : hostConfig.vectorDatabase,
|
|
2344
|
+
rag: this.mergeRetrievalWorkflow(hostConfig.rag, hostConfig.retrieval, hostConfig.workflow)
|
|
2345
|
+
});
|
|
2346
|
+
return this.resolve(normalized, env);
|
|
2347
|
+
}
|
|
2301
2348
|
/**
|
|
2302
2349
|
* Validates the configuration for required fields.
|
|
2303
2350
|
*/
|
|
@@ -2312,10 +2359,53 @@ var ConfigResolver = class {
|
|
|
2312
2359
|
throw new Error("[ConfigResolver] llm.provider is required");
|
|
2313
2360
|
}
|
|
2314
2361
|
}
|
|
2362
|
+
static mergeRetrievalWorkflow(rag, retrieval, workflow) {
|
|
2363
|
+
var _a, _b, _c, _d, _e;
|
|
2364
|
+
if (!rag && !retrieval && !workflow) return void 0;
|
|
2365
|
+
const normalized = __spreadValues({}, rag != null ? rag : {});
|
|
2366
|
+
if (retrieval) {
|
|
2367
|
+
normalized.topK = (_a = retrieval.topK) != null ? _a : normalized.topK;
|
|
2368
|
+
normalized.scoreThreshold = (_b = retrieval.scoreThreshold) != null ? _b : normalized.scoreThreshold;
|
|
2369
|
+
normalized.useReranking = (_c = retrieval.useReranking) != null ? _c : normalized.useReranking;
|
|
2370
|
+
if (retrieval.strategy === "hybrid") normalized.architecture = (_d = normalized.architecture) != null ? _d : "hybrid";
|
|
2371
|
+
if (retrieval.strategy === "agentic") normalized.architecture = "agentic";
|
|
2372
|
+
if (retrieval.strategy === "contextual-compression") normalized.useReranking = true;
|
|
2373
|
+
}
|
|
2374
|
+
if (workflow == null ? void 0 : workflow.type) {
|
|
2375
|
+
const type = workflow.type;
|
|
2376
|
+
if (type === "agentic" || type === "agentic-rag") normalized.architecture = "agentic";
|
|
2377
|
+
else if (type === "hybrid-rag") normalized.architecture = "hybrid";
|
|
2378
|
+
else if (type === "graph-rag") {
|
|
2379
|
+
normalized.architecture = "graph";
|
|
2380
|
+
normalized.useGraphRetrieval = true;
|
|
2381
|
+
} else if (type === "simple-rag" || type === "rag") {
|
|
2382
|
+
normalized.architecture = (_e = normalized.architecture) != null ? _e : "simple";
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
return normalized;
|
|
2386
|
+
}
|
|
2315
2387
|
};
|
|
2316
2388
|
|
|
2317
2389
|
// src/llm/providers/OpenAIProvider.ts
|
|
2318
2390
|
var import_openai = __toESM(require("openai"));
|
|
2391
|
+
|
|
2392
|
+
// src/llm/utils.ts
|
|
2393
|
+
function buildSystemContent(systemPrompt, context) {
|
|
2394
|
+
const noContext = !context || context.trim() === "" || context.trim() === "No relevant context found.";
|
|
2395
|
+
if (systemPrompt.includes("{{context}}")) {
|
|
2396
|
+
return systemPrompt.replace("{{context}}", noContext ? "" : context);
|
|
2397
|
+
}
|
|
2398
|
+
if (noContext) {
|
|
2399
|
+
return systemPrompt;
|
|
2400
|
+
}
|
|
2401
|
+
return `${systemPrompt}
|
|
2402
|
+
|
|
2403
|
+
---
|
|
2404
|
+
Context:
|
|
2405
|
+
${context}`;
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
// src/llm/providers/OpenAIProvider.ts
|
|
2319
2409
|
var OpenAIProvider = class {
|
|
2320
2410
|
constructor(llmConfig, embeddingConfig) {
|
|
2321
2411
|
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
@@ -2379,16 +2469,10 @@ var OpenAIProvider = class {
|
|
|
2379
2469
|
}
|
|
2380
2470
|
async chat(messages, context, options) {
|
|
2381
2471
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2384
|
-
Context:
|
|
2385
|
-
${context}`;
|
|
2472
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2386
2473
|
const systemMessage = {
|
|
2387
2474
|
role: "system",
|
|
2388
|
-
content:
|
|
2389
|
-
|
|
2390
|
-
Context:
|
|
2391
|
-
${context}`
|
|
2475
|
+
content: buildSystemContent(basePrompt, context)
|
|
2392
2476
|
};
|
|
2393
2477
|
const formattedMessages = [
|
|
2394
2478
|
systemMessage,
|
|
@@ -2409,16 +2493,10 @@ ${context}`
|
|
|
2409
2493
|
chatStream(messages, context, options) {
|
|
2410
2494
|
return __asyncGenerator(this, null, function* () {
|
|
2411
2495
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2412
|
-
const
|
|
2413
|
-
|
|
2414
|
-
Context:
|
|
2415
|
-
${context}`;
|
|
2496
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2416
2497
|
const systemMessage = {
|
|
2417
2498
|
role: "system",
|
|
2418
|
-
content:
|
|
2419
|
-
|
|
2420
|
-
Context:
|
|
2421
|
-
${context}`
|
|
2499
|
+
content: buildSystemContent(basePrompt, context)
|
|
2422
2500
|
};
|
|
2423
2501
|
const formattedMessages = [
|
|
2424
2502
|
systemMessage,
|
|
@@ -2539,58 +2617,87 @@ var AnthropicProvider = class {
|
|
|
2539
2617
|
};
|
|
2540
2618
|
}
|
|
2541
2619
|
async chat(messages, context, options) {
|
|
2542
|
-
var _a, _b, _c, _d;
|
|
2543
|
-
const
|
|
2544
|
-
|
|
2545
|
-
Context:
|
|
2546
|
-
${context}`;
|
|
2547
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2548
|
-
|
|
2549
|
-
Context:
|
|
2550
|
-
${context}`;
|
|
2620
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2621
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the context below to answer the user's question accurately.";
|
|
2622
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2551
2623
|
const anthropicMessages = messages.map((m) => ({
|
|
2552
2624
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2553
2625
|
content: m.content
|
|
2554
2626
|
}));
|
|
2555
|
-
const
|
|
2627
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2628
|
+
const isThinkingEnabled = ((_c = this.llmConfig.options) == null ? void 0 : _c.thinking) === true || ((_d = this.llmConfig.options) == null ? void 0 : _d.thinking) === "enabled" || isClaude37 && ((_e = this.llmConfig.options) == null ? void 0 : _e.thinking) !== false;
|
|
2629
|
+
const extraParams = {};
|
|
2630
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2631
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2632
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2633
|
+
const budget = Math.min(
|
|
2634
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2635
|
+
maxTokens - 1
|
|
2636
|
+
);
|
|
2637
|
+
extraParams.thinking = {
|
|
2638
|
+
type: "enabled",
|
|
2639
|
+
budget_tokens: budget
|
|
2640
|
+
};
|
|
2641
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2642
|
+
}
|
|
2643
|
+
const response = await this.client.messages.create(__spreadValues({
|
|
2556
2644
|
model: this.llmConfig.model,
|
|
2557
|
-
max_tokens: (
|
|
2645
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2558
2646
|
system,
|
|
2559
2647
|
messages: anthropicMessages
|
|
2560
|
-
});
|
|
2561
|
-
|
|
2562
|
-
|
|
2648
|
+
}, extraParams));
|
|
2649
|
+
let reply = "";
|
|
2650
|
+
for (const block of response.content) {
|
|
2651
|
+
if (block.type === "text") {
|
|
2652
|
+
reply += block.text;
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
return reply;
|
|
2563
2656
|
}
|
|
2564
2657
|
chatStream(messages, context, options) {
|
|
2565
2658
|
return __asyncGenerator(this, null, function* () {
|
|
2566
|
-
var _a, _b, _c, _d;
|
|
2567
|
-
const
|
|
2568
|
-
|
|
2569
|
-
Context:
|
|
2570
|
-
${context}`;
|
|
2571
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2572
|
-
|
|
2573
|
-
Context:
|
|
2574
|
-
${context}`;
|
|
2659
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2660
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the context below to answer the user's question accurately.";
|
|
2661
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2575
2662
|
const anthropicMessages = messages.map((m) => ({
|
|
2576
2663
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2577
2664
|
content: m.content
|
|
2578
2665
|
}));
|
|
2579
|
-
const
|
|
2666
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2667
|
+
const isThinkingEnabled = ((_c = this.llmConfig.options) == null ? void 0 : _c.thinking) === true || ((_d = this.llmConfig.options) == null ? void 0 : _d.thinking) === "enabled" || isClaude37 && ((_e = this.llmConfig.options) == null ? void 0 : _e.thinking) !== false;
|
|
2668
|
+
const extraParams = {};
|
|
2669
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2670
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2671
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2672
|
+
const budget = Math.min(
|
|
2673
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2674
|
+
maxTokens - 1
|
|
2675
|
+
);
|
|
2676
|
+
extraParams.thinking = {
|
|
2677
|
+
type: "enabled",
|
|
2678
|
+
budget_tokens: budget
|
|
2679
|
+
};
|
|
2680
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2681
|
+
}
|
|
2682
|
+
const stream = yield new __await(this.client.messages.create(__spreadValues({
|
|
2580
2683
|
model: this.llmConfig.model,
|
|
2581
|
-
max_tokens: (
|
|
2684
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2582
2685
|
system,
|
|
2583
2686
|
messages: anthropicMessages,
|
|
2584
2687
|
stream: true
|
|
2585
|
-
}));
|
|
2688
|
+
}, extraParams)));
|
|
2586
2689
|
if (!stream) {
|
|
2587
2690
|
throw new Error("[AnthropicProvider] messages.create stream is undefined");
|
|
2588
2691
|
}
|
|
2589
2692
|
try {
|
|
2590
2693
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
2591
2694
|
const chunk = temp.value;
|
|
2592
|
-
if (chunk.type === "content_block_delta"
|
|
2593
|
-
|
|
2695
|
+
if (chunk.type === "content_block_delta") {
|
|
2696
|
+
if (chunk.delta.type === "text_delta") {
|
|
2697
|
+
yield chunk.delta.text;
|
|
2698
|
+
} else if (chunk.delta.type === "thinking_delta") {
|
|
2699
|
+
yield `\0THINKING\0${chunk.delta.thinking}`;
|
|
2700
|
+
}
|
|
2594
2701
|
}
|
|
2595
2702
|
}
|
|
2596
2703
|
} catch (temp) {
|
|
@@ -2634,9 +2741,10 @@ ${context}`;
|
|
|
2634
2741
|
var import_axios = __toESM(require("axios"));
|
|
2635
2742
|
var OllamaProvider = class {
|
|
2636
2743
|
constructor(llmConfig, embeddingConfig) {
|
|
2637
|
-
var _a;
|
|
2744
|
+
var _a, _b;
|
|
2638
2745
|
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
2639
|
-
|
|
2746
|
+
const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
|
|
2747
|
+
this.http = import_axios.default.create({ baseURL, timeout });
|
|
2640
2748
|
this.llmConfig = llmConfig;
|
|
2641
2749
|
this.embeddingConfig = embeddingConfig;
|
|
2642
2750
|
}
|
|
@@ -2692,14 +2800,15 @@ var OllamaProvider = class {
|
|
|
2692
2800
|
};
|
|
2693
2801
|
}
|
|
2694
2802
|
async chat(messages, context, options) {
|
|
2695
|
-
var _a, _b, _c, _d;
|
|
2696
|
-
const
|
|
2803
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2804
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the provided context to answer the user's question.";
|
|
2805
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2697
2806
|
const { data } = await this.http.post("/api/chat", {
|
|
2698
2807
|
model: this.llmConfig.model,
|
|
2699
2808
|
stream: false,
|
|
2700
2809
|
options: {
|
|
2701
|
-
temperature: (
|
|
2702
|
-
num_predict: (
|
|
2810
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2811
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2703
2812
|
},
|
|
2704
2813
|
messages: [
|
|
2705
2814
|
{ role: "system", content: system },
|
|
@@ -2710,14 +2819,15 @@ var OllamaProvider = class {
|
|
|
2710
2819
|
}
|
|
2711
2820
|
chatStream(messages, context, options) {
|
|
2712
2821
|
return __asyncGenerator(this, null, function* () {
|
|
2713
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2714
|
-
const
|
|
2822
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2823
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Use the provided context to answer the user's question.";
|
|
2824
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2715
2825
|
const response = yield new __await(this.http.post("/api/chat", {
|
|
2716
2826
|
model: this.llmConfig.model,
|
|
2717
2827
|
stream: true,
|
|
2718
2828
|
options: {
|
|
2719
|
-
temperature: (
|
|
2720
|
-
num_predict: (
|
|
2829
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2830
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2721
2831
|
},
|
|
2722
2832
|
messages: [
|
|
2723
2833
|
{ role: "system", content: system },
|
|
@@ -2739,7 +2849,7 @@ var OllamaProvider = class {
|
|
|
2739
2849
|
if (!line.trim()) continue;
|
|
2740
2850
|
try {
|
|
2741
2851
|
const json = JSON.parse(line);
|
|
2742
|
-
if ((
|
|
2852
|
+
if ((_g = json.message) == null ? void 0 : _g.content) {
|
|
2743
2853
|
yield json.message.content;
|
|
2744
2854
|
}
|
|
2745
2855
|
if (json.done) return;
|
|
@@ -2761,26 +2871,12 @@ var OllamaProvider = class {
|
|
|
2761
2871
|
if (lineBuffer.trim()) {
|
|
2762
2872
|
try {
|
|
2763
2873
|
const json = JSON.parse(lineBuffer);
|
|
2764
|
-
if ((
|
|
2874
|
+
if ((_h = json.message) == null ? void 0 : _h.content) yield json.message.content;
|
|
2765
2875
|
} catch (e) {
|
|
2766
2876
|
}
|
|
2767
2877
|
}
|
|
2768
2878
|
});
|
|
2769
2879
|
}
|
|
2770
|
-
buildSystemPrompt(context, overridePrompt) {
|
|
2771
|
-
var _a;
|
|
2772
|
-
if (overridePrompt) {
|
|
2773
|
-
return overridePrompt;
|
|
2774
|
-
}
|
|
2775
|
-
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
2776
|
-
|
|
2777
|
-
Context:
|
|
2778
|
-
${context}`;
|
|
2779
|
-
return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
2780
|
-
|
|
2781
|
-
Context:
|
|
2782
|
-
${context}`;
|
|
2783
|
-
}
|
|
2784
2880
|
async embed(text, options) {
|
|
2785
2881
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2786
2882
|
const model = (_c = (_b = options == null ? void 0 : options.model) != null ? _b : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _c : "nomic-embed-text";
|
|
@@ -2927,21 +3023,6 @@ var GeminiProvider = class {
|
|
|
2927
3023
|
var _a, _b;
|
|
2928
3024
|
return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
|
|
2929
3025
|
}
|
|
2930
|
-
/**
|
|
2931
|
-
* Build the system instruction string, inserting the RAG context either via
|
|
2932
|
-
* the `{{context}}` placeholder or by appending it.
|
|
2933
|
-
*/
|
|
2934
|
-
buildSystemInstruction(context) {
|
|
2935
|
-
var _a;
|
|
2936
|
-
const base = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
2937
|
-
|
|
2938
|
-
Context:
|
|
2939
|
-
${context}`;
|
|
2940
|
-
return base.includes("{{context}}") ? base.replace("{{context}}", context) : `${base}
|
|
2941
|
-
|
|
2942
|
-
Context:
|
|
2943
|
-
${context}`;
|
|
2944
|
-
}
|
|
2945
3026
|
/**
|
|
2946
3027
|
* Convert ChatMessage[] to the Gemini contents format.
|
|
2947
3028
|
*
|
|
@@ -2960,16 +3041,17 @@ ${context}`;
|
|
|
2960
3041
|
// ILLMProvider — chat
|
|
2961
3042
|
// -------------------------------------------------------------------------
|
|
2962
3043
|
async chat(messages, context, options) {
|
|
2963
|
-
var _a, _b, _c, _d;
|
|
3044
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3045
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2964
3046
|
const model = this.client.getGenerativeModel({
|
|
2965
3047
|
model: this.llmConfig.model,
|
|
2966
|
-
systemInstruction:
|
|
3048
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2967
3049
|
});
|
|
2968
3050
|
const result = await model.generateContent({
|
|
2969
3051
|
contents: this.buildGeminiContents(messages),
|
|
2970
3052
|
generationConfig: {
|
|
2971
|
-
temperature: (
|
|
2972
|
-
maxOutputTokens: (
|
|
3053
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3054
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2973
3055
|
stopSequences: options == null ? void 0 : options.stop
|
|
2974
3056
|
}
|
|
2975
3057
|
});
|
|
@@ -2977,16 +3059,17 @@ ${context}`;
|
|
|
2977
3059
|
}
|
|
2978
3060
|
chatStream(messages, context, options) {
|
|
2979
3061
|
return __asyncGenerator(this, null, function* () {
|
|
2980
|
-
var _a, _b, _c, _d;
|
|
3062
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3063
|
+
const basePrompt = (_b = (_a = options == null ? void 0 : options.systemPrompt) != null ? _a : this.llmConfig.systemPrompt) != null ? _b : "You are a helpful assistant. Answer questions based on the provided context.";
|
|
2981
3064
|
const model = this.client.getGenerativeModel({
|
|
2982
3065
|
model: this.llmConfig.model,
|
|
2983
|
-
systemInstruction:
|
|
3066
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2984
3067
|
});
|
|
2985
3068
|
const result = yield new __await(model.generateContentStream({
|
|
2986
3069
|
contents: this.buildGeminiContents(messages),
|
|
2987
3070
|
generationConfig: {
|
|
2988
|
-
temperature: (
|
|
2989
|
-
maxOutputTokens: (
|
|
3071
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3072
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2990
3073
|
stopSequences: options == null ? void 0 : options.stop
|
|
2991
3074
|
}
|
|
2992
3075
|
}));
|
|
@@ -3314,6 +3397,78 @@ ${context != null ? context : "None"}` },
|
|
|
3314
3397
|
}
|
|
3315
3398
|
};
|
|
3316
3399
|
|
|
3400
|
+
// src/exceptions/index.ts
|
|
3401
|
+
var RetrivoraError = class extends Error {
|
|
3402
|
+
constructor(message, code, details) {
|
|
3403
|
+
super(message);
|
|
3404
|
+
this.name = new.target.name;
|
|
3405
|
+
this.code = code;
|
|
3406
|
+
this.details = details;
|
|
3407
|
+
}
|
|
3408
|
+
};
|
|
3409
|
+
var ProviderNotFoundException = class extends RetrivoraError {
|
|
3410
|
+
constructor(providerType, provider, details) {
|
|
3411
|
+
super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
|
|
3412
|
+
}
|
|
3413
|
+
};
|
|
3414
|
+
var EmbeddingFailedException = class extends RetrivoraError {
|
|
3415
|
+
constructor(message = "Embedding generation failed", details) {
|
|
3416
|
+
super(message, "EMBEDDING_FAILED", details);
|
|
3417
|
+
}
|
|
3418
|
+
};
|
|
3419
|
+
var RetrievalException = class extends RetrivoraError {
|
|
3420
|
+
constructor(message = "Retrieval failed", details) {
|
|
3421
|
+
super(message, "RETRIEVAL_FAILED", details);
|
|
3422
|
+
}
|
|
3423
|
+
};
|
|
3424
|
+
var RateLimitException = class extends RetrivoraError {
|
|
3425
|
+
constructor(message = "Provider rate limit exceeded", details) {
|
|
3426
|
+
super(message, "RATE_LIMITED", details);
|
|
3427
|
+
}
|
|
3428
|
+
};
|
|
3429
|
+
var ConfigurationException = class extends RetrivoraError {
|
|
3430
|
+
constructor(message, details) {
|
|
3431
|
+
super(message, "CONFIGURATION_ERROR", details);
|
|
3432
|
+
}
|
|
3433
|
+
};
|
|
3434
|
+
var AuthenticationException = class extends RetrivoraError {
|
|
3435
|
+
constructor(message = "Provider authentication failed", details) {
|
|
3436
|
+
super(message, "AUTHENTICATION_ERROR", details);
|
|
3437
|
+
}
|
|
3438
|
+
};
|
|
3439
|
+
function wrapError(err, defaultCode, defaultMessage) {
|
|
3440
|
+
var _a;
|
|
3441
|
+
if (err instanceof RetrivoraError) {
|
|
3442
|
+
return err;
|
|
3443
|
+
}
|
|
3444
|
+
const error = err;
|
|
3445
|
+
const message = (error == null ? void 0 : error.message) || defaultMessage || String(err);
|
|
3446
|
+
const status = (error == null ? void 0 : error.status) || (error == null ? void 0 : error.statusCode) || ((_a = error == null ? void 0 : error.response) == null ? void 0 : _a.status);
|
|
3447
|
+
const code = error == null ? void 0 : error.code;
|
|
3448
|
+
if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
|
|
3449
|
+
return new RateLimitException(message, err);
|
|
3450
|
+
}
|
|
3451
|
+
if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
|
|
3452
|
+
return new AuthenticationException(message, err);
|
|
3453
|
+
}
|
|
3454
|
+
switch (defaultCode) {
|
|
3455
|
+
case "PROVIDER_NOT_FOUND":
|
|
3456
|
+
return new ProviderNotFoundException("provider", message, err);
|
|
3457
|
+
case "EMBEDDING_FAILED":
|
|
3458
|
+
return new EmbeddingFailedException(message, err);
|
|
3459
|
+
case "RETRIEVAL_FAILED":
|
|
3460
|
+
return new RetrievalException(message, err);
|
|
3461
|
+
case "RATE_LIMITED":
|
|
3462
|
+
return new RateLimitException(message, err);
|
|
3463
|
+
case "CONFIGURATION_ERROR":
|
|
3464
|
+
return new ConfigurationException(message, err);
|
|
3465
|
+
case "AUTHENTICATION_ERROR":
|
|
3466
|
+
return new AuthenticationException(message, err);
|
|
3467
|
+
default:
|
|
3468
|
+
return new RetrivoraError(message, defaultCode, err);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3317
3472
|
// src/llm/LLMFactory.ts
|
|
3318
3473
|
var customProviders = /* @__PURE__ */ new Map();
|
|
3319
3474
|
var LLMFactory = class _LLMFactory {
|
|
@@ -3359,7 +3514,7 @@ var LLMFactory = class _LLMFactory {
|
|
|
3359
3514
|
];
|
|
3360
3515
|
}
|
|
3361
3516
|
static create(llmConfig, embeddingConfig) {
|
|
3362
|
-
var _a, _b;
|
|
3517
|
+
var _a, _b, _c;
|
|
3363
3518
|
switch (llmConfig.provider) {
|
|
3364
3519
|
case "openai":
|
|
3365
3520
|
return new OpenAIProvider(llmConfig, embeddingConfig);
|
|
@@ -3382,8 +3537,13 @@ var LLMFactory = class _LLMFactory {
|
|
|
3382
3537
|
if (llmConfig.baseUrl || ((_b = llmConfig.options) == null ? void 0 : _b.baseUrl)) {
|
|
3383
3538
|
return new UniversalLLMAdapter(llmConfig);
|
|
3384
3539
|
}
|
|
3385
|
-
throw new
|
|
3386
|
-
|
|
3540
|
+
throw new ProviderNotFoundException(
|
|
3541
|
+
"llm",
|
|
3542
|
+
(_c = llmConfig.provider) != null ? _c : "undefined",
|
|
3543
|
+
{
|
|
3544
|
+
message: `Unknown provider "${llmConfig.provider}". Register a custom provider with LLMFactory.register().`,
|
|
3545
|
+
available: _LLMFactory.listProviders()
|
|
3546
|
+
}
|
|
3387
3547
|
);
|
|
3388
3548
|
}
|
|
3389
3549
|
}
|
|
@@ -3514,7 +3674,7 @@ var ProviderRegistry = class {
|
|
|
3514
3674
|
return UniversalVectorProvider2;
|
|
3515
3675
|
}
|
|
3516
3676
|
default:
|
|
3517
|
-
throw new
|
|
3677
|
+
throw new ProviderNotFoundException("vector", provider);
|
|
3518
3678
|
}
|
|
3519
3679
|
}
|
|
3520
3680
|
static async createVectorProvider(config) {
|
|
@@ -3532,12 +3692,18 @@ var ProviderRegistry = class {
|
|
|
3532
3692
|
return new SimpleGraphProvider2(config);
|
|
3533
3693
|
}
|
|
3534
3694
|
default:
|
|
3535
|
-
throw new
|
|
3695
|
+
throw new ProviderNotFoundException("graph", provider);
|
|
3536
3696
|
}
|
|
3537
3697
|
}
|
|
3538
3698
|
static createLLMProvider(llmConfig, embeddingConfig) {
|
|
3539
3699
|
return LLMFactory.create(llmConfig, embeddingConfig);
|
|
3540
3700
|
}
|
|
3701
|
+
static registerLLMProvider(name, factory) {
|
|
3702
|
+
LLMFactory.register(name, factory);
|
|
3703
|
+
}
|
|
3704
|
+
static createEmbeddingProvider(embeddingConfig) {
|
|
3705
|
+
return LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
3706
|
+
}
|
|
3541
3707
|
};
|
|
3542
3708
|
ProviderRegistry.vectorProviders = {};
|
|
3543
3709
|
ProviderRegistry.graphProviders = {};
|
|
@@ -3684,8 +3850,8 @@ var ConfigValidator = class {
|
|
|
3684
3850
|
const errorItems = errors.filter((e) => e.severity === "error");
|
|
3685
3851
|
if (errorItems.length > 0) {
|
|
3686
3852
|
const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
|
|
3687
|
-
throw new
|
|
3688
|
-
${message}
|
|
3853
|
+
throw new ConfigurationException(`[ConfigValidator] Configuration validation failed:
|
|
3854
|
+
${message}`, errorItems);
|
|
3689
3855
|
}
|
|
3690
3856
|
}
|
|
3691
3857
|
};
|
|
@@ -3872,20 +4038,17 @@ var Reranker = class {
|
|
|
3872
4038
|
}
|
|
3873
4039
|
try {
|
|
3874
4040
|
const topN = matches.slice(0, 10);
|
|
3875
|
-
const
|
|
3876
|
-
|
|
3877
|
-
Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
|
|
3878
|
-
Use the indices provided in brackets like [0], [1], etc.
|
|
3879
|
-
Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
|
|
3880
|
-
|
|
3881
|
-
Query: "${query}"
|
|
4041
|
+
const response = await llm.chat(
|
|
4042
|
+
[{ role: "user", content: `Query: "${query}"
|
|
3882
4043
|
|
|
3883
4044
|
Documents:
|
|
3884
|
-
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}
|
|
3885
|
-
const response = await llm.chat(
|
|
3886
|
-
[{ role: "user", content: prompt }],
|
|
4045
|
+
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}` }],
|
|
3887
4046
|
"",
|
|
3888
|
-
{
|
|
4047
|
+
{
|
|
4048
|
+
systemPrompt: 'You are a relevance ranking expert. Given the user query and a list of retrieved document chunks, rank the chunks by relevance to the query. Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant. Use the indices provided in brackets like [0], [1], etc. Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.',
|
|
4049
|
+
temperature: 0,
|
|
4050
|
+
maxTokens: 50
|
|
4051
|
+
}
|
|
3889
4052
|
);
|
|
3890
4053
|
const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
|
|
3891
4054
|
const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
|
|
@@ -4391,6 +4554,18 @@ var QueryProcessor = class {
|
|
|
4391
4554
|
}, normalizedField ? { field: normalizedField } : {}));
|
|
4392
4555
|
}
|
|
4393
4556
|
};
|
|
4557
|
+
const activeValidFields = validFields.length > 0 ? validFields : ["brand", "category", "price", "rating", "stock", "stock_quantity", "status", "name", "title"];
|
|
4558
|
+
const resolveValidField = (fieldStr) => {
|
|
4559
|
+
const cleaned = this.normalizeHintValue(fieldStr).replace(/\b(?:provide|show|get|give|list|all|the|a|an|of|organizations?|companies?|records?|items?|whose|with|having|where|that|which|have|has|is|are|was|were)\b/gi, " ").replace(/\s+/g, " ").trim();
|
|
4560
|
+
const comparable = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4561
|
+
const cleanedComparable = comparable(cleaned);
|
|
4562
|
+
if (!cleanedComparable) return null;
|
|
4563
|
+
const matchedField = activeValidFields.find((fieldName) => {
|
|
4564
|
+
const candidate = comparable(fieldName);
|
|
4565
|
+
return candidate === cleanedComparable || candidate.includes(cleanedComparable) || cleanedComparable.includes(candidate);
|
|
4566
|
+
});
|
|
4567
|
+
return matchedField != null ? matchedField : null;
|
|
4568
|
+
};
|
|
4394
4569
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
4395
4570
|
addHint(match[1]);
|
|
4396
4571
|
}
|
|
@@ -4415,7 +4590,13 @@ var QueryProcessor = class {
|
|
|
4415
4590
|
const field = match[1];
|
|
4416
4591
|
const value = match[2];
|
|
4417
4592
|
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4418
|
-
|
|
4593
|
+
const resolvedField = resolveValidField(field);
|
|
4594
|
+
if (resolvedField) {
|
|
4595
|
+
addHint(value, resolvedField);
|
|
4596
|
+
} else {
|
|
4597
|
+
addHint(value);
|
|
4598
|
+
addHint(field);
|
|
4599
|
+
}
|
|
4419
4600
|
}
|
|
4420
4601
|
}
|
|
4421
4602
|
if (validFields.length > 0) {
|
|
@@ -4461,12 +4642,16 @@ var QueryProcessor = class {
|
|
|
4461
4642
|
];
|
|
4462
4643
|
for (const pattern of fieldValuePatterns) {
|
|
4463
4644
|
for (const match of question.matchAll(pattern)) {
|
|
4464
|
-
const field =
|
|
4645
|
+
const field = (_c = match[1]) != null ? _c : "";
|
|
4465
4646
|
const value = (_d = match[2]) != null ? _d : "";
|
|
4466
|
-
|
|
4467
|
-
|
|
4647
|
+
const resolvedField = resolveValidField(field);
|
|
4648
|
+
if (resolvedField) {
|
|
4649
|
+
addHint(value, resolvedField);
|
|
4468
4650
|
} else {
|
|
4469
4651
|
addHint(value);
|
|
4652
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4653
|
+
addHint(field);
|
|
4654
|
+
}
|
|
4470
4655
|
}
|
|
4471
4656
|
}
|
|
4472
4657
|
}
|
|
@@ -4693,7 +4878,7 @@ var LLMRouter = class {
|
|
|
4693
4878
|
|
|
4694
4879
|
// src/utils/UITransformer.ts
|
|
4695
4880
|
init_synonyms();
|
|
4696
|
-
var UITransformer = class {
|
|
4881
|
+
var UITransformer = class _UITransformer {
|
|
4697
4882
|
// ─── Public Entry Points ─────────────────────────────────────────────────
|
|
4698
4883
|
/**
|
|
4699
4884
|
* Heuristic-only transform (no LLM required).
|
|
@@ -4755,7 +4940,7 @@ var UITransformer = class {
|
|
|
4755
4940
|
static async analyzeAndDecide(query, sources, llm) {
|
|
4756
4941
|
let intent;
|
|
4757
4942
|
try {
|
|
4758
|
-
intent = await this.detectIntent(query, llm);
|
|
4943
|
+
intent = await this.detectIntent(query, llm, sources);
|
|
4759
4944
|
console.debug("[UITransformer] Detected intent:", intent);
|
|
4760
4945
|
} catch (err) {
|
|
4761
4946
|
console.warn("[UITransformer] Intent detection failed; using heuristic.", err);
|
|
@@ -4773,11 +4958,23 @@ var UITransformer = class {
|
|
|
4773
4958
|
try {
|
|
4774
4959
|
const context = this.buildContextSummary(sources);
|
|
4775
4960
|
const systemPrompt = this.buildVisualizationSystemPrompt();
|
|
4961
|
+
const profile = this.profileData(sources);
|
|
4962
|
+
const schemaContext = `
|
|
4963
|
+
RETRIEVED DATA SCHEMA:
|
|
4964
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4965
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4966
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4967
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4968
|
+
- Text/Other fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
4969
|
+
`;
|
|
4776
4970
|
const userPrompt = [
|
|
4777
4971
|
`USER QUESTION: ${query}`,
|
|
4778
4972
|
"",
|
|
4779
4973
|
`DETECTED INTENT (JSON): ${JSON.stringify(intent)}`,
|
|
4780
4974
|
"",
|
|
4975
|
+
`RETRIEVED DATA SCHEMA:`,
|
|
4976
|
+
schemaContext,
|
|
4977
|
+
"",
|
|
4781
4978
|
"RETRIEVED DATA (JSON):",
|
|
4782
4979
|
context
|
|
4783
4980
|
].join("\n");
|
|
@@ -4788,6 +4985,20 @@ var UITransformer = class {
|
|
|
4788
4985
|
);
|
|
4789
4986
|
const parsed = this.parseTransformationResponse(rawResponse);
|
|
4790
4987
|
if (parsed) {
|
|
4988
|
+
let isCompatible = true;
|
|
4989
|
+
if (parsed.type === "line_chart" && (profile.dateFields.length === 0 || profile.numericFields.length === 0)) {
|
|
4990
|
+
isCompatible = false;
|
|
4991
|
+
} else if (["bar_chart", "horizontal_bar", "pie_chart", "donut_chart"].includes(parsed.type) && (profile.categoricalFields.length === 0 && profile.numericFields.length === 0)) {
|
|
4992
|
+
isCompatible = false;
|
|
4993
|
+
} else if (parsed.type === "scatter_plot" && profile.numericFields.length < 2) {
|
|
4994
|
+
isCompatible = false;
|
|
4995
|
+
} else if (parsed.type === "metric_card" && profile.numericFields.length === 0) {
|
|
4996
|
+
isCompatible = false;
|
|
4997
|
+
}
|
|
4998
|
+
if (!isCompatible) {
|
|
4999
|
+
console.warn(`[UITransformer] LLM chose incompatible visualization type "${parsed.type}" for retrieved schema. Falling back to heuristic.`);
|
|
5000
|
+
return this.transform(query, sources, void 0, void 0, intent);
|
|
5001
|
+
}
|
|
4791
5002
|
const intentAllowsTable = intent.wantsExplicitTable || ["tabular", "table", "geographic"].includes(intent.visualizationHint);
|
|
4792
5003
|
const intentWantsPieLikeChart = ["pie_chart", "donut_chart"].includes(intent.recommendedChart) || ["composition", "category_breakdown"].includes(intent.visualizationHint);
|
|
4793
5004
|
if (parsed.type === "table" && !intentAllowsTable) {
|
|
@@ -4817,7 +5028,26 @@ var UITransformer = class {
|
|
|
4817
5028
|
* - The intent object can be reused across both the heuristic and LLM paths.
|
|
4818
5029
|
* - It is easy to unit-test intent detection in isolation.
|
|
4819
5030
|
*/
|
|
4820
|
-
static async detectIntent(query, llm) {
|
|
5031
|
+
static async detectIntent(query, llm, sources) {
|
|
5032
|
+
let schemaProfileText = "";
|
|
5033
|
+
if (sources && sources.length > 0) {
|
|
5034
|
+
const profile = this.profileData(sources);
|
|
5035
|
+
const hasProducts = sources.some((item) => this.isProductData(item));
|
|
5036
|
+
schemaProfileText = `
|
|
5037
|
+
RETRIEVED DATA SCHEMA:
|
|
5038
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5039
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5040
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5041
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5042
|
+
- Text fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
5043
|
+
|
|
5044
|
+
Please choose visualizationHint and recommendedChart dynamically based on the available schema.
|
|
5045
|
+
- Do NOT recommend "trend" or "line_chart" if there are no Date fields.
|
|
5046
|
+
- Do NOT recommend numeric-dependent visualizations (like bar_chart, line_chart, pie_chart, scatter_plot, histogram, metric_card) if there are no Numeric fields. Fall back to tabular/table or text.
|
|
5047
|
+
- If there are multiple metadata fields but no clear categories or numbers, "tabular"/"table" is a highly accurate default.
|
|
5048
|
+
${hasProducts ? `- Note: The retrieved data matches product/catalog structure. If the user query is looking for, exploring, searching, browsing, or listing products/items, you MUST recommend visualizationHint: "product_browse" and recommendedChart: "text".` : ""}
|
|
5049
|
+
`;
|
|
5050
|
+
}
|
|
4821
5051
|
const systemPrompt = `You are an intent classifier for a product-search RAG system.
|
|
4822
5052
|
Given a user query, return ONLY a valid JSON object with this exact shape \u2014 no prose, no markdown:
|
|
4823
5053
|
|
|
@@ -4853,8 +5083,11 @@ RULES:
|
|
|
4853
5083
|
- isTemporal: true if time words appear (trend, historical, over time, last year, monthly, etc.)
|
|
4854
5084
|
- isComparison: true if user compares, ranks, or contrasts entities or categories.
|
|
4855
5085
|
- language: detect from the query text itself; default "en" if uncertain.`;
|
|
5086
|
+
const userPrompt = `QUERY: ${query}${schemaProfileText ? `
|
|
5087
|
+
|
|
5088
|
+
${schemaProfileText}` : ""}`;
|
|
4856
5089
|
const rawResponse = await llm.chat(
|
|
4857
|
-
[{ role: "user", content:
|
|
5090
|
+
[{ role: "user", content: userPrompt }],
|
|
4858
5091
|
"",
|
|
4859
5092
|
{ systemPrompt, temperature: 0 }
|
|
4860
5093
|
);
|
|
@@ -5297,10 +5530,21 @@ RULES:
|
|
|
5297
5530
|
const hasEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5298
5531
|
return asksForRecords && (hasNumericPredicate || hasEntityTerms);
|
|
5299
5532
|
}
|
|
5533
|
+
/** @internal kept private for transform() internal logic */
|
|
5300
5534
|
static isProductQuery(query) {
|
|
5535
|
+
return _UITransformer._productQueryTest(query);
|
|
5536
|
+
}
|
|
5537
|
+
/**
|
|
5538
|
+
* Public entry-point for external callers (e.g. Pipeline)
|
|
5539
|
+
* to check whether a query maps to product_browse without triggering an LLM call.
|
|
5540
|
+
*/
|
|
5541
|
+
static isProductQueryPublic(query) {
|
|
5542
|
+
return _UITransformer._productQueryTest(query);
|
|
5543
|
+
}
|
|
5544
|
+
static _productQueryTest(query) {
|
|
5301
5545
|
const q = query.toLowerCase();
|
|
5302
5546
|
const productTerms = /\b(product|products|item|items|sku|catalog|catalogue|price|prices|brand|model|stock|inventory|in stock|out of stock|buy|shop)\b/.test(q);
|
|
5303
|
-
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list)\b/.test(q);
|
|
5547
|
+
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list|explore|exploring)\b/.test(q);
|
|
5304
5548
|
const nonProductEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5305
5549
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
5306
5550
|
}
|
|
@@ -6073,23 +6317,19 @@ function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
|
6073
6317
|
async function scoreHallucination(llm, answer, context) {
|
|
6074
6318
|
const maxContextChars = 3e3;
|
|
6075
6319
|
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
CONTEXT:
|
|
6320
|
+
try {
|
|
6321
|
+
const raw = await llm.chat(
|
|
6322
|
+
[{ role: "user", content: `CONTEXT:
|
|
6079
6323
|
${truncatedContext}
|
|
6080
6324
|
|
|
6081
6325
|
ANSWER:
|
|
6082
|
-
${answer}
|
|
6083
|
-
|
|
6084
|
-
Return ONLY a valid JSON object with no markdown fences:
|
|
6085
|
-
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
6086
|
-
|
|
6087
|
-
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
6088
|
-
try {
|
|
6089
|
-
const raw = await llm.chat(
|
|
6090
|
-
[{ role: "user", content: prompt }],
|
|
6326
|
+
${answer}` }],
|
|
6091
6327
|
"",
|
|
6092
|
-
{
|
|
6328
|
+
{
|
|
6329
|
+
systemPrompt: '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. Return ONLY a valid JSON object with no markdown fences: {"score": <float 0-1>, "reason": "<one sentence>"} where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.',
|
|
6330
|
+
temperature: 0,
|
|
6331
|
+
maxTokens: 120
|
|
6332
|
+
}
|
|
6093
6333
|
);
|
|
6094
6334
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
6095
6335
|
if (!jsonMatch) return void 0;
|
|
@@ -6149,7 +6389,10 @@ var Pipeline = class {
|
|
|
6149
6389
|
- For product descriptions, summarize customer-facing details only. Do NOT list internal catalog fields such as Handle, Body (HTML), Vendor, Type, Tags, Published, Option, Variant, SKU, or raw metadata labels.
|
|
6150
6390
|
- Do NOT try to format product lists as tables.
|
|
6151
6391
|
`;
|
|
6152
|
-
this.config.llm.systemPrompt
|
|
6392
|
+
const userPromptPrefix = this.config.llm.systemPrompt ? `${this.config.llm.systemPrompt}
|
|
6393
|
+
|
|
6394
|
+
` : "";
|
|
6395
|
+
this.config.llm.systemPrompt = `${userPromptPrefix}${chartInstruction}`;
|
|
6153
6396
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
6154
6397
|
this.llmRouter = new LLMRouter(this.config);
|
|
6155
6398
|
const { llmProvider: resolvedLLM, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
@@ -6229,7 +6472,7 @@ var Pipeline = class {
|
|
|
6229
6472
|
embedBatchOptions
|
|
6230
6473
|
);
|
|
6231
6474
|
if (vectors.length !== chunks.length) {
|
|
6232
|
-
throw new
|
|
6475
|
+
throw new EmbeddingFailedException(
|
|
6233
6476
|
`[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
|
|
6234
6477
|
);
|
|
6235
6478
|
}
|
|
@@ -6327,11 +6570,11 @@ var Pipeline = class {
|
|
|
6327
6570
|
*/
|
|
6328
6571
|
askStream(_0) {
|
|
6329
6572
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
6330
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
6573
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
6331
6574
|
yield new __await(this.initialize());
|
|
6332
6575
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
6333
6576
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
6334
|
-
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
6577
|
+
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
6335
6578
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6336
6579
|
const requestStart = performance.now();
|
|
6337
6580
|
try {
|
|
@@ -6350,12 +6593,13 @@ var Pipeline = class {
|
|
|
6350
6593
|
const embedStart = performance.now();
|
|
6351
6594
|
const cacheKey = `${ns}::${searchQuery}`;
|
|
6352
6595
|
const cachedVector = this.embeddingCache.get(cacheKey);
|
|
6596
|
+
const useGraph = this.graphDB && ((_h = this.config.rag) == null ? void 0 : _h.useGraphRetrieval);
|
|
6353
6597
|
const [strategyResult, embeddedVector] = yield new __await(Promise.all([
|
|
6354
6598
|
QueryProcessor.determineRetrievalStrategy(
|
|
6355
6599
|
searchQuery,
|
|
6356
|
-
this.llmRouter.get("fast"),
|
|
6357
|
-
(
|
|
6358
|
-
(
|
|
6600
|
+
useGraph ? this.llmRouter.get("fast") : void 0,
|
|
6601
|
+
(_i = this.config.rag) == null ? void 0 : _i.graphKeywords,
|
|
6602
|
+
(_j = this.config.rag) == null ? void 0 : _j.vectorKeywords
|
|
6359
6603
|
),
|
|
6360
6604
|
// Embed immediately regardless of strategy — costs nothing if cached.
|
|
6361
6605
|
// If strategy turns out to be 'graph'-only we just won't use the vector.
|
|
@@ -6365,7 +6609,7 @@ var Pipeline = class {
|
|
|
6365
6609
|
if (!cachedVector && queryVector.length > 0) {
|
|
6366
6610
|
this.embeddingCache.set(cacheKey, queryVector);
|
|
6367
6611
|
}
|
|
6368
|
-
const graphData = (strategyResult === "graph" || strategyResult === "both") && this.graphDB && ((
|
|
6612
|
+
const graphData = (strategyResult === "graph" || strategyResult === "both") && this.graphDB && ((_k = this.config.rag) == null ? void 0 : _k.useGraphRetrieval) ? yield new __await(this.graphDB.query(searchQuery)) : void 0;
|
|
6369
6613
|
const hasMetadataFilter = filter.metadata && Object.keys(filter.metadata).length > 0;
|
|
6370
6614
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
6371
6615
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
@@ -6378,7 +6622,7 @@ var Pipeline = class {
|
|
|
6378
6622
|
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
6379
6623
|
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => m.score >= scoreThreshold);
|
|
6380
6624
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
6381
|
-
if (!hasNumericPredicates && ((
|
|
6625
|
+
if (!hasNumericPredicates && ((_l = this.config.rag) == null ? void 0 : _l.useReranking)) {
|
|
6382
6626
|
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
6383
6627
|
} else if (!wantsExhaustiveList) {
|
|
6384
6628
|
fullSources = fullSources.slice(0, topK);
|
|
@@ -6409,34 +6653,117 @@ VECTOR CONTEXT:
|
|
|
6409
6653
|
${context}`;
|
|
6410
6654
|
}
|
|
6411
6655
|
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
6412
|
-
const
|
|
6413
|
-
|
|
6414
|
-
(
|
|
6656
|
+
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
6657
|
+
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
6658
|
+
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
6659
|
+
});
|
|
6660
|
+
}
|
|
6661
|
+
const isProductQ = UITransformer.isProductQueryPublic(question);
|
|
6662
|
+
const uiTransformationPromise = isProductQ ? Promise.resolve(
|
|
6663
|
+
UITransformer.transform(
|
|
6415
6664
|
question,
|
|
6416
6665
|
sources,
|
|
6417
|
-
|
|
6418
|
-
|
|
6666
|
+
this.config,
|
|
6667
|
+
cachedSchema,
|
|
6668
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6419
6669
|
)
|
|
6670
|
+
) : this.generateUiTransformation(
|
|
6671
|
+
question,
|
|
6672
|
+
sources,
|
|
6673
|
+
cachedSchema,
|
|
6674
|
+
hasNumericPredicates
|
|
6420
6675
|
).catch((uiError) => {
|
|
6421
6676
|
console.warn("[Pipeline] UI transformation failed concurrently:", uiError);
|
|
6422
|
-
return UITransformer.transform(question, sources, this.config);
|
|
6677
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6423
6678
|
});
|
|
6679
|
+
let hallucinationScoringPromise = Promise.resolve(void 0);
|
|
6424
6680
|
const restrictionSuffix = "\n\n(IMPORTANT: Format your response beautifully using rich Markdown! Use bolding for emphasis, headings (##) for structure, bullet points for lists, and proper line breaks between paragraphs to make it highly readable. However, NEVER generate Markdown tables, HTML figures, or text charts (the UI renders requested charts separately, so NEVER say you cannot create, display, draw, render, or provide a chart/visualization). If listing products, use simple markdown bullet points or comma-separated names. NEVER use plus signs (+) to separate product names, category names, or list items. For product description/detail questions, provide customer-facing prose only and do NOT list internal catalog fields such as Handle, Body (HTML), Vendor, Type, Tags, Published, Option, Variant, SKU, or raw metadata labels. You CAN use numbers for years/counts like 2006 or 5800, but NEVER put a dollar sign ($) before them.)";
|
|
6681
|
+
const isClaude37 = this.config.llm.model.includes("claude-3-7-sonnet");
|
|
6682
|
+
const isNativeThinking = isClaude37 && (((_m = this.config.llm.options) == null ? void 0 : _m.thinking) === true || ((_n = this.config.llm.options) == null ? void 0 : _n.thinking) === "enabled" || ((_o = this.config.llm.options) == null ? void 0 : _o.thinking) !== false);
|
|
6683
|
+
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
6684
|
+
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
6685
|
+
const isSimulatedThinking = !isNativeThinking && (((_p = this.config.llm.options) == null ? void 0 : _p.thinking) === true || ((_q = this.config.llm.options) == null ? void 0 : _q.thinking) === "enabled" || isReasoningModel && ((_r = this.config.llm.options) == null ? void 0 : _r.thinking) !== false);
|
|
6686
|
+
let finalRestrictionSuffix = restrictionSuffix;
|
|
6687
|
+
if (isSimulatedThinking) {
|
|
6688
|
+
finalRestrictionSuffix += "\n\n(IMPORTANT: You must think step-by-step before answering. Write your thinking process inside a `<think>...</think>` block. Write the `<think>` block first, then follow it with your final response. Keep the thinking process thorough and detailed. Example: `<think>Thinking details here...</think>Final answer text here.`)";
|
|
6689
|
+
}
|
|
6425
6690
|
const hardenedHistory = [...history];
|
|
6426
|
-
const userQuestion = { role: "user", content: question +
|
|
6691
|
+
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
6427
6692
|
const messages = [...hardenedHistory, userQuestion];
|
|
6428
|
-
const systemPrompt = (
|
|
6693
|
+
const systemPrompt = (_s = this.config.llm.systemPrompt) != null ? _s : "";
|
|
6429
6694
|
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
6430
6695
|
let fullReply = "";
|
|
6696
|
+
let thinkingText = "";
|
|
6697
|
+
let thinkingStartMs = performance.now();
|
|
6698
|
+
let thinkingDurationMs = 0;
|
|
6431
6699
|
const generateStart = performance.now();
|
|
6432
6700
|
if (this.llmProvider.chatStream) {
|
|
6433
6701
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
6434
6702
|
if (!stream) throw new Error(`[Pipeline] ${this.config.llm.provider} chatStream returned undefined`);
|
|
6703
|
+
let inThinking = false;
|
|
6704
|
+
let textBuffer = "";
|
|
6435
6705
|
try {
|
|
6436
6706
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
6437
6707
|
const chunk = temp.value;
|
|
6438
|
-
|
|
6439
|
-
|
|
6708
|
+
if (chunk.startsWith("\0THINKING\0")) {
|
|
6709
|
+
const thinkingDelta = chunk.slice("\0THINKING\0".length);
|
|
6710
|
+
thinkingText += thinkingDelta;
|
|
6711
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6712
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6713
|
+
continue;
|
|
6714
|
+
}
|
|
6715
|
+
if (isSimulatedThinking) {
|
|
6716
|
+
textBuffer += chunk;
|
|
6717
|
+
while (textBuffer.length > 0) {
|
|
6718
|
+
if (!inThinking) {
|
|
6719
|
+
const thinkIndex = textBuffer.indexOf("<think>");
|
|
6720
|
+
if (thinkIndex !== -1) {
|
|
6721
|
+
const before = textBuffer.slice(0, thinkIndex);
|
|
6722
|
+
if (before) {
|
|
6723
|
+
fullReply += before;
|
|
6724
|
+
yield before;
|
|
6725
|
+
}
|
|
6726
|
+
inThinking = true;
|
|
6727
|
+
thinkingStartMs = performance.now();
|
|
6728
|
+
textBuffer = textBuffer.slice(thinkIndex + "<think>".length);
|
|
6729
|
+
} else {
|
|
6730
|
+
const maxSafeLength = textBuffer.length - "<think>".length;
|
|
6731
|
+
if (maxSafeLength > 0) {
|
|
6732
|
+
const safeText = textBuffer.slice(0, maxSafeLength);
|
|
6733
|
+
fullReply += safeText;
|
|
6734
|
+
yield safeText;
|
|
6735
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6736
|
+
}
|
|
6737
|
+
break;
|
|
6738
|
+
}
|
|
6739
|
+
} else {
|
|
6740
|
+
const endThinkIndex = textBuffer.indexOf("</think>");
|
|
6741
|
+
if (endThinkIndex !== -1) {
|
|
6742
|
+
const thinkingDelta = textBuffer.slice(0, endThinkIndex);
|
|
6743
|
+
if (thinkingDelta) {
|
|
6744
|
+
thinkingText += thinkingDelta;
|
|
6745
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6746
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6747
|
+
}
|
|
6748
|
+
inThinking = false;
|
|
6749
|
+
textBuffer = textBuffer.slice(endThinkIndex + "</think>".length);
|
|
6750
|
+
} else {
|
|
6751
|
+
const maxSafeLength = textBuffer.length - "</think>".length;
|
|
6752
|
+
if (maxSafeLength > 0) {
|
|
6753
|
+
const thinkingDelta = textBuffer.slice(0, maxSafeLength);
|
|
6754
|
+
thinkingText += thinkingDelta;
|
|
6755
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6756
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6757
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6758
|
+
}
|
|
6759
|
+
break;
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6762
|
+
}
|
|
6763
|
+
} else {
|
|
6764
|
+
fullReply += chunk;
|
|
6765
|
+
yield chunk;
|
|
6766
|
+
}
|
|
6440
6767
|
}
|
|
6441
6768
|
} catch (temp) {
|
|
6442
6769
|
error = [temp];
|
|
@@ -6448,17 +6775,42 @@ ${context}`;
|
|
|
6448
6775
|
throw error[0];
|
|
6449
6776
|
}
|
|
6450
6777
|
}
|
|
6778
|
+
if (isSimulatedThinking && textBuffer.length > 0) {
|
|
6779
|
+
if (inThinking) {
|
|
6780
|
+
thinkingText += textBuffer;
|
|
6781
|
+
yield { type: "thinking", text: textBuffer };
|
|
6782
|
+
} else {
|
|
6783
|
+
fullReply += textBuffer;
|
|
6784
|
+
yield textBuffer;
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6451
6787
|
} else {
|
|
6452
6788
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
6453
|
-
|
|
6454
|
-
|
|
6789
|
+
if (isSimulatedThinking) {
|
|
6790
|
+
const thinkStart = reply.indexOf("<think>");
|
|
6791
|
+
const thinkEnd = reply.indexOf("</think>");
|
|
6792
|
+
if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {
|
|
6793
|
+
thinkingText = reply.slice(thinkStart + "<think>".length, thinkEnd);
|
|
6794
|
+
fullReply = reply.slice(0, thinkStart) + reply.slice(thinkEnd + "</think>".length);
|
|
6795
|
+
thinkingDurationMs = 100;
|
|
6796
|
+
} else {
|
|
6797
|
+
fullReply = reply;
|
|
6798
|
+
}
|
|
6799
|
+
} else {
|
|
6800
|
+
fullReply = reply;
|
|
6801
|
+
}
|
|
6802
|
+
yield fullReply;
|
|
6803
|
+
}
|
|
6804
|
+
const runHallucination = ((_t = this.config.llm.options) == null ? void 0 : _t.hallucinationScoring) === true || this.config.llm.provider !== "ollama" && ((_u = this.config.llm.options) == null ? void 0 : _u.hallucinationScoring) !== false;
|
|
6805
|
+
if (runHallucination) {
|
|
6806
|
+
hallucinationScoringPromise = scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0);
|
|
6455
6807
|
}
|
|
6456
6808
|
const generateMs = performance.now() - generateStart;
|
|
6457
6809
|
const totalMs = performance.now() - requestStart;
|
|
6458
6810
|
const latency = {
|
|
6459
6811
|
embedMs: Math.round(embedMs),
|
|
6460
6812
|
retrieveMs: Math.round(retrieveMs),
|
|
6461
|
-
rerankMs: ((
|
|
6813
|
+
rerankMs: ((_v = this.config.rag) == null ? void 0 : _v.useReranking) ? Math.round(rerankMs) : void 0,
|
|
6462
6814
|
generateMs: Math.round(generateMs),
|
|
6463
6815
|
totalMs: Math.round(totalMs)
|
|
6464
6816
|
};
|
|
@@ -6471,12 +6823,16 @@ ${context}`;
|
|
|
6471
6823
|
totalTokens: promptTokens + completionTokens,
|
|
6472
6824
|
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
6473
6825
|
};
|
|
6474
|
-
const
|
|
6826
|
+
const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
|
|
6827
|
+
uiTransformationPromise,
|
|
6828
|
+
hallucinationScoringPromise
|
|
6829
|
+
]));
|
|
6830
|
+
const trace = __spreadValues({
|
|
6475
6831
|
requestId,
|
|
6476
6832
|
query: question,
|
|
6477
6833
|
rewrittenQuery,
|
|
6478
6834
|
systemPrompt,
|
|
6479
|
-
userPrompt: question +
|
|
6835
|
+
userPrompt: question + finalRestrictionSuffix,
|
|
6480
6836
|
chunks: sources.map((s) => {
|
|
6481
6837
|
var _a2;
|
|
6482
6838
|
return {
|
|
@@ -6490,22 +6846,19 @@ ${context}`;
|
|
|
6490
6846
|
latency,
|
|
6491
6847
|
tokens,
|
|
6492
6848
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6493
|
-
}
|
|
6494
|
-
|
|
6849
|
+
}, hallucinationResult ? {
|
|
6850
|
+
hallucinationScore: hallucinationResult.score,
|
|
6851
|
+
hallucinationReason: hallucinationResult.reason
|
|
6852
|
+
} : {});
|
|
6495
6853
|
yield {
|
|
6496
6854
|
reply: "",
|
|
6497
6855
|
sources,
|
|
6498
6856
|
graphData,
|
|
6499
6857
|
ui_transformation: uiTransformation,
|
|
6500
|
-
trace
|
|
6858
|
+
trace,
|
|
6859
|
+
thinking: thinkingText || void 0,
|
|
6860
|
+
thinkingMs: thinkingDurationMs > 0 ? Math.round(thinkingDurationMs) : void 0
|
|
6501
6861
|
};
|
|
6502
|
-
scoreHallucination(this.llmProvider, fullReply, context).then((hallucinationResult) => {
|
|
6503
|
-
if (hallucinationResult) {
|
|
6504
|
-
trace.hallucinationScore = hallucinationResult.score;
|
|
6505
|
-
trace.hallucinationReason = hallucinationResult.reason;
|
|
6506
|
-
}
|
|
6507
|
-
}).catch(() => {
|
|
6508
|
-
});
|
|
6509
6862
|
} catch (error2) {
|
|
6510
6863
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
6511
6864
|
}
|
|
@@ -6515,18 +6868,30 @@ ${context}`;
|
|
|
6515
6868
|
* Universal retrieval method combining all enabled providers.
|
|
6516
6869
|
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
6517
6870
|
*/
|
|
6518
|
-
async generateUiTransformation(question, sources,
|
|
6871
|
+
async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
|
|
6872
|
+
var _a;
|
|
6519
6873
|
if (!sources || sources.length === 0) {
|
|
6520
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6874
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6875
|
+
}
|
|
6876
|
+
if (UITransformer.isProductQueryPublic(question)) {
|
|
6877
|
+
return UITransformer.transform(
|
|
6878
|
+
question,
|
|
6879
|
+
sources,
|
|
6880
|
+
this.config,
|
|
6881
|
+
cachedSchema,
|
|
6882
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6883
|
+
);
|
|
6521
6884
|
}
|
|
6522
|
-
|
|
6523
|
-
|
|
6885
|
+
const isLocalProvider = this.config.llm.provider === "ollama";
|
|
6886
|
+
const disableLlmUiTransform = ((_a = this.config.llm.options) == null ? void 0 : _a.disableLlmUiTransform) === true;
|
|
6887
|
+
if (forceDeterministic || isLocalProvider || disableLlmUiTransform) {
|
|
6888
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6524
6889
|
}
|
|
6525
6890
|
try {
|
|
6526
6891
|
return await UITransformer.analyzeAndDecide(question, sources, this.llmRouter.get("fast"));
|
|
6527
6892
|
} catch (err) {
|
|
6528
6893
|
console.warn("[Pipeline] generateUiTransformation failed, using heuristic fallback:", err);
|
|
6529
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6894
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6530
6895
|
}
|
|
6531
6896
|
}
|
|
6532
6897
|
applyStructuredFilters(sources, filter) {
|
|
@@ -6610,25 +6975,29 @@ ${context}`;
|
|
|
6610
6975
|
return Number.isFinite(numeric) ? numeric : null;
|
|
6611
6976
|
}
|
|
6612
6977
|
async retrieve(query, options) {
|
|
6613
|
-
var _a, _b, _c, _d, _e, _f;
|
|
6978
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
6614
6979
|
const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
|
|
6615
6980
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
6616
6981
|
const cacheKey = `${ns}::${query}`;
|
|
6617
6982
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
6618
|
-
const
|
|
6983
|
+
const useGraph = this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval);
|
|
6984
|
+
const strategy = await QueryProcessor.determineRetrievalStrategy(
|
|
6985
|
+
query,
|
|
6986
|
+
useGraph ? this.llmRouter.get("fast") : void 0
|
|
6987
|
+
);
|
|
6619
6988
|
console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
|
|
6620
6989
|
const [retrievedVector, graphData] = await Promise.all([
|
|
6621
6990
|
// Only embed if we need vector search (strategy is 'vector' or 'both')
|
|
6622
6991
|
strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
|
|
6623
6992
|
// Only query graph if we need graph search (strategy is 'graph' or 'both')
|
|
6624
|
-
(strategy === "graph" || strategy === "both") && this.graphDB && ((
|
|
6993
|
+
(strategy === "graph" || strategy === "both") && this.graphDB && ((_d = this.config.rag) == null ? void 0 : _d.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
|
|
6625
6994
|
]);
|
|
6626
6995
|
if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
|
|
6627
6996
|
this.embeddingCache.set(cacheKey, retrievedVector);
|
|
6628
6997
|
queryVector = retrievedVector;
|
|
6629
6998
|
}
|
|
6630
|
-
const baseFilter = __spreadProps(__spreadValues({}, (
|
|
6631
|
-
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (
|
|
6999
|
+
const baseFilter = __spreadProps(__spreadValues({}, (_e = options.filter) != null ? _e : {}), { queryText: query });
|
|
7000
|
+
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
6632
7001
|
if (numericPredicates.length > 0) {
|
|
6633
7002
|
baseFilter.__numericPredicates = numericPredicates;
|
|
6634
7003
|
}
|
|
@@ -6639,7 +7008,7 @@ ${context}`;
|
|
|
6639
7008
|
) : [];
|
|
6640
7009
|
const resolvedSources = [];
|
|
6641
7010
|
for (const source of sources) {
|
|
6642
|
-
const parentId = (
|
|
7011
|
+
const parentId = (_g = source.metadata) == null ? void 0 : _g.parent_id;
|
|
6643
7012
|
if (parentId) {
|
|
6644
7013
|
console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
|
|
6645
7014
|
resolvedSources.push(source);
|
|
@@ -6662,10 +7031,13 @@ New Question: ${question}
|
|
|
6662
7031
|
Optimized Search Query:`;
|
|
6663
7032
|
const rewrite = await this.llmProvider.chat(
|
|
6664
7033
|
[
|
|
6665
|
-
{ role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
|
|
6666
7034
|
{ role: "user", content: prompt }
|
|
6667
7035
|
],
|
|
6668
|
-
""
|
|
7036
|
+
"",
|
|
7037
|
+
{
|
|
7038
|
+
systemPrompt: "You are an assistant that optimizes search queries for RAG systems. Focus on extracting the core intent and entities. Do not answer the question, just rewrite it.",
|
|
7039
|
+
temperature: 0
|
|
7040
|
+
}
|
|
6669
7041
|
);
|
|
6670
7042
|
return rewrite.trim() || question;
|
|
6671
7043
|
}
|
|
@@ -6689,10 +7061,13 @@ ${context}
|
|
|
6689
7061
|
Suggestions:`;
|
|
6690
7062
|
const response = await this.llmProvider.chat(
|
|
6691
7063
|
[
|
|
6692
|
-
{ role: "system", content: "You are a helpful assistant that generates search suggestions." },
|
|
6693
7064
|
{ role: "user", content: prompt }
|
|
6694
7065
|
],
|
|
6695
|
-
""
|
|
7066
|
+
"",
|
|
7067
|
+
{
|
|
7068
|
+
systemPrompt: "You are a helpful assistant that generates search suggestions based on the provided snippets. Focus on questions that can be answered by the context, keep each under 10 words, and return ONLY a JSON array of strings.",
|
|
7069
|
+
temperature: 0
|
|
7070
|
+
}
|
|
6696
7071
|
);
|
|
6697
7072
|
const match = response.match(/\[[\s\S]*\]/);
|
|
6698
7073
|
if (match) {
|
|
@@ -6816,6 +7191,8 @@ var VectorPlugin = class {
|
|
|
6816
7191
|
constructor(hostConfig) {
|
|
6817
7192
|
this.config = ConfigResolver.resolve(hostConfig);
|
|
6818
7193
|
this.validationPromise = ConfigValidator.validateAndThrow(this.config);
|
|
7194
|
+
this.validationPromise.catch(() => {
|
|
7195
|
+
});
|
|
6819
7196
|
this.pipeline = new Pipeline(this.config);
|
|
6820
7197
|
}
|
|
6821
7198
|
/**
|
|
@@ -6849,31 +7226,93 @@ var VectorPlugin = class {
|
|
|
6849
7226
|
* Run a chat query.
|
|
6850
7227
|
*/
|
|
6851
7228
|
async chat(message, history = [], namespace) {
|
|
6852
|
-
|
|
6853
|
-
|
|
7229
|
+
try {
|
|
7230
|
+
await this.validationPromise;
|
|
7231
|
+
} catch (err) {
|
|
7232
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7233
|
+
}
|
|
7234
|
+
try {
|
|
7235
|
+
return await this.pipeline.ask(message, history, namespace);
|
|
7236
|
+
} catch (err) {
|
|
7237
|
+
const msg = String(err);
|
|
7238
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7239
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7240
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7241
|
+
}
|
|
7242
|
+
throw wrapError(err, defaultCode);
|
|
7243
|
+
}
|
|
6854
7244
|
}
|
|
6855
7245
|
/**
|
|
6856
7246
|
* Run a streaming chat query.
|
|
6857
7247
|
*/
|
|
6858
7248
|
chatStream(_0) {
|
|
6859
7249
|
return __asyncGenerator(this, arguments, function* (message, history = [], namespace) {
|
|
6860
|
-
|
|
6861
|
-
|
|
7250
|
+
try {
|
|
7251
|
+
yield new __await(this.validationPromise);
|
|
7252
|
+
} catch (err) {
|
|
7253
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7254
|
+
}
|
|
7255
|
+
try {
|
|
7256
|
+
const stream = this.pipeline.askStream(message, history, namespace);
|
|
7257
|
+
try {
|
|
7258
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
7259
|
+
const chunk = temp.value;
|
|
7260
|
+
yield chunk;
|
|
7261
|
+
}
|
|
7262
|
+
} catch (temp) {
|
|
7263
|
+
error = [temp];
|
|
7264
|
+
} finally {
|
|
7265
|
+
try {
|
|
7266
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
7267
|
+
} finally {
|
|
7268
|
+
if (error)
|
|
7269
|
+
throw error[0];
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
} catch (err) {
|
|
7273
|
+
const msg = String(err);
|
|
7274
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7275
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7276
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7277
|
+
}
|
|
7278
|
+
throw wrapError(err, defaultCode);
|
|
7279
|
+
}
|
|
6862
7280
|
});
|
|
6863
7281
|
}
|
|
6864
7282
|
/**
|
|
6865
7283
|
* Ingest documents into the vector database.
|
|
6866
7284
|
*/
|
|
6867
7285
|
async ingest(documents, namespace) {
|
|
6868
|
-
|
|
6869
|
-
|
|
7286
|
+
try {
|
|
7287
|
+
await this.validationPromise;
|
|
7288
|
+
} catch (err) {
|
|
7289
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7290
|
+
}
|
|
7291
|
+
try {
|
|
7292
|
+
return await this.pipeline.ingest(documents, namespace);
|
|
7293
|
+
} catch (err) {
|
|
7294
|
+
const msg = String(err);
|
|
7295
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7296
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7297
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7298
|
+
}
|
|
7299
|
+
throw wrapError(err, defaultCode);
|
|
7300
|
+
}
|
|
6870
7301
|
}
|
|
6871
7302
|
/**
|
|
6872
7303
|
* Get auto-suggestions based on a query prefix.
|
|
6873
7304
|
*/
|
|
6874
7305
|
async getSuggestions(query, namespace) {
|
|
6875
|
-
|
|
6876
|
-
|
|
7306
|
+
try {
|
|
7307
|
+
await this.validationPromise;
|
|
7308
|
+
} catch (err) {
|
|
7309
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7310
|
+
}
|
|
7311
|
+
try {
|
|
7312
|
+
return await this.pipeline.getSuggestions(query, namespace);
|
|
7313
|
+
} catch (err) {
|
|
7314
|
+
throw wrapError(err, "RETRIEVAL_FAILED");
|
|
7315
|
+
}
|
|
6877
7316
|
}
|
|
6878
7317
|
};
|
|
6879
7318
|
|
|
@@ -7031,6 +7470,10 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7031
7470
|
if (!isActive) break;
|
|
7032
7471
|
if (typeof chunk === "string") {
|
|
7033
7472
|
enqueue(sseTextFrame(chunk));
|
|
7473
|
+
} else if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "thinking") {
|
|
7474
|
+
enqueue(`data: ${JSON.stringify(chunk)}
|
|
7475
|
+
|
|
7476
|
+
`);
|
|
7034
7477
|
} else {
|
|
7035
7478
|
enqueue(sseMetaFrame(chunk));
|
|
7036
7479
|
const responseChunk = chunk;
|
|
@@ -7219,11 +7662,68 @@ function createSuggestionsHandler(configOrPlugin) {
|
|
|
7219
7662
|
}
|
|
7220
7663
|
};
|
|
7221
7664
|
}
|
|
7665
|
+
function createRagHandler(configOrPlugin) {
|
|
7666
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
7667
|
+
const chatHandler = createChatHandler(plugin);
|
|
7668
|
+
const streamHandler = createStreamHandler(plugin);
|
|
7669
|
+
const uploadHandler = createUploadHandler(plugin);
|
|
7670
|
+
const healthHandler = createHealthHandler(plugin);
|
|
7671
|
+
const suggestionsHandler = createSuggestionsHandler(plugin);
|
|
7672
|
+
async function routePostRequest(req, segment) {
|
|
7673
|
+
switch (segment) {
|
|
7674
|
+
case "chat":
|
|
7675
|
+
return streamHandler(req);
|
|
7676
|
+
case "chat-sync":
|
|
7677
|
+
return chatHandler(req);
|
|
7678
|
+
case "upload":
|
|
7679
|
+
return uploadHandler(req);
|
|
7680
|
+
case "suggestions":
|
|
7681
|
+
return suggestionsHandler(req);
|
|
7682
|
+
case "health":
|
|
7683
|
+
return healthHandler();
|
|
7684
|
+
default:
|
|
7685
|
+
return import_server.NextResponse.json({ error: `Not Found: POST segment "${segment}" not supported.` }, { status: 404 });
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7688
|
+
async function routeGetRequest(req, segment) {
|
|
7689
|
+
if (segment === "health") {
|
|
7690
|
+
return healthHandler();
|
|
7691
|
+
}
|
|
7692
|
+
return import_server.NextResponse.json({ error: `Method Not Allowed: GET is only supported for "health" segment.` }, { status: 405 });
|
|
7693
|
+
}
|
|
7694
|
+
async function getSegment(context) {
|
|
7695
|
+
var _a;
|
|
7696
|
+
const resolvedParams = typeof ((_a = context == null ? void 0 : context.params) == null ? void 0 : _a.then) === "function" ? await context.params : context == null ? void 0 : context.params;
|
|
7697
|
+
const segments = (resolvedParams == null ? void 0 : resolvedParams.retrivora) || [];
|
|
7698
|
+
return segments[0] || "chat";
|
|
7699
|
+
}
|
|
7700
|
+
return {
|
|
7701
|
+
GET: async (req, context) => {
|
|
7702
|
+
try {
|
|
7703
|
+
const segment = await getSegment(context);
|
|
7704
|
+
return await routeGetRequest(req, segment);
|
|
7705
|
+
} catch (err) {
|
|
7706
|
+
const msg = err instanceof Error ? err.message : "GET Routing failed";
|
|
7707
|
+
return import_server.NextResponse.json({ error: msg }, { status: 500 });
|
|
7708
|
+
}
|
|
7709
|
+
},
|
|
7710
|
+
POST: async (req, context) => {
|
|
7711
|
+
try {
|
|
7712
|
+
const segment = await getSegment(context);
|
|
7713
|
+
return await routePostRequest(req, segment);
|
|
7714
|
+
} catch (err) {
|
|
7715
|
+
const msg = err instanceof Error ? err.message : "POST Routing failed";
|
|
7716
|
+
return import_server.NextResponse.json({ error: msg }, { status: 500 });
|
|
7717
|
+
}
|
|
7718
|
+
}
|
|
7719
|
+
};
|
|
7720
|
+
}
|
|
7222
7721
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7223
7722
|
0 && (module.exports = {
|
|
7224
7723
|
createChatHandler,
|
|
7225
7724
|
createHealthHandler,
|
|
7226
7725
|
createIngestHandler,
|
|
7726
|
+
createRagHandler,
|
|
7227
7727
|
createStreamHandler,
|
|
7228
7728
|
createSuggestionsHandler,
|
|
7229
7729
|
createUploadHandler,
|