@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.
Files changed (60) hide show
  1. package/README.md +27 -0
  2. package/dist/{ILLMProvider-Bw2A28nU.d.mts → ILLMProvider-DNhyOYoK.d.mts} +41 -1
  3. package/dist/{ILLMProvider-Bw2A28nU.d.ts → ILLMProvider-DNhyOYoK.d.ts} +41 -1
  4. package/dist/handlers/index.d.mts +2 -2
  5. package/dist/handlers/index.d.ts +2 -2
  6. package/dist/handlers/index.js +563 -205
  7. package/dist/handlers/index.mjs +563 -205
  8. package/dist/index-C9v7-tWd.d.mts +183 -0
  9. package/dist/{index-B70ZLkfG.d.mts → index-CHL1jdYm.d.mts} +1 -1
  10. package/dist/index-CjQdL0cX.d.ts +183 -0
  11. package/dist/{index-DVu-mkAM.d.ts → index-Hgbwl9X4.d.ts} +1 -1
  12. package/dist/index.css +40 -0
  13. package/dist/index.d.mts +3 -3
  14. package/dist/index.d.ts +3 -3
  15. package/dist/index.js +326 -158
  16. package/dist/index.mjs +312 -151
  17. package/dist/server.d.mts +15 -94
  18. package/dist/server.d.ts +15 -94
  19. package/dist/server.js +623 -205
  20. package/dist/server.mjs +615 -205
  21. package/package.json +1 -1
  22. package/src/app/api/chat/route.ts +2 -2
  23. package/src/app/api/health/route.ts +3 -2
  24. package/src/app/api/ingest/route.ts +3 -2
  25. package/src/app/api/suggestions/route.ts +3 -2
  26. package/src/app/api/upload/route.ts +3 -2
  27. package/src/app/constants.tsx +85 -30
  28. package/src/app/layout.tsx +18 -7
  29. package/src/components/MessageBubble.tsx +39 -2
  30. package/src/components/ThinkingBlock.tsx +75 -0
  31. package/src/components/VisualizationRenderer.tsx +27 -1
  32. package/src/config/RagConfig.ts +47 -0
  33. package/src/config/serverConfig.ts +25 -0
  34. package/src/core/ConfigResolver.ts +56 -4
  35. package/src/core/ConfigValidator.ts +2 -1
  36. package/src/core/Pipeline.ts +226 -68
  37. package/src/core/ProviderRegistry.ts +11 -2
  38. package/src/core/QueryProcessor.ts +38 -4
  39. package/src/core/Retrivora.ts +51 -0
  40. package/src/exceptions/index.ts +59 -0
  41. package/src/handlers/index.ts +2 -0
  42. package/src/hooks/useRagChat.ts +26 -4
  43. package/src/index.ts +25 -1
  44. package/src/lib/plugin.ts +24 -0
  45. package/src/llm/LLMFactory.ts +4 -1
  46. package/src/llm/providers/AnthropicProvider.ts +70 -20
  47. package/src/llm/providers/GeminiProvider.ts +14 -15
  48. package/src/llm/providers/OllamaProvider.ts +13 -16
  49. package/src/llm/providers/OpenAIProvider.ts +9 -14
  50. package/src/llm/providers/UniversalLLMAdapter.ts +5 -5
  51. package/src/llm/utils.ts +46 -0
  52. package/src/providers/vectordb/MongoDBProvider.ts +19 -7
  53. package/src/providers/vectordb/MultiTablePostgresProvider.ts +45 -13
  54. package/src/rag/EntityExtractor.ts +2 -2
  55. package/src/rag/Reranker.ts +9 -16
  56. package/src/server.ts +27 -1
  57. package/src/types/chat.ts +7 -0
  58. package/src/utils/UITransformer.ts +73 -4
  59. package/dist/DocumentChunker-Dh9TvmGG.d.mts +0 -45
  60. 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.map((k) => `LOWER(metadata->>'${k}')`);
805
- return `COALESCE(${coalesceExprs.join(", ")}) = LOWER($${paramIdx})`;
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(to_jsonb(t)) AS kv(key, val)
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
- SELECT *,
821
- (1 - (embedding <=> $1::vector)) AS vector_score,
822
- COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_score,
823
- ((1 - (embedding <=> $1::vector)) + (COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) * 2.0) ${exactNameScoreExpr}) AS hybrid_score
824
- FROM "${table}" t
825
- ${whereClause}
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 hybrid_score DESC
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
- matchFilter[key] = value;
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
- id: res._id,
1074
- content: res[this.contentKey],
1075
- metadata: res[this.metadataKey] || {},
1076
- score: res.score
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 Error("[ConfigResolver] projectId is required");
2386
+ throw new ConfigurationException("[ConfigResolver] projectId is required");
2267
2387
  }
2268
2388
  if (!config.vectorDb.provider) {
2269
- throw new Error("[ConfigResolver] vectorDb.provider is required");
2389
+ throw new ConfigurationException("[ConfigResolver] vectorDb.provider is required");
2270
2390
  }
