@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.js CHANGED
@@ -661,6 +661,11 @@ var init_MultiTablePostgresProvider = __esm({
661
661
  CREATE INDEX IF NOT EXISTS ${this.uploadTable}_embedding_idx
662
662
  ON ${this.uploadTable}
663
663
  USING hnsw (embedding vector_cosine_ops)
664
+ `);
665
+ await client.query(`
666
+ CREATE INDEX IF NOT EXISTS "${this.uploadTable}_fts_idx"
667
+ ON "${this.uploadTable}"
668
+ USING gin (to_tsvector('english', content))
664
669
  `);
665
670
  const res = await client.query(`
666
671
  SELECT table_name
@@ -731,6 +736,11 @@ var init_MultiTablePostgresProvider = __esm({
731
736
  CREATE INDEX IF NOT EXISTS "${tableName}_embedding_idx"
732
737
  ON "${tableName}"
733
738
  USING hnsw (embedding vector_cosine_ops)
739
+ `);
740
+ await client.query(`
741
+ CREATE INDEX IF NOT EXISTS "${tableName}_fts_idx"
742
+ ON "${tableName}"
743
+ USING gin (to_tsvector('english', content))
734
744
  `);
735
745
  if (!this.tables.includes(tableName)) {
736
746
  this.tables.push(tableName);
@@ -822,8 +832,11 @@ var init_MultiTablePostgresProvider = __esm({
822
832
  const paramIdx = baseOffset + filterParams.length;
823
833
  const synonyms = (_a3 = FIELD_SYNONYMS[key]) != null ? _a3 : [];
824
834
  const keysToCheck = [key, ...synonyms];
825
- const coalesceExprs = keysToCheck.map((k) => `LOWER(metadata->>'${k}')`);
826
- return `COALESCE(${coalesceExprs.join(", ")}) = LOWER($${paramIdx})`;
835
+ const coalesceExprs = keysToCheck.flatMap((k) => [
836
+ `metadata->>'${k}'`,
837
+ `to_jsonb(t)->>'${k}'`
838
+ ]);
839
+ return `COALESCE(${coalesceExprs.map((expr) => `LOWER(${expr})`).join(", ")}) = LOWER($${paramIdx})`;
827
840
  });
828
841
  whereClause = `WHERE ${conditions.join(" AND ")}`;
829
842
  }
@@ -834,16 +847,35 @@ var init_MultiTablePostgresProvider = __esm({
834
847
  CASE WHEN LOWER(val) IN (${entityHints.map((h) => `'${h.replace(/'/g, "''")}'`).join(", ")})
835
848
  THEN 1.0 ELSE 0.0 END
836
849
  ), 0)
837
- FROM jsonb_each_text(to_jsonb(t)) AS kv(key, val)
850
+ FROM jsonb_each_text(COALESCE(v.metadata, k.metadata)) AS kv(key, val)
838
851
  WHERE key IN (${this.searchFields.map((f) => `'${f}'`).join(", ")})
839
852
  ) * 3.0` : "";
840
853
  sqlQuery = `
