@infino-ai/infino 0.4.0 → 0.5.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/infino/index.d.ts +10 -11
- package/infino/index.js +72 -23
- package/infino/native.d.ts +6 -5
- package/package.json +7 -7
package/infino/index.d.ts
CHANGED
|
@@ -115,22 +115,16 @@ export interface VectorFilter {
|
|
|
115
115
|
mode?: BoolMode;
|
|
116
116
|
}
|
|
117
117
|
export interface VectorSearchOptions {
|
|
118
|
-
/** IVF partitions to probe (higher = better recall, more work). */
|
|
119
|
-
nprobe?: number;
|
|
120
|
-
/** Over-fetch multiplier for the exact-rerank stage (higher = better recall). */
|
|
121
|
-
rerankMult?: number;
|
|
122
118
|
projection?: string[];
|
|
123
119
|
arrow?: boolean;
|
|
124
120
|
/** Restrict the kNN to rows matching a text predicate (pushdown pre-filter). */
|
|
125
121
|
filter?: VectorFilter;
|
|
126
122
|
}
|
|
127
|
-
/** Options for `hybridSearch`. `mode` applies to the BM25 side;
|
|
128
|
-
*
|
|
123
|
+
/** Options for `hybridSearch`. `mode` applies to the BM25 side; vector
|
|
124
|
+
* probe width and rerank budget are engine-decided. */
|
|
129
125
|
export interface HybridSearchOptions {
|
|
130
126
|
/** BM25 boolean mode: `"or"` (default) or `"and"`. */
|
|
131
127
|
mode?: BoolMode;
|
|
132
|
-
/** IVF partitions to probe on the vector side (higher = better recall). */
|
|
133
|
-
nprobe?: number;
|
|
134
128
|
projection?: string[];
|
|
135
129
|
arrow?: boolean;
|
|
136
130
|
}
|
|
@@ -152,8 +146,9 @@ export interface QueryOptions {
|
|
|
152
146
|
/** A table handle. Obtain one from {@link Connection.createTable} or {@link Connection.openTable}. */
|
|
153
147
|
export declare class Table {
|
|
154
148
|
private inner;
|
|
149
|
+
private remote;
|
|
155
150
|
/** @hidden */
|
|
156
|
-
constructor(inner: any);
|
|
151
|
+
constructor(inner: any, remote?: boolean);
|
|
157
152
|
/** The table's Arrow schema. */
|
|
158
153
|
schema(): arrow.Schema;
|
|
159
154
|
/**
|
|
@@ -224,8 +219,9 @@ export declare class Table {
|
|
|
224
219
|
/** A catalog connection. Create one with {@link connect}. */
|
|
225
220
|
export declare class Connection {
|
|
226
221
|
private inner;
|
|
222
|
+
private remote;
|
|
227
223
|
/** @hidden */
|
|
228
|
-
constructor(inner: any);
|
|
224
|
+
constructor(inner: any, remote?: boolean);
|
|
229
225
|
/**
|
|
230
226
|
* Provision the database this connection targets. On the hosted service this
|
|
231
227
|
* registers the database (throws if it already exists); on a local backend the
|
|
@@ -254,7 +250,10 @@ export declare class Connection {
|
|
|
254
250
|
* For a hosted (`https://`) target, authenticate with an API key: pass
|
|
255
251
|
* {@link ConnectOptions.apiKey}, or set the `INFINO_API_KEY` environment
|
|
256
252
|
* variable. Storage and cache tuning the URI can't carry also goes in
|
|
257
|
-
* `options` (see {@link ConnectOptions}).
|
|
253
|
+
* `options` (see {@link ConnectOptions}). An operation failure the hosted
|
|
254
|
+
* API reported carries the HTTP status it returned as `status` on the
|
|
255
|
+
* thrown Error (`409` create conflict, `404` missing, `503` transient —
|
|
256
|
+
* retry with backoff); errors on local connections have no `status`.
|
|
258
257
|
*
|
|
259
258
|
* ```ts
|
|
260
259
|
* // local
|
package/infino/index.js
CHANGED
|
@@ -121,7 +121,10 @@ function schemaToIpc(schema) {
|
|
|
121
121
|
// built by hand. The schema here is ours (from the addon), so its types
|
|
122
122
|
// are same-instance.
|
|
123
123
|
function buildColumn(field, rows) {
|
|
124
|
-
|
|
124
|
+
// An omitted key reads as `undefined`, which `vectorFromArray` would
|
|
125
|
+
// coerce to the type's zero value (0, "", NaN, false) rather than null —
|
|
126
|
+
// its null set is `[null]` only. Normalize so omitted == explicit null.
|
|
127
|
+
const values = rows.map((r) => r[field.name] ?? null);
|
|
125
128
|
const t = field.type;
|
|
126
129
|
if (t && typeof t.listSize === "number") {
|
|
127
130
|
const flat = Float32Array.from(values.flat());
|
|
@@ -189,17 +192,54 @@ function decode(buf, asArrow) {
|
|
|
189
192
|
return obj;
|
|
190
193
|
});
|
|
191
194
|
}
|
|
195
|
+
// --- hosted-API error status ---
|
|
196
|
+
// The hosted transport types a few statuses before the number itself is
|
|
197
|
+
// lost (the message keeps a stable prefix); every other HTTP failure it
|
|
198
|
+
// reports as "<op>: server returned NNN: <body>". Both let the status be
|
|
199
|
+
// recovered here. This table goes away once the transport carries the
|
|
200
|
+
// status structurally.
|
|
201
|
+
const PREFIX_STATUS = [
|
|
202
|
+
["NotFound: ", 404],
|
|
203
|
+
["AlreadyExists: ", 409],
|
|
204
|
+
["ConflictError: ", 412],
|
|
205
|
+
];
|
|
206
|
+
const SERVER_STATUS = /\bserver returned (\d{3}): /;
|
|
207
|
+
/**
|
|
208
|
+
* Run a native call. On a hosted (Infino Cloud) connection, a failure is
|
|
209
|
+
* rethrown as-is but tagged with `status` — the HTTP status the API
|
|
210
|
+
* actually returned — so callers can branch (e.g. `409` = already exists,
|
|
211
|
+
* `503` = transient, retry) without parsing message text. Local
|
|
212
|
+
* connections rethrow untouched.
|
|
213
|
+
*/
|
|
214
|
+
function guard(remote, fn) {
|
|
215
|
+
if (!remote)
|
|
216
|
+
return fn();
|
|
217
|
+
try {
|
|
218
|
+
return fn();
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
if (e instanceof Error && e.status === undefined) {
|
|
222
|
+
const prefixed = PREFIX_STATUS.find(([prefix]) => e.message.startsWith(prefix));
|
|
223
|
+
const status = prefixed ? prefixed[1] : Number(SERVER_STATUS.exec(e.message)?.[1]);
|
|
224
|
+
if (Number.isFinite(status))
|
|
225
|
+
e.status = status;
|
|
226
|
+
}
|
|
227
|
+
throw e;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
192
230
|
// --- friendly handles ---
|
|
193
231
|
/** A table handle. Obtain one from {@link Connection.createTable} or {@link Connection.openTable}. */
|
|
194
232
|
class Table {
|
|
195
233
|
inner;
|
|
234
|
+
remote;
|
|
196
235
|
/** @hidden */
|
|
197
|
-
constructor(inner) {
|
|
236
|
+
constructor(inner, remote = false) {
|
|
198
237
|
this.inner = inner;
|
|
238
|
+
this.remote = remote;
|
|
199
239
|
}
|
|
200
240
|
/** The table's Arrow schema. */
|
|
201
241
|
schema() {
|
|
202
|
-
return arrow.tableFromIPC(this.inner.schema()).schema;
|
|
242
|
+
return arrow.tableFromIPC(guard(this.remote, () => this.inner.schema())).schema;
|
|
203
243
|
}
|
|
204
244
|
/**
|
|
205
245
|
* Append rows. Accepts an array of objects, an apache-arrow
|
|
@@ -207,45 +247,47 @@ class Table {
|
|
|
207
247
|
* append == one commit.
|
|
208
248
|
*/
|
|
209
249
|
append(data) {
|
|
210
|
-
|
|
250
|
+
const ipc = dataToIpc(data, () => this.schema());
|
|
251
|
+
guard(this.remote, () => this.inner.append(ipc));
|
|
211
252
|
}
|
|
212
253
|
bm25Search(column, query, k, opts = {}) {
|
|
213
|
-
const buf = this.inner.bm25Search(column, query, k, opts.mode, opts.stats, opts.projection);
|
|
254
|
+
const buf = guard(this.remote, () => this.inner.bm25Search(column, query, k, opts.mode, opts.stats, opts.projection));
|
|
214
255
|
return decode(buf, opts.arrow);
|
|
215
256
|
}
|
|
216
257
|
vectorSearch(column, query, k, opts = {}) {
|
|
217
258
|
const q = query instanceof Float32Array ? query : Float32Array.from(query);
|
|
218
|
-
const buf = this.inner.vectorSearch(column, q, k, opts.
|
|
259
|
+
const buf = guard(this.remote, () => this.inner.vectorSearch(column, q, k, opts.projection, opts.filter));
|
|
219
260
|
return decode(buf, opts.arrow);
|
|
220
261
|
}
|
|
221
262
|
hybridSearch(textColumn, textQuery, vectorColumn, vectorQuery, k, opts = {}) {
|
|
222
263
|
const q = vectorQuery instanceof Float32Array ? vectorQuery : Float32Array.from(vectorQuery);
|
|
223
|
-
const buf = this.inner.hybridSearch(textColumn, textQuery, vectorColumn, q, k, opts.mode, opts.
|
|
264
|
+
const buf = guard(this.remote, () => this.inner.hybridSearch(textColumn, textQuery, vectorColumn, q, k, opts.mode, opts.projection));
|
|
224
265
|
return decode(buf, opts.arrow);
|
|
225
266
|
}
|
|
226
267
|
tokenMatch(column, query, opts = {}) {
|
|
227
|
-
const buf = this.inner.tokenMatch(column, query, opts.mode, opts.projection);
|
|
268
|
+
const buf = guard(this.remote, () => this.inner.tokenMatch(column, query, opts.mode, opts.projection));
|
|
228
269
|
return decode(buf, opts.arrow);
|
|
229
270
|
}
|
|
230
271
|
exactMatch(column, value, opts = {}) {
|
|
231
|
-
const buf = this.inner.exactMatch(column, value, opts.projection);
|
|
272
|
+
const buf = guard(this.remote, () => this.inner.exactMatch(column, value, opts.projection));
|
|
232
273
|
return decode(buf, opts.arrow);
|
|
233
274
|
}
|
|
234
275
|
/** Count rows matching a BM25 keyword `query` over `column`, without
|
|
235
276
|
* fetching them. `mode` is `"or"` (default) or `"and"`. */
|
|
236
277
|
count(column, query, opts = {}) {
|
|
237
|
-
return this.inner.count(column, query, opts.mode);
|
|
278
|
+
return guard(this.remote, () => this.inner.count(column, query, opts.mode));
|
|
238
279
|
}
|
|
239
280
|
/** Replace rows matching a SQL predicate (e.g. `"status = 'spam'"`) with
|
|
240
281
|
* `data` (same shapes as `append`), 1:1 — the matched count must equal the
|
|
241
282
|
* replacement-row count. Requires durable storage (not `memory://`). */
|
|
242
283
|
update(predicate, data) {
|
|
243
|
-
|
|
284
|
+
const ipc = dataToIpc(data, () => this.schema());
|
|
285
|
+
return guard(this.remote, () => this.inner.update(predicate, ipc));
|
|
244
286
|
}
|
|
245
287
|
/** Delete rows matching a SQL predicate (e.g. `"status = 'spam'"`).
|
|
246
288
|
* Requires durable storage (not `memory://`). */
|
|
247
289
|
delete(predicate) {
|
|
248
|
-
return this.inner.delete(predicate);
|
|
290
|
+
return guard(this.remote, () => this.inner.delete(predicate));
|
|
249
291
|
}
|
|
250
292
|
/**
|
|
251
293
|
* Merge small / underfilled superfiles into larger ones (omit `settings`
|
|
@@ -255,7 +297,7 @@ class Table {
|
|
|
255
297
|
* so calling this on a hosted (`https://…`) connection throws.
|
|
256
298
|
*/
|
|
257
299
|
optimize(settings) {
|
|
258
|
-
this.inner.optimize(settings);
|
|
300
|
+
guard(this.remote, () => this.inner.optimize(settings));
|
|
259
301
|
}
|
|
260
302
|
/**
|
|
261
303
|
* Delete orphaned storage objects left by compaction or interrupted writes.
|
|
@@ -266,16 +308,18 @@ class Table {
|
|
|
266
308
|
* calling this on a hosted connection throws.
|
|
267
309
|
*/
|
|
268
310
|
gc(graceSecs) {
|
|
269
|
-
return this.inner.gc(graceSecs);
|
|
311
|
+
return guard(this.remote, () => this.inner.gc(graceSecs));
|
|
270
312
|
}
|
|
271
313
|
}
|
|
272
314
|
exports.Table = Table;
|
|
273
315
|
/** A catalog connection. Create one with {@link connect}. */
|
|
274
316
|
class Connection {
|
|
275
317
|
inner;
|
|
318
|
+
remote;
|
|
276
319
|
/** @hidden */
|
|
277
|
-
constructor(inner) {
|
|
320
|
+
constructor(inner, remote = false) {
|
|
278
321
|
this.inner = inner;
|
|
322
|
+
this.remote = remote;
|
|
279
323
|
}
|
|
280
324
|
/**
|
|
281
325
|
* Provision the database this connection targets. On the hosted service this
|
|
@@ -283,23 +327,24 @@ class Connection {
|
|
|
283
327
|
* catalog root is the database, so it is a no-op success.
|
|
284
328
|
*/
|
|
285
329
|
createDatabase() {
|
|
286
|
-
this.inner.createDatabase();
|
|
330
|
+
guard(this.remote, () => this.inner.createDatabase());
|
|
287
331
|
}
|
|
288
332
|
/** Create a table from an apache-arrow `Schema` or `{ column: type }`. */
|
|
289
333
|
createTable(name, schema, indexes) {
|
|
290
|
-
|
|
334
|
+
const ipc = schemaToIpc(schema);
|
|
335
|
+
return new Table(guard(this.remote, () => this.inner.createTable(name, ipc, indexes)), this.remote);
|
|
291
336
|
}
|
|
292
337
|
openTable(name) {
|
|
293
|
-
return new Table(this.inner.openTable(name));
|
|
338
|
+
return new Table(guard(this.remote, () => this.inner.openTable(name)), this.remote);
|
|
294
339
|
}
|
|
295
340
|
dropTable(name, purge) {
|
|
296
|
-
this.inner.dropTable(name, purge);
|
|
341
|
+
guard(this.remote, () => this.inner.dropTable(name, purge));
|
|
297
342
|
}
|
|
298
343
|
listTables() {
|
|
299
|
-
return this.inner.listTables();
|
|
344
|
+
return guard(this.remote, () => this.inner.listTables());
|
|
300
345
|
}
|
|
301
346
|
querySql(sql, opts = {}) {
|
|
302
|
-
return decode(this.inner.querySql(sql), opts.arrow);
|
|
347
|
+
return decode(guard(this.remote, () => this.inner.querySql(sql)), opts.arrow);
|
|
303
348
|
}
|
|
304
349
|
}
|
|
305
350
|
exports.Connection = Connection;
|
|
@@ -314,7 +359,10 @@ exports.Connection = Connection;
|
|
|
314
359
|
* For a hosted (`https://`) target, authenticate with an API key: pass
|
|
315
360
|
* {@link ConnectOptions.apiKey}, or set the `INFINO_API_KEY` environment
|
|
316
361
|
* variable. Storage and cache tuning the URI can't carry also goes in
|
|
317
|
-
* `options` (see {@link ConnectOptions}).
|
|
362
|
+
* `options` (see {@link ConnectOptions}). An operation failure the hosted
|
|
363
|
+
* API reported carries the HTTP status it returned as `status` on the
|
|
364
|
+
* thrown Error (`409` create conflict, `404` missing, `503` transient —
|
|
365
|
+
* retry with backoff); errors on local connections have no `status`.
|
|
318
366
|
*
|
|
319
367
|
* ```ts
|
|
320
368
|
* // local
|
|
@@ -324,5 +372,6 @@ exports.Connection = Connection;
|
|
|
324
372
|
* ```
|
|
325
373
|
*/
|
|
326
374
|
function connect(uri, options) {
|
|
327
|
-
|
|
375
|
+
const remote = /^https?:\/\//.test(uri);
|
|
376
|
+
return new Connection(guard(remote, () => (0, native_js_1.connect)(uri, options)), remote);
|
|
328
377
|
}
|
package/infino/native.d.ts
CHANGED
|
@@ -175,7 +175,7 @@ export declare class Table {
|
|
|
175
175
|
* opposite direction from `bm25Search`'s similarity. Fuse with
|
|
176
176
|
* `hybridSearch`.
|
|
177
177
|
*/
|
|
178
|
-
vectorSearch(column: string, query: Float32Array, k: number,
|
|
178
|
+
vectorSearch(column: string, query: Float32Array, k: number, projection?: Array<string> | undefined | null, filter?: VectorFilter | undefined | null): Buffer
|
|
179
179
|
/**
|
|
180
180
|
* Unranked token match over one FTS column — every row whose `column`
|
|
181
181
|
* matches the query's tokens under `mode` (`"or"` default, `"and"`).
|
|
@@ -197,11 +197,12 @@ export declare class Table {
|
|
|
197
197
|
/**
|
|
198
198
|
* Hybrid BM25 + vector search fused with reciprocal-rank fusion.
|
|
199
199
|
* `text_column`/`text_query` (under `mode`) drive BM25; `vector_column`/
|
|
200
|
-
* `vector_query` (a `Float32Array
|
|
201
|
-
*
|
|
202
|
-
* fused RRF score (higher is
|
|
200
|
+
* `vector_query` (a `Float32Array`) drive vector kNN — probe width and
|
|
201
|
+
* rerank budget are engine-decided. Returns Arrow rows like
|
|
202
|
+
* [`Table::bm25_search`], with `score` the fused RRF score (higher is
|
|
203
|
+
* better); `projection` selects columns.
|
|
203
204
|
*/
|
|
204
|
-
hybridSearch(textColumn: string, textQuery: string, vectorColumn: string, vectorQuery: Float32Array, k: number, mode?: string | undefined | null,
|
|
205
|
+
hybridSearch(textColumn: string, textQuery: string, vectorColumn: string, vectorQuery: Float32Array, k: number, mode?: string | undefined | null, projection?: Array<string> | undefined | null): Buffer
|
|
205
206
|
/**
|
|
206
207
|
* Delete every row matching a SQL `predicate` (e.g. `"status = 'spam'"`),
|
|
207
208
|
* returning the mutation counts. Requires durable storage — a `memory://`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@infino-ai/infino",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Fast search on object storage — SQL, full-text, and vector search.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -82,12 +82,12 @@
|
|
|
82
82
|
"apache-arrow": "^17"
|
|
83
83
|
},
|
|
84
84
|
"optionalDependencies": {
|
|
85
|
-
"infx-darwin-x64": "0.
|
|
86
|
-
"infx-darwin-arm64": "0.
|
|
87
|
-
"infx-linux-x64-gnu": "0.
|
|
88
|
-
"infx-linux-arm64-gnu": "0.
|
|
89
|
-
"infx-linux-x64-musl": "0.
|
|
90
|
-
"infx-linux-arm64-musl": "0.
|
|
85
|
+
"infx-darwin-x64": "0.5.0",
|
|
86
|
+
"infx-darwin-arm64": "0.5.0",
|
|
87
|
+
"infx-linux-x64-gnu": "0.5.0",
|
|
88
|
+
"infx-linux-arm64-gnu": "0.5.0",
|
|
89
|
+
"infx-linux-x64-musl": "0.5.0",
|
|
90
|
+
"infx-linux-arm64-musl": "0.5.0"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
93
|
"@napi-rs/cli": "^2.18.0",
|