@deuz-sdk/react 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +420 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +197 -10
- package/dist/index.d.ts +197 -10
- package/dist/index.js +422 -138
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -19,50 +19,166 @@ function foldPart(turn, part) {
|
|
|
19
19
|
return next;
|
|
20
20
|
}
|
|
21
21
|
var defaultGenerateId = () => typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `deuz-ui-${Math.random().toString(36).slice(2)}`;
|
|
22
|
+
async function partsFromFiles(files) {
|
|
23
|
+
if (!files) return [];
|
|
24
|
+
const list = Array.from(
|
|
25
|
+
files
|
|
26
|
+
);
|
|
27
|
+
return list.length === 0 ? [] : chat.filesToImageParts(list);
|
|
28
|
+
}
|
|
29
|
+
function loadCursor(resume) {
|
|
30
|
+
try {
|
|
31
|
+
return resume.cursor?.load();
|
|
32
|
+
} catch {
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function saveCursor(resume, id) {
|
|
37
|
+
try {
|
|
38
|
+
resume.cursor?.save(id);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
22
42
|
function useChat(options) {
|
|
23
43
|
const genId = options.generateId ?? defaultGenerateId;
|
|
24
|
-
const [
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
const [snapshot, setSnapshot] = react.useState(() => ({
|
|
45
|
+
history: {
|
|
46
|
+
ui: chat.uiFromMessages(options.initialMessages ?? [], genId),
|
|
47
|
+
canonical: options.initialMessages ? [...options.initialMessages] : []
|
|
48
|
+
},
|
|
49
|
+
dataParts: [],
|
|
50
|
+
citations: [],
|
|
51
|
+
activity: []
|
|
52
|
+
}));
|
|
27
53
|
const [status, setStatus] = react.useState("idle");
|
|
28
54
|
const [error, setError] = react.useState(void 0);
|
|
29
55
|
const [pendingApprovals, setPendingApprovals] = react.useState([]);
|
|
30
|
-
const [
|
|
31
|
-
const
|
|
32
|
-
void 0
|
|
33
|
-
);
|
|
34
|
-
const [dataParts, setDataParts] = react.useState([]);
|
|
35
|
-
const [citations, setCitations] = react.useState([]);
|
|
36
|
-
const [plan, setPlan] = react.useState(void 0);
|
|
37
|
-
const [activity, setActivity] = react.useState([]);
|
|
38
|
-
const uiRef = react.useRef(messages);
|
|
39
|
-
const canonicalRef = react.useRef(
|
|
40
|
-
options.initialMessages ? [...options.initialMessages] : []
|
|
41
|
-
);
|
|
56
|
+
const [pendingToolCalls, setPendingToolCalls] = react.useState([]);
|
|
57
|
+
const snapshotRef = react.useRef(snapshot);
|
|
42
58
|
const turnRef = react.useRef(void 0);
|
|
59
|
+
const parkedTurnRef = react.useRef(void 0);
|
|
60
|
+
const clientToolsRef = react.useRef([]);
|
|
61
|
+
const toolResultsRef = react.useRef(
|
|
62
|
+
[]
|
|
63
|
+
);
|
|
43
64
|
const approvalsRef = react.useRef([]);
|
|
44
65
|
const verdictsRef = react.useRef([]);
|
|
45
66
|
const abortRef = react.useRef(null);
|
|
46
67
|
const lastEventIdRef = react.useRef(options.resume?.lastEventId);
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
const placedIdRef = react.useRef(void 0);
|
|
69
|
+
const throttleMs = options.throttleMs ?? 0;
|
|
70
|
+
const flushTimerRef = react.useRef(void 0);
|
|
71
|
+
const dirtyRef = react.useRef(false);
|
|
72
|
+
const publish = react.useCallback(() => {
|
|
73
|
+
flushTimerRef.current = void 0;
|
|
74
|
+
dirtyRef.current = false;
|
|
75
|
+
setSnapshot(snapshotRef.current);
|
|
50
76
|
}, []);
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
const commit = react.useCallback(
|
|
78
|
+
(next) => {
|
|
79
|
+
snapshotRef.current = next;
|
|
80
|
+
if (throttleMs <= 0) {
|
|
81
|
+
publish();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
dirtyRef.current = true;
|
|
85
|
+
if (flushTimerRef.current === void 0) {
|
|
86
|
+
flushTimerRef.current = setTimeout(publish, throttleMs);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
[throttleMs, publish]
|
|
90
|
+
);
|
|
91
|
+
const flush = react.useCallback(() => {
|
|
92
|
+
if (flushTimerRef.current !== void 0) {
|
|
93
|
+
clearTimeout(flushTimerRef.current);
|
|
94
|
+
flushTimerRef.current = void 0;
|
|
59
95
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
if (dirtyRef.current) publish();
|
|
97
|
+
}, [publish]);
|
|
98
|
+
react.useEffect(
|
|
99
|
+
() => () => {
|
|
100
|
+
if (flushTimerRef.current !== void 0) clearTimeout(flushTimerRef.current);
|
|
101
|
+
},
|
|
102
|
+
[]
|
|
103
|
+
);
|
|
104
|
+
const syncTurn = react.useCallback(
|
|
105
|
+
(turn) => {
|
|
106
|
+
const previous = snapshotRef.current;
|
|
107
|
+
const placed = placedIdRef.current;
|
|
108
|
+
const ui = previous.history.ui;
|
|
109
|
+
const index = placed === void 0 ? -1 : ui.findIndex((m) => m.id === placed);
|
|
110
|
+
placedIdRef.current = turn.message.id;
|
|
111
|
+
commit({
|
|
112
|
+
...previous,
|
|
113
|
+
history: {
|
|
114
|
+
// NEW array either way — the turn's message is never spliced in place.
|
|
115
|
+
ui: index === -1 ? [...ui, turn.message] : [...ui.slice(0, index), turn.message, ...ui.slice(index + 1)],
|
|
116
|
+
canonical: previous.history.canonical
|
|
117
|
+
},
|
|
118
|
+
// `cost` is CUMULATIVE for the chat, so an absent reading keeps the
|
|
119
|
+
// previous one (1.7 behaviour, verbatim); every other readout is
|
|
120
|
+
// turn-scoped and resets when the turn does.
|
|
121
|
+
cost: turn.costUsd !== void 0 ? {
|
|
122
|
+
costUsd: turn.costUsd,
|
|
123
|
+
...turn.cacheSavingsUsd !== void 0 ? { cacheSavingsUsd: turn.cacheSavingsUsd } : {}
|
|
124
|
+
} : previous.cost,
|
|
125
|
+
budgetExceeded: turn.budgetExceeded,
|
|
126
|
+
dataParts: turn.dataParts,
|
|
127
|
+
citations: turn.citations,
|
|
128
|
+
plan: turn.plan,
|
|
129
|
+
activity: turn.activity,
|
|
130
|
+
verifications: turn.verifications,
|
|
131
|
+
warnings: turn.warnings,
|
|
132
|
+
falseFinishes: turn.falseFinishes,
|
|
133
|
+
subAgents: turn.subAgents,
|
|
134
|
+
steps: turn.steps,
|
|
135
|
+
usage: turn.usage,
|
|
136
|
+
finishReason: turn.finishReason
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
[commit]
|
|
140
|
+
);
|
|
141
|
+
const appendCanonical = react.useCallback(
|
|
142
|
+
(message) => {
|
|
143
|
+
const previous = snapshotRef.current;
|
|
144
|
+
commit({
|
|
145
|
+
...previous,
|
|
146
|
+
history: {
|
|
147
|
+
ui: previous.history.ui,
|
|
148
|
+
canonical: [...previous.history.canonical, message]
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
[commit]
|
|
153
|
+
);
|
|
154
|
+
const clearGates = react.useCallback(() => {
|
|
155
|
+
approvalsRef.current = [];
|
|
156
|
+
verdictsRef.current = [];
|
|
157
|
+
clientToolsRef.current = [];
|
|
158
|
+
toolResultsRef.current = [];
|
|
159
|
+
parkedTurnRef.current = void 0;
|
|
160
|
+
setPendingApprovals([]);
|
|
161
|
+
setPendingToolCalls([]);
|
|
65
162
|
}, []);
|
|
163
|
+
const fold = react.useCallback(
|
|
164
|
+
(turn, part) => {
|
|
165
|
+
const next = foldPart(turn, part);
|
|
166
|
+
const onData = options.onData;
|
|
167
|
+
if (onData && typeof part.type === "string" && part.type.startsWith("data-")) {
|
|
168
|
+
const data = part;
|
|
169
|
+
try {
|
|
170
|
+
onData({
|
|
171
|
+
name: data.type.slice("data-".length),
|
|
172
|
+
...typeof data.id === "string" ? { id: data.id } : {},
|
|
173
|
+
payload: data.payload
|
|
174
|
+
});
|
|
175
|
+
} catch {
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return next;
|
|
179
|
+
},
|
|
180
|
+
[options.onData]
|
|
181
|
+
);
|
|
66
182
|
const stop = react.useCallback(() => {
|
|
67
183
|
abortRef.current?.abort();
|
|
68
184
|
}, []);
|
|
@@ -79,7 +195,7 @@ function useChat(options) {
|
|
|
79
195
|
method: "POST",
|
|
80
196
|
headers: { "content-type": "application/json", ...options.headers },
|
|
81
197
|
body: JSON.stringify({
|
|
82
|
-
messages:
|
|
198
|
+
messages: snapshotRef.current.history.canonical,
|
|
83
199
|
...options.chatId !== void 0 ? { chatId: options.chatId } : {},
|
|
84
200
|
...approvalResponses?.length ? { approvalResponses } : {},
|
|
85
201
|
...options.body
|
|
@@ -89,15 +205,25 @@ function useChat(options) {
|
|
|
89
205
|
approvalResponses = void 0;
|
|
90
206
|
let turn = chat.createAssistantTurn(genId());
|
|
91
207
|
turnRef.current = turn;
|
|
92
|
-
|
|
208
|
+
placedIdRef.current = void 0;
|
|
93
209
|
syncTurn(turn);
|
|
94
|
-
|
|
95
|
-
|
|
210
|
+
flush();
|
|
211
|
+
for await (const part of ui.readDeuzStream(res, {
|
|
212
|
+
...options.onHttpError ? { onHttpError: options.onHttpError } : {}
|
|
213
|
+
})) {
|
|
214
|
+
turn = fold(turn, part);
|
|
215
|
+
turnRef.current = turn;
|
|
216
|
+
syncTurn(turn);
|
|
217
|
+
}
|
|
218
|
+
const sealed = chat.sealAssistantTurn(turn);
|
|
219
|
+
if (sealed !== turn) {
|
|
220
|
+
turn = sealed;
|
|
96
221
|
turnRef.current = turn;
|
|
97
222
|
syncTurn(turn);
|
|
98
223
|
}
|
|
224
|
+
flush();
|
|
99
225
|
if (turn.error !== void 0) throw new Error(turn.error);
|
|
100
|
-
|
|
226
|
+
appendCanonical(chat.assistantMessageFromTurn(turn));
|
|
101
227
|
turnRef.current = void 0;
|
|
102
228
|
if (turn.approvals.length > 0) {
|
|
103
229
|
approvalsRef.current = turn.approvals;
|
|
@@ -108,7 +234,14 @@ function useChat(options) {
|
|
|
108
234
|
const toolCalls = turn.message.toolCalls ?? [];
|
|
109
235
|
const serverResults = new Set(turn.serverResults);
|
|
110
236
|
const clientPending = toolCalls.filter((t) => !serverResults.has(t.toolCallId));
|
|
111
|
-
if (clientPending.length === 0
|
|
237
|
+
if (clientPending.length === 0) return;
|
|
238
|
+
if (!options.onToolCall) {
|
|
239
|
+
parkedTurnRef.current = turn;
|
|
240
|
+
clientToolsRef.current = clientPending;
|
|
241
|
+
toolResultsRef.current = [];
|
|
242
|
+
setPendingToolCalls(clientPending);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
112
245
|
const results = [];
|
|
113
246
|
for (const call of clientPending) {
|
|
114
247
|
try {
|
|
@@ -137,10 +270,18 @@ function useChat(options) {
|
|
|
137
270
|
}
|
|
138
271
|
syncTurn(turn);
|
|
139
272
|
}
|
|
140
|
-
|
|
273
|
+
appendCanonical(chat.clientToolResultMessage(results));
|
|
141
274
|
}
|
|
142
275
|
} catch (err) {
|
|
143
|
-
if (controller.signal.aborted)
|
|
276
|
+
if (controller.signal.aborted) {
|
|
277
|
+
const open = turnRef.current;
|
|
278
|
+
if (open) {
|
|
279
|
+
const sealed = chat.sealAssistantTurn(open);
|
|
280
|
+
turnRef.current = sealed;
|
|
281
|
+
if (sealed !== open) syncTurn(sealed);
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
144
285
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
145
286
|
setError(e);
|
|
146
287
|
setStatus("error");
|
|
@@ -148,6 +289,7 @@ function useChat(options) {
|
|
|
148
289
|
return;
|
|
149
290
|
} finally {
|
|
150
291
|
setStatus((s) => s === "error" ? s : "idle");
|
|
292
|
+
flush();
|
|
151
293
|
}
|
|
152
294
|
},
|
|
153
295
|
[
|
|
@@ -159,47 +301,112 @@ function useChat(options) {
|
|
|
159
301
|
options.generateId,
|
|
160
302
|
options.onToolCall,
|
|
161
303
|
options.onError,
|
|
162
|
-
|
|
304
|
+
options.onHttpError,
|
|
305
|
+
appendCanonical,
|
|
306
|
+
fold,
|
|
307
|
+
flush,
|
|
163
308
|
syncTurn
|
|
164
309
|
]
|
|
165
310
|
);
|
|
166
311
|
const sendMessage = react.useCallback(
|
|
167
|
-
async (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
312
|
+
async (input) => {
|
|
313
|
+
const message = chat.userMessageFromInput(input);
|
|
314
|
+
const bubble = chat.uiFromMessages([message], genId)[0];
|
|
315
|
+
const previous = snapshotRef.current;
|
|
316
|
+
commit({
|
|
317
|
+
...previous,
|
|
318
|
+
history: {
|
|
319
|
+
ui: bubble ? [...previous.history.ui, bubble] : previous.history.ui,
|
|
320
|
+
canonical: [...previous.history.canonical, message]
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
clearGates();
|
|
324
|
+
flush();
|
|
173
325
|
await run();
|
|
174
326
|
},
|
|
175
|
-
[run,
|
|
327
|
+
[run, commit, clearGates, flush, options.generateId]
|
|
176
328
|
);
|
|
177
329
|
const regenerate = react.useCallback(async () => {
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
approvalsRef.current = [];
|
|
183
|
-
verdictsRef.current = [];
|
|
184
|
-
setPendingApprovals([]);
|
|
330
|
+
const previous = snapshotRef.current;
|
|
331
|
+
commit({ ...previous, history: chat.dropTrailingAssistant(previous.history) });
|
|
332
|
+
clearGates();
|
|
333
|
+
flush();
|
|
185
334
|
await run();
|
|
186
|
-
}, [run]);
|
|
335
|
+
}, [run, commit, clearGates, flush]);
|
|
187
336
|
const editAndResend = react.useCallback(
|
|
188
|
-
async (messageId,
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
messageId
|
|
192
|
-
);
|
|
337
|
+
async (messageId, input) => {
|
|
338
|
+
const previous = snapshotRef.current;
|
|
339
|
+
const cut = chat.branchBeforeUserMessage(previous.history, messageId);
|
|
193
340
|
if (!cut) return;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
341
|
+
commit({ ...previous, history: cut });
|
|
342
|
+
clearGates();
|
|
343
|
+
flush();
|
|
344
|
+
await sendMessage(input);
|
|
345
|
+
},
|
|
346
|
+
[sendMessage, commit, clearGates, flush]
|
|
347
|
+
);
|
|
348
|
+
const setHistory = react.useCallback(
|
|
349
|
+
(update) => {
|
|
350
|
+
const previous = snapshotRef.current;
|
|
351
|
+
const next = typeof update === "function" ? update(previous.history) : update;
|
|
352
|
+
commit({
|
|
353
|
+
...previous,
|
|
354
|
+
history: { ui: [...next.ui], canonical: [...next.canonical] }
|
|
355
|
+
});
|
|
356
|
+
clearGates();
|
|
357
|
+
flush();
|
|
201
358
|
},
|
|
202
|
-
[
|
|
359
|
+
[commit, clearGates, flush]
|
|
360
|
+
);
|
|
361
|
+
const setMessages = react.useCallback(
|
|
362
|
+
(update) => {
|
|
363
|
+
setHistory((prev) => {
|
|
364
|
+
const ui = typeof update === "function" ? update(prev.ui) : update;
|
|
365
|
+
return { ui, canonical: chat.canonicalFromUI(ui) };
|
|
366
|
+
});
|
|
367
|
+
},
|
|
368
|
+
[setHistory]
|
|
369
|
+
);
|
|
370
|
+
const clearError = react.useCallback(() => {
|
|
371
|
+
setError(void 0);
|
|
372
|
+
setStatus((s) => s === "error" ? "idle" : s);
|
|
373
|
+
}, []);
|
|
374
|
+
const addToolResult = react.useCallback(
|
|
375
|
+
async (result) => {
|
|
376
|
+
const parked = clientToolsRef.current;
|
|
377
|
+
const call = parked.find((c) => c.toolCallId === result.toolCallId);
|
|
378
|
+
if (!call) return;
|
|
379
|
+
toolResultsRef.current = [
|
|
380
|
+
...toolResultsRef.current.filter((r) => r.toolCallId !== result.toolCallId),
|
|
381
|
+
{
|
|
382
|
+
toolCallId: result.toolCallId,
|
|
383
|
+
result: result.output,
|
|
384
|
+
...result.isError ? { isError: true } : {}
|
|
385
|
+
}
|
|
386
|
+
];
|
|
387
|
+
const turn = parkedTurnRef.current;
|
|
388
|
+
if (turn) {
|
|
389
|
+
const next = chat.applyUIPart(turn, {
|
|
390
|
+
type: "tool-result",
|
|
391
|
+
toolCallId: result.toolCallId,
|
|
392
|
+
toolName: call.toolName,
|
|
393
|
+
output: result.output,
|
|
394
|
+
...result.isError ? { isError: true } : {}
|
|
395
|
+
});
|
|
396
|
+
parkedTurnRef.current = next;
|
|
397
|
+
syncTurn(next);
|
|
398
|
+
flush();
|
|
399
|
+
}
|
|
400
|
+
const answered = toolResultsRef.current;
|
|
401
|
+
if (!parked.every((c) => answered.some((r) => r.toolCallId === c.toolCallId))) return;
|
|
402
|
+
clientToolsRef.current = [];
|
|
403
|
+
toolResultsRef.current = [];
|
|
404
|
+
parkedTurnRef.current = void 0;
|
|
405
|
+
setPendingToolCalls([]);
|
|
406
|
+
appendCanonical(chat.clientToolResultMessage(answered));
|
|
407
|
+
await run();
|
|
408
|
+
},
|
|
409
|
+
[run, appendCanonical, syncTurn, flush]
|
|
203
410
|
);
|
|
204
411
|
const addToolApprovalResponse = react.useCallback(
|
|
205
412
|
async (response) => {
|
|
@@ -221,79 +428,118 @@ function useChat(options) {
|
|
|
221
428
|
},
|
|
222
429
|
[run]
|
|
223
430
|
);
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
turn
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const parts = ui.connectDeuzStream(resume.endpoint, {
|
|
242
|
-
...cursor !== void 0 ? { lastEventId: cursor } : {},
|
|
243
|
-
onCursor: (id) => {
|
|
244
|
-
lastEventIdRef.current = id;
|
|
245
|
-
},
|
|
246
|
-
signal: controller.signal,
|
|
247
|
-
...options.fetch ? { fetch: options.fetch } : {},
|
|
248
|
-
...options.headers ? { headers: options.headers } : {}
|
|
249
|
-
});
|
|
250
|
-
for await (const part of parts) {
|
|
251
|
-
turn = foldPart(turn, part);
|
|
431
|
+
const runResume = react.useCallback(
|
|
432
|
+
async (silent) => {
|
|
433
|
+
const resume = options.resume;
|
|
434
|
+
if (!resume) return;
|
|
435
|
+
const controller = new AbortController();
|
|
436
|
+
abortRef.current = controller;
|
|
437
|
+
setStatus("streaming");
|
|
438
|
+
setError(void 0);
|
|
439
|
+
let folded = 0;
|
|
440
|
+
try {
|
|
441
|
+
const cursor = lastEventIdRef.current ?? loadCursor(resume);
|
|
442
|
+
const inFlight = turnRef.current;
|
|
443
|
+
let turn = inFlight;
|
|
444
|
+
if (turn === void 0 || cursor === void 0) {
|
|
445
|
+
if (inFlight === void 0) placedIdRef.current = void 0;
|
|
446
|
+
turn = chat.createAssistantTurn(genId());
|
|
447
|
+
}
|
|
252
448
|
turnRef.current = turn;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
449
|
+
const parts = ui.connectDeuzStream(resume.endpoint, {
|
|
450
|
+
...cursor !== void 0 ? { lastEventId: cursor } : {},
|
|
451
|
+
onCursor: (id) => {
|
|
452
|
+
lastEventIdRef.current = id;
|
|
453
|
+
saveCursor(resume, id);
|
|
454
|
+
},
|
|
455
|
+
signal: controller.signal,
|
|
456
|
+
...options.fetch ? { fetch: options.fetch } : {},
|
|
457
|
+
...options.headers ? { headers: options.headers } : {}
|
|
458
|
+
});
|
|
459
|
+
for await (const part of parts) {
|
|
460
|
+
folded++;
|
|
461
|
+
turn = fold(turn, part);
|
|
462
|
+
turnRef.current = turn;
|
|
463
|
+
syncTurn(turn);
|
|
464
|
+
}
|
|
465
|
+
if (folded === 0) {
|
|
466
|
+
turnRef.current = inFlight === void 0 ? void 0 : chat.sealAssistantTurn(inFlight);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const sealed = chat.sealAssistantTurn(turn);
|
|
470
|
+
if (sealed !== turn) {
|
|
471
|
+
turn = sealed;
|
|
472
|
+
syncTurn(turn);
|
|
473
|
+
}
|
|
474
|
+
turnRef.current = turn;
|
|
475
|
+
if (turn.error !== void 0) throw new Error(turn.error);
|
|
476
|
+
appendCanonical(chat.assistantMessageFromTurn(turn));
|
|
477
|
+
turnRef.current = void 0;
|
|
478
|
+
if (turn.approvals.length > 0) {
|
|
479
|
+
approvalsRef.current = turn.approvals;
|
|
480
|
+
verdictsRef.current = [];
|
|
481
|
+
setPendingApprovals(turn.approvals);
|
|
482
|
+
}
|
|
483
|
+
} catch (err) {
|
|
484
|
+
if (controller.signal.aborted) return;
|
|
485
|
+
if (silent) return;
|
|
486
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
487
|
+
setError(e);
|
|
488
|
+
setStatus("error");
|
|
489
|
+
options.onError?.(e);
|
|
490
|
+
return;
|
|
491
|
+
} finally {
|
|
492
|
+
setStatus((s) => s === "error" ? s : "idle");
|
|
493
|
+
flush();
|
|
262
494
|
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
options.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
495
|
+
},
|
|
496
|
+
[
|
|
497
|
+
options.resume,
|
|
498
|
+
options.fetch,
|
|
499
|
+
options.headers,
|
|
500
|
+
options.generateId,
|
|
501
|
+
options.onError,
|
|
502
|
+
appendCanonical,
|
|
503
|
+
fold,
|
|
504
|
+
flush,
|
|
505
|
+
syncTurn
|
|
506
|
+
]
|
|
507
|
+
);
|
|
508
|
+
const reconnect = react.useCallback(() => runResume(false), [runResume]);
|
|
509
|
+
const autoResumedRef = react.useRef(false);
|
|
510
|
+
react.useEffect(() => {
|
|
511
|
+
if (options.resume?.auto !== true || autoResumedRef.current) return;
|
|
512
|
+
autoResumedRef.current = true;
|
|
513
|
+
void runResume(true);
|
|
514
|
+
}, [options.resume?.auto, runResume]);
|
|
282
515
|
return {
|
|
283
|
-
messages,
|
|
516
|
+
messages: snapshot.history.ui,
|
|
517
|
+
history: snapshot.history,
|
|
284
518
|
status,
|
|
285
519
|
error,
|
|
286
520
|
pendingApprovals,
|
|
287
|
-
|
|
288
|
-
...
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
521
|
+
pendingToolCalls,
|
|
522
|
+
...snapshot.cost !== void 0 ? { cost: snapshot.cost } : {},
|
|
523
|
+
...snapshot.budgetExceeded !== void 0 ? { budgetExceeded: snapshot.budgetExceeded } : {},
|
|
524
|
+
dataParts: snapshot.dataParts,
|
|
525
|
+
citations: snapshot.citations,
|
|
526
|
+
...snapshot.plan !== void 0 ? { plan: snapshot.plan } : {},
|
|
527
|
+
activity: snapshot.activity,
|
|
528
|
+
...snapshot.verifications !== void 0 ? { verifications: snapshot.verifications } : {},
|
|
529
|
+
...snapshot.warnings !== void 0 ? { warnings: snapshot.warnings } : {},
|
|
530
|
+
...snapshot.falseFinishes !== void 0 ? { falseFinishes: snapshot.falseFinishes } : {},
|
|
531
|
+
...snapshot.subAgents !== void 0 ? { subAgents: snapshot.subAgents } : {},
|
|
532
|
+
...snapshot.steps !== void 0 ? { steps: snapshot.steps } : {},
|
|
533
|
+
...snapshot.usage !== void 0 ? { usage: snapshot.usage } : {},
|
|
534
|
+
...snapshot.finishReason !== void 0 ? { finishReason: snapshot.finishReason } : {},
|
|
293
535
|
sendMessage,
|
|
294
536
|
stop,
|
|
295
537
|
regenerate,
|
|
296
538
|
editAndResend,
|
|
539
|
+
setHistory,
|
|
540
|
+
setMessages,
|
|
541
|
+
addToolResult,
|
|
542
|
+
clearError,
|
|
297
543
|
addToolApprovalResponse,
|
|
298
544
|
reconnect
|
|
299
545
|
};
|
|
@@ -303,6 +549,40 @@ function useObject(options) {
|
|
|
303
549
|
const [isLoading, setLoading] = react.useState(false);
|
|
304
550
|
const [error, setError] = react.useState(void 0);
|
|
305
551
|
const abortRef = react.useRef(null);
|
|
552
|
+
const throttleMs = options.throttleMs ?? 0;
|
|
553
|
+
const latestRef = react.useRef(void 0);
|
|
554
|
+
const timerRef = react.useRef(void 0);
|
|
555
|
+
const dirtyRef = react.useRef(false);
|
|
556
|
+
const publish = react.useCallback(() => {
|
|
557
|
+
timerRef.current = void 0;
|
|
558
|
+
dirtyRef.current = false;
|
|
559
|
+
setObject(latestRef.current);
|
|
560
|
+
}, []);
|
|
561
|
+
const commit = react.useCallback(
|
|
562
|
+
(next) => {
|
|
563
|
+
latestRef.current = next;
|
|
564
|
+
if (throttleMs <= 0) {
|
|
565
|
+
publish();
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
dirtyRef.current = true;
|
|
569
|
+
if (timerRef.current === void 0) timerRef.current = setTimeout(publish, throttleMs);
|
|
570
|
+
},
|
|
571
|
+
[throttleMs, publish]
|
|
572
|
+
);
|
|
573
|
+
const flush = react.useCallback(() => {
|
|
574
|
+
if (timerRef.current !== void 0) {
|
|
575
|
+
clearTimeout(timerRef.current);
|
|
576
|
+
timerRef.current = void 0;
|
|
577
|
+
}
|
|
578
|
+
if (dirtyRef.current) publish();
|
|
579
|
+
}, [publish]);
|
|
580
|
+
react.useEffect(
|
|
581
|
+
() => () => {
|
|
582
|
+
if (timerRef.current !== void 0) clearTimeout(timerRef.current);
|
|
583
|
+
},
|
|
584
|
+
[]
|
|
585
|
+
);
|
|
306
586
|
const stop = react.useCallback(() => {
|
|
307
587
|
abortRef.current?.abort();
|
|
308
588
|
}, []);
|
|
@@ -313,7 +593,8 @@ function useObject(options) {
|
|
|
313
593
|
abortRef.current = controller;
|
|
314
594
|
setLoading(true);
|
|
315
595
|
setError(void 0);
|
|
316
|
-
|
|
596
|
+
commit(void 0);
|
|
597
|
+
flush();
|
|
317
598
|
try {
|
|
318
599
|
const doFetch = options.fetch ?? fetch;
|
|
319
600
|
const res = await doFetch(options.api, {
|
|
@@ -322,9 +603,11 @@ function useObject(options) {
|
|
|
322
603
|
body: JSON.stringify({ input }),
|
|
323
604
|
signal: controller.signal
|
|
324
605
|
});
|
|
325
|
-
for await (const part of ui.readDeuzStream(res
|
|
606
|
+
for await (const part of ui.readDeuzStream(res, {
|
|
607
|
+
...options.onHttpError ? { onHttpError: options.onHttpError } : {}
|
|
608
|
+
})) {
|
|
326
609
|
if (part.type === "object-delta") {
|
|
327
|
-
|
|
610
|
+
commit(part.object);
|
|
328
611
|
} else if (part.type === "error") {
|
|
329
612
|
setError(new Error(part.message));
|
|
330
613
|
break;
|
|
@@ -336,9 +619,10 @@ function useObject(options) {
|
|
|
336
619
|
}
|
|
337
620
|
} finally {
|
|
338
621
|
setLoading(false);
|
|
622
|
+
flush();
|
|
339
623
|
}
|
|
340
624
|
},
|
|
341
|
-
[options.api, options.fetch, options.headers]
|
|
625
|
+
[options.api, options.fetch, options.headers, options.onHttpError, commit, flush]
|
|
342
626
|
);
|
|
343
627
|
return { object, isLoading, error, submit, stop };
|
|
344
628
|
}
|
|
@@ -368,6 +652,7 @@ function CostBadge({ cost, format }) {
|
|
|
368
652
|
|
|
369
653
|
exports.CostBadge = CostBadge;
|
|
370
654
|
exports.ToolApprovalCard = ToolApprovalCard;
|
|
655
|
+
exports.partsFromFiles = partsFromFiles;
|
|
371
656
|
exports.useChat = useChat;
|
|
372
657
|
exports.useObject = useObject;
|
|
373
658
|
//# sourceMappingURL=index.cjs.map
|