@babav/knowledge-core-client 0.23.2 → 0.24.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/dist/index.d.ts +79 -5
- package/dist/index.js +51 -5
- package/package.json +1 -1
- package/src/index.ts +94 -5
package/dist/index.d.ts
CHANGED
|
@@ -224,6 +224,24 @@ export interface BatchUploadUrls {
|
|
|
224
224
|
}>;
|
|
225
225
|
expires_in: number;
|
|
226
226
|
}
|
|
227
|
+
/** One RESUMABLE GCS upload session (corpora.createUploadSessions). Hand `upload_url` to the
|
|
228
|
+
* browser: it PUTs bytes straight to GCS there, needing NO KC/tenant credential (the URI is the
|
|
229
|
+
* per-object write capability). Resumable → a dropped connection resumes, not restarts. */
|
|
230
|
+
export interface UploadSession {
|
|
231
|
+
filename: string;
|
|
232
|
+
document_id: UUID;
|
|
233
|
+
upload_url: string;
|
|
234
|
+
gcs_uri: string;
|
|
235
|
+
method: "PUT";
|
|
236
|
+
headers: Record<string, string>;
|
|
237
|
+
expires_at: string;
|
|
238
|
+
}
|
|
239
|
+
/** Per-item result of corpora.finalizeUploads (idempotent registration). */
|
|
240
|
+
export interface FinalizeResult {
|
|
241
|
+
document_id: UUID;
|
|
242
|
+
status: "ingesting" | "failed";
|
|
243
|
+
error?: string;
|
|
244
|
+
}
|
|
227
245
|
export interface Conversation {
|
|
228
246
|
id: UUID;
|
|
229
247
|
title: string | null;
|
|
@@ -398,7 +416,9 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
398
416
|
/** @param opts.apiKey a TENANT key, supplied by the caller. */
|
|
399
417
|
constructor(opts: ClientOptions);
|
|
400
418
|
query(agentId: UUID, body: QueryRequest): Promise<QueryResponse>;
|
|
401
|
-
/** Streaming query (SSE). Resolves when the stream ends
|
|
419
|
+
/** Streaming query (SSE). Resolves when the stream ends (the Promise resolving is normal, not an
|
|
420
|
+
* error). Pass `signal` and abort() on unmount / when the user cancels or navigates away so the
|
|
421
|
+
* connection doesn't linger. Transient (ends on `done`) — no reopen logic needed. */
|
|
402
422
|
queryStream(agentId: UUID, body: QueryRequest, handlers: StreamHandlers, signal?: AbortSignal): Promise<void>;
|
|
403
423
|
/** Shared SSE reader for the document-status streams (documents.events / folders.events).
|
|
404
424
|
* Resolves when the stream ends (server sends `complete` once nothing is in-flight, or the
|
|
@@ -496,6 +516,43 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
496
516
|
custom_metadata?: Record<string, unknown>;
|
|
497
517
|
concurrency?: number;
|
|
498
518
|
}) => Promise<Document[]>;
|
|
519
|
+
/** (server-side) Mint one RESUMABLE GCS upload session per file. Return only the sessions to the
|
|
520
|
+
* browser; the browser PUTs bytes to each `upload_url` and needs NO KC/tenant credential (the
|
|
521
|
+
* session URI IS the per-object write capability). `origin` = the browser app origin (scopes the
|
|
522
|
+
* session; the bucket CORS must allow PUT/POST from it). */
|
|
523
|
+
createUploadSessions: (id: UUID, files: Array<{
|
|
524
|
+
filename: string;
|
|
525
|
+
content_type?: string;
|
|
526
|
+
size?: number;
|
|
527
|
+
}>, opts?: {
|
|
528
|
+
origin?: string;
|
|
529
|
+
}) => Promise<{
|
|
530
|
+
items: UploadSession[];
|
|
531
|
+
}>;
|
|
532
|
+
/** (server-side) Register documents whose bytes the browser already PUT to GCS. IDEMPOTENT +
|
|
533
|
+
* PER-ITEM — safe to retry; each result is { document_id, status: 'ingesting' | 'failed' }. Call
|
|
534
|
+
* after the browser reports its PUTs done. folder_id/visibility/custom_metadata apply to all
|
|
535
|
+
* items. `document_id` is stable from createUploadSessions, so documents.events (SSE) tracks it. */
|
|
536
|
+
finalizeUploads: (id: UUID, items: Array<{
|
|
537
|
+
document_id: UUID;
|
|
538
|
+
filename: string;
|
|
539
|
+
content_type?: string;
|
|
540
|
+
}>, opts?: {
|
|
541
|
+
folder_id?: UUID;
|
|
542
|
+
visibility?: Visibility;
|
|
543
|
+
custom_metadata?: Record<string, unknown>;
|
|
544
|
+
}) => Promise<{
|
|
545
|
+
results: FinalizeResult[];
|
|
546
|
+
}>;
|
|
547
|
+
/** (server-side) Cancel upload sessions the browser gave up on (idempotent cleanup). Pass the
|
|
548
|
+
* `upload_url` from createUploadSessions so the resumable session is dropped; any pending row is
|
|
549
|
+
* removed too. Safe to call repeatedly. */
|
|
550
|
+
abortUploadSessions: (id: UUID, items: Array<{
|
|
551
|
+
document_id: UUID;
|
|
552
|
+
upload_url?: string;
|
|
553
|
+
}>) => Promise<{
|
|
554
|
+
aborted: UUID[];
|
|
555
|
+
}>;
|
|
499
556
|
};
|
|
500
557
|
parse(a: {
|
|
501
558
|
file: FileData;
|
|
@@ -519,7 +576,8 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
519
576
|
limit?: number;
|
|
520
577
|
cursor?: string;
|
|
521
578
|
}) => Promise<Page<Document>>;
|
|
522
|
-
/** Live ingestion-status stream for ONE folder
|
|
579
|
+
/** Live ingestion-status stream for ONE folder. Same contract AND lifecycle rules as
|
|
580
|
+
* `documents.events` (abort on unmount, one per view, open lazily, don't reopen on complete). */
|
|
523
581
|
events: (folderId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) => Promise<void>;
|
|
524
582
|
};
|
|
525
583
|
documents: {
|
|
@@ -540,9 +598,25 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
540
598
|
deleting: UUID[];
|
|
541
599
|
count: number;
|
|
542
600
|
}>;
|
|
543
|
-
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count).
|
|
544
|
-
*
|
|
545
|
-
*
|
|
601
|
+
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). On connect:
|
|
602
|
+
* `onSnapshot` (current state), then `onDocument` deltas as docs transition (pending → ingesting
|
|
603
|
+
* → indexed | failed, and deleting → deleted), then `onComplete` when nothing is in-flight.
|
|
604
|
+
* 501 (onError) if the env has no doc-status bus — fall back to polling countDocuments().
|
|
605
|
+
*
|
|
606
|
+
* A watcher is a held connection — manage its lifecycle:
|
|
607
|
+
* 1. ALWAYS abort on unmount/navigation via `signal` (else it stays open until the ~30-min cap):
|
|
608
|
+
* const c = new AbortController();
|
|
609
|
+
* kc.documents.events(id, handlers, c.signal).catch(()=>{});
|
|
610
|
+
* return () => c.abort(); // e.g. React useEffect cleanup
|
|
611
|
+
* 2. ONE watcher per view — never one per document/row.
|
|
612
|
+
* 3. Open lazily — only while the view has in-flight docs, or right after an upload. If nothing
|
|
613
|
+
* is in-flight the server sends `complete` and closes immediately (nothing to hold).
|
|
614
|
+
* 4. Do NOT reopen on `onComplete` — that's a permanent connection. Reopen only on NEW work
|
|
615
|
+
* (another upload) or an unexpected disconnect while work remains.
|
|
616
|
+
* 5. De-dupe — keep the AbortController in a ref and abort the previous before opening a new one
|
|
617
|
+
* (guards against re-renders / StrictMode double-mount).
|
|
618
|
+
* 6. On a network drop, reconnect with backoff and rebuild from the fresh `snapshot` (authoritative).
|
|
619
|
+
* 7. The Promise RESOLVING is normal (stream ended: complete or the cap) — only onError is a failure. */
|
|
546
620
|
events: (corpusId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) => Promise<void>;
|
|
547
621
|
};
|
|
548
622
|
conversations: {
|
package/dist/index.js
CHANGED
|
@@ -151,7 +151,9 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
151
151
|
query(agentId, body) {
|
|
152
152
|
return this.request("POST", `/v1/agents/${agentId}/query`, { json: body });
|
|
153
153
|
}
|
|
154
|
-
/** Streaming query (SSE). Resolves when the stream ends
|
|
154
|
+
/** Streaming query (SSE). Resolves when the stream ends (the Promise resolving is normal, not an
|
|
155
|
+
* error). Pass `signal` and abort() on unmount / when the user cancels or navigates away so the
|
|
156
|
+
* connection doesn't linger. Transient (ends on `done`) — no reopen logic needed. */
|
|
155
157
|
async queryStream(agentId, body, handlers, signal) {
|
|
156
158
|
const res = await this.raw("POST", `/v1/agents/${agentId}/query/stream`, { json: body, signal });
|
|
157
159
|
if (!res.ok || !res.body) {
|
|
@@ -281,6 +283,33 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
281
283
|
} });
|
|
282
284
|
return res.documents;
|
|
283
285
|
},
|
|
286
|
+
// --- browser direct-to-GCS uploads (ANY size; the browser NEVER needs an API key) ---
|
|
287
|
+
// Split flow so the browser uploads bytes ITSELF while the tenant key stays server-side:
|
|
288
|
+
// 1. (server) createUploadSessions -> returns a resumable GCS session URI per file
|
|
289
|
+
// 2. (browser) PUT the bytes straight to each session URI (resumable; no credential)
|
|
290
|
+
// 3. (server) finalizeUploads -> register + enqueue; or abortUploadSessions to cancel
|
|
291
|
+
// Use this instead of uploadMany when bytes must not pass through your worker (browser -> GCS
|
|
292
|
+
// direct), e.g. multi-GB files or a browser that can't hold the tenant key.
|
|
293
|
+
/** (server-side) Mint one RESUMABLE GCS upload session per file. Return only the sessions to the
|
|
294
|
+
* browser; the browser PUTs bytes to each `upload_url` and needs NO KC/tenant credential (the
|
|
295
|
+
* session URI IS the per-object write capability). `origin` = the browser app origin (scopes the
|
|
296
|
+
* session; the bucket CORS must allow PUT/POST from it). */
|
|
297
|
+
createUploadSessions: (id, files, opts) => this.request("POST", `/v1/corpora/${id}/documents/upload-sessions`, { json: {
|
|
298
|
+
items: files.map((f) => ({ filename: f.filename, content_type: f.content_type })),
|
|
299
|
+
origin: opts?.origin,
|
|
300
|
+
} }),
|
|
301
|
+
/** (server-side) Register documents whose bytes the browser already PUT to GCS. IDEMPOTENT +
|
|
302
|
+
* PER-ITEM — safe to retry; each result is { document_id, status: 'ingesting' | 'failed' }. Call
|
|
303
|
+
* after the browser reports its PUTs done. folder_id/visibility/custom_metadata apply to all
|
|
304
|
+
* items. `document_id` is stable from createUploadSessions, so documents.events (SSE) tracks it. */
|
|
305
|
+
finalizeUploads: (id, items, opts) => this.request("POST", `/v1/corpora/${id}/documents/finalize`, { json: {
|
|
306
|
+
items, folder_id: opts?.folder_id, visibility: opts?.visibility,
|
|
307
|
+
custom_metadata: opts?.custom_metadata,
|
|
308
|
+
} }),
|
|
309
|
+
/** (server-side) Cancel upload sessions the browser gave up on (idempotent cleanup). Pass the
|
|
310
|
+
* `upload_url` from createUploadSessions so the resumable session is dropped; any pending row is
|
|
311
|
+
* removed too. Safe to call repeatedly. */
|
|
312
|
+
abortUploadSessions: (id, items) => this.request("POST", `/v1/corpora/${id}/documents/abort-uploads`, { json: { items } }),
|
|
284
313
|
};
|
|
285
314
|
// --- parsing (utility: file -> text, stores nothing) ---
|
|
286
315
|
parse(a) {
|
|
@@ -301,7 +330,8 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
301
330
|
countDocuments: (folderId) => this.request("GET", `/v1/folders/${folderId}/documents/count`),
|
|
302
331
|
/** Documents in the folder whose filename contains `q` (case-insensitive), paginated. */
|
|
303
332
|
searchDocuments: (folderId, q, opts) => this.request("GET", `/v1/folders/${folderId}/documents/search`, { query: { q, ...opts } }),
|
|
304
|
-
/** Live ingestion-status stream for ONE folder
|
|
333
|
+
/** Live ingestion-status stream for ONE folder. Same contract AND lifecycle rules as
|
|
334
|
+
* `documents.events` (abort on unmount, one per view, open lazily, don't reopen on complete). */
|
|
305
335
|
events: (folderId, handlers, signal) => this._streamDocEvents(`/v1/folders/${folderId}/documents/events`, handlers, signal),
|
|
306
336
|
};
|
|
307
337
|
// --- documents ---
|
|
@@ -317,9 +347,25 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
317
347
|
* one request. IDs not in the corpus are skipped. Completion observable via documents.events /
|
|
318
348
|
* documents.get() → 404. Returns { deleting: ids, count }. */
|
|
319
349
|
deleteMany: (corpusId, documentIds) => this.request("POST", `/v1/corpora/${corpusId}/documents/batch-delete`, { json: { document_ids: documentIds } }),
|
|
320
|
-
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count).
|
|
321
|
-
*
|
|
322
|
-
*
|
|
350
|
+
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). On connect:
|
|
351
|
+
* `onSnapshot` (current state), then `onDocument` deltas as docs transition (pending → ingesting
|
|
352
|
+
* → indexed | failed, and deleting → deleted), then `onComplete` when nothing is in-flight.
|
|
353
|
+
* 501 (onError) if the env has no doc-status bus — fall back to polling countDocuments().
|
|
354
|
+
*
|
|
355
|
+
* A watcher is a held connection — manage its lifecycle:
|
|
356
|
+
* 1. ALWAYS abort on unmount/navigation via `signal` (else it stays open until the ~30-min cap):
|
|
357
|
+
* const c = new AbortController();
|
|
358
|
+
* kc.documents.events(id, handlers, c.signal).catch(()=>{});
|
|
359
|
+
* return () => c.abort(); // e.g. React useEffect cleanup
|
|
360
|
+
* 2. ONE watcher per view — never one per document/row.
|
|
361
|
+
* 3. Open lazily — only while the view has in-flight docs, or right after an upload. If nothing
|
|
362
|
+
* is in-flight the server sends `complete` and closes immediately (nothing to hold).
|
|
363
|
+
* 4. Do NOT reopen on `onComplete` — that's a permanent connection. Reopen only on NEW work
|
|
364
|
+
* (another upload) or an unexpected disconnect while work remains.
|
|
365
|
+
* 5. De-dupe — keep the AbortController in a ref and abort the previous before opening a new one
|
|
366
|
+
* (guards against re-renders / StrictMode double-mount).
|
|
367
|
+
* 6. On a network drop, reconnect with backoff and rebuild from the fresh `snapshot` (authoritative).
|
|
368
|
+
* 7. The Promise RESOLVING is normal (stream ended: complete or the cap) — only onError is a failure. */
|
|
323
369
|
events: (corpusId, handlers, signal) => this._streamDocEvents(`/v1/corpora/${corpusId}/documents/events`, handlers, signal),
|
|
324
370
|
};
|
|
325
371
|
// --- conversations ---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babav/knowledge-core-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
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
|
@@ -240,6 +240,24 @@ export interface BatchUploadUrls {
|
|
|
240
240
|
items: Array<{ filename: string; document_id: UUID; upload_url: string; gcs_uri: string }>;
|
|
241
241
|
expires_in: number;
|
|
242
242
|
}
|
|
243
|
+
/** One RESUMABLE GCS upload session (corpora.createUploadSessions). Hand `upload_url` to the
|
|
244
|
+
* browser: it PUTs bytes straight to GCS there, needing NO KC/tenant credential (the URI is the
|
|
245
|
+
* per-object write capability). Resumable → a dropped connection resumes, not restarts. */
|
|
246
|
+
export interface UploadSession {
|
|
247
|
+
filename: string;
|
|
248
|
+
document_id: UUID;
|
|
249
|
+
upload_url: string;
|
|
250
|
+
gcs_uri: string;
|
|
251
|
+
method: "PUT";
|
|
252
|
+
headers: Record<string, string>;
|
|
253
|
+
expires_at: string;
|
|
254
|
+
}
|
|
255
|
+
/** Per-item result of corpora.finalizeUploads (idempotent registration). */
|
|
256
|
+
export interface FinalizeResult {
|
|
257
|
+
document_id: UUID;
|
|
258
|
+
status: "ingesting" | "failed";
|
|
259
|
+
error?: string;
|
|
260
|
+
}
|
|
243
261
|
export interface Conversation {
|
|
244
262
|
id: UUID;
|
|
245
263
|
title: string | null;
|
|
@@ -525,7 +543,9 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
525
543
|
return this.request("POST", `/v1/agents/${agentId}/query`, { json: body });
|
|
526
544
|
}
|
|
527
545
|
|
|
528
|
-
/** Streaming query (SSE). Resolves when the stream ends
|
|
546
|
+
/** Streaming query (SSE). Resolves when the stream ends (the Promise resolving is normal, not an
|
|
547
|
+
* error). Pass `signal` and abort() on unmount / when the user cancels or navigates away so the
|
|
548
|
+
* connection doesn't linger. Transient (ends on `done`) — no reopen logic needed. */
|
|
529
549
|
async queryStream(agentId: UUID, body: QueryRequest, handlers: StreamHandlers, signal?: AbortSignal): Promise<void> {
|
|
530
550
|
const res = await this.raw("POST", `/v1/agents/${agentId}/query/stream`, { json: body, signal });
|
|
531
551
|
if (!res.ok || !res.body) {
|
|
@@ -667,6 +687,58 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
667
687
|
);
|
|
668
688
|
return res.documents;
|
|
669
689
|
},
|
|
690
|
+
|
|
691
|
+
// --- browser direct-to-GCS uploads (ANY size; the browser NEVER needs an API key) ---
|
|
692
|
+
// Split flow so the browser uploads bytes ITSELF while the tenant key stays server-side:
|
|
693
|
+
// 1. (server) createUploadSessions -> returns a resumable GCS session URI per file
|
|
694
|
+
// 2. (browser) PUT the bytes straight to each session URI (resumable; no credential)
|
|
695
|
+
// 3. (server) finalizeUploads -> register + enqueue; or abortUploadSessions to cancel
|
|
696
|
+
// Use this instead of uploadMany when bytes must not pass through your worker (browser -> GCS
|
|
697
|
+
// direct), e.g. multi-GB files or a browser that can't hold the tenant key.
|
|
698
|
+
|
|
699
|
+
/** (server-side) Mint one RESUMABLE GCS upload session per file. Return only the sessions to the
|
|
700
|
+
* browser; the browser PUTs bytes to each `upload_url` and needs NO KC/tenant credential (the
|
|
701
|
+
* session URI IS the per-object write capability). `origin` = the browser app origin (scopes the
|
|
702
|
+
* session; the bucket CORS must allow PUT/POST from it). */
|
|
703
|
+
createUploadSessions: (
|
|
704
|
+
id: UUID,
|
|
705
|
+
files: Array<{ filename: string; content_type?: string; size?: number }>,
|
|
706
|
+
opts?: { origin?: string },
|
|
707
|
+
): Promise<{ items: UploadSession[] }> =>
|
|
708
|
+
this.request<{ items: UploadSession[] }>(
|
|
709
|
+
"POST", `/v1/corpora/${id}/documents/upload-sessions`,
|
|
710
|
+
{ json: {
|
|
711
|
+
items: files.map((f) => ({ filename: f.filename, content_type: f.content_type })),
|
|
712
|
+
origin: opts?.origin,
|
|
713
|
+
} },
|
|
714
|
+
),
|
|
715
|
+
|
|
716
|
+
/** (server-side) Register documents whose bytes the browser already PUT to GCS. IDEMPOTENT +
|
|
717
|
+
* PER-ITEM — safe to retry; each result is { document_id, status: 'ingesting' | 'failed' }. Call
|
|
718
|
+
* after the browser reports its PUTs done. folder_id/visibility/custom_metadata apply to all
|
|
719
|
+
* items. `document_id` is stable from createUploadSessions, so documents.events (SSE) tracks it. */
|
|
720
|
+
finalizeUploads: (
|
|
721
|
+
id: UUID,
|
|
722
|
+
items: Array<{ document_id: UUID; filename: string; content_type?: string }>,
|
|
723
|
+
opts?: { folder_id?: UUID; visibility?: Visibility; custom_metadata?: Record<string, unknown> },
|
|
724
|
+
): Promise<{ results: FinalizeResult[] }> =>
|
|
725
|
+
this.request<{ results: FinalizeResult[] }>(
|
|
726
|
+
"POST", `/v1/corpora/${id}/documents/finalize`,
|
|
727
|
+
{ json: {
|
|
728
|
+
items, folder_id: opts?.folder_id, visibility: opts?.visibility,
|
|
729
|
+
custom_metadata: opts?.custom_metadata,
|
|
730
|
+
} },
|
|
731
|
+
),
|
|
732
|
+
|
|
733
|
+
/** (server-side) Cancel upload sessions the browser gave up on (idempotent cleanup). Pass the
|
|
734
|
+
* `upload_url` from createUploadSessions so the resumable session is dropped; any pending row is
|
|
735
|
+
* removed too. Safe to call repeatedly. */
|
|
736
|
+
abortUploadSessions: (
|
|
737
|
+
id: UUID,
|
|
738
|
+
items: Array<{ document_id: UUID; upload_url?: string }>,
|
|
739
|
+
): Promise<{ aborted: UUID[] }> =>
|
|
740
|
+
this.request<{ aborted: UUID[] }>(
|
|
741
|
+
"POST", `/v1/corpora/${id}/documents/abort-uploads`, { json: { items } }),
|
|
670
742
|
};
|
|
671
743
|
|
|
672
744
|
// --- parsing (utility: file -> text, stores nothing) ---
|
|
@@ -693,7 +765,8 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
693
765
|
/** Documents in the folder whose filename contains `q` (case-insensitive), paginated. */
|
|
694
766
|
searchDocuments: (folderId: UUID, q: string, opts?: { limit?: number; cursor?: string }) =>
|
|
695
767
|
this.request<Page<Document>>("GET", `/v1/folders/${folderId}/documents/search`, { query: { q, ...opts } }),
|
|
696
|
-
/** Live ingestion-status stream for ONE folder
|
|
768
|
+
/** Live ingestion-status stream for ONE folder. Same contract AND lifecycle rules as
|
|
769
|
+
* `documents.events` (abort on unmount, one per view, open lazily, don't reopen on complete). */
|
|
697
770
|
events: (folderId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) =>
|
|
698
771
|
this._streamDocEvents(`/v1/folders/${folderId}/documents/events`, handlers, signal),
|
|
699
772
|
};
|
|
@@ -715,9 +788,25 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
715
788
|
deleteMany: (corpusId: UUID, documentIds: UUID[]) =>
|
|
716
789
|
this.request<{ deleting: UUID[]; count: number }>(
|
|
717
790
|
"POST", `/v1/corpora/${corpusId}/documents/batch-delete`, { json: { document_ids: documentIds } }),
|
|
718
|
-
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count).
|
|
719
|
-
*
|
|
720
|
-
*
|
|
791
|
+
/** Live ingestion-status stream for a corpus (SSE PUSH — replaces polling count). On connect:
|
|
792
|
+
* `onSnapshot` (current state), then `onDocument` deltas as docs transition (pending → ingesting
|
|
793
|
+
* → indexed | failed, and deleting → deleted), then `onComplete` when nothing is in-flight.
|
|
794
|
+
* 501 (onError) if the env has no doc-status bus — fall back to polling countDocuments().
|
|
795
|
+
*
|
|
796
|
+
* A watcher is a held connection — manage its lifecycle:
|
|
797
|
+
* 1. ALWAYS abort on unmount/navigation via `signal` (else it stays open until the ~30-min cap):
|
|
798
|
+
* const c = new AbortController();
|
|
799
|
+
* kc.documents.events(id, handlers, c.signal).catch(()=>{});
|
|
800
|
+
* return () => c.abort(); // e.g. React useEffect cleanup
|
|
801
|
+
* 2. ONE watcher per view — never one per document/row.
|
|
802
|
+
* 3. Open lazily — only while the view has in-flight docs, or right after an upload. If nothing
|
|
803
|
+
* is in-flight the server sends `complete` and closes immediately (nothing to hold).
|
|
804
|
+
* 4. Do NOT reopen on `onComplete` — that's a permanent connection. Reopen only on NEW work
|
|
805
|
+
* (another upload) or an unexpected disconnect while work remains.
|
|
806
|
+
* 5. De-dupe — keep the AbortController in a ref and abort the previous before opening a new one
|
|
807
|
+
* (guards against re-renders / StrictMode double-mount).
|
|
808
|
+
* 6. On a network drop, reconnect with backoff and rebuild from the fresh `snapshot` (authoritative).
|
|
809
|
+
* 7. The Promise RESOLVING is normal (stream ended: complete or the cap) — only onError is a failure. */
|
|
721
810
|
events: (corpusId: UUID, handlers: DocumentEventHandlers, signal?: AbortSignal) =>
|
|
722
811
|
this._streamDocEvents(`/v1/corpora/${corpusId}/documents/events`, handlers, signal),
|
|
723
812
|
};
|