@kernl-sdk/turbopuffer 0.1.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.
Files changed (91) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-check-types.log +60 -0
  3. package/CHANGELOG.md +33 -0
  4. package/LICENSE +201 -0
  5. package/README.md +60 -0
  6. package/dist/__tests__/convert.test.d.ts +2 -0
  7. package/dist/__tests__/convert.test.d.ts.map +1 -0
  8. package/dist/__tests__/convert.test.js +346 -0
  9. package/dist/__tests__/filter.test.d.ts +8 -0
  10. package/dist/__tests__/filter.test.d.ts.map +1 -0
  11. package/dist/__tests__/filter.test.js +649 -0
  12. package/dist/__tests__/filters.integration.test.d.ts +8 -0
  13. package/dist/__tests__/filters.integration.test.d.ts.map +1 -0
  14. package/dist/__tests__/filters.integration.test.js +502 -0
  15. package/dist/__tests__/integration/filters.integration.test.d.ts +8 -0
  16. package/dist/__tests__/integration/filters.integration.test.d.ts.map +1 -0
  17. package/dist/__tests__/integration/filters.integration.test.js +475 -0
  18. package/dist/__tests__/integration/integration.test.d.ts +2 -0
  19. package/dist/__tests__/integration/integration.test.d.ts.map +1 -0
  20. package/dist/__tests__/integration/integration.test.js +329 -0
  21. package/dist/__tests__/integration/lifecycle.integration.test.d.ts +8 -0
  22. package/dist/__tests__/integration/lifecycle.integration.test.d.ts.map +1 -0
  23. package/dist/__tests__/integration/lifecycle.integration.test.js +370 -0
  24. package/dist/__tests__/integration/memory.integration.test.d.ts +2 -0
  25. package/dist/__tests__/integration/memory.integration.test.d.ts.map +1 -0
  26. package/dist/__tests__/integration/memory.integration.test.js +287 -0
  27. package/dist/__tests__/integration/query.integration.test.d.ts +8 -0
  28. package/dist/__tests__/integration/query.integration.test.d.ts.map +1 -0
  29. package/dist/__tests__/integration/query.integration.test.js +385 -0
  30. package/dist/__tests__/integration.test.d.ts +2 -0
  31. package/dist/__tests__/integration.test.d.ts.map +1 -0
  32. package/dist/__tests__/integration.test.js +343 -0
  33. package/dist/__tests__/lifecycle.integration.test.d.ts +8 -0
  34. package/dist/__tests__/lifecycle.integration.test.d.ts.map +1 -0
  35. package/dist/__tests__/lifecycle.integration.test.js +385 -0
  36. package/dist/__tests__/query.integration.test.d.ts +8 -0
  37. package/dist/__tests__/query.integration.test.d.ts.map +1 -0
  38. package/dist/__tests__/query.integration.test.js +423 -0
  39. package/dist/__tests__/query.test.d.ts +8 -0
  40. package/dist/__tests__/query.test.d.ts.map +1 -0
  41. package/dist/__tests__/query.test.js +472 -0
  42. package/dist/convert/document.d.ts +20 -0
  43. package/dist/convert/document.d.ts.map +1 -0
  44. package/dist/convert/document.js +72 -0
  45. package/dist/convert/filter.d.ts +15 -0
  46. package/dist/convert/filter.d.ts.map +1 -0
  47. package/dist/convert/filter.js +109 -0
  48. package/dist/convert/index.d.ts +8 -0
  49. package/dist/convert/index.d.ts.map +1 -0
  50. package/dist/convert/index.js +7 -0
  51. package/dist/convert/query.d.ts +22 -0
  52. package/dist/convert/query.d.ts.map +1 -0
  53. package/dist/convert/query.js +111 -0
  54. package/dist/convert/schema.d.ts +39 -0
  55. package/dist/convert/schema.d.ts.map +1 -0
  56. package/dist/convert/schema.js +124 -0
  57. package/dist/convert.d.ts +68 -0
  58. package/dist/convert.d.ts.map +1 -0
  59. package/dist/convert.js +333 -0
  60. package/dist/handle.d.ts +34 -0
  61. package/dist/handle.d.ts.map +1 -0
  62. package/dist/handle.js +72 -0
  63. package/dist/index.d.ts +27 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +26 -0
  66. package/dist/search.d.ts +85 -0
  67. package/dist/search.d.ts.map +1 -0
  68. package/dist/search.js +167 -0
  69. package/dist/types.d.ts +14 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +1 -0
  72. package/package.json +57 -0
  73. package/src/__tests__/convert.test.ts +425 -0
  74. package/src/__tests__/filter.test.ts +730 -0
  75. package/src/__tests__/integration/filters.integration.test.ts +558 -0
  76. package/src/__tests__/integration/integration.test.ts +399 -0
  77. package/src/__tests__/integration/lifecycle.integration.test.ts +464 -0
  78. package/src/__tests__/integration/memory.integration.test.ts +353 -0
  79. package/src/__tests__/integration/query.integration.test.ts +471 -0
  80. package/src/__tests__/query.test.ts +636 -0
  81. package/src/convert/document.ts +95 -0
  82. package/src/convert/filter.ts +123 -0
  83. package/src/convert/index.ts +8 -0
  84. package/src/convert/query.ts +151 -0
  85. package/src/convert/schema.ts +163 -0
  86. package/src/handle.ts +104 -0
  87. package/src/index.ts +31 -0
  88. package/src/search.ts +207 -0
  89. package/src/types.ts +14 -0
  90. package/tsconfig.json +13 -0
  91. package/vitest.config.ts +15 -0
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Codecs for converting between kernl and Turbopuffer types.
3
+ */
4
+ /**
5
+ * Codec for converting kernl scalar types to Turbopuffer attribute types.
6
+ */
7
+ export const SCALAR_TYPE = {
8
+ encode: (type) => {
9
+ switch (type) {
10
+ case "string":
11
+ return "string";
12
+ case "int":
13
+ return "int";
14
+ case "float":
15
+ return "int"; // tpuf doesn't have float
16
+ case "boolean":
17
+ return "bool";
18
+ case "date":
19
+ return "datetime";
20
+ case "string[]":
21
+ return "[]string";
22
+ case "int[]":
23
+ return "[]int";
24
+ case "date[]":
25
+ return "[]datetime";
26
+ default:
27
+ return "string";
28
+ }
29
+ },
30
+ decode: (type) => {
31
+ switch (type) {
32
+ case "string":
33
+ return "string";
34
+ case "int":
35
+ return "int";
36
+ case "bool":
37
+ return "boolean";
38
+ case "datetime":
39
+ return "date";
40
+ case "[]string":
41
+ return "string[]";
42
+ case "[]int":
43
+ return "int[]";
44
+ case "[]datetime":
45
+ return "date[]";
46
+ default:
47
+ return "string";
48
+ }
49
+ },
50
+ };
51
+ /**
52
+ * Codec for converting similarity metric to Turbopuffer distance metric.
53
+ *
54
+ * Turbopuffer supports: cosine_distance, euclidean_squared
55
+ * We support: cosine, euclidean, dot_product
56
+ */
57
+ export const SIMILARITY = {
58
+ encode: (similarity) => {
59
+ switch (similarity) {
60
+ case "euclidean":
61
+ return "euclidean_squared";
62
+ case "cosine":
63
+ case "dot_product":
64
+ default:
65
+ return "cosine_distance";
66
+ }
67
+ },
68
+ decode: (metric) => {
69
+ switch (metric) {
70
+ case "euclidean_squared":
71
+ return "euclidean";
72
+ case "cosine_distance":
73
+ default:
74
+ return "cosine";
75
+ }
76
+ },
77
+ };
78
+ /**
79
+ * Codec-like converter for FieldSchema to Turbopuffer AttributeSchema.
80
+ *
81
+ * Takes the field name as context since Turbopuffer requires `ann: true`
82
+ * only on the special `vector` attribute.
83
+ */
84
+ export const FIELD_SCHEMA = {
85
+ encode: (field, name) => {
86
+ // Vector fields
87
+ if (field.type === "vector" || field.type === "sparse-vector") {
88
+ const vf = field;
89
+ const precision = vf.quantization === "f16" ? "f16" : "f32";
90
+ return {
91
+ type: `[${vf.dimensions}]${precision}`,
92
+ ann: name === "vector",
93
+ };
94
+ }
95
+ // Scalar fields
96
+ const config = {
97
+ type: SCALAR_TYPE.encode(field.type),
98
+ };
99
+ if (field.filterable) {
100
+ config.filterable = true;
101
+ }
102
+ if (field.fts) {
103
+ config.full_text_search =
104
+ typeof field.fts === "object"
105
+ ? { language: field.fts.language }
106
+ : true;
107
+ }
108
+ return config;
109
+ },
110
+ decode: () => {
111
+ throw new Error("FIELD_SCHEMA.decode: not implemented");
112
+ },
113
+ };
114
+ /**
115
+ * Codec for converting a full schema record.
116
+ *
117
+ * Validates that vector fields are named `vector` since Turbopuffer only
118
+ * supports ANN indexing on that specific attribute name.
119
+ */
120
+ export const INDEX_SCHEMA = {
121
+ encode: (schema) => {
122
+ const result = {};
123
+ for (const [name, field] of Object.entries(schema)) {
124
+ const isVector = field.type === "vector" || field.type === "sparse-vector";
125
+ // Enforce vector field naming
126
+ if (isVector && name !== "vector") {
127
+ throw new Error(`Turbopuffer requires vector fields to be named "vector", got "${name}". ` +
128
+ `Rename your field or use a different search provider.`);
129
+ }
130
+ result[name] = FIELD_SCHEMA.encode(field, name);
131
+ }
132
+ return result;
133
+ },
134
+ decode: () => {
135
+ throw new Error("INDEX_SCHEMA.decode: not implemented");
136
+ },
137
+ };
138
+ /**
139
+ * Check if a value is a DenseVector.
140
+ */
141
+ function isDenseVector(val) {
142
+ return (typeof val === "object" &&
143
+ val !== null &&
144
+ "kind" in val &&
145
+ val.kind === "vector");
146
+ }
147
+ /**
148
+ * Convert a FieldValue to Turbopuffer attribute value.
149
+ * Extracts vector values from DenseVector wrapper.
150
+ */
151
+ function encodeFieldValue(val) {
152
+ if (isDenseVector(val)) {
153
+ return val.values;
154
+ }
155
+ return val;
156
+ }
157
+ /**
158
+ * Codec for converting SearchDocument to Turbopuffer Row.
159
+ */
160
+ export const DOCUMENT = {
161
+ encode: (doc) => {
162
+ const row = { id: doc.id };
163
+ for (const [key, val] of Object.entries(doc.fields)) {
164
+ row[key] = encodeFieldValue(val);
165
+ }
166
+ return row;
167
+ },
168
+ decode: (_row) => {
169
+ throw new Error("DOCUMENT.decode: not implemented");
170
+ },
171
+ };
172
+ const FILTER_OP_MAP = {
173
+ eq: "Eq",
174
+ neq: "NotEq",
175
+ gt: "Gt",
176
+ gte: "Gte",
177
+ lt: "Lt",
178
+ lte: "Lte",
179
+ in: "In",
180
+ nin: "NotIn",
181
+ contains: "Contains",
182
+ starts_with: "Glob", // will add * suffix
183
+ ends_with: "Glob", // will add * prefix
184
+ contains_all: "ContainsAny", // closest match
185
+ contains_any: "ContainsAny",
186
+ };
187
+ /**
188
+ * Type guards for filter expressions.
189
+ */
190
+ function isFieldFilter(f) {
191
+ return "field" in f && "op" in f && "value" in f;
192
+ }
193
+ function isExistsFilter(f) {
194
+ return ("field" in f && "op" in f && (f.op === "exists" || f.op === "not_exists"));
195
+ }
196
+ function isAndFilter(f) {
197
+ return "and" in f;
198
+ }
199
+ function isOrFilter(f) {
200
+ return "or" in f;
201
+ }
202
+ function isNotFilter(f) {
203
+ return "not" in f;
204
+ }
205
+ /**
206
+ * Codec for converting FilterExpression to Turbopuffer Filter.
207
+ */
208
+ export const FILTER = {
209
+ encode: (filter) => {
210
+ if (isAndFilter(filter)) {
211
+ return ["And", filter.and.map(FILTER.encode)];
212
+ }
213
+ if (isOrFilter(filter)) {
214
+ return ["Or", filter.or.map(FILTER.encode)];
215
+ }
216
+ if (isNotFilter(filter)) {
217
+ return ["Not", FILTER.encode(filter.not)];
218
+ }
219
+ if (isExistsFilter(filter)) {
220
+ // exists → NotEq null, not_exists → Eq null
221
+ return filter.op === "exists"
222
+ ? [filter.field, "NotEq", null]
223
+ : [filter.field, "Eq", null];
224
+ }
225
+ if (isFieldFilter(filter)) {
226
+ const { field, op, value } = filter;
227
+ // Handle glob patterns for starts_with/ends_with
228
+ if (op === "starts_with") {
229
+ return [field, "Glob", `${value}*`];
230
+ }
231
+ if (op === "ends_with") {
232
+ return [field, "Glob", `*${value}`];
233
+ }
234
+ const tpufOp = FILTER_OP_MAP[op];
235
+ return [field, tpufOp, value];
236
+ }
237
+ throw new Error(`Unknown filter type: ${JSON.stringify(filter)}`);
238
+ },
239
+ decode: (_filter) => {
240
+ throw new Error("FILTER.decode: not implemented");
241
+ },
242
+ };
243
+ /**
244
+ * Build rank_by for vector search.
245
+ */
246
+ function buildVectorRankBy(vector) {
247
+ return ["vector", "ANN", vector];
248
+ }
249
+ /**
250
+ * Build rank_by for full-text search.
251
+ */
252
+ function buildTextRankBy(text, fields) {
253
+ if (!fields || fields.length === 0) {
254
+ throw new Error("textFields required for full-text search");
255
+ }
256
+ if (fields.length === 1) {
257
+ return [fields[0], "BM25", text];
258
+ }
259
+ // Multiple fields: combine with Sum
260
+ const subQueries = fields.map((field) => [field, "BM25", text]);
261
+ return ["Sum", subQueries];
262
+ }
263
+ /**
264
+ * Codec for converting SearchQuery to Turbopuffer NamespaceQueryParams.
265
+ *
266
+ * Note: Hybrid search (vector + text) requires multi-query which is
267
+ * handled separately.
268
+ */
269
+ export const QUERY = {
270
+ encode: (query) => {
271
+ const params = {};
272
+ // Determine ranking method
273
+ if (query.vector) {
274
+ params.rank_by = buildVectorRankBy(query.vector);
275
+ }
276
+ else if (query.text) {
277
+ params.rank_by = buildTextRankBy(query.text, query.textFields);
278
+ }
279
+ // Top K
280
+ if (query.topK !== undefined) {
281
+ params.top_k = query.topK;
282
+ }
283
+ // Filters
284
+ if (query.filter) {
285
+ params.filters = FILTER.encode(query.filter);
286
+ }
287
+ // Include attributes
288
+ if (query.includeFields !== undefined) {
289
+ if (typeof query.includeFields === "boolean") {
290
+ params.include_attributes = query.includeFields;
291
+ }
292
+ else {
293
+ const attrs = [...query.includeFields];
294
+ if (query.includeVectors && !attrs.includes("vector")) {
295
+ attrs.push("vector");
296
+ }
297
+ params.include_attributes = attrs;
298
+ }
299
+ }
300
+ else if (query.includeVectors) {
301
+ params.include_attributes = true; // include all to get vector
302
+ }
303
+ return params;
304
+ },
305
+ decode: (_params) => {
306
+ throw new Error("QUERY.decode: not implemented");
307
+ },
308
+ };
309
+ /**
310
+ * Codec for converting Turbopuffer Row to SearchHit.
311
+ */
312
+ export const SEARCH_HIT = {
313
+ encode: (_hit) => {
314
+ throw new Error("SEARCH_HIT.encode: not implemented");
315
+ },
316
+ decode: (row, index) => {
317
+ const { id, $dist, vector, ...rest } = row;
318
+ const hit = {
319
+ id: String(id),
320
+ index,
321
+ score: typeof $dist === "number" ? $dist : 0,
322
+ };
323
+ // Include vector if present
324
+ if (vector !== undefined) {
325
+ hit.vector = vector;
326
+ }
327
+ // Include other fields
328
+ if (Object.keys(rest).length > 0) {
329
+ hit.fields = rest;
330
+ }
331
+ return hit;
332
+ },
333
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Turbopuffer index handle implementation.
3
+ */
4
+ import type Turbopuffer from "@turbopuffer/turbopuffer";
5
+ import type { IndexHandle, DocumentPatch, UpsertResult, PatchResult, DeleteResult, QueryInput, SearchHit, FieldSchema, UnknownDocument } from "@kernl-sdk/retrieval";
6
+ /**
7
+ * Handle for operating on a specific Turbopuffer namespace (index).
8
+ */
9
+ export declare class TurbopufferIndexHandle<TDocument = UnknownDocument> implements IndexHandle<TDocument> {
10
+ readonly id: string;
11
+ private client;
12
+ constructor(client: Turbopuffer, id: string);
13
+ /**
14
+ * Query the index.
15
+ */
16
+ query(query: QueryInput): Promise<SearchHit<TDocument>[]>;
17
+ /**
18
+ * Upsert one or more documents.
19
+ */
20
+ upsert(docs: TDocument | TDocument[]): Promise<UpsertResult>;
21
+ /**
22
+ * Patch one or more documents.
23
+ */
24
+ patch(patches: DocumentPatch<TDocument> | DocumentPatch<TDocument>[]): Promise<PatchResult>;
25
+ /**
26
+ * Delete one or more documents by ID.
27
+ */
28
+ delete(ids: string | string[]): Promise<DeleteResult>;
29
+ /**
30
+ * Add a field to the index schema.
31
+ */
32
+ addField(_field: string, _schema: FieldSchema): Promise<void>;
33
+ }
34
+ //# sourceMappingURL=handle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../src/handle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,WAAW,MAAM,0BAA0B,CAAC;AACxD,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAK9B;;GAEG;AACH,qBAAa,sBAAsB,CAAC,SAAS,GAAG,eAAe,CAC7D,YAAW,WAAW,CAAC,SAAS,CAAC;IAEjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM;IAK3C;;OAEG;IACG,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;IAY/D;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAalE;;OAEG;IACG,KAAK,CACT,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,SAAS,CAAC,EAAE,GAC7D,OAAO,CAAC,WAAW,CAAC;IAavB;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;IAW3D;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAGpE"}
package/dist/handle.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Turbopuffer index handle implementation.
3
+ */
4
+ import { normalizeQuery } from "@kernl-sdk/retrieval";
5
+ import { DOCUMENT, PATCH, QUERY, SEARCH_HIT } from "./convert.js";
6
+ /**
7
+ * Handle for operating on a specific Turbopuffer namespace (index).
8
+ */
9
+ export class TurbopufferIndexHandle {
10
+ id;
11
+ client;
12
+ constructor(client, id) {
13
+ this.client = client;
14
+ this.id = id;
15
+ }
16
+ /**
17
+ * Query the index.
18
+ */
19
+ async query(query) {
20
+ const q = normalizeQuery(query);
21
+ const ns = this.client.namespace(this.id);
22
+ const res = await ns.query(QUERY.encode(q));
23
+ if (!res.rows) {
24
+ return [];
25
+ }
26
+ return res.rows.map((row) => SEARCH_HIT.decode(row, this.id));
27
+ }
28
+ /**
29
+ * Upsert one or more documents.
30
+ */
31
+ async upsert(docs) {
32
+ const arr = Array.isArray(docs) ? docs : [docs];
33
+ if (arr.length === 0) {
34
+ return { count: 0 };
35
+ }
36
+ const ns = this.client.namespace(this.id);
37
+ const rows = arr.map((doc) => DOCUMENT.encode(doc));
38
+ await ns.write({ upsert_rows: rows });
39
+ return { count: arr.length };
40
+ }
41
+ /**
42
+ * Patch one or more documents.
43
+ */
44
+ async patch(patches) {
45
+ const arr = Array.isArray(patches) ? patches : [patches];
46
+ if (arr.length === 0) {
47
+ return { count: 0 };
48
+ }
49
+ const ns = this.client.namespace(this.id);
50
+ const rows = arr.map((p) => PATCH.encode(p));
51
+ await ns.write({ patch_rows: rows });
52
+ return { count: arr.length };
53
+ }
54
+ /**
55
+ * Delete one or more documents by ID.
56
+ */
57
+ async delete(ids) {
58
+ const arr = Array.isArray(ids) ? ids : [ids];
59
+ if (arr.length === 0) {
60
+ return { count: 0 };
61
+ }
62
+ const ns = this.client.namespace(this.id);
63
+ await ns.write({ deletes: arr });
64
+ return { count: arr.length };
65
+ }
66
+ /**
67
+ * Add a field to the index schema.
68
+ */
69
+ async addField(_field, _schema) {
70
+ throw new Error("addField not supported by Turbopuffer");
71
+ }
72
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Turbopuffer search index adapter.
3
+ */
4
+ import { TurbopufferSearchIndex } from "./search.js";
5
+ import type { TurbopufferConfig } from "./types.js";
6
+ export { TurbopufferSearchIndex } from "./search.js";
7
+ export { TurbopufferIndexHandle } from "./handle.js";
8
+ export type { TurbopufferConfig } from "./types.js";
9
+ /**
10
+ * Create a Turbopuffer search index.
11
+ *
12
+ * @param config - Turbopuffer API key and region
13
+ * @returns SearchIndex instance backed by Turbopuffer
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const tpuf = turbopuffer({
18
+ * apiKey: "your-api-key",
19
+ * region: "us-east-1",
20
+ * });
21
+ *
22
+ * const docs = tpuf.index("my-index");
23
+ * await docs.upsert({ id: "doc-1", fields: { text: "Hello" } });
24
+ * ```
25
+ */
26
+ export declare function turbopuffer(config: TurbopufferConfig): TurbopufferSearchIndex;
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,0BAEpD"}
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Turbopuffer search index adapter.
3
+ */
4
+ import { TurbopufferSearchIndex } from "./search.js";
5
+ export { TurbopufferSearchIndex } from "./search.js";
6
+ export { TurbopufferIndexHandle } from "./handle.js";
7
+ /**
8
+ * Create a Turbopuffer search index.
9
+ *
10
+ * @param config - Turbopuffer API key and region
11
+ * @returns SearchIndex instance backed by Turbopuffer
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const tpuf = turbopuffer({
16
+ * apiKey: "your-api-key",
17
+ * region: "us-east-1",
18
+ * });
19
+ *
20
+ * const docs = tpuf.index("my-index");
21
+ * await docs.upsert({ id: "doc-1", fields: { text: "Hello" } });
22
+ * ```
23
+ */
24
+ export function turbopuffer(config) {
25
+ return new TurbopufferSearchIndex(config);
26
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Turbopuffer SearchIndex implementation.
3
+ *
4
+ * Adapts the Turbopuffer vector database to the kernl SearchIndex interface.
5
+ * Turbopuffer uses "namespaces" as the equivalent of our "indexes".
6
+ *
7
+ * @see https://turbopuffer.com/docs
8
+ */
9
+ import type { SearchIndex, IndexHandle, NewIndexParams, ListIndexesParams, IndexSummary, IndexStats, UnknownDocument, SearchCapabilities } from "@kernl-sdk/retrieval";
10
+ import { CursorPage } from "@kernl-sdk/shared";
11
+ import { TurbopufferConfig } from "./types.js";
12
+ /**
13
+ * Turbopuffer search index adapter.
14
+ *
15
+ * Implements the kernl SearchIndex interface backed by Turbopuffer's
16
+ * vector database. Supports vector search, full-text search (BM25),
17
+ * and hybrid queries.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const tpuf = turbopuffer({
22
+ * apiKey: "your-api-key",
23
+ * region: "us-east-1",
24
+ * });
25
+ *
26
+ * const docs = tpuf.index("my-index");
27
+ *
28
+ * await docs.upsert({
29
+ * id: "doc-1",
30
+ * text: "Hello world",
31
+ * vector: [0.1, 0.2, ...],
32
+ * });
33
+ *
34
+ * const hits = await docs.query({
35
+ * query: [{ vector: [0.1, 0.2, ...] }],
36
+ * topK: 10,
37
+ * });
38
+ * ```
39
+ */
40
+ export declare class TurbopufferSearchIndex implements SearchIndex {
41
+ readonly id = "turbopuffer";
42
+ private readonly client;
43
+ /**
44
+ * Create a new Turbopuffer search index.
45
+ */
46
+ constructor(config: TurbopufferConfig);
47
+ /**
48
+ * Create a new index (namespace) in Turbopuffer.
49
+ */
50
+ createIndex(params: NewIndexParams): Promise<void>;
51
+ /**
52
+ * List indexes (namespaces) with optional pagination and prefix filtering.
53
+ */
54
+ listIndexes(params?: ListIndexesParams): Promise<CursorPage<IndexSummary, ListIndexesParams>>;
55
+ /**
56
+ * Get metadata and statistics about an index (namespace).
57
+ */
58
+ describeIndex(id: string): Promise<IndexStats>;
59
+ /**
60
+ * Delete an index (namespace) and all its documents.
61
+ */
62
+ deleteIndex(id: string): Promise<void>;
63
+ /**
64
+ * Get a handle for operating on a specific index.
65
+ */
66
+ index<TDocument = UnknownDocument>(id: string): IndexHandle<TDocument>;
67
+ /**
68
+ * Bind an existing resource as an index.
69
+ *
70
+ * Not supported by Turbopuffer - indexes are created implicitly.
71
+ */
72
+ bindIndex(_id: string, _config: unknown): Promise<void>;
73
+ /**
74
+ * Warm/preload an index for faster queries.
75
+ */
76
+ warm(id: string): Promise<void>;
77
+ /**
78
+ * Turbopuffer capabilities.
79
+ *
80
+ * Note: Turbopuffer supports text (BM25) and vector (ANN) separately,
81
+ * but not hybrid fusion (text + vector in the same query).
82
+ */
83
+ capabilities(): SearchCapabilities;
84
+ }
85
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,eAAe,EACf,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAA2B,MAAM,mBAAmB,CAAC;AAExE,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAI5C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,sBAAuB,YAAW,WAAW;IACxD,QAAQ,CAAC,EAAE,iBAAiB;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IAErC;;OAEG;gBACS,MAAM,EAAE,iBAAiB;IASrC;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBxD;;OAEG;IACG,WAAW,CACf,MAAM,GAAE,iBAAsB,GAC7B,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;IAkCvD;;OAEG;IACG,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAYpD;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5C;;OAEG;IACH,KAAK,CAAC,SAAS,GAAG,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC;IAItE;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7D;;OAEG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrC;;;;;OAKG;IACH,YAAY,IAAI,kBAAkB;CAUnC"}