@deuz-sdk/react 1.7.1 → 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 -129
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +201 -10
- package/dist/index.d.ts +201 -10
- package/dist/index.js +422 -132
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -19,46 +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 uiRef = react.useRef(messages);
|
|
37
|
-
const canonicalRef = react.useRef(
|
|
38
|
-
options.initialMessages ? [...options.initialMessages] : []
|
|
39
|
-
);
|
|
56
|
+
const [pendingToolCalls, setPendingToolCalls] = react.useState([]);
|
|
57
|
+
const snapshotRef = react.useRef(snapshot);
|
|
40
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
|
+
);
|
|
41
64
|
const approvalsRef = react.useRef([]);
|
|
42
65
|
const verdictsRef = react.useRef([]);
|
|
43
66
|
const abortRef = react.useRef(null);
|
|
44
67
|
const lastEventIdRef = react.useRef(options.resume?.lastEventId);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
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);
|
|
48
76
|
}, []);
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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;
|
|
57
95
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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([]);
|
|
61
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
|
+
);
|
|
62
182
|
const stop = react.useCallback(() => {
|
|
63
183
|
abortRef.current?.abort();
|
|
64
184
|
}, []);
|
|
@@ -75,7 +195,7 @@ function useChat(options) {
|
|
|
75
195
|
method: "POST",
|
|
76
196
|
headers: { "content-type": "application/json", ...options.headers },
|
|
77
197
|
body: JSON.stringify({
|
|
78
|
-
messages:
|
|
198
|
+
messages: snapshotRef.current.history.canonical,
|
|
79
199
|
...options.chatId !== void 0 ? { chatId: options.chatId } : {},
|
|
80
200
|
...approvalResponses?.length ? { approvalResponses } : {},
|
|
81
201
|
...options.body
|
|
@@ -85,15 +205,25 @@ function useChat(options) {
|
|
|
85
205
|
approvalResponses = void 0;
|
|
86
206
|
let turn = chat.createAssistantTurn(genId());
|
|
87
207
|
turnRef.current = turn;
|
|
88
|
-
|
|
208
|
+
placedIdRef.current = void 0;
|
|
89
209
|
syncTurn(turn);
|
|
90
|
-
|
|
91
|
-
|
|
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;
|
|
92
221
|
turnRef.current = turn;
|
|
93
222
|
syncTurn(turn);
|
|
94
223
|
}
|
|
224
|
+
flush();
|
|
95
225
|
if (turn.error !== void 0) throw new Error(turn.error);
|
|
96
|
-
|
|
226
|
+
appendCanonical(chat.assistantMessageFromTurn(turn));
|
|
97
227
|
turnRef.current = void 0;
|
|
98
228
|
if (turn.approvals.length > 0) {
|
|
99
229
|
approvalsRef.current = turn.approvals;
|
|
@@ -104,7 +234,14 @@ function useChat(options) {
|
|
|
104
234
|
const toolCalls = turn.message.toolCalls ?? [];
|
|
105
235
|
const serverResults = new Set(turn.serverResults);
|
|
106
236
|
const clientPending = toolCalls.filter((t) => !serverResults.has(t.toolCallId));
|
|
107
|
-
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
|
+
}
|
|
108
245
|
const results = [];
|
|
109
246
|
for (const call of clientPending) {
|
|
110
247
|
try {
|
|
@@ -133,10 +270,18 @@ function useChat(options) {
|
|
|
133
270
|
}
|
|
134
271
|
syncTurn(turn);
|
|
135
272
|
}
|
|
136
|
-
|
|
273
|
+
appendCanonical(chat.clientToolResultMessage(results));
|
|
137
274
|
}
|
|
138
275
|
} catch (err) {
|
|
139
|
-
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
|
+
}
|
|
140
285
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
141
286
|
setError(e);
|
|
142
287
|
setStatus("error");
|
|
@@ -144,6 +289,7 @@ function useChat(options) {
|
|
|
144
289
|
return;
|
|
145
290
|
} finally {
|
|
146
291
|
setStatus((s) => s === "error" ? s : "idle");
|
|
292
|
+
flush();
|
|
147
293
|
}
|
|
148
294
|
},
|
|
149
295
|
[
|
|
@@ -155,47 +301,112 @@ function useChat(options) {
|
|
|
155
301
|
options.generateId,
|
|
156
302
|
options.onToolCall,
|
|
157
303
|
options.onError,
|
|
158
|
-
|
|
304
|
+
options.onHttpError,
|
|
305
|
+
appendCanonical,
|
|
306
|
+
fold,
|
|
307
|
+
flush,
|
|
159
308
|
syncTurn
|
|
160
309
|
]
|
|
161
310
|
);
|
|
162
311
|
const sendMessage = react.useCallback(
|
|
163
|
-
async (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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();
|
|
169
325
|
await run();
|
|
170
326
|
},
|
|
171
|
-
[run,
|
|
327
|
+
[run, commit, clearGates, flush, options.generateId]
|
|
172
328
|
);
|
|
173
329
|
const regenerate = react.useCallback(async () => {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
approvalsRef.current = [];
|
|
179
|
-
verdictsRef.current = [];
|
|
180
|
-
setPendingApprovals([]);
|
|
330
|
+
const previous = snapshotRef.current;
|
|
331
|
+
commit({ ...previous, history: chat.dropTrailingAssistant(previous.history) });
|
|
332
|
+
clearGates();
|
|
333
|
+
flush();
|
|
181
334
|
await run();
|
|
182
|
-
}, [run]);
|
|
335
|
+
}, [run, commit, clearGates, flush]);
|
|
183
336
|
const editAndResend = react.useCallback(
|
|
184
|
-
async (messageId,
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
messageId
|
|
188
|
-
);
|
|
337
|
+
async (messageId, input) => {
|
|
338
|
+
const previous = snapshotRef.current;
|
|
339
|
+
const cut = chat.branchBeforeUserMessage(previous.history, messageId);
|
|
189
340
|
if (!cut) return;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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();
|
|
197
358
|
},
|
|
198
|
-
[
|
|
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]
|
|
199
410
|
);
|
|
200
411
|
const addToolApprovalResponse = react.useCallback(
|
|
201
412
|
async (response) => {
|
|
@@ -217,77 +428,118 @@ function useChat(options) {
|
|
|
217
428
|
},
|
|
218
429
|
[run]
|
|
219
430
|
);
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
turn
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const parts = ui.connectDeuzStream(resume.endpoint, {
|
|
238
|
-
...cursor !== void 0 ? { lastEventId: cursor } : {},
|
|
239
|
-
onCursor: (id) => {
|
|
240
|
-
lastEventIdRef.current = id;
|
|
241
|
-
},
|
|
242
|
-
signal: controller.signal,
|
|
243
|
-
...options.fetch ? { fetch: options.fetch } : {},
|
|
244
|
-
...options.headers ? { headers: options.headers } : {}
|
|
245
|
-
});
|
|
246
|
-
for await (const part of parts) {
|
|
247
|
-
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
|
+
}
|
|
248
448
|
turnRef.current = turn;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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();
|
|
258
494
|
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
options.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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]);
|
|
278
515
|
return {
|
|
279
|
-
messages,
|
|
516
|
+
messages: snapshot.history.ui,
|
|
517
|
+
history: snapshot.history,
|
|
280
518
|
status,
|
|
281
519
|
error,
|
|
282
520
|
pendingApprovals,
|
|
283
|
-
|
|
284
|
-
...
|
|
285
|
-
|
|
286
|
-
|
|
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 } : {},
|
|
287
535
|
sendMessage,
|
|
288
536
|
stop,
|
|
289
537
|
regenerate,
|
|
290
538
|
editAndResend,
|
|
539
|
+
setHistory,
|
|
540
|
+
setMessages,
|
|
541
|
+
addToolResult,
|
|
542
|
+
clearError,
|
|
291
543
|
addToolApprovalResponse,
|
|
292
544
|
reconnect
|
|
293
545
|
};
|
|
@@ -297,6 +549,40 @@ function useObject(options) {
|
|
|
297
549
|
const [isLoading, setLoading] = react.useState(false);
|
|
298
550
|
const [error, setError] = react.useState(void 0);
|
|
299
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
|
+
);
|
|
300
586
|
const stop = react.useCallback(() => {
|
|
301
587
|
abortRef.current?.abort();
|
|
302
588
|
}, []);
|
|
@@ -307,7 +593,8 @@ function useObject(options) {
|
|
|
307
593
|
abortRef.current = controller;
|
|
308
594
|
setLoading(true);
|
|
309
595
|
setError(void 0);
|
|
310
|
-
|
|
596
|
+
commit(void 0);
|
|
597
|
+
flush();
|
|
311
598
|
try {
|
|
312
599
|
const doFetch = options.fetch ?? fetch;
|
|
313
600
|
const res = await doFetch(options.api, {
|
|
@@ -316,9 +603,11 @@ function useObject(options) {
|
|
|
316
603
|
body: JSON.stringify({ input }),
|
|
317
604
|
signal: controller.signal
|
|
318
605
|
});
|
|
319
|
-
for await (const part of ui.readDeuzStream(res
|
|
606
|
+
for await (const part of ui.readDeuzStream(res, {
|
|
607
|
+
...options.onHttpError ? { onHttpError: options.onHttpError } : {}
|
|
608
|
+
})) {
|
|
320
609
|
if (part.type === "object-delta") {
|
|
321
|
-
|
|
610
|
+
commit(part.object);
|
|
322
611
|
} else if (part.type === "error") {
|
|
323
612
|
setError(new Error(part.message));
|
|
324
613
|
break;
|
|
@@ -330,9 +619,10 @@ function useObject(options) {
|
|
|
330
619
|
}
|
|
331
620
|
} finally {
|
|
332
621
|
setLoading(false);
|
|
622
|
+
flush();
|
|
333
623
|
}
|
|
334
624
|
},
|
|
335
|
-
[options.api, options.fetch, options.headers]
|
|
625
|
+
[options.api, options.fetch, options.headers, options.onHttpError, commit, flush]
|
|
336
626
|
);
|
|
337
627
|
return { object, isLoading, error, submit, stop };
|
|
338
628
|
}
|
|
@@ -362,6 +652,7 @@ function CostBadge({ cost, format }) {
|
|
|
362
652
|
|
|
363
653
|
exports.CostBadge = CostBadge;
|
|
364
654
|
exports.ToolApprovalCard = ToolApprovalCard;
|
|
655
|
+
exports.partsFromFiles = partsFromFiles;
|
|
365
656
|
exports.useChat = useChat;
|
|
366
657
|
exports.useObject = useObject;
|
|
367
658
|
//# sourceMappingURL=index.cjs.map
|