@babav/knowledge-core-client 0.22.3 → 0.22.5
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 -1
- package/dist/index.js +66 -1
- package/package.json +1 -1
- package/src/index.ts +83 -1
package/dist/index.d.ts
CHANGED
|
@@ -359,12 +359,41 @@ export interface StreamHandlers {
|
|
|
359
359
|
/** Catch-all for any event (incl. unknown ones). */
|
|
360
360
|
onEvent?: (event: string, data: unknown) => void;
|
|
361
361
|
}
|
|
362
|
+
/** A live document status transition pushed over the ingestion-status SSE stream
|
|
363
|
+
* (documents.events / folders.events). Replaces polling GET /documents/count. */
|
|
364
|
+
export interface DocumentStatusEvent {
|
|
365
|
+
document_id: string;
|
|
366
|
+
corpus_id?: string | null;
|
|
367
|
+
folder_id?: string | null;
|
|
368
|
+
status: string;
|
|
369
|
+
filename?: string | null;
|
|
370
|
+
chunk_count?: number | null;
|
|
371
|
+
error?: string | null;
|
|
372
|
+
counts?: DocumentCount;
|
|
373
|
+
}
|
|
374
|
+
export interface DocumentEventsSnapshot {
|
|
375
|
+
documents: DocumentStatusEvent[];
|
|
376
|
+
counts: DocumentCount;
|
|
377
|
+
}
|
|
378
|
+
export interface DocumentEventHandlers {
|
|
379
|
+
/** Current state on connect (one query) — render the list from this. */
|
|
380
|
+
onSnapshot?: (s: DocumentEventsSnapshot) => void;
|
|
381
|
+
/** A single document's status changed (the moment it happened). Update that row + counts. */
|
|
382
|
+
onDocument?: (e: DocumentStatusEvent) => void;
|
|
383
|
+
/** Nothing is in-flight anymore — the stream is about to close. Authoritative final counts. */
|
|
384
|
+
onComplete?: (counts: DocumentCount) => void;
|
|
385
|
+
onError?: (err: unknown) => void;
|
|
386
|
+
}
|
|
362
387
|
export declare class KnowledgeCoreClient extends HttpBase {
|
|
363
388
|
/** @param opts.apiKey a TENANT key, supplied by the caller. */
|
|
364
389
|
constructor(opts: ClientOptions);
|
|
365
390
|
query(agentId: UUID, body: QueryRequest): Promise<QueryResponse>;
|
|
366
391
|
/** Streaming query (SSE). Resolves when the stream ends. */
|
|
367
392
|
queryStream(agentId: UUID, body: QueryRequest, handlers: StreamHandlers, signal?: AbortSignal): Promise<void>;
|
|
393
|
+
/** Shared SSE reader for the document-status streams (documents.events / folders.events).
|
|
394
|
+
* Resolves when the stream ends (server sends `complete` once nothing is in-flight, or the
|
|
395
|
+
* cap is hit). Abort via the signal to stop watching. Throws 501 if the env has no bus. */
|
|
396
|
+
_streamDocEvents(path: string, handlers: DocumentEventHandlers, signal?: AbortSignal): Promise<void>;
|
|
368
397
|
retrieve(body: {
|
|
369
398
|
query: string;
|
|
370
399
|
corpus_ids: UUID[];
|
|
@@ -454,7 +483,7 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
454
483
|
folders: {
|
|
455
484
|
create: (corpusId: UUID, name: string) => Promise<Folder>;
|
|
456
485
|
rename: (folderId: UUID, name: string) => Promise<Folder>;
|
|
457
|
-
delete: (folderId: UUID) => Promise<void>;
|
|
486
|
+
delete: (folderId: UUID, confirmName?: string) => Promise<void>;
|
|
458
487
|
listDocuments: (folderId: UUID, q?: {
|
|
459
488
|
limit?: number;
|
|
460
489
|
cursor?: string;
|
|
@@ -466,6 +495,8 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
466
495
|
limit?: number;
|
|
467
496
|
cursor?: string;
|
|
468
497
|
}) => Promise<Page<Document>>;
|
|
498
|
+
/** Live ingestion-status stream for ONE folder (same contract as documents.events). */
|
|
499
|
+
events: (folderId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) => Promise<void>;
|
|
469
500
|
};
|
|
470
501
|
documents: {
|
|
471
502
|
get: (id: UUID) => Promise<Document>;
|
|
@@ -478,6 +509,10 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
478
509
|
}) => Promise<Document>;
|
|
479
510
|
contentUrl: (id: UUID, disposition?: "inline" | "attachment") => Promise<ContentUrl>;
|
|
480
511
|
delete: (id: UUID) => Promise<void>;
|
|
512
|
+
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). Snapshot on
|
|
513
|
+
* connect, then a delta per transition, then `complete` when nothing is in-flight. Abort via
|
|
514
|
+
* the signal. 501 (onError) if the environment has no doc-status bus — fall back to polling. */
|
|
515
|
+
events: (corpusId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) => Promise<void>;
|
|
481
516
|
};
|
|
482
517
|
conversations: {
|
|
483
518
|
create: (b?: {
|
package/dist/index.js
CHANGED
|
@@ -176,6 +176,38 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
176
176
|
if (buf.trim())
|
|
177
177
|
dispatchSse(buf, handlers);
|
|
178
178
|
}
|
|
179
|
+
/** Shared SSE reader for the document-status streams (documents.events / folders.events).
|
|
180
|
+
* Resolves when the stream ends (server sends `complete` once nothing is in-flight, or the
|
|
181
|
+
* cap is hit). Abort via the signal to stop watching. Throws 501 if the env has no bus. */
|
|
182
|
+
async _streamDocEvents(path, handlers, signal) {
|
|
183
|
+
const res = await this.raw("GET", path, { signal });
|
|
184
|
+
if (!res.ok || !res.body) {
|
|
185
|
+
const t = await res.text();
|
|
186
|
+
const err = new KnowledgeCoreError(res.status, safeJson(t), path);
|
|
187
|
+
if (handlers.onError) {
|
|
188
|
+
handlers.onError(err);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
throw err;
|
|
192
|
+
}
|
|
193
|
+
const reader = res.body.getReader();
|
|
194
|
+
const decoder = new TextDecoder();
|
|
195
|
+
let buf = "";
|
|
196
|
+
for (;;) {
|
|
197
|
+
const { value, done } = await reader.read();
|
|
198
|
+
if (done)
|
|
199
|
+
break;
|
|
200
|
+
buf += decoder.decode(value, { stream: true });
|
|
201
|
+
let idx;
|
|
202
|
+
while ((idx = buf.indexOf("\n\n")) !== -1) {
|
|
203
|
+
const frame = buf.slice(0, idx);
|
|
204
|
+
buf = buf.slice(idx + 2);
|
|
205
|
+
dispatchDocEvent(frame, handlers);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (buf.trim())
|
|
209
|
+
dispatchDocEvent(buf, handlers);
|
|
210
|
+
}
|
|
179
211
|
// --- retrieve (cross-corpus primitive, no generation) ---
|
|
180
212
|
retrieve(body) {
|
|
181
213
|
return this.request("POST", "/v1/retrieve", { json: body });
|
|
@@ -246,12 +278,17 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
246
278
|
folders = {
|
|
247
279
|
create: (corpusId, name) => this.request("POST", `/v1/corpora/${corpusId}/folders`, { json: { name } }),
|
|
248
280
|
rename: (folderId, name) => this.request("PATCH", `/v1/folders/${folderId}`, { json: { name } }),
|
|
249
|
-
delete
|
|
281
|
+
// Plain delete requires the folder to be EMPTY (else 409). Pass `confirmName` (the folder's
|
|
282
|
+
// name) to CASCADE — delete the folder AND every document in it (async, 202). The default
|
|
283
|
+
// folder cannot be deleted either way.
|
|
284
|
+
delete: (folderId, confirmName) => this.request("DELETE", `/v1/folders/${folderId}`, confirmName ? { query: { confirm: confirmName } } : undefined),
|
|
250
285
|
listDocuments: (folderId, q) => this.request("GET", `/v1/folders/${folderId}/documents`, { query: q }),
|
|
251
286
|
/** Count documents in the folder: { total, indexed, in_flight, failed }. */
|
|
252
287
|
countDocuments: (folderId) => this.request("GET", `/v1/folders/${folderId}/documents/count`),
|
|
253
288
|
/** Documents in the folder whose filename contains `q` (case-insensitive), paginated. */
|
|
254
289
|
searchDocuments: (folderId, q, opts) => this.request("GET", `/v1/folders/${folderId}/documents/search`, { query: { q, ...opts } }),
|
|
290
|
+
/** Live ingestion-status stream for ONE folder (same contract as documents.events). */
|
|
291
|
+
events: (folderId, handlers, signal) => this._streamDocEvents(`/v1/folders/${folderId}/documents/events`, handlers, signal),
|
|
255
292
|
};
|
|
256
293
|
// --- documents ---
|
|
257
294
|
documents = {
|
|
@@ -262,6 +299,10 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
262
299
|
update: (id, b) => this.request("PATCH", `/v1/documents/${id}`, { json: b }),
|
|
263
300
|
contentUrl: (id, disposition = "inline") => this.request("GET", `/v1/documents/${id}/content-url`, { query: { disposition } }),
|
|
264
301
|
delete: (id) => this.request("DELETE", `/v1/documents/${id}`),
|
|
302
|
+
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). Snapshot on
|
|
303
|
+
* connect, then a delta per transition, then `complete` when nothing is in-flight. Abort via
|
|
304
|
+
* the signal. 501 (onError) if the environment has no doc-status bus — fall back to polling. */
|
|
305
|
+
events: (corpusId, handlers, signal) => this._streamDocEvents(`/v1/corpora/${corpusId}/documents/events`, handlers, signal),
|
|
265
306
|
};
|
|
266
307
|
// --- conversations ---
|
|
267
308
|
conversations = {
|
|
@@ -381,3 +422,27 @@ function dispatchSse(frame, h) {
|
|
|
381
422
|
break;
|
|
382
423
|
}
|
|
383
424
|
}
|
|
425
|
+
function dispatchDocEvent(frame, h) {
|
|
426
|
+
let event = "message";
|
|
427
|
+
const dataLines = [];
|
|
428
|
+
for (const line of frame.split("\n")) {
|
|
429
|
+
if (line.startsWith("event:"))
|
|
430
|
+
event = line.slice(6).trim();
|
|
431
|
+
else if (line.startsWith("data:"))
|
|
432
|
+
dataLines.push(line.slice(5).trim());
|
|
433
|
+
}
|
|
434
|
+
if (dataLines.length === 0)
|
|
435
|
+
return; // heartbeat / comment frame
|
|
436
|
+
const data = safeJson(dataLines.join("\n"));
|
|
437
|
+
switch (event) {
|
|
438
|
+
case "snapshot":
|
|
439
|
+
h.onSnapshot?.(data);
|
|
440
|
+
break;
|
|
441
|
+
case "document":
|
|
442
|
+
h.onDocument?.(data);
|
|
443
|
+
break;
|
|
444
|
+
case "complete":
|
|
445
|
+
h.onComplete?.(data);
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babav/knowledge-core-client",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.5",
|
|
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
|
@@ -480,6 +480,32 @@ export interface StreamHandlers {
|
|
|
480
480
|
onEvent?: (event: string, data: unknown) => void;
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
+
/** A live document status transition pushed over the ingestion-status SSE stream
|
|
484
|
+
* (documents.events / folders.events). Replaces polling GET /documents/count. */
|
|
485
|
+
export interface DocumentStatusEvent {
|
|
486
|
+
document_id: string;
|
|
487
|
+
corpus_id?: string | null;
|
|
488
|
+
folder_id?: string | null;
|
|
489
|
+
status: string; // pending | ingesting | indexed | failed | deleting
|
|
490
|
+
filename?: string | null;
|
|
491
|
+
chunk_count?: number | null;
|
|
492
|
+
error?: string | null;
|
|
493
|
+
counts?: DocumentCount; // running aggregate, included on each `document` delta
|
|
494
|
+
}
|
|
495
|
+
export interface DocumentEventsSnapshot {
|
|
496
|
+
documents: DocumentStatusEvent[]; // current state of every doc in the corpus/folder
|
|
497
|
+
counts: DocumentCount;
|
|
498
|
+
}
|
|
499
|
+
export interface DocumentEventHandlers {
|
|
500
|
+
/** Current state on connect (one query) — render the list from this. */
|
|
501
|
+
onSnapshot?: (s: DocumentEventsSnapshot) => void;
|
|
502
|
+
/** A single document's status changed (the moment it happened). Update that row + counts. */
|
|
503
|
+
onDocument?: (e: DocumentStatusEvent) => void;
|
|
504
|
+
/** Nothing is in-flight anymore — the stream is about to close. Authoritative final counts. */
|
|
505
|
+
onComplete?: (counts: DocumentCount) => void;
|
|
506
|
+
onError?: (err: unknown) => void;
|
|
507
|
+
}
|
|
508
|
+
|
|
483
509
|
// ---------------------------------------------------------------------------
|
|
484
510
|
// Tenant client (data ops) — use a TENANT key
|
|
485
511
|
// ---------------------------------------------------------------------------
|
|
@@ -518,6 +544,34 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
518
544
|
if (buf.trim()) dispatchSse(buf, handlers);
|
|
519
545
|
}
|
|
520
546
|
|
|
547
|
+
/** Shared SSE reader for the document-status streams (documents.events / folders.events).
|
|
548
|
+
* Resolves when the stream ends (server sends `complete` once nothing is in-flight, or the
|
|
549
|
+
* cap is hit). Abort via the signal to stop watching. Throws 501 if the env has no bus. */
|
|
550
|
+
async _streamDocEvents(path: string, handlers: DocumentEventHandlers, signal?: AbortSignal): Promise<void> {
|
|
551
|
+
const res = await this.raw("GET", path, { signal });
|
|
552
|
+
if (!res.ok || !res.body) {
|
|
553
|
+
const t = await res.text();
|
|
554
|
+
const err = new KnowledgeCoreError(res.status, safeJson(t), path);
|
|
555
|
+
if (handlers.onError) { handlers.onError(err); return; }
|
|
556
|
+
throw err;
|
|
557
|
+
}
|
|
558
|
+
const reader = res.body.getReader();
|
|
559
|
+
const decoder = new TextDecoder();
|
|
560
|
+
let buf = "";
|
|
561
|
+
for (;;) {
|
|
562
|
+
const { value, done } = await reader.read();
|
|
563
|
+
if (done) break;
|
|
564
|
+
buf += decoder.decode(value, { stream: true });
|
|
565
|
+
let idx: number;
|
|
566
|
+
while ((idx = buf.indexOf("\n\n")) !== -1) {
|
|
567
|
+
const frame = buf.slice(0, idx);
|
|
568
|
+
buf = buf.slice(idx + 2);
|
|
569
|
+
dispatchDocEvent(frame, handlers);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (buf.trim()) dispatchDocEvent(buf, handlers);
|
|
573
|
+
}
|
|
574
|
+
|
|
521
575
|
// --- retrieve (cross-corpus primitive, no generation) ---
|
|
522
576
|
retrieve(body: { query: string; corpus_ids: UUID[]; top_k_retrieved_chunks?: number; top_k_reranked_chunks?: number; rerank?: boolean; instruction?: string; filter?: MetadataFilter; }): Promise<{ retrieval_contents: RetrievalContent[] }> {
|
|
523
577
|
return this.request("POST", "/v1/retrieve", { json: body });
|
|
@@ -594,7 +648,11 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
594
648
|
folders = {
|
|
595
649
|
create: (corpusId: UUID, name: string) => this.request<Folder>("POST", `/v1/corpora/${corpusId}/folders`, { json: { name } }),
|
|
596
650
|
rename: (folderId: UUID, name: string) => this.request<Folder>("PATCH", `/v1/folders/${folderId}`, { json: { name } }),
|
|
597
|
-
delete
|
|
651
|
+
// Plain delete requires the folder to be EMPTY (else 409). Pass `confirmName` (the folder's
|
|
652
|
+
// name) to CASCADE — delete the folder AND every document in it (async, 202). The default
|
|
653
|
+
// folder cannot be deleted either way.
|
|
654
|
+
delete: (folderId: UUID, confirmName?: string) =>
|
|
655
|
+
this.request<void>("DELETE", `/v1/folders/${folderId}`, confirmName ? { query: { confirm: confirmName } } : undefined),
|
|
598
656
|
listDocuments: (folderId: UUID, q?: { limit?: number; cursor?: string }) =>
|
|
599
657
|
this.request<Page<Document>>("GET", `/v1/folders/${folderId}/documents`, { query: q }),
|
|
600
658
|
/** Count documents in the folder: { total, indexed, in_flight, failed }. */
|
|
@@ -603,6 +661,9 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
603
661
|
/** Documents in the folder whose filename contains `q` (case-insensitive), paginated. */
|
|
604
662
|
searchDocuments: (folderId: UUID, q: string, opts?: { limit?: number; cursor?: string }) =>
|
|
605
663
|
this.request<Page<Document>>("GET", `/v1/folders/${folderId}/documents/search`, { query: { q, ...opts } }),
|
|
664
|
+
/** Live ingestion-status stream for ONE folder (same contract as documents.events). */
|
|
665
|
+
events: (folderId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) =>
|
|
666
|
+
this._streamDocEvents(`/v1/folders/${folderId}/documents/events`, handlers, signal),
|
|
606
667
|
};
|
|
607
668
|
|
|
608
669
|
// --- documents ---
|
|
@@ -616,6 +677,11 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
616
677
|
contentUrl: (id: UUID, disposition: "inline" | "attachment" = "inline") =>
|
|
617
678
|
this.request<ContentUrl>("GET", `/v1/documents/${id}/content-url`, { query: { disposition } }),
|
|
618
679
|
delete: (id: UUID) => this.request<void>("DELETE", `/v1/documents/${id}`),
|
|
680
|
+
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). Snapshot on
|
|
681
|
+
* connect, then a delta per transition, then `complete` when nothing is in-flight. Abort via
|
|
682
|
+
* the signal. 501 (onError) if the environment has no doc-status bus — fall back to polling. */
|
|
683
|
+
events: (corpusId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) =>
|
|
684
|
+
this._streamDocEvents(`/v1/corpora/${corpusId}/documents/events`, handlers, signal),
|
|
619
685
|
};
|
|
620
686
|
|
|
621
687
|
// --- conversations ---
|
|
@@ -729,3 +795,19 @@ function dispatchSse(frame: string, h: StreamHandlers): void {
|
|
|
729
795
|
case "done": h.onDone?.(); break;
|
|
730
796
|
}
|
|
731
797
|
}
|
|
798
|
+
|
|
799
|
+
function dispatchDocEvent(frame: string, h: DocumentEventHandlers): void {
|
|
800
|
+
let event = "message";
|
|
801
|
+
const dataLines: string[] = [];
|
|
802
|
+
for (const line of frame.split("\n")) {
|
|
803
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
804
|
+
else if (line.startsWith("data:")) dataLines.push(line.slice(5).trim());
|
|
805
|
+
}
|
|
806
|
+
if (dataLines.length === 0) return; // heartbeat / comment frame
|
|
807
|
+
const data = safeJson(dataLines.join("\n"));
|
|
808
|
+
switch (event) {
|
|
809
|
+
case "snapshot": h.onSnapshot?.(data as DocumentEventsSnapshot); break;
|
|
810
|
+
case "document": h.onDocument?.(data as DocumentStatusEvent); break;
|
|
811
|
+
case "complete": h.onComplete?.(data as DocumentCount); break;
|
|
812
|
+
}
|
|
813
|
+
}
|