@epam/ai-dial-chat-hooks 1.3.0-dev.357 → 1.3.0-dev.360

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.
Files changed (37) hide show
  1. package/conversation/useConversationStream/conversation-stream.types.d.ts +136 -0
  2. package/conversation/useConversationStream/conversation-stream.types.d.ts.map +1 -0
  3. package/conversation/useConversationStream/generation-resume.d.ts +1 -1
  4. package/conversation/useConversationStream/generation-resume.d.ts.map +1 -1
  5. package/conversation/useConversationStream/stream-errors.d.ts +10 -0
  6. package/conversation/useConversationStream/stream-errors.d.ts.map +1 -0
  7. package/conversation/useConversationStream/stream-errors.js +10 -0
  8. package/conversation/useConversationStream/stream-session.d.ts +51 -0
  9. package/conversation/useConversationStream/stream-session.d.ts.map +1 -0
  10. package/conversation/useConversationStream/stream-session.js +179 -0
  11. package/conversation/useConversationStream/useConversationReloadErrors.d.ts +25 -0
  12. package/conversation/useConversationStream/useConversationReloadErrors.d.ts.map +1 -0
  13. package/conversation/useConversationStream/useConversationReloadErrors.js +38 -0
  14. package/conversation/useConversationStream/useConversationStream.d.ts +2 -135
  15. package/conversation/useConversationStream/useConversationStream.d.ts.map +1 -1
  16. package/conversation/useConversationStream/useConversationStream.js +134 -332
  17. package/conversation/useConversationStream/useStopGeneration.d.ts +29 -0
  18. package/conversation/useConversationStream/useStopGeneration.d.ts.map +1 -0
  19. package/conversation/useConversationStream/useStopGeneration.js +48 -0
  20. package/conversation/useConversationStream/useStreamPathState.d.ts +39 -0
  21. package/conversation/useConversationStream/useStreamPathState.d.ts.map +1 -0
  22. package/conversation/useConversationStream/useStreamPathState.js +55 -0
  23. package/index.js +141 -141
  24. package/package.json +17 -17
  25. package/project/project-file-preview.d.ts +4 -0
  26. package/project/project-file-preview.d.ts.map +1 -1
  27. package/project/project-file-preview.js +15 -10
  28. package/project/projects-api-client.d.ts +7 -1
  29. package/project/projects-api-client.d.ts.map +1 -1
  30. package/project/projects-api-client.js +1 -0
  31. package/project/use-project-conversations.d.ts +2 -1
  32. package/project/use-project-conversations.d.ts.map +1 -1
  33. package/project/use-project-conversations.js +11 -7
  34. package/project/use-project-file-preview.d.ts +6 -1
  35. package/project/use-project-file-preview.d.ts.map +1 -1
  36. package/project/use-project-file-preview.js +35 -31
  37. package/project.js +16 -16
