@babav/knowledge-core-client 0.22.5 → 0.23.1
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/dist/index.d.ts +36 -5
- package/dist/index.js +45 -18
- package/package.json +1 -1
- package/src/index.ts +61 -14
package/dist/index.d.ts
CHANGED
|
@@ -214,6 +214,16 @@ export interface UploadUrl {
|
|
|
214
214
|
gcs_uri: string;
|
|
215
215
|
expires_in: number;
|
|
216
216
|
}
|
|
217
|
+
/** Batch of signed PUT URLs (corpora.uploadMany drives this). `items` is in request order. */
|
|
218
|
+
export interface BatchUploadUrls {
|
|
219
|
+
items: Array<{
|
|
220
|
+
filename: string;
|
|
221
|
+
document_id: UUID;
|
|
222
|
+
upload_url: string;
|
|
223
|
+
gcs_uri: string;
|
|
224
|
+
}>;
|
|
225
|
+
expires_in: number;
|
|
226
|
+
}
|
|
217
227
|
export interface Conversation {
|
|
218
228
|
id: UUID;
|
|
219
229
|
title: string | null;
|
|
@@ -433,11 +443,10 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
433
443
|
limit?: number;
|
|
434
444
|
cursor?: string;
|
|
435
445
|
}) => Promise<Page<Document>>;
|
|
436
|
-
/** Ingest
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* ~32 MB), smaller files via one multipart request. One method, any size. */
|
|
446
|
+
/** Ingest ONE document ASYNCHRONOUSLY — resolves with the created document at status
|
|
447
|
+
* `pending` (HTTP 202); track via documents.events()/get() or the tenant webhook.
|
|
448
|
+
* ALWAYS uploads the bytes straight to GCS via a signed URL (uploadUrl → PUT → ingestFromUpload),
|
|
449
|
+
* regardless of size — so there is NO request-size cap, ever. One method, any size. */
|
|
441
450
|
ingestDocument: (id: UUID, a: {
|
|
442
451
|
file: FileData;
|
|
443
452
|
filename: string;
|
|
@@ -472,6 +481,21 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
472
481
|
folder_id?: UUID;
|
|
473
482
|
custom_metadata?: Record<string, unknown>;
|
|
474
483
|
}) => Promise<Document>;
|
|
484
|
+
/** Ingest MANY files in ONE go — any size, no 429, no client backoff. Mints all signed URLs in
|
|
485
|
+
* one request, PUTs the bytes straight to GCS (bounded concurrency; never touches the KC), then
|
|
486
|
+
* registers the whole batch in one request (N pending docs + N Cloud Tasks). `opts`
|
|
487
|
+
* (folder_id / visibility / custom_metadata — the latter carries `owner`) applies to ALL files.
|
|
488
|
+
* Resolves with the created `pending` documents. */
|
|
489
|
+
uploadMany: (id: UUID, files: Array<{
|
|
490
|
+
file: FileData;
|
|
491
|
+
filename: string;
|
|
492
|
+
content_type?: string;
|
|
493
|
+
}>, opts?: {
|
|
494
|
+
folder_id?: UUID;
|
|
495
|
+
visibility?: Visibility;
|
|
496
|
+
custom_metadata?: Record<string, unknown>;
|
|
497
|
+
concurrency?: number;
|
|
498
|
+
}) => Promise<Document[]>;
|
|
475
499
|
};
|
|
476
500
|
parse(a: {
|
|
477
501
|
file: FileData;
|
|
@@ -509,6 +533,13 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
509
533
|
}) => Promise<Document>;
|
|
510
534
|
contentUrl: (id: UUID, disposition?: "inline" | "attachment") => Promise<ContentUrl>;
|
|
511
535
|
delete: (id: UUID) => Promise<void>;
|
|
536
|
+
/** Delete MANY documents (by id) from one corpus in ONE call (202) — collapses N deletes into
|
|
537
|
+
* one request. IDs not in the corpus are skipped. Completion observable via documents.events /
|
|
538
|
+
* documents.get() → 404. Returns { deleting: ids, count }. */
|
|
539
|
+
deleteMany: (corpusId: UUID, documentIds: UUID[]) => Promise<{
|
|
540
|
+
deleting: UUID[];
|
|
541
|
+
count: number;
|
|
542
|
+
}>;
|
|
512
543
|
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). Snapshot on
|
|
513
544
|
* connect, then a delta per transition, then `complete` when nothing is in-flight. Abort via
|
|
514
545
|
* the signal. 501 (onError) if the environment has no doc-status bus — fall back to polling. */
|
package/dist/index.js
CHANGED
|
@@ -226,24 +226,11 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
226
226
|
countDocuments: (id) => this.request("GET", `/v1/corpora/${id}/documents/count`),
|
|
227
227
|
/** Documents in the corpus whose filename contains `q` (case-insensitive), paginated. */
|
|
228
228
|
searchDocuments: (id, q, opts) => this.request("GET", `/v1/corpora/${id}/documents/search`, { query: { q, ...opts } }),
|
|
229
|
-
/** Ingest
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
|
|
234
|
-
ingestDocument: (id, a) => {
|
|
235
|
-
if (fileSize(a.file) > MULTIPART_MAX_BYTES)
|
|
236
|
-
return this.corpora.uploadDocument(id, a);
|
|
237
|
-
const fd = new FormData();
|
|
238
|
-
fd.append("file", toBlob(a.file, a.content_type), a.filename);
|
|
239
|
-
if (a.custom_metadata)
|
|
240
|
-
fd.append("custom_metadata", JSON.stringify(a.custom_metadata));
|
|
241
|
-
if (a.visibility)
|
|
242
|
-
fd.append("visibility", a.visibility);
|
|
243
|
-
if (a.folder_id)
|
|
244
|
-
fd.append("folder_id", a.folder_id);
|
|
245
|
-
return this.request("POST", `/v1/corpora/${id}/documents`, { body: fd });
|
|
246
|
-
},
|
|
229
|
+
/** Ingest ONE document ASYNCHRONOUSLY — resolves with the created document at status
|
|
230
|
+
* `pending` (HTTP 202); track via documents.events()/get() or the tenant webhook.
|
|
231
|
+
* ALWAYS uploads the bytes straight to GCS via a signed URL (uploadUrl → PUT → ingestFromUpload),
|
|
232
|
+
* regardless of size — so there is NO request-size cap, ever. One method, any size. */
|
|
233
|
+
ingestDocument: (id, a) => this.corpora.uploadDocument(id, a),
|
|
247
234
|
/** Mint a signed PUT URL to upload a large file straight to GCS (bypasses the
|
|
248
235
|
* ~32 MB request limit). PUT the bytes to upload_url, then ingestFromUpload. */
|
|
249
236
|
uploadUrl: (id, a) => this.request("POST", `/v1/corpora/${id}/documents/upload-url`, { json: a }),
|
|
@@ -267,6 +254,33 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
267
254
|
visibility: a.visibility, folder_id: a.folder_id, custom_metadata: a.custom_metadata,
|
|
268
255
|
});
|
|
269
256
|
},
|
|
257
|
+
/** Ingest MANY files in ONE go — any size, no 429, no client backoff. Mints all signed URLs in
|
|
258
|
+
* one request, PUTs the bytes straight to GCS (bounded concurrency; never touches the KC), then
|
|
259
|
+
* registers the whole batch in one request (N pending docs + N Cloud Tasks). `opts`
|
|
260
|
+
* (folder_id / visibility / custom_metadata — the latter carries `owner`) applies to ALL files.
|
|
261
|
+
* Resolves with the created `pending` documents. */
|
|
262
|
+
uploadMany: async (id, files, opts) => {
|
|
263
|
+
if (files.length === 0)
|
|
264
|
+
return [];
|
|
265
|
+
const urls = await this.request("POST", `/v1/corpora/${id}/documents/upload-urls`, { json: { items: files.map((f) => ({ filename: f.filename, content_type: f.content_type })) } });
|
|
266
|
+
const items = urls.items; // same order as the request
|
|
267
|
+
await poolMap(files.length, opts?.concurrency ?? 6, async (i) => {
|
|
268
|
+
const f = files[i];
|
|
269
|
+
const it = items[i];
|
|
270
|
+
const put = await this._fetch(it.upload_url, {
|
|
271
|
+
method: "PUT",
|
|
272
|
+
body: toBlob(f.file, f.content_type),
|
|
273
|
+
headers: f.content_type ? { "content-type": f.content_type } : {},
|
|
274
|
+
});
|
|
275
|
+
if (!put.ok)
|
|
276
|
+
throw new KnowledgeCoreError(put.status, safeJson(await put.text()), it.upload_url);
|
|
277
|
+
});
|
|
278
|
+
const res = await this.request("POST", `/v1/corpora/${id}/documents/batch`, { json: {
|
|
279
|
+
folder_id: opts?.folder_id, visibility: opts?.visibility, custom_metadata: opts?.custom_metadata,
|
|
280
|
+
items: items.map((it) => ({ document_id: it.document_id, filename: it.filename })),
|
|
281
|
+
} });
|
|
282
|
+
return res.documents;
|
|
283
|
+
},
|
|
270
284
|
};
|
|
271
285
|
// --- parsing (utility: file -> text, stores nothing) ---
|
|
272
286
|
parse(a) {
|
|
@@ -299,6 +313,10 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
299
313
|
update: (id, b) => this.request("PATCH", `/v1/documents/${id}`, { json: b }),
|
|
300
314
|
contentUrl: (id, disposition = "inline") => this.request("GET", `/v1/documents/${id}/content-url`, { query: { disposition } }),
|
|
301
315
|
delete: (id) => this.request("DELETE", `/v1/documents/${id}`),
|
|
316
|
+
/** Delete MANY documents (by id) from one corpus in ONE call (202) — collapses N deletes into
|
|
317
|
+
* one request. IDs not in the corpus are skipped. Completion observable via documents.events /
|
|
318
|
+
* documents.get() → 404. Returns { deleting: ids, count }. */
|
|
319
|
+
deleteMany: (corpusId, documentIds) => this.request("POST", `/v1/corpora/${corpusId}/documents/batch-delete`, { json: { document_ids: documentIds } }),
|
|
302
320
|
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). Snapshot on
|
|
303
321
|
* connect, then a delta per transition, then `complete` when nothing is in-flight. Abort via
|
|
304
322
|
* the signal. 501 (onError) if the environment has no doc-status bus — fall back to polling. */
|
|
@@ -376,6 +394,15 @@ export class AdminClient extends HttpBase {
|
|
|
376
394
|
// ---------------------------------------------------------------------------
|
|
377
395
|
// helpers
|
|
378
396
|
// ---------------------------------------------------------------------------
|
|
397
|
+
/** Run fn(0..n-1) with bounded concurrency (used by uploadMany for the direct-to-GCS PUTs). */
|
|
398
|
+
async function poolMap(n, concurrency, fn) {
|
|
399
|
+
let next = 0;
|
|
400
|
+
const workers = Array.from({ length: Math.max(1, Math.min(concurrency, n)) }, async () => {
|
|
401
|
+
for (let i = next++; i < n; i = next++)
|
|
402
|
+
await fn(i);
|
|
403
|
+
});
|
|
404
|
+
await Promise.all(workers);
|
|
405
|
+
}
|
|
379
406
|
function dispatchSse(frame, h) {
|
|
380
407
|
let event = "message";
|
|
381
408
|
const dataLines = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babav/knowledge-core-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"description": "TypeScript client for the Babav Knowledge Core API (Deno + Node 18+, zero deps). Includes the babav.visual grammar TYPES at the ./visual subpath (types only; all visual rendering is server-side).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/index.ts
CHANGED
|
@@ -235,6 +235,11 @@ export interface UploadUrl {
|
|
|
235
235
|
gcs_uri: string;
|
|
236
236
|
expires_in: number;
|
|
237
237
|
}
|
|
238
|
+
/** Batch of signed PUT URLs (corpora.uploadMany drives this). `items` is in request order. */
|
|
239
|
+
export interface BatchUploadUrls {
|
|
240
|
+
items: Array<{ filename: string; document_id: UUID; upload_url: string; gcs_uri: string }>;
|
|
241
|
+
expires_in: number;
|
|
242
|
+
}
|
|
238
243
|
export interface Conversation {
|
|
239
244
|
id: UUID;
|
|
240
245
|
title: string | null;
|
|
@@ -596,20 +601,12 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
596
601
|
/** Documents in the corpus whose filename contains `q` (case-insensitive), paginated. */
|
|
597
602
|
searchDocuments: (id: UUID, q: string, opts?: { limit?: number; cursor?: string }) =>
|
|
598
603
|
this.request<Page<Document>>("GET", `/v1/corpora/${id}/documents/search`, { query: { q, ...opts } }),
|
|
599
|
-
/** Ingest
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (fileSize(a.file) > MULTIPART_MAX_BYTES) return this.corpora.uploadDocument(id, a);
|
|
606
|
-
const fd = new FormData();
|
|
607
|
-
fd.append("file", toBlob(a.file, a.content_type), a.filename);
|
|
608
|
-
if (a.custom_metadata) fd.append("custom_metadata", JSON.stringify(a.custom_metadata));
|
|
609
|
-
if (a.visibility) fd.append("visibility", a.visibility);
|
|
610
|
-
if (a.folder_id) fd.append("folder_id", a.folder_id);
|
|
611
|
-
return this.request<Document>("POST", `/v1/corpora/${id}/documents`, { body: fd });
|
|
612
|
-
},
|
|
604
|
+
/** Ingest ONE document ASYNCHRONOUSLY — resolves with the created document at status
|
|
605
|
+
* `pending` (HTTP 202); track via documents.events()/get() or the tenant webhook.
|
|
606
|
+
* ALWAYS uploads the bytes straight to GCS via a signed URL (uploadUrl → PUT → ingestFromUpload),
|
|
607
|
+
* regardless of size — so there is NO request-size cap, ever. One method, any size. */
|
|
608
|
+
ingestDocument: (id: UUID, a: { file: FileData; filename: string; content_type?: string; visibility?: Visibility; folder_id?: UUID; custom_metadata?: Record<string, unknown> }) =>
|
|
609
|
+
this.corpora.uploadDocument(id, a),
|
|
613
610
|
/** Mint a signed PUT URL to upload a large file straight to GCS (bypasses the
|
|
614
611
|
* ~32 MB request limit). PUT the bytes to upload_url, then ingestFromUpload. */
|
|
615
612
|
uploadUrl: (id: UUID, a: { filename: string; content_type?: string }) =>
|
|
@@ -635,6 +632,41 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
635
632
|
visibility: a.visibility, folder_id: a.folder_id, custom_metadata: a.custom_metadata,
|
|
636
633
|
});
|
|
637
634
|
},
|
|
635
|
+
/** Ingest MANY files in ONE go — any size, no 429, no client backoff. Mints all signed URLs in
|
|
636
|
+
* one request, PUTs the bytes straight to GCS (bounded concurrency; never touches the KC), then
|
|
637
|
+
* registers the whole batch in one request (N pending docs + N Cloud Tasks). `opts`
|
|
638
|
+
* (folder_id / visibility / custom_metadata — the latter carries `owner`) applies to ALL files.
|
|
639
|
+
* Resolves with the created `pending` documents. */
|
|
640
|
+
uploadMany: async (
|
|
641
|
+
id: UUID,
|
|
642
|
+
files: Array<{ file: FileData; filename: string; content_type?: string }>,
|
|
643
|
+
opts?: { folder_id?: UUID; visibility?: Visibility; custom_metadata?: Record<string, unknown>; concurrency?: number },
|
|
644
|
+
): Promise<Document[]> => {
|
|
645
|
+
if (files.length === 0) return [];
|
|
646
|
+
const urls = await this.request<BatchUploadUrls>(
|
|
647
|
+
"POST", `/v1/corpora/${id}/documents/upload-urls`,
|
|
648
|
+
{ json: { items: files.map((f) => ({ filename: f.filename, content_type: f.content_type })) } },
|
|
649
|
+
);
|
|
650
|
+
const items = urls.items; // same order as the request
|
|
651
|
+
await poolMap(files.length, opts?.concurrency ?? 6, async (i) => {
|
|
652
|
+
const f = files[i]!;
|
|
653
|
+
const it = items[i]!;
|
|
654
|
+
const put = await this._fetch(it.upload_url, {
|
|
655
|
+
method: "PUT",
|
|
656
|
+
body: toBlob(f.file, f.content_type),
|
|
657
|
+
headers: f.content_type ? { "content-type": f.content_type } : {},
|
|
658
|
+
});
|
|
659
|
+
if (!put.ok) throw new KnowledgeCoreError(put.status, safeJson(await put.text()), it.upload_url);
|
|
660
|
+
});
|
|
661
|
+
const res = await this.request<{ documents: Document[] }>(
|
|
662
|
+
"POST", `/v1/corpora/${id}/documents/batch`,
|
|
663
|
+
{ json: {
|
|
664
|
+
folder_id: opts?.folder_id, visibility: opts?.visibility, custom_metadata: opts?.custom_metadata,
|
|
665
|
+
items: items.map((it) => ({ document_id: it.document_id, filename: it.filename })),
|
|
666
|
+
} },
|
|
667
|
+
);
|
|
668
|
+
return res.documents;
|
|
669
|
+
},
|
|
638
670
|
};
|
|
639
671
|
|
|
640
672
|
// --- parsing (utility: file -> text, stores nothing) ---
|
|
@@ -677,6 +709,12 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
677
709
|
contentUrl: (id: UUID, disposition: "inline" | "attachment" = "inline") =>
|
|
678
710
|
this.request<ContentUrl>("GET", `/v1/documents/${id}/content-url`, { query: { disposition } }),
|
|
679
711
|
delete: (id: UUID) => this.request<void>("DELETE", `/v1/documents/${id}`),
|
|
712
|
+
/** Delete MANY documents (by id) from one corpus in ONE call (202) — collapses N deletes into
|
|
713
|
+
* one request. IDs not in the corpus are skipped. Completion observable via documents.events /
|
|
714
|
+
* documents.get() → 404. Returns { deleting: ids, count }. */
|
|
715
|
+
deleteMany: (corpusId: UUID, documentIds: UUID[]) =>
|
|
716
|
+
this.request<{ deleting: UUID[]; count: number }>(
|
|
717
|
+
"POST", `/v1/corpora/${corpusId}/documents/batch-delete`, { json: { document_ids: documentIds } }),
|
|
680
718
|
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). Snapshot on
|
|
681
719
|
* connect, then a delta per transition, then `complete` when nothing is in-flight. Abort via
|
|
682
720
|
* the signal. 501 (onError) if the environment has no doc-status bus — fall back to polling. */
|
|
@@ -772,6 +810,15 @@ export class AdminClient extends HttpBase {
|
|
|
772
810
|
// ---------------------------------------------------------------------------
|
|
773
811
|
// helpers
|
|
774
812
|
// ---------------------------------------------------------------------------
|
|
813
|
+
/** Run fn(0..n-1) with bounded concurrency (used by uploadMany for the direct-to-GCS PUTs). */
|
|
814
|
+
async function poolMap(n: number, concurrency: number, fn: (i: number) => Promise<void>): Promise<void> {
|
|
815
|
+
let next = 0;
|
|
816
|
+
const workers = Array.from({ length: Math.max(1, Math.min(concurrency, n)) }, async () => {
|
|
817
|
+
for (let i = next++; i < n; i = next++) await fn(i);
|
|
818
|
+
});
|
|
819
|
+
await Promise.all(workers);
|
|
820
|
+
}
|
|
821
|
+
|
|
775
822
|
function dispatchSse(frame: string, h: StreamHandlers): void {
|
|
776
823
|
let event = "message";
|
|
777
824
|
const dataLines: string[] = [];
|