libsql-search 0.1.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.
- package/LICENSE +21 -0
- package/README.md +477 -0
- package/dist/index.d.ts +134 -0
- package/dist/index.esm.js +389 -0
- package/dist/index.js +400 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 llbbl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
# libsql-search
|
|
2
|
+
|
|
3
|
+
Semantic search for static sites using libSQL/Turso with multi-provider embeddings.
|
|
4
|
+
|
|
5
|
+
Add AI-powered vector search to your Astro, Next.js, or any static site with minimal configuration. Index markdown content, generate embeddings locally or via API, and provide lightning-fast semantic search to your users.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🔍 **Semantic Search** - Find content by meaning, not just keywords
|
|
10
|
+
- 🌐 **Multi-Provider Embeddings** - Choose local (Xenova), Gemini, or OpenAI
|
|
11
|
+
- ⚡ **Edge-Ready** - Works with Turso's global edge database
|
|
12
|
+
- 📝 **Markdown Support** - Built-in gray-matter parsing
|
|
13
|
+
- 🎯 **Type-Safe** - Full TypeScript support
|
|
14
|
+
- 🆓 **Free Tier Friendly** - Local embeddings require no API keys
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
**npm:**
|
|
19
|
+
```bash
|
|
20
|
+
npm install libsql-search @libsql/client
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**pnpm:**
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add libsql-search @libsql/client
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**JSR:**
|
|
29
|
+
```bash
|
|
30
|
+
deno add @llbbl/libsql-search
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### 1. Set Up Your Database
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { createClient } from '@libsql/client';
|
|
39
|
+
import { createTable } from 'libsql-search';
|
|
40
|
+
|
|
41
|
+
const client = createClient({
|
|
42
|
+
url: 'libsql://your-db.turso.io',
|
|
43
|
+
authToken: 'your-auth-token'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Create the articles table with vector index
|
|
47
|
+
await createTable(client, 'articles', 768);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. Index Your Content
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { indexContent } from 'libsql-search';
|
|
54
|
+
|
|
55
|
+
const result = await indexContent({
|
|
56
|
+
client,
|
|
57
|
+
contentPath: './content',
|
|
58
|
+
embeddingOptions: {
|
|
59
|
+
provider: 'local', // or 'gemini', 'openai'
|
|
60
|
+
dimensions: 768
|
|
61
|
+
},
|
|
62
|
+
onProgress: (current, total, file) => {
|
|
63
|
+
console.log(`[${current}/${total}] Indexing: ${file}`);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log(`Indexed ${result.success}/${result.total} documents`);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3. Search Your Content
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { search } from 'libsql-search';
|
|
74
|
+
|
|
75
|
+
const results = await search({
|
|
76
|
+
client,
|
|
77
|
+
query: 'how to deploy astro',
|
|
78
|
+
limit: 5,
|
|
79
|
+
embeddingOptions: {
|
|
80
|
+
provider: 'local'
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
results.forEach(result => {
|
|
85
|
+
console.log(`${result.title} (${result.distance})`);
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Embedding Providers
|
|
90
|
+
|
|
91
|
+
### Local (Xenova/Transformers.js)
|
|
92
|
+
|
|
93
|
+
**Free, no API key required**. Runs `all-MiniLM-L6-v2` in Node.js using ONNX.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
embeddingOptions: {
|
|
97
|
+
provider: 'local',
|
|
98
|
+
dimensions: 768 // 384 native, padded to 768
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Pros:**
|
|
103
|
+
- ✅ No API costs
|
|
104
|
+
- ✅ No rate limits
|
|
105
|
+
- ✅ Works offline
|
|
106
|
+
- ✅ Privacy-friendly
|
|
107
|
+
|
|
108
|
+
**Cons:**
|
|
109
|
+
- ⚠️ First run downloads model (~50MB)
|
|
110
|
+
- ⚠️ Slower than API-based options
|
|
111
|
+
- ⚠️ Lower quality than large models
|
|
112
|
+
|
|
113
|
+
### Google Gemini
|
|
114
|
+
|
|
115
|
+
**Free tier: 1,500 requests/day**. Uses `text-embedding-004` model.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
embeddingOptions: {
|
|
119
|
+
provider: 'gemini',
|
|
120
|
+
apiKey: process.env.GEMINI_API_KEY,
|
|
121
|
+
dimensions: 768 // native
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Pros:**
|
|
126
|
+
- ✅ Generous free tier
|
|
127
|
+
- ✅ High quality embeddings
|
|
128
|
+
- ✅ Fast
|
|
129
|
+
|
|
130
|
+
**Cons:**
|
|
131
|
+
- ⚠️ Requires API key
|
|
132
|
+
- ⚠️ Rate limited
|
|
133
|
+
|
|
134
|
+
### OpenAI
|
|
135
|
+
|
|
136
|
+
**Paid only**. Uses `text-embedding-3-small` or `text-embedding-3-large`.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
embeddingOptions: {
|
|
140
|
+
provider: 'openai',
|
|
141
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
142
|
+
dimensions: 1536 // or 3072 for large
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Pros:**
|
|
147
|
+
- ✅ Highest quality
|
|
148
|
+
- ✅ Very fast
|
|
149
|
+
- ✅ Configurable dimensions
|
|
150
|
+
|
|
151
|
+
**Cons:**
|
|
152
|
+
- ⚠️ Costs money ($0.02 per 1M tokens)
|
|
153
|
+
- ⚠️ Requires API key
|
|
154
|
+
|
|
155
|
+
## API Reference
|
|
156
|
+
|
|
157
|
+
### Indexing
|
|
158
|
+
|
|
159
|
+
#### `indexContent(options)`
|
|
160
|
+
|
|
161
|
+
Index markdown files from a directory.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
interface IndexerOptions {
|
|
165
|
+
client: Client; // libSQL client
|
|
166
|
+
contentPath: string; // Path to content directory
|
|
167
|
+
embeddingOptions?: EmbeddingOptions;
|
|
168
|
+
fileExtensions?: string[]; // Default: ['.md', '.markdown']
|
|
169
|
+
exclude?: string[]; // Default: ['node_modules', '.git']
|
|
170
|
+
tableName?: string; // Default: 'articles'
|
|
171
|
+
onProgress?: (current, total, file) => void;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### `createTable(client, tableName?, dimensions?)`
|
|
176
|
+
|
|
177
|
+
Create the articles table with vector index.
|
|
178
|
+
|
|
179
|
+
### Searching
|
|
180
|
+
|
|
181
|
+
#### `search(options)`
|
|
182
|
+
|
|
183
|
+
Perform semantic search.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
interface SearchOptions {
|
|
187
|
+
client: Client;
|
|
188
|
+
query: string;
|
|
189
|
+
limit?: number; // Default: 10
|
|
190
|
+
tableName?: string; // Default: 'articles'
|
|
191
|
+
embeddingOptions?: EmbeddingOptions;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Returns `SearchResult[]`:
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
interface SearchResult {
|
|
199
|
+
id: number;
|
|
200
|
+
slug: string;
|
|
201
|
+
title: string;
|
|
202
|
+
content: string;
|
|
203
|
+
folder: string;
|
|
204
|
+
tags: string[];
|
|
205
|
+
distance: number; // Lower is better
|
|
206
|
+
created_at: string;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
#### `getAllArticles(client, tableName?)`
|
|
211
|
+
|
|
212
|
+
Get all articles (useful for building static pages).
|
|
213
|
+
|
|
214
|
+
#### `getArticleBySlug(client, slug, tableName?)`
|
|
215
|
+
|
|
216
|
+
Get a single article by slug.
|
|
217
|
+
|
|
218
|
+
#### `getArticlesByFolder(client, folder, tableName?)`
|
|
219
|
+
|
|
220
|
+
Get all articles in a folder.
|
|
221
|
+
|
|
222
|
+
#### `getFolders(client, tableName?)`
|
|
223
|
+
|
|
224
|
+
Get all unique folders.
|
|
225
|
+
|
|
226
|
+
### Embeddings
|
|
227
|
+
|
|
228
|
+
#### `generateEmbedding(text, options?)`
|
|
229
|
+
|
|
230
|
+
Generate embeddings for arbitrary text.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
interface EmbeddingOptions {
|
|
234
|
+
provider?: 'local' | 'gemini' | 'openai';
|
|
235
|
+
apiKey?: string;
|
|
236
|
+
dimensions?: number;
|
|
237
|
+
maxLength?: number; // Default: 8000
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### `prepareTextForEmbedding(fields)`
|
|
242
|
+
|
|
243
|
+
Combine multiple fields into embedding text.
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const text = prepareTextForEmbedding({
|
|
247
|
+
title: 'My Article',
|
|
248
|
+
description: 'A description',
|
|
249
|
+
content: '# Content here',
|
|
250
|
+
tags: ['astro', 'turso']
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Framework Integration
|
|
255
|
+
|
|
256
|
+
### Astro
|
|
257
|
+
|
|
258
|
+
**Search API Endpoint** (`src/pages/api/search.json.ts`):
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import type { APIRoute } from 'astro';
|
|
262
|
+
import { createClient } from '@libsql/client';
|
|
263
|
+
import { search } from 'libsql-search';
|
|
264
|
+
|
|
265
|
+
export const prerender = false;
|
|
266
|
+
|
|
267
|
+
const client = createClient({
|
|
268
|
+
url: import.meta.env.TURSO_DB_URL,
|
|
269
|
+
authToken: import.meta.env.TURSO_AUTH_TOKEN
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
export const POST: APIRoute = async ({ request }) => {
|
|
273
|
+
const { query, limit = 10 } = await request.json();
|
|
274
|
+
|
|
275
|
+
const results = await search({
|
|
276
|
+
client,
|
|
277
|
+
query,
|
|
278
|
+
limit,
|
|
279
|
+
embeddingOptions: { provider: 'local' }
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return new Response(JSON.stringify({ results }), {
|
|
283
|
+
headers: { 'Content-Type': 'application/json' }
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Static Page Generation** (`src/pages/[...slug].astro`):
|
|
289
|
+
|
|
290
|
+
```astro
|
|
291
|
+
---
|
|
292
|
+
import { createClient } from '@libsql/client';
|
|
293
|
+
import { getAllArticles, getArticleBySlug } from 'libsql-search';
|
|
294
|
+
|
|
295
|
+
export const prerender = true;
|
|
296
|
+
|
|
297
|
+
const client = createClient({
|
|
298
|
+
url: import.meta.env.TURSO_DB_URL,
|
|
299
|
+
authToken: import.meta.env.TURSO_AUTH_TOKEN
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
export async function getStaticPaths() {
|
|
303
|
+
const articles = await getAllArticles(client);
|
|
304
|
+
return articles.map(article => ({
|
|
305
|
+
params: { slug: article.slug }
|
|
306
|
+
}));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const { slug } = Astro.params;
|
|
310
|
+
const article = await getArticleBySlug(client, slug);
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
<article>
|
|
314
|
+
<h1>{article.title}</h1>
|
|
315
|
+
<div set:html={article.content} />
|
|
316
|
+
</article>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Next.js
|
|
320
|
+
|
|
321
|
+
**API Route** (`app/api/search/route.ts`):
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { createClient } from '@libsql/client';
|
|
325
|
+
import { search } from 'libsql-search';
|
|
326
|
+
import { NextRequest } from 'next/server';
|
|
327
|
+
|
|
328
|
+
const client = createClient({
|
|
329
|
+
url: process.env.TURSO_DB_URL!,
|
|
330
|
+
authToken: process.env.TURSO_AUTH_TOKEN!
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
export async function POST(request: NextRequest) {
|
|
334
|
+
const { query, limit = 10 } = await request.json();
|
|
335
|
+
|
|
336
|
+
const results = await search({
|
|
337
|
+
client,
|
|
338
|
+
query,
|
|
339
|
+
limit,
|
|
340
|
+
embeddingOptions: { provider: 'local' }
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
return Response.json({ results });
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Static Generation** (`app/[slug]/page.tsx`):
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { createClient } from '@libsql/client';
|
|
351
|
+
import { getAllArticles, getArticleBySlug } from 'libsql-search';
|
|
352
|
+
|
|
353
|
+
const client = createClient({
|
|
354
|
+
url: process.env.TURSO_DB_URL!,
|
|
355
|
+
authToken: process.env.TURSO_AUTH_TOKEN!
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
export async function generateStaticParams() {
|
|
359
|
+
const articles = await getAllArticles(client);
|
|
360
|
+
return articles.map(article => ({
|
|
361
|
+
slug: article.slug
|
|
362
|
+
}));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export default async function Page({ params }: { params: { slug: string } }) {
|
|
366
|
+
const article = await getArticleBySlug(client, params.slug);
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<article>
|
|
370
|
+
<h1>{article.title}</h1>
|
|
371
|
+
<div dangerouslySetInnerHTML={{ __html: article.content }} />
|
|
372
|
+
</article>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Best Practices
|
|
378
|
+
|
|
379
|
+
### Embedding Dimensions
|
|
380
|
+
|
|
381
|
+
- Use **768 dimensions** for best compatibility
|
|
382
|
+
- Local model outputs 384, automatically padded to 768
|
|
383
|
+
- Gemini outputs 768 natively
|
|
384
|
+
- OpenAI supports custom dimensions
|
|
385
|
+
|
|
386
|
+
### Index Updates
|
|
387
|
+
|
|
388
|
+
Create a script to re-index content:
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"scripts": {
|
|
393
|
+
"index": "node scripts/index.js",
|
|
394
|
+
"build": "npm run index && astro build"
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Search Quality
|
|
400
|
+
|
|
401
|
+
Improve search results:
|
|
402
|
+
|
|
403
|
+
1. **Include relevant fields** in embedding text (title, description, tags)
|
|
404
|
+
2. **Truncate long content** to avoid noise
|
|
405
|
+
3. **Use the same provider** for indexing and search
|
|
406
|
+
4. **Experiment with distance thresholds** (lower is better)
|
|
407
|
+
|
|
408
|
+
### Performance
|
|
409
|
+
|
|
410
|
+
- **Cache the embedding model** (done automatically)
|
|
411
|
+
- **Use edge databases** (Turso) for low latency
|
|
412
|
+
- **Implement search debouncing** in the UI
|
|
413
|
+
- **Limit result count** to 5-10 for best UX
|
|
414
|
+
|
|
415
|
+
## Examples
|
|
416
|
+
|
|
417
|
+
See the `/examples` directory for complete implementations:
|
|
418
|
+
|
|
419
|
+
- [Astro Documentation Site](./examples/astro-docs)
|
|
420
|
+
- [Next.js Blog](./examples/nextjs-blog)
|
|
421
|
+
- [CLI Indexer](./examples/cli-indexer)
|
|
422
|
+
|
|
423
|
+
## CLI Usage
|
|
424
|
+
|
|
425
|
+
For a standalone indexing script:
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
// scripts/index.js
|
|
429
|
+
import { createClient } from '@libsql/client';
|
|
430
|
+
import { createTable, indexContent } from 'libsql-search';
|
|
431
|
+
|
|
432
|
+
const client = createClient({
|
|
433
|
+
url: process.env.TURSO_DB_URL,
|
|
434
|
+
authToken: process.env.TURSO_AUTH_TOKEN
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
await createTable(client);
|
|
438
|
+
|
|
439
|
+
const result = await indexContent({
|
|
440
|
+
client,
|
|
441
|
+
contentPath: './content',
|
|
442
|
+
embeddingOptions: {
|
|
443
|
+
provider: process.env.EMBEDDING_PROVIDER || 'local'
|
|
444
|
+
},
|
|
445
|
+
onProgress: (current, total, file) => {
|
|
446
|
+
console.log(`[${current}/${total}] ${file}`);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
console.log(`✅ Indexed ${result.success} documents`);
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Run with:
|
|
454
|
+
```bash
|
|
455
|
+
node --env-file=.env scripts/index.js
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## License
|
|
459
|
+
|
|
460
|
+
MIT
|
|
461
|
+
|
|
462
|
+
## Contributing
|
|
463
|
+
|
|
464
|
+
Contributions welcome! Please open an issue or PR on [GitHub](https://github.com/llbbl/libsql-search).
|
|
465
|
+
|
|
466
|
+
## Related Projects
|
|
467
|
+
|
|
468
|
+
- [Turso](https://turso.tech) - Edge SQLite database
|
|
469
|
+
- [libSQL](https://github.com/tursodatabase/libsql) - Open source SQLite fork
|
|
470
|
+
- [Astro](https://astro.build) - Static site framework
|
|
471
|
+
- [Transformers.js](https://huggingface.co/docs/transformers.js) - ML models in JavaScript
|
|
472
|
+
|
|
473
|
+
## Support
|
|
474
|
+
|
|
475
|
+
- [Documentation](https://github.com/llbbl/libsql-search)
|
|
476
|
+
- [Issues](https://github.com/llbbl/libsql-search/issues)
|
|
477
|
+
- [Discussions](https://github.com/llbbl/libsql-search/discussions)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Client } from '@libsql/client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Multi-provider embedding generation
|
|
5
|
+
* Supports local (Xenova), Gemini, and OpenAI
|
|
6
|
+
*/
|
|
7
|
+
type EmbeddingProvider = 'local' | 'gemini' | 'openai';
|
|
8
|
+
interface EmbeddingOptions {
|
|
9
|
+
provider?: EmbeddingProvider;
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
dimensions?: number;
|
|
12
|
+
maxLength?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generate embeddings using the specified provider
|
|
16
|
+
*/
|
|
17
|
+
declare function generateEmbedding(text: string, options?: EmbeddingOptions): Promise<number[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Pad or truncate embedding to target dimensions
|
|
20
|
+
*/
|
|
21
|
+
declare function padEmbedding(embedding: number[], targetDimensions: number): number[];
|
|
22
|
+
/**
|
|
23
|
+
* Prepare text for embedding by combining multiple fields
|
|
24
|
+
*/
|
|
25
|
+
declare function prepareTextForEmbedding(fields: {
|
|
26
|
+
title?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
content?: string;
|
|
29
|
+
tags?: string[];
|
|
30
|
+
[key: string]: any;
|
|
31
|
+
}): string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Content indexer for markdown and other formats
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
interface IndexerOptions {
|
|
38
|
+
client: Client;
|
|
39
|
+
contentPath: string;
|
|
40
|
+
embeddingOptions?: EmbeddingOptions;
|
|
41
|
+
fileExtensions?: string[];
|
|
42
|
+
exclude?: string[];
|
|
43
|
+
tableName?: string;
|
|
44
|
+
onProgress?: (current: number, total: number, file: string) => void;
|
|
45
|
+
}
|
|
46
|
+
interface IndexedDocument {
|
|
47
|
+
slug: string;
|
|
48
|
+
title: string;
|
|
49
|
+
content: string;
|
|
50
|
+
folder: string;
|
|
51
|
+
tags: string[];
|
|
52
|
+
embedding: number[];
|
|
53
|
+
metadata?: Record<string, any>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Index markdown content from a directory
|
|
57
|
+
*/
|
|
58
|
+
declare function indexContent(options: IndexerOptions): Promise<{
|
|
59
|
+
success: number;
|
|
60
|
+
failed: number;
|
|
61
|
+
total: number;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* Create the articles table if it doesn't exist
|
|
65
|
+
*/
|
|
66
|
+
declare function createTable(client: Client, tableName?: string, dimensions?: number): Promise<void>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Vector search functionality
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
interface SearchOptions {
|
|
73
|
+
client: Client;
|
|
74
|
+
query: string;
|
|
75
|
+
limit?: number;
|
|
76
|
+
tableName?: string;
|
|
77
|
+
embeddingOptions?: EmbeddingOptions;
|
|
78
|
+
}
|
|
79
|
+
interface SearchResult {
|
|
80
|
+
id: number;
|
|
81
|
+
slug: string;
|
|
82
|
+
title: string;
|
|
83
|
+
content: string;
|
|
84
|
+
folder: string;
|
|
85
|
+
tags: string[];
|
|
86
|
+
distance: number;
|
|
87
|
+
created_at: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Perform semantic search using vector similarity
|
|
91
|
+
*/
|
|
92
|
+
declare function search(options: SearchOptions): Promise<SearchResult[]>;
|
|
93
|
+
/**
|
|
94
|
+
* Get all articles (for building static pages, navigation, etc.)
|
|
95
|
+
*/
|
|
96
|
+
declare function getAllArticles(client: Client, tableName?: string): Promise<Array<{
|
|
97
|
+
id: number;
|
|
98
|
+
slug: string;
|
|
99
|
+
title: string;
|
|
100
|
+
folder: string;
|
|
101
|
+
tags: string[];
|
|
102
|
+
created_at: string;
|
|
103
|
+
updated_at: string;
|
|
104
|
+
}>>;
|
|
105
|
+
/**
|
|
106
|
+
* Get a single article by slug
|
|
107
|
+
*/
|
|
108
|
+
declare function getArticleBySlug(client: Client, slug: string, tableName?: string): Promise<{
|
|
109
|
+
id: number;
|
|
110
|
+
slug: string;
|
|
111
|
+
title: string;
|
|
112
|
+
content: string;
|
|
113
|
+
folder: string;
|
|
114
|
+
tags: string[];
|
|
115
|
+
created_at: string;
|
|
116
|
+
updated_at: string;
|
|
117
|
+
} | null>;
|
|
118
|
+
/**
|
|
119
|
+
* Get articles by folder
|
|
120
|
+
*/
|
|
121
|
+
declare function getArticlesByFolder(client: Client, folder: string, tableName?: string): Promise<Array<{
|
|
122
|
+
id: number;
|
|
123
|
+
slug: string;
|
|
124
|
+
title: string;
|
|
125
|
+
folder: string;
|
|
126
|
+
tags: string[];
|
|
127
|
+
}>>;
|
|
128
|
+
/**
|
|
129
|
+
* Get all unique folders
|
|
130
|
+
*/
|
|
131
|
+
declare function getFolders(client: Client, tableName?: string): Promise<string[]>;
|
|
132
|
+
|
|
133
|
+
export { createTable, generateEmbedding, getAllArticles, getArticleBySlug, getArticlesByFolder, getFolders, indexContent, padEmbedding, prepareTextForEmbedding, search };
|
|
134
|
+
export type { EmbeddingOptions, EmbeddingProvider, IndexedDocument, IndexerOptions, SearchOptions, SearchResult };
|