sliftutils 1.4.86 → 1.4.88
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/index.d.ts +2 -0
- package/package.json +1 -1
- package/storage/BulkDatabase2/BulkDatabaseFormat.d.ts +2 -0
- package/storage/BulkDatabase2/BulkDatabaseFormat.ts +5 -0
- package/storage/BulkDatabase2/LoadedIndex.ts +28 -4
- package/storage/BulkDatabase2/streamLog.ts +1 -0
- package/storage/proxydatabase/ivfEmbeddingDatabase.ts +2 -2
package/index.d.ts
CHANGED
|
@@ -1153,6 +1153,7 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseFormat" {
|
|
|
1153
1153
|
export declare function buildFileBuffer(rows: Record<string, unknown>[], times: number[], targetBytes?: number): BuiltFile[];
|
|
1154
1154
|
export declare function buildFileBufferRaw(rows: RawRow[], targetBytes?: number): BuiltFile[];
|
|
1155
1155
|
export type BaseBulkDatabaseReader = {
|
|
1156
|
+
name?: string;
|
|
1156
1157
|
rowCount: number;
|
|
1157
1158
|
totalBytes: number;
|
|
1158
1159
|
minTime: number;
|
|
@@ -1194,6 +1195,7 @@ declare module "sliftutils/storage/BulkDatabase2/BulkDatabaseFormat" {
|
|
|
1194
1195
|
export declare function loadBulkDatabase(config: {
|
|
1195
1196
|
totalBytes: number;
|
|
1196
1197
|
getRange: (start: number, end: number) => Promise<Buffer>;
|
|
1198
|
+
name?: string;
|
|
1197
1199
|
}): Promise<BaseBulkDatabaseReader>;
|
|
1198
1200
|
|
|
1199
1201
|
}
|
package/package.json
CHANGED
|
@@ -41,6 +41,7 @@ export interface BuiltFile {
|
|
|
41
41
|
export declare function buildFileBuffer(rows: Record<string, unknown>[], times: number[], targetBytes?: number): BuiltFile[];
|
|
42
42
|
export declare function buildFileBufferRaw(rows: RawRow[], targetBytes?: number): BuiltFile[];
|
|
43
43
|
export type BaseBulkDatabaseReader = {
|
|
44
|
+
name?: string;
|
|
44
45
|
rowCount: number;
|
|
45
46
|
totalBytes: number;
|
|
46
47
|
minTime: number;
|
|
@@ -82,4 +83,5 @@ export declare function loadBulkHeader(getRange: (start: number, end: number) =>
|
|
|
82
83
|
export declare function loadBulkDatabase(config: {
|
|
83
84
|
totalBytes: number;
|
|
84
85
|
getRange: (start: number, end: number) => Promise<Buffer>;
|
|
86
|
+
name?: string;
|
|
85
87
|
}): Promise<BaseBulkDatabaseReader>;
|
|
@@ -410,6 +410,9 @@ export function buildFileBufferRaw(rows: RawRow[], targetBytes = TARGET_FILE_BYT
|
|
|
410
410
|
}
|
|
411
411
|
|
|
412
412
|
export type BaseBulkDatabaseReader = {
|
|
413
|
+
// Identifies the source this reader came from (the bulk file name, or "(streams)") so the join can
|
|
414
|
+
// name the offending file when one of its reads fails. Undefined for readers built without one.
|
|
415
|
+
name?: string;
|
|
413
416
|
rowCount: number;
|
|
414
417
|
totalBytes: number;
|
|
415
418
|
// Write-time bounds of this reader's data (0 if unknown — old bulk files).
|
|
@@ -468,6 +471,7 @@ export async function loadBulkHeader(getRange: (start: number, end: number) => P
|
|
|
468
471
|
export async function loadBulkDatabase(config: {
|
|
469
472
|
totalBytes: number;
|
|
470
473
|
getRange: (start: number, end: number) => Promise<Buffer>;
|
|
474
|
+
name?: string;
|
|
471
475
|
}): Promise<BaseBulkDatabaseReader> {
|
|
472
476
|
const headerLength = (await config.getRange(0, 4)).readUInt32LE(0);
|
|
473
477
|
if (headerLength <= 0 || headerLength > config.totalBytes) {
|
|
@@ -502,6 +506,7 @@ export async function loadBulkDatabase(config: {
|
|
|
502
506
|
: keys.map(() => header.maxTime || 0);
|
|
503
507
|
|
|
504
508
|
return {
|
|
509
|
+
name: config.name,
|
|
505
510
|
rowCount,
|
|
506
511
|
totalBytes: config.totalBytes,
|
|
507
512
|
minTime: header.minTime || 0,
|
|
@@ -257,7 +257,7 @@ export async function loadFileReader(name: string, storage: FileStorage, f: Bulk
|
|
|
257
257
|
const raw = await makeRawGetRange(storage, f.fileName);
|
|
258
258
|
const fileId = nullJoin(name, f.fileName);
|
|
259
259
|
const opened = await blockCache.open(fileId, raw.size, raw.rawGetRange);
|
|
260
|
-
const reader = await loadBulkDatabase({ totalBytes: opened.uncompressedSize, getRange: opened.getRange });
|
|
260
|
+
const reader = await loadBulkDatabase({ totalBytes: opened.uncompressedSize, getRange: opened.getRange, name: f.fileName });
|
|
261
261
|
cache.set(f.fileName, reader);
|
|
262
262
|
return reader;
|
|
263
263
|
}
|
|
@@ -327,6 +327,19 @@ function pruneSubCaches(bulkFiles: BulkFileInfo[], streamFiles: StreamFileInfo[]
|
|
|
327
327
|
for (const n of subCaches.stream.keys()) if (!liveStream.has(n)) subCaches.stream.delete(n);
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
// A corrupt/unreadable column in ONE underlying file must not break the whole joined read. When a
|
|
331
|
+
// source's column read throws, we drop that source for that column (falling through to older readers,
|
|
332
|
+
// exactly as if the file never set the column) and warn once per (file, column) so the corruption is
|
|
333
|
+
// visible without flooding the log on every rebuild.
|
|
334
|
+
const warnedCorruptReads = new Set<string>();
|
|
335
|
+
function warnCorruptRead(db: BaseBulkDatabaseReader, column: string, e: unknown): void {
|
|
336
|
+
const fileName = db.name || "(unknown source)";
|
|
337
|
+
const dedupeKey = nullJoin(fileName, column);
|
|
338
|
+
if (warnedCorruptReads.has(dedupeKey)) return;
|
|
339
|
+
warnedCorruptReads.add(dedupeKey);
|
|
340
|
+
console.warn(`${red("corrupt read")}: file ${fileName} column ${JSON.stringify(column)} could not be read - skipping it (its data is dropped from results): ${(e as Error).message}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
330
343
|
function joinBulkDatabases(databases: BaseBulkDatabaseReader[]): ResolvedReader {
|
|
331
344
|
const deleteTime = new Map<string, number>();
|
|
332
345
|
for (const db of databases) {
|
|
@@ -367,8 +380,13 @@ function joinBulkDatabases(databases: BaseBulkDatabaseReader[]): ResolvedReader
|
|
|
367
380
|
async getColumn(column) {
|
|
368
381
|
const perReader = await Promise.all(databases.map(async db => {
|
|
369
382
|
if (!db.columns.some(c => c.column === column)) return undefined;
|
|
370
|
-
|
|
371
|
-
|
|
383
|
+
try {
|
|
384
|
+
const entries = await db.getColumn(column);
|
|
385
|
+
return new Map(entries.map(e => [e.key, { value: e.value, time: e.time }]));
|
|
386
|
+
} catch (e) {
|
|
387
|
+
warnCorruptRead(db, column, e);
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
372
390
|
}));
|
|
373
391
|
return keys.map(key => {
|
|
374
392
|
let bestTime = -Infinity;
|
|
@@ -391,7 +409,13 @@ function joinBulkDatabases(databases: BaseBulkDatabaseReader[]): ResolvedReader
|
|
|
391
409
|
let found = false;
|
|
392
410
|
for (const db of databases) {
|
|
393
411
|
if (!db.columns.some(c => c.column === column)) continue;
|
|
394
|
-
|
|
412
|
+
let r;
|
|
413
|
+
try {
|
|
414
|
+
r = await db.getSingleField(key, column);
|
|
415
|
+
} catch (e) {
|
|
416
|
+
warnCorruptRead(db, column, e);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
395
419
|
if (r === ABSENT) continue;
|
|
396
420
|
if (r.time > bestTime) { bestTime = r.time; bestVal = r.value; found = true; }
|
|
397
421
|
}
|
|
@@ -106,6 +106,7 @@ export function streamReaderFromEntries(entries: StreamEntry[], totalBytes: numb
|
|
|
106
106
|
}
|
|
107
107
|
const keyIndexMap = buildKeyIndex(keys);
|
|
108
108
|
let reader: BaseBulkDatabaseReader = {
|
|
109
|
+
name: "(streams)",
|
|
109
110
|
totalBytes,
|
|
110
111
|
rowCount: keys.length,
|
|
111
112
|
minTime: times.size ? minTime : 0,
|
|
@@ -2,7 +2,6 @@ import cborx from "cbor-x";
|
|
|
2
2
|
import { Database, namespaceDatabase } from "./Database";
|
|
3
3
|
import { TransactionSetStore, transactionRead, transactionMutate, transactionDelete, replayTransactionStore } from "./transactionSet";
|
|
4
4
|
import { StoredEmbedding, EmbeddingFormat, getCloseness, embeddingToFloat32, releaseFloat32, encodeEmbedding, hashEmbedding } from "../embeddingFormats";
|
|
5
|
-
import { isDefined } from "../../misc/types";
|
|
6
5
|
|
|
7
6
|
export type IvfConfig = {
|
|
8
7
|
model: string;
|
|
@@ -241,7 +240,8 @@ export function searchEmbeddings(
|
|
|
241
240
|
if (!centroids.size) return [];
|
|
242
241
|
const probeCellCount = Math.max(1, Math.ceil(options.probeBudget / config.cellTargetSize));
|
|
243
242
|
const probeCellIds = rankCellsByCloseness(query, centroids).slice(0, probeCellCount);
|
|
244
|
-
const stores =
|
|
243
|
+
const stores = database.readData(root => probeCellIds.map(cellId => root.cells[cellId]));
|
|
244
|
+
if (!stores) return undefined;
|
|
245
245
|
for (const store of stores) {
|
|
246
246
|
const members = replayTransactionStore<StoredEmbedding>(store);
|
|
247
247
|
for (const ref of members.keys()) {
|