@sinas/sdk 0.5.0 → 0.8.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/{chunk-IABVF56L.js → chunk-RHQVV5O6.js} +344 -99
- package/dist/chunk-RHQVV5O6.js.map +1 -0
- package/dist/client.cjs +356 -98
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +330 -5
- package/dist/client.d.ts +330 -5
- package/dist/client.js +29 -1
- package/dist/index.cjs +624 -244
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -4
- package/dist/index.d.ts +62 -4
- package/dist/index.js +298 -147
- package/dist/index.js.map +1 -1
- package/dist/sinas-sdk.umd.js +610 -244
- package/dist/sinas-sdk.umd.js.map +1 -1
- package/package.json +6 -3
- package/dist/chunk-IABVF56L.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -6,19 +6,32 @@ import {
|
|
|
6
6
|
SinasNotFoundError,
|
|
7
7
|
SinasPermissionError,
|
|
8
8
|
SinasTimeoutError,
|
|
9
|
+
answerPendingInput,
|
|
10
|
+
answerResumeChannel,
|
|
11
|
+
approvalResumeChannel,
|
|
9
12
|
approveToolCall,
|
|
13
|
+
cancelBatch,
|
|
10
14
|
checkPermissions,
|
|
11
15
|
createChat,
|
|
16
|
+
enqueueFunction,
|
|
12
17
|
executeFunction,
|
|
13
18
|
executeQuery,
|
|
14
19
|
generateFileUrl,
|
|
20
|
+
getBatch,
|
|
15
21
|
getChat,
|
|
16
22
|
getInfo,
|
|
17
23
|
getMe,
|
|
24
|
+
invokeAgent,
|
|
25
|
+
listBatchExecutions,
|
|
18
26
|
listCollections,
|
|
19
27
|
listFiles,
|
|
28
|
+
listPendingInputs,
|
|
20
29
|
login,
|
|
21
30
|
logout,
|
|
31
|
+
parseApproval,
|
|
32
|
+
parsePendingInput,
|
|
33
|
+
parsePendingInputs,
|
|
34
|
+
pendingInputFromChunk,
|
|
22
35
|
refresh,
|
|
23
36
|
sendMessage,
|
|
24
37
|
stateDelete,
|
|
@@ -27,9 +40,11 @@ import {
|
|
|
27
40
|
stateSet,
|
|
28
41
|
streamFromChannel,
|
|
29
42
|
streamMessage,
|
|
43
|
+
submitAgentBatch,
|
|
44
|
+
submitFunctionBatch,
|
|
30
45
|
uploadFile,
|
|
31
46
|
verifyOTP
|
|
32
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-RHQVV5O6.js";
|
|
33
48
|
|
|
34
49
|
// src/hooks/useQuery.ts
|
|
35
50
|
import React2 from "react";
|
|
@@ -212,29 +227,172 @@ function useStateStore(ns, key) {
|
|
|
212
227
|
|
|
213
228
|
// src/hooks/useChat.ts
|
|
214
229
|
import React5 from "react";
|
|
230
|
+
|
|
231
|
+
// src/hooks/chatState.ts
|
|
232
|
+
function parseMessageChunk(data) {
|
|
233
|
+
let parsed;
|
|
234
|
+
try {
|
|
235
|
+
parsed = JSON.parse(data);
|
|
236
|
+
} catch {
|
|
237
|
+
return { text: data };
|
|
238
|
+
}
|
|
239
|
+
if (!parsed || typeof parsed !== "object") return { text: "" };
|
|
240
|
+
const p = parsed;
|
|
241
|
+
const out = { text: typeof p.content === "string" ? p.content : "" };
|
|
242
|
+
if (typeof p.thinking === "string") out.thinking = p.thinking;
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
function applyTextDelta(messages, text, chatId, now = /* @__PURE__ */ new Date()) {
|
|
246
|
+
if (!text) return messages;
|
|
247
|
+
const last = messages[messages.length - 1];
|
|
248
|
+
if (last && last.role === "assistant" && !last.toolCalls && last.streaming) {
|
|
249
|
+
const next = [...messages];
|
|
250
|
+
next[next.length - 1] = { ...last, content: (last.content || "") + text };
|
|
251
|
+
return next;
|
|
252
|
+
}
|
|
253
|
+
return [
|
|
254
|
+
...messages,
|
|
255
|
+
{
|
|
256
|
+
id: `tmp-text-${now.getTime()}`,
|
|
257
|
+
chatId,
|
|
258
|
+
role: "assistant",
|
|
259
|
+
content: text,
|
|
260
|
+
toolCalls: null,
|
|
261
|
+
toolCallId: null,
|
|
262
|
+
name: null,
|
|
263
|
+
createdAt: now.toISOString(),
|
|
264
|
+
streaming: true
|
|
265
|
+
}
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
function applyToolStart(messages, event, chatId, now = /* @__PURE__ */ new Date()) {
|
|
269
|
+
if (messages.some((m) => m.toolCalls?.some((tc) => tc.id === event.tool_call_id))) return messages;
|
|
270
|
+
const frozen = messages.map(
|
|
271
|
+
(m, i) => i === messages.length - 1 && m.streaming && m.role === "assistant" && !m.toolCalls ? { ...m, streaming: false } : m
|
|
272
|
+
);
|
|
273
|
+
return [
|
|
274
|
+
...frozen,
|
|
275
|
+
{
|
|
276
|
+
id: `tmp-tc-${event.tool_call_id}`,
|
|
277
|
+
chatId,
|
|
278
|
+
role: "assistant",
|
|
279
|
+
content: null,
|
|
280
|
+
toolCalls: [
|
|
281
|
+
{
|
|
282
|
+
id: event.tool_call_id,
|
|
283
|
+
type: "function",
|
|
284
|
+
function: { name: event.name, arguments: event.arguments || "{}" },
|
|
285
|
+
description: event.description
|
|
286
|
+
}
|
|
287
|
+
],
|
|
288
|
+
toolCallId: null,
|
|
289
|
+
name: null,
|
|
290
|
+
createdAt: now.toISOString(),
|
|
291
|
+
streaming: true
|
|
292
|
+
}
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
function applyToolEnd(messages, event) {
|
|
296
|
+
if (event.result === void 0) return messages;
|
|
297
|
+
const content = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
|
|
298
|
+
return messages.map(
|
|
299
|
+
(m) => m.toolCalls?.some((tc) => tc.id === event.tool_call_id) ? { ...m, toolResults: { ...m.toolResults, [event.tool_call_id]: { content, name: event.name } } } : m
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
function addApproval(current, approval) {
|
|
303
|
+
return current.some((a) => a.toolCallId === approval.toolCallId) ? current : [...current, approval];
|
|
304
|
+
}
|
|
305
|
+
function mergePendingInputs(current, incoming) {
|
|
306
|
+
const seen = new Set(current.map((p) => p.toolCallId));
|
|
307
|
+
const added = incoming.filter((p) => !seen.has(p.toolCallId) && seen.add(p.toolCallId));
|
|
308
|
+
return added.length ? [...current, ...added] : current;
|
|
309
|
+
}
|
|
310
|
+
var NOT_THINKING = { active: false, rounds: [] };
|
|
311
|
+
function applyThinking(state, thinking) {
|
|
312
|
+
if (!state.active) return { active: true, rounds: [...state.rounds, thinking] };
|
|
313
|
+
if (!thinking) return state;
|
|
314
|
+
const rounds = [...state.rounds];
|
|
315
|
+
rounds[rounds.length - 1] += thinking;
|
|
316
|
+
return { active: true, rounds };
|
|
317
|
+
}
|
|
318
|
+
function stopThinking(state) {
|
|
319
|
+
return state.active ? { ...state, active: false } : state;
|
|
320
|
+
}
|
|
321
|
+
function thinkingView(state) {
|
|
322
|
+
return { active: state.active, text: state.rounds.filter((r) => r).join("\n\n") };
|
|
323
|
+
}
|
|
324
|
+
function createLatestGate() {
|
|
325
|
+
let latest = 0;
|
|
326
|
+
return {
|
|
327
|
+
next: () => ++latest,
|
|
328
|
+
isCurrent: (ticket) => ticket === latest
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
var PENDING_POLL_MAX_MS = 3e4;
|
|
332
|
+
var PENDING_POLL_MIN_MS = 1e3;
|
|
333
|
+
var EXPIRY_GRACE_MS = 5e3;
|
|
334
|
+
var OVERDUE_POLL_MS = 1e4;
|
|
335
|
+
function nextPendingPollMs(inputs, nowMs) {
|
|
336
|
+
let wait = PENDING_POLL_MAX_MS;
|
|
337
|
+
for (const input of inputs) {
|
|
338
|
+
const deadline = input.expiresAt ? Date.parse(input.expiresAt) : NaN;
|
|
339
|
+
if (Number.isNaN(deadline)) continue;
|
|
340
|
+
const untilDue = deadline + EXPIRY_GRACE_MS - nowMs;
|
|
341
|
+
wait = Math.min(wait, untilDue > 0 ? untilDue : OVERDUE_POLL_MS);
|
|
342
|
+
}
|
|
343
|
+
return Math.max(PENDING_POLL_MIN_MS, wait);
|
|
344
|
+
}
|
|
345
|
+
var TRAILING_POLLS_MS = [5e3, 15e3, 3e4];
|
|
346
|
+
|
|
347
|
+
// src/hooks/useChat.ts
|
|
215
348
|
function useChat(agentRef, options) {
|
|
216
349
|
const client = useClient();
|
|
350
|
+
const includeThinking = !!options?.includeThinking;
|
|
351
|
+
const pollPendingInputs = options?.pollPendingInputs !== false;
|
|
217
352
|
const [chatId, setChatId] = React5.useState(options?.chatId ?? null);
|
|
218
353
|
const [messages, setMessages] = React5.useState([]);
|
|
219
354
|
const [loading, setLoading] = React5.useState(false);
|
|
220
355
|
const [streaming, setStreaming] = React5.useState(false);
|
|
221
356
|
const [error, setError] = React5.useState(null);
|
|
222
357
|
const [pendingApprovals, setPendingApprovals] = React5.useState([]);
|
|
358
|
+
const [pendingInputs, setPendingInputs] = React5.useState([]);
|
|
359
|
+
const [thinking, setThinking] = React5.useState(NOT_THINKING);
|
|
360
|
+
const thinkingShown = React5.useMemo(() => thinkingView(thinking), [thinking]);
|
|
223
361
|
const [toolStatus, setToolStatus] = React5.useState([]);
|
|
362
|
+
const [trailingPoll, setTrailingPoll] = React5.useState(TRAILING_POLLS_MS.length);
|
|
363
|
+
const [pollTick, setPollTick] = React5.useState(0);
|
|
224
364
|
const abortRef = React5.useRef(null);
|
|
225
365
|
const chatIdRef = React5.useRef(chatId);
|
|
226
366
|
chatIdRef.current = chatId;
|
|
367
|
+
const [gate] = React5.useState(createLatestGate);
|
|
368
|
+
const refresh2 = React5.useCallback(
|
|
369
|
+
async (id) => {
|
|
370
|
+
const ticket = gate.next();
|
|
371
|
+
try {
|
|
372
|
+
const detail = await getChat(client, id);
|
|
373
|
+
if (!gate.isCurrent(ticket)) return null;
|
|
374
|
+
setMessages(detail.messages);
|
|
375
|
+
setPendingApprovals(detail.pendingApprovals ?? []);
|
|
376
|
+
const inputs = detail.pendingInputs ?? [];
|
|
377
|
+
setPendingInputs(inputs);
|
|
378
|
+
return inputs;
|
|
379
|
+
} catch {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
[client, gate]
|
|
384
|
+
);
|
|
227
385
|
React5.useEffect(() => {
|
|
228
386
|
if (!options?.chatId) return;
|
|
229
387
|
let cancelled = false;
|
|
388
|
+
const ticket = gate.next();
|
|
230
389
|
setLoading(true);
|
|
231
390
|
getChat(client, options.chatId).then((detail) => {
|
|
232
|
-
if (cancelled) return;
|
|
391
|
+
if (cancelled || !gate.isCurrent(ticket)) return;
|
|
233
392
|
setChatId(detail.id);
|
|
234
393
|
setMessages(detail.messages);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
394
|
+
setPendingApprovals(detail.pendingApprovals ?? []);
|
|
395
|
+
setPendingInputs(detail.pendingInputs ?? []);
|
|
238
396
|
}).catch((e) => {
|
|
239
397
|
if (cancelled) return;
|
|
240
398
|
setError(e.message);
|
|
@@ -244,74 +402,52 @@ function useChat(agentRef, options) {
|
|
|
244
402
|
return () => {
|
|
245
403
|
cancelled = true;
|
|
246
404
|
};
|
|
247
|
-
}, [client, options?.chatId]);
|
|
405
|
+
}, [client, gate, options?.chatId]);
|
|
248
406
|
React5.useEffect(() => {
|
|
249
407
|
return () => {
|
|
250
408
|
abortRef.current?.abort();
|
|
251
409
|
};
|
|
252
410
|
}, []);
|
|
253
|
-
|
|
411
|
+
React5.useEffect(() => {
|
|
412
|
+
if (!pollPendingInputs || streaming || !chatId) return;
|
|
413
|
+
let delay = null;
|
|
414
|
+
if (pendingInputs.length > 0) delay = nextPendingPollMs(pendingInputs, Date.now());
|
|
415
|
+
else if (trailingPoll < TRAILING_POLLS_MS.length) delay = TRAILING_POLLS_MS[trailingPoll];
|
|
416
|
+
if (delay === null) return;
|
|
417
|
+
const waiting = pendingInputs.map((p) => p.toolCallId);
|
|
418
|
+
const timer = setTimeout(async () => {
|
|
419
|
+
const now = await refresh2(chatId);
|
|
420
|
+
if (waiting.length === 0) setTrailingPoll((i) => i + 1);
|
|
421
|
+
else if (now && waiting.some((id) => !now.some((p) => p.toolCallId === id))) setTrailingPoll(0);
|
|
422
|
+
setPollTick((t) => t + 1);
|
|
423
|
+
}, delay);
|
|
424
|
+
return () => clearTimeout(timer);
|
|
425
|
+
}, [pollPendingInputs, chatId, streaming, pendingInputs, trailingPoll, pollTick, refresh2]);
|
|
426
|
+
const handleStreamChunk = React5.useCallback(
|
|
254
427
|
(chunk) => {
|
|
428
|
+
const asked = pendingInputFromChunk(chunk);
|
|
429
|
+
if (asked) {
|
|
430
|
+
setThinking(stopThinking);
|
|
431
|
+
setPendingInputs((prev) => mergePendingInputs(prev, [asked]));
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const chatIdNow = chatIdRef.current || "";
|
|
255
435
|
if (chunk.event === "message") {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
})();
|
|
264
|
-
if (!newContent) return;
|
|
265
|
-
setMessages((prev) => {
|
|
266
|
-
const last = prev[prev.length - 1];
|
|
267
|
-
if (last && last.role === "assistant" && !last.toolCalls && last.streaming) {
|
|
268
|
-
const next = [...prev];
|
|
269
|
-
next[next.length - 1] = { ...last, content: (last.content || "") + newContent };
|
|
270
|
-
return next;
|
|
271
|
-
}
|
|
272
|
-
return [...prev, {
|
|
273
|
-
id: `tmp-text-${Date.now()}`,
|
|
274
|
-
chatId: chatIdRef.current || "",
|
|
275
|
-
role: "assistant",
|
|
276
|
-
content: newContent,
|
|
277
|
-
toolCalls: null,
|
|
278
|
-
toolCallId: null,
|
|
279
|
-
name: null,
|
|
280
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
281
|
-
streaming: true
|
|
282
|
-
}];
|
|
283
|
-
});
|
|
436
|
+
const { text, thinking: thought } = parseMessageChunk(chunk.data);
|
|
437
|
+
if (thought !== void 0) setThinking((prev) => applyThinking(prev, thought));
|
|
438
|
+
if (text) {
|
|
439
|
+
setThinking(stopThinking);
|
|
440
|
+
setMessages((prev) => applyTextDelta(prev, text, chatIdNow));
|
|
441
|
+
}
|
|
284
442
|
} else if (chunk.event === "tool_start") {
|
|
443
|
+
setThinking(stopThinking);
|
|
285
444
|
try {
|
|
286
445
|
const parsed = JSON.parse(chunk.data);
|
|
287
446
|
setToolStatus((prev) => [
|
|
288
447
|
...prev,
|
|
289
448
|
{ toolCallId: parsed.tool_call_id, name: parsed.name, description: parsed.description, status: "running" }
|
|
290
449
|
]);
|
|
291
|
-
setMessages((prev) =>
|
|
292
|
-
if (prev.some((m) => m.toolCalls?.some((tc) => tc.id === parsed.tool_call_id))) {
|
|
293
|
-
return prev;
|
|
294
|
-
}
|
|
295
|
-
const next = prev.map(
|
|
296
|
-
(m, i) => i === prev.length - 1 && m.streaming && m.role === "assistant" && !m.toolCalls ? { ...m, streaming: false } : m
|
|
297
|
-
);
|
|
298
|
-
return [...next, {
|
|
299
|
-
id: `tmp-tc-${parsed.tool_call_id}`,
|
|
300
|
-
chatId: chatIdRef.current || "",
|
|
301
|
-
role: "assistant",
|
|
302
|
-
content: null,
|
|
303
|
-
toolCalls: [{
|
|
304
|
-
id: parsed.tool_call_id,
|
|
305
|
-
type: "function",
|
|
306
|
-
function: { name: parsed.name, arguments: parsed.arguments || "{}" },
|
|
307
|
-
description: parsed.description
|
|
308
|
-
}],
|
|
309
|
-
toolCallId: null,
|
|
310
|
-
name: null,
|
|
311
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
312
|
-
streaming: true
|
|
313
|
-
}];
|
|
314
|
-
});
|
|
450
|
+
setMessages((prev) => applyToolStart(prev, parsed, chatIdNow));
|
|
315
451
|
} catch {
|
|
316
452
|
}
|
|
317
453
|
} else if (chunk.event === "tool_end") {
|
|
@@ -320,39 +456,18 @@ function useChat(agentRef, options) {
|
|
|
320
456
|
setToolStatus(
|
|
321
457
|
(prev) => prev.map((t) => t.toolCallId === parsed.tool_call_id ? { ...t, status: "complete" } : t)
|
|
322
458
|
);
|
|
323
|
-
|
|
324
|
-
const resultContent = typeof parsed.result === "string" ? parsed.result : JSON.stringify(parsed.result);
|
|
325
|
-
setMessages(
|
|
326
|
-
(prev) => prev.map((m) => {
|
|
327
|
-
if (m.toolCalls?.some((tc) => tc.id === parsed.tool_call_id)) {
|
|
328
|
-
return {
|
|
329
|
-
...m,
|
|
330
|
-
toolResults: {
|
|
331
|
-
...m.toolResults,
|
|
332
|
-
[parsed.tool_call_id]: { content: resultContent, name: parsed.name }
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
return m;
|
|
337
|
-
})
|
|
338
|
-
);
|
|
339
|
-
}
|
|
459
|
+
setMessages((prev) => applyToolEnd(prev, parsed));
|
|
340
460
|
} catch {
|
|
341
461
|
}
|
|
342
462
|
} else if (chunk.event === "approval_required") {
|
|
463
|
+
setThinking(stopThinking);
|
|
343
464
|
try {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
type: raw.type,
|
|
347
|
-
toolCallId: raw.tool_call_id,
|
|
348
|
-
functionNamespace: raw.function_namespace,
|
|
349
|
-
functionName: raw.function_name,
|
|
350
|
-
arguments: raw.arguments
|
|
351
|
-
};
|
|
352
|
-
setPendingApprovals((prev) => [...prev, parsed]);
|
|
465
|
+
const approval = parseApproval(JSON.parse(chunk.data));
|
|
466
|
+
if (approval) setPendingApprovals((prev) => addApproval(prev, approval));
|
|
353
467
|
} catch {
|
|
354
468
|
}
|
|
355
469
|
} else if (chunk.event === "error") {
|
|
470
|
+
setThinking(stopThinking);
|
|
356
471
|
try {
|
|
357
472
|
const parsed = JSON.parse(chunk.data);
|
|
358
473
|
setError(parsed.error || "Stream error");
|
|
@@ -363,18 +478,37 @@ function useChat(agentRef, options) {
|
|
|
363
478
|
},
|
|
364
479
|
[]
|
|
365
480
|
);
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
481
|
+
const streamCallbacks = React5.useCallback(
|
|
482
|
+
(id) => [
|
|
483
|
+
(_done) => {
|
|
484
|
+
setStreaming(false);
|
|
485
|
+
setToolStatus([]);
|
|
486
|
+
setThinking(stopThinking);
|
|
487
|
+
refresh2(id);
|
|
488
|
+
},
|
|
489
|
+
(err) => {
|
|
490
|
+
setError(err.message);
|
|
491
|
+
setStreaming(false);
|
|
492
|
+
setToolStatus([]);
|
|
493
|
+
setThinking(stopThinking);
|
|
375
494
|
}
|
|
495
|
+
],
|
|
496
|
+
[refresh2]
|
|
497
|
+
);
|
|
498
|
+
const followChannel = React5.useCallback(
|
|
499
|
+
(id, channelId, lastId) => {
|
|
500
|
+
abortRef.current?.abort();
|
|
501
|
+
gate.next();
|
|
502
|
+
setStreaming(true);
|
|
503
|
+
setError(null);
|
|
504
|
+
setThinking(NOT_THINKING);
|
|
505
|
+
const [onDone, onError] = streamCallbacks(id);
|
|
506
|
+
abortRef.current = streamFromChannel(client, id, channelId, handleStreamChunk, onDone, onError, {
|
|
507
|
+
includeThinking,
|
|
508
|
+
lastId
|
|
509
|
+
});
|
|
376
510
|
},
|
|
377
|
-
[client]
|
|
511
|
+
[client, gate, includeThinking, handleStreamChunk, streamCallbacks]
|
|
378
512
|
);
|
|
379
513
|
const send = React5.useCallback(
|
|
380
514
|
(content) => {
|
|
@@ -396,10 +530,12 @@ function useChat(agentRef, options) {
|
|
|
396
530
|
setLoading(false);
|
|
397
531
|
}
|
|
398
532
|
abortRef.current?.abort();
|
|
533
|
+
gate.next();
|
|
399
534
|
setStreaming(true);
|
|
400
535
|
setError(null);
|
|
401
536
|
setPendingApprovals([]);
|
|
402
537
|
setToolStatus([]);
|
|
538
|
+
setThinking(NOT_THINKING);
|
|
403
539
|
const displayContent = typeof content === "string" ? content : JSON.stringify(content);
|
|
404
540
|
const userMsg = {
|
|
405
541
|
id: `tmp-user-${Date.now()}`,
|
|
@@ -412,80 +548,77 @@ function useChat(agentRef, options) {
|
|
|
412
548
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
413
549
|
};
|
|
414
550
|
setMessages((prev) => [...prev, userMsg]);
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
handleStreamChunks,
|
|
420
|
-
() => {
|
|
421
|
-
setStreaming(false);
|
|
422
|
-
setToolStatus([]);
|
|
423
|
-
refreshMessages(activeChatId);
|
|
424
|
-
},
|
|
425
|
-
(err) => {
|
|
426
|
-
setError(err.message);
|
|
427
|
-
setStreaming(false);
|
|
428
|
-
setToolStatus([]);
|
|
429
|
-
}
|
|
430
|
-
);
|
|
431
|
-
abortRef.current = controller;
|
|
551
|
+
const [onDone, onError] = streamCallbacks(activeChatId);
|
|
552
|
+
abortRef.current = streamMessage(client, activeChatId, content, handleStreamChunk, onDone, onError, {
|
|
553
|
+
includeThinking
|
|
554
|
+
});
|
|
432
555
|
};
|
|
433
556
|
doSend();
|
|
434
557
|
},
|
|
435
|
-
[client, agentRef, options?.input,
|
|
558
|
+
[client, agentRef, options?.input, gate, includeThinking, handleStreamChunk, streamCallbacks]
|
|
436
559
|
);
|
|
437
|
-
const
|
|
438
|
-
(toolCallId, approved) => {
|
|
439
|
-
const
|
|
440
|
-
if (!
|
|
560
|
+
const decide = React5.useCallback(
|
|
561
|
+
(toolCallId, approved, alwaysAllow) => {
|
|
562
|
+
const id = chatIdRef.current;
|
|
563
|
+
if (!id) return;
|
|
564
|
+
gate.next();
|
|
441
565
|
setPendingApprovals((prev) => prev.filter((a) => a.toolCallId !== toolCallId));
|
|
442
|
-
setStreaming(true);
|
|
443
566
|
setError(null);
|
|
444
567
|
(async () => {
|
|
445
568
|
try {
|
|
446
|
-
const result = await approveToolCall(client,
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
const controller = streamFromChannel(
|
|
453
|
-
client,
|
|
454
|
-
currentChatId,
|
|
455
|
-
result.channelId,
|
|
456
|
-
handleStreamChunks,
|
|
457
|
-
() => {
|
|
458
|
-
setStreaming(false);
|
|
459
|
-
setToolStatus([]);
|
|
460
|
-
refreshMessages(currentChatId);
|
|
461
|
-
},
|
|
462
|
-
(err) => {
|
|
463
|
-
setError(err.message);
|
|
464
|
-
setStreaming(false);
|
|
465
|
-
setToolStatus([]);
|
|
466
|
-
}
|
|
467
|
-
);
|
|
468
|
-
abortRef.current = controller;
|
|
569
|
+
const result = await approveToolCall(client, id, toolCallId, approved, { alwaysAllow });
|
|
570
|
+
const channelId = approvalResumeChannel(result, approved);
|
|
571
|
+
if (channelId) followChannel(id, channelId);
|
|
572
|
+
else if (result.resume === void 0) refresh2(id);
|
|
469
573
|
} catch (e) {
|
|
470
574
|
setError(e.message);
|
|
471
|
-
|
|
575
|
+
refresh2(id);
|
|
472
576
|
}
|
|
473
577
|
})();
|
|
474
578
|
},
|
|
475
|
-
[client,
|
|
579
|
+
[client, gate, followChannel, refresh2]
|
|
476
580
|
);
|
|
477
581
|
const approve = React5.useCallback(
|
|
478
|
-
(toolCallId) =>
|
|
479
|
-
[
|
|
582
|
+
(toolCallId, opts) => decide(toolCallId, true, !!opts?.alwaysAllow),
|
|
583
|
+
[decide]
|
|
480
584
|
);
|
|
481
585
|
const reject = React5.useCallback(
|
|
482
|
-
(toolCallId) =>
|
|
483
|
-
[
|
|
586
|
+
(toolCallId) => decide(toolCallId, false, false),
|
|
587
|
+
[decide]
|
|
588
|
+
);
|
|
589
|
+
const answer = React5.useCallback(
|
|
590
|
+
async (toolCallId, text) => {
|
|
591
|
+
const id = chatIdRef.current;
|
|
592
|
+
if (!id) return false;
|
|
593
|
+
setError(null);
|
|
594
|
+
try {
|
|
595
|
+
const result = await answerPendingInput(client, id, toolCallId, text);
|
|
596
|
+
gate.next();
|
|
597
|
+
setPendingInputs((prev) => prev.filter((p) => p.toolCallId !== toolCallId));
|
|
598
|
+
const channelId = answerResumeChannel(result);
|
|
599
|
+
if (channelId) followChannel(id, channelId);
|
|
600
|
+
return true;
|
|
601
|
+
} catch (e) {
|
|
602
|
+
setError(e.message);
|
|
603
|
+
refresh2(id);
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
[client, gate, followChannel, refresh2]
|
|
608
|
+
);
|
|
609
|
+
const follow = React5.useCallback(
|
|
610
|
+
(channelId, opts) => {
|
|
611
|
+
const id = chatIdRef.current;
|
|
612
|
+
if (!id) return;
|
|
613
|
+
followChannel(id, channelId, opts?.lastId);
|
|
614
|
+
},
|
|
615
|
+
[followChannel]
|
|
484
616
|
);
|
|
485
617
|
const stop = React5.useCallback(() => {
|
|
486
618
|
abortRef.current?.abort();
|
|
487
619
|
abortRef.current = null;
|
|
488
620
|
setStreaming(false);
|
|
621
|
+
setThinking(stopThinking);
|
|
489
622
|
}, []);
|
|
490
623
|
const clearError = React5.useCallback(() => {
|
|
491
624
|
setError(null);
|
|
@@ -497,10 +630,14 @@ function useChat(agentRef, options) {
|
|
|
497
630
|
streaming,
|
|
498
631
|
error,
|
|
499
632
|
pendingApprovals,
|
|
633
|
+
pendingInputs,
|
|
634
|
+
thinking: thinkingShown,
|
|
500
635
|
toolStatus,
|
|
501
636
|
send,
|
|
502
637
|
approve,
|
|
503
638
|
reject,
|
|
639
|
+
answer,
|
|
640
|
+
follow,
|
|
504
641
|
stop,
|
|
505
642
|
clearError
|
|
506
643
|
};
|
|
@@ -603,19 +740,31 @@ export {
|
|
|
603
740
|
SinasPermissionError,
|
|
604
741
|
SinasProvider,
|
|
605
742
|
SinasTimeoutError,
|
|
743
|
+
answerPendingInput,
|
|
744
|
+
answerResumeChannel,
|
|
745
|
+
approvalResumeChannel,
|
|
606
746
|
approveToolCall,
|
|
747
|
+
cancelBatch,
|
|
607
748
|
checkPermissions,
|
|
608
749
|
createChat,
|
|
750
|
+
enqueueFunction,
|
|
609
751
|
executeFunction,
|
|
610
752
|
executeQuery,
|
|
611
753
|
generateFileUrl,
|
|
754
|
+
getBatch,
|
|
612
755
|
getChat,
|
|
613
756
|
getInfo,
|
|
614
757
|
getMe,
|
|
758
|
+
invokeAgent,
|
|
759
|
+
listBatchExecutions,
|
|
615
760
|
listCollections,
|
|
616
761
|
listFiles,
|
|
762
|
+
listPendingInputs,
|
|
617
763
|
login,
|
|
618
764
|
logout,
|
|
765
|
+
parsePendingInput,
|
|
766
|
+
parsePendingInputs,
|
|
767
|
+
pendingInputFromChunk,
|
|
619
768
|
refresh,
|
|
620
769
|
sendMessage,
|
|
621
770
|
stateDelete,
|
|
@@ -624,6 +773,8 @@ export {
|
|
|
624
773
|
stateSet,
|
|
625
774
|
streamFromChannel,
|
|
626
775
|
streamMessage,
|
|
776
|
+
submitAgentBatch,
|
|
777
|
+
submitFunctionBatch,
|
|
627
778
|
uploadFile,
|
|
628
779
|
useAuth,
|
|
629
780
|
useChat,
|