@@ -0,0 +1,136 @@
1
+ import { SendCompletionDtoModeEnum } from '@epam/ai-dial-chat-api-client';
2
+ import { Conversation, MessageCustomContent, StreamChunk } from '@epam/ai-dial-chat-shared';
3
+ import { Dispatch, MutableRefObject, SetStateAction } from 'react';
4
+ /** Options accepted by {@link ConversationStreamTransport.streamCompletion}. */
5
+ export interface StreamCompletionOptions {
6
+ onChunk: (chunk: StreamChunk) => void;
7
+ onComplete: () => void | Promise<void>;
8
+ onError: (error: Error) => void;
9
+ signal: AbortSignal;
10
+ }
11
+ /**
12
+ * Host-owned transport for the DIAL Core completion protocol. Implemented at
13
+ * the app edge — never hardcodes an `/api` path, CSRF handling, or a
14
+ * `server-api` import; the library only depends on this interface's shape.
15
+ */
16
+ export interface ConversationStreamTransport {
17
+ /** Starts a completion; delivers chunks/completion/error through `options`, aborted via `options.signal`. */
18
+ streamCompletion(path: string, message: string | undefined, model: string, options: StreamCompletionOptions, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, messageIndex?: number, clientChannelId?: string): void;
19
+ /**
20
+ * Requests the backend stop an active generation. `content` is the answer text shown
21
+ * so far; the backend saves it for a generation whose text it does not hold.
22
+ */
23
+ stopCompletion(params: {
24
+ generationId: string;
25
+ path: string;
26
+ content?: string;
27
+ }): Promise<void>;
28
+ /** Opens a stream of resource-update events for `path`, until aborted via `signal`. */
29
+ watchConversation(path: string, signal: AbortSignal): Promise<ReadableStream<Uint8Array>>;
30
+ /**
31
+ * Attaches to an active generation's live replay: a `snapshot` event
32
+ * carrying the assistant message as assembled so far, then a `chunk` event
33
+ * per subsequent delta, then exactly one terminal event (`done`/`error`/
34
+ * `stopped`), until aborted via `signal`. Rejects when no active
35
+ * generation exists for `path` — including one that already finished —
36
+ * so the caller can fall back to `watchConversation`.
37
+ */
38
+ attachToGeneration(path: string, signal: AbortSignal): Promise<ReadableStream<Uint8Array>>;
39
+ /** Reloads the persisted conversation by its full (bucket-qualified) id. */
40
+ getConversation(conversationId: string, signal?: AbortSignal): Promise<Conversation>;
41
+ /**
42
+ * Saves `conversation` at the bucket-stripped `path`, resolving with the saved
43
+ * conversation. Used only for the single recovery save after the backend's
44
+ * terminal save was rejected with `401`; without it that failure shows the
45
+ * persistence warning immediately.
46
+ */
47
+ saveConversation?(path: string, conversation: Conversation): Promise<Conversation>;
48
+ }
49
+ /** Host-owned generation lifecycle, backing cross-navigation `AbortController` ownership. */
50
+ export interface ConversationGenerationLifecycle {
51
+ /** Starts (or replaces) tracking for a generation on `path`, returning its `AbortController`. */
52
+ startGeneration: (path: string, generationId: string) => AbortController;
53
+ /** Marks the generation identified by `path`/`generationId` complete, if it is still the active one. */
54
+ completeGeneration: (path: string, generationId: string) => void;
55
+ }
56
+ /** Optional host-owned client-channel connection, used to nudge tool-signin delivery. */
57
+ export interface ConversationStreamChannel {
58
+ channelId: string | null;
59
+ ensureConnected: () => void;
60
+ /** Resolves with a channel id, waiting for an in-flight subscribe if one isn't established yet, so a completion sent immediately after mount can still carry it. */
61
+ waitForChannel: (timeoutMs?: number) => Promise<string | null>;
62
+ /** Notifies the host that a generation just ended (completed or errored), so it can consider disconnecting an idle channel. */
63
+ notifyGenerationSettled?: () => void;
64
+ }
65
+ /** Optional host-owned overlay generation-lifecycle notifications. Each method is independently optional. */
66
+ export interface ConversationStreamOverlayNotifier {
67
+ notifyGenerationStart?: () => void;
68
+ notifyGenerationEnd?: () => void;
69
+ notifyStopGenerating?: () => void;
70
+ }
71
+ /** Shared mutable channel through which the displayed conversation's state is read/written. */
72
+ export interface ConversationStateAccessor {
73
+ setConversation: Dispatch<SetStateAction<Conversation | null>>;
74
+ conversationRef: MutableRefObject<Conversation | null>;
75
+ }
76
+ /** Parameters for {@link useConversationStream}. */
77
+ export interface UseConversationStreamParams {
78
+ conversationId: string | undefined;
79
+ state: ConversationStateAccessor;
80
+ transport: ConversationStreamTransport;
81
+ generation: ConversationGenerationLifecycle;
82
+ channel?: ConversationStreamChannel;
83
+ overlay?: ConversationStreamOverlayNotifier;
84
+ onStopError?: (error: Error) => void;
85
+ /**
86
+ * Message shown on the message bubble when the backend rejects a completion
87
+ * because this conversation is already generating — the case a second
88
+ * browser tab of the same session hits. Defaults to
89
+ * {@link DEFAULT_GENERATION_CONFLICT_MESSAGE}.
90
+ */
91
+ generationConflictMessage?: string;
92
+ /** Host-translated warning shown when the received answer could not be saved. */
93
+ generationPersistenceErrorMessage?: string;
94
+ /**
95
+ * Receives the original error of every failed stream. The hook shows only
96
+ * host-supplied or upstream-supplied text on the message bubble and hides
97
+ * transport detail (e.g. `Failed to fetch`), so this is where the host can
98
+ * still log or report the raw error.
99
+ */
100
+ onStreamError?: (error: Error) => void;
101
+ /**
102
+ * Coalesces the displayed chunk updates into one `setConversation` per
103
+ * animation frame. Chunks still reach the per-path buffer immediately, and
104
+ * any pending update is flushed before completion, error, stop, or a
105
+ * superseding generation. Defaults to `false` (one update per chunk).
106
+ */
107
+ batchChunksPerFrame?: boolean;
108
+ }
109
+ /** Per-start options of {@link UseConversationStreamResult.startStream}. */
110
+ export interface StartStreamOptions {
111
+ /**
112
+ * Treats a `GenerationConflictError` as "this turn is already being
113
+ * generated" — e.g. a page reloaded before that generation saved its start
114
+ * state — and joins the running generation instead of showing the conflict
115
+ * message, which remains the fallback. Only for a start that sends no new
116
+ * user text; a send the user typed must keep reporting the conflict.
117
+ */
118
+ resumeOnConflict?: boolean;
119
+ }
120
+ /** Return value of {@link useConversationStream}. */
121
+ export interface UseConversationStreamResult {
122
+ startStream: (conversationId: string, userContent: string, messageIndex: number, model: string, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, options?: StartStreamOptions) => void;
123
+ handleStop: () => void;
124
+ resumeIfAwaitingGeneration: (currentConversationId: string, conversation: Conversation) => void;
125
+ /** Restores the accumulated live assistant message when its conversation is loaded again before completion. */
126
+ restoreBufferedGeneration: (currentConversationId: string, conversation: Conversation) => Conversation;
127
+ isStreaming: boolean;
128
+ canStopStreaming: boolean;
129
+ /** Whether the displayed conversation's terminal read failed. */
130
+ hasConversationReloadError: boolean;
131
+ /** Whether a retry of that read is in flight. */
132
+ isReloadingConversation: boolean;
133
+ /** Retries the failed read without saving or regenerating the answer. */
134
+ retryConversationReload: () => Promise<void>;
135
+ }
136
+ //# sourceMappingURL=conversation-stream.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-stream.types.d.ts","sourceRoot":"","sources":["../../../src/conversation/useConversationStream/conversation-stream.types.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { Conversation, Message } from '@epam/ai-dial-chat-shared';
2
2
  import { Dispatch, MutableRefObject, SetStateAction } from 'react';
