@retrivora-ai/rag-engine 1.9.2 → 1.9.6
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 +27 -0
- package/dist/{ILLMProvider-Bw2A28nU.d.mts → ILLMProvider-DNhyOYoK.d.mts} +41 -1
- package/dist/{ILLMProvider-Bw2A28nU.d.ts → ILLMProvider-DNhyOYoK.d.ts} +41 -1
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +563 -205
- package/dist/handlers/index.mjs +563 -205
- package/dist/index-C9v7-tWd.d.mts +183 -0
- package/dist/{index-B70ZLkfG.d.mts → index-CHL1jdYm.d.mts} +1 -1
- package/dist/index-CjQdL0cX.d.ts +183 -0
- package/dist/{index-DVu-mkAM.d.ts → index-Hgbwl9X4.d.ts} +1 -1
- package/dist/index.css +40 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +326 -158
- package/dist/index.mjs +312 -151
- package/dist/server.d.mts +15 -94
- package/dist/server.d.ts +15 -94
- package/dist/server.js +623 -205
- package/dist/server.mjs +615 -205
- package/package.json +1 -1
- 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 +85 -30
- package/src/app/layout.tsx +18 -7
- package/src/components/MessageBubble.tsx +39 -2
- package/src/components/ThinkingBlock.tsx +75 -0
- package/src/components/VisualizationRenderer.tsx +27 -1
- package/src/config/RagConfig.ts +47 -0
- package/src/config/serverConfig.ts +25 -0
- package/src/core/ConfigResolver.ts +56 -4
- package/src/core/ConfigValidator.ts +2 -1
- package/src/core/Pipeline.ts +226 -68
- package/src/core/ProviderRegistry.ts +11 -2
- package/src/core/QueryProcessor.ts +38 -4
- package/src/core/Retrivora.ts +51 -0
- package/src/exceptions/index.ts +59 -0
- package/src/handlers/index.ts +2 -0
- package/src/hooks/useRagChat.ts +26 -4
- package/src/index.ts +25 -1
- package/src/lib/plugin.ts +24 -0
- package/src/llm/LLMFactory.ts +4 -1
- 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 +19 -7
- 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 +27 -1
- package/src/types/chat.ts +7 -0
- 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
|
@@ -640,6 +640,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
640
640
|
CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
|
|
641
641
|
ON ${this.uploadTable}
|
|
642
642
|
USING hnsw (embedding vector_cosine_ops)
|
|
643
|
+
`);
|
|
644
|
+
await client.query(`
|
|
645
|
+
CREATE INDEX IF NOT EXISTS "${this.uploadTable}_fts_idx"
|
|
646
|
+
ON "${this.uploadTable}"
|
|
647
|
+
USING gin (to_tsvector('english', content))
|
|
643
648
|
`);
|
|
644
649
|
const res = await client.query(`
|
|
645
650
|
SELECT table_name
|
|
@@ -710,6 +715,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
710
715
|
CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
|
|
711
716
|
ON "${tableName}"
|
|
712
717
|
USING hnsw (embedding vector_cosine_ops)
|
|
718
|
+
`);
|
|
719
|
+
await client.query(`
|
|
720
|
+
CREATE INDEX IF NOT EXISTS "${tableName}_fts_idx"
|
|
721
|
+
ON "${tableName}"
|
|
722
|
+
USING gin (to_tsvector('english', content))
|
|
713
723
|
`);
|
|
714
724
|
if (!this.tables.includes(tableName)) {
|
|
715
725
|
this.tables.push(tableName);
|
|
@@ -801,8 +811,11 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
801
811
|
const paramIdx = baseOffset + filterParams.length;
|
|
802
812
|
const synonyms = (_a3 = FIELD_SYNONYMS[key]) != null ? _a3 : [];
|
|
803
813
|
const keysToCheck = [key, ...synonyms];
|
|
804
|
-
const coalesceExprs = keysToCheck.
|
|
805
|
-
|
|
814
|
+
const coalesceExprs = keysToCheck.flatMap((k) => [
|
|
815
|
+
`metadata->>'${k}'`,
|
|
816
|
+
`to_jsonb(t)->>'${k}'`
|
|
817
|
+
]);
|
|
818
|
+
return `COALESCE(${coalesceExprs.map((expr) => `LOWER(${expr})`).join(", ")}) = LOWER($${paramIdx})`;
|
|
806
819
|
});
|
|
807
820
|
whereClause = `WHERE ${conditions.join(" AND ")}`;
|
|
808
821
|
}
|
|
@@ -813,16 +826,35 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
813
826
|
CASE WHEN LOWER(val) IN (${entityHints.map((h) => `'${h.replace(/'/g, "''")}'`).join(", ")})
|
|
814
827
|
THEN 1.0 ELSE 0.0 END
|
|
815
828
|
), 0)
|
|
816
|
-
FROM jsonb_each_text(
|
|
829
|
+
FROM jsonb_each_text(COALESCE(v.metadata, k.metadata)) AS kv(key, val)
|
|
817
830
|
WHERE key IN (${this.searchFields.map((f) => `'${f}'`).join(", ")})
|
|
818
831
|
) * 3.0` : "";
|
|
819
832
|
sqlQuery = `
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
833
|
+
WITH vector_search AS (
|
|
834
|
+
SELECT *, (1 - (embedding <=> $1::vector)) AS vector_score
|
|
835
|
+
FROM "${table}" t
|
|
836
|
+
${whereClause}
|
|
837
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
838
|
+
LIMIT ${tableLimit}
|
|
839
|
+
),
|
|
840
|
+
keyword_search AS (
|
|
841
|
+
SELECT *, COALESCE(ts_rank(to_tsvector('english', content), to_tsquery('english', $2)), 0) AS keyword_score
|
|
842
|
+
FROM "${table}" t
|
|
843
|
+
WHERE ${whereClause ? whereClause.replace("WHERE", "") : "TRUE"}
|
|
844
|
+
AND to_tsvector('english', content) @@ to_tsquery('english', $2)
|
|
845
|
+
ORDER BY keyword_score DESC
|
|
846
|
+
LIMIT ${tableLimit}
|
|
847
|
+
)
|
|
848
|
+
SELECT
|
|
849
|
+
COALESCE(v.id, k.id) AS id,
|
|
850
|
+
COALESCE(v.namespace, k.namespace) AS namespace,
|
|
851
|
+
COALESCE(v.content, k.content) AS content,
|
|
852
|
+
COALESCE(v.metadata, k.metadata) AS metadata,
|
|
853
|
+
COALESCE(v.vector_score, 0) AS vector_score,
|
|
854
|
+
COALESCE(k.keyword_score, 0) AS keyword_score,
|
|
855
|
+
(COALESCE(v.vector_score, 0) + COALESCE(k.keyword_score, 0) * 2.0 ${exactNameScoreExpr}) AS hybrid_score
|
|
856
|
+
FROM vector_search v
|
|
857
|
+
FULL OUTER JOIN keyword_search k ON v.id = k.id
|
|
826
858
|
ORDER BY hybrid_score DESC
|
|
827
859
|
LIMIT ${tableLimit}
|
|
828
860
|
`;
|
|
@@ -833,7 +865,7 @@ var init_MultiTablePostgresProvider = __esm({
|
|
|
833
865
|
(1 - (embedding <=> $1::vector)) AS hybrid_score
|
|
834
866
|
FROM "${table}" t
|
|
835
867
|
${whereClause}
|
|
836
|
-
ORDER BY
|
|
868
|
+
ORDER BY embedding <=> $1::vector ASC
|
|
837
869
|
LIMIT ${tableLimit}
|
|
838
870
|
`;
|
|
839
871
|
params = [vectorLiteral, ...filterParams];
|
|
@@ -1039,7 +1071,11 @@ var init_MongoDBProvider = __esm({
|
|
|
1039
1071
|
if (key === "namespace") {
|
|
1040
1072
|
vectorSearchFilter.namespace = value;
|
|
1041
1073
|
} else {
|
|
1042
|
-
|
|
1074
|
+
if (typeof value === "string") {
|
|
1075
|
+
matchFilter[key] = { $regex: new RegExp(`^${value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i") };
|
|
1076
|
+
} else {
|
|
1077
|
+
matchFilter[key] = value;
|
|
1078
|
+
}
|
|
1043
1079
|
}
|
|
1044
1080
|
}
|
|
1045
1081
|
const pipeline = [
|
|
@@ -1069,12 +1105,15 @@ var init_MongoDBProvider = __esm({
|
|
|
1069
1105
|
}
|
|
1070
1106
|
);
|
|
1071
1107
|
const results = await this.collection.aggregate(pipeline).toArray();
|
|
1072
|
-
return results.map((res) =>
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1108
|
+
return results.map((res) => {
|
|
1109
|
+
const normalizedScore = res.score * 2 - 1;
|
|
1110
|
+
return {
|
|
1111
|
+
id: res._id,
|
|
1112
|
+
content: res[this.contentKey],
|
|
1113
|
+
metadata: res[this.metadataKey] || {},
|
|
1114
|
+
score: normalizedScore
|
|
1115
|
+
};
|
|
1116
|
+
});
|
|
1078
1117
|
}
|
|
1079
1118
|
async delete(id, namespace) {
|
|
1080
1119
|
await this.collection.deleteOne(__spreadValues({ _id: id }, namespace ? { namespace } : {}));
|
|
@@ -2109,7 +2148,7 @@ function getRagConfig(baseConfig, env = process.env) {
|
|
|
2109
2148
|
return getEnvConfig(env, baseConfig);
|
|
2110
2149
|
}
|
|
2111
2150
|
function getEnvConfig(env = process.env, base) {
|
|
2112
|
-
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;
|
|
2151
|
+
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;
|
|
2113
2152
|
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__";
|
|
2114
2153
|
const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
|
|
2115
2154
|
const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
|
|
@@ -2179,7 +2218,7 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2179
2218
|
rest: "default",
|
|
2180
2219
|
custom: "default"
|
|
2181
2220
|
};
|
|
2182
|
-
return {
|
|
2221
|
+
return __spreadValues({
|
|
2183
2222
|
projectId,
|
|
2184
2223
|
vectorDb: {
|
|
2185
2224
|
provider: vectorProvider,
|
|
@@ -2195,7 +2234,9 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2195
2234
|
maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
|
|
2196
2235
|
temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
|
|
2197
2236
|
options: {
|
|
2198
|
-
profile: readString(env, "LLM_UNIVERSAL_PROFILE")
|
|
2237
|
+
profile: readString(env, "LLM_UNIVERSAL_PROFILE"),
|
|
2238
|
+
thinking: readString(env, "LLM_THINKING") === "true" || readString(env, "LLM_THINKING") === "enabled" ? true : readString(env, "LLM_THINKING") === "false" ? false : void 0,
|
|
2239
|
+
thinkingBudget: env.LLM_THINKING_BUDGET ? parseInt(env.LLM_THINKING_BUDGET, 10) : void 0
|
|
2199
2240
|
}
|
|
2200
2241
|
},
|
|
2201
2242
|
embedding: {
|
|
@@ -2227,11 +2268,76 @@ function getEnvConfig(env = process.env, base) {
|
|
|
2227
2268
|
topK: readNumber(env, "RAG_TOP_K", 5),
|
|
2228
2269
|
scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
|
|
2229
2270
|
chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
|
|
2230
|
-
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
|
|
2271
|
+
chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
|
|
2272
|
+
filterableFields: (_Ha = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Ha.split(",").map((f) => f.trim()),
|
|
2273
|
+
// Query pipeline toggles — read from .env.local
|
|
2274
|
+
useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
|
|
2275
|
+
useReranking: readString(env, "RAG_USE_RERANKING") === "true",
|
|
2276
|
+
useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
|
|
2277
|
+
architecture: (_Ia = readString(env, "RAG_ARCHITECTURE")) != null ? _Ia : "simple",
|
|
2278
|
+
chunkingStrategy: (_Ja = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _Ja : "recursive",
|
|
2279
|
+
uiMapping: (() => {
|
|
2280
|
+
const raw = readString(env, "RAG_UI_MAPPING");
|
|
2281
|
+
if (!raw) return void 0;
|
|
2282
|
+
try {
|
|
2283
|
+
return JSON.parse(raw);
|
|
2284
|
+
} catch (e) {
|
|
2285
|
+
return void 0;
|
|
2286
|
+
}
|
|
2287
|
+
})()
|
|
2231
2288
|
}
|
|
2232
|
-
}
|
|
2289
|
+
}, readString(env, "GRAPH_DB_PROVIDER") ? {
|
|
2290
|
+
graphDb: {
|
|
2291
|
+
provider: readString(env, "GRAPH_DB_PROVIDER"),
|
|
2292
|
+
options: {
|
|
2293
|
+
uri: readString(env, "GRAPH_DB_URI"),
|
|
2294
|
+
username: readString(env, "GRAPH_DB_USERNAME"),
|
|
2295
|
+
password: readString(env, "GRAPH_DB_PASSWORD")
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
} : {});
|
|
2233
2299
|
}
|
|
2234
2300
|
|
|
2301
|
+
// src/exceptions/index.ts
|
|
2302
|
+
var RetrivoraError = class extends Error {
|
|
2303
|
+
constructor(message, code, details) {
|
|
2304
|
+
super(message);
|
|
2305
|
+
this.name = new.target.name;
|
|
2306
|
+
this.code = code;
|
|
2307
|
+
this.details = details;
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
var ProviderNotFoundException = class extends RetrivoraError {
|
|
2311
|
+
constructor(providerType, provider, details) {
|
|
2312
|
+
super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
|
|
2313
|
+
}
|
|
2314
|
+
};
|
|
2315
|
+
var EmbeddingFailedException = class extends RetrivoraError {
|
|
2316
|
+
constructor(message = "Embedding generation failed", details) {
|
|
2317
|
+
super(message, "EMBEDDING_FAILED", details);
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
var RetrievalException = class extends RetrivoraError {
|
|
2321
|
+
constructor(message = "Retrieval failed", details) {
|
|
2322
|
+
super(message, "RETRIEVAL_FAILED", details);
|
|
2323
|
+
}
|
|
2324
|
+
};
|
|
2325
|
+
var RateLimitException = class extends RetrivoraError {
|
|
2326
|
+
constructor(message = "Provider rate limit exceeded", details) {
|
|
2327
|
+
super(message, "RATE_LIMITED", details);
|
|
2328
|
+
}
|
|
2329
|
+
};
|
|
2330
|
+
var ConfigurationException = class extends RetrivoraError {
|
|
2331
|
+
constructor(message, details) {
|
|
2332
|
+
super(message, "CONFIGURATION_ERROR", details);
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
var AuthenticationException = class extends RetrivoraError {
|
|
2336
|
+
constructor(message = "Provider authentication failed", details) {
|
|
2337
|
+
super(message, "AUTHENTICATION_ERROR", details);
|
|
2338
|
+
}
|
|
2339
|
+
};
|
|
2340
|
+
|
|
2235
2341
|
// src/core/ConfigResolver.ts
|
|
2236
2342
|
var ConfigResolver = class {
|
|
2237
2343
|
/**
|
|
@@ -2258,24 +2364,81 @@ var ConfigResolver = class {
|
|
|
2258
2364
|
rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
|
|
2259
2365
|
});
|
|
2260
2366
|
}
|
|
2367
|
+
/**
|
|
2368
|
+
* Resolves the public SDK config shape used by `new Retrivora({...})`.
|
|
2369
|
+
* Supports aliases from the product prompt while preserving existing env
|
|
2370
|
+
* fallback behavior.
|
|
2371
|
+
*/
|
|
2372
|
+
static resolveUniversal(hostConfig, env = process.env) {
|
|
2373
|
+
var _a;
|
|
2374
|
+
if (!hostConfig) return this.resolve(void 0, env);
|
|
2375
|
+
const normalized = __spreadProps(__spreadValues({}, hostConfig), {
|
|
2376
|
+
vectorDb: (_a = hostConfig.vectorDb) != null ? _a : hostConfig.vectorDatabase,
|
|
2377
|
+
rag: this.mergeRetrievalWorkflow(hostConfig.rag, hostConfig.retrieval, hostConfig.workflow)
|
|
2378
|
+
});
|
|
2379
|
+
return this.resolve(normalized, env);
|
|
2380
|
+
}
|
|
2261
2381
|
/**
|
|
2262
2382
|
* Validates the configuration for required fields.
|
|
2263
2383
|
*/
|
|
2264
2384
|
static validate(config) {
|
|
2265
2385
|
if (!config.projectId) {
|
|
2266
|
-
throw new
|
|
2386
|
+
throw new ConfigurationException("[ConfigResolver] projectId is required");
|
|
2267
2387
|
}
|
|
2268
2388
|
if (!config.vectorDb.provider) {
|
|
2269
|
-
throw new
|
|
2389
|
+
throw new ConfigurationException("[ConfigResolver] vectorDb.provider is required");
|
|
2270
2390
|
}
|
|
2271
2391
|
if (!config.llm.provider) {
|
|
2272
|
-
throw new
|
|
2392
|
+
throw new ConfigurationException("[ConfigResolver] llm.provider is required");
|
|
2273
2393
|
}
|
|
2274
2394
|
}
|
|
2395
|
+
static mergeRetrievalWorkflow(rag, retrieval, workflow) {
|
|
2396
|
+
var _a, _b, _c, _d, _e;
|
|
2397
|
+
if (!rag && !retrieval && !workflow) return void 0;
|
|
2398
|
+
const normalized = __spreadValues({}, rag != null ? rag : {});
|
|
2399
|
+
if (retrieval) {
|
|
2400
|
+
normalized.topK = (_a = retrieval.topK) != null ? _a : normalized.topK;
|
|
2401
|
+
normalized.scoreThreshold = (_b = retrieval.scoreThreshold) != null ? _b : normalized.scoreThreshold;
|
|
2402
|
+
normalized.useReranking = (_c = retrieval.useReranking) != null ? _c : normalized.useReranking;
|
|
2403
|
+
if (retrieval.strategy === "hybrid") normalized.architecture = (_d = normalized.architecture) != null ? _d : "hybrid";
|
|
2404
|
+
if (retrieval.strategy === "agentic") normalized.architecture = "agentic";
|
|
2405
|
+
if (retrieval.strategy === "contextual-compression") normalized.useReranking = true;
|
|
2406
|
+
}
|
|
2407
|
+
if (workflow == null ? void 0 : workflow.type) {
|
|
2408
|
+
const type = workflow.type;
|
|
2409
|
+
if (type === "agentic" || type === "agentic-rag") normalized.architecture = "agentic";
|
|
2410
|
+
else if (type === "hybrid-rag") normalized.architecture = "hybrid";
|
|
2411
|
+
else if (type === "graph-rag") {
|
|
2412
|
+
normalized.architecture = "graph";
|
|
2413
|
+
normalized.useGraphRetrieval = true;
|
|
2414
|
+
} else if (type === "simple-rag" || type === "rag") {
|
|
2415
|
+
normalized.architecture = (_e = normalized.architecture) != null ? _e : "simple";
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
return normalized;
|
|
2419
|
+
}
|
|
2275
2420
|
};
|
|
2276
2421
|
|
|
2277
2422
|
// src/llm/providers/OpenAIProvider.ts
|
|
2278
2423
|
import OpenAI from "openai";
|
|
2424
|
+
|
|
2425
|
+
// src/llm/utils.ts
|
|
2426
|
+
function buildSystemContent(systemPrompt, context) {
|
|
2427
|
+
const noContext = !context || context.trim() === "" || context.trim() === "No relevant context found.";
|
|
2428
|
+
if (systemPrompt.includes("{{context}}")) {
|
|
2429
|
+
return systemPrompt.replace("{{context}}", noContext ? "" : context);
|
|
2430
|
+
}
|
|
2431
|
+
if (noContext) {
|
|
2432
|
+
return systemPrompt;
|
|
2433
|
+
}
|
|
2434
|
+
return `${systemPrompt}
|
|
2435
|
+
|
|
2436
|
+
---
|
|
2437
|
+
Context:
|
|
2438
|
+
${context}`;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
// src/llm/providers/OpenAIProvider.ts
|
|
2279
2442
|
var OpenAIProvider = class {
|
|
2280
2443
|
constructor(llmConfig, embeddingConfig) {
|
|
2281
2444
|
if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
|
|
@@ -2339,16 +2502,10 @@ var OpenAIProvider = class {
|
|
|
2339
2502
|
}
|
|
2340
2503
|
async chat(messages, context, options) {
|
|
2341
2504
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2344
|
-
Context:
|
|
2345
|
-
${context}`;
|
|
2505
|
+
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.";
|
|
2346
2506
|
const systemMessage = {
|
|
2347
2507
|
role: "system",
|
|
2348
|
-
content:
|
|
2349
|
-
|
|
2350
|
-
Context:
|
|
2351
|
-
${context}`
|
|
2508
|
+
content: buildSystemContent(basePrompt, context)
|
|
2352
2509
|
};
|
|
2353
2510
|
const formattedMessages = [
|
|
2354
2511
|
systemMessage,
|
|
@@ -2369,16 +2526,10 @@ ${context}`
|
|
|
2369
2526
|
chatStream(messages, context, options) {
|
|
2370
2527
|
return __asyncGenerator(this, null, function* () {
|
|
2371
2528
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
Context:
|
|
2375
|
-
${context}`;
|
|
2529
|
+
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.";
|
|
2376
2530
|
const systemMessage = {
|
|
2377
2531
|
role: "system",
|
|
2378
|
-
content:
|
|
2379
|
-
|
|
2380
|
-
Context:
|
|
2381
|
-
${context}`
|
|
2532
|
+
content: buildSystemContent(basePrompt, context)
|
|
2382
2533
|
};
|
|
2383
2534
|
const formattedMessages = [
|
|
2384
2535
|
systemMessage,
|
|
@@ -2499,58 +2650,87 @@ var AnthropicProvider = class {
|
|
|
2499
2650
|
};
|
|
2500
2651
|
}
|
|
2501
2652
|
async chat(messages, context, options) {
|
|
2502
|
-
var _a, _b, _c, _d;
|
|
2503
|
-
const
|
|
2504
|
-
|
|
2505
|
-
Context:
|
|
2506
|
-
${context}`;
|
|
2507
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2508
|
-
|
|
2509
|
-
Context:
|
|
2510
|
-
${context}`;
|
|
2653
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2654
|
+
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.";
|
|
2655
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2511
2656
|
const anthropicMessages = messages.map((m) => ({
|
|
2512
2657
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2513
2658
|
content: m.content
|
|
2514
2659
|
}));
|
|
2515
|
-
const
|
|
2660
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2661
|
+
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;
|
|
2662
|
+
const extraParams = {};
|
|
2663
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2664
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2665
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2666
|
+
const budget = Math.min(
|
|
2667
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2668
|
+
maxTokens - 1
|
|
2669
|
+
);
|
|
2670
|
+
extraParams.thinking = {
|
|
2671
|
+
type: "enabled",
|
|
2672
|
+
budget_tokens: budget
|
|
2673
|
+
};
|
|
2674
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2675
|
+
}
|
|
2676
|
+
const response = await this.client.messages.create(__spreadValues({
|
|
2516
2677
|
model: this.llmConfig.model,
|
|
2517
|
-
max_tokens: (
|
|
2678
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2518
2679
|
system,
|
|
2519
2680
|
messages: anthropicMessages
|
|
2520
|
-
});
|
|
2521
|
-
|
|
2522
|
-
|
|
2681
|
+
}, extraParams));
|
|
2682
|
+
let reply = "";
|
|
2683
|
+
for (const block of response.content) {
|
|
2684
|
+
if (block.type === "text") {
|
|
2685
|
+
reply += block.text;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
return reply;
|
|
2523
2689
|
}
|
|
2524
2690
|
chatStream(messages, context, options) {
|
|
2525
2691
|
return __asyncGenerator(this, null, function* () {
|
|
2526
|
-
var _a, _b, _c, _d;
|
|
2527
|
-
const
|
|
2528
|
-
|
|
2529
|
-
Context:
|
|
2530
|
-
${context}`;
|
|
2531
|
-
const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
|
|
2532
|
-
|
|
2533
|
-
Context:
|
|
2534
|
-
${context}`;
|
|
2692
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2693
|
+
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.";
|
|
2694
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2535
2695
|
const anthropicMessages = messages.map((m) => ({
|
|
2536
2696
|
role: m.role === "assistant" ? "assistant" : "user",
|
|
2537
2697
|
content: m.content
|
|
2538
2698
|
}));
|
|
2539
|
-
const
|
|
2699
|
+
const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
|
|
2700
|
+
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;
|
|
2701
|
+
const extraParams = {};
|
|
2702
|
+
if (isThinkingEnabled && isClaude37) {
|
|
2703
|
+
extraParams.betas = ["interleaved-thinking-2025-05-14"];
|
|
2704
|
+
const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
|
|
2705
|
+
const budget = Math.min(
|
|
2706
|
+
typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
|
|
2707
|
+
maxTokens - 1
|
|
2708
|
+
);
|
|
2709
|
+
extraParams.thinking = {
|
|
2710
|
+
type: "enabled",
|
|
2711
|
+
budget_tokens: budget
|
|
2712
|
+
};
|
|
2713
|
+
extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
|
|
2714
|
+
}
|
|
2715
|
+
const stream = yield new __await(this.client.messages.create(__spreadValues({
|
|
2540
2716
|
model: this.llmConfig.model,
|
|
2541
|
-
max_tokens: (
|
|
2717
|
+
max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
|
|
2542
2718
|
system,
|
|
2543
2719
|
messages: anthropicMessages,
|
|
2544
2720
|
stream: true
|
|
2545
|
-
}));
|
|
2721
|
+
}, extraParams)));
|
|
2546
2722
|
if (!stream) {
|
|
2547
2723
|
throw new Error("[AnthropicProvider] messages.create stream is undefined");
|
|
2548
2724
|
}
|
|
2549
2725
|
try {
|
|
2550
2726
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
2551
2727
|
const chunk = temp.value;
|
|
2552
|
-
if (chunk.type === "content_block_delta"
|
|
2553
|
-
|
|
2728
|
+
if (chunk.type === "content_block_delta") {
|
|
2729
|
+
if (chunk.delta.type === "text_delta") {
|
|
2730
|
+
yield chunk.delta.text;
|
|
2731
|
+
} else if (chunk.delta.type === "thinking_delta") {
|
|
2732
|
+
yield `\0THINKING\0${chunk.delta.thinking}`;
|
|
2733
|
+
}
|
|
2554
2734
|
}
|
|
2555
2735
|
}
|
|
2556
2736
|
} catch (temp) {
|
|
@@ -2594,9 +2774,10 @@ ${context}`;
|
|
|
2594
2774
|
import axios from "axios";
|
|
2595
2775
|
var OllamaProvider = class {
|
|
2596
2776
|
constructor(llmConfig, embeddingConfig) {
|
|
2597
|
-
var _a;
|
|
2777
|
+
var _a, _b;
|
|
2598
2778
|
const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
|
|
2599
|
-
|
|
2779
|
+
const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
|
|
2780
|
+
this.http = axios.create({ baseURL, timeout });
|
|
2600
2781
|
this.llmConfig = llmConfig;
|
|
2601
2782
|
this.embeddingConfig = embeddingConfig;
|
|
2602
2783
|
}
|
|
@@ -2652,14 +2833,15 @@ var OllamaProvider = class {
|
|
|
2652
2833
|
};
|
|
2653
2834
|
}
|
|
2654
2835
|
async chat(messages, context, options) {
|
|
2655
|
-
var _a, _b, _c, _d;
|
|
2656
|
-
const
|
|
2836
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2837
|
+
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.";
|
|
2838
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2657
2839
|
const { data } = await this.http.post("/api/chat", {
|
|
2658
2840
|
model: this.llmConfig.model,
|
|
2659
2841
|
stream: false,
|
|
2660
2842
|
options: {
|
|
2661
|
-
temperature: (
|
|
2662
|
-
num_predict: (
|
|
2843
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2844
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2663
2845
|
},
|
|
2664
2846
|
messages: [
|
|
2665
2847
|
{ role: "system", content: system },
|
|
@@ -2670,14 +2852,15 @@ var OllamaProvider = class {
|
|
|
2670
2852
|
}
|
|
2671
2853
|
chatStream(messages, context, options) {
|
|
2672
2854
|
return __asyncGenerator(this, null, function* () {
|
|
2673
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2674
|
-
const
|
|
2855
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2856
|
+
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.";
|
|
2857
|
+
const system = buildSystemContent(basePrompt, context);
|
|
2675
2858
|
const response = yield new __await(this.http.post("/api/chat", {
|
|
2676
2859
|
model: this.llmConfig.model,
|
|
2677
2860
|
stream: true,
|
|
2678
2861
|
options: {
|
|
2679
|
-
temperature: (
|
|
2680
|
-
num_predict: (
|
|
2862
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
|
|
2863
|
+
num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
|
|
2681
2864
|
},
|
|
2682
2865
|
messages: [
|
|
2683
2866
|
{ role: "system", content: system },
|
|
@@ -2699,7 +2882,7 @@ var OllamaProvider = class {
|
|
|
2699
2882
|
if (!line.trim()) continue;
|
|
2700
2883
|
try {
|
|
2701
2884
|
const json = JSON.parse(line);
|
|
2702
|
-
if ((
|
|
2885
|
+
if ((_g = json.message) == null ? void 0 : _g.content) {
|
|
2703
2886
|
yield json.message.content;
|
|
2704
2887
|
}
|
|
2705
2888
|
if (json.done) return;
|
|
@@ -2721,26 +2904,12 @@ var OllamaProvider = class {
|
|
|
2721
2904
|
if (lineBuffer.trim()) {
|
|
2722
2905
|
try {
|
|
2723
2906
|
const json = JSON.parse(lineBuffer);
|
|
2724
|
-
if ((
|
|
2907
|
+
if ((_h = json.message) == null ? void 0 : _h.content) yield json.message.content;
|
|
2725
2908
|
} catch (e) {
|
|
2726
2909
|
}
|
|
2727
2910
|
}
|
|
2728
2911
|
});
|
|
2729
2912
|
}
|
|
2730
|
-
buildSystemPrompt(context, overridePrompt) {
|
|
2731
|
-
var _a;
|
|
2732
|
-
if (overridePrompt) {
|
|
2733
|
-
return overridePrompt;
|
|
2734
|
-
}
|
|
2735
|
-
const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
|
|
2736
|
-
|
|
2737
|
-
Context:
|
|
2738
|
-
${context}`;
|
|
2739
|
-
return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
|
|
2740
|
-
|
|
2741
|
-
Context:
|
|
2742
|
-
${context}`;
|
|
2743
|
-
}
|
|
2744
2913
|
async embed(text, options) {
|
|
2745
2914
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2746
2915
|
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";
|
|
@@ -2887,21 +3056,6 @@ var GeminiProvider = class {
|
|
|
2887
3056
|
var _a, _b;
|
|
2888
3057
|
return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
|
|
2889
3058
|
}
|
|
2890
|
-
/**
|
|
2891
|
-
* Build the system instruction string, inserting the RAG context either via
|
|
2892
|
-
* the `{{context}}` placeholder or by appending it.
|
|
2893
|
-
*/
|
|
2894
|
-
buildSystemInstruction(context) {
|
|
2895
|
-
var _a;
|
|
2896
|
-
const base = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
|
|
2897
|
-
|
|
2898
|
-
Context:
|
|
2899
|
-
${context}`;
|
|
2900
|
-
return base.includes("{{context}}") ? base.replace("{{context}}", context) : `${base}
|
|
2901
|
-
|
|
2902
|
-
Context:
|
|
2903
|
-
${context}`;
|
|
2904
|
-
}
|
|
2905
3059
|
/**
|
|
2906
3060
|
* Convert ChatMessage[] to the Gemini contents format.
|
|
2907
3061
|
*
|
|
@@ -2920,16 +3074,17 @@ ${context}`;
|
|
|
2920
3074
|
// ILLMProvider — chat
|
|
2921
3075
|
// -------------------------------------------------------------------------
|
|
2922
3076
|
async chat(messages, context, options) {
|
|
2923
|
-
var _a, _b, _c, _d;
|
|
3077
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3078
|
+
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.";
|
|
2924
3079
|
const model = this.client.getGenerativeModel({
|
|
2925
3080
|
model: this.llmConfig.model,
|
|
2926
|
-
systemInstruction:
|
|
3081
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2927
3082
|
});
|
|
2928
3083
|
const result = await model.generateContent({
|
|
2929
3084
|
contents: this.buildGeminiContents(messages),
|
|
2930
3085
|
generationConfig: {
|
|
2931
|
-
temperature: (
|
|
2932
|
-
maxOutputTokens: (
|
|
3086
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3087
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2933
3088
|
stopSequences: options == null ? void 0 : options.stop
|
|
2934
3089
|
}
|
|
2935
3090
|
});
|
|
@@ -2937,16 +3092,17 @@ ${context}`;
|
|
|
2937
3092
|
}
|
|
2938
3093
|
chatStream(messages, context, options) {
|
|
2939
3094
|
return __asyncGenerator(this, null, function* () {
|
|
2940
|
-
var _a, _b, _c, _d;
|
|
3095
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3096
|
+
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.";
|
|
2941
3097
|
const model = this.client.getGenerativeModel({
|
|
2942
3098
|
model: this.llmConfig.model,
|
|
2943
|
-
systemInstruction:
|
|
3099
|
+
systemInstruction: buildSystemContent(basePrompt, context)
|
|
2944
3100
|
});
|
|
2945
3101
|
const result = yield new __await(model.generateContentStream({
|
|
2946
3102
|
contents: this.buildGeminiContents(messages),
|
|
2947
3103
|
generationConfig: {
|
|
2948
|
-
temperature: (
|
|
2949
|
-
maxOutputTokens: (
|
|
3104
|
+
temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
|
|
3105
|
+
maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
|
|
2950
3106
|
stopSequences: options == null ? void 0 : options.stop
|
|
2951
3107
|
}
|
|
2952
3108
|
}));
|
|
@@ -3381,7 +3537,9 @@ var LLMFactory = class _LLMFactory {
|
|
|
3381
3537
|
if (llmConfig.baseUrl || ((_b = llmConfig.options) == null ? void 0 : _b.baseUrl)) {
|
|
3382
3538
|
return new UniversalLLMAdapter(llmConfig);
|
|
3383
3539
|
}
|
|
3384
|
-
throw new
|
|
3540
|
+
throw new ProviderNotFoundException(
|
|
3541
|
+
"LLM",
|
|
3542
|
+
String(llmConfig.provider),
|
|
3385
3543
|
`[LLMFactory] Unknown provider "${llmConfig.provider}". Built-in providers: ${_LLMFactory.listProviders().join(", ")}. Register a custom provider with LLMFactory.register().`
|
|
3386
3544
|
);
|
|
3387
3545
|
}
|
|
@@ -3513,7 +3671,7 @@ var ProviderRegistry = class {
|
|
|
3513
3671
|
return UniversalVectorProvider2;
|
|
3514
3672
|
}
|
|
3515
3673
|
default:
|
|
3516
|
-
throw new
|
|
3674
|
+
throw new ProviderNotFoundException("vector", provider);
|
|
3517
3675
|
}
|
|
3518
3676
|
}
|
|
3519
3677
|
static async createVectorProvider(config) {
|
|
@@ -3531,12 +3689,18 @@ var ProviderRegistry = class {
|
|
|
3531
3689
|
return new SimpleGraphProvider2(config);
|
|
3532
3690
|
}
|
|
3533
3691
|
default:
|
|
3534
|
-
throw new
|
|
3692
|
+
throw new ProviderNotFoundException("graph", provider);
|
|
3535
3693
|
}
|
|
3536
3694
|
}
|
|
3537
3695
|
static createLLMProvider(llmConfig, embeddingConfig) {
|
|
3538
3696
|
return LLMFactory.create(llmConfig, embeddingConfig);
|
|
3539
3697
|
}
|
|
3698
|
+
static registerLLMProvider(name, factory) {
|
|
3699
|
+
LLMFactory.register(name, factory);
|
|
3700
|
+
}
|
|
3701
|
+
static createEmbeddingProvider(embeddingConfig) {
|
|
3702
|
+
return LLMFactory.createEmbeddingProvider(embeddingConfig);
|
|
3703
|
+
}
|
|
3540
3704
|
};
|
|
3541
3705
|
ProviderRegistry.vectorProviders = {};
|
|
3542
3706
|
ProviderRegistry.graphProviders = {};
|
|
@@ -3683,8 +3847,8 @@ var ConfigValidator = class {
|
|
|
3683
3847
|
const errorItems = errors.filter((e) => e.severity === "error");
|
|
3684
3848
|
if (errorItems.length > 0) {
|
|
3685
3849
|
const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
|
|
3686
|
-
throw new
|
|
3687
|
-
${message}
|
|
3850
|
+
throw new ConfigurationException(`[ConfigValidator] Configuration validation failed:
|
|
3851
|
+
${message}`, errorItems);
|
|
3688
3852
|
}
|
|
3689
3853
|
}
|
|
3690
3854
|
};
|
|
@@ -3871,20 +4035,17 @@ var Reranker = class {
|
|
|
3871
4035
|
}
|
|
3872
4036
|
try {
|
|
3873
4037
|
const topN = matches.slice(0, 10);
|
|
3874
|
-
const
|
|
3875
|
-
|
|
3876
|
-
Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
|
|
3877
|
-
Use the indices provided in brackets like [0], [1], etc.
|
|
3878
|
-
Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
|
|
3879
|
-
|
|
3880
|
-
Query: "${query}"
|
|
4038
|
+
const response = await llm.chat(
|
|
4039
|
+
[{ role: "user", content: `Query: "${query}"
|
|
3881
4040
|
|
|
3882
4041
|
Documents:
|
|
3883
|
-
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}
|
|
3884
|
-
const response = await llm.chat(
|
|
3885
|
-
[{ role: "user", content: prompt }],
|
|
4042
|
+
${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}` }],
|
|
3886
4043
|
"",
|
|
3887
|
-
{
|
|
4044
|
+
{
|
|
4045
|
+
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.',
|
|
4046
|
+
temperature: 0,
|
|
4047
|
+
maxTokens: 50
|
|
4048
|
+
}
|
|
3888
4049
|
);
|
|
3889
4050
|
const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
|
|
3890
4051
|
const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
|
|
@@ -4396,6 +4557,18 @@ var QueryProcessor = class {
|
|
|
4396
4557
|
}, normalizedField ? { field: normalizedField } : {}));
|
|
4397
4558
|
}
|
|
4398
4559
|
};
|
|
4560
|
+
const activeValidFields = validFields.length > 0 ? validFields : ["brand", "category", "price", "rating", "stock", "stock_quantity", "status", "name", "title"];
|
|
4561
|
+
const resolveValidField = (fieldStr) => {
|
|
4562
|
+
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();
|
|
4563
|
+
const comparable = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
4564
|
+
const cleanedComparable = comparable(cleaned);
|
|
4565
|
+
if (!cleanedComparable) return null;
|
|
4566
|
+
const matchedField = activeValidFields.find((fieldName) => {
|
|
4567
|
+
const candidate = comparable(fieldName);
|
|
4568
|
+
return candidate === cleanedComparable || candidate.includes(cleanedComparable) || cleanedComparable.includes(candidate);
|
|
4569
|
+
});
|
|
4570
|
+
return matchedField != null ? matchedField : null;
|
|
4571
|
+
};
|
|
4399
4572
|
for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
|
|
4400
4573
|
addHint(match[1]);
|
|
4401
4574
|
}
|
|
@@ -4420,7 +4593,13 @@ var QueryProcessor = class {
|
|
|
4420
4593
|
const field = match[1];
|
|
4421
4594
|
const value = match[2];
|
|
4422
4595
|
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4423
|
-
|
|
4596
|
+
const resolvedField = resolveValidField(field);
|
|
4597
|
+
if (resolvedField) {
|
|
4598
|
+
addHint(value, resolvedField);
|
|
4599
|
+
} else {
|
|
4600
|
+
addHint(value);
|
|
4601
|
+
addHint(field);
|
|
4602
|
+
}
|
|
4424
4603
|
}
|
|
4425
4604
|
}
|
|
4426
4605
|
if (validFields.length > 0) {
|
|
@@ -4466,12 +4645,16 @@ var QueryProcessor = class {
|
|
|
4466
4645
|
];
|
|
4467
4646
|
for (const pattern of fieldValuePatterns) {
|
|
4468
4647
|
for (const match of question.matchAll(pattern)) {
|
|
4469
|
-
const field =
|
|
4648
|
+
const field = (_c = match[1]) != null ? _c : "";
|
|
4470
4649
|
const value = (_d = match[2]) != null ? _d : "";
|
|
4471
|
-
|
|
4472
|
-
|
|
4650
|
+
const resolvedField = resolveValidField(field);
|
|
4651
|
+
if (resolvedField) {
|
|
4652
|
+
addHint(value, resolvedField);
|
|
4473
4653
|
} else {
|
|
4474
4654
|
addHint(value);
|
|
4655
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
4656
|
+
addHint(field);
|
|
4657
|
+
}
|
|
4475
4658
|
}
|
|
4476
4659
|
}
|
|
4477
4660
|
}
|
|
@@ -4698,7 +4881,7 @@ var LLMRouter = class {
|
|
|
4698
4881
|
|
|
4699
4882
|
// src/utils/UITransformer.ts
|
|
4700
4883
|
init_synonyms();
|
|
4701
|
-
var UITransformer = class {
|
|
4884
|
+
var UITransformer = class _UITransformer {
|
|
4702
4885
|
// ─── Public Entry Points ─────────────────────────────────────────────────
|
|
4703
4886
|
/**
|
|
4704
4887
|
* Heuristic-only transform (no LLM required).
|
|
@@ -4760,7 +4943,7 @@ var UITransformer = class {
|
|
|
4760
4943
|
static async analyzeAndDecide(query, sources, llm) {
|
|
4761
4944
|
let intent;
|
|
4762
4945
|
try {
|
|
4763
|
-
intent = await this.detectIntent(query, llm);
|
|
4946
|
+
intent = await this.detectIntent(query, llm, sources);
|
|
4764
4947
|
console.debug("[UITransformer] Detected intent:", intent);
|
|
4765
4948
|
} catch (err) {
|
|
4766
4949
|
console.warn("[UITransformer] Intent detection failed; using heuristic.", err);
|
|
@@ -4778,11 +4961,23 @@ var UITransformer = class {
|
|
|
4778
4961
|
try {
|
|
4779
4962
|
const context = this.buildContextSummary(sources);
|
|
4780
4963
|
const systemPrompt = this.buildVisualizationSystemPrompt();
|
|
4964
|
+
const profile = this.profileData(sources);
|
|
4965
|
+
const schemaContext = `
|
|
4966
|
+
RETRIEVED DATA SCHEMA:
|
|
4967
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4968
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4969
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4970
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
4971
|
+
- Text/Other fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
4972
|
+
`;
|
|
4781
4973
|
const userPrompt = [
|
|
4782
4974
|
`USER QUESTION: ${query}`,
|
|
4783
4975
|
"",
|
|
4784
4976
|
`DETECTED INTENT (JSON): ${JSON.stringify(intent)}`,
|
|
4785
4977
|
"",
|
|
4978
|
+
`RETRIEVED DATA SCHEMA:`,
|
|
4979
|
+
schemaContext,
|
|
4980
|
+
"",
|
|
4786
4981
|
"RETRIEVED DATA (JSON):",
|
|
4787
4982
|
context
|
|
4788
4983
|
].join("\n");
|
|
@@ -4793,6 +4988,20 @@ var UITransformer = class {
|
|
|
4793
4988
|
);
|
|
4794
4989
|
const parsed = this.parseTransformationResponse(rawResponse);
|
|
4795
4990
|
if (parsed) {
|
|
4991
|
+
let isCompatible = true;
|
|
4992
|
+
if (parsed.type === "line_chart" && (profile.dateFields.length === 0 || profile.numericFields.length === 0)) {
|
|
4993
|
+
isCompatible = false;
|
|
4994
|
+
} else if (["bar_chart", "horizontal_bar", "pie_chart", "donut_chart"].includes(parsed.type) && (profile.categoricalFields.length === 0 && profile.numericFields.length === 0)) {
|
|
4995
|
+
isCompatible = false;
|
|
4996
|
+
} else if (parsed.type === "scatter_plot" && profile.numericFields.length < 2) {
|
|
4997
|
+
isCompatible = false;
|
|
4998
|
+
} else if (parsed.type === "metric_card" && profile.numericFields.length === 0) {
|
|
4999
|
+
isCompatible = false;
|
|
5000
|
+
}
|
|
5001
|
+
if (!isCompatible) {
|
|
5002
|
+
console.warn(`[UITransformer] LLM chose incompatible visualization type "${parsed.type}" for retrieved schema. Falling back to heuristic.`);
|
|
5003
|
+
return this.transform(query, sources, void 0, void 0, intent);
|
|
5004
|
+
}
|
|
4796
5005
|
const intentAllowsTable = intent.wantsExplicitTable || ["tabular", "table", "geographic"].includes(intent.visualizationHint);
|
|
4797
5006
|
const intentWantsPieLikeChart = ["pie_chart", "donut_chart"].includes(intent.recommendedChart) || ["composition", "category_breakdown"].includes(intent.visualizationHint);
|
|
4798
5007
|
if (parsed.type === "table" && !intentAllowsTable) {
|
|
@@ -4822,7 +5031,26 @@ var UITransformer = class {
|
|
|
4822
5031
|
* - The intent object can be reused across both the heuristic and LLM paths.
|
|
4823
5032
|
* - It is easy to unit-test intent detection in isolation.
|
|
4824
5033
|
*/
|
|
4825
|
-
static async detectIntent(query, llm) {
|
|
5034
|
+
static async detectIntent(query, llm, sources) {
|
|
5035
|
+
let schemaProfileText = "";
|
|
5036
|
+
if (sources && sources.length > 0) {
|
|
5037
|
+
const profile = this.profileData(sources);
|
|
5038
|
+
const hasProducts = sources.some((item) => this.isProductData(item));
|
|
5039
|
+
schemaProfileText = `
|
|
5040
|
+
RETRIEVED DATA SCHEMA:
|
|
5041
|
+
- Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5042
|
+
- Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5043
|
+
- Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5044
|
+
- Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
|
|
5045
|
+
- Text fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
|
|
5046
|
+
|
|
5047
|
+
Please choose visualizationHint and recommendedChart dynamically based on the available schema.
|
|
5048
|
+
- Do NOT recommend "trend" or "line_chart" if there are no Date fields.
|
|
5049
|
+
- 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.
|
|
5050
|
+
- If there are multiple metadata fields but no clear categories or numbers, "tabular"/"table" is a highly accurate default.
|
|
5051
|
+
${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".` : ""}
|
|
5052
|
+
`;
|
|
5053
|
+
}
|
|
4826
5054
|
const systemPrompt = `You are an intent classifier for a product-search RAG system.
|
|
4827
5055
|
Given a user query, return ONLY a valid JSON object with this exact shape \u2014 no prose, no markdown:
|
|
4828
5056
|
|
|
@@ -4858,8 +5086,11 @@ RULES:
|
|
|
4858
5086
|
- isTemporal: true if time words appear (trend, historical, over time, last year, monthly, etc.)
|
|
4859
5087
|
- isComparison: true if user compares, ranks, or contrasts entities or categories.
|
|
4860
5088
|
- language: detect from the query text itself; default "en" if uncertain.`;
|
|
5089
|
+
const userPrompt = `QUERY: ${query}${schemaProfileText ? `
|
|
5090
|
+
|
|
5091
|
+
${schemaProfileText}` : ""}`;
|
|
4861
5092
|
const rawResponse = await llm.chat(
|
|
4862
|
-
[{ role: "user", content:
|
|
5093
|
+
[{ role: "user", content: userPrompt }],
|
|
4863
5094
|
"",
|
|
4864
5095
|
{ systemPrompt, temperature: 0 }
|
|
4865
5096
|
);
|
|
@@ -5302,10 +5533,21 @@ RULES:
|
|
|
5302
5533
|
const hasEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5303
5534
|
return asksForRecords && (hasNumericPredicate || hasEntityTerms);
|
|
5304
5535
|
}
|
|
5536
|
+
/** @internal kept private for transform() internal logic */
|
|
5305
5537
|
static isProductQuery(query) {
|
|
5538
|
+
return _UITransformer._productQueryTest(query);
|
|
5539
|
+
}
|
|
5540
|
+
/**
|
|
5541
|
+
* Public entry-point for external callers (e.g. Pipeline)
|
|
5542
|
+
* to check whether a query maps to product_browse without triggering an LLM call.
|
|
5543
|
+
*/
|
|
5544
|
+
static isProductQueryPublic(query) {
|
|
5545
|
+
return _UITransformer._productQueryTest(query);
|
|
5546
|
+
}
|
|
5547
|
+
static _productQueryTest(query) {
|
|
5306
5548
|
const q = query.toLowerCase();
|
|
5307
5549
|
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);
|
|
5308
|
-
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list)\b/.test(q);
|
|
5550
|
+
const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list|explore|exploring)\b/.test(q);
|
|
5309
5551
|
const nonProductEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
|
|
5310
5552
|
return productTerms && productAction && !nonProductEntityTerms;
|
|
5311
5553
|
}
|
|
@@ -6078,23 +6320,19 @@ function estimateCostUsd(promptTokens, completionTokens, model) {
|
|
|
6078
6320
|
async function scoreHallucination(llm, answer, context) {
|
|
6079
6321
|
const maxContextChars = 3e3;
|
|
6080
6322
|
const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
CONTEXT:
|
|
6323
|
+
try {
|
|
6324
|
+
const raw = await llm.chat(
|
|
6325
|
+
[{ role: "user", content: `CONTEXT:
|
|
6084
6326
|
${truncatedContext}
|
|
6085
6327
|
|
|
6086
6328
|
ANSWER:
|
|
6087
|
-
${answer}
|
|
6088
|
-
|
|
6089
|
-
Return ONLY a valid JSON object with no markdown fences:
|
|
6090
|
-
{"score": <float 0-1>, "reason": "<one sentence>"}
|
|
6091
|
-
|
|
6092
|
-
Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
|
|
6093
|
-
try {
|
|
6094
|
-
const raw = await llm.chat(
|
|
6095
|
-
[{ role: "user", content: prompt }],
|
|
6329
|
+
${answer}` }],
|
|
6096
6330
|
"",
|
|
6097
|
-
{
|
|
6331
|
+
{
|
|
6332
|
+
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.',
|
|
6333
|
+
temperature: 0,
|
|
6334
|
+
maxTokens: 120
|
|
6335
|
+
}
|
|
6098
6336
|
);
|
|
6099
6337
|
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
6100
6338
|
if (!jsonMatch) return void 0;
|
|
@@ -6154,7 +6392,10 @@ var Pipeline = class {
|
|
|
6154
6392
|
- 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.
|
|
6155
6393
|
- Do NOT try to format product lists as tables.
|
|
6156
6394
|
`;
|
|
6157
|
-
this.config.llm.systemPrompt
|
|
6395
|
+
const userPromptPrefix = this.config.llm.systemPrompt ? `${this.config.llm.systemPrompt}
|
|
6396
|
+
|
|
6397
|
+
` : "";
|
|
6398
|
+
this.config.llm.systemPrompt = `${userPromptPrefix}${chartInstruction}`;
|
|
6158
6399
|
this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
|
|
6159
6400
|
this.llmRouter = new LLMRouter(this.config);
|
|
6160
6401
|
const { llmProvider: resolvedLLM, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
|
|
@@ -6332,11 +6573,11 @@ var Pipeline = class {
|
|
|
6332
6573
|
*/
|
|
6333
6574
|
askStream(_0) {
|
|
6334
6575
|
return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
|
|
6335
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
6576
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
6336
6577
|
yield new __await(this.initialize());
|
|
6337
6578
|
const ns = namespace != null ? namespace : this.config.projectId;
|
|
6338
6579
|
const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
|
|
6339
|
-
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
|
|
6580
|
+
const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
|
|
6340
6581
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
6341
6582
|
const requestStart = performance.now();
|
|
6342
6583
|
try {
|
|
@@ -6355,12 +6596,13 @@ var Pipeline = class {
|
|
|
6355
6596
|
const embedStart = performance.now();
|
|
6356
6597
|
const cacheKey = `${ns}::${searchQuery}`;
|
|
6357
6598
|
const cachedVector = this.embeddingCache.get(cacheKey);
|
|
6599
|
+
const useGraph = this.graphDB && ((_h = this.config.rag) == null ? void 0 : _h.useGraphRetrieval);
|
|
6358
6600
|
const [strategyResult, embeddedVector] = yield new __await(Promise.all([
|
|
6359
6601
|
QueryProcessor.determineRetrievalStrategy(
|
|
6360
6602
|
searchQuery,
|
|
6361
|
-
this.llmRouter.get("fast"),
|
|
6362
|
-
(
|
|
6363
|
-
(
|
|
6603
|
+
useGraph ? this.llmRouter.get("fast") : void 0,
|
|
6604
|
+
(_i = this.config.rag) == null ? void 0 : _i.graphKeywords,
|
|
6605
|
+
(_j = this.config.rag) == null ? void 0 : _j.vectorKeywords
|
|
6364
6606
|
),
|
|
6365
6607
|
// Embed immediately regardless of strategy — costs nothing if cached.
|
|
6366
6608
|
// If strategy turns out to be 'graph'-only we just won't use the vector.
|
|
@@ -6370,7 +6612,7 @@ var Pipeline = class {
|
|
|
6370
6612
|
if (!cachedVector && queryVector.length > 0) {
|
|
6371
6613
|
this.embeddingCache.set(cacheKey, queryVector);
|
|
6372
6614
|
}
|
|
6373
|
-
const graphData = (strategyResult === "graph" || strategyResult === "both") && this.graphDB && ((
|
|
6615
|
+
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;
|
|
6374
6616
|
const hasMetadataFilter = filter.metadata && Object.keys(filter.metadata).length > 0;
|
|
6375
6617
|
const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
|
|
6376
6618
|
const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
|
|
@@ -6383,7 +6625,7 @@ var Pipeline = class {
|
|
|
6383
6625
|
const structuredSources = this.applyStructuredFilters(rawSources, filter);
|
|
6384
6626
|
let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => m.score >= scoreThreshold);
|
|
6385
6627
|
const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
|
|
6386
|
-
if (!hasNumericPredicates && ((
|
|
6628
|
+
if (!hasNumericPredicates && ((_l = this.config.rag) == null ? void 0 : _l.useReranking)) {
|
|
6387
6629
|
fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
|
|
6388
6630
|
} else if (!wantsExhaustiveList) {
|
|
6389
6631
|
fullSources = fullSources.slice(0, topK);
|
|
@@ -6395,7 +6637,8 @@ ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
|
|
|
6395
6637
|
if (hasMetadataFilter) {
|
|
6396
6638
|
displayCount = fullSources.length;
|
|
6397
6639
|
} else {
|
|
6398
|
-
const
|
|
6640
|
+
const baseThreshold = Math.max(scoreThreshold, 0.4);
|
|
6641
|
+
const highlyRelevant = fullSources.filter((m) => m.score >= baseThreshold);
|
|
6399
6642
|
displayCount = Math.max(highlyRelevant.length, topK);
|
|
6400
6643
|
if (displayCount > 15) {
|
|
6401
6644
|
displayCount = 15;
|
|
@@ -6413,34 +6656,117 @@ VECTOR CONTEXT:
|
|
|
6413
6656
|
${context}`;
|
|
6414
6657
|
}
|
|
6415
6658
|
const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
|
|
6416
|
-
const
|
|
6417
|
-
|
|
6418
|
-
(
|
|
6659
|
+
const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
|
|
6660
|
+
if (allMetadataKeys.length > 0 && !cachedSchema) {
|
|
6661
|
+
SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
|
|
6662
|
+
});
|
|
6663
|
+
}
|
|
6664
|
+
const isProductQ = UITransformer.isProductQueryPublic(question);
|
|
6665
|
+
const uiTransformationPromise = isProductQ ? Promise.resolve(
|
|
6666
|
+
UITransformer.transform(
|
|
6419
6667
|
question,
|
|
6420
6668
|
sources,
|
|
6421
|
-
|
|
6422
|
-
|
|
6669
|
+
this.config,
|
|
6670
|
+
cachedSchema,
|
|
6671
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6423
6672
|
)
|
|
6673
|
+
) : this.generateUiTransformation(
|
|
6674
|
+
question,
|
|
6675
|
+
sources,
|
|
6676
|
+
cachedSchema,
|
|
6677
|
+
hasNumericPredicates
|
|
6424
6678
|
).catch((uiError) => {
|
|
6425
6679
|
console.warn("[Pipeline] UI transformation failed concurrently:", uiError);
|
|
6426
|
-
return UITransformer.transform(question, sources, this.config);
|
|
6680
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6427
6681
|
});
|
|
6682
|
+
let hallucinationScoringPromise = Promise.resolve(void 0);
|
|
6428
6683
|
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.)";
|
|
6684
|
+
const isClaude37 = this.config.llm.model.includes("claude-3-7-sonnet");
|
|
6685
|
+
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);
|
|
6686
|
+
const modelNameLower = this.config.llm.model.toLowerCase();
|
|
6687
|
+
const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
|
|
6688
|
+
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);
|
|
6689
|
+
let finalRestrictionSuffix = restrictionSuffix;
|
|
6690
|
+
if (isSimulatedThinking) {
|
|
6691
|
+
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.`)";
|
|
6692
|
+
}
|
|
6429
6693
|
const hardenedHistory = [...history];
|
|
6430
|
-
const userQuestion = { role: "user", content: question +
|
|
6694
|
+
const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
|
|
6431
6695
|
const messages = [...hardenedHistory, userQuestion];
|
|
6432
|
-
const systemPrompt = (
|
|
6696
|
+
const systemPrompt = (_s = this.config.llm.systemPrompt) != null ? _s : "";
|
|
6433
6697
|
const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
6434
6698
|
let fullReply = "";
|
|
6699
|
+
let thinkingText = "";
|
|
6700
|
+
let thinkingStartMs = performance.now();
|
|
6701
|
+
let thinkingDurationMs = 0;
|
|
6435
6702
|
const generateStart = performance.now();
|
|
6436
6703
|
if (this.llmProvider.chatStream) {
|
|
6437
6704
|
const stream = this.llmProvider.chatStream(messages, context);
|
|
6438
6705
|
if (!stream) throw new Error(`[Pipeline] ${this.config.llm.provider} chatStream returned undefined`);
|
|
6706
|
+
let inThinking = false;
|
|
6707
|
+
let textBuffer = "";
|
|
6439
6708
|
try {
|
|
6440
6709
|
for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
|
|
6441
6710
|
const chunk = temp.value;
|
|
6442
|
-
|
|
6443
|
-
|
|
6711
|
+
if (chunk.startsWith("\0THINKING\0")) {
|
|
6712
|
+
const thinkingDelta = chunk.slice("\0THINKING\0".length);
|
|
6713
|
+
thinkingText += thinkingDelta;
|
|
6714
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6715
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6716
|
+
continue;
|
|
6717
|
+
}
|
|
6718
|
+
if (isSimulatedThinking) {
|
|
6719
|
+
textBuffer += chunk;
|
|
6720
|
+
while (textBuffer.length > 0) {
|
|
6721
|
+
if (!inThinking) {
|
|
6722
|
+
const thinkIndex = textBuffer.indexOf("<think>");
|
|
6723
|
+
if (thinkIndex !== -1) {
|
|
6724
|
+
const before = textBuffer.slice(0, thinkIndex);
|
|
6725
|
+
if (before) {
|
|
6726
|
+
fullReply += before;
|
|
6727
|
+
yield before;
|
|
6728
|
+
}
|
|
6729
|
+
inThinking = true;
|
|
6730
|
+
thinkingStartMs = performance.now();
|
|
6731
|
+
textBuffer = textBuffer.slice(thinkIndex + "<think>".length);
|
|
6732
|
+
} else {
|
|
6733
|
+
const maxSafeLength = textBuffer.length - "<think>".length;
|
|
6734
|
+
if (maxSafeLength > 0) {
|
|
6735
|
+
const safeText = textBuffer.slice(0, maxSafeLength);
|
|
6736
|
+
fullReply += safeText;
|
|
6737
|
+
yield safeText;
|
|
6738
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6739
|
+
}
|
|
6740
|
+
break;
|
|
6741
|
+
}
|
|
6742
|
+
} else {
|
|
6743
|
+
const endThinkIndex = textBuffer.indexOf("</think>");
|
|
6744
|
+
if (endThinkIndex !== -1) {
|
|
6745
|
+
const thinkingDelta = textBuffer.slice(0, endThinkIndex);
|
|
6746
|
+
if (thinkingDelta) {
|
|
6747
|
+
thinkingText += thinkingDelta;
|
|
6748
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6749
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6750
|
+
}
|
|
6751
|
+
inThinking = false;
|
|
6752
|
+
textBuffer = textBuffer.slice(endThinkIndex + "</think>".length);
|
|
6753
|
+
} else {
|
|
6754
|
+
const maxSafeLength = textBuffer.length - "</think>".length;
|
|
6755
|
+
if (maxSafeLength > 0) {
|
|
6756
|
+
const thinkingDelta = textBuffer.slice(0, maxSafeLength);
|
|
6757
|
+
thinkingText += thinkingDelta;
|
|
6758
|
+
thinkingDurationMs = performance.now() - thinkingStartMs;
|
|
6759
|
+
yield { type: "thinking", text: thinkingDelta };
|
|
6760
|
+
textBuffer = textBuffer.slice(maxSafeLength);
|
|
6761
|
+
}
|
|
6762
|
+
break;
|
|
6763
|
+
}
|
|
6764
|
+
}
|
|
6765
|
+
}
|
|
6766
|
+
} else {
|
|
6767
|
+
fullReply += chunk;
|
|
6768
|
+
yield chunk;
|
|
6769
|
+
}
|
|
6444
6770
|
}
|
|
6445
6771
|
} catch (temp) {
|
|
6446
6772
|
error = [temp];
|
|
@@ -6452,17 +6778,42 @@ ${context}`;
|
|
|
6452
6778
|
throw error[0];
|
|
6453
6779
|
}
|
|
6454
6780
|
}
|
|
6781
|
+
if (isSimulatedThinking && textBuffer.length > 0) {
|
|
6782
|
+
if (inThinking) {
|
|
6783
|
+
thinkingText += textBuffer;
|
|
6784
|
+
yield { type: "thinking", text: textBuffer };
|
|
6785
|
+
} else {
|
|
6786
|
+
fullReply += textBuffer;
|
|
6787
|
+
yield textBuffer;
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6455
6790
|
} else {
|
|
6456
6791
|
const reply = yield new __await(this.llmProvider.chat(messages, context));
|
|
6457
|
-
|
|
6458
|
-
|
|
6792
|
+
if (isSimulatedThinking) {
|
|
6793
|
+
const thinkStart = reply.indexOf("<think>");
|
|
6794
|
+
const thinkEnd = reply.indexOf("</think>");
|
|
6795
|
+
if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {
|
|
6796
|
+
thinkingText = reply.slice(thinkStart + "<think>".length, thinkEnd);
|
|
6797
|
+
fullReply = reply.slice(0, thinkStart) + reply.slice(thinkEnd + "</think>".length);
|
|
6798
|
+
thinkingDurationMs = 100;
|
|
6799
|
+
} else {
|
|
6800
|
+
fullReply = reply;
|
|
6801
|
+
}
|
|
6802
|
+
} else {
|
|
6803
|
+
fullReply = reply;
|
|
6804
|
+
}
|
|
6805
|
+
yield fullReply;
|
|
6806
|
+
}
|
|
6807
|
+
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;
|
|
6808
|
+
if (runHallucination) {
|
|
6809
|
+
hallucinationScoringPromise = scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0);
|
|
6459
6810
|
}
|
|
6460
6811
|
const generateMs = performance.now() - generateStart;
|
|
6461
6812
|
const totalMs = performance.now() - requestStart;
|
|
6462
6813
|
const latency = {
|
|
6463
6814
|
embedMs: Math.round(embedMs),
|
|
6464
6815
|
retrieveMs: Math.round(retrieveMs),
|
|
6465
|
-
rerankMs: ((
|
|
6816
|
+
rerankMs: ((_v = this.config.rag) == null ? void 0 : _v.useReranking) ? Math.round(rerankMs) : void 0,
|
|
6466
6817
|
generateMs: Math.round(generateMs),
|
|
6467
6818
|
totalMs: Math.round(totalMs)
|
|
6468
6819
|
};
|
|
@@ -6475,12 +6826,16 @@ ${context}`;
|
|
|
6475
6826
|
totalTokens: promptTokens + completionTokens,
|
|
6476
6827
|
estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
|
|
6477
6828
|
};
|
|
6478
|
-
const
|
|
6829
|
+
const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
|
|
6830
|
+
uiTransformationPromise,
|
|
6831
|
+
hallucinationScoringPromise
|
|
6832
|
+
]));
|
|
6833
|
+
const trace = __spreadValues({
|
|
6479
6834
|
requestId,
|
|
6480
6835
|
query: question,
|
|
6481
6836
|
rewrittenQuery,
|
|
6482
6837
|
systemPrompt,
|
|
6483
|
-
userPrompt: question +
|
|
6838
|
+
userPrompt: question + finalRestrictionSuffix,
|
|
6484
6839
|
chunks: sources.map((s) => {
|
|
6485
6840
|
var _a2;
|
|
6486
6841
|
return {
|
|
@@ -6494,22 +6849,19 @@ ${context}`;
|
|
|
6494
6849
|
latency,
|
|
6495
6850
|
tokens,
|
|
6496
6851
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6497
|
-
}
|
|
6498
|
-
|
|
6852
|
+
}, hallucinationResult ? {
|
|
6853
|
+
hallucinationScore: hallucinationResult.score,
|
|
6854
|
+
hallucinationReason: hallucinationResult.reason
|
|
6855
|
+
} : {});
|
|
6499
6856
|
yield {
|
|
6500
6857
|
reply: "",
|
|
6501
6858
|
sources,
|
|
6502
6859
|
graphData,
|
|
6503
6860
|
ui_transformation: uiTransformation,
|
|
6504
|
-
trace
|
|
6861
|
+
trace,
|
|
6862
|
+
thinking: thinkingText || void 0,
|
|
6863
|
+
thinkingMs: thinkingDurationMs > 0 ? Math.round(thinkingDurationMs) : void 0
|
|
6505
6864
|
};
|
|
6506
|
-
scoreHallucination(this.llmProvider, fullReply, context).then((hallucinationResult) => {
|
|
6507
|
-
if (hallucinationResult) {
|
|
6508
|
-
trace.hallucinationScore = hallucinationResult.score;
|
|
6509
|
-
trace.hallucinationReason = hallucinationResult.reason;
|
|
6510
|
-
}
|
|
6511
|
-
}).catch(() => {
|
|
6512
|
-
});
|
|
6513
6865
|
} catch (error2) {
|
|
6514
6866
|
throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
6515
6867
|
}
|
|
@@ -6519,18 +6871,30 @@ ${context}`;
|
|
|
6519
6871
|
* Universal retrieval method combining all enabled providers.
|
|
6520
6872
|
* Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
|
|
6521
6873
|
*/
|
|
6522
|
-
async generateUiTransformation(question, sources,
|
|
6874
|
+
async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
|
|
6875
|
+
var _a;
|
|
6523
6876
|
if (!sources || sources.length === 0) {
|
|
6524
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6877
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6525
6878
|
}
|
|
6526
|
-
if (
|
|
6527
|
-
return UITransformer.transform(
|
|
6879
|
+
if (UITransformer.isProductQueryPublic(question)) {
|
|
6880
|
+
return UITransformer.transform(
|
|
6881
|
+
question,
|
|
6882
|
+
sources,
|
|
6883
|
+
this.config,
|
|
6884
|
+
cachedSchema,
|
|
6885
|
+
{ visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
|
|
6886
|
+
);
|
|
6887
|
+
}
|
|
6888
|
+
const isLocalProvider = this.config.llm.provider === "ollama";
|
|
6889
|
+
const disableLlmUiTransform = ((_a = this.config.llm.options) == null ? void 0 : _a.disableLlmUiTransform) === true;
|
|
6890
|
+
if (forceDeterministic || isLocalProvider || disableLlmUiTransform) {
|
|
6891
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6528
6892
|
}
|
|
6529
6893
|
try {
|
|
6530
6894
|
return await UITransformer.analyzeAndDecide(question, sources, this.llmRouter.get("fast"));
|
|
6531
6895
|
} catch (err) {
|
|
6532
6896
|
console.warn("[Pipeline] generateUiTransformation failed, using heuristic fallback:", err);
|
|
6533
|
-
return UITransformer.transform(question, sources, this.config,
|
|
6897
|
+
return UITransformer.transform(question, sources, this.config, cachedSchema);
|
|
6534
6898
|
}
|
|
6535
6899
|
}
|
|
6536
6900
|
applyStructuredFilters(sources, filter) {
|
|
@@ -6614,25 +6978,29 @@ ${context}`;
|
|
|
6614
6978
|
return Number.isFinite(numeric) ? numeric : null;
|
|
6615
6979
|
}
|
|
6616
6980
|
async retrieve(query, options) {
|
|
6617
|
-
var _a, _b, _c, _d, _e, _f;
|
|
6981
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
6618
6982
|
const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
|
|
6619
6983
|
const topK = (_b = options.topK) != null ? _b : 5;
|
|
6620
6984
|
const cacheKey = `${ns}::${query}`;
|
|
6621
6985
|
let queryVector = this.embeddingCache.get(cacheKey);
|
|
6622
|
-
const
|
|
6986
|
+
const useGraph = this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval);
|
|
6987
|
+
const strategy = await QueryProcessor.determineRetrievalStrategy(
|
|
6988
|
+
query,
|
|
6989
|
+
useGraph ? this.llmRouter.get("fast") : void 0
|
|
6990
|
+
);
|
|
6623
6991
|
console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
|
|
6624
6992
|
const [retrievedVector, graphData] = await Promise.all([
|
|
6625
6993
|
// Only embed if we need vector search (strategy is 'vector' or 'both')
|
|
6626
6994
|
strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
|
|
6627
6995
|
// Only query graph if we need graph search (strategy is 'graph' or 'both')
|
|
6628
|
-
(strategy === "graph" || strategy === "both") && this.graphDB && ((
|
|
6996
|
+
(strategy === "graph" || strategy === "both") && this.graphDB && ((_d = this.config.rag) == null ? void 0 : _d.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
|
|
6629
6997
|
]);
|
|
6630
6998
|
if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
|
|
6631
6999
|
this.embeddingCache.set(cacheKey, retrievedVector);
|
|
6632
7000
|
queryVector = retrievedVector;
|
|
6633
7001
|
}
|
|
6634
|
-
const baseFilter = __spreadProps(__spreadValues({}, (
|
|
6635
|
-
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (
|
|
7002
|
+
const baseFilter = __spreadProps(__spreadValues({}, (_e = options.filter) != null ? _e : {}), { queryText: query });
|
|
7003
|
+
const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
|
|
6636
7004
|
if (numericPredicates.length > 0) {
|
|
6637
7005
|
baseFilter.__numericPredicates = numericPredicates;
|
|
6638
7006
|
}
|
|
@@ -6643,7 +7011,7 @@ ${context}`;
|
|
|
6643
7011
|
) : [];
|
|
6644
7012
|
const resolvedSources = [];
|
|
6645
7013
|
for (const source of sources) {
|
|
6646
|
-
const parentId = (
|
|
7014
|
+
const parentId = (_g = source.metadata) == null ? void 0 : _g.parent_id;
|
|
6647
7015
|
if (parentId) {
|
|
6648
7016
|
console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
|
|
6649
7017
|
resolvedSources.push(source);
|
|
@@ -6666,10 +7034,13 @@ New Question: ${question}
|
|
|
6666
7034
|
Optimized Search Query:`;
|
|
6667
7035
|
const rewrite = await this.llmProvider.chat(
|
|
6668
7036
|
[
|
|
6669
|
-
{ role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
|
|
6670
7037
|
{ role: "user", content: prompt }
|
|
6671
7038
|
],
|
|
6672
|
-
""
|
|
7039
|
+
"",
|
|
7040
|
+
{
|
|
7041
|
+
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.",
|
|
7042
|
+
temperature: 0
|
|
7043
|
+
}
|
|
6673
7044
|
);
|
|
6674
7045
|
return rewrite.trim() || question;
|
|
6675
7046
|
}
|
|
@@ -6693,10 +7064,13 @@ ${context}
|
|
|
6693
7064
|
Suggestions:`;
|
|
6694
7065
|
const response = await this.llmProvider.chat(
|
|
6695
7066
|
[
|
|
6696
|
-
{ role: "system", content: "You are a helpful assistant that generates search suggestions." },
|
|
6697
7067
|
{ role: "user", content: prompt }
|
|
6698
7068
|
],
|
|
6699
|
-
""
|
|
7069
|
+
"",
|
|
7070
|
+
{
|
|
7071
|
+
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.",
|
|
7072
|
+
temperature: 0
|
|
7073
|
+
}
|
|
6700
7074
|
);
|
|
6701
7075
|
const match = response.match(/\[[\s\S]*\]/);
|
|
6702
7076
|
if (match) {
|
|
@@ -6881,6 +7255,30 @@ var VectorPlugin = class {
|
|
|
6881
7255
|
}
|
|
6882
7256
|
};
|
|
6883
7257
|
|
|
7258
|
+
// src/core/Retrivora.ts
|
|
7259
|
+
var Retrivora = class {
|
|
7260
|
+
constructor(config) {
|
|
7261
|
+
this.config = ConfigResolver.resolveUniversal(config);
|
|
7262
|
+
this.pipeline = new Pipeline(this.config);
|
|
7263
|
+
}
|
|
7264
|
+
async initialize() {
|
|
7265
|
+
await ConfigValidator.validateAndThrow(this.config);
|
|
7266
|
+
await this.pipeline.initialize();
|
|
7267
|
+
}
|
|
7268
|
+
async ingest(documents, namespace) {
|
|
7269
|
+
return this.pipeline.ingest(documents, namespace);
|
|
7270
|
+
}
|
|
7271
|
+
async ask(question, history = [], namespace) {
|
|
7272
|
+
return this.pipeline.ask(question, history, namespace);
|
|
7273
|
+
}
|
|
7274
|
+
askStream(question, history = [], namespace) {
|
|
7275
|
+
return this.pipeline.askStream(question, history, namespace);
|
|
7276
|
+
}
|
|
7277
|
+
getPipeline() {
|
|
7278
|
+
return this.pipeline;
|
|
7279
|
+
}
|
|
7280
|
+
};
|
|
7281
|
+
|
|
6884
7282
|
// src/config/ConfigBuilder.ts
|
|
6885
7283
|
var ConfigBuilder = class {
|
|
6886
7284
|
/**
|
|
@@ -7249,6 +7647,10 @@ function createStreamHandler(configOrPlugin) {
|
|
|
7249
7647
|
if (!isActive) break;
|
|
7250
7648
|
if (typeof chunk === "string") {
|
|
7251
7649
|
enqueue(sseTextFrame(chunk));
|
|
7650
|
+
} else if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "thinking") {
|
|
7651
|
+
enqueue(`data: ${JSON.stringify(chunk)}
|
|
7652
|
+
|
|
7653
|
+
`);
|
|
7252
7654
|
} else {
|
|
7253
7655
|
enqueue(sseMetaFrame(chunk));
|
|
7254
7656
|
const responseChunk = chunk;
|
|
@@ -7422,14 +7824,17 @@ function createUploadHandler(configOrPlugin) {
|
|
|
7422
7824
|
}
|
|
7423
7825
|
export {
|
|
7424
7826
|
AnthropicProvider,
|
|
7827
|
+
AuthenticationException,
|
|
7425
7828
|
BaseVectorProvider,
|
|
7426
7829
|
BatchProcessor,
|
|
7427
7830
|
ChromaDBProvider,
|
|
7428
7831
|
ConfigBuilder,
|
|
7429
7832
|
ConfigResolver,
|
|
7430
7833
|
ConfigValidator,
|
|
7834
|
+
ConfigurationException,
|
|
7431
7835
|
DocumentChunker,
|
|
7432
7836
|
DocumentParser,
|
|
7837
|
+
EmbeddingFailedException,
|
|
7433
7838
|
EmbeddingStrategy,
|
|
7434
7839
|
EmbeddingStrategyResolver,
|
|
7435
7840
|
LLMFactory,
|
|
@@ -7444,9 +7849,14 @@ export {
|
|
|
7444
7849
|
Pipeline,
|
|
7445
7850
|
PostgreSQLProvider,
|
|
7446
7851
|
ProviderHealthCheck,
|
|
7852
|
+
ProviderNotFoundException,
|
|
7447
7853
|
ProviderRegistry,
|
|
7448
7854
|
QdrantProvider,
|
|
7855
|
+
RateLimitException,
|
|
7449
7856
|
RedisProvider,
|
|
7857
|
+
RetrievalException,
|
|
7858
|
+
Retrivora,
|
|
7859
|
+
RetrivoraError,
|
|
7450
7860
|
UniversalLLMAdapter,
|
|
7451
7861
|
UniversalVectorProvider,
|
|
7452
7862
|
VECTOR_PROFILES,
|