@retrivora-ai/rag-engine 1.0.5 → 1.0.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.
@@ -244,6 +244,10 @@ interface RAGConfig {
244
244
  useGraphRetrieval?: boolean;
245
245
  /** Whether to perform reranking on retrieved results */
246
246
  useReranking?: boolean;
247
+ /** List of metadata fields that are valid for filtering.
248
+ * Used to dynamically extract hints from natural language queries.
249
+ */
250
+ filterableFields?: string[];
247
251
  }
248
252
  interface RagConfig {
249
253
  /**
@@ -244,6 +244,10 @@ interface RAGConfig {
244
244
  useGraphRetrieval?: boolean;
245
245
  /** Whether to perform reranking on retrieved results */
246
246
  useReranking?: boolean;
247
+ /** List of metadata fields that are valid for filtering.
248
+ * Used to dynamically extract hints from natural language queries.
249
+ */
250
+ filterableFields?: string[];
247
251
  }
248
252
  interface RagConfig {
249
253
  /**
@@ -1904,15 +1904,18 @@ var QueryProcessor = class {
1904
1904
  * Checks if a string is likely a question word or common prompt phrase.
1905
1905
  */
1906
1906
  static isLikelyPromptPhrase(value) {
1907
- return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
1907
+ return /^(what|which|who|where|when|why|how|the|is|are|was|were|in|on|at|by|for|with|about|a|an|to|of)\b/i.test(value.trim());
1908
1908
  }
1909
1909
  /**
1910
1910
  * Scans a natural language question for potential metadata hints and keywords.
1911
+ * @param question The user's query
1912
+ * @param validFields Optional list of known filterable fields to look for
1911
1913
  */
1912
- static extractQueryFieldHints(question) {
1914
+ static extractQueryFieldHints(question, validFields = []) {
1913
1915
  var _a, _b, _c, _d;
1914
1916
  if (!question.trim()) return [];
1915
1917
  const hints = /* @__PURE__ */ new Map();
1918
+ const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
1916
1919
  const addHint = (value, field) => {
1917
1920
  const normalizedValue = this.normalizeHintValue(value);
1918
1921
  if (!normalizedValue) return;
@@ -1943,11 +1946,28 @@ var QueryProcessor = class {
1943
1946
  if (name) addHint(name, "name");
1944
1947
  }
1945
1948
  }
1949
+ if (fieldsToSearch.length > 0) {
1950
+ for (const field of fieldsToSearch) {
1951
+ const forwardPattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{2,60})["']?(?=[?.!,]|$)`, "gi");
1952
+ for (const match of question.matchAll(forwardPattern)) {
1953
+ const value = match[1];
1954
+ if (value && !this.isLikelyPromptPhrase(value)) {
1955
+ addHint(value, field);
1956
+ }
1957
+ }
1958
+ const reversePattern = new RegExp(`\\b([^"\\n?.!,\\s]{2,60})\\s+${field}\\b`, "gi");
1959
+ for (const match of question.matchAll(reversePattern)) {
1960
+ const value = match[1];
1961
+ if (value && !this.isLikelyPromptPhrase(value)) {
1962
+ addHint(value, field);
1963
+ }
1964
+ }
1965
+ }
1966
+ }
1946
1967
  const universalPatterns = [
1947
1968
  { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: "email", group: 1 },
1948
1969
  { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: "phone", group: 1 },
1949
1970
  { regex: /(\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2}\/\d{1,2}\/\d{2,4}\b)/g, field: "date", group: 1 },
1950
- { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: "amount", group: 1 },
1951
1971
  { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: "id", group: 2 },
1952
1972
  { regex: /"([^"]{2,120})"/g, group: 1 },
1953
1973
  { regex: /'([^']{2,120})'/g, group: 1 }
@@ -2012,6 +2032,28 @@ var QueryProcessor = class {
2012
2032
  return filter;
2013
2033
  }
2014
2034
  };
2035
+ QueryProcessor.COMMON_METADATA_FIELDS = [
2036
+ "category",
2037
+ "type",
2038
+ "brand",
2039
+ "status",
2040
+ "priority",
2041
+ "id",
2042
+ "name",
2043
+ "email",
2044
+ "phone",
2045
+ "price",
2046
+ "rating",
2047
+ "color",
2048
+ "size",
2049
+ "material",
2050
+ "sku",
2051
+ "role",
2052
+ "department",
2053
+ "location",
2054
+ "tag",
2055
+ "label"
2056
+ ];
2015
2057
 
