@retrivora-ai/rag-engine 1.8.0 → 1.8.2

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 (71) hide show
  1. package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
  2. package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
  3. package/dist/{index-DPsQodME.d.mts → ILLMProvider-BOJFz3Na.d.mts} +104 -1
  4. package/dist/{index-DPsQodME.d.ts → ILLMProvider-BOJFz3Na.d.ts} +104 -1
  5. package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +8 -0
  6. package/dist/chunk-ICKRMZQK.mjs +76 -0
  7. package/dist/{chunk-PV3MFHWU.mjs → chunk-LZVVLSDN.mjs} +977 -516
  8. package/dist/chunk-OZFBG4BA.mjs +291 -0
  9. package/dist/handlers/index.d.mts +2 -2
  10. package/dist/handlers/index.d.ts +2 -2
  11. package/dist/handlers/index.js +1269 -656
  12. package/dist/handlers/index.mjs +4 -1
  13. package/dist/{index-Bb2yEopi.d.mts → index-BwpcaziY.d.ts} +10 -2
  14. package/dist/{index-CkbTzj9J.d.ts → index-D3V9Et2M.d.mts} +10 -2
  15. package/dist/index.d.mts +24 -4
  16. package/dist/index.d.ts +24 -4
  17. package/dist/index.js +1354 -826
  18. package/dist/index.mjs +1284 -795
  19. package/dist/server.d.mts +47 -27
  20. package/dist/server.d.ts +47 -27
  21. package/dist/server.js +1417 -829
  22. package/dist/server.mjs +164 -176
  23. package/package.json +6 -2
  24. package/src/app/api/upload/route.ts +4 -0
  25. package/src/app/constants.tsx +2 -2
  26. package/src/app/page.tsx +12 -322
  27. package/src/components/AmbientBackground.tsx +29 -0
  28. package/src/components/ArchitectureCard.tsx +17 -0
  29. package/src/components/ArchitectureCardsSection.tsx +15 -0
  30. package/src/components/ChatWindow.tsx +32 -0
  31. package/src/components/CodeViewer.tsx +51 -0
  32. package/src/components/ConfigProvider.tsx +1 -0
  33. package/src/components/DocViewer.tsx +37 -0
  34. package/src/components/DocumentUpload.tsx +44 -1
  35. package/src/components/Documentation.tsx +58 -0
  36. package/src/components/DynamicChart.tsx +27 -2
  37. package/src/components/Hero.tsx +59 -0
  38. package/src/components/HourglassLoader.tsx +87 -0
  39. package/src/components/Lifecycle.tsx +37 -0
  40. package/src/components/MarkdownComponents.tsx +140 -0
  41. package/src/components/MessageBubble.tsx +124 -904
  42. package/src/components/Navbar.tsx +55 -0
  43. package/src/components/ObservabilityPanel.tsx +374 -0
  44. package/src/components/ProductCard.tsx +5 -3
  45. package/src/components/UIDispatcher.tsx +344 -0
  46. package/src/components/VisualizationRenderer.tsx +372 -250
  47. package/src/config/RagConfig.ts +5 -0
  48. package/src/config/serverConfig.ts +3 -1
  49. package/src/core/Pipeline.ts +240 -271
  50. package/src/core/ProviderRegistry.ts +2 -2
  51. package/src/core/VectorPlugin.ts +9 -0
  52. package/src/handlers/index.ts +91 -15
  53. package/src/hooks/useRagChat.ts +21 -11
  54. package/src/index.ts +9 -1
  55. package/src/llm/LLMFactory.ts +54 -2
  56. package/src/llm/providers/AnthropicProvider.ts +12 -8
  57. package/src/llm/providers/GeminiProvider.ts +188 -143
  58. package/src/llm/providers/OllamaProvider.ts +7 -3
  59. package/src/llm/providers/OpenAIProvider.ts +12 -8
  60. package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
  61. package/src/types/chat.ts +8 -0
  62. package/src/types/index.ts +132 -0
  63. package/src/types/props.ts +9 -1
  64. package/src/utils/ProductExtractor.ts +347 -0
  65. package/src/utils/SchemaMapper.ts +129 -0
  66. package/src/utils/UITransformer.ts +470 -209
  67. package/src/utils/synonyms.ts +78 -0
  68. package/dist/DocumentChunker-C1GEEosY.d.ts +0 -93
  69. package/dist/DocumentChunker-CFEiRopR.d.mts +0 -93
  70. package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
  71. package/dist/chunk-FLOSGE6A.mjs +0 -202
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Shared synonym map used by UITransformer and MessageBubble to resolve
3
+ * schema-agnostic field names from vector metadata.
4
+ */
5
+ export const FIELD_SYNONYMS: Record<string, string[]> = {
6
+ name: ['product', 'item', 'title', 'label', 'heading', 'subject', 'product name', 'item name'],
7
+ price: ['cost', 'amount', 'msrp', 'price', 'rate', 'value', 'price_usd'],
8
+ brand: ['manufacturer', 'vendor', 'make', 'company', 'brand_name', 'supplier'],
9
+ image: ['imageUrl', 'thumbnail', 'img', 'url', 'photo', 'picture', 'media', 'image_url',
10
+ 'main_image', 'product_image', 'thumb'],
11
+ stock: ['inventory', 'quantity', 'count', 'availability', 'stock_level', 'inStock',
12
+ 'is_available', 'in stock', 'status'],
13
+ description: ['summary', 'content', 'body', 'text', 'info', 'details'],
14
+ link: ['url', 'href', 'product_url', 'page_url', 'link'],
15
+ };
16
+
17
+ /**
18
+ * Dynamically registers new synonyms into the global FIELD_SYNONYMS dictionary.
19
+ * You can call this during your app initialization to support custom schema fields.
20
+ */
21
+ export function addSynonyms(customSynonyms: Record<string, string[]>) {
22
+ for (const [key, aliases] of Object.entries(customSynonyms)) {
23
+ if (!FIELD_SYNONYMS[key]) {
24
+ FIELD_SYNONYMS[key] = [];
25
+ }
26
+ const existing = new Set(FIELD_SYNONYMS[key].map(s => s.toLowerCase()));
27
+
28
+ for (const alias of aliases) {
29
+ if (!existing.has(alias.toLowerCase())) {
30
+ FIELD_SYNONYMS[key].push(alias);
31
+ existing.add(alias.toLowerCase());
32
+ }
33
+ }
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Robustly extract a value from metadata by checking exact, case-insensitive,
39
+ * and synonym-based key matches.
40
+ */
41
+ export function resolveMetadataValue(
42
+ meta: Record<string, unknown>,
43
+ uiKey: string,
44
+ ): unknown {
45
+ const synonyms = FIELD_SYNONYMS[uiKey] ?? [];
46
+
47
+ const keys = Object.keys(meta);
48
+ const systemKeys = ['namespace', 'filename', 'filesize', 'filetype', 'docid', 'uploadedat', 'dimension', 'chunkindex'];
49
+
50
+ // 1. Exact case-insensitive match for primary key
51
+ let match = keys.find((k) => k.toLowerCase() === uiKey.toLowerCase());
52
+ if (match !== undefined) return meta[match];
53
+
54
+ // 2. Exact case-insensitive match for synonyms
55
+ match = keys.find((k) => synonyms.map(s => s.toLowerCase()).includes(k.toLowerCase()));
56
+ if (match !== undefined) return meta[match];
57
+
58
+ const isBlacklisted = (kl: string) => {
59
+ return systemKeys.includes(kl) || kl.startsWith('option1') || kl.startsWith('option2') || kl.startsWith('option3');
60
+ };
61
+
62
+ // 3. Partial match (substring) strictly for primary key
63
+ match = keys.find((k) => {
64
+ const kl = k.toLowerCase();
65
+ if (isBlacklisted(kl)) return false;
66
+ return kl.includes(uiKey.toLowerCase());
67
+ });
68
+ if (match !== undefined) return meta[match];
69
+
70
+ // 4. Partial match (substring) for synonyms
71
+ match = keys.find((k) => {
72
+ const kl = k.toLowerCase();
73
+ if (isBlacklisted(kl)) return false;
74
+ return synonyms.some((sk) => kl.includes(sk.toLowerCase()) || sk.toLowerCase().includes(kl));
75
+ });
76
+
77
+ return match !== undefined ? meta[match] : undefined;
78
+ }
@@ -1,93 +0,0 @@
1
- import { C as ChatMessage, c as ChatOptions } from './index-DPsQodME.js';
2
-
3
- /**
4
- * Generic LLM Provider interface.
5
- * Covers both chat completion and embedding generation so a single
6
- * provider can handle both responsibilities when appropriate.
7
- */
8
-
9
- interface EmbedOptions {
10
- /** Override model for this specific embed call */
11
- model?: string;
12
- /** Specify the task type for models that require prefixes (e.g. nomic-embed-text) */
13
- taskType?: 'query' | 'document';
14
- }
15
- interface ILLMProvider {
16
- /**
17
- * Send a chat completion request.
18
- * @param messages – the full conversation history
19
- * @param context – retrieved RAG context to inject
20
- * @param options – optional per-call overrides
21
- * @returns – the assistant's reply text
22
- */
23
- chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
24
- /**
25
- * Send a streaming chat completion request.
26
- * @returns – an async iterable of text chunks
27
- */
28
- chatStream?(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
29
- /**
30
- * Generate an embedding vector for the given text.
31
- * @param text – text to embed
32
- * @param options – optional overrides
33
- * @returns – float array (the embedding)
34
- */
35
- embed(text: string, options?: EmbedOptions): Promise<number[]>;
36
- /**
37
- * Generate embedding vectors for multiple texts in a single batch.
38
- * @param texts – array of texts to embed
39
- * @param options – optional overrides
40
- * @returns – array of float arrays
41
- */
42
- batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
43
- /**
44
- * Check if the provider endpoint is reachable.
45
- */
46
- ping(): Promise<boolean>;
47
- }
48
-
49
- /**
50
- * DocumentChunker — splits long text into overlapping chunks
51
- * suitable for vector embedding and retrieval.
52
- *
53
- * Strategy: sentence-aware sliding window
54
- * 1. Split on sentence boundaries
55
- * 2. Accumulate sentences into chunks up to `chunkSize` characters
56
- * 3. Carry over `chunkOverlap` characters from the previous chunk
57
- */
58
- interface Chunk {
59
- id: string;
60
- content: string;
61
- metadata?: Record<string, unknown>;
62
- }
63
- interface ChunkOptions {
64
- /** Target chunk size in characters (default 1000) */
65
- chunkSize?: number;
66
- /** Overlap between consecutive chunks in characters (default 200) */
67
- chunkOverlap?: number;
68
- /** Source document identifier used as ID prefix */
69
- docId?: string | number;
70
- /** Extra metadata to attach to every chunk */
71
- metadata?: Record<string, unknown>;
72
- /** Characters used to split text, in order of priority */
73
- separators?: string[];
74
- }
75
- declare class DocumentChunker {
76
- private readonly chunkSize;
77
- private readonly chunkOverlap;
78
- private readonly separators;
79
- constructor(chunkSize?: number, chunkOverlap?: number, separators?: string[]);
80
- /**
81
- * Split a single text string into overlapping chunks using a recursive strategy.
82
- * Preserves structural boundaries (Markdown headers) where possible.
83
- */
84
- chunk(text: string, options?: ChunkOptions): Chunk[];
85
- private recursiveSplit;
86
- chunkMany(documents: Array<{
87
- content: string;
88
- docId?: string | number;
89
- metadata?: Record<string, unknown>;
90
- }>): Chunk[];
91
- }
92
-
93
- export { type Chunk as C, DocumentChunker as D, type EmbedOptions as E, type ILLMProvider as I, type ChunkOptions as a };
@@ -1,93 +0,0 @@
1
- import { C as ChatMessage, c as ChatOptions } from './index-DPsQodME.mjs';
2
-
3
- /**
4
- * Generic LLM Provider interface.
5
- * Covers both chat completion and embedding generation so a single
6
- * provider can handle both responsibilities when appropriate.
7
- */
8
-
9
- interface EmbedOptions {
10
- /** Override model for this specific embed call */
11
- model?: string;
12
- /** Specify the task type for models that require prefixes (e.g. nomic-embed-text) */
13
- taskType?: 'query' | 'document';
14
- }
15
- interface ILLMProvider {
16
- /**
17
- * Send a chat completion request.
18
- * @param messages – the full conversation history
19
- * @param context – retrieved RAG context to inject
20
- * @param options – optional per-call overrides
21
- * @returns – the assistant's reply text
22
- */
23
- chat(messages: ChatMessage[], context: string, options?: ChatOptions): Promise<string>;
24
- /**
25
- * Send a streaming chat completion request.
26
- * @returns – an async iterable of text chunks
27
- */
28
- chatStream?(messages: ChatMessage[], context: string, options?: ChatOptions): AsyncIterable<string>;
29
- /**
30
- * Generate an embedding vector for the given text.
31
- * @param text – text to embed
32
- * @param options – optional overrides
33
- * @returns – float array (the embedding)
34
- */
35
- embed(text: string, options?: EmbedOptions): Promise<number[]>;
36
- /**
37
- * Generate embedding vectors for multiple texts in a single batch.
38
- * @param texts – array of texts to embed
39
- * @param options – optional overrides
40
- * @returns – array of float arrays
41
- */
42
- batchEmbed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
43
- /**
44
- * Check if the provider endpoint is reachable.
45
- */
46
- ping(): Promise<boolean>;
47
- }
48
-
49
- /**
50
- * DocumentChunker — splits long text into overlapping chunks
51
- * suitable for vector embedding and retrieval.
52
- *
53
- * Strategy: sentence-aware sliding window
54
- * 1. Split on sentence boundaries
55
- * 2. Accumulate sentences into chunks up to `chunkSize` characters
56
- * 3. Carry over `chunkOverlap` characters from the previous chunk
57
- */
58
- interface Chunk {
59
- id: string;
60
- content: string;
61
- metadata?: Record<string, unknown>;
62
- }
63
- interface ChunkOptions {
64
- /** Target chunk size in characters (default 1000) */
65
- chunkSize?: number;
66
- /** Overlap between consecutive chunks in characters (default 200) */
67
- chunkOverlap?: number;
68
- /** Source document identifier used as ID prefix */
69
- docId?: string | number;
70
- /** Extra metadata to attach to every chunk */
71
- metadata?: Record<string, unknown>;
72
- /** Characters used to split text, in order of priority */
73
- separators?: string[];
74
- }
75
- declare class DocumentChunker {
76
- private readonly chunkSize;
77
- private readonly chunkOverlap;
78
- private readonly separators;
79
- constructor(chunkSize?: number, chunkOverlap?: number, separators?: string[]);
80
- /**
81
- * Split a single text string into overlapping chunks using a recursive strategy.
82
- * Preserves structural boundaries (Markdown headers) where possible.
83
- */
84
- chunk(text: string, options?: ChunkOptions): Chunk[];
85
- private recursiveSplit;
86
- chunkMany(documents: Array<{
87
- content: string;
88
- docId?: string | number;
89
- metadata?: Record<string, unknown>;
90
- }>): Chunk[];
91
- }
92
-
93
- export { type Chunk as C, DocumentChunker as D, type EmbedOptions as E, type ILLMProvider as I, type ChunkOptions as a };
@@ -1,8 +0,0 @@
1
- import {
2
- PostgreSQLProvider
3
- } from "./chunk-FLOSGE6A.mjs";
4
- import "./chunk-IMP6FUCY.mjs";
5
- import "./chunk-X4TOT24V.mjs";
6
- export {
7
- PostgreSQLProvider
8
- };
@@ -1,202 +0,0 @@
1
- import {
2
- BaseVectorProvider
3
- } from "./chunk-IMP6FUCY.mjs";
4
-
5
- // src/providers/vectordb/PostgreSQLProvider.ts
6
- import { Pool } from "pg";
7
- var PostgreSQLProvider = class extends BaseVectorProvider {
8
- constructor(config) {
9
- var _a;
10
- super(config);
11
- this.tableName = this.indexName.replace(/[^a-z0-9_]/gi, "_");
12
- const opts = config.options;
13
- if (!opts.connectionString) throw new Error("[PostgreSQLProvider] options.connectionString is required");
14
- this.connectionString = opts.connectionString;
15
- this.dimensions = (_a = opts.dimensions) != null ? _a : 1536;
16
- }
17
- static getValidator() {
18
- return {
19
- validate(config) {
20
- const errors = [];
21
- const opts = config.options || {};
22
- if (!opts.connectionString) {
23
- errors.push({
24
- field: "vectorDb.options.connectionString",
25
- message: "PostgreSQL connection string is required",
26
- suggestion: "Set PGVECTOR_CONNECTION_STRING environment variable",
27
- severity: "error"
28
- });
29
- }
30
- if (opts.tables && typeof opts.tables !== "string" && !Array.isArray(opts.tables)) {
31
- errors.push({
32
- field: "vectorDb.options.tables",
33
- message: "PostgreSQL tables must be a string or a string array",
34
- severity: "error"
35
- });
36
- }
37
- return errors;
38
- }
39
- };
40
- }
41
- static getHealthChecker() {
42
- return {
43
- async check(config) {
44
- const opts = config.options || {};
45
- const timestamp = Date.now();
46
- try {
47
- const { Client } = await import("pg");
48
- const client = new Client({ connectionString: opts.connectionString });
49
- await client.connect();
50
- const result = await client.query(`
51
- SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'vector');
52
- `);
53
- const hasVector = result.rows[0].exists;
54
- await client.end();
55
- return {
56
- healthy: true,
57
- provider: "postgresql",
58
- capabilities: { pgvectorInstalled: hasVector },
59
- timestamp
60
- };
61
- } catch (error) {
62
- return {
63
- healthy: false,
64
- provider: "postgresql",
65
- error: `Connection failed: ${error instanceof Error ? error.message : String(error)}`,
66
- timestamp
67
- };
68
- }
69
- }
70
- };
71
- }
72
- async initialize() {
73
- this.pool = new Pool({ connectionString: this.connectionString });
74
- const client = await this.pool.connect();
75
- try {
76
- await client.query("CREATE EXTENSION IF NOT EXISTS vector");
77
- await client.query(`
78
- CREATE TABLE IF NOT EXISTS ${this.tableName} (
79
- id TEXT PRIMARY KEY,
80
- namespace TEXT NOT NULL DEFAULT '',
81
- content TEXT NOT NULL,
82
- metadata JSONB,
83
- embedding VECTOR(${this.dimensions})
84
- )
85
- `);
86
- await client.query(`
87
- CREATE INDEX IF NOT EXISTS ${this.tableName}_embedding_idx
88
- ON ${this.tableName}
89
- USING hnsw (embedding vector_cosine_ops)
90
- `);
91
- } finally {
92
- client.release();
93
- }
94
- }
95
- async upsert(doc, namespace = "") {
96
- var _a;
97
- const vectorLiteral = `[${doc.vector.join(",")}]`;
98
- await this.pool.query(
99
- `INSERT INTO ${this.tableName} (id, namespace, content, metadata, embedding)
100
- VALUES ($1, $2, $3, $4, $5::vector)
101
- ON CONFLICT (id) DO UPDATE
102
- SET namespace = EXCLUDED.namespace,
103
- content = EXCLUDED.content,
104
- metadata = EXCLUDED.metadata,
105
- embedding = EXCLUDED.embedding`,
106
- [doc.id, namespace, doc.content, JSON.stringify((_a = doc.metadata) != null ? _a : {}), vectorLiteral]
107
- );
108
- }
109
- async batchUpsert(docs, namespace = "") {
110
- if (docs.length === 0) return;
111
- const client = await this.pool.connect();
112
- try {
113
- await client.query("BEGIN");
114
- const BATCH_SIZE = 50;
115
- for (let i = 0; i < docs.length; i += BATCH_SIZE) {
116
- const batch = docs.slice(i, i + BATCH_SIZE);
117
- const values = [];
118
- const valuePlaceholders = batch.map((doc, idx) => {
119
- var _a;
120
- const offset = idx * 5;
121
- values.push(doc.id, namespace, doc.content, JSON.stringify((_a = doc.metadata) != null ? _a : {}), `[${doc.vector.join(",")}]`);
122
- return `($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}::vector)`;
123
- }).join(", ");
124
- const query = `
125
- INSERT INTO ${this.tableName} (id, namespace, content, metadata, embedding)
126
- VALUES ${valuePlaceholders}
127
- ON CONFLICT (id) DO UPDATE
128
- SET namespace = EXCLUDED.namespace,
129
- content = EXCLUDED.content,
130
- metadata = EXCLUDED.metadata,
131
- embedding = EXCLUDED.embedding
132
- `;
133
- await client.query(query, values);
134
- }
135
- await client.query("COMMIT");
136
- } catch (error) {
137
- await client.query("ROLLBACK");
138
- throw error;
139
- } finally {
140
- client.release();
141
- }
142
- }
143
- async query(vector, topK, namespace, filter) {
144
- const vectorLiteral = `[${vector.join(",")}]`;
145
- let whereClause = namespace ? `WHERE namespace = $3` : "";
146
- const params = [vectorLiteral, topK];
147
- if (namespace) params.push(namespace);
148
- const publicFilter = this.sanitizeFilter(filter);
149
- if (Object.keys(publicFilter).length > 0) {
150
- const filterConditions = Object.entries(publicFilter).map(([key, val]) => {
151
- const paramIdx = params.length + 1;
152
- params.push(JSON.stringify(val));
153
- return `metadata->>'${key}' = $${paramIdx}`;
154
- }).join(" AND ");
155
- whereClause = whereClause ? `${whereClause} AND ${filterConditions}` : `WHERE ${filterConditions}`;
156
- }
157
- const client = await this.pool.connect();
158
- try {
159
- const efSearch = this.config.options.efSearch || Math.max(topK * 10, 40);
160
- await client.query(`SET LOCAL hnsw.ef_search = ${efSearch}`);
161
- const result = await client.query(
162
- `SELECT id, content, metadata, 1 - (embedding <=> $1::vector) AS score
163
- FROM ${this.tableName}
164
- ${whereClause}
165
- ORDER BY embedding <=> $1::vector
166
- LIMIT $2`,
167
- params
168
- );
169
- return result.rows.map((row) => ({
170
- id: String(row["id"]),
171
- score: parseFloat(String(row["score"])),
172
- content: String(row["content"]),
173
- metadata: row["metadata"]
174
- }));
175
- } finally {
176
- client.release();
177
- }
178
- }
179
- async delete(id, namespace) {
180
- const where = namespace ? "WHERE id = $1 AND namespace = $2" : "WHERE id = $1";
181
- const params = namespace ? [id, namespace] : [id];
182
- await this.pool.query(`DELETE FROM ${this.tableName} ${where}`, params);
183
- }
184
- async deleteNamespace(namespace) {
185
- await this.pool.query(`DELETE FROM ${this.tableName} WHERE namespace = $1`, [namespace]);
186
- }
187
- async ping() {
188
- try {
189
- await this.pool.query("SELECT 1");
190
- return true;
191
- } catch (e) {
192
- return false;
193
- }
194
- }
195
- async disconnect() {
196
- await this.pool.end();
197
- }
198
- };
199
-
200
- export {
201
- PostgreSQLProvider
202
- };