@epam/ai-dial-chat-hooks 1.1.6 → 1.1.7

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.
@@ -1,156 +1,227 @@
1
1
  import { safeDecodeURI as e } from "../../shared/string-utils.js";
2
- import { DEFAULT_GENERATION_CONFLICT_MESSAGE as t, GenerationConflictError as n } from "../create-chat-stream-api.js";
3
- import { getConversationPath as r } from "./conversation-path.js";
4
- import { applyChunkToMessages as i } from "./apply-chunk.js";
5
- import { restoreBufferedMessage as a } from "./buffered-generation.js";
6
- import { createResumeIfAwaitingGeneration as o } from "./generation-resume.js";
7
- import { generateUUID as s } from "@epam/ai-dial-chat-shared";
8
- import { useCallback as c, useEffect as l, useMemo as u, useRef as d, useState as f } from "react";
9
- import { SendCompletionDtoModeEnum as p } from "@epam/ai-dial-chat-api-client";
2
+ import { DEFAULT_GENERATION_CONFLICT_MESSAGE as t, DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE as n, GenerationConflictError as r, GenerationPersistenceError as i, StreamInterruptedError as a, StreamUpstreamError as o } from "../create-chat-stream-api.js";
3
+ import { getConversationPath as s } from "./conversation-path.js";
4
+ import { applyChunkToMessages as c } from "./apply-chunk.js";
5
+ import { restoreBufferedMessage as l } from "./buffered-generation.js";
6
+ import { createResumeIfAwaitingGeneration as u, fetchConversationForRecovery as d, hasGeneratedPayload as f, isAwaitingGenerationResume as p } from "./generation-resume.js";
7
+ import { MessageRole as m, generateUUID as h } from "@epam/ai-dial-chat-shared";
8
+ import { useCallback as g, useEffect as _, useMemo as v, useRef as y, useState as b } from "react";
9
+ import { SendCompletionDtoModeEnum as x } from "@epam/ai-dial-chat-api-client";
10
10
  //#region src/conversation/useConversationStream/useConversationStream.ts