2016
2058
  // src/core/Pipeline.ts
2017
2059
  var LRUEmbeddingCache = class {
@@ -2241,7 +2283,7 @@ var Pipeline = class {
2241
2283
  */
2242
2284
  askStream(_0) {
2243
2285
  return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
2244
- var _a, _b, _c, _d, _e, _f;
2286
+ var _a, _b, _c, _d, _e, _f, _g;
2245
2287
  yield new __await(this.initialize());
2246
2288
  const ns = namespace != null ? namespace : this.config.projectId;
2247
2289
  const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
@@ -2251,7 +2293,7 @@ var Pipeline = class {
2251
2293
  if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
2252
2294
  searchQuery = yield new __await(this.rewriteQuery(question, history));
2253
2295
  }
2254
- const hints = QueryProcessor.extractQueryFieldHints(question);
2296
+ const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
2255
2297
  const filter = QueryProcessor.buildQueryFilter(question, hints);
2256
2298
  const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
2257
2299
  namespace: ns,
@@ -2259,7 +2301,7 @@ var Pipeline = class {
2259
2301
  filter
2260
2302
  }));
2261
2303
  let sources = rawSources.filter((m) => m.score >= scoreThreshold);
2262
- if ((_f = this.config.rag) == null ? void 0 : _f.useReranking) {
2304
+ if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
2263
2305
  sources = yield new __await(this.reranker.rerank(sources, question, topK));
2264
2306
  } else {
2265
2307
  sources = sources.slice(0, topK);
@@ -1,3 +1,3 @@
1
- import '../RagConfig-BEmz4hVP.mjs';
2
- export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-DFSy0CG6.mjs';
1
+ import '../RagConfig-BjC6zSTV.mjs';
2
+ export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-CU_fQq__.mjs';
3
3
  import 'next/server';
@@ -1,3 +1,3 @@
1
- import '../RagConfig-BEmz4hVP.js';
2
- export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-CSfaCeux.js';
1
+ import '../RagConfig-BjC6zSTV.js';
2
+ export { c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from '../index-C3bLmWcR.js';
3
3
  import 'next/server';
@@ -3362,15 +3362,18 @@ var QueryProcessor = class {
3362
3362
  * Checks if a string is likely a question word or common prompt phrase.
3363
3363
  */
3364
3364
  static isLikelyPromptPhrase(value) {
3365
- return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
3365
+ return /^(what|which|who|where|when|why|how|the|is|are|was|were|in|on|at|by|for|with|about|a|an|to|of)\b/i.test(value.trim());
3366
3366
  }
3367
3367
  /**
3368
3368
  * Scans a natural language question for potential metadata hints and keywords.
3369
+ * @param question The user's query
3370
+ * @param validFields Optional list of known filterable fields to look for
3369
3371
  */
3370
- static extractQueryFieldHints(question) {
3372
+ static extractQueryFieldHints(question, validFields = []) {
3371
3373
  var _a, _b, _c, _d;
3372
3374
  if (!question.trim()) return [];
3373
3375
  const hints = /* @__PURE__ */ new Map();
3376
+ const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
3374
3377
  const addHint = (value, field) => {
3375
3378
  const normalizedValue = this.normalizeHintValue(value);
3376
3379
  if (!normalizedValue) return;
@@ -3401,11 +3404,28 @@ var QueryProcessor = class {
3401
3404
  if (name) addHint(name, "name");
3402
3405
  }
3403
3406
  }
3407
+ if (fieldsToSearch.length > 0) {
3408
+ for (const field of fieldsToSearch) {
3409
+ const forwardPattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{2,60})["']?(?=[?.!,]|$)`, "gi");
3410
+ for (const match of question.matchAll(forwardPattern)) {
3411
+ const value = match[1];
3412
+ if (value && !this.isLikelyPromptPhrase(value)) {
3413
+ addHint(value, field);
3414
+ }
3415
+ }
3416
+ const reversePattern = new RegExp(`\\b([^"\\n?.!,\\s]{2,60})\\s+${field}\\b`, "gi");
3417
+ for (const match of question.matchAll(reversePattern)) {
3418
+ const value = match[1];
3419
+ if (value && !this.isLikelyPromptPhrase(value)) {
3420
+ addHint(value, field);
3421
+ }
3422
+ }
3423
+ }
3424
+ }
3404
3425
  const universalPatterns = [
3405
3426
  { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: "email", group: 1 },
3406
3427
  { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: "phone", group: 1 },
3407
3428
  { regex: /(\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2}\/\d{1,2}\/\d{2,4}\b)/g, field: "date", group: 1 },
3408
- { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: "amount", group: 1 },
3409
3429
  { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: "id", group: 2 },
3410
3430
  { regex: /"([^"]{2,120})"/g, group: 1 },
3411
3431
  { regex: /'([^']{2,120})'/g, group: 1 }
@@ -3470,6 +3490,28 @@ var QueryProcessor = class {
3470
3490
  return filter;
3471
3491
  }
3472
3492
  };
3493
+ QueryProcessor.COMMON_METADATA_FIELDS = [
3494
+ "category",
3495
+ "type",
3496
+ "brand",
3497
+ "status",
3498
+ "priority",
3499
+ "id",
3500
+ "name",
3501
+ "email",
3502
+ "phone",
3503
+ "price",
3504
+ "rating",
3505
+ "color",
3506
+ "size",
3507
+ "material",
3508
+ "sku",
3509
+ "role",
3510
+ "department",
3511
+ "location",
3512
+ "tag",
3513
+ "label"
3514
+ ];
3473
3515
 
3474
3516
  // src/core/Pipeline.ts
3475
3517
  var LRUEmbeddingCache = class {
@@ -3699,7 +3741,7 @@ var Pipeline = class {
3699
3741
  */
3700
3742
  askStream(_0) {
3701
3743
  return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
3702
- var _a, _b, _c, _d, _e, _f;
3744
+ var _a, _b, _c, _d, _e, _f, _g;
3703
3745
  yield new __await(this.initialize());
3704
3746
  const ns = namespace != null ? namespace : this.config.projectId;
3705
3747
  const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
@@ -3709,7 +3751,7 @@ var Pipeline = class {
3709
3751
  if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
3710
3752
  searchQuery = yield new __await(this.rewriteQuery(question, history));
3711
3753
  }
3712
- const hints = QueryProcessor.extractQueryFieldHints(question);
3754
+ const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
3713
3755
  const filter = QueryProcessor.buildQueryFilter(question, hints);
3714
3756
  const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
3715
3757
  namespace: ns,
@@ -3717,7 +3759,7 @@ var Pipeline = class {
3717
3759
  filter
3718
3760
  }));
3719
3761
  let sources = rawSources.filter((m) => m.score >= scoreThreshold);
3720
- if ((_f = this.config.rag) == null ? void 0 : _f.useReranking) {
3762
+ if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
3721
3763
  sources = yield new __await(this.reranker.rerank(sources, question, topK));
3722
3764
  } else {
3723
3765
  sources = sources.slice(0, topK);
@@ -8,7 +8,7 @@ import {
8
8
  sseFrame,
9
9
  sseMetaFrame,
10
10
  sseTextFrame
11
- } from "../chunk-MWL4AGQI.mjs";
11
+ } from "../chunk-ZCDJSGUW.mjs";
12
12
  import "../chunk-YLTMFW4M.mjs";
13
13
  import "../chunk-X4TOT24V.mjs";
14
14
  export {
@@ -1,4 +1,4 @@
1
- import { g as RagConfig, C as ChatMessage, b as ChatResponse, e as IngestDocument } from './RagConfig-BEmz4hVP.js';
1
+ import { g as RagConfig, C as ChatMessage, b as ChatResponse, e as IngestDocument } from './RagConfig-BjC6zSTV.js';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
 
4
4
  interface ValidationError {
@@ -1,4 +1,4 @@
1
- import { g as RagConfig, C as ChatMessage, b as ChatResponse, e as IngestDocument } from './RagConfig-BEmz4hVP.mjs';
1
+ import { g as RagConfig, C as ChatMessage, b as ChatResponse, e as IngestDocument } from './RagConfig-BjC6zSTV.mjs';
2
2
  import { NextRequest, NextResponse } from 'next/server';
3
3
 
4
4
  interface ValidationError {
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { C as ChatMessage, V as VectorMatch, U as UIConfig } from './RagConfig-BEmz4hVP.mjs';
3
- export { a as ChatOptions, b as ChatResponse, E as EmbedOptions, c as EmbeddingConfig, d as EmbeddingProvider, I as ILLMProvider, e as IngestDocument, L as LLMConfig, f as LLMProvider, R as RAGConfig, g as RagConfig, h as UpsertDocument, i as VectorDBConfig, j as VectorDBProvider } from './RagConfig-BEmz4hVP.mjs';
2
+ import { C as ChatMessage, V as VectorMatch, U as UIConfig } from './RagConfig-BjC6zSTV.mjs';
3
+ export { a as ChatOptions, b as ChatResponse, E as EmbedOptions, c as EmbeddingConfig, d as EmbeddingProvider, I as ILLMProvider, e as IngestDocument, L as LLMConfig, f as LLMProvider, R as RAGConfig, g as RagConfig, h as UpsertDocument, i as VectorDBConfig, j as VectorDBProvider } from './RagConfig-BjC6zSTV.mjs';
4
4
  export { C as Chunk, a as ChunkOptions } from './DocumentChunker-Dh9TvmGG.mjs';
5
5
 
6
6
  interface ChatWidgetProps {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1, { ReactNode } from 'react';
2
- import { C as ChatMessage, V as VectorMatch, U as UIConfig } from './RagConfig-BEmz4hVP.js';
3
- export { a as ChatOptions, b as ChatResponse, E as EmbedOptions, c as EmbeddingConfig, d as EmbeddingProvider, I as ILLMProvider, e as IngestDocument, L as LLMConfig, f as LLMProvider, R as RAGConfig, g as RagConfig, h as UpsertDocument, i as VectorDBConfig, j as VectorDBProvider } from './RagConfig-BEmz4hVP.js';
2
+ import { C as ChatMessage, V as VectorMatch, U as UIConfig } from './RagConfig-BjC6zSTV.js';
3
+ export { a as ChatOptions, b as ChatResponse, E as EmbedOptions, c as EmbeddingConfig, d as EmbeddingProvider, I as ILLMProvider, e as IngestDocument, L as LLMConfig, f as LLMProvider, R as RAGConfig, g as RagConfig, h as UpsertDocument, i as VectorDBConfig, j as VectorDBProvider } from './RagConfig-BjC6zSTV.js';
4
4
  export { C as Chunk, a as ChunkOptions } from './DocumentChunker-Dh9TvmGG.js';
5
5
 
6
6
  interface ChatWidgetProps {
package/dist/server.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { i as VectorDBConfig, L as LLMConfig, c as EmbeddingConfig, g as RagConfig, e as IngestDocument, C as ChatMessage, b as ChatResponse, k as RetrievalResult, h as UpsertDocument, V as VectorMatch, G as GraphDBConfig, l as GraphNode, m as Edge, n as GraphSearchResult, I as ILLMProvider, j as VectorDBProvider, f as LLMProvider, d as EmbeddingProvider, R as RAGConfig, U as UIConfig, a as ChatOptions, E as EmbedOptions } from './RagConfig-BEmz4hVP.mjs';
1
+ import { i as VectorDBConfig, L as LLMConfig, c as EmbeddingConfig, g as RagConfig, e as IngestDocument, C as ChatMessage, b as ChatResponse, k as RetrievalResult, h as UpsertDocument, V as VectorMatch, G as GraphDBConfig, l as GraphNode, m as Edge, n as GraphSearchResult, I as ILLMProvider, j as VectorDBProvider, f as LLMProvider, d as EmbeddingProvider, R as RAGConfig, U as UIConfig, a as ChatOptions, E as EmbedOptions } from './RagConfig-BjC6zSTV.mjs';
2
2
  export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-Dh9TvmGG.mjs';
3
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-DFSy0CG6.mjs';
4
- export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-DFSy0CG6.mjs';
3
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-CU_fQq__.mjs';
4
+ export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-CU_fQq__.mjs';
5
5
  import 'next/server';
6
6
 
7
7
  /**
package/dist/server.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { i as VectorDBConfig, L as LLMConfig, c as EmbeddingConfig, g as RagConfig, e as IngestDocument, C as ChatMessage, b as ChatResponse, k as RetrievalResult, h as UpsertDocument, V as VectorMatch, G as GraphDBConfig, l as GraphNode, m as Edge, n as GraphSearchResult, I as ILLMProvider, j as VectorDBProvider, f as LLMProvider, d as EmbeddingProvider, R as RAGConfig, U as UIConfig, a as ChatOptions, E as EmbedOptions } from './RagConfig-BEmz4hVP.js';
1
+ import { i as VectorDBConfig, L as LLMConfig, c as EmbeddingConfig, g as RagConfig, e as IngestDocument, C as ChatMessage, b as ChatResponse, k as RetrievalResult, h as UpsertDocument, V as VectorMatch, G as GraphDBConfig, l as GraphNode, m as Edge, n as GraphSearchResult, I as ILLMProvider, j as VectorDBProvider, f as LLMProvider, d as EmbeddingProvider, R as RAGConfig, U as UIConfig, a as ChatOptions, E as EmbedOptions } from './RagConfig-BjC6zSTV.js';
2
2
  export { C as Chunk, a as ChunkOptions, D as DocumentChunker } from './DocumentChunker-Dh9TvmGG.js';
3
- import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-CSfaCeux.js';
4
- export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-CSfaCeux.js';
3
+ import { H as HealthCheckResult, I as IProviderValidator, a as IProviderHealthChecker } from './index-C3bLmWcR.js';
4
+ export { C as ConfigValidator, V as ValidationError, b as VectorPlugin, c as createChatHandler, d as createHealthHandler, e as createIngestHandler, f as createStreamHandler, g as createUploadHandler, s as sseErrorFrame, h as sseFrame, i as sseMetaFrame, j as sseTextFrame } from './index-C3bLmWcR.js';
5
5
  import 'next/server';
6
6
 
7
7
  /**
package/dist/server.js CHANGED
@@ -3455,15 +3455,18 @@ var QueryProcessor = class {
3455
3455
  * Checks if a string is likely a question word or common prompt phrase.
3456
3456
  */
3457
3457
  static isLikelyPromptPhrase(value) {
3458
- return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
3458
+ return /^(what|which|who|where|when|why|how|the|is|are|was|were|in|on|at|by|for|with|about|a|an|to|of)\b/i.test(value.trim());
3459
3459
  }
3460
3460
  /**
3461
3461
  * Scans a natural language question for potential metadata hints and keywords.
3462
+ * @param question The user's query
3463
+ * @param validFields Optional list of known filterable fields to look for
3462
3464
  */
3463
- static extractQueryFieldHints(question) {
3465
+ static extractQueryFieldHints(question, validFields = []) {
3464
3466
  var _a, _b, _c, _d;
3465
3467
  if (!question.trim()) return [];
3466
3468
  const hints = /* @__PURE__ */ new Map();
3469
+ const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
3467
3470
  const addHint = (value, field) => {
3468
3471
  const normalizedValue = this.normalizeHintValue(value);
3469
3472
  if (!normalizedValue) return;
@@ -3494,11 +3497,28 @@ var QueryProcessor = class {
3494
3497
  if (name) addHint(name, "name");
3495
3498
  }
3496
3499
  }
3500
+ if (fieldsToSearch.length > 0) {
3501
+ for (const field of fieldsToSearch) {
3502
+ const forwardPattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{2,60})["']?(?=[?.!,]|$)`, "gi");
3503
+ for (const match of question.matchAll(forwardPattern)) {
3504
+ const value = match[1];
3505
+ if (value && !this.isLikelyPromptPhrase(value)) {
3506
+ addHint(value, field);
3507
+ }
3508
+ }
3509
+ const reversePattern = new RegExp(`\\b([^"\\n?.!,\\s]{2,60})\\s+${field}\\b`, "gi");
3510
+ for (const match of question.matchAll(reversePattern)) {
3511
+ const value = match[1];
3512
+ if (value && !this.isLikelyPromptPhrase(value)) {
3513
+ addHint(value, field);
3514
+ }
3515
+ }
3516
+ }
3517
+ }
3497
3518
  const universalPatterns = [
3498
3519
  { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: "email", group: 1 },
3499
3520
  { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: "phone", group: 1 },
3500
3521
  { regex: /(\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2}\/\d{1,2}\/\d{2,4}\b)/g, field: "date", group: 1 },
3501
- { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: "amount", group: 1 },
3502
3522
  { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: "id", group: 2 },
3503
3523
  { regex: /"([^"]{2,120})"/g, group: 1 },
3504
3524
  { regex: /'([^']{2,120})'/g, group: 1 }
@@ -3563,6 +3583,28 @@ var QueryProcessor = class {
3563
3583
  return filter;
3564
3584
  }
3565
3585
  };
3586
+ QueryProcessor.COMMON_METADATA_FIELDS = [
3587
+ "category",
3588
+ "type",
3589
+ "brand",
3590
+ "status",
3591
+ "priority",
3592
+ "id",
3593
+ "name",
3594
+ "email",
3595
+ "phone",
3596
+ "price",
3597
+ "rating",
3598
+ "color",
3599
+ "size",
3600
+ "material",
3601
+ "sku",
3602
+ "role",
3603
+ "department",
3604
+ "location",
3605
+ "tag",
3606
+ "label"
3607
+ ];
3566
3608
 
3567
3609
  // src/core/Pipeline.ts
3568
3610
  var LRUEmbeddingCache = class {
@@ -3792,7 +3834,7 @@ var Pipeline = class {
3792
3834
  */
3793
3835
  askStream(_0) {
3794
3836
  return __asyncGenerator(this, arguments, function* (question, history = [], namespace) {
3795
- var _a, _b, _c, _d, _e, _f;
3837
+ var _a, _b, _c, _d, _e, _f, _g;
3796
3838
  yield new __await(this.initialize());
3797
3839
  const ns = namespace != null ? namespace : this.config.projectId;
3798
3840
  const topK = (_b = (_a = this.config.rag) == null ? void 0 : _a.topK) != null ? _b : 5;
@@ -3802,7 +3844,7 @@ var Pipeline = class {
3802
3844
  if ((_e = this.config.rag) == null ? void 0 : _e.useQueryTransformation) {
3803
3845
  searchQuery = yield new __await(this.rewriteQuery(question, history));
3804
3846
  }
3805
- const hints = QueryProcessor.extractQueryFieldHints(question);
3847
+ const hints = QueryProcessor.extractQueryFieldHints(question, (_f = this.config.rag) == null ? void 0 : _f.filterableFields);
3806
3848
  const filter = QueryProcessor.buildQueryFilter(question, hints);
3807
3849
  const { sources: rawSources, graphData } = yield new __await(this.retrieve(searchQuery, {
3808
3850
  namespace: ns,
@@ -3810,7 +3852,7 @@ var Pipeline = class {
3810
3852
  filter
3811
3853
  }));
3812
3854
  let sources = rawSources.filter((m) => m.score >= scoreThreshold);
3813
- if ((_f = this.config.rag) == null ? void 0 : _f.useReranking) {
3855
+ if ((_g = this.config.rag) == null ? void 0 : _g.useReranking) {
3814
3856
  sources = yield new __await(this.reranker.rerank(sources, question, topK));
3815
3857
  } else {
3816
3858
  sources = sources.slice(0, topK);
package/dist/server.mjs CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  sseFrame,
41
41
  sseMetaFrame,
42
42
  sseTextFrame
43
- } from "./chunk-MWL4AGQI.mjs";
43
+ } from "./chunk-ZCDJSGUW.mjs";
44
44
  import "./chunk-YLTMFW4M.mjs";
45
45
  import {
46
46
  PineconeProvider
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
5
  "author": "Abhinav Alkuchi",
6
6
  "license": "MIT",
@@ -174,6 +174,10 @@ export interface RAGConfig {
174
174
  useGraphRetrieval?: boolean;
175
175
  /** Whether to perform reranking on retrieved results */
176
176
  useReranking?: boolean;
177
+ /** List of metadata fields that are valid for filtering.
178
+ * Used to dynamically extract hints from natural language queries.
179
+ */
180
+ filterableFields?: string[];
177
181
  }
178
182
 
179
183
  // ---------------------------------------------------------------------------
@@ -315,7 +315,7 @@ export class Pipeline {
315
315
  }
316
316
 
317
317
  // 2. Parallel Retrieval
318
- const hints = QueryProcessor.extractQueryFieldHints(question);
318
+ const hints = QueryProcessor.extractQueryFieldHints(question, this.config.rag?.filterableFields);
319
319
  const filter = QueryProcessor.buildQueryFilter(question, hints);
320
320
 
321
321
  const { sources: rawSources, graphData } = await this.retrieve(searchQuery, {
@@ -31,16 +31,25 @@ export class QueryProcessor {
31
31
  * Checks if a string is likely a question word or common prompt phrase.
32
32
  */
33
33
  static isLikelyPromptPhrase(value: string): boolean {
34
- return /^(what|which|who|where|when|why|how)\b/i.test(value.trim());
34
+ return /^(what|which|who|where|when|why|how|the|is|are|was|were|in|on|at|by|for|with|about|a|an|to|of)\b/i.test(value.trim());
35
35
  }
36
36
 
37
+ private static readonly COMMON_METADATA_FIELDS = [
38
+ 'category', 'type', 'brand', 'status', 'priority', 'id', 'name',
39
+ 'email', 'phone', 'price', 'rating', 'color', 'size', 'material',
40
+ 'sku', 'role', 'department', 'location', 'tag', 'label'
41
+ ];
42
+
37
43
  /**
38
44
  * Scans a natural language question for potential metadata hints and keywords.
45
+ * @param question The user's query
46
+ * @param validFields Optional list of known filterable fields to look for
39
47
  */
40
- static extractQueryFieldHints(question: string): QueryFieldHint[] {
48
+ static extractQueryFieldHints(question: string, validFields: string[] = []): QueryFieldHint[] {
41
49
  if (!question.trim()) return [];
42
50
 
43
51
  const hints = new Map<string, QueryFieldHint>();
52
+ const fieldsToSearch = [...new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
44
53
 
45
54
  const addHint = (value: string, field?: string) => {
46
55
  const normalizedValue = this.normalizeHintValue(value);
@@ -88,12 +97,35 @@ export class QueryProcessor {
88
97
  }
89
98
  }
90
99
 
91
- // 4. Universal patterns (email, phone, etc.)
100
+ // 4. Dynamic Schema-driven patterns
101
+ // We look for common metadata fields (and any user-defined ones) in natural language.
102
+ if (fieldsToSearch.length > 0) {
103
+ for (const field of fieldsToSearch) {
104
+ // Pattern 1: "[field] is [value]", "[field]: [value]", "[field] of [value]"
105
+ const forwardPattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{2,60})["']?(?=[?.!,]|$)`, 'gi');
106
+ for (const match of question.matchAll(forwardPattern)) {
107
+ const value = match[1];
108
+ if (value && !this.isLikelyPromptPhrase(value)) {
109
+ addHint(value, field);
110
+ }
111
+ }
112
+
113
+ // Pattern 2: "[value] [field]" (e.g. "blue color", "electronics category")
114
+ const reversePattern = new RegExp(`\\b([^"\\n?.!,\\s]{2,60})\\s+${field}\\b`, 'gi');
115
+ for (const match of question.matchAll(reversePattern)) {
116
+ const value = match[1];
117
+ if (value && !this.isLikelyPromptPhrase(value)) {
118
+ addHint(value, field);
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ // 5. Universal patterns (email, phone, etc.)
92
125
  const universalPatterns: Array<{ regex: RegExp; field?: string; group?: number }> = [
93
126
  { regex: /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/gi, field: 'email', group: 1 },
94
127
  { regex: /(\+?\d[\d\-\.\s\(\)]{6,}\d)/g, field: 'phone', group: 1 },
95
128
  { regex: /(\b\d{4}-\d{2}-\d{2}\b|\b\d{1,2}\/\d{1,2}\/\d{2,4}\b)/g, field: 'date', group: 1 },
96
- { regex: /(\$\s?\d{1,3}(?:,\d{3})*(?:\.\d+)?)/g, field: 'amount', group: 1 },
97
129
  { regex: /\b(ID|id|identifier)[: ]\s*([A-Za-z0-9\-]{3,})\b/gi, field: 'id', group: 2 },
98
130
  { regex: /"([^"]{2,120})"/g, group: 1 },
99
131
  { regex: /'([^']{2,120})'/g, group: 1 },