@sqlrooms/ai-rag 0.26.1-rc.0
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.
- package/LICENSE.md +9 -0
- package/README.md +485 -0
- package/dist/RagSlice.d.ts +107 -0
- package/dist/RagSlice.d.ts.map +1 -0
- package/dist/RagSlice.js +285 -0
- package/dist/RagSlice.js.map +1 -0
- package/dist/createAiEmbeddingProvider.d.ts +67 -0
- package/dist/createAiEmbeddingProvider.d.ts.map +1 -0
- package/dist/createAiEmbeddingProvider.js +77 -0
- package/dist/createAiEmbeddingProvider.js.map +1 -0
- package/dist/createRagTool.d.ts +48 -0
- package/dist/createRagTool.d.ts.map +1 -0
- package/dist/createRagTool.js +140 -0
- package/dist/createRagTool.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2025 Ilya Boyandin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the βSoftwareβ), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED βAS ISβ, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
Retrieval Augmented Generation (RAG) slice for SQLRooms. Query vector embeddings stored in DuckDB for semantic search and AI-powered applications.
|
|
2
|
+
|
|
3
|
+
This package is designed to work with [sqlrooms-rag](https://pypi.org/project/sqlrooms-rag/), a Python package that prepares embedding/FTS-index databases for RAG search.
|
|
4
|
+
|
|
5
|
+
Refer to the [ai-rag example](https://github.com/sqlrooms/examples/tree/main/ai-rag) for a complete working example.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- π **Hybrid Search** - Combines vector similarity with full-text search (BM25) using Reciprocal Rank Fusion
|
|
10
|
+
- π **Semantic Search** - Query embeddings using vector similarity (cosine similarity)
|
|
11
|
+
- ποΈ **Multiple Databases** - Attach and search across multiple embedding databases
|
|
12
|
+
- π― **Per-Database Embedding Providers** - Each database can use a different embedding model
|
|
13
|
+
- β
**Metadata Validation** - Automatic validation of embedding dimensions and models
|
|
14
|
+
- π **DuckDB-Powered** - Fast, in-process vector search with SQL and FTS
|
|
15
|
+
- π **Flexible** - Works with OpenAI, HuggingFace, Transformers.js, or custom embeddings
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @sqlrooms/ai-rag @sqlrooms/duckdb @sqlrooms/room-store
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import {createDuckDbSlice} from '@sqlrooms/duckdb';
|
|
27
|
+
import {createRagSlice, createAiEmbeddingProvider} from '@sqlrooms/ai-rag';
|
|
28
|
+
import {createRoomStore} from '@sqlrooms/room-store';
|
|
29
|
+
import {openai} from '@ai-sdk/openai';
|
|
30
|
+
|
|
31
|
+
// 1. Create an embedding provider (matches your database preparation)
|
|
32
|
+
const embeddingProvider = createAiEmbeddingProvider(
|
|
33
|
+
openai,
|
|
34
|
+
'text-embedding-3-small',
|
|
35
|
+
1536,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// 2. Configure your embedding databases
|
|
39
|
+
const embeddingsDatabases = [
|
|
40
|
+
{
|
|
41
|
+
databaseFilePathOrUrl: '/path/to/docs.duckdb',
|
|
42
|
+
databaseName: 'docs',
|
|
43
|
+
embeddingProvider,
|
|
44
|
+
embeddingDimensions: 1536,
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// 3. Create the store with RAG capabilities
|
|
49
|
+
const store = createRoomStore({
|
|
50
|
+
slices: [
|
|
51
|
+
createDuckDbSlice({databasePath: ':memory:'}),
|
|
52
|
+
createRagSlice({embeddingsDatabases}),
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// 4. Initialize and query
|
|
57
|
+
await store.getState().rag.initialize();
|
|
58
|
+
|
|
59
|
+
const results = await store
|
|
60
|
+
.getState()
|
|
61
|
+
.rag.queryByText('How do I create a table?', {topK: 5});
|
|
62
|
+
|
|
63
|
+
console.log(results);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API Reference
|
|
67
|
+
|
|
68
|
+
### `createRagSlice(options)`
|
|
69
|
+
|
|
70
|
+
Creates a RAG slice for your store.
|
|
71
|
+
|
|
72
|
+
#### Options
|
|
73
|
+
|
|
74
|
+
- `embeddingsDatabases` - Array of embedding database configurations
|
|
75
|
+
|
|
76
|
+
#### Returns
|
|
77
|
+
|
|
78
|
+
A state creator function for Zustand.
|
|
79
|
+
|
|
80
|
+
### `EmbeddingDatabase`
|
|
81
|
+
|
|
82
|
+
Configuration for an embedding database:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
type EmbeddingDatabase = {
|
|
86
|
+
/** Path or URL to the DuckDB embedding database file */
|
|
87
|
+
databaseFilePathOrUrl: string;
|
|
88
|
+
|
|
89
|
+
/** Name to use when attaching the database */
|
|
90
|
+
databaseName: string;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Embedding provider for this database.
|
|
94
|
+
* MUST match the model used during database preparation.
|
|
95
|
+
*/
|
|
96
|
+
embeddingProvider: EmbeddingProvider;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Expected embedding dimensions (for validation).
|
|
100
|
+
* Optional but recommended.
|
|
101
|
+
*/
|
|
102
|
+
embeddingDimensions?: number;
|
|
103
|
+
};
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `EmbeddingProvider`
|
|
107
|
+
|
|
108
|
+
Function that converts text to embeddings:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
type EmbeddingProvider = (text: string) => Promise<number[]>;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Important**: The embedding provider MUST match the model used when preparing the database. Check your database metadata to ensure compatibility.
|
|
115
|
+
|
|
116
|
+
### Store Methods
|
|
117
|
+
|
|
118
|
+
#### `rag.initialize()`
|
|
119
|
+
|
|
120
|
+
Initialize RAG by attaching all embedding databases and validating metadata.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
await store.getState().rag.initialize();
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### `rag.queryByText(text, options)`
|
|
127
|
+
|
|
128
|
+
Query embeddings using text. By default, performs **hybrid search** combining vector similarity with full-text search (BM25) using Reciprocal Rank Fusion (RRF).
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const results = await store.getState().rag.queryByText('search query', {
|
|
132
|
+
topK: 5, // Number of results to return (default: 5)
|
|
133
|
+
database: 'docs', // Database to search (default: first database)
|
|
134
|
+
hybrid: true, // Enable hybrid search (default: true)
|
|
135
|
+
// hybrid: false, // Disable hybrid search (vector-only)
|
|
136
|
+
// hybrid: 60, // Custom RRF k value (default: 60)
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Hybrid Search** combines:
|
|
141
|
+
|
|
142
|
+
- **Vector similarity**: Semantic understanding of the query
|
|
143
|
+
- **Full-text search (BM25)**: Keyword matching and ranking
|
|
144
|
+
- **Reciprocal Rank Fusion**: Smart combination of both result sets
|
|
145
|
+
|
|
146
|
+
This approach typically provides better results than vector-only search, especially for queries with specific keywords or technical terms.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
type EmbeddingResult = {
|
|
152
|
+
score: number; // Cosine similarity (0-1, higher is better)
|
|
153
|
+
text: string; // The matched text chunk
|
|
154
|
+
nodeId: string; // Unique identifier for the chunk
|
|
155
|
+
metadata?: Record<string, unknown>; // Optional metadata (file path, etc.)
|
|
156
|
+
};
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
#### `rag.queryEmbeddings(embedding, options)`
|
|
160
|
+
|
|
161
|
+
Query embeddings using a pre-computed embedding vector.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const embedding = await embeddingProvider('search query');
|
|
165
|
+
const results = await store.getState().rag.queryEmbeddings(embedding, {
|
|
166
|
+
topK: 5,
|
|
167
|
+
database: 'docs',
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### `rag.getMetadata(databaseName)`
|
|
172
|
+
|
|
173
|
+
Get metadata for a specific database:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const metadata = await store.getState().rag.getMetadata('docs');
|
|
177
|
+
console.log(metadata);
|
|
178
|
+
// {
|
|
179
|
+
// provider: 'openai',
|
|
180
|
+
// model: 'text-embedding-3-small',
|
|
181
|
+
// dimensions: 1536,
|
|
182
|
+
// chunkingStrategy: 'markdown-aware'
|
|
183
|
+
// }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### `rag.listDatabases()`
|
|
187
|
+
|
|
188
|
+
List all attached embedding databases:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const databases = store.getState().rag.listDatabases();
|
|
192
|
+
console.log(databases); // ['docs', 'tutorials', 'api']
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Multiple Databases
|
|
196
|
+
|
|
197
|
+
You can attach multiple embedding databases, each with its own embedding model:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import {openai} from '@ai-sdk/openai';
|
|
201
|
+
import {google} from '@ai-sdk/google';
|
|
202
|
+
import {createAiEmbeddingProvider} from '@sqlrooms/ai-rag';
|
|
203
|
+
|
|
204
|
+
const embeddingsDatabases = [
|
|
205
|
+
{
|
|
206
|
+
databaseFilePathOrUrl: '/data/duckdb_docs.duckdb',
|
|
207
|
+
databaseName: 'duckdb_docs',
|
|
208
|
+
// OpenAI text-embedding-3-small (1536d)
|
|
209
|
+
embeddingProvider: createAiEmbeddingProvider(
|
|
210
|
+
openai,
|
|
211
|
+
'text-embedding-3-small',
|
|
212
|
+
1536,
|
|
213
|
+
),
|
|
214
|
+
embeddingDimensions: 1536,
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
databaseFilePathOrUrl: '/data/react_docs.duckdb',
|
|
218
|
+
databaseName: 'react_docs',
|
|
219
|
+
// OpenAI text-embedding-3-small with reduced dimensions (512d)
|
|
220
|
+
embeddingProvider: createAiEmbeddingProvider(
|
|
221
|
+
openai,
|
|
222
|
+
'text-embedding-3-small',
|
|
223
|
+
512,
|
|
224
|
+
),
|
|
225
|
+
embeddingDimensions: 512,
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
databaseFilePathOrUrl: '/data/python_docs.duckdb',
|
|
229
|
+
databaseName: 'python_docs',
|
|
230
|
+
// Google text-embedding-004 (768d)
|
|
231
|
+
embeddingProvider: createAiEmbeddingProvider(
|
|
232
|
+
google,
|
|
233
|
+
'text-embedding-004',
|
|
234
|
+
768,
|
|
235
|
+
),
|
|
236
|
+
embeddingDimensions: 768,
|
|
237
|
+
},
|
|
238
|
+
];
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Query a specific database:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Query DuckDB docs
|
|
245
|
+
const duckdbResults = await store
|
|
246
|
+
.getState()
|
|
247
|
+
.rag.queryByText('How to create a table?', {
|
|
248
|
+
database: 'duckdb_docs',
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Query React docs
|
|
252
|
+
const reactResults = await store
|
|
253
|
+
.getState()
|
|
254
|
+
.rag.queryByText('How to use hooks?', {
|
|
255
|
+
database: 'react_docs',
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Hybrid Search
|
|
260
|
+
|
|
261
|
+
Hybrid search combines vector similarity (semantic understanding) with full-text search (keyword matching) to provide more accurate and comprehensive results.
|
|
262
|
+
|
|
263
|
+
### How It Works
|
|
264
|
+
|
|
265
|
+
1. **Vector Search**: Uses embedding similarity to find semantically related content
|
|
266
|
+
2. **Full-Text Search (BM25)**: Uses DuckDB's FTS extension for keyword-based ranking
|
|
267
|
+
3. **Reciprocal Rank Fusion (RRF)**: Intelligently combines both result sets
|
|
268
|
+
|
|
269
|
+
### Benefits
|
|
270
|
+
|
|
271
|
+
- **Better Recall**: Finds results even when exact keywords aren't in the query
|
|
272
|
+
- **Improved Precision**: Keyword matching helps rank exact matches higher
|
|
273
|
+
- **Balanced Results**: RRF prevents either method from dominating unfairly
|
|
274
|
+
|
|
275
|
+
### Configuration
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// Default: Hybrid search enabled with k=60
|
|
279
|
+
const results = await store.getState().rag.queryByText('query', {
|
|
280
|
+
hybrid: true, // Enable hybrid search (default)
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Pure vector search only
|
|
284
|
+
const vectorOnly = await store.getState().rag.queryByText('query', {
|
|
285
|
+
hybrid: false, // Disable hybrid search
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Custom RRF k value (lower = more weight to top-ranked results)
|
|
289
|
+
const customRRF = await store.getState().rag.queryByText('query', {
|
|
290
|
+
hybrid: 60, // Custom k value for Reciprocal Rank Fusion
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### When to Use What
|
|
295
|
+
|
|
296
|
+
- **Hybrid (default)**: Best for most use cases, especially technical documentation
|
|
297
|
+
- **Vector-only**: When you want pure semantic matching without keyword bias
|
|
298
|
+
- **Lower k value** (e.g., 30): Give more weight to top-ranked results
|
|
299
|
+
- **Higher k value** (e.g., 100): More balanced combination, less bias to top results
|
|
300
|
+
|
|
301
|
+
### Requirements
|
|
302
|
+
|
|
303
|
+
Hybrid search requires that the embedding database was prepared with FTS indexing enabled (which is the default in the Python `prepare-embeddings` command). If FTS is not available, the system automatically falls back to vector-only search.
|
|
304
|
+
|
|
305
|
+
## Embedding Providers
|
|
306
|
+
|
|
307
|
+
The `createAiEmbeddingProvider()` function works with any provider from the Vercel AI SDK.
|
|
308
|
+
|
|
309
|
+
### OpenAI
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import {openai} from '@ai-sdk/openai';
|
|
313
|
+
import {createAiEmbeddingProvider} from '@sqlrooms/ai-rag';
|
|
314
|
+
|
|
315
|
+
const embeddingProvider = createAiEmbeddingProvider(
|
|
316
|
+
openai,
|
|
317
|
+
'text-embedding-3-small',
|
|
318
|
+
1536,
|
|
319
|
+
);
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Google
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import {google} from '@ai-sdk/google';
|
|
326
|
+
import {createAiEmbeddingProvider} from '@sqlrooms/ai-rag';
|
|
327
|
+
|
|
328
|
+
const embeddingProvider = createAiEmbeddingProvider(
|
|
329
|
+
google,
|
|
330
|
+
'text-embedding-004',
|
|
331
|
+
768,
|
|
332
|
+
);
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Custom Provider
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import {createAiEmbeddingProvider} from '@sqlrooms/ai-rag';
|
|
339
|
+
|
|
340
|
+
// Any provider that implements the AiProvider interface
|
|
341
|
+
const embeddingProvider = createAiEmbeddingProvider(
|
|
342
|
+
myCustomProvider,
|
|
343
|
+
'my-model-id',
|
|
344
|
+
512,
|
|
345
|
+
);
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Multiple Providers Example
|
|
349
|
+
|
|
350
|
+
You can use different providers for different databases:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
import {openai} from '@ai-sdk/openai';
|
|
354
|
+
import {google} from '@ai-sdk/google';
|
|
355
|
+
import {createAiEmbeddingProvider} from '@sqlrooms/ai-rag';
|
|
356
|
+
|
|
357
|
+
const embeddingsDatabases = [
|
|
358
|
+
{
|
|
359
|
+
databaseName: 'docs_openai',
|
|
360
|
+
databaseFilePathOrUrl: './embeddings/docs_openai.duckdb',
|
|
361
|
+
embeddingProvider: createAiEmbeddingProvider(
|
|
362
|
+
openai,
|
|
363
|
+
'text-embedding-3-small',
|
|
364
|
+
1536,
|
|
365
|
+
),
|
|
366
|
+
embeddingDimensions: 1536,
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
databaseName: 'docs_google',
|
|
370
|
+
databaseFilePathOrUrl: './embeddings/docs_google.duckdb',
|
|
371
|
+
embeddingProvider: createAiEmbeddingProvider(
|
|
372
|
+
google,
|
|
373
|
+
'text-embedding-004',
|
|
374
|
+
768,
|
|
375
|
+
),
|
|
376
|
+
embeddingDimensions: 768,
|
|
377
|
+
},
|
|
378
|
+
];
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Preparing Databases
|
|
382
|
+
|
|
383
|
+
Use the Python `sqlrooms_rag` package to prepare embedding databases:
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# Install the package
|
|
387
|
+
pip install sqlrooms-rag
|
|
388
|
+
|
|
389
|
+
# Prepare embeddings with OpenAI
|
|
390
|
+
python -m sqlrooms_rag.cli prepare-embeddings \
|
|
391
|
+
docs/ \
|
|
392
|
+
-o embeddings.duckdb \
|
|
393
|
+
--provider openai \
|
|
394
|
+
--model text-embedding-3-small \
|
|
395
|
+
--embed-dim 1536
|
|
396
|
+
|
|
397
|
+
# Prepare embeddings with HuggingFace (local, free)
|
|
398
|
+
python -m sqlrooms_rag.cli prepare-embeddings \
|
|
399
|
+
docs/ \
|
|
400
|
+
-o embeddings.duckdb \
|
|
401
|
+
--provider huggingface \
|
|
402
|
+
--model BAAI/bge-small-en-v1.5
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
See the [Python package documentation](../../python/rag/README.md) for more details.
|
|
406
|
+
|
|
407
|
+
## Database Schema
|
|
408
|
+
|
|
409
|
+
The embedding databases created by `sqlrooms_rag` have the following structure:
|
|
410
|
+
|
|
411
|
+
```sql
|
|
412
|
+
-- Main documents table with embeddings
|
|
413
|
+
CREATE TABLE documents (
|
|
414
|
+
node_id VARCHAR PRIMARY KEY,
|
|
415
|
+
text TEXT,
|
|
416
|
+
metadata_ JSON,
|
|
417
|
+
embedding FLOAT[], -- Vector embedding
|
|
418
|
+
doc_id VARCHAR -- Link to source document
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
-- Original source documents (full, unchunked)
|
|
422
|
+
CREATE TABLE source_documents (
|
|
423
|
+
doc_id VARCHAR PRIMARY KEY,
|
|
424
|
+
file_path VARCHAR,
|
|
425
|
+
file_name VARCHAR,
|
|
426
|
+
text TEXT,
|
|
427
|
+
metadata_ JSON,
|
|
428
|
+
created_at TIMESTAMP
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
-- Metadata about the embedding process
|
|
432
|
+
CREATE TABLE embedding_metadata (
|
|
433
|
+
key VARCHAR PRIMARY KEY,
|
|
434
|
+
value VARCHAR,
|
|
435
|
+
created_at TIMESTAMP
|
|
436
|
+
);
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Error Handling
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
try {
|
|
443
|
+
const results = await store.getState().rag.queryByText('search query', {
|
|
444
|
+
database: 'nonexistent',
|
|
445
|
+
});
|
|
446
|
+
} catch (error) {
|
|
447
|
+
// Error: Database "nonexistent" not found. Available: docs, tutorials
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
const wrongDimEmbedding = new Array(384).fill(0);
|
|
452
|
+
await store.getState().rag.queryEmbeddings(wrongDimEmbedding, {
|
|
453
|
+
database: 'docs', // Expects 1536 dimensions
|
|
454
|
+
});
|
|
455
|
+
} catch (error) {
|
|
456
|
+
// Error: Dimension mismatch: query has 384 dimensions,
|
|
457
|
+
// but database "docs" expects 1536 dimensions
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## Best Practices
|
|
462
|
+
|
|
463
|
+
1. **Match Embedding Models**: Always use the same embedding model and dimensions when querying as when preparing the database.
|
|
464
|
+
|
|
465
|
+
2. **Check Metadata**: Use `getMetadata()` to verify the model and dimensions before querying.
|
|
466
|
+
|
|
467
|
+
3. **Dimension Validation**: Provide `embeddingDimensions` in your database configuration for automatic validation.
|
|
468
|
+
|
|
469
|
+
4. **Database Naming**: Use descriptive database names (e.g., `duckdb_docs`, `react_docs`) to easily identify them.
|
|
470
|
+
|
|
471
|
+
5. **Error Handling**: Always wrap queries in try-catch blocks to handle dimension mismatches and missing databases.
|
|
472
|
+
|
|
473
|
+
6. **Performance**: For large databases, consider using reduced dimensions (e.g., 512 instead of 1536) for faster queries and lower costs.
|
|
474
|
+
|
|
475
|
+
## Examples
|
|
476
|
+
|
|
477
|
+
See the [examples/ai](../../examples/ai) directory for complete examples:
|
|
478
|
+
|
|
479
|
+
- `src/embeddings.ts` - OpenAI embedding provider implementations
|
|
480
|
+
- `src/rag-example.ts` - Comprehensive usage examples
|
|
481
|
+
- `src/store.ts` - Store configuration with RAG
|
|
482
|
+
|
|
483
|
+
## License
|
|
484
|
+
|
|
485
|
+
MIT
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { DuckDbSliceState } from '@sqlrooms/duckdb';
|
|
2
|
+
import { BaseRoomStoreState, StateCreator } from '@sqlrooms/room-store';
|
|
3
|
+
export type EmbeddingResult = {
|
|
4
|
+
score: number;
|
|
5
|
+
text: string;
|
|
6
|
+
nodeId: string;
|
|
7
|
+
metadata?: Record<string, unknown>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Function that generates an embedding vector from text.
|
|
11
|
+
* This can be implemented using:
|
|
12
|
+
* - OpenAI API (e.g., text-embedding-3-small)
|
|
13
|
+
* - Transformers.js (client-side, e.g., BAAI/bge-small-en-v1.5)
|
|
14
|
+
* - Custom embedding service
|
|
15
|
+
* - Cohere, Anthropic, etc.
|
|
16
|
+
*
|
|
17
|
+
* IMPORTANT: The embedding provider MUST match the model used during database preparation.
|
|
18
|
+
* Check the database metadata to ensure compatibility.
|
|
19
|
+
*
|
|
20
|
+
* API key management (if needed) should be handled during provider creation,
|
|
21
|
+
* not at call time. See `createAiEmbeddingProvider` for examples.
|
|
22
|
+
*/
|
|
23
|
+
export type EmbeddingProvider = (text: string) => Promise<number[]>;
|
|
24
|
+
export type EmbeddingDatabase = {
|
|
25
|
+
/** Path or URL to the DuckDB embedding database file */
|
|
26
|
+
databaseFilePathOrUrl: string;
|
|
27
|
+
/** Name to use when attaching the database */
|
|
28
|
+
databaseName: string;
|
|
29
|
+
/**
|
|
30
|
+
* Embedding provider for this database.
|
|
31
|
+
* MUST match the model used during database preparation.
|
|
32
|
+
* Example: If database was prepared with OpenAI text-embedding-3-small,
|
|
33
|
+
* provide an OpenAI embedding function here.
|
|
34
|
+
*
|
|
35
|
+
* Note: API key management (if needed) should be configured during provider
|
|
36
|
+
* creation using `createAiEmbeddingProvider` with a `getApiKey` function.
|
|
37
|
+
*/
|
|
38
|
+
embeddingProvider: EmbeddingProvider;
|
|
39
|
+
/**
|
|
40
|
+
* Expected embedding dimensions (for validation).
|
|
41
|
+
* Should match the model used during preparation.
|
|
42
|
+
* Will be validated against database metadata.
|
|
43
|
+
*/
|
|
44
|
+
embeddingDimensions?: number;
|
|
45
|
+
};
|
|
46
|
+
export type DatabaseMetadata = {
|
|
47
|
+
provider: string;
|
|
48
|
+
model: string;
|
|
49
|
+
dimensions: number;
|
|
50
|
+
chunkingStrategy: string;
|
|
51
|
+
};
|
|
52
|
+
export type QueryOptions = {
|
|
53
|
+
/** Number of results to return (default: 5) */
|
|
54
|
+
topK?: number;
|
|
55
|
+
/** Database to search (defaults to first database) */
|
|
56
|
+
database?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Enable hybrid search combining vector similarity with full-text search (BM25).
|
|
59
|
+
* - true: Use hybrid search with default settings (k=60)
|
|
60
|
+
* - false: Use pure vector search only
|
|
61
|
+
* - number: Use hybrid search with custom k value for RRF
|
|
62
|
+
* Default: true
|
|
63
|
+
*/
|
|
64
|
+
hybrid?: boolean | number;
|
|
65
|
+
};
|
|
66
|
+
export type RagSliceState = {
|
|
67
|
+
rag: {
|
|
68
|
+
/**
|
|
69
|
+
* Initialize RAG by attaching all embedding databases and validating metadata
|
|
70
|
+
*/
|
|
71
|
+
initialize: () => Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Query embeddings using a pre-computed embedding vector
|
|
74
|
+
* @param queryEmbedding - The embedding vector for the query (e.g., 384-dimensional array)
|
|
75
|
+
* @param options - Query options (topK, database to search, hybrid search)
|
|
76
|
+
* @returns Array of results sorted by similarity score
|
|
77
|
+
*/
|
|
78
|
+
queryEmbeddings: (queryEmbedding: number[], options?: QueryOptions) => Promise<EmbeddingResult[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Query embeddings using text.
|
|
81
|
+
* Uses the embedding provider configured for the specified database.
|
|
82
|
+
* By default, performs hybrid search combining vector similarity with full-text search.
|
|
83
|
+
* @param queryText - The text query
|
|
84
|
+
* @param options - Query options (topK, database to search, hybrid search)
|
|
85
|
+
* @returns Array of results sorted by similarity score
|
|
86
|
+
* @throws Error if database not found or no embedding provider configured
|
|
87
|
+
*/
|
|
88
|
+
queryByText: (queryText: string, options?: QueryOptions) => Promise<EmbeddingResult[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Get metadata for a specific database
|
|
91
|
+
* @param databaseName - Name of the database
|
|
92
|
+
* @returns Database metadata (model, dimensions, etc.)
|
|
93
|
+
*/
|
|
94
|
+
getMetadata: (databaseName: string) => Promise<DatabaseMetadata | null>;
|
|
95
|
+
/**
|
|
96
|
+
* List all attached embedding databases
|
|
97
|
+
*/
|
|
98
|
+
listDatabases: () => string[];
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
export declare function createRagSlice({ embeddingsDatabases, }: {
|
|
102
|
+
embeddingsDatabases: EmbeddingDatabase[];
|
|
103
|
+
}): StateCreator<RagSliceState>;
|
|
104
|
+
type RoomStateWithRag = BaseRoomStoreState & DuckDbSliceState & RagSliceState;
|
|
105
|
+
export declare function useStoreWithRag<T>(selector: (state: RoomStateWithRag) => T): T;
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=RagSlice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RagSlice.d.ts","sourceRoot":"","sources":["../src/RagSlice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAClD,OAAO,EACL,kBAAkB,EAElB,YAAY,EAEb,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAEpE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,wDAAwD;IACxD,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;OAQG;IACH,iBAAiB,EAAE,iBAAiB,CAAC;IACrC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAqDF,MAAM,MAAM,YAAY,GAAG;IACzB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE;QACH;;WAEG;QACH,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QAEhC;;;;;WAKG;QACH,eAAe,EAAE,CACf,cAAc,EAAE,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE,YAAY,KACnB,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAEhC;;;;;;;;WAQG;QACH,WAAW,EAAE,CACX,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,YAAY,KACnB,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAEhC;;;;WAIG;QACH,WAAW,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;QAExE;;WAEG;QACH,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;KAC/B,CAAC;CACH,CAAC;AAEF,wBAAgB,cAAc,CAAC,EAC7B,mBAAmB,GACpB,EAAE;IACD,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;CAC1C,GAAG,YAAY,CAAC,aAAa,CAAC,CAmV9B;AAED,KAAK,gBAAgB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAE9E,wBAAgB,eAAe,CAAC,CAAC,EAC/B,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,CAAC,GACvC,CAAC,CAIH"}
|