11
- var m = 2e4, h = ({ conversationId: h, state: { setConversation: g, conversationRef: _ }, transport: v, generation: { startGeneration: y, completeGeneration: b }, channel: x, overlay: S, onStopError: C, generationConflictMessage: w = t }) => {
12
- let [T, E] = f(() => /* @__PURE__ */ new Set()), [D, O] = f(null), k = d(null), A = d(null), j = d(/* @__PURE__ */ new Set()), M = d(/* @__PURE__ */ new Map()), N = d(/* @__PURE__ */ new Set()), P = d(/* @__PURE__ */ new Map()), F = d(h);
13
- l(() => {
14
- F.current = h;
15
- }, [h]);
16
- let I = c((e) => r(F.current ?? "") === e, []), L = c((e) => {
17
- E((t) => {
11
+ var S = 2e4, ee = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e instanceof o ? e.message : "", C = ({ conversationId: r, state: { setConversation: o, conversationRef: C }, transport: w, generation: { startGeneration: T, completeGeneration: E }, channel: D, overlay: O, onStopError: k, generationConflictMessage: A = t, generationPersistenceErrorMessage: j = n, onStreamError: M }) => {
12
+ let N = y(M);
13
+ _(() => {
14
+ N.current = M;
15
+ }, [M]);
16
+ let [P, F] = b(() => /* @__PURE__ */ new Set()), [I, L] = b(null), R = y(null), z = y(null), B = y(/* @__PURE__ */ new Set()), V = y(/* @__PURE__ */ new Map()), H = y(/* @__PURE__ */ new Set()), U = y(/* @__PURE__ */ new Map()), W = y(r);
17
+ _(() => {
18
+ W.current = r;
19
+ }, [r]);
20
+ let G = g((e) => s(W.current ?? "") === e, []), K = g((e) => {
21
+ F((t) => {
18
22
  if (t.has(e)) return t;
19
23
  let n = new Set(t);
20
24
  return n.add(e), n;
21
25
  });
22
- }, []), R = c((e) => {
23
- E((t) => {
26
+ }, []), q = g((e) => {
27
+ F((t) => {
24
28
  if (!t.has(e)) return t;
25
29
  let n = new Set(t);
26
30
  return n.delete(e), n;
27
31
  });
28
- }, []), z = c((t, o, c, l, u, d, f = p.Append) => {
29
- let h = d ?? s(), C = r(t);
30
- k.current = h, A.current = C, P.current.set(C, h), O(C);
31
- let T;
32
- f === p.Regenerate ? T = c : f === p.Edit && (T = c - 1);
33
- let E = () => P.current.get(C) !== h, D = y(C, h), j = _.current?.messages[c];
34
- j ? M.current.set(C, {
35
- generationId: h,
36
- messageIndex: c,
37
- message: j
38
- }) : M.current.delete(C), L(C), S?.notifyGenerationStart?.(), x?.ensureConnected();
39
- let F = {
40
- signal: D.signal,
32
+ }, []), J = v(() => u({
33
+ transport: w,
34
+ setConversation: o,
35
+ conversationRef: C,
36
+ resumingPathsRef: B,
37
+ bufferedGenerationsRef: V,
38
+ addStreamingPath: K,
39
+ removeStreamingPath: q,
40
+ isPathDisplayed: G,
41
+ generationPersistenceErrorMessage: j
42
+ }), [
43
+ K,
44
+ q,
45
+ G,
46
+ w,
47
+ j
48
+ ]), Y = g((t, n, r, u, g, _, v = x.Append) => {
49
+ let y = _ ?? h(), b = s(t);
50
+ B.current.delete(b), R.current = y, z.current = b, U.current.set(b, y), L(b);
51
+ let k;
52
+ v === x.Regenerate ? k = r : v === x.Edit && (k = r - 1);
53
+ let M = () => U.current.get(b) !== y, P = T(b, y), F = C.current?.messages[r];
54
+ F ? V.current.set(b, {
55
+ generationId: y,
56
+ messageIndex: r,
57
+ message: F
58
+ }) : V.current.delete(b), K(b), O?.notifyGenerationStart?.(), D?.ensureConnected();
59
+ let I = () => {
60
+ M() || q(b), R.current === y && (R.current = null, z.current = null, L(null)), E(b, y), D?.notifyGenerationSettled?.();
61
+ }, W = () => {
62
+ if (M()) return;
63
+ let e = V.current.get(b);
64
+ !e || e.generationId !== y || (e.message = {
65
+ ...e.message,
66
+ streamErrorMessage: j
67
+ }, N.current?.(new i()), G(b) && o((t) => {
68
+ if (!t || M() || !G(b)) return t;
69
+ let n = l(t, e);
70
+ return C.current = n, n;
71
+ }));
72
+ }, Y = {
73
+ signal: P.signal,
41
74
  onChunk: (e) => {
42
- if (k.current !== h) return;
43
- let t = M.current.get(C);
44
- if (t?.generationId === h) {
45
- let n = i([t.message], 0, e);
75
+ if (R.current !== y) return;
76
+ let t = V.current.get(b);
77
+ if (t?.generationId === y) {
78
+ let n = c([t.message], 0, e);
46
79
  n && (t.message = n[0]);
47
80
  }
48
- I(C) && g((t) => {
81
+ G(b) && o((t) => {
49
82
  if (!t) return t;
50
- let n = M.current.get(C), r;
51
- if (n?.generationId === h) r = a(t, n);
83
+ let n = V.current.get(b), i;
84
+ if (n?.generationId === y) i = l(t, n);
52
85
  else {
53
- let n = i(t.messages, c, e);
86
+ let n = c(t.messages, r, e);
54
87
  if (!n) return t;
55
- r = {
88
+ i = {
56
89
  ...t,
57
90
  messages: n
58
91
  };
59
92
  }
60
- return _.current = r, r;
93
+ return C.current = i, i;
61
94
  });
62
95
  },
63
96
  onComplete: async () => {
64
- if (M.current.get(C)?.generationId === h && M.current.delete(C), E() || R(C), k.current === h && (k.current = null, A.current = null, O(null)), b(C, h), x?.notifyGenerationSettled?.(), N.current.has(h) ? N.current.delete(h) : E() || S?.notifyGenerationEnd?.(), !E() && I(C)) try {
65
- let n = await v.getConversation(e(t));
66
- if (E() || !I(C)) return;
67
- g(n), _.current = n;
68
- } catch {}
97
+ let n = V.current.get(b), r = n?.generationId === y ? n : void 0;
98
+ if (I(), H.current.has(y) ? H.current.delete(y) : M() || O?.notifyGenerationEnd?.(), !M()) {
99
+ if (!G(b)) {
100
+ r && V.current.delete(b);
101
+ return;
102
+ }
103
+ try {
104
+ let n = await w.getConversation(e(t));
105
+ if (M()) return;
106
+ if (!G(b)) {
107
+ r && V.current.delete(b);
108
+ return;
109
+ }
110
+ if (r && r.messageIndex === n.messages.length - 1 && p(n) && f(r.message)) {
111
+ W();
112
+ return;
113
+ }
114
+ r && V.current.delete(b), o(n), C.current = n;
115
+ } catch {
116
+ M() || W();
117
+ }
118
+ }
69
119
  },
70
120
  onError: (e) => {
71
- let t = M.current.get(C), r = t?.generationId === h ? t : void 0;
72
- if (r && M.current.delete(C), E() || R(C), k.current === h && (k.current = null, A.current = null, O(null)), b(C, h), x?.notifyGenerationSettled?.(), E() || !I(C)) return;
73
- let i = e instanceof n ? w : e.message;
74
- g((e) => {
75
- if (!e) return e;
76
- let t = r?.generationId === h ? a(e, r) : e, n = {
77
- ...t,
78
- messages: t.messages.map((e, t) => t === c ? {
79
- ...e,
80
- streamErrorMessage: i
81
- } : e)
82
- };
83
- return _.current = n, n;
121
+ if (N.current?.(e), e instanceof a && !M()) {
122
+ Q(e);
123
+ return;
124
+ }
125
+ X(e);
126
+ }
127
+ }, X = (e) => {
128
+ let t = V.current.get(b), n = t?.generationId === y ? t : void 0;
129
+ if (n && (e instanceof i ? n.message = {
130
+ ...n.message,
131
+ streamErrorMessage: j
132
+ } : V.current.delete(b)), I(), M() || !G(b)) return;
133
+ let a = ee(e, A, j);
134
+ o((e) => {
135
+ if (!e || M() || !G(b)) return e;
136
+ let t = n?.generationId === y ? l(e, n) : e, i = {
137
+ ...t,
138
+ messages: t.messages.map((e, t) => t === r ? {
139
+ ...e,
140
+ streamErrorMessage: a
141
+ } : e)
142
+ };
143
+ return C.current = i, i;
144
+ });
145
+ }, Z = () => {
146
+ I(), H.current.has(y) ? H.current.delete(y) : M() || O?.notifyGenerationEnd?.();
147
+ }, Q = async (n) => {
148
+ B.current.add(b);
149
+ let i = await d(() => w.getConversation(e(t)), M);
150
+ if (M()) {
151
+ I();
152
+ return;
153
+ }
154
+ let a = i?.messages[i.messages.length - 1], s = i != null && i.messages.length - 1 === r && a?.role === m.Assistant;
155
+ if (!i || !s) {
156
+ B.current.delete(b), X(n);
157
+ return;
158
+ }
159
+ let c = V.current.get(b), l = c?.generationId === y ? c : void 0;
160
+ if (p(i)) {
161
+ J(t, i, {
162
+ seedMessage: l?.message,
163
+ skipDedupe: !0,
164
+ onSettled: Z
84
165
  });
166
+ return;
85
167
  }
168
+ B.current.delete(b), l && V.current.delete(b), G(b) && (o(i), C.current = i), Z();
86
169
  };
87
170
  (async () => {
88
171
  try {
89
- let e = x?.channelId ?? await x?.waitForChannel(m) ?? void 0, t = N.current.has(h);
90
- if (D.signal.aborted || E() || t) {
91
- M.current.get(C)?.generationId === h && M.current.delete(C), E() || R(C), k.current === h && (k.current = null, A.current = null, O(null)), b(C, h), x?.notifyGenerationSettled?.(), t ? N.current.delete(h) : E() || S?.notifyGenerationEnd?.();
172
+ let e = D?.channelId ?? await D?.waitForChannel(S) ?? void 0, t = H.current.has(y);
173
+ if (P.signal.aborted || M() || t) {
174
+ V.current.get(b)?.generationId === y && V.current.delete(b), M() || q(b), R.current === y && (R.current = null, z.current = null, L(null)), E(b, y), D?.notifyGenerationSettled?.(), t ? H.current.delete(y) : M() || O?.notifyGenerationEnd?.();
92
175
  return;
93
176
  }
94
- v.streamCompletion(C, o, l, F, u, h, f, T, e);
177
+ w.streamCompletion(b, n, u, Y, g, y, v, k, e);
95
178
  } catch (e) {
96
- F.onError(e instanceof Error ? e : Error(String(e)));
179
+ Y.onError(e instanceof Error ? e : Error(String(e)));
97
180
  }
98
181
  })();
99
182
  }, [
100
- y,
101
- b,
102
- L,
103
- R,
104
- I,
105
- x?.channelId,
106
- x?.ensureConnected,
107
- x?.waitForChannel,
108
- x?.notifyGenerationSettled,
109
- S,
110
- v,
111
- w
112
- ]), B = c((e, t) => {
113
- let n = r(e), i = M.current.get(n);
114
- return i ? a(t, i) : t;
115
- }, []), V = c(() => {
116
- let e = k.current;
117
- if (!e || !h) return;
118
- let t = r(h);
119
- A.current === t && (N.current.add(e), S?.notifyStopGenerating?.(), v.stopCompletion({
183
+ T,
184
+ E,
185
+ K,
186
+ q,
187
+ G,
188
+ D?.channelId,
189
+ D?.ensureConnected,
190
+ D?.waitForChannel,
191
+ D?.notifyGenerationSettled,
192
+ O,
193
+ w,
194
+ J,
195
+ A,
196
+ j
197
+ ]), X = g((e, t) => {
198
+ let n = s(e), r = V.current.get(n);
199
+ return r ? l(t, r) : t;
200
+ }, []), Z = g(() => {
201
+ let e = R.current;
202
+ if (!e || !r) return;
203
+ let t = s(r);
204
+ z.current === t && (H.current.add(e), O?.notifyStopGenerating?.(), w.stopCompletion({
120
205
  generationId: e,
121
206
  path: t
122
207
  }).catch((e) => {
123
208
  let t = e instanceof Error ? e : Error(String(e));
124
- C?.(t);
209
+ k?.(t);
125
210
  }));
126
211
  }, [
127
- h,
128
- C,
129
- S,
130
- v
131
- ]), H = u(() => o({
132
- transport: v,
133
- setConversation: g,
134
- conversationRef: _,
135
- resumingPathsRef: j,
136
- bufferedGenerationsRef: M,
137
- addStreamingPath: L,
138
- removeStreamingPath: R,
139
- isPathDisplayed: I
140
- }), [
141
- L,
142
- R,
143
- I,
144
- v
145
- ]), U = h != null && T.has(r(h)), W = h == null ? null : r(h);
212
+ r,
213
+ k,
214
+ O,
215
+ w
216
+ ]), Q = g((e, t) => J(e, t), [J]), te = r != null && P.has(s(r)), $ = r == null ? null : s(r);
146
217
  return {
147
- startStream: z,
148
- handleStop: V,
149
- resumeIfAwaitingGeneration: H,
150
- restoreBufferedGeneration: B,
151
- isStreaming: U,
152
- canStopStreaming: W != null && D === W
218
+ startStream: Y,
219
+ handleStop: Z,
220
+ resumeIfAwaitingGeneration: Q,
221
+ restoreBufferedGeneration: X,
222
+ isStreaming: te,
223
+ canStopStreaming: $ != null && I === $
153
224
  };
154
225
  };
155
226
  //#endregion
156
- export { h as useConversationStream };
227
+ export { C as useConversationStream };
package/conversation.d.ts CHANGED
@@ -327,6 +327,12 @@ export declare interface CreateChatStreamApiDeps {
327
327
  getTimezone?: () => string | undefined;
328
328
  /** `fetch` implementation to issue requests through. Defaults to the global `fetch`. */
329
329
  fetchImpl?: typeof fetch;
330
+ /**
331
+ * How long an open completion stream may go without receiving a byte
332
+ * (keepalive comments included) before it is reported as a
333
+ * {@link StreamInterruptedError}. Defaults to {@link DEFAULT_STREAM_IDLE_TIMEOUT_MS}.
334
+ */
335
+ idleTimeoutMs?: number;
330
336
  }
331
337
 
332
338
  /**
@@ -341,6 +347,11 @@ export declare const createMessagePair: (content: string, customContent?: Messag
341
347
  /** Fallback text of a {@link GenerationConflictError} when no message is supplied. */
342
348
  export declare const DEFAULT_GENERATION_CONFLICT_MESSAGE = "A response is already being generated in this conversation. Wait for it to finish or stop it before sending another message.";
343
349
 
350
+ /** Safe fallback for hosts that do not supply a translated persistence warning. */
351
+ export declare const DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE = "The response could not be saved. It is still shown here, but may be lost if you reload or leave this page. Copy it before continuing.";
352
+
353
+ export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 45000;
354
+
344
355
  /**
345
356
  * Derives the row-action visibility state for a single conversation item.
346
357
  *
@@ -375,6 +386,12 @@ export declare class GenerationConflictError extends Error {
375
386
  constructor(message?: string);
376
387
  }
377
388
 
389
+ /** Identifies a failed terminal save independently of the model's generation outcome. */
390
+ export declare class GenerationPersistenceError extends Error {
391
+ static readonly type = "conversation_save_failed";
392
+ constructor();
393
+ }
394
+
378
395
  /**
379
396
  * Strips the leading bucket segment from a conversation id and decodes any
380
397
  * already-percent-encoded remainder back to raw, so callers that re-encode
@@ -387,6 +404,21 @@ export declare const getConversationPath: (conversationId: string) => string;
387
404
  */
388
405
  export declare const getConversationSource: (item: Pick<ConversationListItemDto, "sharedWithMe" | "publishedWithMe">) => FilterTab;
389
406
 
407
+ /**
408
+ * Returns the tool values the conversation's last message sets through its
409
+ * `form_schema`, restricted to `toolIds`, plus a key identifying that exact
410
+ * application. `undefined` when the last message is not an assistant message
411
+ * or sets no known tool.
412
+ *
413
+ * Streamed chunks and reloads re-create an identical `form_schema`; comparing
414
+ * keys rather than object identity applies an app's value once and leaves a
415
+ * later user toggle alone until the app sends a different value.
416
+ */
417
+ export declare const getFormSchemaToolSyncKey: (conversationId: string, messages: Message[], toolIds: readonly string[]) => {
418
+ key: string;
419
+ values: Record<string, boolean>;
420
+ } | undefined;
421
+
390
422
  /**
391
423
  * Returns the deployment the conversation was last running on: scanning
392
424
  * backwards, the `new_deployment_id` of a `model_changed` status message or
@@ -410,6 +442,15 @@ export declare const getLastDeploymentId: (messages: Message[]) => string | null
410
442
  */
411
443
  export declare const getLastUserMessageToolConfiguration: (messages: Message[]) => Record<string, unknown> | undefined;
412
444
 
445
+ /**
446
+ * Returns the tool configuration a (re-)loaded conversation should show: the
447
+ * last user message's `configuration_value`, overlaid in order by the
448
+ * `form_schema` values of the assistant messages after it. An app that turned
449
+ * a toggle off in its final response must not see it re-armed on reload just
450
+ * because the user message that started the run had it on.
451
+ */
452
+ export declare const getLatestToolConfiguration: (messages: Message[]) => Record<string, unknown> | undefined;
453
+
413
454
  /**
414
455
  * Extracts the deployment/model ID from a DIAL Core conversation ID.
415
456
  *
@@ -493,6 +534,15 @@ export declare const getStarterSubmitText: (starter: StarterOption, description?
493
534
  */
494
535
  export declare const getTimeOfDayGreeting: (hour: number, translations: GreetingTranslations, firstName?: string) => string;
495
536
 
537
+ /**
538
+ * Returns the boolean values an assistant message's `form_schema` sets, keyed
539
+ * by property name, or `undefined` when no property resolves to a boolean.
540
+ * Lets a DIAL app switch a tools-menu toggle (e.g. `deep_research`) per
541
+ * message — on while a run is in progress, off once its result is delivered.
542
+ * Not filtered by tool id: `restoreToolConfiguration` ignores unknown ids.
543
+ */
544
+ export declare const getToolConfigurationFromFormSchema: (formSchema: DeploymentConfigurationSchema | undefined) => Record<string, boolean> | undefined;
545
+
496
546
  /** Pre-translated greeting phrase strings for each time-of-day period. */
497
547
  export declare interface GreetingTranslations {
498
548
  morningWithName: string;
@@ -529,8 +579,8 @@ export declare const isAnswerIncomplete: (answer: Message | undefined) => boolea
529
579
  * True when the conversation's last message is an unresolved assistant
530
580
  * placeholder: the backend only persists a conversation at generation start
531
581
  * (empty placeholder) and at generation end (final content, or a partial
532
- * flagged `streamErrorMessage`/`wasStoppedByUser`), so this shape means a
533
- * generation was still active elsewhere when the conversation was loaded.
582
+ * flagged `streamErrorMessage`/`wasStoppedByUser`). This can mean generation
583
+ * is still active elsewhere, or that its terminal save failed.
534
584
  */
535
585
  export declare const isAwaitingGenerationResume: (conversation: Conversation) => boolean;
536
586
 
@@ -661,6 +711,29 @@ export declare interface StreamCompletionOptions {
661
711
  signal: AbortSignal;
662
712
  }
663
713
 
714
+ /**
715
+ * Reported through `onError` when the network connection carrying a
716
+ * completion stream is lost (a rejected `fetch`, or a read that fails after a
717
+ * 2xx response) or goes silent past the idle timeout — what a laptop sleep or
718
+ * a phone lock mid-generation produces. The backend-owned generation may
719
+ * still be running, so the caller can recover it rather than treat the
720
+ * response as failed. `cause` holds the original error, when there is one.
721
+ */
722
+ export declare class StreamInterruptedError extends Error {
723
+ constructor(cause?: unknown);
724
+ }
725
+
726
+ /**
727
+ * Reported through `onError` when DIAL Core itself signals a failure with an
728
+ * in-band `{ error: { message } }` SSE chunk. Its `message` is upstream text
729
+ * intended for the user, unlike a transport failure (failed `fetch`, non-OK
730
+ * status, broken stream), whose message is technical detail. A custom
731
+ * transport raises this for any user-facing upstream error text.
732
+ */
733
+ export declare class StreamUpstreamError extends Error {
734
+ constructor(message: string);
735
+ }
736
+
664
737
  /** Request shape the host's selected-deployment recognition call accepts. */
665
738
  export declare interface TranscribeWithDeploymentParams {
666
739
  audioUrl: string;
@@ -934,7 +1007,7 @@ export declare interface UseConversationPanelItemsResolvers {
934
1007
  * `transport`/`generation`/`channel`/`overlay` capabilities rather than any
935
1008
  * app context or `server-api` import.
936
1009
  */
937
- export declare const useConversationStream: ({ conversationId, state: { setConversation, conversationRef }, transport, generation: { startGeneration, completeGeneration }, channel, overlay, onStopError, generationConflictMessage, }: UseConversationStreamParams) => UseConversationStreamResult;
1010
+ export declare const useConversationStream: ({ conversationId, state: { setConversation, conversationRef }, transport, generation: { startGeneration, completeGeneration }, channel, overlay, onStopError, generationConflictMessage, generationPersistenceErrorMessage, onStreamError, }: UseConversationStreamParams) => UseConversationStreamResult;
938
1011
 
939
1012
  /** Parameters for {@link useConversationStream}. */
940
1013
  export declare interface UseConversationStreamParams {
@@ -952,6 +1025,15 @@ export declare interface UseConversationStreamParams {
952
1025
  * {@link DEFAULT_GENERATION_CONFLICT_MESSAGE}.
953
1026
  */
954
1027
  generationConflictMessage?: string;
1028
+ /** Host-translated warning shown when the received answer could not be saved. */
1029
+ generationPersistenceErrorMessage?: string;
1030
+ /**
1031
+ * Receives the original error of every failed stream. The hook shows only
1032
+ * host-supplied or upstream-supplied text on the message bubble and hides
1033
+ * transport detail (e.g. `Failed to fetch`), so this is where the host can
1034
+ * still log or report the raw error.
1035
+ */
1036
+ onStreamError?: (error: Error) => void;
955
1037
  }
956
1038
 
957
1039
  /** Return value of {@link useConversationStream}. */
package/conversation.js CHANGED
@@ -1,30 +1,30 @@
1
1
  import { buildAnnouncementSignature as e, hasAnnouncementContent as t, hasStructuredAnnouncement as n, sanitizeAnnouncementHtml as r, sanitizeAnnouncementMessageHtml as i } from "./conversation/announcement-message.js";
2
- import { DEFAULT_GENERATION_CONFLICT_MESSAGE as a, GenerationConflictError as o, createChatStreamApi as s } from "./conversation/create-chat-stream-api.js";
3
- import { shouldWatchForDisplayNameUpdate as c } from "./conversation/display-name-watch.js";
4
- import { formatAppVersion as l, sanitizeFooterHtml as u } from "./conversation/footer-message.js";
5
- import { getModelIdFromConversationId as d } from "./conversation/get-model-id-from-conversation-id.js";
6
- import { getTimeOfDayGreeting as f } from "./conversation/greeting.js";
7
- import { createDeploymentChangedMessage as p } from "./conversation/message-factory.js";
8
- import { getLastDeploymentId as m, getLastUserMessageToolConfiguration as h, isMessageStreaming as g, messageHasStages as _, normalizeResponseFormat as v } from "./conversation/message-utils.js";
9
- import { getQuickAppConversationStarters as y } from "./conversation/quick-app-conversation-starters.js";
10
- import { getStarterPopulateText as b, getStartersFromSchema as x } from "./conversation/starter-option.js";
11
- import { deriveConversationRowActionState as S } from "./conversation/deriveConversationRowActionState/deriveConversationRowActionState.js";
12
- import { useActiveConversationSync as C } from "./conversation/useActiveConversationSync/useActiveConversationSync.js";
13
- import { useAsyncConfirmDialog as w } from "./conversation/useAsyncConfirmDialog/useAsyncConfirmDialog.js";
14
- import { useConversationLookupMaps as T } from "./conversation/useConversationLookupMaps/useConversationLookupMaps.js";
15
- import { getConversationSource as E, useConversationPanelItems as D } from "./conversation/useConversationPanelItems/useConversationPanelItems.js";
16
- import { useImportFilePicker as O } from "./conversation/useImportFilePicker/useImportFilePicker.js";
17
- import { useAttachmentUpload as k } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
18
- import { AudioTranscriptionError as A, AudioTranscriptionErrorReason as j } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
19
- import { useTranscribeAudio as M } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
20
- import { attachmentToDto as N, attachmentsToDtos as P } from "./conversation/useConversationHandlers/attachment-to-dto.js";
21
- import { createMessagePair as F } from "./conversation/useConversationHandlers/message-factory.js";
22
- import { hasActiveToolConfig as I, isAnswerIncomplete as L, isMessageChanged as R, shouldRerunGenerationOnEdit as z } from "./conversation/useConversationHandlers/message-utils.js";
23
- import { getStarterConversationText as B, getStarterDisplayText as V, getStarterSubmitText as H } from "./conversation/useConversationHandlers/starter-option.js";
24
- import { getConversationPath as U } from "./conversation/useConversationStream/conversation-path.js";
25
- import { useConversationHandlers as W } from "./conversation/useConversationHandlers/useConversationHandlers.js";
26
- import { isAwaitingGenerationResume as G } from "./conversation/useConversationStream/generation-resume.js";
27
- import { useConversationStream as K } from "./conversation/useConversationStream/useConversationStream.js";
28
- import { useChatSettingsFormConfig as q } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
29
- import { useToolsMenu as J } from "./useToolsMenu/useToolsMenu.js";
30
- export { A as AudioTranscriptionError, j as AudioTranscriptionErrorReason, a as DEFAULT_GENERATION_CONFLICT_MESSAGE, o as GenerationConflictError, N as attachmentToDto, P as attachmentsToDtos, e as buildAnnouncementSignature, s as createChatStreamApi, p as createDeploymentChangedMessage, F as createMessagePair, S as deriveConversationRowActionState, l as formatAppVersion, U as getConversationPath, E as getConversationSource, m as getLastDeploymentId, h as getLastUserMessageToolConfiguration, d as getModelIdFromConversationId, y as getQuickAppConversationStarters, B as getStarterConversationText, V as getStarterDisplayText, b as getStarterPopulateText, H as getStarterSubmitText, x as getStartersFromSchema, f as getTimeOfDayGreeting, I as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, L as isAnswerIncomplete, G as isAwaitingGenerationResume, R as isMessageChanged, g as isMessageStreaming, _ as messageHasStages, v as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, u as sanitizeFooterHtml, z as shouldRerunGenerationOnEdit, c as shouldWatchForDisplayNameUpdate, C as useActiveConversationSync, w as useAsyncConfirmDialog, k as useAttachmentUpload, q as useChatSettingsFormConfig, W as useConversationHandlers, T as useConversationLookupMaps, D as useConversationPanelItems, K as useConversationStream, O as useImportFilePicker, J as useToolsMenu, M as useTranscribeAudio };
2
+ import { DEFAULT_GENERATION_CONFLICT_MESSAGE as a, DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE as o, DEFAULT_STREAM_IDLE_TIMEOUT_MS as s, GenerationConflictError as c, GenerationPersistenceError as l, StreamInterruptedError as u, StreamUpstreamError as d, createChatStreamApi as f } from "./conversation/create-chat-stream-api.js";
3
+ import { shouldWatchForDisplayNameUpdate as p } from "./conversation/display-name-watch.js";
4
+ import { formatAppVersion as m, sanitizeFooterHtml as h } from "./conversation/footer-message.js";
5
+ import { getModelIdFromConversationId as g } from "./conversation/get-model-id-from-conversation-id.js";
6
+ import { getTimeOfDayGreeting as _ } from "./conversation/greeting.js";
7
+ import { createDeploymentChangedMessage as v } from "./conversation/message-factory.js";
8
+ import { getFormSchemaToolSyncKey as y, getLastDeploymentId as b, getLastUserMessageToolConfiguration as x, getLatestToolConfiguration as S, getToolConfigurationFromFormSchema as C, isMessageStreaming as w, messageHasStages as T, normalizeResponseFormat as E } from "./conversation/message-utils.js";
9
+ import { getQuickAppConversationStarters as D } from "./conversation/quick-app-conversation-starters.js";
10
+ import { getStarterPopulateText as O, getStartersFromSchema as k } from "./conversation/starter-option.js";
11
+ import { deriveConversationRowActionState as A } from "./conversation/deriveConversationRowActionState/deriveConversationRowActionState.js";
12
+ import { useActiveConversationSync as j } from "./conversation/useActiveConversationSync/useActiveConversationSync.js";
13
+ import { useAsyncConfirmDialog as M } from "./conversation/useAsyncConfirmDialog/useAsyncConfirmDialog.js";
14
+ import { useConversationLookupMaps as N } from "./conversation/useConversationLookupMaps/useConversationLookupMaps.js";
15
+ import { getConversationSource as P, useConversationPanelItems as F } from "./conversation/useConversationPanelItems/useConversationPanelItems.js";
16
+ import { useImportFilePicker as I } from "./conversation/useImportFilePicker/useImportFilePicker.js";
17
+ import { useAttachmentUpload as L } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
18
+ import { AudioTranscriptionError as R, AudioTranscriptionErrorReason as z } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
19
+ import { useTranscribeAudio as B } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
20
+ import { attachmentToDto as V, attachmentsToDtos as H } from "./conversation/useConversationHandlers/attachment-to-dto.js";
21
+ import { createMessagePair as U } from "./conversation/useConversationHandlers/message-factory.js";
22
+ import { hasActiveToolConfig as W, isAnswerIncomplete as G, isMessageChanged as K, shouldRerunGenerationOnEdit as q } from "./conversation/useConversationHandlers/message-utils.js";
23
+ import { getStarterConversationText as J, getStarterDisplayText as Y, getStarterSubmitText as X } from "./conversation/useConversationHandlers/starter-option.js";
24
+ import { getConversationPath as Z } from "./conversation/useConversationStream/conversation-path.js";
25
+ import { useConversationHandlers as Q } from "./conversation/useConversationHandlers/useConversationHandlers.js";
26
+ import { isAwaitingGenerationResume as $ } from "./conversation/useConversationStream/generation-resume.js";
27
+ import { useConversationStream as ee } from "./conversation/useConversationStream/useConversationStream.js";
28
+ import { useChatSettingsFormConfig as te } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
29
+ import { useToolsMenu as ne } from "./useToolsMenu/useToolsMenu.js";
30
+ export { R as AudioTranscriptionError, z as AudioTranscriptionErrorReason, a as DEFAULT_GENERATION_CONFLICT_MESSAGE, o as DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE, s as DEFAULT_STREAM_IDLE_TIMEOUT_MS, c as GenerationConflictError, l as GenerationPersistenceError, u as StreamInterruptedError, d as StreamUpstreamError, V as attachmentToDto, H as attachmentsToDtos, e as buildAnnouncementSignature, f as createChatStreamApi, v as createDeploymentChangedMessage, U as createMessagePair, A as deriveConversationRowActionState, m as formatAppVersion, Z as getConversationPath, P as getConversationSource, y as getFormSchemaToolSyncKey, b as getLastDeploymentId, x as getLastUserMessageToolConfiguration, S as getLatestToolConfiguration, g as getModelIdFromConversationId, D as getQuickAppConversationStarters, J as getStarterConversationText, Y as getStarterDisplayText, O as getStarterPopulateText, X as getStarterSubmitText, k as getStartersFromSchema, _ as getTimeOfDayGreeting, C as getToolConfigurationFromFormSchema, W as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, G as isAnswerIncomplete, $ as isAwaitingGenerationResume, K as isMessageChanged, w as isMessageStreaming, T as messageHasStages, E as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, h as sanitizeFooterHtml, q as shouldRerunGenerationOnEdit, p as shouldWatchForDisplayNameUpdate, j as useActiveConversationSync, M as useAsyncConfirmDialog, L as useAttachmentUpload, te as useChatSettingsFormConfig, Q as useConversationHandlers, N as useConversationLookupMaps, F as useConversationPanelItems, ee as useConversationStream, I as useImportFilePicker, ne as useToolsMenu, B as useTranscribeAudio };