@epam/ai-dial-chat-hooks 1.1.0-dev.433 → 1.1.0-dev.438
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/catalog/map-entity-details-to-catalog.js +1 -1
- package/conversation/conversation-transfer/import-conversation.js +27 -26
- package/conversation/create-chat-stream-api.js +45 -37
- package/conversation/useConversationHandlers/message-utils.js +6 -2
- package/conversation/useConversationHandlers/useConversationHandlers.js +3 -3
- package/conversation/useConversationImport/useConversationImport.js +1 -1
- package/conversation/useConversationStream/useConversationStream.js +108 -104
- package/conversation.d.ts +39 -1
- package/conversation.js +30 -30
- package/file-manager.d.ts +90 -0
- package/file-manager.js +16 -16
- package/files/attachment-canvas.js +111 -61
- package/files/dial-file-manager.model.js +4 -1
- package/index.d.ts +129 -1
- package/index.js +89 -89
- package/package.json +15 -15
|
@@ -256,7 +256,7 @@ var s = (e) => e >= 1e6 ? `${e / 1e6}M tokens` : e >= 1e3 ? `${e / 1e3}K tokens`
|
|
|
256
256
|
};
|
|
257
257
|
}, T = (e) => {
|
|
258
258
|
if (e == null) return;
|
|
259
|
-
let
|
|
259
|
+
let t = typeof e.unit == "string" ? e.unit : void 0, n = Object.entries(e).flatMap(([e, n]) => {
|
|
260
260
|
if (e === l || typeof n != "string" || n.trim() === "") return [];
|
|
261
261
|
let i = r(n, t);
|
|
262
262
|
return i == null ? [] : [{
|
|
@@ -1,48 +1,49 @@
|
|
|
1
1
|
import { collectAttachmentRefs as e } from "./attachment-refs.js";
|
|
2
2
|
import { resolveDialFileBucketAndPath as t } from "./dial-file-resolve.js";
|
|
3
|
-
import { generateUUID as n } from "@epam/ai-dial-chat-shared";
|
|
3
|
+
import { generateUUID as n, sanitizeConversationName as r, stripTrailingDots as i, truncateToUtf8Bytes as a } from "@epam/ai-dial-chat-shared";
|
|
4
4
|
//#region src/conversation/conversation-transfer/import-conversation.ts
|
|
5
|
-
var
|
|
5
|
+
var o = class extends Error {
|
|
6
6
|
constructor(e) {
|
|
7
7
|
super(e), this.name = "UnsupportedImportFormatError";
|
|
8
8
|
}
|
|
9
|
-
},
|
|
9
|
+
}, s = (e) => typeof e == "object" && !!e, c = 5, l = (e) => {
|
|
10
10
|
let t;
|
|
11
11
|
try {
|
|
12
12
|
t = JSON.parse(e);
|
|
13
13
|
} catch {
|
|
14
|
-
throw new
|
|
14
|
+
throw new o("Import file is not valid JSON");
|
|
15
15
|
}
|
|
16
|
-
if (!
|
|
16
|
+
if (!s(t) || t.version !== c || !Array.isArray(t.history)) throw new o("Import file is not a supported export format");
|
|
17
17
|
let n = Array.isArray(t.folders) ? t.folders : [];
|
|
18
18
|
return {
|
|
19
|
-
version:
|
|
19
|
+
version: c,
|
|
20
20
|
history: t.history,
|
|
21
21
|
folders: n
|
|
22
22
|
};
|
|
23
|
-
},
|
|
24
|
-
let
|
|
25
|
-
if (
|
|
26
|
-
let
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
].
|
|
23
|
+
}, u = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, d = "__", f = 255, p = "applications", m = /[-+]/, h = /^\d+$/, g = (e) => e ? e.split(m)[0].split(".").every((e) => h.test(e)) : !1, _ = (e) => i(a(e.replace(/\r\n|\r/g, "\n").split("\n").map((e) => r(e).trim()).filter(Boolean)[0] ?? "", f)).trimEnd(), v = (e, t, r) => {
|
|
24
|
+
let i = e.split(d);
|
|
25
|
+
if (i.length < 2) return [e, n()].join(d);
|
|
26
|
+
let a = r && g(i[1]) ? i.slice(0, 2) : i.slice(0, 1), o = a.length > 1 ? u.test(i[i.length - 1]) : i.length >= 3, s = i.slice(a.length, o ? -1 : void 0).join(d);
|
|
27
|
+
return [
|
|
28
|
+
...a,
|
|
29
|
+
t || s,
|
|
30
|
+
n()
|
|
31
|
+
].join(d);
|
|
32
|
+
}, y = "conversations/", b = (e) => e.startsWith(y) ? e.slice(14) : e, x = (e, t) => {
|
|
33
|
+
let n = b(e.id), r = n.split("/"), i = b(e.folderId), a = r[0], o = i.startsWith(`${a}/`) ? i.slice(a.length + 1).split("/").filter(Boolean) : [], s = r.slice(1).slice(o.length), c = s.slice(0, -1), l = s.at(-1) ?? r.at(-1) ?? n, u = [...o, ...c], d = _(e.name), f = v(l, d, u[0] === p), m = [...u, f].join("/"), h = o.length ? `${t}/${o.join("/")}` : t;
|
|
34
34
|
return {
|
|
35
35
|
conversation: {
|
|
36
36
|
...e,
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
...d ? { name: d } : {},
|
|
38
|
+
id: `${t}/${m}`,
|
|
39
|
+
folderId: h
|
|
39
40
|
},
|
|
40
|
-
subPath:
|
|
41
|
+
subPath: m
|
|
41
42
|
};
|
|
42
|
-
},
|
|
43
|
-
let t =
|
|
43
|
+
}, S = (e) => {
|
|
44
|
+
let t = b(e.folderId).split("/").filter(Boolean).slice(1);
|
|
44
45
|
return t.length ? t.join(" / ") : void 0;
|
|
45
|
-
},
|
|
46
|
+
}, C = (e) => e.map((e) => `"${e}"`).join(", "), w = (e, t) => ({
|
|
46
47
|
...e,
|
|
47
48
|
messages: e.messages.map((e) => {
|
|
48
49
|
let n = e.custom_content?.attachments;
|
|
@@ -64,12 +65,12 @@ var r = class extends Error {
|
|
|
64
65
|
}
|
|
65
66
|
} : e;
|
|
66
67
|
})
|
|
67
|
-
}),
|
|
68
|
+
}), T = (e) => e.split("/").pop() || e, E = (n, r, i) => {
|
|
68
69
|
let a = [], o = [];
|
|
69
70
|
for (let s of e(n)) {
|
|
70
71
|
let e = t(s.fileId);
|
|
71
72
|
if (!e) {
|
|
72
|
-
o.push(
|
|
73
|
+
o.push(T(s.fileId));
|
|
73
74
|
continue;
|
|
74
75
|
}
|
|
75
76
|
let n = e.path.split("/").pop() ?? e.path, c = r.get(e.path);
|
|
@@ -90,4 +91,4 @@ var r = class extends Error {
|
|
|
90
91
|
};
|
|
91
92
|
};
|
|
92
93
|
//#endregion
|
|
93
|
-
export {
|
|
94
|
+
export { o as UnsupportedImportFormatError, C as formatQuotedNameList, S as getFolderBreadcrumb, l as parseImportEnvelope, E as planAttachmentUploads, x as rebaseConversationId, w as rewriteAttachmentUrls };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
//#region src/conversation/create-chat-stream-api.ts
|
|
2
|
-
var e = { "Content-Type": "application/json" }, t =
|
|
2
|
+
var e = { "Content-Type": "application/json" }, t = 409, n = "A response is already being generated in this conversation. Wait for it to finish or stop it before sending another message.", r = class extends Error {
|
|
3
|
+
constructor(e = n) {
|
|
4
|
+
super(e), this.name = "GenerationConflictError";
|
|
5
|
+
}
|
|
6
|
+
}, i = (e, t, n) => {
|
|
3
7
|
let r = e.trim();
|
|
4
8
|
if (!r || r.startsWith(":") || !r.startsWith("data: ")) return;
|
|
5
9
|
let i = r.slice(6);
|
|
@@ -11,75 +15,79 @@ var e = { "Content-Type": "application/json" }, t = (e, t, n) => {
|
|
|
11
15
|
}
|
|
12
16
|
t(e);
|
|
13
17
|
} catch {}
|
|
14
|
-
},
|
|
15
|
-
let { completionsBasePath:
|
|
18
|
+
}, a = (n) => {
|
|
19
|
+
let { completionsBasePath: a } = n, o = (...e) => (n.fetchImpl ?? fetch)(...e);
|
|
16
20
|
return {
|
|
17
|
-
streamCompletion: (
|
|
18
|
-
let { onChunk:
|
|
21
|
+
streamCompletion: (s, c, l, u, d, f, p, m, h) => {
|
|
22
|
+
let { onChunk: g, onComplete: _, onError: v, signal: y } = u;
|
|
19
23
|
(async () => {
|
|
20
|
-
let
|
|
24
|
+
let u;
|
|
21
25
|
try {
|
|
22
26
|
let t = n.getTimezone?.();
|
|
23
|
-
|
|
27
|
+
u = await o(`${a}/completions`, {
|
|
24
28
|
method: "POST",
|
|
25
29
|
credentials: "include",
|
|
26
|
-
signal:
|
|
30
|
+
signal: y,
|
|
27
31
|
headers: {
|
|
28
32
|
...e,
|
|
29
33
|
...n.getCsrfToken() == null ? {} : { "X-CSRF-Token": n.getCsrfToken() },
|
|
30
34
|
...t ? { "X-Timezone": t } : {}
|
|
31
35
|
},
|
|
32
36
|
body: JSON.stringify({
|
|
33
|
-
path:
|
|
34
|
-
message:
|
|
35
|
-
model:
|
|
36
|
-
custom_content:
|
|
37
|
-
...
|
|
38
|
-
...
|
|
39
|
-
...
|
|
40
|
-
...
|
|
37
|
+
path: s,
|
|
38
|
+
message: c ?? "",
|
|
39
|
+
model: l,
|
|
40
|
+
custom_content: d || {},
|
|
41
|
+
...f != null && { generationId: f },
|
|
42
|
+
...p != null && { mode: p },
|
|
43
|
+
...m != null && { messageIndex: m },
|
|
44
|
+
...h != null && { clientChannelId: h }
|
|
41
45
|
})
|
|
42
46
|
});
|
|
43
47
|
} catch (e) {
|
|
44
48
|
if (e instanceof Error && e.name === "AbortError") return;
|
|
45
|
-
|
|
49
|
+
v(e instanceof Error ? e : Error(String(e)));
|
|
46
50
|
return;
|
|
47
51
|
}
|
|
48
|
-
let
|
|
49
|
-
if (
|
|
50
|
-
|
|
52
|
+
let b = u.headers.get("x-csrf-token");
|
|
53
|
+
if (b && n.setCsrfToken(b), !u.ok) {
|
|
54
|
+
if (u.status === t) {
|
|
55
|
+
v(new r());
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
v(/* @__PURE__ */ Error(`Stream request failed with status ${u.status}`));
|
|
51
59
|
return;
|
|
52
60
|
}
|
|
53
|
-
if (!
|
|
54
|
-
|
|
61
|
+
if (!u.body) {
|
|
62
|
+
v(/* @__PURE__ */ Error("No response body"));
|
|
55
63
|
return;
|
|
56
64
|
}
|
|
57
|
-
let
|
|
58
|
-
|
|
65
|
+
let x = u.body.getReader(), S = new TextDecoder(), C = "", w = !1, T = (e) => {
|
|
66
|
+
w = !0, v(e);
|
|
59
67
|
};
|
|
60
68
|
try {
|
|
61
69
|
for (;;) {
|
|
62
|
-
let { done: e, value:
|
|
70
|
+
let { done: e, value: t } = await x.read();
|
|
63
71
|
if (e) {
|
|
64
|
-
|
|
72
|
+
C.trim() && i(C, g, T);
|
|
65
73
|
break;
|
|
66
74
|
}
|
|
67
|
-
|
|
68
|
-
let
|
|
69
|
-
|
|
70
|
-
for (let e of
|
|
75
|
+
C += S.decode(t, { stream: !0 });
|
|
76
|
+
let n = C.split("\n");
|
|
77
|
+
C = n.pop() ?? "";
|
|
78
|
+
for (let e of n) i(e, g, T);
|
|
71
79
|
}
|
|
72
|
-
|
|
80
|
+
w || _();
|
|
73
81
|
} catch (e) {
|
|
74
82
|
if (e instanceof Error && e.name === "AbortError") return;
|
|
75
|
-
|
|
83
|
+
v(e instanceof Error ? e : Error(String(e)));
|
|
76
84
|
} finally {
|
|
77
|
-
|
|
85
|
+
x.releaseLock();
|
|
78
86
|
}
|
|
79
87
|
})();
|
|
80
88
|
},
|
|
81
89
|
stopCompletion: async (t) => {
|
|
82
|
-
let
|
|
90
|
+
let r = await o(`${a}/completions/stop`, {
|
|
83
91
|
method: "POST",
|
|
84
92
|
credentials: "include",
|
|
85
93
|
headers: {
|
|
@@ -87,10 +95,10 @@ var e = { "Content-Type": "application/json" }, t = (e, t, n) => {
|
|
|
87
95
|
...n.getCsrfToken() == null ? {} : { "X-CSRF-Token": n.getCsrfToken() }
|
|
88
96
|
},
|
|
89
97
|
body: JSON.stringify(t)
|
|
90
|
-
}),
|
|
91
|
-
if (
|
|
98
|
+
}), i = r.headers.get("x-csrf-token");
|
|
99
|
+
if (i && n.setCsrfToken(i), !r.ok) throw Error(`stopCompletion failed: ${r.status}`);
|
|
92
100
|
}
|
|
93
101
|
};
|
|
94
102
|
};
|
|
95
103
|
//#endregion
|
|
96
|
-
export { n as createChatStreamApi };
|
|
104
|
+
export { n as DEFAULT_GENERATION_CONFLICT_MESSAGE, r as GenerationConflictError, a as createChatStreamApi };
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { MessageRole as e } from "@epam/ai-dial-chat-shared";
|
|
1
2
|
//#region src/conversation/useConversationHandlers/message-utils.ts
|
|
2
|
-
var
|
|
3
|
+
var t = (e) => e != null && Object.keys(e).length > 0, n = (e, t, n, r) => {
|
|
3
4
|
if (t !== e.content || r.length > 0) return !0;
|
|
4
5
|
let i = e.custom_content?.attachments?.length ?? 0;
|
|
5
6
|
return n.length !== i;
|
|
7
|
+
}, r = (t) => t == null || t.role !== e.Assistant || !!t.wasStoppedByUser || t.streamErrorMessage != null, i = (e, t, i, a, o) => {
|
|
8
|
+
let s = e[t];
|
|
9
|
+
return s ? n(s, i, a, o) || r(e[t + 1]) : !1;
|
|
6
10
|
};
|
|
7
11
|
//#endregion
|
|
8
|
-
export {
|
|
12
|
+
export { t as hasActiveToolConfig, r as isAnswerIncomplete, n as isMessageChanged, i as shouldRerunGenerationOnEdit };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useAttachmentUpload as e } from "../useAttachmentUpload/useAttachmentUpload.js";
|
|
2
2
|
import { attachmentsToDtos as t } from "./attachment-to-dto.js";
|
|
3
3
|
import { createMessagePair as n } from "./message-factory.js";
|
|
4
|
-
import { hasActiveToolConfig as r,
|
|
4
|
+
import { hasActiveToolConfig as r, shouldRerunGenerationOnEdit as i } from "./message-utils.js";
|
|
5
5
|
import { getStarterSubmitText as a } from "./starter-option.js";
|
|
6
6
|
import { getConversationPath as o } from "../useConversationStream/conversation-path.js";
|
|
7
7
|
import { MessageRole as s, generateUUID as c } from "@epam/ai-dial-chat-shared";
|
|
@@ -211,7 +211,7 @@ var f = ({ conversation: f, conversationId: p, bucket: m, isStreaming: h, startS
|
|
|
211
211
|
let o = e;
|
|
212
212
|
if (o === -1 || f.messages[o].role !== s.User) return;
|
|
213
213
|
let l = f.messages[o];
|
|
214
|
-
if (!i(
|
|
214
|
+
if (!i(f.messages, o, n, r, a)) {
|
|
215
215
|
k((e) => {
|
|
216
216
|
let t = new Set(e);
|
|
217
217
|
return t.delete(o), t;
|
|
@@ -240,7 +240,7 @@ var f = ({ conversation: f, conversationId: p, bucket: m, isStreaming: h, startS
|
|
|
240
240
|
...f,
|
|
241
241
|
messages: D
|
|
242
242
|
};
|
|
243
|
-
|
|
243
|
+
v.current = O, _(O);
|
|
244
244
|
let A = S();
|
|
245
245
|
g(p, n, D.length - 1, A, C, c(), d.Edit), k(/* @__PURE__ */ new Set());
|
|
246
246
|
}, [
|
|
@@ -154,7 +154,7 @@ var b = 5, x = 5, S = (e) => /\.(dial|zip)$/i.test(e.name), C = (e) => new Promi
|
|
|
154
154
|
llmNamingDone: !0
|
|
155
155
|
} }
|
|
156
156
|
}, { signal: f }), f.aborted) return;
|
|
157
|
-
k.push(
|
|
157
|
+
k.push(n.name);
|
|
158
158
|
} catch (t) {
|
|
159
159
|
if (f.aborted) return;
|
|
160
160
|
if (F(t)) {
|
|
@@ -1,148 +1,152 @@
|
|
|
1
1
|
import { safeDecodeURI as e } from "../../shared/string-utils.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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";
|
|
9
10
|
//#region src/conversation/useConversationStream/useConversationStream.ts
|
|
10
|
-
var
|
|
11
|
-
let [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}, [
|
|
15
|
-
let
|
|
16
|
-
|
|
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) => {
|
|
17
18
|
if (t.has(e)) return t;
|
|
18
19
|
let n = new Set(t);
|
|
19
20
|
return n.add(e), n;
|
|
20
21
|
});
|
|
21
|
-
}, []),
|
|
22
|
-
|
|
22
|
+
}, []), R = c((e) => {
|
|
23
|
+
E((t) => {
|
|
23
24
|
if (!t.has(e)) return t;
|
|
24
25
|
let n = new Set(t);
|
|
25
26
|
return n.delete(e), n;
|
|
26
27
|
});
|
|
27
|
-
}, []),
|
|
28
|
-
let
|
|
29
|
-
|
|
30
|
-
let
|
|
31
|
-
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
generationId:
|
|
35
|
-
messageIndex:
|
|
36
|
-
message:
|
|
37
|
-
}) :
|
|
38
|
-
let
|
|
39
|
-
signal:
|
|
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,
|
|
40
41
|
onChunk: (e) => {
|
|
41
|
-
if (
|
|
42
|
-
let t =
|
|
43
|
-
if (t?.generationId ===
|
|
44
|
-
let
|
|
45
|
-
|
|
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);
|
|
46
|
+
n && (t.message = n[0]);
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
+
I(C) && g((t) => {
|
|
48
49
|
if (!t) return t;
|
|
49
|
-
let
|
|
50
|
-
if (
|
|
50
|
+
let n = M.current.get(C), r;
|
|
51
|
+
if (n?.generationId === h) r = a(t, n);
|
|
51
52
|
else {
|
|
52
|
-
let
|
|
53
|
-
if (!
|
|
54
|
-
|
|
53
|
+
let n = i(t.messages, c, e);
|
|
54
|
+
if (!n) return t;
|
|
55
|
+
r = {
|
|
55
56
|
...t,
|
|
56
|
-
messages:
|
|
57
|
+
messages: n
|
|
57
58
|
};
|
|
58
59
|
}
|
|
59
|
-
return
|
|
60
|
+
return _.current = r, r;
|
|
60
61
|
});
|
|
61
62
|
},
|
|
62
63
|
onComplete: async () => {
|
|
63
|
-
if (
|
|
64
|
-
let
|
|
65
|
-
if (!
|
|
66
|
-
|
|
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;
|
|
67
68
|
} catch {}
|
|
68
69
|
},
|
|
69
70
|
onError: (e) => {
|
|
70
|
-
let t =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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)
|
|
79
82
|
};
|
|
80
|
-
return
|
|
83
|
+
return _.current = n, n;
|
|
81
84
|
});
|
|
82
85
|
}
|
|
83
86
|
};
|
|
84
87
|
(async () => {
|
|
85
88
|
try {
|
|
86
|
-
let e =
|
|
87
|
-
|
|
89
|
+
let e = x?.channelId ?? await x?.waitForChannel(m) ?? void 0;
|
|
90
|
+
v.streamCompletion(C, o, l, F, u, h, f, T, e);
|
|
88
91
|
} catch (e) {
|
|
89
|
-
|
|
92
|
+
F.onError(e instanceof Error ? e : Error(String(e)));
|
|
90
93
|
}
|
|
91
94
|
})();
|
|
92
95
|
}, [
|
|
93
|
-
|
|
94
|
-
v,
|
|
95
|
-
N,
|
|
96
|
-
P,
|
|
97
|
-
M,
|
|
98
|
-
y?.channelId,
|
|
99
|
-
y?.ensureConnected,
|
|
100
|
-
y?.waitForChannel,
|
|
101
|
-
y?.notifyGenerationSettled,
|
|
96
|
+
y,
|
|
102
97
|
b,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
98
|
+
L,
|
|
99
|
+
R,
|
|
100
|
+
I,
|
|
101
|
+
x?.channelId,
|
|
102
|
+
x?.ensureConnected,
|
|
103
|
+
x?.waitForChannel,
|
|
104
|
+
x?.notifyGenerationSettled,
|
|
105
|
+
S,
|
|
106
|
+
v,
|
|
107
|
+
w
|
|
108
|
+
]), B = c((e, t) => {
|
|
109
|
+
let n = r(e), i = M.current.get(n);
|
|
110
|
+
return i ? a(t, i) : t;
|
|
111
|
+
}, []), V = c(() => {
|
|
112
|
+
let e = k.current;
|
|
113
|
+
if (!e || !h) return;
|
|
114
|
+
let t = r(h);
|
|
115
|
+
A.current === t && (N.current.add(e), S?.notifyStopGenerating?.(), v.stopCompletion({
|
|
112
116
|
generationId: e,
|
|
113
|
-
path:
|
|
117
|
+
path: t
|
|
114
118
|
}).catch((e) => {
|
|
115
119
|
let t = e instanceof Error ? e : Error(String(e));
|
|
116
|
-
|
|
120
|
+
C?.(t);
|
|
117
121
|
}));
|
|
118
122
|
}, [
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
]),
|
|
124
|
-
transport:
|
|
125
|
-
setConversation:
|
|
126
|
-
conversationRef:
|
|
127
|
-
resumingPathsRef:
|
|
128
|
-
bufferedGenerationsRef:
|
|
129
|
-
addStreamingPath:
|
|
130
|
-
removeStreamingPath:
|
|
131
|
-
isPathDisplayed:
|
|
123
|
+
h,
|
|
124
|
+
C,
|
|
125
|
+
S,
|
|
126
|
+
v
|
|
127
|
+
]), H = u(() => o({
|
|
128
|
+
transport: v,
|
|
129
|
+
setConversation: g,
|
|
130
|
+
conversationRef: _,
|
|
131
|
+
resumingPathsRef: j,
|
|
132
|
+
bufferedGenerationsRef: M,
|
|
133
|
+
addStreamingPath: L,
|
|
134
|
+
removeStreamingPath: R,
|
|
135
|
+
isPathDisplayed: I
|
|
132
136
|
}), [
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
]),
|
|
137
|
+
L,
|
|
138
|
+
R,
|
|
139
|
+
I,
|
|
140
|
+
v
|
|
141
|
+
]), U = h != null && T.has(r(h)), W = h == null ? null : r(h);
|
|
138
142
|
return {
|
|
139
|
-
startStream:
|
|
140
|
-
handleStop:
|
|
141
|
-
resumeIfAwaitingGeneration:
|
|
142
|
-
restoreBufferedGeneration:
|
|
143
|
-
isStreaming:
|
|
144
|
-
canStopStreaming:
|
|
143
|
+
startStream: z,
|
|
144
|
+
handleStop: V,
|
|
145
|
+
resumeIfAwaitingGeneration: H,
|
|
146
|
+
restoreBufferedGeneration: B,
|
|
147
|
+
isStreaming: U,
|
|
148
|
+
canStopStreaming: W != null && D === W
|
|
145
149
|
};
|
|
146
150
|
};
|
|
147
151
|
//#endregion
|
|
148
|
-
export {
|
|
152
|
+
export { h as useConversationStream };
|
package/conversation.d.ts
CHANGED
|
@@ -308,6 +308,9 @@ export declare const createDeploymentChangedMessage: (previousDeploymentId: stri
|
|
|
308
308
|
/** Builds a user message and its empty assistant placeholder, both timestamped `now`. */
|
|
309
309
|
export declare const createMessagePair: (content: string, customContent?: MessageCustomContent, deploymentId?: string | null) => MessagePair;
|
|
310
310
|
|
|
311
|
+
/** Fallback text of a {@link GenerationConflictError} when no message is supplied. */
|
|
312
|
+
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.";
|
|
313
|
+
|
|
311
314
|
/**
|
|
312
315
|
* Derives the row-action visibility state for a single conversation item.
|
|
313
316
|
*
|
|
@@ -331,6 +334,17 @@ export declare const deriveConversationRowActionState: (item: Pick<ConversationL
|
|
|
331
334
|
*/
|
|
332
335
|
export declare const formatAppVersion: (version: string) => string;
|
|
333
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Reported through `onError` when the backend rejects a completion because
|
|
339
|
+
* another generation is already active on the same conversation — what a
|
|
340
|
+
* second browser tab of the same session hits when it submits into a
|
|
341
|
+
* conversation the first tab is still generating. Distinct from a generic
|
|
342
|
+
* transport failure so callers can present it as an expected state.
|
|
343
|
+
*/
|
|
344
|
+
export declare class GenerationConflictError extends Error {
|
|
345
|
+
constructor(message?: string);
|
|
346
|
+
}
|
|
347
|
+
|
|
334
348
|
/**
|
|
335
349
|
* Strips the leading bucket segment from a conversation id and decodes any
|
|
336
350
|
* already-percent-encoded remainder back to raw, so callers that re-encode
|
|
@@ -453,6 +467,13 @@ export declare const hasAnnouncementContent: (content: AnnouncementContent) => b
|
|
|
453
467
|
*/
|
|
454
468
|
export declare const hasStructuredAnnouncement: ({ title, description, }: AnnouncementContent) => boolean;
|
|
455
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Whether the assistant answer that follows an edited user message is missing
|
|
472
|
+
* or incomplete: no message at all, a non-assistant message, an answer the
|
|
473
|
+
* user stopped, or one that ended with a stream error.
|
|
474
|
+
*/
|
|
475
|
+
export declare const isAnswerIncomplete: (answer: Message | undefined) => boolean;
|
|
476
|
+
|
|
456
477
|
/**
|
|
457
478
|
* True when the conversation's last message is an unresolved assistant
|
|
458
479
|
* placeholder: the backend only persists a conversation at generation start
|
|
@@ -561,6 +582,16 @@ export declare const sanitizeAnnouncementMessageHtml: (html: string) => string;
|
|
|
561
582
|
/** Sanitizes footer message HTML to the allowed tag/attribute set. */
|
|
562
583
|
export declare const sanitizeFooterHtml: (html: string) => string;
|
|
563
584
|
|
|
585
|
+
/**
|
|
586
|
+
* Whether submitting an edit has to re-run the generation.
|
|
587
|
+
*
|
|
588
|
+
* True whenever the message itself changed, and also when it is unchanged but
|
|
589
|
+
* its answer never completed — after stopping a generation the user submits
|
|
590
|
+
* the same message precisely to get a full answer, so treating that as "no
|
|
591
|
+
* change" would leave the conversation stuck with the partial one.
|
|
592
|
+
*/
|
|
593
|
+
export declare const shouldRerunGenerationOnEdit: (messages: Message[], messageIndex: number, newText: string, keptDisplayAttachments: DisplayAttachment[], newAttachments: Attachment[]) => boolean;
|
|
594
|
+
|
|
564
595
|
/** True when the first user/assistant exchange is complete and LLM naming may still run. */
|
|
565
596
|
export declare const shouldWatchForDisplayNameUpdate: (conversation: Conversation) => boolean;
|
|
566
597
|
|
|
@@ -847,7 +878,7 @@ export declare interface UseConversationPanelItemsResolvers {
|
|
|
847
878
|
* `transport`/`generation`/`channel`/`overlay` capabilities rather than any
|
|
848
879
|
* app context or `server-api` import.
|
|
849
880
|
*/
|
|
850
|
-
export declare const useConversationStream: ({ conversationId, state: { setConversation, conversationRef }, transport, generation: { startGeneration, completeGeneration }, channel, overlay, onStopError, }: UseConversationStreamParams) => UseConversationStreamResult;
|
|
881
|
+
export declare const useConversationStream: ({ conversationId, state: { setConversation, conversationRef }, transport, generation: { startGeneration, completeGeneration }, channel, overlay, onStopError, generationConflictMessage, }: UseConversationStreamParams) => UseConversationStreamResult;
|
|
851
882
|
|
|
852
883
|
/** Parameters for {@link useConversationStream}. */
|
|
853
884
|
export declare interface UseConversationStreamParams {
|
|
@@ -858,6 +889,13 @@ export declare interface UseConversationStreamParams {
|
|
|
858
889
|
channel?: ConversationStreamChannel;
|
|
859
890
|
overlay?: ConversationStreamOverlayNotifier;
|
|
860
891
|
onStopError?: (error: Error) => void;
|
|
892
|
+
/**
|
|
893
|
+
* Message shown on the message bubble when the backend rejects a completion
|
|
894
|
+
* because this conversation is already generating — the case a second
|
|
895
|
+
* browser tab of the same session hits. Defaults to
|
|
896
|
+
* {@link DEFAULT_GENERATION_CONFLICT_MESSAGE}.
|
|
897
|
+
*/
|
|
898
|
+
generationConflictMessage?: string;
|
|
861
899
|
}
|
|
862
900
|
|
|
863
901
|
/** Return value of {@link useConversationStream}. */
|