2271
2391
  if (!config.llm.provider) {
2272
- throw new Error("[ConfigResolver] llm.provider is required");
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 resolvedSystemPrompt = (_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.
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: resolvedSystemPrompt.includes("{{context}}") ? resolvedSystemPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedSystemPrompt : `${resolvedSystemPrompt}
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 resolvedSystemPrompt = (_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.
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: resolvedSystemPrompt.includes("{{context}}") ? resolvedSystemPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedSystemPrompt : `${resolvedSystemPrompt}
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 resolvedPrompt = (_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.
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 response = await this.client.messages.create({
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: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
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
- const block = response.content[0];
2522
- return block.type === "text" ? block.text : "";
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 resolvedPrompt = (_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.
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 stream = yield new __await(this.client.messages.create({
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: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
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" && chunk.delta.type === "text_delta") {
2553
- yield chunk.delta.text;
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
- this.http = axios.create({ baseURL, timeout: 12e4 });
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 system = this.buildSystemPrompt(context, options == null ? void 0 : options.systemPrompt);
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: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
2662
- num_predict: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024
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 system = this.buildSystemPrompt(context, options == null ? void 0 : options.systemPrompt);
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: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
2680
- num_predict: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024
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 ((_e = json.message) == null ? void 0 : _e.content) {
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 ((_f = json.message) == null ? void 0 : _f.content) yield json.message.content;
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: this.buildSystemInstruction(context)
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: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : DEFAULT_TEMPERATURE,
2932
- maxOutputTokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : DEFAULT_MAX_TOKENS,
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: this.buildSystemInstruction(context)
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: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : DEFAULT_TEMPERATURE,
2949
- maxOutputTokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : DEFAULT_MAX_TOKENS,
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 Error(
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 Error(`Unsupported vector provider: ${provider}`);
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 Error(`Unsupported graph provider: ${provider}`);
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 Error(`[ConfigValidator] Configuration validation failed:
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 prompt = `You are a relevance ranking expert.
3875
- Given the following user query and a list of retrieved document chunks, rank the chunks by relevance to the query.
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
- { temperature: 0, maxTokens: 50 }
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
- addHint(value, field);
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 = this.normalizeHintValue((_c = match[1]) != null ? _c : "");
4648
+ const field = (_c = match[1]) != null ? _c : "";
4470
4649
  const value = (_d = match[2]) != null ? _d : "";
4471
- if (field && !this.isLikelyPromptPhrase(field)) {
4472
- addHint(value, field);
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: `QUERY: ${query}` }],
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
- const prompt = `You are an AI quality checker. Given the CONTEXT retrieved from a knowledge base and the ANSWER generated from it, rate how well-grounded the answer is.
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
- { temperature: 0, maxTokens: 120 }
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 = chartInstruction;
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
- (_h = this.config.rag) == null ? void 0 : _h.graphKeywords,
6363
- (_i = this.config.rag) == null ? void 0 : _i.vectorKeywords
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 && ((_j = this.config.rag) == null ? void 0 : _j.useGraphRetrieval) ? yield new __await(this.graphDB.query(searchQuery)) : void 0;
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 && ((_k = this.config.rag) == null ? void 0 : _k.useReranking)) {
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 highlyRelevant = fullSources.filter((m) => m.score >= 0.4);
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 trainedSchemaPromise = allMetadataKeys.length > 0 ? SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => void 0) : Promise.resolve(void 0);
6417
- const uiTransformationPromise = trainedSchemaPromise.then(
6418
- (trainedSchema) => this.generateUiTransformation(
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
- trainedSchema,
6422
- hasNumericPredicates
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 + restrictionSuffix };
6694
+ const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
6431
6695
  const messages = [...hardenedHistory, userQuestion];
6432
- const systemPrompt = (_l = this.config.llm.systemPrompt) != null ? _l : "";
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
- fullReply += chunk;
6443
- yield chunk;
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
- fullReply = reply;
6458
- yield reply;
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: ((_m = this.config.rag) == null ? void 0 : _m.useReranking) ? Math.round(rerankMs) : void 0,
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 trace = {
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 + restrictionSuffix,
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
- const uiTransformation = yield new __await(uiTransformationPromise);
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, trainedSchema, forceDeterministic = false) {
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, trainedSchema);
6877
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6525
6878
  }
6526
- if (forceDeterministic) {
6527
- return UITransformer.transform(question, sources, this.config, trainedSchema);
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, trainedSchema);
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 strategy = await QueryProcessor.determineRetrievalStrategy(query, this.llmRouter.get("fast"));
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 && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
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({}, (_d = options.filter) != null ? _d : {}), { queryText: query });
6635
- const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_e = this.config.rag) == null ? void 0 : _e.filterableFields);
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 = (_f = source.metadata) == null ? void 0 : _f.parent_id;
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,