@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 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"}