3
3
  import { BufferedGeneration } from './buffered-generation';
4
+ import { ConversationStreamTransport } from './conversation-stream.types';
4
5
  import { FrameScheduler } from './frame-scheduler';
5
- import { ConversationStreamTransport } from './useConversationStream';
6
6
  /**
7
7
  * True when the assistant message carries anything the backend's finalize save
8
8
  * would have written. Text alone is not enough to tell a finished response from
@@ -1 +1 @@
1
- {"version":3,"file":"generation-resume.d.ts","sourceRoot":"","sources":["../../../src/conversation/useConversationStream/generation-resume.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generation-resume.d.ts","sourceRoot":"","sources":["../../../src/conversation/useConversationStream/generation-resume.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { Conversation } from '@epam/ai-dial-chat-shared';
2
+ export declare const CHANNEL_WAIT_TIMEOUT_MS = 20000;
3
+ export declare const UNAUTHORIZED_STATUS = 401;
4
+ export declare const NOT_FOUND_STATUS = 404;
5
+ export declare const STOP_NOT_FOUND_GRACE_MS = 2000;
6
+ export declare const STOP_NOT_FOUND_POLL_MS = 100;
7
+ export declare const resolveStreamErrorMessage: (error: Error, generationConflictMessage: string, generationPersistenceErrorMessage: string) => string;
8
+ /** Generation id of the displayed conversation's pending background message, if any. */
9
+ export declare const findResumedBackgroundGenerationId: (conversation: Conversation | null) => string | undefined;
10
+ //# sourceMappingURL=stream-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-errors.d.ts","sourceRoot":"","sources":["../../../src/conversation/useConversationStream/stream-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAoB9D,eAAO,MAAM,uBAAuB,QAAQ,CAAC;AAG7C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAKpC,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAQ1C,eAAO,MAAM,yBAAyB,GACpC,OAAO,KAAK,EACZ,2BAA2B,MAAM,EACjC,mCAAmC,MAAM,KACxC,MAOF,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,iCAAiC,GAC5C,cAAc,YAAY,GAAG,IAAI,KAChC,MAAM,GAAG,SAMX,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { GenerationConflictError as e, GenerationPersistenceError as t, StreamUpstreamError as n } from "../create-chat-stream-api.js";
2
+ import { findPendingBackgroundMessageIndex as r } from "./generation-resume.js";
3
+ //#region src/conversation/useConversationStream/stream-errors.ts
4
+ var i = 2e4, a = 2e3, o = (r, i, a) => r instanceof t ? a : r instanceof e ? i : r instanceof n ? r.message : "", s = (e) => {
5
+ if (!e) return;
6
+ let t = r(e);
7
+ return t === -1 ? void 0 : e.messages[t].backgroundGeneration?.generationId;
8
+ };
9
+ //#endregion
10
+ export { i as CHANNEL_WAIT_TIMEOUT_MS, a as STOP_NOT_FOUND_GRACE_MS, s as findResumedBackgroundGenerationId, o as resolveStreamErrorMessage };
@@ -0,0 +1,51 @@
1
+ import { SendCompletionDtoModeEnum } from '@epam/ai-dial-chat-api-client';
2
+ import { MessageCustomContent } from '@epam/ai-dial-chat-shared';
3
+ import { MutableRefObject } from 'react';
4
+ import { ConversationGenerationLifecycle, ConversationStateAccessor, ConversationStreamChannel, ConversationStreamOverlayNotifier, ConversationStreamTransport, StartStreamOptions } from './conversation-stream.types';
5
+ import { FrameScheduler } from './frame-scheduler';
6
+ import { createResumeIfAwaitingGeneration } from './generation-resume';
7
+ import { useConversationReloadErrors } from './useConversationReloadErrors';
8
+ import { useStreamPathState } from './useStreamPathState';
9
+ type PathState = ReturnType<typeof useStreamPathState>;
10
+ type ReloadErrors = ReturnType<typeof useConversationReloadErrors>;
11
+ /** Everything a stream session reads from `useConversationStream`: its capabilities, messages, refs and mutators. */
12
+ export interface StreamSessionContext {
13
+ transport: ConversationStreamTransport;
14
+ channel: ConversationStreamChannel | undefined;
15
+ overlay: ConversationStreamOverlayNotifier | undefined;
16
+ startGeneration: ConversationGenerationLifecycle['startGeneration'];
17
+ completeGeneration: ConversationGenerationLifecycle['completeGeneration'];
18
+ setConversation: ConversationStateAccessor['setConversation'];
19
+ conversationRef: ConversationStateAccessor['conversationRef'];
20
+ generationConflictMessage: string;
21
+ generationPersistenceErrorMessage: string;
22
+ onStreamErrorRef: MutableRefObject<((error: Error) => void) | undefined>;
23
+ batchChunksPerFrameRef: MutableRefObject<boolean>;
24
+ mountedRef: MutableRefObject<boolean>;
25
+ frameScheduler: FrameScheduler;
26
+ resumeGeneration: ReturnType<typeof createResumeIfAwaitingGeneration>;
27
+ activeGenerationIdsRef: PathState['activeGenerationIdsRef'];
28
+ latestGenerationIdsRef: PathState['latestGenerationIdsRef'];
29
+ resumingPathsRef: PathState['resumingPathsRef'];
30
+ bufferedGenerationsRef: PathState['bufferedGenerationsRef'];
31
+ stoppedGenerationIdsRef: PathState['stoppedGenerationIdsRef'];
32
+ addStreamingPath: PathState['addStreamingPath'];
33
+ removeStreamingPath: PathState['removeStreamingPath'];
34
+ setPathStoppable: PathState['setPathStoppable'];
35
+ releaseActiveGeneration: PathState['releaseActiveGeneration'];
36
+ isPathDisplayed: PathState['isPathDisplayed'];
37
+ reportReloadError: ReloadErrors['reportReloadError'];
38
+ clearReloadError: ReloadErrors['clearReloadError'];
39
+ }
40
+ /**
41
+ * One generation of `useConversationStream.startStream`: registers the
42
+ * generation for its path, sends the completion, applies chunks (batched per
43
+ * frame when asked), reloads the persisted conversation on completion, shows
44
+ * a failure on the message, recovers an interrupted or conflicted start, and
45
+ * saves an answer whose terminal save was rejected with `401`. Each generation
46
+ * keeps its own closures over its id, path and abort controller; the hook
47
+ * passes a fresh context built from the same values its callback closes over.
48
+ */
49
+ export declare const runStreamSession: ({ transport, channel, overlay, startGeneration, completeGeneration, setConversation, conversationRef, generationConflictMessage, generationPersistenceErrorMessage, onStreamErrorRef, batchChunksPerFrameRef, mountedRef, frameScheduler, resumeGeneration, activeGenerationIdsRef, latestGenerationIdsRef, resumingPathsRef, bufferedGenerationsRef, stoppedGenerationIdsRef, addStreamingPath, removeStreamingPath, setPathStoppable, releaseActiveGeneration, isPathDisplayed, reportReloadError, clearReloadError, }: StreamSessionContext, currentConversationId: string, userContent: string, messageIndex: number, model: string, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, options?: StartStreamOptions) => void;
50
+ export {};
51
+ //# sourceMappingURL=stream-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-session.d.ts","sourceRoot":"","sources":["../../../src/conversation/useConversationStream/stream-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAa9C,OAAO,KAAK,EACV,+BAA+B,EAC/B,yBAAyB,EACzB,yBAAyB,EACzB,iCAAiC,EACjC,2BAA2B,EAC3B,kBAAkB,EAEnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,gCAAgC,EAKjC,MAAM,qBAAqB,CAAC;AAM7B,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEvD,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEnE,qHAAqH;AACrH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,2BAA2B,CAAC;IACvC,OAAO,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAC/C,OAAO,EAAE,iCAAiC,GAAG,SAAS,CAAC;IACvD,eAAe,EAAE,+BAA+B,CAAC,iBAAiB,CAAC,CAAC;IACpE,kBAAkB,EAAE,+BAA+B,CAAC,oBAAoB,CAAC,CAAC;IAC1E,eAAe,EAAE,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;IAC9D,eAAe,EAAE,yBAAyB,CAAC,iBAAiB,CAAC,CAAC;IAC9D,yBAAyB,EAAE,MAAM,CAAC;IAClC,iCAAiC,EAAE,MAAM,CAAC;IAC1C,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IACzE,sBAAsB,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAClD,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACtC,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,UAAU,CAAC,OAAO,gCAAgC,CAAC,CAAC;IACtE,sBAAsB,EAAE,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAC5D,sBAAsB,EAAE,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAC5D,gBAAgB,EAAE,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAChD,sBAAsB,EAAE,SAAS,CAAC,wBAAwB,CAAC,CAAC;IAC5D,uBAAuB,EAAE,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC9D,gBAAgB,EAAE,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAChD,mBAAmB,EAAE,SAAS,CAAC,qBAAqB,CAAC,CAAC;IACtD,gBAAgB,EAAE,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAChD,uBAAuB,EAAE,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC9D,eAAe,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC9C,iBAAiB,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;IACrD,gBAAgB,EAAE,YAAY,CAAC,kBAAkB,CAAC,CAAC;CACpD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAC3B,0fA2BG,oBAAoB,EACvB,uBAAuB,MAAM,EAC7B,aAAa,MAAM,EACnB,cAAc,MAAM,EACpB,OAAO,MAAM,EACb,gBAAgB,oBAAoB,EACpC,eAAe,MAAM,EACrB,OAAM,yBAA4D,EAClE,UAAS,kBAAuB,KAC/B,IA2iBF,CAAC"}
@@ -0,0 +1,179 @@
1
+ import { safeDecodeURI as e } from "../../shared/string-utils.js";
2
+ import { GenerationConflictError as t, GenerationPersistenceError as n, StreamInterruptedError as r } from "../create-chat-stream-api.js";
3
+ import { getConversationPath as i } from "./conversation-path.js";
4
+ import { applyChunkToMessages as a } from "./apply-chunk.js";
5
+ import { restoreBufferedMessage as o } from "./buffered-generation.js";
6
+ import { fetchConversationForRecovery as s, findPendingBackgroundMessageIndex as c, hasGeneratedPayload as l, isAwaitingGenerationResume as u } from "./generation-resume.js";
7
+ import { resolveStreamErrorMessage as ee } from "./stream-errors.js";
8
+ import { SendCompletionDtoModeEnum as d } from "@epam/ai-dial-chat-api-client";
9
+ import { MessageRole as f, generateUUID as p } from "@epam/ai-dial-chat-shared";
10
+ //#region src/conversation/useConversationStream/stream-session.ts
11
+ var m = ({ transport: m, channel: h, overlay: g, startGeneration: te, completeGeneration: _, setConversation: v, conversationRef: y, generationConflictMessage: ne, generationPersistenceErrorMessage: b, onStreamErrorRef: x, batchChunksPerFrameRef: re, mountedRef: S, frameScheduler: C, resumeGeneration: w, activeGenerationIdsRef: T, latestGenerationIdsRef: E, resumingPathsRef: D, bufferedGenerationsRef: O, stoppedGenerationIdsRef: k, addStreamingPath: ie, removeStreamingPath: A, setPathStoppable: j, releaseActiveGeneration: M, isPathDisplayed: N, reportReloadError: ae, clearReloadError: P }, F, I, L, oe, se, ce, R = d.Append, le = {}) => {
12
+ let z = ce ?? p(), B = i(F);
13
+ C.flush(B), D.current.delete(B), T.current.set(B, z), E.current.set(B, z), j(B, !0);
14
+ let V;
15
+ R === d.Regenerate ? V = L : R === d.Edit && (V = L - 1);
16
+ let H = () => E.current.get(B) !== z, U = te(B, z), W = y.current?.messages[L];
17
+ W ? O.current.set(B, {
18
+ generationId: z,
19
+ messageIndex: L,
20
+ message: W
21
+ }) : O.current.delete(B), ie(B), g?.notifyGenerationStart?.(), h?.ensureConnected();
22
+ let G = () => {
23
+ H() || A(B), M(B, z), _(B, z), h?.notifyGenerationSettled?.();
24
+ }, ue = () => {
25
+ if (H()) return;
26
+ let e = O.current.get(B);
27
+ e && e.generationId === z && (e.message = {
28
+ ...e.message,
29
+ streamErrorMessage: b
30
+ }, x.current?.(new n()), N(B) && v((t) => {
31
+ if (!t || H() || !N(B)) return t;
32
+ let n = o(t, e);
33
+ return y.current = n, n;
34
+ }));
35
+ }, K = O.current.get(B), q = () => S.current && !H() && O.current.get(B) === K, J = async () => {
36
+ if (q()) {
37
+ if (!N(B)) K && O.current.delete(B);
38
+ else try {
39
+ let t = await m.getConversation(e(F));
40
+ if (!q()) return;
41
+ if (P(B), !N(B)) {
42
+ K && O.current.delete(B);
43
+ return;
44
+ }
45
+ if (c(t) !== -1) {
46
+ w(F, t, { seedMessage: K?.message });
47
+ return;
48
+ }
49
+ if (K && K.messageIndex === t.messages.length - 1 && u(t) && l(K.message)) {
50
+ ue();
51
+ return;
52
+ }
53
+ K && O.current.delete(B), v(t), y.current = t;
54
+ } catch {
55
+ q() && ae(B, J, q);
56
+ }
57
+ }
58
+ }, Y = {
59
+ signal: U.signal,
60
+ onChunk: (e) => {
61
+ if (H()) return;
62
+ let t = O.current.get(B);
63
+ if (t?.generationId === z) {
64
+ let n = a([t.message], 0, e);
65
+ n && (t.message = n[0]);
66
+ }
67
+ N(B) && (re.current && t?.generationId === z ? C.schedule(B, () => {
68
+ let e = O.current.get(B);
69
+ if (H() || !N(B) || e?.generationId !== z) return;
70
+ let t = { ...e };
71
+ v((e) => {
72
+ if (!e) return e;
73
+ let n = o(e, t);
74
+ return y.current = n, n;
75
+ });
76
+ }) : v((t) => {
77
+ if (!t) return t;
78
+ let n = O.current.get(B), r;
79
+ if (n?.generationId === z) r = o(t, n);
80
+ else {
81
+ let n = a(t.messages, L, e);
82
+ if (!n) return t;
83
+ r = {
84
+ ...t,
85
+ messages: n
86
+ };
87
+ }
88
+ return y.current = r, r;
89
+ }));
90
+ },
91
+ onComplete: async () => {
92
+ C.flush(B), G(), k.current.has(z) ? k.current.delete(z) : H() || g?.notifyGenerationEnd?.(), await J();
93
+ },
94
+ onError: (e) => {
95
+ C.flush(B), x.current?.(e), e instanceof r && !H() ? fe(e) : e instanceof t && le.resumeOnConflict && !H() ? pe(e) : X(e);
96
+ }
97
+ }, X = (e) => {
98
+ let t = O.current.get(B), r = t?.generationId === z ? t : void 0;
99
+ r && !(e instanceof n) && O.current.delete(B), G(), r && m.saveConversation && e instanceof n && e.status === 401 ? de(e, r, m.saveConversation.bind(m)) : Z(e, r);
100
+ }, Z = (e, t) => {
101
+ if (t && e instanceof n && (t.message = {
102
+ ...t.message,
103
+ streamErrorMessage: b
104
+ }), H() || !N(B)) return;
105
+ let r = ee(e, ne, b);
106
+ v((e) => {
107
+ if (!e || H() || !N(B)) return e;
108
+ let n = t?.generationId === z ? o(e, t) : e, i = {
109
+ ...n,
110
+ messages: n.messages.map((e, t) => t === L ? {
111
+ ...e,
112
+ streamErrorMessage: r
113
+ } : e)
114
+ };
115
+ return y.current = i, i;
116
+ });
117
+ }, de = async (t, n, r) => {
118
+ let i = () => S.current && !H() && O.current.get(B) === n;
119
+ try {
120
+ let a = await m.getConversation(e(F));
121
+ if (!i()) return;
122
+ if (n.messageIndex !== a.messages.length - 1 || !u(a)) {
123
+ Z(t, n);
124
+ return;
125
+ }
126
+ let s = t.terminalMarker ? {
127
+ ...n,
128
+ message: {
129
+ ...n.message,
130
+ ...t.terminalMarker
131
+ }
132
+ } : n, c = await r(B, o(a, s));
133
+ if (!i() || (O.current.delete(B), !N(B))) return;
134
+ v(c), y.current = c;
135
+ } catch {
136
+ i() && Z(t, n);
137
+ }
138
+ }, Q = () => {
139
+ G(), k.current.has(z) ? k.current.delete(z) : H() || g?.notifyGenerationEnd?.();
140
+ }, fe = async (t) => {
141
+ D.current.add(B);
142
+ let n = await s(() => m.getConversation(e(F)), H);
143
+ $(n, t, !1);
144
+ }, pe = async (t) => {
145
+ T.current.get(B) === z && j(B, !1), D.current.add(B);
146
+ let n = await s(() => m.getConversation(e(F)), H, (e) => e.messages.length === L && e.messages.at(-1)?.role === f.User);
147
+ $(n, t, !0);
148
+ }, $ = (e, t, n) => {
149
+ if (H()) {
150
+ G();
151
+ return;
152
+ }
153
+ let r = e?.messages[e.messages.length - 1], i = e != null && e.messages.length - 1 === L && r?.role === f.Assistant, a = n && e != null && c(e) !== -1;
154
+ if (!e || !i && !a) {
155
+ D.current.delete(B), X(t);
156
+ return;
157
+ }
158
+ let o = O.current.get(B), s = o?.generationId === z ? o : void 0;
159
+ u(e) ? w(F, e, {
160
+ seedMessage: i ? s?.message : void 0,
161
+ skipDedupe: !0,
162
+ onSettled: Q
163
+ }) : (D.current.delete(B), s && O.current.delete(B), N(B) && (v(e), y.current = e), Q());
164
+ };
165
+ (async () => {
166
+ try {
167
+ let e = h?.channelId ?? await h?.waitForChannel(2e4) ?? void 0, t = k.current.has(z);
168
+ if (U.signal.aborted || H() || t) {
169
+ O.current.get(B)?.generationId === z && O.current.delete(B), H() || A(B), M(B, z), _(B, z), h?.notifyGenerationSettled?.(), t ? k.current.delete(z) : H() || g?.notifyGenerationEnd?.();
170
+ return;
171
+ }
172
+ m.streamCompletion(B, I, oe, Y, se, z, R, V, e);
173
+ } catch (e) {
174
+ Y.onError(e instanceof Error ? e : Error(String(e)));
175
+ }
176
+ })();
177
+ };
178
+ //#endregion
179
+ export { m as runStreamSession };
@@ -0,0 +1,25 @@
1
+ import { MutableRefObject } from 'react';
2
+ interface ConversationReloadFailure {
3
+ retry: () => Promise<void>;
4
+ isCurrent: () => boolean;
5
+ }
6
+ interface UseConversationReloadErrorsParams {
7
+ conversationId: string | undefined;
8
+ mountedRef: MutableRefObject<boolean>;
9
+ }
10
+ /**
11
+ * Terminal-read failures of `useConversationStream`, per conversation path:
12
+ * records a failed read with its retry, clears it on success or a new
13
+ * stream, and retries the displayed conversation's read once at a time. The
14
+ * ref is the source of truth; the state copies re-render the host.
15
+ */
16
+ export declare const useConversationReloadErrors: ({ conversationId, mountedRef, }: UseConversationReloadErrorsParams) => {
17
+ reloadFailuresRef: import('react').RefObject<Map<string, ConversationReloadFailure>>;
18
+ reloadFailures: Map<string, ConversationReloadFailure>;
19
+ retrying: Set<ConversationReloadFailure>;
20
+ clearReloadError: (path: string) => void;
21
+ reportReloadError: (path: string, retry: () => Promise<void>, isCurrent: () => boolean) => void;
22
+ retryConversationReload: () => Promise<void>;
23
+ };
24
+ export {};
25
+ //# sourceMappingURL=useConversationReloadErrors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConversationReloadErrors.d.ts","sourceRoot":"","sources":["../../../src/conversation/useConversationStream/useConversationReloadErrors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAiC,MAAM,OAAO,CAAC;AAG7E,UAAU,yBAAyB;IACjC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAED,UAAU,iCAAiC;IACzC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAEnC,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CACvC;AAED;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GAAI,iCAGzC,iCAAiC;;;;6BAWzB,MAAM;8BAWN,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC,aAAa,MAAM,OAAO;;CAmCtE,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { getConversationPath as e } from "./conversation-path.js";
2
+ import { useCallback as t, useRef as n, useState as r } from "react";
3
+ //#region src/conversation/useConversationStream/useConversationReloadErrors.ts
4
+ var i = ({ conversationId: i, mountedRef: a }) => {
5
+ let o = n(/* @__PURE__ */ new Map()), [s, c] = r(o.current), l = n(/* @__PURE__ */ new Set()), [u, d] = r(l.current);
6
+ return {
7
+ reloadFailuresRef: o,
8
+ reloadFailures: s,
9
+ retrying: u,
10
+ clearReloadError: t((e) => {
11
+ if (!o.current.has(e)) return;
12
+ let t = new Map(o.current);
13
+ t.delete(e), o.current = t, a.current && c(t);
14
+ }, [a]),
15
+ reportReloadError: t((e, t, n) => {
16
+ if (!a.current || !n()) return;
17
+ let r = new Map(o.current);
18
+ r.set(e, {
19
+ retry: t,
20
+ isCurrent: n
21
+ }), o.current = r, c(r);
22
+ }, [a]),
23
+ retryConversationReload: t(async () => {
24
+ if (!i) return;
25
+ let t = o.current.get(e(i));
26
+ if (t && t.isCurrent() && !l.current.has(t)) {
27
+ l.current.add(t), d(new Set(l.current));
28
+ try {
29
+ await t.retry();
30
+ } finally {
31
+ l.current.delete(t), a.current && d(new Set(l.current));
32
+ }
33
+ }
34
+ }, [i, a])
35
+ };
36
+ };
37
+ //#endregion
38
+ export { i as useConversationReloadErrors };
@@ -1,138 +1,5 @@
1
- import { SendCompletionDtoModeEnum } from '@epam/ai-dial-chat-api-client';
2
- import { Conversation, MessageCustomContent, StreamChunk } from '@epam/ai-dial-chat-shared';
3
- import { Dispatch, MutableRefObject, SetStateAction } from 'react';
4
- /** Options accepted by {@link ConversationStreamTransport.streamCompletion}. */
5
- export interface StreamCompletionOptions {
6
- onChunk: (chunk: StreamChunk) => void;
7
- onComplete: () => void | Promise<void>;
8
- onError: (error: Error) => void;
9
- signal: AbortSignal;
10
- }
11
- /**
12
- * Host-owned transport for the DIAL Core completion protocol. Implemented at
13
- * the app edge — never hardcodes an `/api` path, CSRF handling, or a
14
- * `server-api` import; the library only depends on this interface's shape.
15
- */
16
- export interface ConversationStreamTransport {
17
- /** Starts a completion; delivers chunks/completion/error through `options`, aborted via `options.signal`. */
18
- streamCompletion(path: string, message: string | undefined, model: string, options: StreamCompletionOptions, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, messageIndex?: number, clientChannelId?: string): void;
19
- /**
20
- * Requests the backend stop an active generation. `content` is the answer text shown
21
- * so far; the backend saves it for a generation whose text it does not hold.
22
- */
23
- stopCompletion(params: {
24
- generationId: string;
25
- path: string;
26
- content?: string;
27
- }): Promise<void>;
28
- /** Opens a stream of resource-update events for `path`, until aborted via `signal`. */
29
- watchConversation(path: string, signal: AbortSignal): Promise<ReadableStream<Uint8Array>>;
30
- /**
31
- * Attaches to an active generation's live replay: a `snapshot` event
32
- * carrying the assistant message as assembled so far, then a `chunk` event
33
- * per subsequent delta, then exactly one terminal event (`done`/`error`/
34
- * `stopped`), until aborted via `signal`. Rejects when no active
35
- * generation exists for `path` — including one that already finished —
36
- * so the caller can fall back to `watchConversation`.
37
- */
38
- attachToGeneration(path: string, signal: AbortSignal): Promise<ReadableStream<Uint8Array>>;
39
- /** Reloads the persisted conversation by its full (bucket-qualified) id. */
40
- getConversation(conversationId: string, signal?: AbortSignal): Promise<Conversation>;
41
- /**
42
- * Saves `conversation` at the bucket-stripped `path`, resolving with the saved
43
- * conversation. Used only for the single recovery save after the backend's
44
- * terminal save was rejected with `401`; without it that failure shows the
45
- * persistence warning immediately.
46
- */
47
- saveConversation?(path: string, conversation: Conversation): Promise<Conversation>;
48
- }
49
- /** Host-owned generation lifecycle, backing cross-navigation `AbortController` ownership. */
50
- export interface ConversationGenerationLifecycle {
51
- /** Starts (or replaces) tracking for a generation on `path`, returning its `AbortController`. */
52
- startGeneration: (path: string, generationId: string) => AbortController;
53
- /** Marks the generation identified by `path`/`generationId` complete, if it is still the active one. */
54
- completeGeneration: (path: string, generationId: string) => void;
55
- }
56
- /** Optional host-owned client-channel connection, used to nudge tool-signin delivery. */
57
- export interface ConversationStreamChannel {
58
- channelId: string | null;
59
- ensureConnected: () => void;
60
- /** Resolves with a channel id, waiting for an in-flight subscribe if one isn't established yet, so a completion sent immediately after mount can still carry it. */
61
- waitForChannel: (timeoutMs?: number) => Promise<string | null>;
62
- /** Notifies the host that a generation just ended (completed or errored), so it can consider disconnecting an idle channel. */
63
- notifyGenerationSettled?: () => void;
64
- }
65
- /** Optional host-owned overlay generation-lifecycle notifications. Each method is independently optional. */
66
- export interface ConversationStreamOverlayNotifier {
67
- notifyGenerationStart?: () => void;
68
- notifyGenerationEnd?: () => void;
69
- notifyStopGenerating?: () => void;
70
- }
71
- /** Shared mutable channel through which the displayed conversation's state is read/written. */
72
- export interface ConversationStateAccessor {
73
- setConversation: Dispatch<SetStateAction<Conversation | null>>;
74
- conversationRef: MutableRefObject<Conversation | null>;
75
- }
76
- /** Parameters for {@link useConversationStream}. */
77
- export interface UseConversationStreamParams {
78
- conversationId: string | undefined;
79
- state: ConversationStateAccessor;
80
- transport: ConversationStreamTransport;
81
- generation: ConversationGenerationLifecycle;
82
- channel?: ConversationStreamChannel;
83
- overlay?: ConversationStreamOverlayNotifier;
84
- onStopError?: (error: Error) => void;
85
- /**
86
- * Message shown on the message bubble when the backend rejects a completion
87
- * because this conversation is already generating — the case a second
88
- * browser tab of the same session hits. Defaults to
89
- * {@link DEFAULT_GENERATION_CONFLICT_MESSAGE}.
90
- */
91
- generationConflictMessage?: string;
92
- /** Host-translated warning shown when the received answer could not be saved. */
93
- generationPersistenceErrorMessage?: string;
94
- /**
95
- * Receives the original error of every failed stream. The hook shows only
96
- * host-supplied or upstream-supplied text on the message bubble and hides
97
- * transport detail (e.g. `Failed to fetch`), so this is where the host can
98
- * still log or report the raw error.
99
- */
100
- onStreamError?: (error: Error) => void;
101
- /**
102
- * Coalesces the displayed chunk updates into one `setConversation` per
103
- * animation frame. Chunks still reach the per-path buffer immediately, and
104
- * any pending update is flushed before completion, error, stop, or a
105
- * superseding generation. Defaults to `false` (one update per chunk).
106
- */
107
- batchChunksPerFrame?: boolean;
108
- }
109
- /** Per-start options of {@link UseConversationStreamResult.startStream}. */
110
- export interface StartStreamOptions {
111
- /**
112
- * Treats a `GenerationConflictError` as "this turn is already being
113
- * generated" — e.g. a page reloaded before that generation saved its start
114
- * state — and joins the running generation instead of showing the conflict
115
- * message, which remains the fallback. Only for a start that sends no new
116
- * user text; a send the user typed must keep reporting the conflict.
117
- */
118
- resumeOnConflict?: boolean;
119
- }
120
- /** Return value of {@link useConversationStream}. */
121
- export interface UseConversationStreamResult {
122
- startStream: (conversationId: string, userContent: string, messageIndex: number, model: string, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, options?: StartStreamOptions) => void;
123
- handleStop: () => void;
124
- resumeIfAwaitingGeneration: (currentConversationId: string, conversation: Conversation) => void;
125
- /** Restores the accumulated live assistant message when its conversation is loaded again before completion. */
126
- restoreBufferedGeneration: (currentConversationId: string, conversation: Conversation) => Conversation;
127
- isStreaming: boolean;
128
- canStopStreaming: boolean;
129
- /** Whether the displayed conversation's terminal read failed. */
130
- hasConversationReloadError: boolean;
131
- /** Whether a retry of that read is in flight. */
132
- isReloadingConversation: boolean;
133
- /** Retries the failed read without saving or regenerating the answer. */
134
- retryConversationReload: () => Promise<void>;
135
- }
1
+ import { UseConversationStreamParams, UseConversationStreamResult } from './conversation-stream.types';
2
+ export type { ConversationGenerationLifecycle, ConversationStateAccessor, ConversationStreamChannel, ConversationStreamOverlayNotifier, ConversationStreamTransport, StartStreamOptions, StreamCompletionOptions, UseConversationStreamParams, UseConversationStreamResult, } from './conversation-stream.types';
136
3
  /**
137
4
  * Owns completion-streaming state: per-path streaming/stoppable tracking,
138
5
  * stale-chunk rejection, cross-navigation live-message buffering,