@babav/knowledge-core-client 0.29.0 → 0.31.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 +57 -3
- package/dist/index.js +54 -0
- package/package.json +1 -1
- package/src/index.ts +91 -2
package/dist/index.d.ts
CHANGED
|
@@ -95,7 +95,15 @@ export interface QueryRequest {
|
|
|
95
95
|
* registers). */
|
|
96
96
|
export type VisualLayout = "linear-flow" | "ring-cycle" | "nesting" | "radial-hub" | "side-by-side" | "stacked-layers" | "two-state" | "free";
|
|
97
97
|
/** A visual attached to a response, anchored by char offsets into `answer` (SAME coordinate
|
|
98
|
-
* system as citations).
|
|
98
|
+
* system as citations).
|
|
99
|
+
*
|
|
100
|
+
* UNIFORM DELIVERY: every register (chart / structural / conceptual / scenic) carries `image` — a
|
|
101
|
+
* PNG served BY KC. To display it, GET `image.url` (resolve a relative path against your KC base
|
|
102
|
+
* URL) with your normal API key; the response body is the PNG bytes. The URL is a stable, non-
|
|
103
|
+
* expiring KC endpoint for persisted/conversational visuals (`expires_at` absent), so it works on
|
|
104
|
+
* reload/copy/export and on later conversation retrieval — the client never fetches GCS and never
|
|
105
|
+
* parses inline SVG. `rendered_svg` appears ONLY as a rare fail-soft fallback (server rasterization
|
|
106
|
+
* failed); prefer `image` and treat `rendered_svg` as a last resort. */
|
|
99
107
|
export interface Visual {
|
|
100
108
|
id: string;
|
|
101
109
|
anchor: {
|
|
@@ -107,13 +115,15 @@ export interface Visual {
|
|
|
107
115
|
payload: Record<string, unknown>;
|
|
108
116
|
/** Declared spatial arrangement (meaningful for `conceptual`; "free" otherwise). */
|
|
109
117
|
layout: VisualLayout;
|
|
110
|
-
|
|
118
|
+
/** PRIMARY: GET this (relative to your KC base, with your API key) -> PNG bytes. */
|
|
111
119
|
image?: {
|
|
112
120
|
url: string;
|
|
113
|
-
expires_at: number;
|
|
114
121
|
width: number;
|
|
115
122
|
height: number;
|
|
123
|
+
expires_at?: number;
|
|
116
124
|
};
|
|
125
|
+
/** Fail-soft fallback only (rasterize/store failed); normally absent. */
|
|
126
|
+
rendered_svg?: string;
|
|
117
127
|
}
|
|
118
128
|
/** Visual stage (streaming): generation has started. MAY be emitted twice — first with
|
|
119
129
|
* `pending: null` (started, count unknown), then again with the ACCURATE post-vet count before
|
|
@@ -531,6 +541,16 @@ export declare class KnowledgeCoreClient extends HttpBase {
|
|
|
531
541
|
* error). Pass `signal` and abort() on unmount / when the user cancels or navigates away so the
|
|
532
542
|
* connection doesn't linger. Transient (ends on `done`) — no reopen logic needed. */
|
|
533
543
|
queryStream(agentId: UUID, body: QueryRequest, handlers: StreamHandlers, signal?: AbortSignal): Promise<void>;
|
|
544
|
+
/** DUMMY-PROOF CHAT. Every message goes through a conversation — it is structurally impossible
|
|
545
|
+
* to send a chat turn as a non-persisted one-shot. Use this for ANY chat UI. Use the low-level
|
|
546
|
+
* `query`/`queryStream` ONLY for programmatic one-shots (tools).
|
|
547
|
+
* New chat: const chat = kc.chat(agentId, corpusIds);
|
|
548
|
+
* Resume: const chat = kc.chat(agentId, corpusIds, { conversationId });
|
|
549
|
+
* The conversation is created LAZILY on the first `send()` (opening a "new chat" and never
|
|
550
|
+
* sending leaves nothing behind). See ChatSession.send. */
|
|
551
|
+
chat(agentId: UUID, corpusIds: UUID[], opts?: {
|
|
552
|
+
conversationId?: UUID;
|
|
553
|
+
}): ChatSession;
|
|
534
554
|
/** Shared SSE reader for the document-status streams (documents.events / folders.events).
|
|
535
555
|
* Resolves when the stream ends (server sends `complete` once nothing is in-flight, or the
|
|
536
556
|
* cap is hit). Abort via the signal to stop watching. Throws 501 if the env has no bus. */
|
|
@@ -896,3 +916,37 @@ export declare class AdminClient extends HttpBase {
|
|
|
896
916
|
delete: (id: UUID) => Promise<void>;
|
|
897
917
|
};
|
|
898
918
|
}
|
|
919
|
+
export interface ChatSendOptions extends StreamHandlers {
|
|
920
|
+
overrides?: QueryRequest["overrides"];
|
|
921
|
+
filter?: QueryRequest["filter"];
|
|
922
|
+
visual?: QueryRequest["visual"];
|
|
923
|
+
/** Abort on unmount / navigation so the SSE connection doesn't linger. */
|
|
924
|
+
signal?: AbortSignal;
|
|
925
|
+
/** Applied ONLY when the conversation is lazily created on the FIRST message. `title` defaults
|
|
926
|
+
* to the message text (truncated) so a chat is never left untitled. */
|
|
927
|
+
title?: string;
|
|
928
|
+
customMetadata?: Record<string, unknown>;
|
|
929
|
+
ephemeral?: boolean;
|
|
930
|
+
/** Fired with the conversation id the moment it is created (first message) or resumed — use it to
|
|
931
|
+
* update the sidebar/URL/route IMMEDIATELY, before the answer streams. */
|
|
932
|
+
onConversationId?: (id: UUID) => void;
|
|
933
|
+
}
|
|
934
|
+
/** A chat session bound to ONE conversation (created via `kc.chat(...)`). Every `send()` is ALWAYS
|
|
935
|
+
* conversational: the conversation_id is attached for you, so a chat turn can never be a
|
|
936
|
+
* non-persisted one-shot. Combined with server-side persist-on-close salvage + the orphan reaper,
|
|
937
|
+
* a chat turn is never lost and a chat conversation is never left blank. */
|
|
938
|
+
export declare class ChatSession {
|
|
939
|
+
#private;
|
|
940
|
+
/** The conversation id — null until the first `send()` (unless resumed). */
|
|
941
|
+
conversationId: UUID | null;
|
|
942
|
+
constructor(kc: KnowledgeCoreClient, agentId: UUID, corpusIds: UUID[], conversationId: UUID | null);
|
|
943
|
+
/** Send a chat message (streaming). Lazily creates the conversation on the first message and
|
|
944
|
+
* ALWAYS passes conversation_id, so the turn is persisted. `onConversationId` fires before the
|
|
945
|
+
* stream so you can show the conversation immediately. Pass `signal` to cancel on unmount. */
|
|
946
|
+
send(text: string, o?: ChatSendOptions): Promise<void>;
|
|
947
|
+
/** The conversation history (messages), once it exists. Empty page before the first send. */
|
|
948
|
+
messages(q?: {
|
|
949
|
+
limit?: number;
|
|
950
|
+
cursor?: string;
|
|
951
|
+
}): Promise<Page<Message>>;
|
|
952
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -178,6 +178,16 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
178
178
|
if (buf.trim())
|
|
179
179
|
dispatchSse(buf, handlers);
|
|
180
180
|
}
|
|
181
|
+
/** DUMMY-PROOF CHAT. Every message goes through a conversation — it is structurally impossible
|
|
182
|
+
* to send a chat turn as a non-persisted one-shot. Use this for ANY chat UI. Use the low-level
|
|
183
|
+
* `query`/`queryStream` ONLY for programmatic one-shots (tools).
|
|
184
|
+
* New chat: const chat = kc.chat(agentId, corpusIds);
|
|
185
|
+
* Resume: const chat = kc.chat(agentId, corpusIds, { conversationId });
|
|
186
|
+
* The conversation is created LAZILY on the first `send()` (opening a "new chat" and never
|
|
187
|
+
* sending leaves nothing behind). See ChatSession.send. */
|
|
188
|
+
chat(agentId, corpusIds, opts) {
|
|
189
|
+
return new ChatSession(this, agentId, corpusIds, opts?.conversationId ?? null);
|
|
190
|
+
}
|
|
181
191
|
/** Shared SSE reader for the document-status streams (documents.events / folders.events).
|
|
182
192
|
* Resolves when the stream ends (server sends `complete` once nothing is in-flight, or the
|
|
183
193
|
* cap is hit). Abort via the signal to stop watching. Throws 501 if the env has no bus. */
|
|
@@ -460,6 +470,50 @@ export class AdminClient extends HttpBase {
|
|
|
460
470
|
delete: (id) => this.request("DELETE", `/v1/agents/${id}`),
|
|
461
471
|
};
|
|
462
472
|
}
|
|
473
|
+
/** A chat session bound to ONE conversation (created via `kc.chat(...)`). Every `send()` is ALWAYS
|
|
474
|
+
* conversational: the conversation_id is attached for you, so a chat turn can never be a
|
|
475
|
+
* non-persisted one-shot. Combined with server-side persist-on-close salvage + the orphan reaper,
|
|
476
|
+
* a chat turn is never lost and a chat conversation is never left blank. */
|
|
477
|
+
export class ChatSession {
|
|
478
|
+
#kc;
|
|
479
|
+
#agentId;
|
|
480
|
+
#corpusIds;
|
|
481
|
+
#creating = null;
|
|
482
|
+
/** The conversation id — null until the first `send()` (unless resumed). */
|
|
483
|
+
conversationId;
|
|
484
|
+
constructor(kc, agentId, corpusIds, conversationId) {
|
|
485
|
+
this.#kc = kc;
|
|
486
|
+
this.#agentId = agentId;
|
|
487
|
+
this.#corpusIds = corpusIds;
|
|
488
|
+
this.conversationId = conversationId;
|
|
489
|
+
}
|
|
490
|
+
/** Lazy create-once (concurrency-safe): the first send starts creation; a racing send awaits it. */
|
|
491
|
+
#ensure(o, text) {
|
|
492
|
+
if (this.conversationId)
|
|
493
|
+
return Promise.resolve(this.conversationId);
|
|
494
|
+
if (!this.#creating) {
|
|
495
|
+
this.#creating = this.#kc.conversations
|
|
496
|
+
.create({ title: o.title ?? text.slice(0, 80), custom_metadata: o.customMetadata, ephemeral: o.ephemeral })
|
|
497
|
+
.then((c) => { this.conversationId = c.id; return c.id; });
|
|
498
|
+
}
|
|
499
|
+
return this.#creating;
|
|
500
|
+
}
|
|
501
|
+
/** Send a chat message (streaming). Lazily creates the conversation on the first message and
|
|
502
|
+
* ALWAYS passes conversation_id, so the turn is persisted. `onConversationId` fires before the
|
|
503
|
+
* stream so you can show the conversation immediately. Pass `signal` to cancel on unmount. */
|
|
504
|
+
async send(text, o = {}) {
|
|
505
|
+
const convId = await this.#ensure(o, text);
|
|
506
|
+
o.onConversationId?.(convId);
|
|
507
|
+
await this.#kc.queryStream(this.#agentId, { corpus_ids: this.#corpusIds, query: text, conversation_id: convId,
|
|
508
|
+
overrides: o.overrides, filter: o.filter, visual: o.visual }, o, o.signal);
|
|
509
|
+
}
|
|
510
|
+
/** The conversation history (messages), once it exists. Empty page before the first send. */
|
|
511
|
+
messages(q) {
|
|
512
|
+
if (!this.conversationId)
|
|
513
|
+
return Promise.resolve({ items: [], next_cursor: null });
|
|
514
|
+
return this.#kc.conversations.listMessages(this.conversationId, q);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
463
517
|
// ---------------------------------------------------------------------------
|
|
464
518
|
// helpers
|
|
465
519
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babav/knowledge-core-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.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
|
@@ -129,7 +129,15 @@ export type VisualLayout =
|
|
|
129
129
|
| "free";
|
|
130
130
|
|
|
131
131
|
/** A visual attached to a response, anchored by char offsets into `answer` (SAME coordinate
|
|
132
|
-
* system as citations).
|
|
132
|
+
* system as citations).
|
|
133
|
+
*
|
|
134
|
+
* UNIFORM DELIVERY: every register (chart / structural / conceptual / scenic) carries `image` — a
|
|
135
|
+
* PNG served BY KC. To display it, GET `image.url` (resolve a relative path against your KC base
|
|
136
|
+
* URL) with your normal API key; the response body is the PNG bytes. The URL is a stable, non-
|
|
137
|
+
* expiring KC endpoint for persisted/conversational visuals (`expires_at` absent), so it works on
|
|
138
|
+
* reload/copy/export and on later conversation retrieval — the client never fetches GCS and never
|
|
139
|
+
* parses inline SVG. `rendered_svg` appears ONLY as a rare fail-soft fallback (server rasterization
|
|
140
|
+
* failed); prefer `image` and treat `rendered_svg` as a last resort. */
|
|
133
141
|
export interface Visual {
|
|
134
142
|
id: string;
|
|
135
143
|
anchor: { start: number; end: number };
|
|
@@ -138,8 +146,10 @@ export interface Visual {
|
|
|
138
146
|
payload: Record<string, unknown>; // {kind: "grammar"|"vega_lite"|"svg"|"image", ...}
|
|
139
147
|
/** Declared spatial arrangement (meaningful for `conceptual`; "free" otherwise). */
|
|
140
148
|
layout: VisualLayout;
|
|
149
|
+
/** PRIMARY: GET this (relative to your KC base, with your API key) -> PNG bytes. */
|
|
150
|
+
image?: { url: string; width: number; height: number; expires_at?: number };
|
|
151
|
+
/** Fail-soft fallback only (rasterize/store failed); normally absent. */
|
|
141
152
|
rendered_svg?: string;
|
|
142
|
-
image?: { url: string; expires_at: number; width: number; height: number };
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
/** Visual stage (streaming): generation has started. MAY be emitted twice — first with
|
|
@@ -645,6 +655,17 @@ export class KnowledgeCoreClient extends HttpBase {
|
|
|
645
655
|
if (buf.trim()) dispatchSse(buf, handlers);
|
|
646
656
|
}
|
|
647
657
|
|
|
658
|
+
/** DUMMY-PROOF CHAT. Every message goes through a conversation — it is structurally impossible
|
|
659
|
+
* to send a chat turn as a non-persisted one-shot. Use this for ANY chat UI. Use the low-level
|
|
660
|
+
* `query`/`queryStream` ONLY for programmatic one-shots (tools).
|
|
661
|
+
* New chat: const chat = kc.chat(agentId, corpusIds);
|
|
662
|
+
* Resume: const chat = kc.chat(agentId, corpusIds, { conversationId });
|
|
663
|
+
* The conversation is created LAZILY on the first `send()` (opening a "new chat" and never
|
|
664
|
+
* sending leaves nothing behind). See ChatSession.send. */
|
|
665
|
+
chat(agentId: UUID, corpusIds: UUID[], opts?: { conversationId?: UUID }): ChatSession {
|
|
666
|
+
return new ChatSession(this, agentId, corpusIds, opts?.conversationId ?? null);
|
|
667
|
+
}
|
|
668
|
+
|
|
648
669
|
/** Shared SSE reader for the document-status streams (documents.events / folders.events).
|
|
649
670
|
* Resolves when the stream ends (server sends `complete` once nothing is in-flight, or the
|
|
650
671
|
* cap is hit). Abort via the signal to stop watching. Throws 501 if the env has no bus. */
|
|
@@ -1001,6 +1022,74 @@ export class AdminClient extends HttpBase {
|
|
|
1001
1022
|
};
|
|
1002
1023
|
}
|
|
1003
1024
|
|
|
1025
|
+
export interface ChatSendOptions extends StreamHandlers {
|
|
1026
|
+
overrides?: QueryRequest["overrides"];
|
|
1027
|
+
filter?: QueryRequest["filter"];
|
|
1028
|
+
visual?: QueryRequest["visual"];
|
|
1029
|
+
/** Abort on unmount / navigation so the SSE connection doesn't linger. */
|
|
1030
|
+
signal?: AbortSignal;
|
|
1031
|
+
/** Applied ONLY when the conversation is lazily created on the FIRST message. `title` defaults
|
|
1032
|
+
* to the message text (truncated) so a chat is never left untitled. */
|
|
1033
|
+
title?: string;
|
|
1034
|
+
customMetadata?: Record<string, unknown>;
|
|
1035
|
+
ephemeral?: boolean;
|
|
1036
|
+
/** Fired with the conversation id the moment it is created (first message) or resumed — use it to
|
|
1037
|
+
* update the sidebar/URL/route IMMEDIATELY, before the answer streams. */
|
|
1038
|
+
onConversationId?: (id: UUID) => void;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/** A chat session bound to ONE conversation (created via `kc.chat(...)`). Every `send()` is ALWAYS
|
|
1042
|
+
* conversational: the conversation_id is attached for you, so a chat turn can never be a
|
|
1043
|
+
* non-persisted one-shot. Combined with server-side persist-on-close salvage + the orphan reaper,
|
|
1044
|
+
* a chat turn is never lost and a chat conversation is never left blank. */
|
|
1045
|
+
export class ChatSession {
|
|
1046
|
+
#kc: KnowledgeCoreClient;
|
|
1047
|
+
#agentId: UUID;
|
|
1048
|
+
#corpusIds: UUID[];
|
|
1049
|
+
#creating: Promise<UUID> | null = null;
|
|
1050
|
+
/** The conversation id — null until the first `send()` (unless resumed). */
|
|
1051
|
+
conversationId: UUID | null;
|
|
1052
|
+
|
|
1053
|
+
constructor(kc: KnowledgeCoreClient, agentId: UUID, corpusIds: UUID[], conversationId: UUID | null) {
|
|
1054
|
+
this.#kc = kc;
|
|
1055
|
+
this.#agentId = agentId;
|
|
1056
|
+
this.#corpusIds = corpusIds;
|
|
1057
|
+
this.conversationId = conversationId;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
/** Lazy create-once (concurrency-safe): the first send starts creation; a racing send awaits it. */
|
|
1061
|
+
#ensure(o: ChatSendOptions, text: string): Promise<UUID> {
|
|
1062
|
+
if (this.conversationId) return Promise.resolve(this.conversationId);
|
|
1063
|
+
if (!this.#creating) {
|
|
1064
|
+
this.#creating = this.#kc.conversations
|
|
1065
|
+
.create({ title: o.title ?? text.slice(0, 80), custom_metadata: o.customMetadata, ephemeral: o.ephemeral })
|
|
1066
|
+
.then((c) => { this.conversationId = c.id; return c.id; });
|
|
1067
|
+
}
|
|
1068
|
+
return this.#creating;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/** Send a chat message (streaming). Lazily creates the conversation on the first message and
|
|
1072
|
+
* ALWAYS passes conversation_id, so the turn is persisted. `onConversationId` fires before the
|
|
1073
|
+
* stream so you can show the conversation immediately. Pass `signal` to cancel on unmount. */
|
|
1074
|
+
async send(text: string, o: ChatSendOptions = {}): Promise<void> {
|
|
1075
|
+
const convId = await this.#ensure(o, text);
|
|
1076
|
+
o.onConversationId?.(convId);
|
|
1077
|
+
await this.#kc.queryStream(
|
|
1078
|
+
this.#agentId,
|
|
1079
|
+
{ corpus_ids: this.#corpusIds, query: text, conversation_id: convId,
|
|
1080
|
+
overrides: o.overrides, filter: o.filter, visual: o.visual },
|
|
1081
|
+
o,
|
|
1082
|
+
o.signal,
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/** The conversation history (messages), once it exists. Empty page before the first send. */
|
|
1087
|
+
messages(q?: { limit?: number; cursor?: string }): Promise<Page<Message>> {
|
|
1088
|
+
if (!this.conversationId) return Promise.resolve({ items: [], next_cursor: null });
|
|
1089
|
+
return this.#kc.conversations.listMessages(this.conversationId, q);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1004
1093
|
// ---------------------------------------------------------------------------
|
|
1005
1094
|
// helpers
|
|
1006
1095
|
// ---------------------------------------------------------------------------
|