@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/server.mjs
CHANGED
|
@@ -6,9 +6,6 @@ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
7
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
8
|
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
|
|
9
|
-
var __typeError = (msg) => {
|
|
10
|
-
throw TypeError(msg);
|
|
11
|
-
};
|
|
12
9
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
10
|
var __spreadValues = (a, b) => {
|
|
14
11
|
for (var prop in b || (b = {}))
|
|
@@ -56,34 +53,6 @@ var __asyncGenerator = (__this, __arguments, generator) => {
|
|
|
56
53
|
}, method = (k, call, wait, clear) => it[k] = (x) => (call = new Promise((yes, no, run) => (run = () => resume(k, x, yes, no), q ? q.then(run) : run())), clear = () => q === wait && (q = 0), q = wait = call.then(clear, clear), call), q, it = {};
|
|
57
54
|
return generator = generator.apply(__this, __arguments), it[__knownSymbol("asyncIterator")] = () => it, method("next"), method("throw"), method("return"), it;
|
|
58
55
|
};
|
|
59
|
-
var __yieldStar = (value) => {
|
|
60
|
-
var obj = value[__knownSymbol("asyncIterator")], isAwait = false, method, it = {};
|
|
61
|
-
if (obj == null) {
|
|
62
|
-
obj = value[__knownSymbol("iterator")]();
|
|
63
|
-
method = (k) => it[k] = (x) => obj[k](x);
|
|
64
|
-
} else {
|
|
65
|
-
obj = obj.call(value);
|
|
66
|
-
method = (k) => it[k] = (v) => {
|
|
67
|
-
if (isAwait) {
|
|
68
|
-
isAwait = false;
|
|
69
|
-
if (k === "throw") throw v;
|
|
70
|
-
return v;
|
|
71
|
-
}
|
|
72
|
-
isAwait = true;
|
|
73
|
-
return {
|
|
74
|
-
done: false,
|
|
75
|
-
value: new __await(new Promise((resolve) => {
|
|
76
|
-
var x = obj[k](v);
|
|
77
|
-
if (!(x instanceof Object)) __typeError("Object expected");
|
|
78
|
-
resolve(x);
|
|
79
|
-
}), 1)
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
return it[__knownSymbol("iterator")] = () => it, method("next"), "throw" in obj ? method("throw") : it.throw = (x) => {
|
|
84
|
-
throw x;
|
|
85
|
-
}, "return" in obj && method("return"), it;
|
|
86
|
-
};
|
|
87
56
|
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
|
|
88
57
|
|
|
89
58
|
// src/utils/templateUtils.ts
|
|
@@ -640,6 +609,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
640
609
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
641
610
|
ON ${this.uploadTable}
|
|
642
611
|
USING hnsw (embedding vector_cosine_ops)
|
|
612
|
+
`);
|
|
613
|
+
await client.query(`
|
|
614
|
+
CREATE INDEX IF NOT EXISTS "${this.uploadTable}_fts_idx"
|
|
615
|
+
ON "${this.uploadTable}"
|
|
616
|
+
USING gin (to_tsvector('english', content))
|
|
643
617
|
`);
|
|
644
618
|
const res = await client.query(`
|
|
645
619
|
SELECT table_name
|
|
@@ -710,6 +684,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
710
684
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
711
685
|
ON "${tableName}"
|
|
712
686
|
USING hnsw (embedding vector_cosine_ops)
|
|
687
|
+
`);
|
|
688
|
+
await client.query(`
|
|
689
|
+
CREATE INDEX IF NOT EXISTS "${tableName}_fts_idx"
|
|
690
|
+
ON "${tableName}"
|
|
691
|
+
USING gin (to_tsvector('english', content))
|
|
713
692
|
`);
|
|
714
693
|
if (!this.tables.includes(tableName)) {
|
|
715
694
|
this.tables.push(tableName);
|
|
@@ -801,8 +780,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
801
780
|
const paramIdx = baseOffset + filterParams.length;
|
|
802
781
|
const synonyms = (_a3 = FIELD_SYNONYMS[key]) != null ? _a3 : [];
|
|
803
782
|
const keysToCheck = [key, ...synonyms];
|
|
804
|
-
const coalesceExprs = keysToCheck.
|
|
805
|
-
|
|
783
|
+
const coalesceExprs = keysToCheck.flatMap((k) => [
|
|
784
|
+
`metadata->>'${k}'`,
|
|
785
|
+
`to_jsonb(t)->>'${k}'`
|
|
786
|
+
]);
|
|
787
|
+
return `COALESCE(${coalesceExprs.map((expr) => `LOWER(${expr})`).join(", ")}) = LOWER($${paramIdx})`;
|
|
806
788
|
});
|
|
807
789
|
whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
808
790
|
}
|
|
@@ -813,16 +795,35 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
813
795
|
CASE WHEN LOWER(val) IN (${entityHints.map((h) => `'${h.replace(/'/g, "''")}'`).join(", ")})
|
|
814
796
|
THEN 1.0 ELSE 0.0 END
|
|
815
797
|
), 0)
|
|
816
|
-
FROM jsonb_each_text(
|
|
798
|
+
FROM jsonb_each_text(COALESCE(v.metadata, k.metadata)) AS kv(key, val)
|
|
817
799
|
WHERE key IN (${this.searchFields.map((f) => `'${f}'`).join(", ")})
|
|
818
800
|
) * 3.0` : "";
|
|
819
801
|
sqlQuery = `
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
802
|
+
WITH vector_search AS (
|
|
803
|
+
SELECT *, (1 - (embedding <=> $1::vector)) AS vector_score
|
|
804
|
+
FROM "${table}" t
|
|
805
|
+
${whereClause}
|
|
806
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
807
|
+
LIMIT ${tableLimit}
|
|
808
|
+
),
|
|
809
|
+
keyword_search AS (
|
|
810
|
+
SELECT *, COALESCE(ts_rank(to_tsvector('english', content), to_tsquery('english', $2)), 0) AS keyword_score
|
|
811
|
+
FROM "${table}" t
|
|
812
|
+
WHERE ${whereClause ? whereClause.replace("WHERE", "") : "TRUE"}
|
|
813
|
+
AND to_tsvector('english', content) @@ to_tsquery('english', $2)
|
|
814
|
+
ORDER BY keyword_score DESC
|
|
815
|
+
LIMIT ${tableLimit}
|
|
816
|
+
)
|
|
817
|
+
SELECT
|
|
818
|
+
COALESCE(v.id, k.id) AS id,
|
|
819
|
+
COALESCE(v.namespace, k.namespace) AS namespace,
|
|
820
|
+
COALESCE(v.content, k.content) AS content,
|
|
821
|
+
COALESCE(v.metadata, k.metadata) AS metadata,
|
|
822
|
+
COALESCE(v.vector_score, 0) AS vector_score,
|
|
823
|
+
COALESCE(k.keyword_score, 0) AS keyword_score,
|
|
824
|
+
(COALESCE(v.vector_score, 0) + COALESCE(k.keyword_score, 0) * 2.0 ${exactNameScoreExpr}) AS hybrid_score
|
|
825
|
+
FROM vector_search v
|
|
826
|
+
FULL OUTER JOIN keyword_search k ON v.id = k.id
|
|
826
827
|
ORDER BY hybrid_score DESC
|
|
827
828
|
LIMIT ${tableLimit}
|
|
828
829
|
`;
|
|
@@ -833,7 +834,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
833
834
|
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
834
835
|
FROM "${table}" t
|
|
835
836
|
${whereClause}
|
|
836
|
-
ORDER BY
|
|
837
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
837
838
|
LIMIT ${tableLimit}
|
|
838
839
|
`;
|
|
839
840
|
params = [vectorLiteral, ...filterParams];
|
|
@@ -1039,7 +1040,11 @@ var init_MongoDBProvider = __esm({
|
|
|
1039
1040
|
if (key === "namespace") {
|
|
1040
1041
|
vectorSearchFilter.namespace = value;
|
|
1041
1042
|
} else {
|
|
1042
|
-
|
|
1043
|
+
if (typeof value === "string") {
|
|
1044
|
+
matchFilter[key] = { $regex: new RegExp(`^${value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i") };
|
|
1045
|
+
} else {
|
|
1046
|
+
matchFilter[key] = value;
|
|
1047
|
+
}
|
|
1043
1048
|
}
|
|
1044
1049
|
}
|
|
1045
1050
|
const pipeline = [
|
|
@@ -2112,7 +2117,7 @@ function getRagConfig(baseConfig, env = process.env) {
|
|
|
2112
2117
|
return getEnvConfig(env, baseConfig);
|
|
2113
2118
|
}
|
|
2114
2119
|
function getEnvConfig(env = process.env, base) {
|
|
2115
|
-
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;
|
|
2120
|
+
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;
|
|
2116
2121
|
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__";
|
|
2117
2122
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
2118
2123
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
@@ -2182,7 +2187,7 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2182
2187
|
rest: "default",
|
|
2183
2188
|
custom: "default"
|
|
2184
2189
|
};
|
|
2185
|
-
return {
|
|
2190
|
+
return __spreadValues({
|
|
2186
2191
|
projectId,
|
|
2187
2192
|
vectorDb: {
|
|
2188
2193
|
provider: vectorProvider,
|
|
@@ -2198,7 +2203,9 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2198
2203
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
|
|
2199
2204
|
temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
|
|
2200
2205
|
options: {
|
|
2201
|
-
profile: readString(env, "LLM_UNIVERSAL_PROFILE")
|
|
2206
|
+
profile: readString(env, "LLM_UNIVERSAL_PROFILE"),
|
|
2207
|
+
thinking: readString(env, "LLM_THINKING") === "true" || readString(env, "LLM_THINKING") === "enabled" ? true : readString(env, "LLM_THINKING") === "false" ? false : void 0,
|
|
2208
|
+
thinkingBudget: env.LLM_THINKING_BUDGET ? parseInt(env.LLM_THINKING_BUDGET, 10) : void 0
|
|
2202
2209
|
}
|
|
2203
2210
|
},
|
|
2204
2211
|
embedding: {
|
|
@@ -2230,9 +2237,34 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2230
2237
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
2231
2238
|
scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
|
|
2232
2239
|
chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
|
|
2233
|
-
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
|
|
2240
|
+
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
|
|
2241
|
+
filterableFields: (_Ha = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Ha.split(",").map((f) => f.trim()),
|
|
2242
|
+
// Query pipeline toggles — read from .env.local
|
|
2243
|
+
useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
|
|
2244
|
+
useReranking: readString(env, "RAG_USE_RERANKING") === "true",
|
|
2245
|
+
useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
|
|
2246
|
+
architecture: (_Ia = readString(env, "RAG_ARCHITECTURE")) != null ? _Ia : "simple",
|
|
2247
|
+
chunkingStrategy: (_Ja = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _Ja : "recursive",
|
|
2248
|
+
uiMapping: (() => {
|
|
2249
|
+
const raw = readString(env, "RAG_UI_MAPPING");
|
|
2250
|
+
if (!raw) return void 0;
|
|
2251
|
+
try {
|
|
2252
|
+
return JSON.parse(raw);
|
|
2253
|
+
} catch (e) {
|
|
2254
|
+
return void 0;
|
|
2255
|
+
}
|
|
2256
|
+
})()
|
|
2234
2257
|
}
|
|
2235
|
-
}
|
|
2258
|
+
}, readString(env, "GRAPH_DB_PROVIDER") ? {
|
|
2259
|
+
graphDb: {
|
|
2260
|
+
provider: readString(env, "GRAPH_DB_PROVIDER"),
|
|
2261
|
+
options: {
|
|
2262
|
+
uri: readString(env, "GRAPH_DB_URI"),
|
|
2263
|
+
username: readString(env, "GRAPH_DB_USERNAME"),
|
|
2264
|
+
password: readString(env, "GRAPH_DB_PASSWORD")
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
} : {});
|
|
2236
2268
|
}
|
|
2237
2269
|
|
|
2238
2270
|
// src/core/ConfigResolver.ts
|
|
@@ -2261,6 +2293,20 @@ var ConfigResolver = class {
|
|
|
2261
2293
|
rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
|
|
2262
2294
|
});
|
|
2263
2295
|
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Resolves the public SDK config shape used by `new Retrivora({...})`.
|
|
2298
|
+
* Supports aliases from the product prompt while preserving existing env
|
|
2299
|
+
* fallback behavior.
|
|
2300
|
+
*/
|
|
2301
|
+
static resolveUniversal(hostConfig, env = process.env) {
|
|
2302
|
+
var _a;
|
|
2303
|
+
if (!hostConfig) return this.resolve(void 0, env);
|
|
2304
|
+
const normalized = __spreadProps(__spreadValues({}, hostConfig), {
|
|
2305
|
+
vectorDb: (_a = hostConfig.vectorDb) != null ? _a : hostConfig.vectorDatabase,
|
|
2306
|
+
rag: this.mergeRetrievalWorkflow(hostConfig.rag, hostConfig.retrieval, hostConfig.workflow)
|
|
2307
|
+
});
|
|
2308
|
+
return this.resolve(normalized, env);
|
|
2309
|
+
}
|
|
2264
2310
|
/**
|
|
2265
2311
|
* Validates the configuration for required fields.
|
|
2266
2312
|
*/
|
|
@@ -2275,10 +2321,53 @@ var ConfigResolver = class {
|
|
|
2275
2321
|
throw new Error("[ConfigResolver] llm.provider is required");
|
|
2276
2322
|
}
|
|
2277
2323
|
}
|
|
2324
|
+
static mergeRetrievalWorkflow(rag, retrieval, workflow) {
|
|
2325
|
+
var _a, _b, _c, _d, _e;
|
|
2326
|
+
if (!rag && !retrieval && !workflow) return void 0;
|
|
2327
|
+
const normalized = __spreadValues({}, rag != null ? rag : {});
|
|
2328
|
+
if (retrieval) {
|
|
2329
|
+
normalized.topK = (_a = retrieval.topK) != null ? _a : normalized.topK;
|
|
2330
|
+
normalized.scoreThreshold = (_b = retrieval.scoreThreshold) != null ? _b : normalized.scoreThreshold;
|
|
2331
|
+
normalized.useReranking = (_c = retrieval.useReranking) != null ? _c : normalized.useReranking;
|
|
2332
|
+
if (retrieval.strategy === "hybrid") normalized.architecture = (_d = normalized.architecture) != null ? _d : "hybrid";
|
|
2333
|
+
if (retrieval.strategy === "agentic") normalized.architecture = "agentic";
|
|
2334
|
+
if (retrieval.strategy === "contextual-compression") normalized.useReranking = true;
|
|
2335
|
+
}
|
|
2336
|
+
if (workflow == null ? void 0 : workflow.type) {
|
|
2337
|
+
const type = workflow.type;
|
|
2338
|
+
if (type === "agentic" || type === "agentic-rag") normalized.architecture = "agentic";
|
|
2339
|
+
else if (type === "hybrid-rag") normalized.architecture = "hybrid";
|
|
2340
|
+
else if (type === "graph-rag") {
|
|
2341
|
+
normalized.architecture = "graph";
|
|
2342
|
+
normalized.useGraphRetrieval = true;
|
|
2343
|
+
} else if (type === "simple-rag" || type === "rag") {
|
|
2344
|
+
normalized.architecture = (_e = normalized.architecture) != null ? _e : "simple";
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
return normalized;
|
|
2348
|
+
}
|
|
2278
2349
|
};
|
|
2279
2350
|
|
|
2280
2351
|
// src/llm/providers/OpenAIProvider.ts
|
|
2281
2352
|
import OpenAI from "openai";
|
|
2353
|
+
|
|
2354
|
+
// src/llm/utils.ts
|
|
2355
|
+
function buildSystemContent(systemPrompt, context) {
|
|
2356
|
+
const noContext = !context || context.trim() === "" || context.trim() === "No relevant context found.";
|
|
2357
|
+
if (systemPrompt.includes("{{context}}")) {
|
|
2358
|
+
return systemPrompt.replace("{{context}}", noContext ? "" : context);
|
|
2359
|
+
}
|
|
2360
|
+
if (noContext) {
|
|
2361
|
+
return systemPrompt;
|
|
2362
|
+
}
|
|
2363
|
+
return `${systemPrompt}
|
|
2364
|
+
|
|
2365
|
+
---
|
|
2366
|
+
Context:
|
|
2367
|
+
${context}`;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
// src/llm/providers/OpenAIProvider.ts
|
|
2282
2371
|
var OpenAIProvider = class {
|
|
2283
2372
|
constructor(llmConfig, embeddingConfig) {
|
|
2284
2373
|
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
@@ -2342,16 +2431,10 @@ var OpenAIProvider = class {
|
|
|
2342
2431
|
}
|
|
2343
2432
|
async chat(messages, context, options) {
|
|
2344
2433
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
2345
|
-
const
|
|
2346
|
-
|
|
2347
|
-
Context:
|
|
2348
|
-
${context}`;
|
|
2434
|
+
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.";
|
|
2349
2435
|
const systemMessage = {
|
|
2350
2436
|
role: "system",
|
|
2351
|
-
content:
|
|
2352
|
-
|
|
2353
|
-
Context:
|
|
2354
|
-
${context}`
|
|
2437
|
+
content: buildSystemContent(basePrompt, context)
|
|
2355
2438
|
};
|
|
2356
2439
|
const formattedMessages = [
|
|
2357
2440
|
systemMessage,
|
|
@@ -2372,16 +2455,10 @@ ${context}`
|
|
|
2372
2455
|
chatStream(messages, context, options) {
|
|
2373
2456
|
return __asyncGenerator(this, null, function* () {
|
|
2374
2457
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2375
|
-
const
|
|
2376
|
-
|
|
2377
|
-
Context:
|
|
2378
|
-
${context}`;
|
|
2458
|
+
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.";
|
|
2379
2459
|
const systemMessage = {
|
|
2380
2460
|
role: "system",
|
|
2381
|
-
content:
|
|
2382
|
-
|
|
2383
|
-
Context:
|
|
2384
|
-
${context}`
|
|
2461
|
+
content: buildSystemContent(basePrompt, context)
|
|
2385
2462
|
};
|
|
2386
2463
|
const formattedMessages = [
|
|
2387
2464
|
systemMessage,
|
|
@@ -2502,58 +2579,87 @@ var AnthropicProvider = class {
|
|
|
2502
2579
|
};
|
|
2503
2580
|
}
|
|
2504
2581
|
async chat(messages, context, options) {
|
|
2505
|
-
var _a, _b, _c, _d;
|
|
2506
|
-
const
|
|
2507
|
-
|
|
2508
|
-
Context:
|
|
2509
|
-
${context}`;
|
|
2510
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2511
|
-
|
|
2512
|
-
Context:
|
|
2513
|
-
${context}`;
|
|
2582
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2583
|
+
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.";
|
|
2584
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2514
2585
|
const anthropicMessages = messages.map((m) => ({
|
|
2515
2586
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2516
2587
|
content: m.content
|
|
2517
2588
|
}));
|
|
2518
|
-
const
|
|
2589
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2590
|
+
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;
|
|
2591
|
+
const extraParams = {};
|
|
2592
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2593
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2594
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2595
|
+
const budget = Math.min(
|
|
2596
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2597
|
+
maxTokens - 1
|
|
2598
|
+
);
|
|
2599
|
+
extraParams.thinking = {
|
|
2600
|
+
type: "enabled",
|
|
2601
|
+
budget_tokens: budget
|
|
2602
|
+
};
|
|
2603
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2604
|
+
}
|
|
2605
|
+
const response = await this.client.messages.create(__spreadValues({
|
|
2519
2606
|
model: this.llmConfig.model,
|
|
2520
|
-
max_tokens: (
|
|
2607
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2521
2608
|
system,
|
|
2522
2609
|
messages: anthropicMessages
|
|
2523
|
-
});
|
|
2524
|
-
|
|
2525
|
-
|
|
2610
|
+
}, extraParams));
|
|
2611
|
+
let reply = "";
|
|
2612
|
+
for (const block of response.content) {
|
|
2613
|
+
if (block.type === "text") {
|
|
2614
|
+
reply += block.text;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
return reply;
|
|
2526
2618
|
}
|
|
2527
2619
|
chatStream(messages, context, options) {
|
|
2528
2620
|
return __asyncGenerator(this, null, function* () {
|
|
2529
|
-
var _a, _b, _c, _d;
|
|
2530
|
-
const
|
|
2531
|
-
|
|
2532
|
-
Context:
|
|
2533
|
-
${context}`;
|
|
2534
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2535
|
-
|
|
2536
|
-
Context:
|
|
2537
|
-
${context}`;
|
|
2621
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2622
|
+
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.";
|
|
2623
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2538
2624
|
const anthropicMessages = messages.map((m) => ({
|
|
2539
2625
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2540
2626
|
content: m.content
|
|
2541
2627
|
}));
|
|
2542
|
-
const
|
|
2628
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2629
|
+
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;
|
|
2630
|
+
const extraParams = {};
|
|
2631
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2632
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2633
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2634
|
+
const budget = Math.min(
|
|
2635
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2636
|
+
maxTokens - 1
|
|
2637
|
+
);
|
|
2638
|
+
extraParams.thinking = {
|
|
2639
|
+
type: "enabled",
|
|
2640
|
+
budget_tokens: budget
|
|
2641
|
+
};
|
|
2642
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2643
|
+
}
|
|
2644
|
+
const stream = yield new __await(this.client.messages.create(__spreadValues({
|
|
2543
2645
|
model: this.llmConfig.model,
|
|
2544
|
-
max_tokens: (
|
|
2646
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2545
2647
|
system,
|
|
2546
2648
|
messages: anthropicMessages,
|
|
2547
2649
|
stream: true
|
|
2548
|
-
}));
|
|
2650
|
+
}, extraParams)));
|
|
2549
2651
|
if (!stream) {
|
|
2550
2652
|
throw new Error("[AnthropicProvider] messages.create stream is undefined");
|
|
2551
2653
|
}
|
|
2552
2654
|
try {
|
|
2553
2655
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
2554
2656
|
const chunk = temp.value;
|
|
2555
|
-
if (chunk.type === "content_block_delta"
|
|
2556
|
-
|
|
2657
|
+
if (chunk.type === "content_block_delta") {
|
|
2658
|
+
if (chunk.delta.type === "text_delta") {
|
|
2659
|
+
yield chunk.delta.text;
|
|
2660
|
+
} else if (chunk.delta.type === "thinking_delta") {
|
|
2661
|
+
yield `\0THINKING\0${chunk.delta.thinking}`;
|
|
2662
|
+
}
|
|
2557
2663
|
}
|
|
2558
2664
|
}
|
|
2559
2665
|
} catch (temp) {
|
|
@@ -2597,9 +2703,10 @@ ${context}`;
|
|
|
2597
2703
|
import axios from "axios";
|
|
2598
2704
|
var OllamaProvider = class {
|
|
2599
2705
|
constructor(llmConfig, embeddingConfig) {
|
|
2600
|
-
var _a;
|
|
2706
|
+
var _a, _b;
|
|
2601
2707
|
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
2602
|
-
|
|
2708
|
+
const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
|
|
2709
|
+
this.http = axios.create({ baseURL, timeout });
|
|
2603
2710
|
this.llmConfig = llmConfig;
|
|
2604
2711
|
this.embeddingConfig = embeddingConfig;
|
|
2605
2712
|
}
|
|
@@ -2655,14 +2762,15 @@ var OllamaProvider = class {
|
|
|
2655
2762
|
};
|
|
2656
2763
|
}
|
|
2657
2764
|
async chat(messages, context, options) {
|
|
2658
|
-
var _a, _b, _c, _d;
|
|
2659
|
-
const
|
|
2765
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2766
|
+
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.";
|
|
2767
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2660
2768
|
const { data } = await this.http.post("/api/chat", {
|
|
2661
2769
|
model: this.llmConfig.model,
|
|
2662
2770
|
stream: false,
|
|
2663
2771
|
options: {
|
|
2664
|
-
temperature: (
|
|
2665
|
-
num_predict: (
|
|
2772
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2773
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2666
2774
|
},
|
|
2667
2775
|
messages: [
|
|
2668
2776
|
{ role: "system", content: system },
|
|
@@ -2673,14 +2781,15 @@ var OllamaProvider = class {
|
|
|
2673
2781
|
}
|
|
2674
2782
|
chatStream(messages, context, options) {
|
|
2675
2783
|
return __asyncGenerator(this, null, function* () {
|
|
2676
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2677
|
-
const
|
|
2784
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2785
|
+
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.";
|
|
2786
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2678
2787
|
const response = yield new __await(this.http.post("/api/chat", {
|
|
2679
2788
|
model: this.llmConfig.model,
|
|
2680
2789
|
stream: true,
|
|
2681
2790
|
options: {
|
|
2682
|
-
temperature: (
|
|
2683
|
-
num_predict: (
|
|
2791
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2792
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2684
2793
|
},
|
|
2685
2794
|
messages: [
|
|
2686
2795
|
{ role: "system", content: system },
|
|
@@ -2702,7 +2811,7 @@ var OllamaProvider = class {
|
|
|
2702
2811
|
if (!line.trim()) continue;
|
|
2703
2812
|
try {
|
|
2704
2813
|
const json = JSON.parse(line);
|
|
2705
|
-
if ((
|
|
2814
|
+
if ((_g = json.message) == null ? void 0 : _g.content) {
|
|
2706
2815
|
yield json.message.content;
|
|
2707
2816
|
}
|
|
2708
2817
|
if (json.done) return;
|
|
@@ -2724,26 +2833,12 @@ var OllamaProvider = class {
|
|
|
2724
2833
|
if (lineBuffer.trim()) {
|
|
2725
2834
|
try {
|
|
2726
2835
|
const json = JSON.parse(lineBuffer);
|
|
2727
|
-
if ((
|
|
2836
|
+
if ((_h = json.message) == null ? void 0 : _h.content) yield json.message.content;
|
|
2728
2837
|
} catch (e) {
|
|
2729
2838
|
}
|
|
2730
2839
|
}
|
|
2731
2840
|
});
|
|
2732
2841
|
}
|
|
2733
|
-
buildSystemPrompt(context, overridePrompt) {
|
|
2734
|
-
var _a;
|
|
2735
|
-
if (overridePrompt) {
|
|
2736
|
-
return overridePrompt;
|
|
2737
|
-
}
|
|
2738
|
-
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
2739
|
-
|
|
2740
|
-
Context:
|
|
2741
|
-
${context}`;
|
|
2742
|
-
return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
2743
|
-
|
|
2744
|
-
Context:
|
|
2745
|
-
${context}`;
|
|
2746
|
-
}
|
|
2747
2842
|
async embed(text, options) {
|
|
2748
2843
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2749
2844
|
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";
|
|
@@ -2890,21 +2985,6 @@ var GeminiProvider = class {
|
|
|
2890
2985
|
var _a, _b;
|
|
2891
2986
|
return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
|
|
2892
2987
|
}
|
|
2893
|
-
/**
|
|
2894
|
-
* Build the system instruction string, inserting the RAG context either via
|
|
2895
|
-
* the `{{context}}` placeholder or by appending it.
|
|
2896
|
-
*/
|
|
2897
|
-
buildSystemInstruction(context) {
|
|
2898
|
-
var _a;
|
|
2899
|
-
const base = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
2900
|
-
|
|
2901
|
-
Context:
|
|
2902
|
-
${context}`;
|
|
2903
|
-
return base.includes("{{context}}") ? base.replace("{{context}}", context) : `${base}
|
|
2904
|
-
|
|
2905
|
-
Context:
|
|
2906
|
-
${context}`;
|
|
2907
|
-
}
|
|
2908
2988
|
/**
|
|
2909
2989
|
* Convert ChatMessage[] to the Gemini contents format.
|
|
2910
2990
|
*
|
|
@@ -2923,16 +3003,17 @@ ${context}`;
|
|
|
2923
3003
|
// ILLMProvider — chat
|
|
2924
3004
|
// -------------------------------------------------------------------------
|
|
2925
3005
|
async chat(messages, context, options) {
|
|
2926
|
-
var _a, _b, _c, _d;
|
|
3006
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3007
|
+
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.";
|
|
2927
3008
|
const model = this.client.getGenerativeModel({
|
|
2928
3009
|
model: this.llmConfig.model,
|
|
2929
|
-
systemInstruction:
|
|
3010
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2930
3011
|
});
|
|
2931
3012
|
const result = await model.generateContent({
|
|
2932
3013
|
contents: this.buildGeminiContents(messages),
|
|
2933
3014
|
generationConfig: {
|
|
2934
|
-
temperature: (
|
|
2935
|
-
maxOutputTokens: (
|
|
3015
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3016
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2936
3017
|
stopSequences: options == null ? void 0 : options.stop
|
|
2937
3018
|
}
|
|
2938
3019
|
});
|
|
@@ -2940,16 +3021,17 @@ ${context}`;
|
|
|
2940
3021
|
}
|
|
2941
3022
|
chatStream(messages, context, options) {
|
|
2942
3023
|
return __asyncGenerator(this, null, function* () {
|
|
2943
|
-
var _a, _b, _c, _d;
|
|
3024
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3025
|
+
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.";
|
|
2944
3026
|
const model = this.client.getGenerativeModel({
|
|
2945
3027
|
model: this.llmConfig.model,
|
|
2946
|
-
systemInstruction:
|
|
3028
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2947
3029
|
});
|
|
2948
3030
|
const result = yield new __await(model.generateContentStream({
|
|
2949
3031
|
contents: this.buildGeminiContents(messages),
|
|
2950
3032
|
generationConfig: {
|
|
2951
|
-
temperature: (
|
|
2952
|
-
maxOutputTokens: (
|
|
3033
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3034
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2953
3035
|
stopSequences: options == null ? void 0 : options.stop
|
|
2954
3036
|
}
|
|
2955
3037
|
}));
|
|
@@ -3316,6 +3398,78 @@ ${context != null ? context : "None"}` },
|
|
|
3316
3398
|
}
|
|
3317
3399
|
};
|
|
3318
3400
|
|
|
3401
|
+
// src/exceptions/index.ts
|
|
3402
|
+
var RetrivoraError = class extends Error {
|
|
3403
|
+
constructor(message, code, details) {
|
|
3404
|
+
super(message);
|
|
3405
|
+
this.name = new.target.name;
|
|
3406
|
+
this.code = code;
|
|
3407
|
+
this.details = details;
|
|
3408
|
+
}
|
|
3409
|
+
};
|
|
3410
|
+
var ProviderNotFoundException = class extends RetrivoraError {
|
|
3411
|
+
constructor(providerType, provider, details) {
|
|
3412
|
+
super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
|
|
3413
|
+
}
|
|
3414
|
+
};
|
|
3415
|
+
var EmbeddingFailedException = class extends RetrivoraError {
|
|
3416
|
+
constructor(message = "Embedding generation failed", details) {
|
|
3417
|
+
super(message, "EMBEDDING_FAILED", details);
|
|
3418
|
+
}
|
|
3419
|
+
};
|
|
3420
|
+
var RetrievalException = class extends RetrivoraError {
|
|
3421
|
+
constructor(message = "Retrieval failed", details) {
|
|
3422
|
+
super(message, "RETRIEVAL_FAILED", details);
|
|
3423
|
+
}
|
|
3424
|
+
};
|
|
3425
|
+
var RateLimitException = class extends RetrivoraError {
|
|
3426
|
+
constructor(message = "Provider rate limit exceeded", details) {
|
|
3427
|
+
super(message, "RATE_LIMITED", details);
|
|
3428
|
+
}
|
|
3429
|
+
};
|
|
3430
|
+
var ConfigurationException = class extends RetrivoraError {
|
|
3431
|
+
constructor(message, details) {
|
|
3432
|
+
super(message, "CONFIGURATION_ERROR", details);
|
|
3433
|
+
}
|
|
3434
|
+
};
|
|
3435
|
+
var AuthenticationException = class extends RetrivoraError {
|
|
3436
|
+
constructor(message = "Provider authentication failed", details) {
|
|
3437
|
+
super(message, "AUTHENTICATION_ERROR", details);
|
|
3438
|
+
}
|
|
3439
|
+
};
|
|
3440
|
+
function wrapError(err, defaultCode, defaultMessage) {
|
|
3441
|
+
var _a;
|
|
3442
|
+
if (err instanceof RetrivoraError) {
|
|
3443
|
+
return err;
|
|
3444
|
+
}
|
|
3445
|
+
const error = err;
|
|
3446
|
+
const message = (error == null ? void 0 : error.message) || defaultMessage || String(err);
|
|
3447
|
+
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);
|
|
3448
|
+
const code = error == null ? void 0 : error.code;
|
|
3449
|
+
if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
|
|
3450
|
+
return new RateLimitException(message, err);
|
|
3451
|
+
}
|
|
3452
|
+
if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
|
|
3453
|
+
return new AuthenticationException(message, err);
|
|
3454
|
+
}
|
|
3455
|
+
switch (defaultCode) {
|
|
3456
|
+
case "PROVIDER_NOT_FOUND":
|
|
3457
|
+
return new ProviderNotFoundException("provider", message, err);
|
|
3458
|
+
case "EMBEDDING_FAILED":
|
|
3459
|
+
return new EmbeddingFailedException(message, err);
|
|
3460
|
+
case "RETRIEVAL_FAILED":
|
|
3461
|
+
return new RetrievalException(message, err);
|
|
3462
|
+
case "RATE_LIMITED":
|
|
3463
|
+
return new RateLimitException(message, err);
|
|
3464
|
+
case "CONFIGURATION_ERROR":
|
|
3465
|
+
return new ConfigurationException(message, err);
|
|
3466
|
+
case "AUTHENTICATION_ERROR":
|
|
3467
|
+
return new AuthenticationException(message, err);
|
|
3468
|
+
default:
|
|
3469
|
+
return new RetrivoraError(message, defaultCode, err);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3319
3473
|
// src/llm/LLMFactory.ts
|
|
3320
3474
|
var customProviders = /* @__PURE__ */ new Map();
|
|
3321
3475
|
var LLMFactory = class _LLMFactory {
|
|
@@ -3361,7 +3515,7 @@ var LLMFactory = class _LLMFactory {
|
|
|
3361
3515
|
];
|
|
3362
3516
|
}
|
|
3363
3517
|
static create(llmConfig, embeddingConfig) {
|
|
3364
|
-
var _a, _b;
|
|
3518
|
+
var _a, _b, _c;
|
|
3365
3519
|
switch (llmConfig.provider) {
|
|
3366
3520
|
case "openai":
|
|
3367
3521
|
return new OpenAIProvider(llmConfig, embeddingConfig);
|
|
@@ -3384,8 +3538,13 @@ var LLMFactory = class _LLMFactory {
|
|
|
3384
3538
|
if (llmConfig.baseUrl || ((_b = llmConfig.options) == null ? void 0 : _b.baseUrl)) {
|
|
3385
3539
|
return new UniversalLLMAdapter(llmConfig);
|
|
3386
3540
|
}
|
|
3387
|
-
throw new
|
|
3388
|
-
|
|
3541
|
+
throw new ProviderNotFoundException(
|
|
3542
|
+
"llm",
|
|
3543
|
+
(_c = llmConfig.provider) != null ? _c : "undefined",
|
|
3544
|
+
{
|
|
3545
|
+
message: `Unknown provider "${llmConfig.provider}". Register a custom provider with LLMFactory.register().`,
|
|
3546
|
+
available: _LLMFactory.listProviders()
|
|
3547
|
+
}
|
|
3389
3548
|
);
|
|
3390
3549
|
}
|
|
3391
3550
|
}
|
|
@@ -3516,7 +3675,7 @@ var ProviderRegistry = class {
|
|
|
3516
3675
|
return UniversalVectorProvider2;
|
|
3517
3676
|
}
|
|
3518
3677
|
default:
|
|
3519
|
-
throw new
|
|
3678
|
+
throw new ProviderNotFoundException("vector", provider);
|
|
3520
3679
|
}
|
|
3521
3680
|
}
|
|
3522
3681
|
static async createVectorProvider(config) {
|
|
@@ -3534,12 +3693,18 @@ var ProviderRegistry = class {
|
|
|
3534
3693
|
return new SimpleGraphProvider2(config);
|
|
3535
3694
|
}
|
|
3536
3695
|
default:
|
|
3537
|
-
throw new
|
|
3696
|
+
throw new ProviderNotFoundException("graph", provider);
|
|
3538
3697
|
}
|
|
3539
3698
|
}
|
|
3540
3699
|
static createLLMProvider(llmConfig, embeddingConfig) {
|
|
3541
3700
|
return LLMFactory.create(llmConfig, embeddingConfig);
|
|
3542
3701
|
}
|
|
3702
|
+
static registerLLMProvider(name, factory) {
|
|
3703
|
+
LLMFactory.register(name, factory);
|
|
3704
|
+
}
|
|
3705
|
+
static createEmbeddingProvider(embeddingConfig) {
|
|
3706
|
+
return LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
3707
|
+
}
|
|
3543
3708
|
};
|
|
3544
3709
|
ProviderRegistry.vectorProviders = {};
|
|
3545
3710
|
ProviderRegistry.graphProviders = {};
|
|
@@ -3686,8 +3851,8 @@ var ConfigValidator = class {
|
|
|
3686
3851
|
const errorItems = errors.filter((e) => e.severity === "error");
|
|
3687
3852
|
if (errorItems.length > 0) {
|
|
3688
3853
|
const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
|
|
3689
|
-
throw new
|
|
3690
|
-
${message}
|
|
3854
|
+
throw new ConfigurationException(`[ConfigValidator] Configuration validation failed:
|
|
3855
|
+
${message}`, errorItems);
|
|
3691
3856
|
}
|
|
3692
3857
|
}
|
|
3693
3858
|
};
|
|
@@ -3874,20 +4039,17 @@ var Reranker = class {
|
|
|
3874
4039
|
}
|
|
3875
4040
|
try {
|
|
3876
4041
|
const topN = matches.slice(0, 10);
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
|
|
3880
|
-
Use the indices provided in brackets like [0], [1], etc.
|
|
3881
|
-
Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
|
|
3882
|
-
|
|
3883
|
-
Query: "${query}"
|
|
4042
|
+
const response = await llm.chat(
|
|
4043
|
+
[{ role: "user", content: `Query: "${query}"
|
|
3884
4044
|
|
|
3885
4045
|
Documents:
|
|
3886
|
-
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}
|
|
3887
|
-
const response = await llm.chat(
|
|
3888
|
-
[{ role: "user", content: prompt }],
|
|
4046
|
+
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}` }],
|
|
3889
4047
|
"",
|
|
3890
|
-
{
|
|
4048
|
+
{
|
|
4049
|
+
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.',
|
|
4050
|
+
temperature: 0,
|
|
4051
|
+
maxTokens: 50
|
|
4052
|
+
}
|
|
3891
4053
|
);
|
|
3892
4054
|
const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
|
|
3893
4055
|
const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
|
|
@@ -4399,6 +4561,18 @@ var QueryProcessor = class {
|
|
|
4399
4561
|
}, normalizedField ? { field: normalizedField } : {}));
|
|
4400
4562
|
}
|
|
4401
4563
|
};
|
|
4564
|
+
const activeValidFields = validFields.length > 0 ? validFields : ["brand", "category", "price", "rating", "stock", "stock_quantity", "status", "name", "title"];
|
|
4565
|
+
const resolveValidField = (fieldStr) => {
|
|
4566
|
+
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();
|
|
4567
|
+
const comparable = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4568
|
+
const cleanedComparable = comparable(cleaned);
|
|
4569
|
+
if (!cleanedComparable) return null;
|
|
4570
|
+
const matchedField = activeValidFields.find((fieldName) => {
|
|
4571
|
+
const candidate = comparable(fieldName);
|
|
4572
|
+
return candidate === cleanedComparable || candidate.includes(cleanedComparable) || cleanedComparable.includes(candidate);
|
|
4573
|
+
});
|
|
4574
|
+
return matchedField != null ? matchedField : null;
|
|
4575
|
+
};
|
|
4402
4576
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
4403
4577
|
addHint(match[1]);
|
|
4404
4578
|
}
|
|
@@ -4423,7 +4597,13 @@ var QueryProcessor = class {
|
|
|
4423
4597
|
const field = match[1];
|
|
4424
4598
|
const value = match[2];
|
|
4425
4599
|
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4426
|
-
|
|
4600
|
+
const resolvedField = resolveValidField(field);
|
|
4601
|
+
if (resolvedField) {
|
|
4602
|
+
addHint(value, resolvedField);
|
|
4603
|
+
} else {
|
|
4604
|
+
addHint(value);
|
|
4605
|
+
addHint(field);
|
|
4606
|
+
}
|
|
4427
4607
|
}
|
|
4428
4608
|
}
|
|
4429
4609
|
if (validFields.length > 0) {
|
|
@@ -4469,12 +4649,16 @@ var QueryProcessor = class {
|
|
|
4469
4649
|
];
|
|
4470
4650
|
for (const pattern of fieldValuePatterns) {
|
|
4471
4651
|
for (const match of question.matchAll(pattern)) {
|
|
4472
|
-
const field =
|
|
4652
|
+
const field = (_c = match[1]) != null ? _c : "";
|
|
4473
4653
|
const value = (_d = match[2]) != null ? _d : "";
|
|
4474
|
-
|
|
4475
|
-
|
|
4654
|
+
const resolvedField = resolveValidField(field);
|
|
4655
|
+
if (resolvedField) {
|
|
4656
|
+
addHint(value, resolvedField);
|
|
4476
4657
|
} else {
|
|
4477
4658
|
addHint(value);
|
|
4659
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4660
|
+
addHint(field);
|
|
4661
|
+
}
|
|
4478
4662
|
}
|
|
4479
4663
|
}
|
|
4480
4664
|
}
|
|
@@ -4701,7 +4885,7 @@ var LLMRouter = class {
|
|
|
4701
4885
|
|
|
4702
4886
|
// src/utils/UITransformer.ts
|
|
4703
4887
|
init_synonyms();
|
|
4704
|
-
var UITransformer = class {
|
|
4888
|
+
var UITransformer = class _UITransformer {
|
|
4705
4889
|
// ─── Public Entry Points ─────────────────────────────────────────────────
|
|
4706
4890
|
/**
|
|
4707
4891
|
* Heuristic-only transform (no LLM required).
|
|
@@ -4763,7 +4947,7 @@ var UITransformer = class {
|
|
|
4763
4947
|
static async analyzeAndDecide(query, sources, llm) {
|
|
4764
4948
|
let intent;
|
|
4765
4949
|
try {
|
|
4766
|
-
intent = await this.detectIntent(query, llm);
|
|
4950
|
+
intent = await this.detectIntent(query, llm, sources);
|
|
4767
4951
|
console.debug("[UITransformer] Detected intent:", intent);
|
|
4768
4952
|
} catch (err) {
|
|
4769
4953
|
console.warn("[UITransformer] Intent detection failed; using heuristic.", err);
|
|
@@ -4781,11 +4965,23 @@ var UITransformer = class {
|
|
|
4781
4965
|
try {
|
|
4782
4966
|
const context = this.buildContextSummary(sources);
|
|
4783
4967
|
const systemPrompt = this.buildVisualizationSystemPrompt();
|
|
4968
|
+
const profile = this.profileData(sources);
|
|
4969
|
+
const schemaContext = `
|
|
4970
|
+
RETRIEVED DATA SCHEMA:
|
|
4971
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4972
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4973
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4974
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4975
|
+
- Text/Other fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
4976
|
+
`;
|
|
4784
4977
|
const userPrompt = [
|
|
4785
4978
|
`USER QUESTION: ${query}`,
|
|
4786
4979
|
"",
|
|
4787
4980
|
`DETECTED INTENT (JSON): ${JSON.stringify(intent)}`,
|
|
4788
4981
|
"",
|
|
4982
|
+
`RETRIEVED DATA SCHEMA:`,
|
|
4983
|
+
schemaContext,
|
|
4984
|
+
"",
|
|
4789
4985
|
"RETRIEVED DATA (JSON):",
|
|
4790
4986
|
context
|
|
4791
4987
|
].join("\n");
|
|
@@ -4796,6 +4992,20 @@ var UITransformer = class {
|
|
|
4796
4992
|
);
|
|
4797
4993
|
const parsed = this.parseTransformationResponse(rawResponse);
|
|
4798
4994
|
if (parsed) {
|
|
4995
|
+
let isCompatible = true;
|
|
4996
|
+
if (parsed.type === "line_chart" && (profile.dateFields.length === 0 || profile.numericFields.length === 0)) {
|
|
4997
|
+
isCompatible = false;
|
|
4998
|
+
} else if (["bar_chart", "horizontal_bar", "pie_chart", "donut_chart"].includes(parsed.type) && (profile.categoricalFields.length === 0 && profile.numericFields.length === 0)) {
|
|
4999
|
+
isCompatible = false;
|
|
5000
|
+
} else if (parsed.type === "scatter_plot" && profile.numericFields.length < 2) {
|
|
5001
|
+
isCompatible = false;
|
|
5002
|
+
} else if (parsed.type === "metric_card" && profile.numericFields.length === 0) {
|
|
5003
|
+
isCompatible = false;
|
|
5004
|
+
}
|
|
5005
|
+
if (!isCompatible) {
|
|
5006
|
+
console.warn(`[UITransformer] LLM chose incompatible visualization type "${parsed.type}" for retrieved schema. Falling back to heuristic.`);
|
|
5007
|
+
return this.transform(query, sources, void 0, void 0, intent);
|
|
5008
|
+
}
|
|
4799
5009
|
const intentAllowsTable = intent.wantsExplicitTable || ["tabular", "table", "geographic"].includes(intent.visualizationHint);
|
|
4800
5010
|
const intentWantsPieLikeChart = ["pie_chart", "donut_chart"].includes(intent.recommendedChart) || ["composition", "category_breakdown"].includes(intent.visualizationHint);
|
|
4801
5011
|
if (parsed.type === "table" && !intentAllowsTable) {
|
|
@@ -4825,7 +5035,26 @@ var UITransformer = class {
|
|
|
4825
5035
|
* - The intent object can be reused across both the heuristic and LLM paths.
|
|
4826
5036
|
* - It is easy to unit-test intent detection in isolation.
|
|
4827
5037
|
*/
|
|
4828
|
-
static async detectIntent(query, llm) {
|
|
5038
|
+
static async detectIntent(query, llm, sources) {
|
|
5039
|
+
let schemaProfileText = "";
|
|
5040
|
+
if (sources && sources.length > 0) {
|
|
5041
|
+
const profile = this.profileData(sources);
|
|
5042
|
+
const hasProducts = sources.some((item) => this.isProductData(item));
|
|
5043
|
+
schemaProfileText = `
|
|
5044
|
+
RETRIEVED DATA SCHEMA:
|
|
5045
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5046
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5047
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5048
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5049
|
+
- Text fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
5050
|
+
|
|
5051
|
+
Please choose visualizationHint and recommendedChart dynamically based on the available schema.
|
|
5052
|
+
- Do NOT recommend "trend" or "line_chart" if there are no Date fields.
|
|
5053
|
+
- 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.
|
|
5054
|
+
- If there are multiple metadata fields but no clear categories or numbers, "tabular"/"table" is a highly accurate default.
|
|
5055
|
+
${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".` : ""}
|
|
5056
|
+
`;
|
|
5057
|
+
}
|
|
4829
5058
|
const systemPrompt = `You are an intent classifier for a product-search RAG system.
|
|
4830
5059
|
Given a user query, return ONLY a valid JSON object with this exact shape \u2014 no prose, no markdown:
|
|
4831
5060
|
|
|
@@ -4861,8 +5090,11 @@ RULES:
|
|
|
4861
5090
|
- isTemporal: true if time words appear (trend, historical, over time, last year, monthly, etc.)
|
|
4862
5091
|
- isComparison: true if user compares, ranks, or contrasts entities or categories.
|
|
4863
5092
|
- language: detect from the query text itself; default "en" if uncertain.`;
|
|
5093
|
+
const userPrompt = `QUERY: ${query}${schemaProfileText ? `
|
|
5094
|
+
|
|
5095
|
+
${schemaProfileText}` : ""}`;
|
|
4864
5096
|
const rawResponse = await llm.chat(
|
|
4865
|
-
[{ role: "user", content:
|
|
5097
|
+
[{ role: "user", content: userPrompt }],
|
|
4866
5098
|
"",
|
|
4867
5099
|
{ systemPrompt, temperature: 0 }
|
|
4868
5100
|
);
|
|
@@ -5305,10 +5537,21 @@ RULES:
|
|
|
5305
5537
|
const hasEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5306
5538
|
return asksForRecords && (hasNumericPredicate || hasEntityTerms);
|
|
5307
5539
|
}
|
|
5540
|
+
/** @internal kept private for transform() internal logic */
|
|
5308
5541
|
static isProductQuery(query) {
|
|
5542
|
+
return _UITransformer._productQueryTest(query);
|
|
5543
|
+
}
|
|
5544
|
+
/**
|
|
5545
|
+
* Public entry-point for external callers (e.g. Pipeline)
|
|
5546
|
+
* to check whether a query maps to product_browse without triggering an LLM call.
|
|
5547
|
+
*/
|
|
5548
|
+
static isProductQueryPublic(query) {
|
|
5549
|
+
return _UITransformer._productQueryTest(query);
|
|
5550
|
+
}
|
|
5551
|
+
static _productQueryTest(query) {
|
|
5309
5552
|
const q = query.toLowerCase();
|
|
5310
5553
|
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);
|
|
5311
|
-
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list)\b/.test(q);
|
|
5554
|
+
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list|explore|exploring)\b/.test(q);
|
|
5312
5555
|
const nonProductEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5313
5556
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
5314
5557
|
}
|
|
@@ -6081,23 +6324,19 @@ function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
|
6081
6324
|
async function scoreHallucination(llm, answer, context) {
|
|
6082
6325
|
const maxContextChars = 3e3;
|
|
6083
6326
|
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
CONTEXT:
|
|
6327
|
+
try {
|
|
6328
|
+
const raw = await llm.chat(
|
|
6329
|
+
[{ role: "user", content: `CONTEXT:
|
|
6087
6330
|
${truncatedContext}
|
|
6088
6331
|
|
|
6089
6332
|
ANSWER:
|
|
6090
|
-
${answer}
|
|
6091
|
-
|
|
6092
|
-
Return ONLY a valid JSON object with no markdown fences:
|
|
6093
|
-
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
6094
|
-
|
|
6095
|
-
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
6096
|
-
try {
|
|
6097
|
-
const raw = await llm.chat(
|
|
6098
|
-
[{ role: "user", content: prompt }],
|
|
6333
|
+
${answer}` }],
|
|
6099
6334
|
"",
|
|
6100
|
-
{
|
|
6335
|
+
{
|
|
6336
|
+
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.',
|
|
6337
|
+
temperature: 0,
|
|
6338
|
+
maxTokens: 120
|
|
6339
|
+
}
|
|
6101
6340
|
);
|
|
6102
6341
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
6103
6342
|
if (!jsonMatch) return void 0;
|
|
@@ -6157,7 +6396,10 @@ var Pipeline = class {
|
|
|
6157
6396
|
- 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.
|
|
6158
6397
|
- Do NOT try to format product lists as tables.
|
|
6159
6398
|
`;
|
|
6160
|
-
this.config.llm.systemPrompt
|
|
6399
|
+
const userPromptPrefix = this.config.llm.systemPrompt ? `${this.config.llm.systemPrompt}
|
|
6400
|
+
|
|
6401
|
+
` : "";
|
|
6402
|
+
this.config.llm.systemPrompt = `${userPromptPrefix}${chartInstruction}`;
|
|
6161
6403
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
6162
6404
|
this.llmRouter = new LLMRouter(this.config);
|
|
6163
6405
|
const { llmProvider: resolvedLLM, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
@@ -6237,7 +6479,7 @@ var Pipeline = class {
|
|
|
6237
6479
|
embedBatchOptions
|
|
6238
6480
|
);
|
|
6239
6481
|
if (vectors.length !== chunks.length) {
|
|
6240
|
-
throw new
|
|
6482
|
+
throw new EmbeddingFailedException(
|
|
6241
6483
|
`[Pipeline] Embedding mismatch: got ${vectors.length} vectors for ${chunks.length} chunks. Check embedding provider logs for individual chunk failures.`
|
|
6242
6484
|
);
|
|
6243
6485
|
}
|
|
@@ -6335,11 +6577,11 @@ var Pipeline = class {
|
|
|
6335
6577
|
*/
|
|
6336
6578
|
askStream(_0) {
|
|
6337
6579
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
6338
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
6580
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
6339
6581
|
yield new __await(this.initialize());
|
|
6340
6582
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
6341
6583
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
6342
|
-
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
6584
|
+
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
6343
6585
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6344
6586
|
const requestStart = performance.now();
|
|
6345
6587
|
try {
|
|
@@ -6358,12 +6600,13 @@ var Pipeline = class {
|
|
|
6358
6600
|
const embedStart = performance.now();
|
|
6359
6601
|
const cacheKey = `${ns}::${searchQuery}`;
|
|
6360
6602
|
const cachedVector = this.embeddingCache.get(cacheKey);
|
|
6603
|
+
const useGraph = this.graphDB && ((_h = this.config.rag) == null ? void 0 : _h.useGraphRetrieval);
|
|
6361
6604
|
const [strategyResult, embeddedVector] = yield new __await(Promise.all([
|
|
6362
6605
|
QueryProcessor.determineRetrievalStrategy(
|
|
6363
6606
|
searchQuery,
|
|
6364
|
-
this.llmRouter.get("fast"),
|
|
6365
|
-
(
|
|
6366
|
-
(
|
|
6607
|
+
useGraph ? this.llmRouter.get("fast") : void 0,
|
|
6608
|
+
(_i = this.config.rag) == null ? void 0 : _i.graphKeywords,
|
|
6609
|
+
(_j = this.config.rag) == null ? void 0 : _j.vectorKeywords
|
|
6367
6610
|
),
|
|
6368
6611
|
// Embed immediately regardless of strategy — costs nothing if cached.
|
|
6369
6612
|
// If strategy turns out to be 'graph'-only we just won't use the vector.
|
|
@@ -6373,7 +6616,7 @@ var Pipeline = class {
|
|
|
6373
6616
|
if (!cachedVector && queryVector.length > 0) {
|
|
6374
6617
|
this.embeddingCache.set(cacheKey, queryVector);
|
|
6375
6618
|
}
|
|
6376
|
-
const graphData = (strategyResult === "graph" || strategyResult === "both") && this.graphDB && ((
|
|
6619
|
+
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;
|
|
6377
6620
|
const hasMetadataFilter = filter.metadata && Object.keys(filter.metadata).length > 0;
|
|
6378
6621
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
6379
6622
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
@@ -6386,7 +6629,7 @@ var Pipeline = class {
|
|
|
6386
6629
|
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
6387
6630
|
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => m.score >= scoreThreshold);
|
|
6388
6631
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
6389
|
-
if (!hasNumericPredicates && ((
|
|
6632
|
+
if (!hasNumericPredicates && ((_l = this.config.rag) == null ? void 0 : _l.useReranking)) {
|
|
6390
6633
|
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
6391
6634
|
} else if (!wantsExhaustiveList) {
|
|
6392
6635
|
fullSources = fullSources.slice(0, topK);
|
|
@@ -6417,34 +6660,117 @@ VECTOR CONTEXT:
|
|
|
6417
6660
|
${context}`;
|
|
6418
6661
|
}
|
|
6419
6662
|
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
6420
|
-
const
|
|
6421
|
-
|
|
6422
|
-
(
|
|
6663
|
+
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
6664
|
+
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
6665
|
+
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
6666
|
+
});
|
|
6667
|
+
}
|
|
6668
|
+
const isProductQ = UITransformer.isProductQueryPublic(question);
|
|
6669
|
+
const uiTransformationPromise = isProductQ ? Promise.resolve(
|
|
6670
|
+
UITransformer.transform(
|
|
6423
6671
|
question,
|
|
6424
6672
|
sources,
|
|
6425
|
-
|
|
6426
|
-
|
|
6673
|
+
this.config,
|
|
6674
|
+
cachedSchema,
|
|
6675
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6427
6676
|
)
|
|
6677
|
+
) : this.generateUiTransformation(
|
|
6678
|
+
question,
|
|
6679
|
+
sources,
|
|
6680
|
+
cachedSchema,
|
|
6681
|
+
hasNumericPredicates
|
|
6428
6682
|
).catch((uiError) => {
|
|
6429
6683
|
console.warn("[Pipeline] UI transformation failed concurrently:", uiError);
|
|
6430
|
-
return UITransformer.transform(question, sources, this.config);
|
|
6684
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6431
6685
|
});
|
|
6686
|
+
let hallucinationScoringPromise = Promise.resolve(void 0);
|
|
6432
6687
|
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.)";
|
|
6688
|
+
const isClaude37 = this.config.llm.model.includes("claude-3-7-sonnet");
|
|
6689
|
+
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);
|
|
6690
|
+
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
6691
|
+
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
6692
|
+
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);
|
|
6693
|
+
let finalRestrictionSuffix = restrictionSuffix;
|
|
6694
|
+
if (isSimulatedThinking) {
|
|
6695
|
+
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.`)";
|
|
6696
|
+
}
|
|
6433
6697
|
const hardenedHistory = [...history];
|
|
6434
|
-
const userQuestion = { role: "user", content: question +
|
|
6698
|
+
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
6435
6699
|
const messages = [...hardenedHistory, userQuestion];
|
|
6436
|
-
const systemPrompt = (
|
|
6700
|
+
const systemPrompt = (_s = this.config.llm.systemPrompt) != null ? _s : "";
|
|
6437
6701
|
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
6438
6702
|
let fullReply = "";
|
|
6703
|
+
let thinkingText = "";
|
|
6704
|
+
let thinkingStartMs = performance.now();
|
|
6705
|
+
let thinkingDurationMs = 0;
|
|
6439
6706
|
const generateStart = performance.now();
|
|
6440
6707
|
if (this.llmProvider.chatStream) {
|
|
6441
6708
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
6442
6709
|
if (!stream) throw new Error(`[Pipeline] ${this.config.llm.provider} chatStream returned undefined`);
|
|
6710
|
+
let inThinking = false;
|
|
6711
|
+
let textBuffer = "";
|
|
6443
6712
|
try {
|
|
6444
6713
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
6445
6714
|
const chunk = temp.value;
|
|
6446
|
-
|
|
6447
|
-
|
|
6715
|
+
if (chunk.startsWith("\0THINKING\0")) {
|
|
6716
|
+
const thinkingDelta = chunk.slice("\0THINKING\0".length);
|
|
6717
|
+
thinkingText += thinkingDelta;
|
|
6718
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6719
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6720
|
+
continue;
|
|
6721
|
+
}
|
|
6722
|
+
if (isSimulatedThinking) {
|
|
6723
|
+
textBuffer += chunk;
|
|
6724
|
+
while (textBuffer.length > 0) {
|
|
6725
|
+
if (!inThinking) {
|
|
6726
|
+
const thinkIndex = textBuffer.indexOf("<think>");
|
|
6727
|
+
if (thinkIndex !== -1) {
|
|
6728
|
+
const before = textBuffer.slice(0, thinkIndex);
|
|
6729
|
+
if (before) {
|
|
6730
|
+
fullReply += before;
|
|
6731
|
+
yield before;
|
|
6732
|
+
}
|
|
6733
|
+
inThinking = true;
|
|
6734
|
+
thinkingStartMs = performance.now();
|
|
6735
|
+
textBuffer = textBuffer.slice(thinkIndex + "<think>".length);
|
|
6736
|
+
} else {
|
|
6737
|
+
const maxSafeLength = textBuffer.length - "<think>".length;
|
|
6738
|
+
if (maxSafeLength > 0) {
|
|
6739
|
+
const safeText = textBuffer.slice(0, maxSafeLength);
|
|
6740
|
+
fullReply += safeText;
|
|
6741
|
+
yield safeText;
|
|
6742
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6743
|
+
}
|
|
6744
|
+
break;
|
|
6745
|
+
}
|
|
6746
|
+
} else {
|
|
6747
|
+
const endThinkIndex = textBuffer.indexOf("</think>");
|
|
6748
|
+
if (endThinkIndex !== -1) {
|
|
6749
|
+
const thinkingDelta = textBuffer.slice(0, endThinkIndex);
|
|
6750
|
+
if (thinkingDelta) {
|
|
6751
|
+
thinkingText += thinkingDelta;
|
|
6752
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6753
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6754
|
+
}
|
|
6755
|
+
inThinking = false;
|
|
6756
|
+
textBuffer = textBuffer.slice(endThinkIndex + "</think>".length);
|
|
6757
|
+
} else {
|
|
6758
|
+
const maxSafeLength = textBuffer.length - "</think>".length;
|
|
6759
|
+
if (maxSafeLength > 0) {
|
|
6760
|
+
const thinkingDelta = textBuffer.slice(0, maxSafeLength);
|
|
6761
|
+
thinkingText += thinkingDelta;
|
|
6762
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6763
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6764
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6765
|
+
}
|
|
6766
|
+
break;
|
|
6767
|
+
}
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
} else {
|
|
6771
|
+
fullReply += chunk;
|
|
6772
|
+
yield chunk;
|
|
6773
|
+
}
|
|
6448
6774
|
}
|
|
6449
6775
|
} catch (temp) {
|
|
6450
6776
|
error = [temp];
|
|
@@ -6456,17 +6782,42 @@ ${context}`;
|
|
|
6456
6782
|
throw error[0];
|
|
6457
6783
|
}
|
|
6458
6784
|
}
|
|
6785
|
+
if (isSimulatedThinking && textBuffer.length > 0) {
|
|
6786
|
+
if (inThinking) {
|
|
6787
|
+
thinkingText += textBuffer;
|
|
6788
|
+
yield { type: "thinking", text: textBuffer };
|
|
6789
|
+
} else {
|
|
6790
|
+
fullReply += textBuffer;
|
|
6791
|
+
yield textBuffer;
|
|
6792
|
+
}
|
|
6793
|
+
}
|
|
6459
6794
|
} else {
|
|
6460
6795
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
6461
|
-
|
|
6462
|
-
|
|
6796
|
+
if (isSimulatedThinking) {
|
|
6797
|
+
const thinkStart = reply.indexOf("<think>");
|
|
6798
|
+
const thinkEnd = reply.indexOf("</think>");
|
|
6799
|
+
if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {
|
|
6800
|
+
thinkingText = reply.slice(thinkStart + "<think>".length, thinkEnd);
|
|
6801
|
+
fullReply = reply.slice(0, thinkStart) + reply.slice(thinkEnd + "</think>".length);
|
|
6802
|
+
thinkingDurationMs = 100;
|
|
6803
|
+
} else {
|
|
6804
|
+
fullReply = reply;
|
|
6805
|
+
}
|
|
6806
|
+
} else {
|
|
6807
|
+
fullReply = reply;
|
|
6808
|
+
}
|
|
6809
|
+
yield fullReply;
|
|
6810
|
+
}
|
|
6811
|
+
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;
|
|
6812
|
+
if (runHallucination) {
|
|
6813
|
+
hallucinationScoringPromise = scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0);
|
|
6463
6814
|
}
|
|
6464
6815
|
const generateMs = performance.now() - generateStart;
|
|
6465
6816
|
const totalMs = performance.now() - requestStart;
|
|
6466
6817
|
const latency = {
|
|
6467
6818
|
embedMs: Math.round(embedMs),
|
|
6468
6819
|
retrieveMs: Math.round(retrieveMs),
|
|
6469
|
-
rerankMs: ((
|
|
6820
|
+
rerankMs: ((_v = this.config.rag) == null ? void 0 : _v.useReranking) ? Math.round(rerankMs) : void 0,
|
|
6470
6821
|
generateMs: Math.round(generateMs),
|
|
6471
6822
|
totalMs: Math.round(totalMs)
|
|
6472
6823
|
};
|
|
@@ -6479,12 +6830,16 @@ ${context}`;
|
|
|
6479
6830
|
totalTokens: promptTokens + completionTokens,
|
|
6480
6831
|
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
6481
6832
|
};
|
|
6482
|
-
const
|
|
6833
|
+
const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
|
|
6834
|
+
uiTransformationPromise,
|
|
6835
|
+
hallucinationScoringPromise
|
|
6836
|
+
]));
|
|
6837
|
+
const trace = __spreadValues({
|
|
6483
6838
|
requestId,
|
|
6484
6839
|
query: question,
|
|
6485
6840
|
rewrittenQuery,
|
|
6486
6841
|
systemPrompt,
|
|
6487
|
-
userPrompt: question +
|
|
6842
|
+
userPrompt: question + finalRestrictionSuffix,
|
|
6488
6843
|
chunks: sources.map((s) => {
|
|
6489
6844
|
var _a2;
|
|
6490
6845
|
return {
|
|
@@ -6498,22 +6853,19 @@ ${context}`;
|
|
|
6498
6853
|
latency,
|
|
6499
6854
|
tokens,
|
|
6500
6855
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6501
|
-
}
|
|
6502
|
-
|
|
6856
|
+
}, hallucinationResult ? {
|
|
6857
|
+
hallucinationScore: hallucinationResult.score,
|
|
6858
|
+
hallucinationReason: hallucinationResult.reason
|
|
6859
|
+
} : {});
|
|
6503
6860
|
yield {
|
|
6504
6861
|
reply: "",
|
|
6505
6862
|
sources,
|
|
6506
6863
|
graphData,
|
|
6507
6864
|
ui_transformation: uiTransformation,
|
|
6508
|
-
trace
|
|
6865
|
+
trace,
|
|
6866
|
+
thinking: thinkingText || void 0,
|
|
6867
|
+
thinkingMs: thinkingDurationMs > 0 ? Math.round(thinkingDurationMs) : void 0
|
|
6509
6868
|
};
|
|
6510
|
-
scoreHallucination(this.llmProvider, fullReply, context).then((hallucinationResult) => {
|
|
6511
|
-
if (hallucinationResult) {
|
|
6512
|
-
trace.hallucinationScore = hallucinationResult.score;
|
|
6513
|
-
trace.hallucinationReason = hallucinationResult.reason;
|
|
6514
|
-
}
|
|
6515
|
-
}).catch(() => {
|
|
6516
|
-
});
|
|
6517
6869
|
} catch (error2) {
|
|
6518
6870
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
6519
6871
|
}
|
|
@@ -6523,18 +6875,30 @@ ${context}`;
|
|
|
6523
6875
|
* Universal retrieval method combining all enabled providers.
|
|
6524
6876
|
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
6525
6877
|
*/
|
|
6526
|
-
async generateUiTransformation(question, sources,
|
|
6878
|
+
async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
|
|
6879
|
+
var _a;
|
|
6527
6880
|
if (!sources || sources.length === 0) {
|
|
6528
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6881
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6882
|
+
}
|
|
6883
|
+
if (UITransformer.isProductQueryPublic(question)) {
|
|
6884
|
+
return UITransformer.transform(
|
|
6885
|
+
question,
|
|
6886
|
+
sources,
|
|
6887
|
+
this.config,
|
|
6888
|
+
cachedSchema,
|
|
6889
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6890
|
+
);
|
|
6529
6891
|
}
|
|
6530
|
-
|
|
6531
|
-
|
|
6892
|
+
const isLocalProvider = this.config.llm.provider === "ollama";
|
|
6893
|
+
const disableLlmUiTransform = ((_a = this.config.llm.options) == null ? void 0 : _a.disableLlmUiTransform) === true;
|
|
6894
|
+
if (forceDeterministic || isLocalProvider || disableLlmUiTransform) {
|
|
6895
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6532
6896
|
}
|
|
6533
6897
|
try {
|
|
6534
6898
|
return await UITransformer.analyzeAndDecide(question, sources, this.llmRouter.get("fast"));
|
|
6535
6899
|
} catch (err) {
|
|
6536
6900
|
console.warn("[Pipeline] generateUiTransformation failed, using heuristic fallback:", err);
|
|
6537
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6901
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6538
6902
|
}
|
|
6539
6903
|
}
|
|
6540
6904
|
applyStructuredFilters(sources, filter) {
|
|
@@ -6618,25 +6982,29 @@ ${context}`;
|
|
|
6618
6982
|
return Number.isFinite(numeric) ? numeric : null;
|
|
6619
6983
|
}
|
|
6620
6984
|
async retrieve(query, options) {
|
|
6621
|
-
var _a, _b, _c, _d, _e, _f;
|
|
6985
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
6622
6986
|
const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
|
|
6623
6987
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
6624
6988
|
const cacheKey = `${ns}::${query}`;
|
|
6625
6989
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
6626
|
-
const
|
|
6990
|
+
const useGraph = this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval);
|
|
6991
|
+
const strategy = await QueryProcessor.determineRetrievalStrategy(
|
|
6992
|
+
query,
|
|
6993
|
+
useGraph ? this.llmRouter.get("fast") : void 0
|
|
6994
|
+
);
|
|
6627
6995
|
console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
|
|
6628
6996
|
const [retrievedVector, graphData] = await Promise.all([
|
|
6629
6997
|
// Only embed if we need vector search (strategy is 'vector' or 'both')
|
|
6630
6998
|
strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
|
|
6631
6999
|
// Only query graph if we need graph search (strategy is 'graph' or 'both')
|
|
6632
|
-
(strategy === "graph" || strategy === "both") && this.graphDB && ((
|
|
7000
|
+
(strategy === "graph" || strategy === "both") && this.graphDB && ((_d = this.config.rag) == null ? void 0 : _d.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
|
|
6633
7001
|
]);
|
|
6634
7002
|
if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
|
|
6635
7003
|
this.embeddingCache.set(cacheKey, retrievedVector);
|
|
6636
7004
|
queryVector = retrievedVector;
|
|
6637
7005
|
}
|
|
6638
|
-
const baseFilter = __spreadProps(__spreadValues({}, (
|
|
6639
|
-
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (
|
|
7006
|
+
const baseFilter = __spreadProps(__spreadValues({}, (_e = options.filter) != null ? _e : {}), { queryText: query });
|
|
7007
|
+
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
6640
7008
|
if (numericPredicates.length > 0) {
|
|
6641
7009
|
baseFilter.__numericPredicates = numericPredicates;
|
|
6642
7010
|
}
|
|
@@ -6647,7 +7015,7 @@ ${context}`;
|
|
|
6647
7015
|
) : [];
|
|
6648
7016
|
const resolvedSources = [];
|
|
6649
7017
|
for (const source of sources) {
|
|
6650
|
-
const parentId = (
|
|
7018
|
+
const parentId = (_g = source.metadata) == null ? void 0 : _g.parent_id;
|
|
6651
7019
|
if (parentId) {
|
|
6652
7020
|
console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
|
|
6653
7021
|
resolvedSources.push(source);
|
|
@@ -6670,10 +7038,13 @@ New Question: ${question}
|
|
|
6670
7038
|
Optimized Search Query:`;
|
|
6671
7039
|
const rewrite = await this.llmProvider.chat(
|
|
6672
7040
|
[
|
|
6673
|
-
{ role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
|
|
6674
7041
|
{ role: "user", content: prompt }
|
|
6675
7042
|
],
|
|
6676
|
-
""
|
|
7043
|
+
"",
|
|
7044
|
+
{
|
|
7045
|
+
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.",
|
|
7046
|
+
temperature: 0
|
|
7047
|
+
}
|
|
6677
7048
|
);
|
|
6678
7049
|
return rewrite.trim() || question;
|
|
6679
7050
|
}
|
|
@@ -6697,10 +7068,13 @@ ${context}
|
|
|
6697
7068
|
Suggestions:`;
|
|
6698
7069
|
const response = await this.llmProvider.chat(
|
|
6699
7070
|
[
|
|
6700
|
-
{ role: "system", content: "You are a helpful assistant that generates search suggestions." },
|
|
6701
7071
|
{ role: "user", content: prompt }
|
|
6702
7072
|
],
|
|
6703
|
-
""
|
|
7073
|
+
"",
|
|
7074
|
+
{
|
|
7075
|
+
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.",
|
|
7076
|
+
temperature: 0
|
|
7077
|
+
}
|
|
6704
7078
|
);
|
|
6705
7079
|
const match = response.match(/\[[\s\S]*\]/);
|
|
6706
7080
|
if (match) {
|
|
@@ -6716,6 +7090,82 @@ Suggestions:`;
|
|
|
6716
7090
|
}
|
|
6717
7091
|
};
|
|
6718
7092
|
|
|
7093
|
+
// src/core/Retrivora.ts
|
|
7094
|
+
var Retrivora = class {
|
|
7095
|
+
constructor(config) {
|
|
7096
|
+
this.config = ConfigResolver.resolveUniversal(config);
|
|
7097
|
+
this.pipeline = new Pipeline(this.config);
|
|
7098
|
+
}
|
|
7099
|
+
async initialize() {
|
|
7100
|
+
try {
|
|
7101
|
+
await ConfigValidator.validateAndThrow(this.config);
|
|
7102
|
+
} catch (err) {
|
|
7103
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7104
|
+
}
|
|
7105
|
+
try {
|
|
7106
|
+
await this.pipeline.initialize();
|
|
7107
|
+
} catch (err) {
|
|
7108
|
+
throw wrapError(err, "AUTHENTICATION_ERROR");
|
|
7109
|
+
}
|
|
7110
|
+
}
|
|
7111
|
+
async ingest(documents, namespace) {
|
|
7112
|
+
try {
|
|
7113
|
+
return await this.pipeline.ingest(documents, namespace);
|
|
7114
|
+
} catch (err) {
|
|
7115
|
+
const msg = String(err);
|
|
7116
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7117
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7118
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7119
|
+
}
|
|
7120
|
+
throw wrapError(err, defaultCode);
|
|
7121
|
+
}
|
|
7122
|
+
}
|
|
7123
|
+
async ask(question, history = [], namespace) {
|
|
7124
|
+
try {
|
|
7125
|
+
return await this.pipeline.ask(question, history, namespace);
|
|
7126
|
+
} catch (err) {
|
|
7127
|
+
const msg = String(err);
|
|
7128
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7129
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7130
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7131
|
+
}
|
|
7132
|
+
throw wrapError(err, defaultCode);
|
|
7133
|
+
}
|
|
7134
|
+
}
|
|
7135
|
+
askStream(_0) {
|
|
7136
|
+
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
7137
|
+
try {
|
|
7138
|
+
const stream = this.pipeline.askStream(question, history, namespace);
|
|
7139
|
+
try {
|
|
7140
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
7141
|
+
const chunk = temp.value;
|
|
7142
|
+
yield chunk;
|
|
7143
|
+
}
|
|
7144
|
+
} catch (temp) {
|
|
7145
|
+
error = [temp];
|
|
7146
|
+
} finally {
|
|
7147
|
+
try {
|
|
7148
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
7149
|
+
} finally {
|
|
7150
|
+
if (error)
|
|
7151
|
+
throw error[0];
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
} catch (err) {
|
|
7155
|
+
const msg = String(err);
|
|
7156
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7157
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7158
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7159
|
+
}
|
|
7160
|
+
throw wrapError(err, defaultCode);
|
|
7161
|
+
}
|
|
7162
|
+
});
|
|
7163
|
+
}
|
|
7164
|
+
getPipeline() {
|
|
7165
|
+
return this.pipeline;
|
|
7166
|
+
}
|
|
7167
|
+
};
|
|
7168
|
+
|
|
6719
7169
|
// src/core/ProviderHealthCheck.ts
|
|
6720
7170
|
var ProviderHealthCheck = class {
|
|
6721
7171
|
/**
|
|
@@ -6824,6 +7274,8 @@ var VectorPlugin = class {
|
|
|
6824
7274
|
constructor(hostConfig) {
|
|
6825
7275
|
this.config = ConfigResolver.resolve(hostConfig);
|
|
6826
7276
|
this.validationPromise = ConfigValidator.validateAndThrow(this.config);
|
|
7277
|
+
this.validationPromise.catch(() => {
|
|
7278
|
+
});
|
|
6827
7279
|
this.pipeline = new Pipeline(this.config);
|
|
6828
7280
|
}
|
|
6829
7281
|
/**
|
|
@@ -6857,31 +7309,93 @@ var VectorPlugin = class {
|
|
|
6857
7309
|
* Run a chat query.
|
|
6858
7310
|
*/
|
|
6859
7311
|
async chat(message, history = [], namespace) {
|
|
6860
|
-
|
|
6861
|
-
|
|
7312
|
+
try {
|
|
7313
|
+
await this.validationPromise;
|
|
7314
|
+
} catch (err) {
|
|
7315
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7316
|
+
}
|
|
7317
|
+
try {
|
|
7318
|
+
return await this.pipeline.ask(message, history, namespace);
|
|
7319
|
+
} catch (err) {
|
|
7320
|
+
const msg = String(err);
|
|
7321
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7322
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7323
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7324
|
+
}
|
|
7325
|
+
throw wrapError(err, defaultCode);
|
|
7326
|
+
}
|
|
6862
7327
|
}
|
|
6863
7328
|
/**
|
|
6864
7329
|
* Run a streaming chat query.
|
|
6865
7330
|
*/
|
|
6866
7331
|
chatStream(_0) {
|
|
6867
7332
|
return __asyncGenerator(this, arguments, function* (message, history = [], namespace) {
|
|
6868
|
-
|
|
6869
|
-
|
|
7333
|
+
try {
|
|
7334
|
+
yield new __await(this.validationPromise);
|
|
7335
|
+
} catch (err) {
|
|
7336
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7337
|
+
}
|
|
7338
|
+
try {
|
|
7339
|
+
const stream = this.pipeline.askStream(message, history, namespace);
|
|
7340
|
+
try {
|
|
7341
|
+
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
7342
|
+
const chunk = temp.value;
|
|
7343
|
+
yield chunk;
|
|
7344
|
+
}
|
|
7345
|
+
} catch (temp) {
|
|
7346
|
+
error = [temp];
|
|
7347
|
+
} finally {
|
|
7348
|
+
try {
|
|
7349
|
+
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
|
|
7350
|
+
} finally {
|
|
7351
|
+
if (error)
|
|
7352
|
+
throw error[0];
|
|
7353
|
+
}
|
|
7354
|
+
}
|
|
7355
|
+
} catch (err) {
|
|
7356
|
+
const msg = String(err);
|
|
7357
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7358
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7359
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7360
|
+
}
|
|
7361
|
+
throw wrapError(err, defaultCode);
|
|
7362
|
+
}
|
|
6870
7363
|
});
|
|
6871
7364
|
}
|
|
6872
7365
|
/**
|
|
6873
7366
|
* Ingest documents into the vector database.
|
|
6874
7367
|
*/
|
|
6875
7368
|
async ingest(documents, namespace) {
|
|
6876
|
-
|
|
6877
|
-
|
|
7369
|
+
try {
|
|
7370
|
+
await this.validationPromise;
|
|
7371
|
+
} catch (err) {
|
|
7372
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7373
|
+
}
|
|
7374
|
+
try {
|
|
7375
|
+
return await this.pipeline.ingest(documents, namespace);
|
|
7376
|
+
} catch (err) {
|
|
7377
|
+
const msg = String(err);
|
|
7378
|
+
let defaultCode = "RETRIEVAL_FAILED";
|
|
7379
|
+
if (msg.includes("Embed") || msg.includes("embed")) {
|
|
7380
|
+
defaultCode = "EMBEDDING_FAILED";
|
|
7381
|
+
}
|
|
7382
|
+
throw wrapError(err, defaultCode);
|
|
7383
|
+
}
|
|
6878
7384
|
}
|
|
6879
7385
|
/**
|
|
6880
7386
|
* Get auto-suggestions based on a query prefix.
|
|
6881
7387
|
*/
|
|
6882
7388
|
async getSuggestions(query, namespace) {
|
|
6883
|
-
|
|
6884
|
-
|
|
7389
|
+
try {
|
|
7390
|
+
await this.validationPromise;
|
|
7391
|
+
} catch (err) {
|
|
7392
|
+
throw wrapError(err, "CONFIGURATION_ERROR");
|
|
7393
|
+
}
|
|
7394
|
+
try {
|
|
7395
|
+
return await this.pipeline.getSuggestions(query, namespace);
|
|
7396
|
+
} catch (err) {
|
|
7397
|
+
throw wrapError(err, "RETRIEVAL_FAILED");
|
|
7398
|
+
}
|
|
6885
7399
|
}
|
|
6886
7400
|
};
|
|
6887
7401
|
|
|
@@ -7253,6 +7767,10 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7253
7767
|
if (!isActive) break;
|
|
7254
7768
|
if (typeof chunk === "string") {
|
|
7255
7769
|
enqueue(sseTextFrame(chunk));
|
|
7770
|
+
} else if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "thinking") {
|
|
7771
|
+
enqueue(`data: ${JSON.stringify(chunk)}
|
|
7772
|
+
|
|
7773
|
+
`);
|
|
7256
7774
|
} else {
|
|
7257
7775
|
enqueue(sseMetaFrame(chunk));
|
|
7258
7776
|
const responseChunk = chunk;
|
|
@@ -7424,16 +7942,92 @@ function createUploadHandler(configOrPlugin) {
|
|
|
7424
7942
|
}
|
|
7425
7943
|
};
|
|
7426
7944
|
}
|
|
7945
|
+
function createSuggestionsHandler(configOrPlugin) {
|
|
7946
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
7947
|
+
return async function POST(req) {
|
|
7948
|
+
try {
|
|
7949
|
+
const body = await req.json();
|
|
7950
|
+
const { query, namespace } = body;
|
|
7951
|
+
if (typeof query !== "string") {
|
|
7952
|
+
return NextResponse.json({ error: "query is required" }, { status: 400 });
|
|
7953
|
+
}
|
|
7954
|
+
const suggestions = await plugin.getSuggestions(query, namespace);
|
|
7955
|
+
return NextResponse.json({ suggestions });
|
|
7956
|
+
} catch (err) {
|
|
7957
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
7958
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
7959
|
+
}
|
|
7960
|
+
};
|
|
7961
|
+
}
|
|
7962
|
+
function createRagHandler(configOrPlugin) {
|
|
7963
|
+
const plugin = configOrPlugin instanceof VectorPlugin ? configOrPlugin : new VectorPlugin(configOrPlugin);
|
|
7964
|
+
const chatHandler = createChatHandler(plugin);
|
|
7965
|
+
const streamHandler = createStreamHandler(plugin);
|
|
7966
|
+
const uploadHandler = createUploadHandler(plugin);
|
|
7967
|
+
const healthHandler = createHealthHandler(plugin);
|
|
7968
|
+
const suggestionsHandler = createSuggestionsHandler(plugin);
|
|
7969
|
+
async function routePostRequest(req, segment) {
|
|
7970
|
+
switch (segment) {
|
|
7971
|
+
case "chat":
|
|
7972
|
+
return streamHandler(req);
|
|
7973
|
+
case "chat-sync":
|
|
7974
|
+
return chatHandler(req);
|
|
7975
|
+
case "upload":
|
|
7976
|
+
return uploadHandler(req);
|
|
7977
|
+
case "suggestions":
|
|
7978
|
+
return suggestionsHandler(req);
|
|
7979
|
+
case "health":
|
|
7980
|
+
return healthHandler();
|
|
7981
|
+
default:
|
|
7982
|
+
return NextResponse.json({ error: `Not Found: POST segment "${segment}" not supported.` }, { status: 404 });
|
|
7983
|
+
}
|
|
7984
|
+
}
|
|
7985
|
+
async function routeGetRequest(req, segment) {
|
|
7986
|
+
if (segment === "health") {
|
|
7987
|
+
return healthHandler();
|
|
7988
|
+
}
|
|
7989
|
+
return NextResponse.json({ error: `Method Not Allowed: GET is only supported for "health" segment.` }, { status: 405 });
|
|
7990
|
+
}
|
|
7991
|
+
async function getSegment(context) {
|
|
7992
|
+
var _a;
|
|
7993
|
+
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;
|
|
7994
|
+
const segments = (resolvedParams == null ? void 0 : resolvedParams.retrivora) || [];
|
|
7995
|
+
return segments[0] || "chat";
|
|
7996
|
+
}
|
|
7997
|
+
return {
|
|
7998
|
+
GET: async (req, context) => {
|
|
7999
|
+
try {
|
|
8000
|
+
const segment = await getSegment(context);
|
|
8001
|
+
return await routeGetRequest(req, segment);
|
|
8002
|
+
} catch (err) {
|
|
8003
|
+
const msg = err instanceof Error ? err.message : "GET Routing failed";
|
|
8004
|
+
return NextResponse.json({ error: msg }, { status: 500 });
|
|
8005
|
+
}
|
|
8006
|
+
},
|
|
8007
|
+
POST: async (req, context) => {
|
|
8008
|
+
try {
|
|
8009
|
+
const segment = await getSegment(context);
|
|
8010
|
+
return await routePostRequest(req, segment);
|
|
8011
|
+
} catch (err) {
|
|
8012
|
+
const msg = err instanceof Error ? err.message : "POST Routing failed";
|
|
8013
|
+
return NextResponse.json({ error: msg }, { status: 500 });
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
};
|
|
8017
|
+
}
|
|
7427
8018
|
export {
|
|
7428
8019
|
AnthropicProvider,
|
|
8020
|
+
AuthenticationException,
|
|
7429
8021
|
BaseVectorProvider,
|
|
7430
8022
|
BatchProcessor,
|
|
7431
8023
|
ChromaDBProvider,
|
|
7432
8024
|
ConfigBuilder,
|
|
7433
8025
|
ConfigResolver,
|
|
7434
8026
|
ConfigValidator,
|
|
8027
|
+
ConfigurationException,
|
|
7435
8028
|
DocumentChunker,
|
|
7436
8029
|
DocumentParser,
|
|
8030
|
+
EmbeddingFailedException,
|
|
7437
8031
|
EmbeddingStrategy,
|
|
7438
8032
|
EmbeddingStrategyResolver,
|
|
7439
8033
|
LLMFactory,
|
|
@@ -7448,9 +8042,14 @@ export {
|
|
|
7448
8042
|
Pipeline,
|
|
7449
8043
|
PostgreSQLProvider,
|
|
7450
8044
|
ProviderHealthCheck,
|
|
8045
|
+
ProviderNotFoundException,
|
|
7451
8046
|
ProviderRegistry,
|
|
7452
8047
|
QdrantProvider,
|
|
8048
|
+
RateLimitException,
|
|
7453
8049
|
RedisProvider,
|
|
8050
|
+
RetrievalException,
|
|
8051
|
+
Retrivora,
|
|
8052
|
+
RetrivoraError,
|
|
7454
8053
|
UniversalLLMAdapter,
|
|
7455
8054
|
UniversalVectorProvider,
|
|
7456
8055
|
VECTOR_PROFILES,
|
|
@@ -7460,11 +8059,13 @@ export {
|
|
|
7460
8059
|
createFromPreset,
|
|
7461
8060
|
createHealthHandler,
|
|
7462
8061
|
createIngestHandler,
|
|
8062
|
+
createRagHandler,
|
|
7463
8063
|
createStreamHandler,
|
|
7464
8064
|
createUploadHandler,
|
|
7465
8065
|
getRagConfig,
|
|
7466
8066
|
sseErrorFrame,
|
|
7467
8067
|
sseFrame,
|
|
7468
8068
|
sseMetaFrame,
|
|
7469
|
-
sseTextFrame
|
|
8069
|
+
sseTextFrame,
|
|
8070
|
+
wrapError
|
|
7470
8071
|
};
|