841
- SELECT *,
842
- (1 - (embedding <=> $1::vector)) AS vector_score,
843
- COALESCE(ts_rank(to_tsvector('english', (to_jsonb(t) - 'embedding')::text), to_tsquery('english', $2)), 0) AS keyword_score,
844
- ((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
845
- FROM "${table}" t
846
- ${whereClause}
854
+ WITH vector_search AS (
855
+ SELECT *, (1 - (embedding <=> $1::vector)) AS vector_score
856
+ FROM "${table}" t
857
+ ${whereClause}
858
+ ORDER BY embedding <=> $1::vector ASC
859
+ LIMIT ${tableLimit}
860
+ ),
861
+ keyword_search AS (
862
+ SELECT *, COALESCE(ts_rank(to_tsvector('english', content), to_tsquery('english', $2)), 0) AS keyword_score
863
+ FROM "${table}" t
864
+ WHERE ${whereClause ? whereClause.replace("WHERE", "") : "TRUE"}
865
+ AND to_tsvector('english', content) @@ to_tsquery('english', $2)
866
+ ORDER BY keyword_score DESC
867
+ LIMIT ${tableLimit}
868
+ )
869
+ SELECT
870
+ COALESCE(v.id, k.id) AS id,
871
+ COALESCE(v.namespace, k.namespace) AS namespace,
872
+ COALESCE(v.content, k.content) AS content,
873
+ COALESCE(v.metadata, k.metadata) AS metadata,
874
+ COALESCE(v.vector_score, 0) AS vector_score,
875
+ COALESCE(k.keyword_score, 0) AS keyword_score,
876
+ (COALESCE(v.vector_score, 0) + COALESCE(k.keyword_score, 0) * 2.0 ${exactNameScoreExpr}) AS hybrid_score
877
+ FROM vector_search v
878
+ FULL OUTER JOIN keyword_search k ON v.id = k.id
847
879
  ORDER BY hybrid_score DESC
848
880
  LIMIT ${tableLimit}
849
881
  `;
@@ -854,7 +886,7 @@ var init_MultiTablePostgresProvider = __esm({
854
886
  (1 - (embedding <=> $1::vector)) AS hybrid_score
855
887
  FROM "${table}" t
856
888
  ${whereClause}
857
- ORDER BY hybrid_score DESC
889
+ ORDER BY embedding <=> $1::vector ASC
858
890
  LIMIT ${tableLimit}
859
891
  `;
860
892
  params = [vectorLiteral, ...filterParams];
@@ -1060,7 +1092,11 @@ var init_MongoDBProvider = __esm({
1060
1092
  if (key === "namespace") {
1061
1093
  vectorSearchFilter.namespace = value;
1062
1094
  } else {
1063
- matchFilter[key] = value;
1095
+ if (typeof value === "string") {
1096
+ matchFilter[key] = { $regex: new RegExp(`^${value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "i") };
1097
+ } else {
1098
+ matchFilter[key] = value;
1099
+ }
1064
1100
  }
1065
1101
  }
1066
1102
  const pipeline = [
@@ -2066,14 +2102,17 @@ var init_SimpleGraphProvider = __esm({
2066
2102
  var server_exports = {};
2067
2103
  __export(server_exports, {
2068
2104
  AnthropicProvider: () => AnthropicProvider,
2105
+ AuthenticationException: () => AuthenticationException,
2069
2106
  BaseVectorProvider: () => BaseVectorProvider,
2070
2107
  BatchProcessor: () => BatchProcessor,
2071
2108
  ChromaDBProvider: () => ChromaDBProvider,
2072
2109
  ConfigBuilder: () => ConfigBuilder,
2073
2110
  ConfigResolver: () => ConfigResolver,
2074
2111
  ConfigValidator: () => ConfigValidator,
2112
+ ConfigurationException: () => ConfigurationException,
2075
2113
  DocumentChunker: () => DocumentChunker,
2076
2114
  DocumentParser: () => DocumentParser,
2115
+ EmbeddingFailedException: () => EmbeddingFailedException,
2077
2116
  EmbeddingStrategy: () => EmbeddingStrategy,
2078
2117
  EmbeddingStrategyResolver: () => EmbeddingStrategyResolver,
2079
2118
  LLMFactory: () => LLMFactory,
@@ -2088,9 +2127,14 @@ __export(server_exports, {
2088
2127
  Pipeline: () => Pipeline,
2089
2128
  PostgreSQLProvider: () => PostgreSQLProvider,
2090
2129
  ProviderHealthCheck: () => ProviderHealthCheck,
2130
+ ProviderNotFoundException: () => ProviderNotFoundException,
2091
2131
  ProviderRegistry: () => ProviderRegistry,
2092
2132
  QdrantProvider: () => QdrantProvider,
2133
+ RateLimitException: () => RateLimitException,
2093
2134
  RedisProvider: () => RedisProvider,
2135
+ RetrievalException: () => RetrievalException,
2136
+ Retrivora: () => Retrivora,
2137
+ RetrivoraError: () => RetrivoraError,
2094
2138
  UniversalLLMAdapter: () => UniversalLLMAdapter,
2095
2139
  UniversalVectorProvider: () => UniversalVectorProvider,
2096
2140
  VECTOR_PROFILES: () => VECTOR_PROFILES,
@@ -2181,7 +2225,7 @@ function getRagConfig(baseConfig, env = process.env) {
2181
2225
  return getEnvConfig(env, baseConfig);
2182
2226
  }
2183
2227
  function getEnvConfig(env = process.env, base) {
2184
- 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;
2228
+ 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;
2185
2229
  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__";
2186
2230
  const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
2187
2231
  const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
@@ -2251,7 +2295,7 @@ function getEnvConfig(env = process.env, base) {
2251
2295
  rest: "default",
2252
2296
  custom: "default"
2253
2297
  };
2254
- return {
2298
+ return __spreadValues({
2255
2299
  projectId,
2256
2300
  vectorDb: {
2257
2301
  provider: vectorProvider,
@@ -2267,7 +2311,9 @@ function getEnvConfig(env = process.env, base) {
2267
2311
  maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
2268
2312
  temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
2269
2313
  options: {
2270
- profile: readString(env, "LLM_UNIVERSAL_PROFILE")
2314
+ profile: readString(env, "LLM_UNIVERSAL_PROFILE"),
2315
+ thinking: readString(env, "LLM_THINKING") === "true" || readString(env, "LLM_THINKING") === "enabled" ? true : readString(env, "LLM_THINKING") === "false" ? false : void 0,
2316
+ thinkingBudget: env.LLM_THINKING_BUDGET ? parseInt(env.LLM_THINKING_BUDGET, 10) : void 0
2271
2317
  }
2272
2318
  },
2273
2319
  embedding: {
@@ -2299,11 +2345,76 @@ function getEnvConfig(env = process.env, base) {
2299
2345
  topK: readNumber(env, "RAG_TOP_K", 5),
2300
2346
  scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
2301
2347
  chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
2302
- chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200)
2348
+ chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
2349
+ filterableFields: (_Ha = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Ha.split(",").map((f) => f.trim()),
2350
+ // Query pipeline toggles — read from .env.local
2351
+ useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
2352
+ useReranking: readString(env, "RAG_USE_RERANKING") === "true",
2353
+ useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
2354
+ architecture: (_Ia = readString(env, "RAG_ARCHITECTURE")) != null ? _Ia : "simple",
2355
+ chunkingStrategy: (_Ja = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _Ja : "recursive",
2356
+ uiMapping: (() => {
2357
+ const raw = readString(env, "RAG_UI_MAPPING");
2358
+ if (!raw) return void 0;
2359
+ try {
2360
+ return JSON.parse(raw);
2361
+ } catch (e) {
2362
+ return void 0;
2363
+ }
2364
+ })()
2303
2365
  }
2304
- };
2366
+ }, readString(env, "GRAPH_DB_PROVIDER") ? {
2367
+ graphDb: {
2368
+ provider: readString(env, "GRAPH_DB_PROVIDER"),
2369
+ options: {
2370
+ uri: readString(env, "GRAPH_DB_URI"),
2371
+ username: readString(env, "GRAPH_DB_USERNAME"),
2372
+ password: readString(env, "GRAPH_DB_PASSWORD")
2373
+ }
2374
+ }
2375
+ } : {});
2305
2376
  }
2306
2377
 
2378
+ // src/exceptions/index.ts
2379
+ var RetrivoraError = class extends Error {
2380
+ constructor(message, code, details) {
2381
+ super(message);
2382
+ this.name = new.target.name;
2383
+ this.code = code;
2384
+ this.details = details;
2385
+ }
2386
+ };
2387
+ var ProviderNotFoundException = class extends RetrivoraError {
2388
+ constructor(providerType, provider, details) {
2389
+ super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
2390
+ }
2391
+ };
2392
+ var EmbeddingFailedException = class extends RetrivoraError {
2393
+ constructor(message = "Embedding generation failed", details) {
2394
+ super(message, "EMBEDDING_FAILED", details);
2395
+ }
2396
+ };
2397
+ var RetrievalException = class extends RetrivoraError {
2398
+ constructor(message = "Retrieval failed", details) {
2399
+ super(message, "RETRIEVAL_FAILED", details);
2400
+ }
2401
+ };
2402
+ var RateLimitException = class extends RetrivoraError {
2403
+ constructor(message = "Provider rate limit exceeded", details) {
2404
+ super(message, "RATE_LIMITED", details);
2405
+ }
2406
+ };
2407
+ var ConfigurationException = class extends RetrivoraError {
2408
+ constructor(message, details) {
2409
+ super(message, "CONFIGURATION_ERROR", details);
2410
+ }
2411
+ };
2412
+ var AuthenticationException = class extends RetrivoraError {
2413
+ constructor(message = "Provider authentication failed", details) {
2414
+ super(message, "AUTHENTICATION_ERROR", details);
2415
+ }
2416
+ };
2417
+
2307
2418
  // src/core/ConfigResolver.ts
2308
2419
  var ConfigResolver = class {
2309
2420
  /**
@@ -2330,24 +2441,81 @@ var ConfigResolver = class {
2330
2441
  rag: hostConfig.rag ? __spreadValues(__spreadValues({}, envConfig.rag), hostConfig.rag) : envConfig.rag
2331
2442
  });
2332
2443
  }
2444
+ /**
2445
+ * Resolves the public SDK config shape used by `new Retrivora({...})`.
2446
+ * Supports aliases from the product prompt while preserving existing env
2447
+ * fallback behavior.
2448
+ */
2449
+ static resolveUniversal(hostConfig, env = process.env) {
2450
+ var _a;
2451
+ if (!hostConfig) return this.resolve(void 0, env);
2452
+ const normalized = __spreadProps(__spreadValues({}, hostConfig), {
2453
+ vectorDb: (_a = hostConfig.vectorDb) != null ? _a : hostConfig.vectorDatabase,
2454
+ rag: this.mergeRetrievalWorkflow(hostConfig.rag, hostConfig.retrieval, hostConfig.workflow)
2455
+ });
2456
+ return this.resolve(normalized, env);
2457
+ }
2333
2458
  /**
2334
2459
  * Validates the configuration for required fields.
2335
2460
  */
2336
2461
  static validate(config) {
2337
2462
  if (!config.projectId) {
2338
- throw new Error("[ConfigResolver] projectId is required");
2463
+ throw new ConfigurationException("[ConfigResolver] projectId is required");
2339
2464
  }
2340
2465
  if (!config.vectorDb.provider) {
2341
- throw new Error("[ConfigResolver] vectorDb.provider is required");
2466
+ throw new ConfigurationException("[ConfigResolver] vectorDb.provider is required");
2342
2467
  }
2343
2468
  if (!config.llm.provider) {
2344
- throw new Error("[ConfigResolver] llm.provider is required");
2469
+ throw new ConfigurationException("[ConfigResolver] llm.provider is required");
2345
2470
  }
2346
2471
  }
2472
+ static mergeRetrievalWorkflow(rag, retrieval, workflow) {
2473
+ var _a, _b, _c, _d, _e;
2474
+ if (!rag && !retrieval && !workflow) return void 0;
2475
+ const normalized = __spreadValues({}, rag != null ? rag : {});
2476
+ if (retrieval) {
2477
+ normalized.topK = (_a = retrieval.topK) != null ? _a : normalized.topK;
2478
+ normalized.scoreThreshold = (_b = retrieval.scoreThreshold) != null ? _b : normalized.scoreThreshold;
2479
+ normalized.useReranking = (_c = retrieval.useReranking) != null ? _c : normalized.useReranking;
2480
+ if (retrieval.strategy === "hybrid") normalized.architecture = (_d = normalized.architecture) != null ? _d : "hybrid";
2481
+ if (retrieval.strategy === "agentic") normalized.architecture = "agentic";
2482
+ if (retrieval.strategy === "contextual-compression") normalized.useReranking = true;
2483
+ }
2484
+ if (workflow == null ? void 0 : workflow.type) {
2485
+ const type = workflow.type;
2486
+ if (type === "agentic" || type === "agentic-rag") normalized.architecture = "agentic";
2487
+ else if (type === "hybrid-rag") normalized.architecture = "hybrid";
2488
+ else if (type === "graph-rag") {
2489
+ normalized.architecture = "graph";
2490
+ normalized.useGraphRetrieval = true;
2491
+ } else if (type === "simple-rag" || type === "rag") {
2492
+ normalized.architecture = (_e = normalized.architecture) != null ? _e : "simple";
2493
+ }
2494
+ }
2495
+ return normalized;
2496
+ }
2347
2497
  };
2348
2498
 
2349
2499
  // src/llm/providers/OpenAIProvider.ts
2350
2500
  var import_openai = __toESM(require("openai"));
2501
+
2502
+ // src/llm/utils.ts
2503
+ function buildSystemContent(systemPrompt, context) {
2504
+ const noContext = !context || context.trim() === "" || context.trim() === "No relevant context found.";
2505
+ if (systemPrompt.includes("{{context}}")) {
2506
+ return systemPrompt.replace("{{context}}", noContext ? "" : context);
2507
+ }
2508
+ if (noContext) {
2509
+ return systemPrompt;
2510
+ }
2511
+ return `${systemPrompt}
2512
+
2513
+ ---
2514
+ Context:
2515
+ ${context}`;
2516
+ }
2517
+
2518
+ // src/llm/providers/OpenAIProvider.ts
2351
2519
  var OpenAIProvider = class {
2352
2520
  constructor(llmConfig, embeddingConfig) {
2353
2521
  if (!llmConfig.apiKey) throw new Error("[OpenAIProvider] llmConfig.apiKey is required");
@@ -2411,16 +2579,10 @@ var OpenAIProvider = class {
2411
2579
  }
2412
2580
  async chat(messages, context, options) {
2413
2581
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2414
- 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.
2415
-
2416
- Context:
2417
- ${context}`;
2582
+ 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.";
2418
2583
  const systemMessage = {
2419
2584
  role: "system",
2420
- content: resolvedSystemPrompt.includes("{{context}}") ? resolvedSystemPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedSystemPrompt : `${resolvedSystemPrompt}
2421
-
2422
- Context:
2423
- ${context}`
2585
+ content: buildSystemContent(basePrompt, context)
2424
2586
  };
2425
2587
  const formattedMessages = [
2426
2588
  systemMessage,
@@ -2441,16 +2603,10 @@ ${context}`
2441
2603
  chatStream(messages, context, options) {
2442
2604
  return __asyncGenerator(this, null, function* () {
2443
2605
  var _a, _b, _c, _d, _e, _f, _g, _h;
2444
- 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.
2445
-
2446
- Context:
2447
- ${context}`;
2606
+ 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.";
2448
2607
  const systemMessage = {
2449
2608
  role: "system",
2450
- content: resolvedSystemPrompt.includes("{{context}}") ? resolvedSystemPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedSystemPrompt : `${resolvedSystemPrompt}
2451
-
2452
- Context:
2453
- ${context}`
2609
+ content: buildSystemContent(basePrompt, context)
2454
2610
  };
2455
2611
  const formattedMessages = [
2456
2612
  systemMessage,
@@ -2571,58 +2727,87 @@ var AnthropicProvider = class {
2571
2727
  };
2572
2728
  }
2573
2729
  async chat(messages, context, options) {
2574
- var _a, _b, _c, _d;
2575
- 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.
2576
-
2577
- Context:
2578
- ${context}`;
2579
- const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
2580
-
2581
- Context:
2582
- ${context}`;
2730
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2731
+ 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.";
2732
+ const system = buildSystemContent(basePrompt, context);
2583
2733
  const anthropicMessages = messages.map((m) => ({
2584
2734
  role: m.role === "assistant" ? "assistant" : "user",
2585
2735
  content: m.content
2586
2736
  }));
2587
- const response = await this.client.messages.create({
2737
+ const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
2738
+ 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;
2739
+ const extraParams = {};
2740
+ if (isThinkingEnabled && isClaude37) {
2741
+ extraParams.betas = ["interleaved-thinking-2025-05-14"];
2742
+ const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
2743
+ const budget = Math.min(
2744
+ typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
2745
+ maxTokens - 1
2746
+ );
2747
+ extraParams.thinking = {
2748
+ type: "enabled",
2749
+ budget_tokens: budget
2750
+ };
2751
+ extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
2752
+ }
2753
+ const response = await this.client.messages.create(__spreadValues({
2588
2754
  model: this.llmConfig.model,
2589
- max_tokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
2755
+ max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
2590
2756
  system,
2591
2757
  messages: anthropicMessages
2592
- });
2593
- const block = response.content[0];
2594
- return block.type === "text" ? block.text : "";
2758
+ }, extraParams));
2759
+ let reply = "";
2760
+ for (const block of response.content) {
2761
+ if (block.type === "text") {
2762
+ reply += block.text;
2763
+ }
2764
+ }
2765
+ return reply;
2595
2766
  }
2596
2767
  chatStream(messages, context, options) {
2597
2768
  return __asyncGenerator(this, null, function* () {
2598
- var _a, _b, _c, _d;
2599
- 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.
2600
-
2601
- Context:
2602
- ${context}`;
2603
- const system = resolvedPrompt.includes("{{context}}") ? resolvedPrompt.replace("{{context}}", context) : (options == null ? void 0 : options.systemPrompt) ? resolvedPrompt : `${resolvedPrompt}
2604
-
2605
- Context:
2606
- ${context}`;
2769
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2770
+ 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.";
2771
+ const system = buildSystemContent(basePrompt, context);
2607
2772
  const anthropicMessages = messages.map((m) => ({
2608
2773
  role: m.role === "assistant" ? "assistant" : "user",
2609
2774
  content: m.content
2610
2775
  }));
2611
- const stream = yield new __await(this.client.messages.create({
2776
+ const isClaude37 = this.llmConfig.model.includes("claude-3-7-sonnet");
2777
+ 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;
2778
+ const extraParams = {};
2779
+ if (isThinkingEnabled && isClaude37) {
2780
+ extraParams.betas = ["interleaved-thinking-2025-05-14"];
2781
+ const maxTokens = (_g = (_f = options == null ? void 0 : options.maxTokens) != null ? _f : this.llmConfig.maxTokens) != null ? _g : 4096;
2782
+ const budget = Math.min(
2783
+ typeof ((_h = this.llmConfig.options) == null ? void 0 : _h.thinkingBudget) === "number" ? (_i = this.llmConfig.options) == null ? void 0 : _i.thinkingBudget : 2048,
2784
+ maxTokens - 1
2785
+ );
2786
+ extraParams.thinking = {
2787
+ type: "enabled",
2788
+ budget_tokens: budget
2789
+ };
2790
+ extraParams.max_tokens = Math.max(maxTokens, budget + 1024);
2791
+ }
2792
+ const stream = yield new __await(this.client.messages.create(__spreadValues({
2612
2793
  model: this.llmConfig.model,
2613
- max_tokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024,
2794
+ max_tokens: (_k = (_j = options == null ? void 0 : options.maxTokens) != null ? _j : this.llmConfig.maxTokens) != null ? _k : 1024,
2614
2795
  system,
2615
2796
  messages: anthropicMessages,
2616
2797
  stream: true
2617
- }));
2798
+ }, extraParams)));
2618
2799
  if (!stream) {
2619
2800
  throw new Error("[AnthropicProvider] messages.create stream is undefined");
2620
2801
  }
2621
2802
  try {
2622
2803
  for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
2623
2804
  const chunk = temp.value;
2624
- if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
2625
- yield chunk.delta.text;
2805
+ if (chunk.type === "content_block_delta") {
2806
+ if (chunk.delta.type === "text_delta") {
2807
+ yield chunk.delta.text;
2808
+ } else if (chunk.delta.type === "thinking_delta") {
2809
+ yield `\0THINKING\0${chunk.delta.thinking}`;
2810
+ }
2626
2811
  }
2627
2812
  }
2628
2813
  } catch (temp) {
@@ -2666,9 +2851,10 @@ ${context}`;
2666
2851
  var import_axios = __toESM(require("axios"));
2667
2852
  var OllamaProvider = class {
2668
2853
  constructor(llmConfig, embeddingConfig) {
2669
- var _a;
2854
+ var _a, _b;
2670
2855
  const baseURL = (_a = llmConfig.baseUrl) != null ? _a : "http://localhost:11434";
2671
- this.http = import_axios.default.create({ baseURL, timeout: 12e4 });
2856
+ const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
2857
+ this.http = import_axios.default.create({ baseURL, timeout });
2672
2858
  this.llmConfig = llmConfig;
2673
2859
  this.embeddingConfig = embeddingConfig;
2674
2860
  }
@@ -2724,14 +2910,15 @@ var OllamaProvider = class {
2724
2910
  };
2725
2911
  }
2726
2912
  async chat(messages, context, options) {
2727
- var _a, _b, _c, _d;
2728
- const system = this.buildSystemPrompt(context, options == null ? void 0 : options.systemPrompt);
2913
+ var _a, _b, _c, _d, _e, _f;
2914
+ 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.";
2915
+ const system = buildSystemContent(basePrompt, context);
2729
2916
  const { data } = await this.http.post("/api/chat", {
2730
2917
  model: this.llmConfig.model,
2731
2918
  stream: false,
2732
2919
  options: {
2733
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
2734
- num_predict: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024
2920
+ temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
2921
+ num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
2735
2922
  },
2736
2923
  messages: [
2737
2924
  { role: "system", content: system },
@@ -2742,14 +2929,15 @@ var OllamaProvider = class {
2742
2929
  }
2743
2930
  chatStream(messages, context, options) {
2744
2931
  return __asyncGenerator(this, null, function* () {
2745
- var _a, _b, _c, _d, _e, _f;
2746
- const system = this.buildSystemPrompt(context, options == null ? void 0 : options.systemPrompt);
2932
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2933
+ 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.";
2934
+ const system = buildSystemContent(basePrompt, context);
2747
2935
  const response = yield new __await(this.http.post("/api/chat", {
2748
2936
  model: this.llmConfig.model,
2749
2937
  stream: true,
2750
2938
  options: {
2751
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : 0.7,
2752
- num_predict: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : 1024
2939
+ temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : 0.7,
2940
+ num_predict: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : 1024
2753
2941
  },
2754
2942
  messages: [
2755
2943
  { role: "system", content: system },
@@ -2771,7 +2959,7 @@ var OllamaProvider = class {
2771
2959
  if (!line.trim()) continue;
2772
2960
  try {
2773
2961
  const json = JSON.parse(line);
2774
- if ((_e = json.message) == null ? void 0 : _e.content) {
2962
+ if ((_g = json.message) == null ? void 0 : _g.content) {
2775
2963
  yield json.message.content;
2776
2964
  }
2777
2965
  if (json.done) return;
@@ -2793,26 +2981,12 @@ var OllamaProvider = class {
2793
2981
  if (lineBuffer.trim()) {
2794
2982
  try {
2795
2983
  const json = JSON.parse(lineBuffer);
2796
- if ((_f = json.message) == null ? void 0 : _f.content) yield json.message.content;
2984
+ if ((_h = json.message) == null ? void 0 : _h.content) yield json.message.content;
2797
2985
  } catch (e) {
2798
2986
  }
2799
2987
  }
2800
2988
  });
2801
2989
  }
2802
- buildSystemPrompt(context, overridePrompt) {
2803
- var _a;
2804
- if (overridePrompt) {
2805
- return overridePrompt;
2806
- }
2807
- const systemPrompt = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Use the provided context to answer the user's question.
2808
-
2809
- Context:
2810
- ${context}`;
2811
- return systemPrompt.includes("{{context}}") ? systemPrompt.replace("{{context}}", context) : `${systemPrompt}
2812
-
2813
- Context:
2814
- ${context}`;
2815
- }
2816
2990
  async embed(text, options) {
2817
2991
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2818
2992
  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";
@@ -2959,21 +3133,6 @@ var GeminiProvider = class {
2959
3133
  var _a, _b;
2960
3134
  return sanitizeModel((_b = optionsModel != null ? optionsModel : (_a = this.embeddingConfig) == null ? void 0 : _a.model) != null ? _b : DEFAULT_EMBEDDING_MODEL);
2961
3135
  }
2962
- /**
2963
- * Build the system instruction string, inserting the RAG context either via
2964
- * the `{{context}}` placeholder or by appending it.
2965
- */
2966
- buildSystemInstruction(context) {
2967
- var _a;
2968
- const base = (_a = this.llmConfig.systemPrompt) != null ? _a : `You are a helpful assistant. Answer questions based on the provided context.
2969
-
2970
- Context:
2971
- ${context}`;
2972
- return base.includes("{{context}}") ? base.replace("{{context}}", context) : `${base}
2973
-
2974
- Context:
2975
- ${context}`;
2976
- }
2977
3136
  /**
2978
3137
  * Convert ChatMessage[] to the Gemini contents format.
2979
3138
  *
@@ -2992,16 +3151,17 @@ ${context}`;
2992
3151
  // ILLMProvider — chat
2993
3152
  // -------------------------------------------------------------------------
2994
3153
  async chat(messages, context, options) {
2995
- var _a, _b, _c, _d;
3154
+ var _a, _b, _c, _d, _e, _f;
3155
+ 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.";
2996
3156
  const model = this.client.getGenerativeModel({
2997
3157
  model: this.llmConfig.model,
2998
- systemInstruction: this.buildSystemInstruction(context)
3158
+ systemInstruction: buildSystemContent(basePrompt, context)
2999
3159
  });
3000
3160
  const result = await model.generateContent({
3001
3161
  contents: this.buildGeminiContents(messages),
3002
3162
  generationConfig: {
3003
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : DEFAULT_TEMPERATURE,
3004
- maxOutputTokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : DEFAULT_MAX_TOKENS,
3163
+ temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
3164
+ maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
3005
3165
  stopSequences: options == null ? void 0 : options.stop
3006
3166
  }
3007
3167
  });
@@ -3009,16 +3169,17 @@ ${context}`;
3009
3169
  }
3010
3170
  chatStream(messages, context, options) {
3011
3171
  return __asyncGenerator(this, null, function* () {
3012
- var _a, _b, _c, _d;
3172
+ var _a, _b, _c, _d, _e, _f;
3173
+ 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.";
3013
3174
  const model = this.client.getGenerativeModel({
3014
3175
  model: this.llmConfig.model,
3015
- systemInstruction: this.buildSystemInstruction(context)
3176
+ systemInstruction: buildSystemContent(basePrompt, context)
3016
3177
  });
3017
3178
  const result = yield new __await(model.generateContentStream({
3018
3179
  contents: this.buildGeminiContents(messages),
3019
3180
  generationConfig: {
3020
- temperature: (_b = (_a = options == null ? void 0 : options.temperature) != null ? _a : this.llmConfig.temperature) != null ? _b : DEFAULT_TEMPERATURE,
3021
- maxOutputTokens: (_d = (_c = options == null ? void 0 : options.maxTokens) != null ? _c : this.llmConfig.maxTokens) != null ? _d : DEFAULT_MAX_TOKENS,
3181
+ temperature: (_d = (_c = options == null ? void 0 : options.temperature) != null ? _c : this.llmConfig.temperature) != null ? _d : DEFAULT_TEMPERATURE,
3182
+ maxOutputTokens: (_f = (_e = options == null ? void 0 : options.maxTokens) != null ? _e : this.llmConfig.maxTokens) != null ? _f : DEFAULT_MAX_TOKENS,
3022
3183
  stopSequences: options == null ? void 0 : options.stop
3023
3184
  }
3024
3185
  }));
@@ -3453,7 +3614,9 @@ var LLMFactory = class _LLMFactory {
3453
3614
  if (llmConfig.baseUrl || ((_b = llmConfig.options) == null ? void 0 : _b.baseUrl)) {
3454
3615
  return new UniversalLLMAdapter(llmConfig);
3455
3616
  }
3456
- throw new Error(
3617
+ throw new ProviderNotFoundException(
3618
+ "LLM",
3619
+ String(llmConfig.provider),
3457
3620
  `[LLMFactory] Unknown provider "${llmConfig.provider}". Built-in providers: ${_LLMFactory.listProviders().join(", ")}. Register a custom provider with LLMFactory.register().`
3458
3621
  );
3459
3622
  }
@@ -3585,7 +3748,7 @@ var ProviderRegistry = class {
3585
3748
  return UniversalVectorProvider2;
3586
3749
  }
3587
3750
  default:
3588
- throw new Error(`Unsupported vector provider: ${provider}`);
3751
+ throw new ProviderNotFoundException("vector", provider);
3589
3752
  }
3590
3753
  }
3591
3754
  static async createVectorProvider(config) {
@@ -3603,12 +3766,18 @@ var ProviderRegistry = class {
3603
3766
  return new SimpleGraphProvider2(config);
3604
3767
  }
3605
3768
  default:
3606
- throw new Error(`Unsupported graph provider: ${provider}`);
3769
+ throw new ProviderNotFoundException("graph", provider);
3607
3770
  }
3608
3771
  }
3609
3772
  static createLLMProvider(llmConfig, embeddingConfig) {
3610
3773
  return LLMFactory.create(llmConfig, embeddingConfig);
3611
3774
  }
3775
+ static registerLLMProvider(name, factory) {
3776
+ LLMFactory.register(name, factory);
3777
+ }
3778
+ static createEmbeddingProvider(embeddingConfig) {
3779
+ return LLMFactory.createEmbeddingProvider(embeddingConfig);
3780
+ }
3612
3781
  };
3613
3782
  ProviderRegistry.vectorProviders = {};
3614
3783
  ProviderRegistry.graphProviders = {};
@@ -3755,8 +3924,8 @@ var ConfigValidator = class {
3755
3924
  const errorItems = errors.filter((e) => e.severity === "error");
3756
3925
  if (errorItems.length > 0) {
3757
3926
  const message = errorItems.map((e) => `${e.field}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ""}`).join("\n ");
3758
- throw new Error(`[ConfigValidator] Configuration validation failed:
3759
- ${message}`);
3927
+ throw new ConfigurationException(`[ConfigValidator] Configuration validation failed:
3928
+ ${message}`, errorItems);
3760
3929
  }
3761
3930
  }
3762
3931
  };
@@ -3943,20 +4112,17 @@ var Reranker = class {
3943
4112
  }
3944
4113
  try {
3945
4114
  const topN = matches.slice(0, 10);
3946
- const prompt = `You are a relevance ranking expert.
3947
- Given the following user query and a list of retrieved document chunks, rank the chunks by relevance to the query.
3948
- Return the result as a comma-separated list of document indices in order of relevance, from most relevant to least relevant.
3949
- Use the indices provided in brackets like [0], [1], etc.
3950
- Only return the indices (e.g., "2,0,1"), nothing else. Do not include brackets in the output.
3951
-
3952
- Query: "${query}"
4115
+ const response = await llm.chat(
4116
+ [{ role: "user", content: `Query: "${query}"
3953
4117
 
3954
4118
  Documents:
3955
- ${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}`;
3956
- const response = await llm.chat(
3957
- [{ role: "user", content: prompt }],
4119
+ ${topN.map((m, i) => `[${i}] ${m.content.replace(/\n/g, " ")}`).join("\n")}` }],
3958
4120
  "",
3959
- { temperature: 0, maxTokens: 50 }
4121
+ {
4122
+ 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.',
4123
+ temperature: 0,
4124
+ maxTokens: 50
4125
+ }
3960
4126
  );
3961
4127
  const cleanedResponse = response.trim().replace(/[\[\]\s]/g, "");
3962
4128
  const rankedIndices = cleanedResponse.split(",").map(Number).filter((n) => !isNaN(n));
@@ -4468,6 +4634,18 @@ var QueryProcessor = class {
4468
4634
  }, normalizedField ? { field: normalizedField } : {}));
4469
4635
  }
4470
4636
  };
4637
+ const activeValidFields = validFields.length > 0 ? validFields : ["brand", "category", "price", "rating", "stock", "stock_quantity", "status", "name", "title"];
4638
+ const resolveValidField = (fieldStr) => {
4639
+ 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();
4640
+ const comparable = (v) => v.toLowerCase().replace(/[^a-z0-9]/g, "");
4641
+ const cleanedComparable = comparable(cleaned);
4642
+ if (!cleanedComparable) return null;
4643
+ const matchedField = activeValidFields.find((fieldName) => {
4644
+ const candidate = comparable(fieldName);
4645
+ return candidate === cleanedComparable || candidate.includes(cleanedComparable) || cleanedComparable.includes(candidate);
4646
+ });
4647
+ return matchedField != null ? matchedField : null;
4648
+ };
4471
4649
  for (const match of question.matchAll(/["']([^"']{2,100})["']/g)) {
4472
4650
  addHint(match[1]);
4473
4651
  }
@@ -4492,7 +4670,13 @@ var QueryProcessor = class {
4492
4670
  const field = match[1];
4493
4671
  const value = match[2];
4494
4672
  if (field && !this.isLikelyPromptPhrase(field)) {
4495
- addHint(value, field);
4673
+ const resolvedField = resolveValidField(field);
4674
+ if (resolvedField) {
4675
+ addHint(value, resolvedField);
4676
+ } else {
4677
+ addHint(value);
4678
+ addHint(field);
4679
+ }
4496
4680
  }
4497
4681
  }
4498
4682
  if (validFields.length > 0) {
@@ -4538,12 +4722,16 @@ var QueryProcessor = class {
4538
4722
  ];
4539
4723
  for (const pattern of fieldValuePatterns) {
4540
4724
  for (const match of question.matchAll(pattern)) {
4541
- const field = this.normalizeHintValue((_c = match[1]) != null ? _c : "");
4725
+ const field = (_c = match[1]) != null ? _c : "";
4542
4726
  const value = (_d = match[2]) != null ? _d : "";
4543
- if (field && !this.isLikelyPromptPhrase(field)) {
4544
- addHint(value, field);
4727
+ const resolvedField = resolveValidField(field);
4728
+ if (resolvedField) {
4729
+ addHint(value, resolvedField);
4545
4730
  } else {
4546
4731
  addHint(value);
4732
+ if (field && !this.isLikelyPromptPhrase(field)) {
4733
+ addHint(field);
4734
+ }
4547
4735
  }
4548
4736
  }
4549
4737
  }
@@ -4770,7 +4958,7 @@ var LLMRouter = class {
4770
4958
 
4771
4959
  // src/utils/UITransformer.ts
4772
4960
  init_synonyms();
4773
- var UITransformer = class {
4961
+ var UITransformer = class _UITransformer {
4774
4962
  // ─── Public Entry Points ─────────────────────────────────────────────────
4775
4963
  /**
4776
4964
  * Heuristic-only transform (no LLM required).
@@ -4832,7 +5020,7 @@ var UITransformer = class {
4832
5020
  static async analyzeAndDecide(query, sources, llm) {
4833
5021
  let intent;
4834
5022
  try {
4835
- intent = await this.detectIntent(query, llm);
5023
+ intent = await this.detectIntent(query, llm, sources);
4836
5024
  console.debug("[UITransformer] Detected intent:", intent);
4837
5025
  } catch (err) {
4838
5026
  console.warn("[UITransformer] Intent detection failed; using heuristic.", err);
@@ -4850,11 +5038,23 @@ var UITransformer = class {
4850
5038
  try {
4851
5039
  const context = this.buildContextSummary(sources);
4852
5040
  const systemPrompt = this.buildVisualizationSystemPrompt();
5041
+ const profile = this.profileData(sources);
5042
+ const schemaContext = `
5043
+ RETRIEVED DATA SCHEMA:
5044
+ - Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5045
+ - Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5046
+ - Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5047
+ - Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5048
+ - Text/Other fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
5049
+ `;
4853
5050
  const userPrompt = [
4854
5051
  `USER QUESTION: ${query}`,
4855
5052
  "",
4856
5053
  `DETECTED INTENT (JSON): ${JSON.stringify(intent)}`,
4857
5054
  "",
5055
+ `RETRIEVED DATA SCHEMA:`,
5056
+ schemaContext,
5057
+ "",
4858
5058
  "RETRIEVED DATA (JSON):",
4859
5059
  context
4860
5060
  ].join("\n");
@@ -4865,6 +5065,20 @@ var UITransformer = class {
4865
5065
  );
4866
5066
  const parsed = this.parseTransformationResponse(rawResponse);
4867
5067
  if (parsed) {
5068
+ let isCompatible = true;
5069
+ if (parsed.type === "line_chart" && (profile.dateFields.length === 0 || profile.numericFields.length === 0)) {
5070
+ isCompatible = false;
5071
+ } else if (["bar_chart", "horizontal_bar", "pie_chart", "donut_chart"].includes(parsed.type) && (profile.categoricalFields.length === 0 && profile.numericFields.length === 0)) {
5072
+ isCompatible = false;
5073
+ } else if (parsed.type === "scatter_plot" && profile.numericFields.length < 2) {
5074
+ isCompatible = false;
5075
+ } else if (parsed.type === "metric_card" && profile.numericFields.length === 0) {
5076
+ isCompatible = false;
5077
+ }
5078
+ if (!isCompatible) {
5079
+ console.warn(`[UITransformer] LLM chose incompatible visualization type "${parsed.type}" for retrieved schema. Falling back to heuristic.`);
5080
+ return this.transform(query, sources, void 0, void 0, intent);
5081
+ }
4868
5082
  const intentAllowsTable = intent.wantsExplicitTable || ["tabular", "table", "geographic"].includes(intent.visualizationHint);
4869
5083
  const intentWantsPieLikeChart = ["pie_chart", "donut_chart"].includes(intent.recommendedChart) || ["composition", "category_breakdown"].includes(intent.visualizationHint);
4870
5084
  if (parsed.type === "table" && !intentAllowsTable) {
@@ -4894,7 +5108,26 @@ var UITransformer = class {
4894
5108
  * - The intent object can be reused across both the heuristic and LLM paths.
4895
5109
  * - It is easy to unit-test intent detection in isolation.
4896
5110
  */
4897
- static async detectIntent(query, llm) {
5111
+ static async detectIntent(query, llm, sources) {
5112
+ let schemaProfileText = "";
5113
+ if (sources && sources.length > 0) {
5114
+ const profile = this.profileData(sources);
5115
+ const hasProducts = sources.some((item) => this.isProductData(item));
5116
+ schemaProfileText = `
5117
+ RETRIEVED DATA SCHEMA:
5118
+ - Numeric fields (measures): ${profile.numericFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5119
+ - Category fields (dimensions): ${profile.categoricalFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5120
+ - Date fields (temporal): ${profile.dateFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5121
+ - Boolean fields: ${profile.booleanFields.map((f) => `${f.key} (unique values: ${f.uniqueCount})`).join(", ") || "None"}
5122
+ - Text fields: ${profile.fields.filter((f) => f.kind === "text").map((f) => f.key).join(", ") || "None"}
5123
+
5124
+ Please choose visualizationHint and recommendedChart dynamically based on the available schema.
5125
+ - Do NOT recommend "trend" or "line_chart" if there are no Date fields.
5126
+ - 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.
5127
+ - If there are multiple metadata fields but no clear categories or numbers, "tabular"/"table" is a highly accurate default.
5128
+ ${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".` : ""}
5129
+ `;
5130
+ }
4898
5131
  const systemPrompt = `You are an intent classifier for a product-search RAG system.
4899
5132
  Given a user query, return ONLY a valid JSON object with this exact shape \u2014 no prose, no markdown:
4900
5133
 
@@ -4930,8 +5163,11 @@ RULES:
4930
5163
  - isTemporal: true if time words appear (trend, historical, over time, last year, monthly, etc.)
4931
5164
  - isComparison: true if user compares, ranks, or contrasts entities or categories.
4932
5165
  - language: detect from the query text itself; default "en" if uncertain.`;
5166
+ const userPrompt = `QUERY: ${query}${schemaProfileText ? `
5167
+
5168
+ ${schemaProfileText}` : ""}`;
4933
5169
  const rawResponse = await llm.chat(
4934
- [{ role: "user", content: `QUERY: ${query}` }],
5170
+ [{ role: "user", content: userPrompt }],
4935
5171
  "",
4936
5172
  { systemPrompt, temperature: 0 }
4937
5173
  );
@@ -5374,10 +5610,21 @@ RULES:
5374
5610
  const hasEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
5375
5611
  return asksForRecords && (hasNumericPredicate || hasEntityTerms);
5376
5612
  }
5613
+ /** @internal kept private for transform() internal logic */
5377
5614
  static isProductQuery(query) {
5615
+ return _UITransformer._productQueryTest(query);
5616
+ }
5617
+ /**
5618
+ * Public entry-point for external callers (e.g. Pipeline)
5619
+ * to check whether a query maps to product_browse without triggering an LLM call.
5620
+ */
5621
+ static isProductQueryPublic(query) {
5622
+ return _UITransformer._productQueryTest(query);
5623
+ }
5624
+ static _productQueryTest(query) {
5378
5625
  const q = query.toLowerCase();
5379
5626
  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);
5380
- const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list)\b/.test(q);
5627
+ const productAction = /\b(recommend|suggest|describe|description|detail|details|about|show|find|search|browse|list|explore|exploring)\b/.test(q);
5381
5628
  const nonProductEntityTerms = /\b(organization|organisations?|organizations?|company|companies|employee|employees|staff|headcount|workforce)\b/.test(q);
5382
5629
  return productTerms && productAction && !nonProductEntityTerms;
5383
5630
  }
@@ -6150,23 +6397,19 @@ function estimateCostUsd(promptTokens, completionTokens, model) {
6150
6397
  async function scoreHallucination(llm, answer, context) {
6151
6398
  const maxContextChars = 3e3;
6152
6399
  const truncatedContext = context.length > maxContextChars ? context.slice(0, maxContextChars) + "\n...[truncated]" : context;
6153
- 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.
6154
-
6155
- CONTEXT:
6400
+ try {
6401
+ const raw = await llm.chat(
6402
+ [{ role: "user", content: `CONTEXT:
6156
6403
  ${truncatedContext}
6157
6404
 
6158
6405
  ANSWER:
6159
- ${answer}
6160
-
6161
- Return ONLY a valid JSON object with no markdown fences:
6162
- {"score": <float 0-1>, "reason": "<one sentence>"}
6163
-
6164
- Where score 0 = fully grounded in context, score 1 = likely hallucinated or unsupported.`;
6165
- try {
6166
- const raw = await llm.chat(
6167
- [{ role: "user", content: prompt }],
6406
+ ${answer}` }],
6168
6407
  "",
6169
- { temperature: 0, maxTokens: 120 }
6408
+ {
6409
+ 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.',
6410
+ temperature: 0,
6411
+ maxTokens: 120
6412
+ }
6170
6413
  );
6171
6414
  const jsonMatch = raw.match(/\{[\s\S]*\}/);
6172
6415
  if (!jsonMatch) return void 0;
@@ -6226,7 +6469,10 @@ var Pipeline = class {
6226
6469
  - 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.
6227
6470
  - Do NOT try to format product lists as tables.
6228
6471
  `;
6229
- this.config.llm.systemPrompt = chartInstruction;
6472
+ const userPromptPrefix = this.config.llm.systemPrompt ? `${this.config.llm.systemPrompt}
6473
+
6474
+ ` : "";
6475
+ this.config.llm.systemPrompt = `${userPromptPrefix}${chartInstruction}`;
6230
6476
  this.vectorDB = await ProviderRegistry.createVectorProvider(this.config.vectorDb);
6231
6477
  this.llmRouter = new LLMRouter(this.config);
6232
6478
  const { llmProvider: resolvedLLM, embeddingProvider } = await EmbeddingStrategyResolver.resolve(
@@ -6404,11 +6650,11 @@ var Pipeline = class {
6404
6650
  */
6405
6651
  askStream(_0) {
6406
6652
  return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
6407
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
6653
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
6408
6654
  yield new __await(this.initialize());
6409
6655
  const ns = namespace != null ? namespace : this.config.projectId;
6410
6656
  const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
6411
- const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0;
6657
+ const scoreThreshold = (_d = (_c = this.config.rag) == null ? void 0 : _c.scoreThreshold) != null ? _d : 0.3;
6412
6658
  const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
6413
6659
  const requestStart = performance.now();
6414
6660
  try {
@@ -6427,12 +6673,13 @@ var Pipeline = class {
6427
6673
  const embedStart = performance.now();
6428
6674
  const cacheKey = `${ns}::${searchQuery}`;
6429
6675
  const cachedVector = this.embeddingCache.get(cacheKey);
6676
+ const useGraph = this.graphDB && ((_h = this.config.rag) == null ? void 0 : _h.useGraphRetrieval);
6430
6677
  const [strategyResult, embeddedVector] = yield new __await(Promise.all([
6431
6678
  QueryProcessor.determineRetrievalStrategy(
6432
6679
  searchQuery,
6433
- this.llmRouter.get("fast"),
6434
- (_h = this.config.rag) == null ? void 0 : _h.graphKeywords,
6435
- (_i = this.config.rag) == null ? void 0 : _i.vectorKeywords
6680
+ useGraph ? this.llmRouter.get("fast") : void 0,
6681
+ (_i = this.config.rag) == null ? void 0 : _i.graphKeywords,
6682
+ (_j = this.config.rag) == null ? void 0 : _j.vectorKeywords
6436
6683
  ),
6437
6684
  // Embed immediately regardless of strategy — costs nothing if cached.
6438
6685
  // If strategy turns out to be 'graph'-only we just won't use the vector.
@@ -6442,7 +6689,7 @@ var Pipeline = class {
6442
6689
  if (!cachedVector && queryVector.length > 0) {
6443
6690
  this.embeddingCache.set(cacheKey, queryVector);
6444
6691
  }
6445
- 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;
6692
+ 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;
6446
6693
  const hasMetadataFilter = filter.metadata && Object.keys(filter.metadata).length > 0;
6447
6694
  const hasNumericPredicates = Array.isArray(filter.__numericPredicates) && filter.__numericPredicates.length > 0;
6448
6695
  const wantsExhaustiveList = hasNumericPredicates || hasMetadataFilter || /\b(list|all|show|provide|give|get|browse|find|search|display)\b/i.test(question);
@@ -6455,7 +6702,7 @@ var Pipeline = class {
6455
6702
  const structuredSources = this.applyStructuredFilters(rawSources, filter);
6456
6703
  let fullSources = hasNumericPredicates ? structuredSources : structuredSources.filter((m) => m.score >= scoreThreshold);
6457
6704
  const rerankLimit = wantsExhaustiveList ? retrievalLimit : topK;
6458
- if (!hasNumericPredicates && ((_k = this.config.rag) == null ? void 0 : _k.useReranking)) {
6705
+ if (!hasNumericPredicates && ((_l = this.config.rag) == null ? void 0 : _l.useReranking)) {
6459
6706
  fullSources = yield new __await(this.reranker.rerank(fullSources, question, rerankLimit));
6460
6707
  } else if (!wantsExhaustiveList) {
6461
6708
  fullSources = fullSources.slice(0, topK);
@@ -6486,34 +6733,117 @@ VECTOR CONTEXT:
6486
6733
  ${context}`;
6487
6734
  }
6488
6735
  const allMetadataKeys = Array.from(new Set(sources.flatMap((s) => Object.keys(s.metadata || {}))));
6489
- const trainedSchemaPromise = allMetadataKeys.length > 0 ? SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => void 0) : Promise.resolve(void 0);
6490
- const uiTransformationPromise = trainedSchemaPromise.then(
6491
- (trainedSchema) => this.generateUiTransformation(
6736
+ const cachedSchema = allMetadataKeys.length > 0 ? SchemaMapper.getCached(ns, allMetadataKeys) : void 0;
6737
+ if (allMetadataKeys.length > 0 && !cachedSchema) {
6738
+ SchemaMapper.train(this.llmProvider, ns, allMetadataKeys).catch(() => {
6739
+ });
6740
+ }
6741
+ const isProductQ = UITransformer.isProductQueryPublic(question);
6742
+ const uiTransformationPromise = isProductQ ? Promise.resolve(
6743
+ UITransformer.transform(
6492
6744
  question,
6493
6745
  sources,
6494
- trainedSchema,
6495
- hasNumericPredicates
6746
+ this.config,
6747
+ cachedSchema,
6748
+ { visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
6496
6749
  )
6750
+ ) : this.generateUiTransformation(
6751
+ question,
6752
+ sources,
6753
+ cachedSchema,
6754
+ hasNumericPredicates
6497
6755
  ).catch((uiError) => {
6498
6756
  console.warn("[Pipeline] UI transformation failed concurrently:", uiError);
6499
- return UITransformer.transform(question, sources, this.config);
6757
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6500
6758
  });
6759
+ let hallucinationScoringPromise = Promise.resolve(void 0);
6501
6760
  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.)";
6761
+ const isClaude37 = this.config.llm.model.includes("claude-3-7-sonnet");
6762
+ 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);
6763
+ const modelNameLower = this.config.llm.model.toLowerCase();
6764
+ const isReasoningModel = modelNameLower.includes("-r1") || modelNameLower.includes("deepseek-r1") || modelNameLower.includes("reasoning") || modelNameLower.includes("thinking");
6765
+ 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);
6766
+ let finalRestrictionSuffix = restrictionSuffix;
6767
+ if (isSimulatedThinking) {
6768
+ 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.`)";
6769
+ }
6502
6770
  const hardenedHistory = [...history];
6503
- const userQuestion = { role: "user", content: question + restrictionSuffix };
6771
+ const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
6504
6772
  const messages = [...hardenedHistory, userQuestion];
6505
- const systemPrompt = (_l = this.config.llm.systemPrompt) != null ? _l : "";
6773
+ const systemPrompt = (_s = this.config.llm.systemPrompt) != null ? _s : "";
6506
6774
  const userPrompt = messages.map((m) => `${m.role}: ${m.content}`).join("\n");
6507
6775
  let fullReply = "";
6776
+ let thinkingText = "";
6777
+ let thinkingStartMs = performance.now();
6778
+ let thinkingDurationMs = 0;
6508
6779
  const generateStart = performance.now();
6509
6780
  if (this.llmProvider.chatStream) {
6510
6781
  const stream = this.llmProvider.chatStream(messages, context);
6511
6782
  if (!stream) throw new Error(`[Pipeline] ${this.config.llm.provider} chatStream returned undefined`);
6783
+ let inThinking = false;
6784
+ let textBuffer = "";
6512
6785
  try {
6513
6786
  for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
6514
6787
  const chunk = temp.value;
6515
- fullReply += chunk;
6516
- yield chunk;
6788
+ if (chunk.startsWith("\0THINKING\0")) {
6789
+ const thinkingDelta = chunk.slice("\0THINKING\0".length);
6790
+ thinkingText += thinkingDelta;
6791
+ thinkingDurationMs = performance.now() - thinkingStartMs;
6792
+ yield { type: "thinking", text: thinkingDelta };
6793
+ continue;
6794
+ }
6795
+ if (isSimulatedThinking) {
6796
+ textBuffer += chunk;
6797
+ while (textBuffer.length > 0) {
6798
+ if (!inThinking) {
6799
+ const thinkIndex = textBuffer.indexOf("<think>");
6800
+ if (thinkIndex !== -1) {
6801
+ const before = textBuffer.slice(0, thinkIndex);
6802
+ if (before) {
6803
+ fullReply += before;
6804
+ yield before;
6805
+ }
6806
+ inThinking = true;
6807
+ thinkingStartMs = performance.now();
6808
+ textBuffer = textBuffer.slice(thinkIndex + "<think>".length);
6809
+ } else {
6810
+ const maxSafeLength = textBuffer.length - "<think>".length;
6811
+ if (maxSafeLength > 0) {
6812
+ const safeText = textBuffer.slice(0, maxSafeLength);
6813
+ fullReply += safeText;
6814
+ yield safeText;
6815
+ textBuffer = textBuffer.slice(maxSafeLength);
6816
+ }
6817
+ break;
6818
+ }
6819
+ } else {
6820
+ const endThinkIndex = textBuffer.indexOf("</think>");
6821
+ if (endThinkIndex !== -1) {
6822
+ const thinkingDelta = textBuffer.slice(0, endThinkIndex);
6823
+ if (thinkingDelta) {
6824
+ thinkingText += thinkingDelta;
6825
+ thinkingDurationMs = performance.now() - thinkingStartMs;
6826
+ yield { type: "thinking", text: thinkingDelta };
6827
+ }
6828
+ inThinking = false;
6829
+ textBuffer = textBuffer.slice(endThinkIndex + "</think>".length);
6830
+ } else {
6831
+ const maxSafeLength = textBuffer.length - "</think>".length;
6832
+ if (maxSafeLength > 0) {
6833
+ const thinkingDelta = textBuffer.slice(0, maxSafeLength);
6834
+ thinkingText += thinkingDelta;
6835
+ thinkingDurationMs = performance.now() - thinkingStartMs;
6836
+ yield { type: "thinking", text: thinkingDelta };
6837
+ textBuffer = textBuffer.slice(maxSafeLength);
6838
+ }
6839
+ break;
6840
+ }
6841
+ }
6842
+ }
6843
+ } else {
6844
+ fullReply += chunk;
6845
+ yield chunk;
6846
+ }
6517
6847
  }
6518
6848
  } catch (temp) {
6519
6849
  error = [temp];
@@ -6525,17 +6855,42 @@ ${context}`;
6525
6855
  throw error[0];
6526
6856
  }
6527
6857
  }
6858
+ if (isSimulatedThinking && textBuffer.length > 0) {
6859
+ if (inThinking) {
6860
+ thinkingText += textBuffer;
6861
+ yield { type: "thinking", text: textBuffer };
6862
+ } else {
6863
+ fullReply += textBuffer;
6864
+ yield textBuffer;
6865
+ }
6866
+ }
6528
6867
  } else {
6529
6868
  const reply = yield new __await(this.llmProvider.chat(messages, context));
6530
- fullReply = reply;
6531
- yield reply;
6869
+ if (isSimulatedThinking) {
6870
+ const thinkStart = reply.indexOf("<think>");
6871
+ const thinkEnd = reply.indexOf("</think>");
6872
+ if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {
6873
+ thinkingText = reply.slice(thinkStart + "<think>".length, thinkEnd);
6874
+ fullReply = reply.slice(0, thinkStart) + reply.slice(thinkEnd + "</think>".length);
6875
+ thinkingDurationMs = 100;
6876
+ } else {
6877
+ fullReply = reply;
6878
+ }
6879
+ } else {
6880
+ fullReply = reply;
6881
+ }
6882
+ yield fullReply;
6883
+ }
6884
+ 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;
6885
+ if (runHallucination) {
6886
+ hallucinationScoringPromise = scoreHallucination(this.llmProvider, fullReply, context).catch(() => void 0);
6532
6887
  }
6533
6888
  const generateMs = performance.now() - generateStart;
6534
6889
  const totalMs = performance.now() - requestStart;
6535
6890
  const latency = {
6536
6891
  embedMs: Math.round(embedMs),
6537
6892
  retrieveMs: Math.round(retrieveMs),
6538
- rerankMs: ((_m = this.config.rag) == null ? void 0 : _m.useReranking) ? Math.round(rerankMs) : void 0,
6893
+ rerankMs: ((_v = this.config.rag) == null ? void 0 : _v.useReranking) ? Math.round(rerankMs) : void 0,
6539
6894
  generateMs: Math.round(generateMs),
6540
6895
  totalMs: Math.round(totalMs)
6541
6896
  };
@@ -6548,12 +6903,16 @@ ${context}`;
6548
6903
  totalTokens: promptTokens + completionTokens,
6549
6904
  estimatedCostUsd: estimateCostUsd(promptTokens, completionTokens, this.config.llm.model)
6550
6905
  };
6551
- const trace = {
6906
+ const [uiTransformation, hallucinationResult] = yield new __await(Promise.all([
6907
+ uiTransformationPromise,
6908
+ hallucinationScoringPromise
6909
+ ]));
6910
+ const trace = __spreadValues({
6552
6911
  requestId,
6553
6912
  query: question,
6554
6913
  rewrittenQuery,
6555
6914
  systemPrompt,
6556
- userPrompt: question + restrictionSuffix,
6915
+ userPrompt: question + finalRestrictionSuffix,
6557
6916
  chunks: sources.map((s) => {
6558
6917
  var _a2;
6559
6918
  return {
@@ -6567,22 +6926,19 @@ ${context}`;
6567
6926
  latency,
6568
6927
  tokens,
6569
6928
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6570
- };
6571
- const uiTransformation = yield new __await(uiTransformationPromise);
6929
+ }, hallucinationResult ? {
6930
+ hallucinationScore: hallucinationResult.score,
6931
+ hallucinationReason: hallucinationResult.reason
6932
+ } : {});
6572
6933
  yield {
6573
6934
  reply: "",
6574
6935
  sources,
6575
6936
  graphData,
6576
6937
  ui_transformation: uiTransformation,
6577
- trace
6938
+ trace,
6939
+ thinking: thinkingText || void 0,
6940
+ thinkingMs: thinkingDurationMs > 0 ? Math.round(thinkingDurationMs) : void 0
6578
6941
  };
6579
- scoreHallucination(this.llmProvider, fullReply, context).then((hallucinationResult) => {
6580
- if (hallucinationResult) {
6581
- trace.hallucinationScore = hallucinationResult.score;
6582
- trace.hallucinationReason = hallucinationResult.reason;
6583
- }
6584
- }).catch(() => {
6585
- });
6586
6942
  } catch (error2) {
6587
6943
  throw new Error(`[Pipeline] Stream failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
6588
6944
  }
@@ -6592,18 +6948,30 @@ ${context}`;
6592
6948
  * Universal retrieval method combining all enabled providers.
6593
6949
  * Uses an LRU-bounded embedding cache to avoid re-embedding the same query.
6594
6950
  */
6595
- async generateUiTransformation(question, sources, trainedSchema, forceDeterministic = false) {
6951
+ async generateUiTransformation(question, sources, cachedSchema, forceDeterministic = false) {
6952
+ var _a;
6596
6953
  if (!sources || sources.length === 0) {
6597
- return UITransformer.transform(question, sources, this.config, trainedSchema);
6954
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6598
6955
  }
6599
- if (forceDeterministic) {
6600
- return UITransformer.transform(question, sources, this.config, trainedSchema);
6956
+ if (UITransformer.isProductQueryPublic(question)) {
6957
+ return UITransformer.transform(
6958
+ question,
6959
+ sources,
6960
+ this.config,
6961
+ cachedSchema,
6962
+ { visualizationHint: "product_browse", recommendedChart: "text", filterInStockOnly: false, wantsExplicitTable: false, isTemporal: false, isComparison: false, language: "en" }
6963
+ );
6964
+ }
6965
+ const isLocalProvider = this.config.llm.provider === "ollama";
6966
+ const disableLlmUiTransform = ((_a = this.config.llm.options) == null ? void 0 : _a.disableLlmUiTransform) === true;
6967
+ if (forceDeterministic || isLocalProvider || disableLlmUiTransform) {
6968
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6601
6969
  }
6602
6970
  try {
6603
6971
  return await UITransformer.analyzeAndDecide(question, sources, this.llmRouter.get("fast"));
6604
6972
  } catch (err) {
6605
6973
  console.warn("[Pipeline] generateUiTransformation failed, using heuristic fallback:", err);
6606
- return UITransformer.transform(question, sources, this.config, trainedSchema);
6974
+ return UITransformer.transform(question, sources, this.config, cachedSchema);
6607
6975
  }
6608
6976
  }
6609
6977
  applyStructuredFilters(sources, filter) {
@@ -6687,25 +7055,29 @@ ${context}`;
6687
7055
  return Number.isFinite(numeric) ? numeric : null;
6688
7056
  }
6689
7057
  async retrieve(query, options) {
6690
- var _a, _b, _c, _d, _e, _f;
7058
+ var _a, _b, _c, _d, _e, _f, _g;
6691
7059
  const ns = (_a = options.namespace) != null ? _a : this.config.projectId;
6692
7060
  const topK = (_b = options.topK) != null ? _b : 5;
6693
7061
  const cacheKey = `${ns}::${query}`;
6694
7062
  let queryVector = this.embeddingCache.get(cacheKey);
6695
- const strategy = await QueryProcessor.determineRetrievalStrategy(query, this.llmRouter.get("fast"));
7063
+ const useGraph = this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval);
7064
+ const strategy = await QueryProcessor.determineRetrievalStrategy(
7065
+ query,
7066
+ useGraph ? this.llmRouter.get("fast") : void 0
7067
+ );
6696
7068
  console.debug(`[Pipeline] Determined retrieval strategy: ${strategy}`);
6697
7069
  const [retrievedVector, graphData] = await Promise.all([
6698
7070
  // Only embed if we need vector search (strategy is 'vector' or 'both')
6699
7071
  strategy === "vector" || strategy === "both" ? queryVector ? Promise.resolve(queryVector) : this.embeddingProvider.embed(query, { taskType: "query" }) : Promise.resolve([]),
6700
7072
  // Only query graph if we need graph search (strategy is 'graph' or 'both')
6701
- (strategy === "graph" || strategy === "both") && this.graphDB && ((_c = this.config.rag) == null ? void 0 : _c.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
7073
+ (strategy === "graph" || strategy === "both") && this.graphDB && ((_d = this.config.rag) == null ? void 0 : _d.useGraphRetrieval) ? this.graphDB.query(query) : Promise.resolve(void 0)
6702
7074
  ]);
6703
7075
  if ((strategy === "vector" || strategy === "both") && !queryVector && retrievedVector && retrievedVector.length > 0) {
6704
7076
  this.embeddingCache.set(cacheKey, retrievedVector);
6705
7077
  queryVector = retrievedVector;
6706
7078
  }
6707
- const baseFilter = __spreadProps(__spreadValues({}, (_d = options.filter) != null ? _d : {}), { queryText: query });
6708
- const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_e = this.config.rag) == null ? void 0 : _e.filterableFields);
7079
+ const baseFilter = __spreadProps(__spreadValues({}, (_e = options.filter) != null ? _e : {}), { queryText: query });
7080
+ const numericPredicates = QueryProcessor.extractNumericPredicates(query, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
6709
7081
  if (numericPredicates.length > 0) {
6710
7082
  baseFilter.__numericPredicates = numericPredicates;
6711
7083
  }
@@ -6716,7 +7088,7 @@ ${context}`;
6716
7088
  ) : [];
6717
7089
  const resolvedSources = [];
6718
7090
  for (const source of sources) {
6719
- const parentId = (_f = source.metadata) == null ? void 0 : _f.parent_id;
7091
+ const parentId = (_g = source.metadata) == null ? void 0 : _g.parent_id;
6720
7092
  if (parentId) {
6721
7093
  console.log(`[Pipeline] Multi-Vector: Found child chunk. Parent ID: ${parentId}`);
6722
7094
  resolvedSources.push(source);
@@ -6739,10 +7111,13 @@ New Question: ${question}
6739
7111
  Optimized Search Query:`;
6740
7112
  const rewrite = await this.llmProvider.chat(
6741
7113
  [
6742
- { role: "system", content: "You are an assistant that optimizes search queries for RAG systems." },
6743
7114
  { role: "user", content: prompt }
6744
7115
  ],
6745
- ""
7116
+ "",
7117
+ {
7118
+ 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.",
7119
+ temperature: 0
7120
+ }
6746
7121
  );
6747
7122
  return rewrite.trim() || question;
6748
7123
  }
@@ -6766,10 +7141,13 @@ ${context}
6766
7141
  Suggestions:`;
6767
7142
  const response = await this.llmProvider.chat(
6768
7143
  [
6769
- { role: "system", content: "You are a helpful assistant that generates search suggestions." },
6770
7144
  { role: "user", content: prompt }
6771
7145
  ],
6772
- ""
7146
+ "",
7147
+ {
7148
+ 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.",
7149
+ temperature: 0
7150
+ }
6773
7151
  );
6774
7152
  const match = response.match(/\[[\s\S]*\]/);
6775
7153
  if (match) {
@@ -6954,6 +7332,30 @@ var VectorPlugin = class {
6954
7332
  }
6955
7333
  };
6956
7334
 
7335
+ // src/core/Retrivora.ts
7336
+ var Retrivora = class {
7337
+ constructor(config) {
7338
+ this.config = ConfigResolver.resolveUniversal(config);
7339
+ this.pipeline = new Pipeline(this.config);
7340
+ }
7341
+ async initialize() {
7342
+ await ConfigValidator.validateAndThrow(this.config);
7343
+ await this.pipeline.initialize();
7344
+ }
7345
+ async ingest(documents, namespace) {
7346
+ return this.pipeline.ingest(documents, namespace);
7347
+ }
7348
+ async ask(question, history = [], namespace) {
7349
+ return this.pipeline.ask(question, history, namespace);
7350
+ }
7351
+ askStream(question, history = [], namespace) {
7352
+ return this.pipeline.askStream(question, history, namespace);
7353
+ }
7354
+ getPipeline() {
7355
+ return this.pipeline;
7356
+ }
7357
+ };
7358
+
6957
7359
  // src/config/ConfigBuilder.ts
6958
7360
  var ConfigBuilder = class {
6959
7361
  /**
@@ -7322,6 +7724,10 @@ function createStreamHandler(configOrPlugin) {
7322
7724
  if (!isActive) break;
7323
7725
  if (typeof chunk === "string") {
7324
7726
  enqueue(sseTextFrame(chunk));
7727
+ } else if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "thinking") {
7728
+ enqueue(`data: ${JSON.stringify(chunk)}
7729
+
7730
+ `);
7325
7731
  } else {
7326
7732
  enqueue(sseMetaFrame(chunk));
7327
7733
  const responseChunk = chunk;
@@ -7496,14 +7902,17 @@ function createUploadHandler(configOrPlugin) {
7496
7902
  // Annotate the CommonJS export names for ESM import in node:
7497
7903
  0 && (module.exports = {
7498
7904
  AnthropicProvider,
7905
+ AuthenticationException,
7499
7906
  BaseVectorProvider,
7500
7907
  BatchProcessor,
7501
7908
  ChromaDBProvider,
7502
7909
  ConfigBuilder,
7503
7910
  ConfigResolver,
7504
7911
  ConfigValidator,
7912
+ ConfigurationException,
7505
7913
  DocumentChunker,
7506
7914
  DocumentParser,
7915
+ EmbeddingFailedException,
7507
7916
  EmbeddingStrategy,
7508
7917
  EmbeddingStrategyResolver,
7509
7918
  LLMFactory,
@@ -7518,9 +7927,14 @@ function createUploadHandler(configOrPlugin) {
7518
7927
  Pipeline,
7519
7928
  PostgreSQLProvider,
7520
7929
  ProviderHealthCheck,
7930
+ ProviderNotFoundException,
7521
7931
  ProviderRegistry,
7522
7932
  QdrantProvider,
7933
+ RateLimitException,
7523
7934
  RedisProvider,
7935
+ RetrievalException,
7936
+ Retrivora,
7937
+ RetrivoraError,
7524
7938
  UniversalLLMAdapter,
7525
7939
  UniversalVectorProvider,
7526
7940
  VECTOR_PROFILES,