@retrivora-ai/rag-engine 1.9.3 → 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 +552 -198
  7. package/dist/handlers/index.mjs +552 -198
  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 +612 -198
  20. package/dist/server.mjs +604 -198
  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 +224 -67
  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 +9 -4
  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 = [
@@ -2112,7 +2148,7 @@ function getRagConfig(baseConfig, env = process.env) {
2112
2148
  return getEnvConfig(env, baseConfig);
2113
2149
  }
2114
2150
  function getEnvConfig(env = process.env, base) {
2115
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga;
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;
2116
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__";
2117
2153
  const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
2118
2154
  const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
@@ -2182,7 +2218,7 @@ function getEnvConfig(env = process.env, base) {
2182
2218
  rest: "default",
2183
2219
  custom: "default"
2184
2220
  };
2185
- return {
2221
+ return __spreadValues({
2186
2222
  projectId,
2187
2223
  vectorDb: {
2188
2224
  provider: vectorProvider,
@@ -2198,7 +2234,9 @@ function getEnvConfig(env = process.env, base) {
2198
2234
  maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
2199
2235
  temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
2200
2236
  options: {
2201
- 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
2202
2240
  }
2203
2241
  },
2204
2242
  embedding: {
@@ -2230,11 +2268,76 @@ function getEnvConfig(env = process.env, base) {
2230
2268
  topK: readNumber(env, "RAG_TOP_K", 5),
2231
2269
  scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
2232
2270
  chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
2233
- 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
+ })()
2234
2288
  }
2235
- };
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
+ } : {});
2236
2299
  }
2237
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
+
2238
2341
  // src/core/ConfigResolver.ts
2239
2342
  var ConfigResolver = class {
2240
2343
  /**
@@ -2261,24 +2364,81 @@ var ConfigResolver = class {
2261
2364
  rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
2262
2365
  });
2263
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
+ }
2264
2381
  /**
2265
2382
  * Validates the configuration for required fields.
2266
2383
  */
2267
2384
  static validate(config) {
2268
2385
  if (!config.projectId) {
2269
- throw new Error("[ConfigResolver] projectId is required");
2386
+ throw new ConfigurationException("[ConfigResolver] projectId is required");
2270
2387
  }
2271
2388
  if (!config.vectorDb.provider) {
2272
- throw new Error("[ConfigResolver] vectorDb.provider is required");
2389
+ throw new ConfigurationException("[ConfigResolver] vectorDb.provider is required");
2273
2390
  }
2274
2391
  if (!config.llm.provider) {
2275
- throw new Error("[ConfigResolver] llm.provider is required");
2392
+ throw new ConfigurationException("[ConfigResolver] llm.provider is required");
2276
2393
  }
2277
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
+ }
2278
2420
  };
2279
2421
 
2280
2422
  // src/llm/providers/OpenAIProvider.ts
2281
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
2282
2442
  var OpenAIProvider = class {
2283
2443
  constructor(llmConfig, embeddingConfig) {
2284
2444
  if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
@@ -2342,16 +2502,10 @@ var OpenAIProvider = class {
2342
2502
  }
2343
2503
  async chat(messages, context, options) {
2344
2504
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2345
- 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.
2346
-
2347
- Context:
2348
- ${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.";
2349
2506
  const systemMessage = {
2350
2507
  role: "system",
2351
- content: resolvedSystemPrompt.includes("{{context}}") ? resolvedSystemPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedSystemPrompt : `${resolvedSystemPrompt}
2352
-
2353
- Context:
2354
- ${context}`
2508
+ content: buildSystemContent(basePrompt, context)
2355
2509
  };
2356
2510
  const formattedMessages = [
2357
2511
  systemMessage,
@@ -2372,16 +2526,10 @@ ${context}`
2372
2526
  chatStream(messages, context, options) {
2373
2527
  return __asyncGenerator(this, null, function* () {
2374
2528
  var _a, _b, _c, _d, _e, _f, _g, _h;
2375
- 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.
2376
-
2377
- Context:
2378
- ${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.";
2379
2530
  const systemMessage = {
2380
2531
  role: "system",
2381
- content: resolvedSystemPrompt.includes("{{context}}") ? resolvedSystemPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedSystemPrompt : `${resolvedSystemPrompt}
2382
-
2383
- Context:
2384
- ${context}`
2532
+ content: buildSystemContent(basePrompt, context)
2385
2533
  };
2386
2534
  const formattedMessages = [
2387
2535
  systemMessage,
@@ -2502,58 +2650,87 @@ var AnthropicProvider = class {
2502
2650
  };
2503
2651
  }
2504
2652
  async chat(messages, context, options) {
2505
- var _a, _b, _c, _d;
2506
- 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.
2507
-
2508
- Context:
2509
- ${context}`;
2510
- const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
2511
-
2512
- Context:
2513
- ${context}`;
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);
2514
2656
  const anthropicMessages = messages.map((m) => ({
2515
2657
  role: m.role === "assistant" ? "assistant" : "user",
2516
2658
  content: m.content
2517
2659
  }));
2518
- 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({
2519
2677
  model: this.llmConfig.model,
2520
- 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,
2521
2679
  system,
2522
2680
  messages: anthropicMessages
2523
- });
2524
- const block = response.content[0];
2525
- 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;
2526
2689
  }
2527
2690
  chatStream(messages, context, options) {
2528
2691
  return __asyncGenerator(this, null, function* () {
2529
- var _a, _b, _c, _d;
2530
- 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.
2531
-
2532
- Context:
2533
- ${context}`;
2534
- const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
2535
-
2536
- Context:
2537
- ${context}`;
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);
2538
2695
  const anthropicMessages = messages.map((m) => ({
2539
2696
  role: m.role === "assistant" ? "assistant" : "user",
2540
2697
  content: m.content
2541
2698
  }));
2542
- 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({
2543
2716
  model: this.llmConfig.model,
2544
- 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,
2545
2718
  system,
2546
2719
  messages: anthropicMessages,
2547
2720
  stream: true
2548
- }));
2721
+ }, extraParams)));
2549
2722
  if (!stream) {
2550
2723
  throw new Error("[AnthropicProvider] messages.create stream is undefined");
2551
2724
  }
2552
2725
  try {
2553
2726
  for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2554
2727
  const chunk = temp.value;
2555
- if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
2556
- 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
+ }
2557
2734
  }
2558
2735
  }
2559
2736
  } catch (temp) {
@@ -2597,9 +2774,10 @@ ${context}`;
2597
2774
  import axios from "axios";
2598
2775
  var OllamaProvider = class {
2599
2776
  constructor(llmConfig, embeddingConfig) {
2600
- var _a;
2777
+ var _a, _b;
2601
2778
  const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
2602
- 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 });
2603
2781
  this.llmConfig = llmConfig;
2604
2782
  this.embeddingConfig = embeddingConfig;
2605
2783
  }
@@ -2655,14 +2833,15 @@ var OllamaProvider = class {
2655
2833
  };
2656
2834
  }
2657
2835
  async chat(messages, context, options) {
2658
- var _a, _b, _c, _d;
2659
- 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);
2660
2839
  const { data } = await this.http.post("/api/chat", {
2661
2840
  model: this.llmConfig.model,
2662
2841
  stream: false,
2663
2842
  options: {
2664
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
2665
- 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
2666
2845
  },
2667
2846
  messages: [
2668
2847
  { role: "system", content: system },
@@ -2673,14 +2852,15 @@ var OllamaProvider = class {
2673
2852
  }
2674
2853
  chatStream(messages, context, options) {
2675
2854
  return __asyncGenerator(this, null, function* () {
2676
- var _a, _b, _c, _d, _e, _f;
2677
- 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);
2678
2858
  const response = yield new __await(this.http.post("/api/chat", {
2679
2859
  model: this.llmConfig.model,
2680
2860
  stream: true,
2681
2861
  options: {
2682
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
2683
- 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
2684
2864
  },
2685
2865
  messages: [
2686
2866
  { role: "system", content: system },
@@ -2702,7 +2882,7 @@ var OllamaProvider = class {
2702
2882
  if (!line.trim()) continue;
2703
2883
  try {
2704
2884
  const json = JSON.parse(line);
2705
- if ((_e = json.message) == null ? void 0 : _e.content) {
2885
+ if ((_g = json.message) == null ? void 0 : _g.content) {
2706
2886
  yield json.message.content;
2707
2887
  }
2708
2888
  if (json.done) return;
@@ -2724,26 +2904,12 @@ var OllamaProvider = class {
2724
2904
  if (lineBuffer.trim()) {
2725
2905
  try {
2726
2906
  const json = JSON.parse(lineBuffer);
2727
- 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;
2728
2908
  } catch (e) {
2729
2909
  }
2730
2910
  }
2731
2911
  });
2732
2912
  }
2733
- buildSystemPrompt(context, overridePrompt) {
2734
- var _a;
2735
- if (overridePrompt) {
2736
- return overridePrompt;
2737
- }
2738
- const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
2739
-
2740
- Context:
2741
- ${context}`;
2742
- return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
2743
-
2744
- Context:
2745
- ${context}`;
2746
- }
2747
2913
  async embed(text, options) {
2748
2914
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2749
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";
@@ -2890,21 +3056,6 @@ var GeminiProvider = class {
2890
3056
  var _a, _b;
2891
3057
  return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
2892
3058
  }
2893
- /**
2894
- * Build the system instruction string, inserting the RAG context either via
2895
- * the `{{context}}` placeholder or by appending it.
2896
- */
2897
- buildSystemInstruction(context) {
2898
- var _a;
2899
- const base = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
2900
-
2901
- Context:
2902
- ${context}`;
2903
- return base.includes("{{context}}") ? base.replace("{{context}}", context) : `${base}
2904
-
2905
- Context:
2906
- ${context}`;
2907
- }
2908
3059
  /**
2909
3060
  * Convert ChatMessage[] to the Gemini contents format.
2910
3061
  *
@@ -2923,16 +3074,17 @@ ${context}`;
2923
3074
  // ILLMProvider — chat
2924
3075
  // -------------------------------------------------------------------------
2925
3076
  async chat(messages, context, options) {
2926
- 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.";
2927
3079
  const model = this.client.getGenerativeModel({
2928
3080
  model: this.llmConfig.model,
2929
- systemInstruction: this.buildSystemInstruction(context)
3081
+ systemInstruction: buildSystemContent(basePrompt, context)
2930
3082
  });
2931
3083
  const result = await model.generateContent({
2932
3084
  contents: this.buildGeminiContents(messages),
2933
3085
  generationConfig: {
2934
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : DEFAULT_TEMPERATURE,
2935
- 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,
2936
3088
  stopSequences: options == null ? void 0 : options.stop
2937
3089
  }
2938
3090
  });
@@ -2940,16 +3092,17 @@ ${context}`;
2940
3092
  }
2941
3093
  chatStream(messages, context, options) {
2942
3094
  return __asyncGenerator(this, null, function* () {
2943
- 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.";
2944
3097
  const model = this.client.getGenerativeModel({
2945
3098
  model: this.llmConfig.model,
2946
- systemInstruction: this.buildSystemInstruction(context)
3099
+ systemInstruction: buildSystemContent(basePrompt, context)
2947
3100
  });
2948
3101
  const result = yield new __await(model.generateContentStream({
2949
3102
  contents: this.buildGeminiContents(messages),
2950
3103
  generationConfig: {
2951
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : DEFAULT_TEMPERATURE,
2952
- 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,
2953
3106
  stopSequences: options == null ? void 0 : options.stop
2954
3107
  }
2955
3108
  }));
@@ -3384,7 +3537,9 @@ var LLMFactory = class _LLMFactory {
3384
3537
  if (llmConfig.baseUrl || ((_b = llmConfig.options) == null ? void 0 : _b.baseUrl)) {
3385
3538
  return new UniversalLLMAdapter(llmConfig);
3386
3539
  }
3387
- throw new Error(
3540
+ throw new ProviderNotFoundException(
3541
+ "LLM",
3542
+ String(llmConfig.provider),
3388
3543
  `[LLMFactory] Unknown provider "${llmConfig.provider}". Built-in providers: ${_LLMFactory.listProviders().join(", ")}. Register a custom provider with LLMFactory.register().`
3389
3544
  );
3390
3545
  }
@@ -3516,7 +3671,7 @@ var ProviderRegistry = class {
3516
3671
  return UniversalVectorProvider2;
3517
3672
  }
3518
3673
  default:
3519
- throw new Error(`Unsupported vector provider: ${provider}`);
3674
+ throw new ProviderNotFoundException("vector", provider);
3520
3675
  }
3521
3676
  }
3522
3677
  static async createVectorProvider(config) {
@@ -3534,12 +3689,18 @@ var ProviderRegistry = class {
3534
3689
  return new SimpleGraphProvider2(config);
3535
3690
  }
3536
3691
  default:
3537
- throw new Error(`Unsupported graph provider: ${provider}`);
3692
+ throw new ProviderNotFoundException("graph", provider);
3538
3693
  }
3539
3694
  }
3540
3695
  static createLLMProvider(llmConfig, embeddingConfig) {
3541
3696
  return LLMFactory.create(llmConfig, embeddingConfig);
3542
3697
  }
3698
+ static registerLLMProvider(name, factory) {
3699
+ LLMFactory.register(name, factory);
3700
+ }
3701
+ static createEmbeddingProvider(embeddingConfig) {
3702
+ return LLMFactory.createEmbeddingProvider(embeddingConfig);
3703
+ }
3543
3704
  };
3544
3705
  ProviderRegistry.vectorProviders = {};
3545
3706
  ProviderRegistry.graphProviders = {};
@@ -3686,8 +3847,8 @@ var ConfigValidator = class {
3686
3847
  const errorItems = errors.filter((e) => e.severity === "error");
3687
3848
  if (errorItems.length > 0) {
3688
3849
  const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
3689
- throw new Error(`[ConfigValidator] Configuration validation failed:
3690
- ${message}`);
3850
+ throw new ConfigurationException(`[ConfigValidator] Configuration validation failed:
3851
+ ${message}`, errorItems);
3691
3852
  }
3692
3853
  }
3693
3854
  };
@@ -3874,20 +4035,17 @@ var Reranker = class {
3874
4035
  }
3875
4036
  try {
3876
4037
  const topN = matches.slice(0, 10);
3877
- const prompt = `You are a relevance ranking expert.
3878
- Given the following user query and a list of retrieved document chunks, rank the chunks by relevance to the query.
3879
- Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
3880
- Use the indices provided in brackets like [0], [1], etc.
3881
- Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
3882
-
3883
- Query: "${query}"
4038
+ const response = await llm.chat(
4039
+ [{ role: "user", content: `Query: "${query}"
3884
4040
 
3885
4041
  Documents:
3886
- ${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}`;
3887
- const response = await llm.chat(
3888
- [{ role: "user", content: prompt }],
4042
+ ${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}` }],
3889
4043
  "",
3890
- { 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
+ }
3891
4049
  );
3892
4050
  const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
3893
4051
  const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
@@ -4399,6 +4557,18 @@ var QueryProcessor = class {
4399
4557
  }, normalizedField ? { field: normalizedField } : {}));
4400
4558
  }
4401
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
+ };
4402
4572
  for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
4403
4573
  addHint(match[1]);
4404
4574
  }
@@ -4423,7 +4593,13 @@ var QueryProcessor = class {
4423
4593
  const field = match[1];
4424
4594
  const value = match[2];
4425
4595
  if (field && !this.isLikelyPromptPhrase(field)) {
4426
- addHint(value, field);
4596
+ const resolvedField = resolveValidField(field);
4597
+ if (resolvedField) {
4598
+ addHint(value, resolvedField);
4599
+ } else {
4600
+ addHint(value);
4601
+ addHint(field);
4602
+ }
4427
4603
  }
4428
4604
  }
4429
4605
  if (validFields.length > 0) {
@@ -4469,12 +4645,16 @@ var QueryProcessor = class {
4469
4645
  ];
4470
4646
  for (const pattern of fieldValuePatterns) {
4471
4647
  for (const match of question.matchAll(pattern)) {
4472
- const field = this.normalizeHintValue((_c = match[1]) != null ? _c : "");
4648
+ const field = (_c = match[1]) != null ? _c : "";
4473
4649
  const value = (_d = match[2]) != null ? _d : "";
4474
- if (field && !this.isLikelyPromptPhrase(field)) {
4475
- addHint(value, field);
4650
+ const resolvedField = resolveValidField(field);
4651
+ if (resolvedField) {
4652
+ addHint(value, resolvedField);
4476
4653
  } else {
4477
4654
  addHint(value);
4655
+ if (field && !this.isLikelyPromptPhrase(field)) {
4656
+ addHint(field);
4657
+ }
4478
4658
  }
4479
4659
  }
4480
4660
  }
@@ -4701,7 +4881,7 @@ var LLMRouter = class {
4701
4881
 
4702
4882
  // src/utils/UITransformer.ts
4703
4883
  init_synonyms();
4704
- var UITransformer = class {
4884
+ var UITransformer = class _UITransformer {
4705
4885
  // ─── Public Entry Points ─────────────────────────────────────────────────
4706
4886
  /**
4707
4887
  * Heuristic-only transform (no LLM required).
@@ -4763,7 +4943,7 @@ var UITransformer = class {
4763
4943
  static async analyzeAndDecide(query, sources, llm) {
4764
4944
  let intent;
4765
4945
  try {
4766
- intent = await this.detectIntent(query, llm);
4946
+ intent = await this.detectIntent(query, llm, sources);
4767
4947
  console.debug("[UITransformer] Detected intent:", intent);
4768
4948
  } catch (err) {
4769
4949
  console.warn("[UITransformer] Intent detection failed; using heuristic.", err);
@@ -4781,11 +4961,23 @@ var UITransformer = class {
4781
4961
  try {
4782
4962
  const context = this.buildContextSummary(sources);
4783
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
+ `;
4784
4973
  const userPrompt = [
4785
4974
  `USER QUESTION: ${query}`,
4786
4975
  "",
4787
4976
  `DETECTED INTENT (JSON): ${JSON.stringify(intent)}`,
4788
4977
  "",
4978
+ `RETRIEVED DATA SCHEMA:`,
4979
+ schemaContext,
4980
+ "",
4789
4981
  "RETRIEVED DATA (JSON):",
4790
4982
  context
4791
4983
  ].join("\n");
@@ -4796,6 +4988,20 @@ var UITransformer = class {
4796
4988
  );
4797
4989
  const parsed = this.parseTransformationResponse(rawResponse);
4798
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
+ }
4799
5005
  const intentAllowsTable = intent.wantsExplicitTable || ["tabular", "table", "geographic"].includes(intent.visualizationHint);
4800
5006
  const intentWantsPieLikeChart = ["pie_chart", "donut_chart"].includes(intent.recommendedChart) || ["composition", "category_breakdown"].includes(intent.visualizationHint);
4801
5007
  if (parsed.type === "table" && !intentAllowsTable) {
@@ -4825,7 +5031,26 @@ var UITransformer = class {
4825
5031
  * - The intent object can be reused across both the heuristic and LLM paths.
4826
5032
  * - It is easy to unit-test intent detection in isolation.
4827
5033
  */
4828
- 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
+ }
4829
5054
  const systemPrompt = `You are an intent classifier for a product-search RAG system.
4830
5055
  Given a user query, return ONLY a valid JSON object with this exact shape \u2014 no prose, no markdown:
4831
5056
 
@@ -4861,8 +5086,11 @@ RULES:
4861
5086
  - isTemporal: true if time words appear (trend, historical, over time, last year, monthly, etc.)
4862
5087
  - isComparison: true if user compares, ranks, or contrasts entities or categories.
4863
5088
  - language: detect from the query text itself; default "en" if uncertain.`;
5089
+ const userPrompt = `QUERY: ${query}${schemaProfileText ? `
5090
+
5091
+ ${schemaProfileText}` : ""}`;
4864
5092
  const rawResponse = await llm.chat(
4865
- [{ role: "user", content: `QUERY: ${query}` }],
5093
+ [{ role: "user", content: userPrompt }],
4866
5094
  "",
4867
5095
  { systemPrompt, temperature: 0 }
4868
5096
  );
@@ -5305,10 +5533,21 @@ RULES:
5305
5533
  const hasEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
5306
5534
  return asksForRecords && (hasNumericPredicate || hasEntityTerms);
5307
5535
  }
5536
+ /** @internal kept private for transform() internal logic */
5308
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) {
5309
5548
  const q = query.toLowerCase();
5310
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);
5311
- 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);
5312
5551
  const nonProductEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
5313
5552
  return productTerms && productAction && !nonProductEntityTerms;
5314
5553
  }
@@ -6081,23 +6320,19 @@ function estimateCostUsd(promptTokens, completionTokens, model) {
6081
6320
  async function scoreHallucination(llm, answer, context) {
6082
6321
  const maxContextChars = 3e3;
6083
6322
  const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
6084
- 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.
6085
-
6086
- CONTEXT:
6323
+ try {
6324
+ const raw = await llm.chat(
6325
+ [{ role: "user", content: `CONTEXT:
6087
6326
  ${truncatedContext}
6088
6327
 
6089
6328
  ANSWER:
6090
- ${answer}
6091
-
6092
- Return ONLY a valid JSON object with no markdown fences:
6093
- {"score": <float 0-1>, "reason": "<one sentence>"}
6094
-
6095
- Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
6096
- try {
6097
- const raw = await llm.chat(
6098
- [{ role: "user", content: prompt }],
6329
+ ${answer}` }],
6099
6330
  "",
6100
- { 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
+ }
6101
6336
  );
6102
6337
  const jsonMatch = raw.match(/\{[\s\S]*\}/);
6103
6338
  if (!jsonMatch) return void 0;
@@ -6157,7 +6392,10 @@ var Pipeline = class {
6157
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.
6158
6393
  - Do NOT try to format product lists as tables.
6159
6394
  `;
6160
- 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}`;
6161
6399
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
6162
6400
  this.llmRouter = new LLMRouter(this.config);
6163
6401
  const { llmProvider: resolvedLLM, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
@@ -6335,11 +6573,11 @@ var Pipeline = class {
6335
6573
  */
6336
6574
  askStream(_0) {
6337
6575
  return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
6338
- 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;
6339
6577
  yield new __await(this.initialize());
6340
6578
  const ns = namespace != null ? namespace : this.config.projectId;
6341
6579
  const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
6342
- const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
6580
+ const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
6343
6581
  const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
6344
6582
  const requestStart = performance.now();
6345
6583
  try {
@@ -6358,12 +6596,13 @@ var Pipeline = class {
6358
6596
  const embedStart = performance.now();
6359
6597
  const cacheKey = `${ns}::${searchQuery}`;
6360
6598
  const cachedVector = this.embeddingCache.get(cacheKey);
6599
+ const useGraph = this.graphDB && ((_h = this.config.rag) == null ? void 0 : _h.useGraphRetrieval);
6361
6600
  const [strategyResult, embeddedVector] = yield new __await(Promise.all([
6362
6601
  QueryProcessor.determineRetrievalStrategy(
6363
6602
  searchQuery,
6364
- this.llmRouter.get("fast"),
6365
- (_h = this.config.rag) == null ? void 0 : _h.graphKeywords,
6366
- (_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
6367
6606
  ),
6368
6607
  // Embed immediately regardless of strategy — costs nothing if cached.
6369
6608
  // If strategy turns out to be 'graph'-only we just won't use the vector.
@@ -6373,7 +6612,7 @@ var Pipeline = class {
6373
6612
  if (!cachedVector && queryVector.length > 0) {
6374
6613
  this.embeddingCache.set(cacheKey, queryVector);
6375
6614
  }
6376
- 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;
6377
6616
  const hasMetadataFilter = filter.metadata && Object.keys(filter.metadata).length > 0;
6378
6617
  const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
6379
6618
  const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
@@ -6386,7 +6625,7 @@ var Pipeline = class {
6386
6625
  const structuredSources = this.applyStructuredFilters(rawSources, filter);
6387
6626
  let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => m.score >= scoreThreshold);
6388
6627
  const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
6389
- if (!hasNumericPredicates && ((_k = this.config.rag) == null ? void 0 : _k.useReranking)) {
6628
+ if (!hasNumericPredicates && ((_l = this.config.rag) == null ? void 0 : _l.useReranking)) {
6390
6629
  fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
6391
6630
  } else if (!wantsExhaustiveList) {
6392
6631
  fullSources = fullSources.slice(0, topK);
@@ -6417,34 +6656,117 @@ VECTOR CONTEXT:
6417
6656
  ${context}`;
6418
6657
  }
6419
6658
  const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
6420
- const trainedSchemaPromise = allMetadataKeys.length > 0 ? SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => void 0) : Promise.resolve(void 0);
6421
- const uiTransformationPromise = trainedSchemaPromise.then(
6422
- (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(
6423
6667
  question,
6424
6668
  sources,
6425
- trainedSchema,
6426
- hasNumericPredicates
6669
+ this.config,
6670
+ cachedSchema,
6671
+ { visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
6427
6672
  )
6673
+ ) : this.generateUiTransformation(
6674
+ question,
6675
+ sources,
6676
+ cachedSchema,
6677
+ hasNumericPredicates
6428
6678
  ).catch((uiError) => {
6429
6679
  console.warn("[Pipeline] UI transformation failed concurrently:", uiError);
6430
- return UITransformer.transform(question, sources, this.config);
6680
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6431
6681
  });
6682
+ let hallucinationScoringPromise = Promise.resolve(void 0);
6432
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
+ }
6433
6693
  const hardenedHistory = [...history];
6434
- const userQuestion = { role: "user", content: question + restrictionSuffix };
6694
+ const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
6435
6695
  const messages = [...hardenedHistory, userQuestion];
6436
- const systemPrompt = (_l = this.config.llm.systemPrompt) != null ? _l : "";
6696
+ const systemPrompt = (_s = this.config.llm.systemPrompt) != null ? _s : "";
6437
6697
  const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
6438
6698
  let fullReply = "";
6699
+ let thinkingText = "";
6700
+ let thinkingStartMs = performance.now();
6701
+ let thinkingDurationMs = 0;
6439
6702
  const generateStart = performance.now();
6440
6703
  if (this.llmProvider.chatStream) {
6441
6704
  const stream = this.llmProvider.chatStream(messages, context);
6442
6705
  if (!stream) throw new Error(`[Pipeline] ${this.config.llm.provider} chatStream returned undefined`);
6706
+ let inThinking = false;
6707
+ let textBuffer = "";
6443
6708
  try {
6444
6709
  for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
6445
6710
  const chunk = temp.value;
6446
- fullReply += chunk;
6447
- 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
+ }
6448
6770
  }
6449
6771
  } catch (temp) {
6450
6772
  error = [temp];
@@ -6456,17 +6778,42 @@ ${context}`;
6456
6778
  throw error[0];
6457
6779
  }
6458
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
+ }
6459
6790
  } else {
6460
6791
  const reply = yield new __await(this.llmProvider.chat(messages, context));
6461
- fullReply = reply;
6462
- 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);
6463
6810
  }
6464
6811
  const generateMs = performance.now() - generateStart;
6465
6812
  const totalMs = performance.now() - requestStart;
6466
6813
  const latency = {
6467
6814
  embedMs: Math.round(embedMs),
6468
6815
  retrieveMs: Math.round(retrieveMs),
6469
- 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,
6470
6817
  generateMs: Math.round(generateMs),
6471
6818
  totalMs: Math.round(totalMs)
6472
6819
  };
@@ -6479,12 +6826,16 @@ ${context}`;
6479
6826
  totalTokens: promptTokens + completionTokens,
6480
6827
  estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
6481
6828
  };
6482
- const trace = {
6829
+ const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
6830
+ uiTransformationPromise,
6831
+ hallucinationScoringPromise
6832
+ ]));
6833
+ const trace = __spreadValues({
6483
6834
  requestId,
6484
6835
  query: question,
6485
6836
  rewrittenQuery,
6486
6837
  systemPrompt,
6487
- userPrompt: question + restrictionSuffix,
6838
+ userPrompt: question + finalRestrictionSuffix,
6488
6839
  chunks: sources.map((s) => {
6489
6840
  var _a2;
6490
6841
  return {
@@ -6498,22 +6849,19 @@ ${context}`;
6498
6849
  latency,
6499
6850
  tokens,
6500
6851
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6501
- };
6502
- const uiTransformation = yield new __await(uiTransformationPromise);
6852
+ }, hallucinationResult ? {
6853
+ hallucinationScore: hallucinationResult.score,
6854
+ hallucinationReason: hallucinationResult.reason
6855
+ } : {});
6503
6856
  yield {
6504
6857
  reply: "",
6505
6858
  sources,
6506
6859
  graphData,
6507
6860
  ui_transformation: uiTransformation,
6508
- trace
6861
+ trace,
6862
+ thinking: thinkingText || void 0,
6863
+ thinkingMs: thinkingDurationMs > 0 ? Math.round(thinkingDurationMs) : void 0
6509
6864
  };
6510
- scoreHallucination(this.llmProvider, fullReply, context).then((hallucinationResult) => {
6511
- if (hallucinationResult) {
6512
- trace.hallucinationScore = hallucinationResult.score;
6513
- trace.hallucinationReason = hallucinationResult.reason;
6514
- }
6515
- }).catch(() => {
6516
- });
6517
6865
  } catch (error2) {
6518
6866
  throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
6519
6867
  }
@@ -6523,18 +6871,30 @@ ${context}`;
6523
6871
  * Universal retrieval method combining all enabled providers.
6524
6872
  * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
6525
6873
  */
6526
- async generateUiTransformation(question, sources, trainedSchema, forceDeterministic = false) {
6874
+ async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
6875
+ var _a;
6527
6876
  if (!sources || sources.length === 0) {
6528
- return UITransformer.transform(question, sources, this.config, trainedSchema);
6877
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6529
6878
  }
6530
- if (forceDeterministic) {
6531
- 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);
6532
6892
  }
6533
6893
  try {
6534
6894
  return await UITransformer.analyzeAndDecide(question, sources, this.llmRouter.get("fast"));
6535
6895
  } catch (err) {
6536
6896
  console.warn("[Pipeline] generateUiTransformation failed, using heuristic fallback:", err);
6537
- return UITransformer.transform(question, sources, this.config, trainedSchema);
6897
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6538
6898
  }
6539
6899
  }
6540
6900
  applyStructuredFilters(sources, filter) {
@@ -6618,25 +6978,29 @@ ${context}`;
6618
6978
  return Number.isFinite(numeric) ? numeric : null;
6619
6979
  }
6620
6980
  async retrieve(query, options) {
6621
- var _a, _b, _c, _d, _e, _f;
6981
+ var _a, _b, _c, _d, _e, _f, _g;
6622
6982
  const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
6623
6983
  const topK = (_b = options.topK) != null ? _b : 5;
6624
6984
  const cacheKey = `${ns}::${query}`;
6625
6985
  let queryVector = this.embeddingCache.get(cacheKey);
6626
- 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
+ );
6627
6991
  console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
6628
6992
  const [retrievedVector, graphData] = await Promise.all([
6629
6993
  // Only embed if we need vector search (strategy is 'vector' or 'both')
6630
6994
  strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
6631
6995
  // Only query graph if we need graph search (strategy is 'graph' or 'both')
6632
- (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)
6633
6997
  ]);
6634
6998
  if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
6635
6999
  this.embeddingCache.set(cacheKey, retrievedVector);
6636
7000
  queryVector = retrievedVector;
6637
7001
  }
6638
- const baseFilter = __spreadProps(__spreadValues({}, (_d = options.filter) != null ? _d : {}), { queryText: query });
6639
- 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);
6640
7004
  if (numericPredicates.length > 0) {
6641
7005
  baseFilter.__numericPredicates = numericPredicates;
6642
7006
  }
@@ -6647,7 +7011,7 @@ ${context}`;
6647
7011
  ) : [];
6648
7012
  const resolvedSources = [];
6649
7013
  for (const source of sources) {
6650
- const parentId = (_f = source.metadata) == null ? void 0 : _f.parent_id;
7014
+ const parentId = (_g = source.metadata) == null ? void 0 : _g.parent_id;
6651
7015
  if (parentId) {
6652
7016
  console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
6653
7017
  resolvedSources.push(source);
@@ -6670,10 +7034,13 @@ New Question: ${question}
6670
7034
  Optimized Search Query:`;
6671
7035
  const rewrite = await this.llmProvider.chat(
6672
7036
  [
6673
- { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
6674
7037
  { role: "user", content: prompt }
6675
7038
  ],
6676
- ""
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
+ }
6677
7044
  );
6678
7045
  return rewrite.trim() || question;
6679
7046
  }
@@ -6697,10 +7064,13 @@ ${context}
6697
7064
  Suggestions:`;
6698
7065
  const response = await this.llmProvider.chat(
6699
7066
  [
6700
- { role: "system", content: "You are a helpful assistant that generates search suggestions." },
6701
7067
  { role: "user", content: prompt }
6702
7068
  ],
6703
- ""
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
+ }
6704
7074
  );
6705
7075
  const match = response.match(/\[[\s\S]*\]/);
6706
7076
  if (match) {
@@ -6885,6 +7255,30 @@ var VectorPlugin = class {
6885
7255
  }
6886
7256
  };
6887
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
+
6888
7282
  // src/config/ConfigBuilder.ts
6889
7283
  var ConfigBuilder = class {
6890
7284
  /**
@@ -7253,6 +7647,10 @@ function createStreamHandler(configOrPlugin) {
7253
7647
  if (!isActive) break;
7254
7648
  if (typeof chunk === "string") {
7255
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
+ `);
7256
7654
  } else {
7257
7655
  enqueue(sseMetaFrame(chunk));
7258
7656
  const responseChunk = chunk;
@@ -7426,14 +7824,17 @@ function createUploadHandler(configOrPlugin) {
7426
7824
  }
7427
7825
  export {
7428
7826
  AnthropicProvider,
7827
+ AuthenticationException,
7429
7828
  BaseVectorProvider,
7430
7829
  BatchProcessor,
7431
7830
  ChromaDBProvider,
7432
7831
  ConfigBuilder,
7433
7832
  ConfigResolver,
7434
7833
  ConfigValidator,
7834
+ ConfigurationException,
7435
7835
  DocumentChunker,
7436
7836
  DocumentParser,
7837
+ EmbeddingFailedException,
7437
7838
  EmbeddingStrategy,
7438
7839
  EmbeddingStrategyResolver,
7439
7840
  LLMFactory,
@@ -7448,9 +7849,14 @@ export {
7448
7849
  Pipeline,
7449
7850
  PostgreSQLProvider,
7450
7851
  ProviderHealthCheck,
7852
+ ProviderNotFoundException,
7451
7853
  ProviderRegistry,
7452
7854
  QdrantProvider,
7855
+ RateLimitException,
7453
7856
  RedisProvider,
7857
+ RetrievalException,
7858
+ Retrivora,
7859
+ RetrivoraError,
7454
7860
  UniversalLLMAdapter,
7455
7861
  UniversalVectorProvider,
7456
7862
  VECTOR_PROFILES,