@lov3kaizen/agentsea-react 0.4.0 → 0.5.2
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.d.mts +153 -6
- package/dist/index.d.ts +153 -6
- package/dist/index.js +806 -0
- package/dist/index.mjs +798 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -32,6 +32,14 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
AgentResponse: () => AgentResponse,
|
|
34
34
|
StreamingResponse: () => StreamingResponse,
|
|
35
|
+
createHTTPStreamAdapter: () => createHTTPStreamAdapter,
|
|
36
|
+
createMessage: () => createMessage,
|
|
37
|
+
createSSEAdapter: () => createSSEAdapter,
|
|
38
|
+
fetchChat: () => fetchChat,
|
|
39
|
+
generateId: () => generateId,
|
|
40
|
+
getAdapter: () => getAdapter,
|
|
41
|
+
useAgent: () => useAgent,
|
|
42
|
+
useChat: () => useChat,
|
|
35
43
|
useFormattedContent: () => useFormattedContent,
|
|
36
44
|
useStreamingContent: () => useStreamingContent
|
|
37
45
|
});
|
|
@@ -259,10 +267,808 @@ var useStreamingContent = () => {
|
|
|
259
267
|
consumeStream
|
|
260
268
|
};
|
|
261
269
|
};
|
|
270
|
+
|
|
271
|
+
// src/useChat.ts
|
|
272
|
+
var import_react3 = require("react");
|
|
273
|
+
|
|
274
|
+
// src/types.ts
|
|
275
|
+
function generateId() {
|
|
276
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
277
|
+
}
|
|
278
|
+
function createMessage(role, content, options) {
|
|
279
|
+
return {
|
|
280
|
+
id: generateId(),
|
|
281
|
+
role,
|
|
282
|
+
content,
|
|
283
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
284
|
+
...options
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/adapters.ts
|
|
289
|
+
function createSSEAdapter() {
|
|
290
|
+
let eventSource = null;
|
|
291
|
+
let abortController = null;
|
|
292
|
+
let messageCallback = null;
|
|
293
|
+
let errorCallback = null;
|
|
294
|
+
let closeCallback = null;
|
|
295
|
+
let currentUrl = "";
|
|
296
|
+
let currentOptions = {};
|
|
297
|
+
return {
|
|
298
|
+
connect(url, options) {
|
|
299
|
+
currentUrl = url;
|
|
300
|
+
currentOptions = options || {};
|
|
301
|
+
abortController = new AbortController();
|
|
302
|
+
if (options?.signal) {
|
|
303
|
+
options.signal.addEventListener("abort", () => {
|
|
304
|
+
abortController?.abort();
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return Promise.resolve();
|
|
308
|
+
},
|
|
309
|
+
async send(data) {
|
|
310
|
+
const response = await fetch(currentUrl, {
|
|
311
|
+
method: "POST",
|
|
312
|
+
headers: {
|
|
313
|
+
"Content-Type": "application/json",
|
|
314
|
+
Accept: "text/event-stream",
|
|
315
|
+
...currentOptions.headers
|
|
316
|
+
},
|
|
317
|
+
body: JSON.stringify(data),
|
|
318
|
+
signal: abortController?.signal
|
|
319
|
+
});
|
|
320
|
+
if (!response.ok) {
|
|
321
|
+
const error = new Error(`HTTP error: ${response.status}`);
|
|
322
|
+
errorCallback?.(error);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const reader = response.body?.getReader();
|
|
326
|
+
if (!reader) {
|
|
327
|
+
errorCallback?.(new Error("No response body"));
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const decoder = new TextDecoder();
|
|
331
|
+
let buffer = "";
|
|
332
|
+
try {
|
|
333
|
+
let reading = true;
|
|
334
|
+
while (reading) {
|
|
335
|
+
const { done, value } = await reader.read();
|
|
336
|
+
if (done) {
|
|
337
|
+
reading = false;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
buffer += decoder.decode(value, { stream: true });
|
|
341
|
+
const lines = buffer.split("\n");
|
|
342
|
+
buffer = lines.pop() || "";
|
|
343
|
+
for (const line of lines) {
|
|
344
|
+
if (line.startsWith("data: ")) {
|
|
345
|
+
const eventData = line.slice(6);
|
|
346
|
+
if (eventData === "[DONE]") {
|
|
347
|
+
closeCallback?.();
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
const chunk = JSON.parse(eventData);
|
|
352
|
+
messageCallback?.(chunk);
|
|
353
|
+
} catch {
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
} catch (error) {
|
|
359
|
+
if (error.name !== "AbortError") {
|
|
360
|
+
errorCallback?.(error);
|
|
361
|
+
}
|
|
362
|
+
} finally {
|
|
363
|
+
closeCallback?.();
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
onMessage(callback) {
|
|
367
|
+
messageCallback = callback;
|
|
368
|
+
},
|
|
369
|
+
onError(callback) {
|
|
370
|
+
errorCallback = callback;
|
|
371
|
+
},
|
|
372
|
+
onClose(callback) {
|
|
373
|
+
closeCallback = callback;
|
|
374
|
+
},
|
|
375
|
+
close() {
|
|
376
|
+
abortController?.abort();
|
|
377
|
+
eventSource?.close();
|
|
378
|
+
eventSource = null;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
function createHTTPStreamAdapter() {
|
|
383
|
+
let abortController = null;
|
|
384
|
+
let messageCallback = null;
|
|
385
|
+
let errorCallback = null;
|
|
386
|
+
let closeCallback = null;
|
|
387
|
+
let currentUrl = "";
|
|
388
|
+
let currentOptions = {};
|
|
389
|
+
return {
|
|
390
|
+
connect(url, options) {
|
|
391
|
+
currentUrl = url;
|
|
392
|
+
currentOptions = options || {};
|
|
393
|
+
abortController = new AbortController();
|
|
394
|
+
if (options?.signal) {
|
|
395
|
+
options.signal.addEventListener("abort", () => {
|
|
396
|
+
abortController?.abort();
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return Promise.resolve();
|
|
400
|
+
},
|
|
401
|
+
async send(data) {
|
|
402
|
+
try {
|
|
403
|
+
const response = await fetch(currentUrl, {
|
|
404
|
+
method: "POST",
|
|
405
|
+
headers: {
|
|
406
|
+
"Content-Type": "application/json",
|
|
407
|
+
Accept: "application/x-ndjson",
|
|
408
|
+
...currentOptions.headers
|
|
409
|
+
},
|
|
410
|
+
body: JSON.stringify(data),
|
|
411
|
+
signal: abortController?.signal
|
|
412
|
+
});
|
|
413
|
+
if (!response.ok) {
|
|
414
|
+
const error = new Error(`HTTP error: ${response.status}`);
|
|
415
|
+
errorCallback?.(error);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const reader = response.body?.getReader();
|
|
419
|
+
if (!reader) {
|
|
420
|
+
errorCallback?.(new Error("No response body"));
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
const decoder = new TextDecoder();
|
|
424
|
+
let buffer = "";
|
|
425
|
+
let reading = true;
|
|
426
|
+
while (reading) {
|
|
427
|
+
const { done, value } = await reader.read();
|
|
428
|
+
if (done) {
|
|
429
|
+
reading = false;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
buffer += decoder.decode(value, { stream: true });
|
|
433
|
+
const lines = buffer.split("\n");
|
|
434
|
+
buffer = lines.pop() || "";
|
|
435
|
+
for (const line of lines) {
|
|
436
|
+
if (line.trim()) {
|
|
437
|
+
try {
|
|
438
|
+
const chunk = JSON.parse(line);
|
|
439
|
+
messageCallback?.(chunk);
|
|
440
|
+
} catch {
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (buffer.trim()) {
|
|
446
|
+
try {
|
|
447
|
+
const chunk = JSON.parse(buffer);
|
|
448
|
+
messageCallback?.(chunk);
|
|
449
|
+
} catch {
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
} catch (error) {
|
|
453
|
+
if (error.name !== "AbortError") {
|
|
454
|
+
errorCallback?.(error);
|
|
455
|
+
}
|
|
456
|
+
} finally {
|
|
457
|
+
closeCallback?.();
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
onMessage(callback) {
|
|
461
|
+
messageCallback = callback;
|
|
462
|
+
},
|
|
463
|
+
onError(callback) {
|
|
464
|
+
errorCallback = callback;
|
|
465
|
+
},
|
|
466
|
+
onClose(callback) {
|
|
467
|
+
closeCallback = callback;
|
|
468
|
+
},
|
|
469
|
+
close() {
|
|
470
|
+
abortController?.abort();
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
async function fetchChat(url, data, options) {
|
|
475
|
+
const response = await fetch(url, {
|
|
476
|
+
method: "POST",
|
|
477
|
+
headers: {
|
|
478
|
+
"Content-Type": "application/json",
|
|
479
|
+
...options?.headers
|
|
480
|
+
},
|
|
481
|
+
body: JSON.stringify({ ...data, stream: false }),
|
|
482
|
+
signal: options?.signal
|
|
483
|
+
});
|
|
484
|
+
if (!response.ok) {
|
|
485
|
+
throw new Error(`HTTP error: ${response.status}`);
|
|
486
|
+
}
|
|
487
|
+
const result = await response.json();
|
|
488
|
+
return result.chunks || [result];
|
|
489
|
+
}
|
|
490
|
+
function getAdapter(type) {
|
|
491
|
+
if (typeof type === "object") {
|
|
492
|
+
return type;
|
|
493
|
+
}
|
|
494
|
+
return type === "sse" ? createSSEAdapter() : createHTTPStreamAdapter();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/useChat.ts
|
|
498
|
+
function useChat(config) {
|
|
499
|
+
const {
|
|
500
|
+
endpoint,
|
|
501
|
+
conversationId: initialConversationId,
|
|
502
|
+
agentId,
|
|
503
|
+
initialMessages = [],
|
|
504
|
+
adapter: adapterType = "sse",
|
|
505
|
+
headers = {},
|
|
506
|
+
onMessage,
|
|
507
|
+
onContentUpdate,
|
|
508
|
+
onToolApproval,
|
|
509
|
+
onError,
|
|
510
|
+
onComplete,
|
|
511
|
+
onThinking,
|
|
512
|
+
autoApprove = false,
|
|
513
|
+
maxRetries = 3
|
|
514
|
+
} = config;
|
|
515
|
+
const [messages, setMessages] = (0, import_react3.useState)(initialMessages);
|
|
516
|
+
const [streamingContent, setStreamingContent] = (0, import_react3.useState)("");
|
|
517
|
+
const [thinkingContent, setThinkingContent] = (0, import_react3.useState)("");
|
|
518
|
+
const [activeToolCalls, setActiveToolCalls] = (0, import_react3.useState)([]);
|
|
519
|
+
const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
|
|
520
|
+
const [isStreaming, setIsStreaming] = (0, import_react3.useState)(false);
|
|
521
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
522
|
+
const adapterRef = (0, import_react3.useRef)(getAdapter(adapterType));
|
|
523
|
+
const abortControllerRef = (0, import_react3.useRef)(null);
|
|
524
|
+
const conversationIdRef = (0, import_react3.useRef)(initialConversationId || generateId());
|
|
525
|
+
const pendingApprovalsRef = (0, import_react3.useRef)(
|
|
526
|
+
/* @__PURE__ */ new Map()
|
|
527
|
+
);
|
|
528
|
+
const retryCountRef = (0, import_react3.useRef)(0);
|
|
529
|
+
(0, import_react3.useEffect)(() => {
|
|
530
|
+
return () => {
|
|
531
|
+
abortControllerRef.current?.abort();
|
|
532
|
+
adapterRef.current.close();
|
|
533
|
+
};
|
|
534
|
+
}, []);
|
|
535
|
+
const processChunk = (0, import_react3.useCallback)(
|
|
536
|
+
(chunk) => {
|
|
537
|
+
switch (chunk.type) {
|
|
538
|
+
case "content":
|
|
539
|
+
if (chunk.delta) {
|
|
540
|
+
setStreamingContent((prev) => prev + chunk.content);
|
|
541
|
+
} else {
|
|
542
|
+
setStreamingContent(chunk.content);
|
|
543
|
+
}
|
|
544
|
+
onContentUpdate?.(chunk.content);
|
|
545
|
+
break;
|
|
546
|
+
case "thinking": {
|
|
547
|
+
if (chunk.delta) {
|
|
548
|
+
setThinkingContent((prev) => prev + chunk.content);
|
|
549
|
+
} else {
|
|
550
|
+
setThinkingContent(chunk.content);
|
|
551
|
+
}
|
|
552
|
+
const thinking = {
|
|
553
|
+
type: "thinking",
|
|
554
|
+
content: chunk.content,
|
|
555
|
+
isComplete: false
|
|
556
|
+
};
|
|
557
|
+
onThinking?.(thinking);
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
case "tool_call":
|
|
561
|
+
setActiveToolCalls((prev) => {
|
|
562
|
+
const existing = prev.find((tc) => tc.id === chunk.toolCall.id);
|
|
563
|
+
if (existing) {
|
|
564
|
+
return prev.map(
|
|
565
|
+
(tc) => tc.id === chunk.toolCall.id ? chunk.toolCall : tc
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
return [...prev, chunk.toolCall];
|
|
569
|
+
});
|
|
570
|
+
if (chunk.toolCall.needsApproval && !autoApprove) {
|
|
571
|
+
onToolApproval?.(chunk.toolCall);
|
|
572
|
+
} else if (autoApprove && chunk.toolCall.needsApproval) {
|
|
573
|
+
pendingApprovalsRef.current.set(chunk.toolCall.id, {
|
|
574
|
+
toolCallId: chunk.toolCall.id,
|
|
575
|
+
approved: true
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
case "tool_result":
|
|
580
|
+
setActiveToolCalls(
|
|
581
|
+
(prev) => prev.map(
|
|
582
|
+
(tc) => tc.id === chunk.toolCall.id ? { ...chunk.toolCall, state: "complete" } : tc
|
|
583
|
+
)
|
|
584
|
+
);
|
|
585
|
+
break;
|
|
586
|
+
case "tool_state":
|
|
587
|
+
setActiveToolCalls(
|
|
588
|
+
(prev) => prev.map(
|
|
589
|
+
(tc) => tc.id === chunk.toolCallId ? { ...tc, state: chunk.state } : tc
|
|
590
|
+
)
|
|
591
|
+
);
|
|
592
|
+
break;
|
|
593
|
+
case "done": {
|
|
594
|
+
setIsStreaming(false);
|
|
595
|
+
setIsLoading(false);
|
|
596
|
+
const finalContent = streamingContent;
|
|
597
|
+
const finalThinking = thinkingContent;
|
|
598
|
+
const finalToolCalls = [...activeToolCalls];
|
|
599
|
+
if (finalContent || finalToolCalls.length > 0) {
|
|
600
|
+
const assistantMessage = createMessage("assistant", finalContent, {
|
|
601
|
+
toolCalls: finalToolCalls.length > 0 ? finalToolCalls : void 0,
|
|
602
|
+
thinking: finalThinking ? { type: "thinking", content: finalThinking, isComplete: true } : void 0,
|
|
603
|
+
metadata: chunk.metadata
|
|
604
|
+
});
|
|
605
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
606
|
+
onMessage?.(assistantMessage);
|
|
607
|
+
onComplete?.(assistantMessage);
|
|
608
|
+
}
|
|
609
|
+
setStreamingContent("");
|
|
610
|
+
setThinkingContent("");
|
|
611
|
+
setActiveToolCalls([]);
|
|
612
|
+
retryCountRef.current = 0;
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
case "error": {
|
|
616
|
+
const err = new Error(chunk.error);
|
|
617
|
+
setError(err);
|
|
618
|
+
setIsStreaming(false);
|
|
619
|
+
setIsLoading(false);
|
|
620
|
+
onError?.(err);
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
[
|
|
626
|
+
streamingContent,
|
|
627
|
+
thinkingContent,
|
|
628
|
+
activeToolCalls,
|
|
629
|
+
autoApprove,
|
|
630
|
+
onContentUpdate,
|
|
631
|
+
onThinking,
|
|
632
|
+
onToolApproval,
|
|
633
|
+
onMessage,
|
|
634
|
+
onComplete,
|
|
635
|
+
onError
|
|
636
|
+
]
|
|
637
|
+
);
|
|
638
|
+
const sendMessage = (0, import_react3.useCallback)(
|
|
639
|
+
async (content) => {
|
|
640
|
+
if (!content.trim()) return;
|
|
641
|
+
const userMessage = createMessage("user", content);
|
|
642
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
643
|
+
setError(null);
|
|
644
|
+
setIsLoading(true);
|
|
645
|
+
setIsStreaming(true);
|
|
646
|
+
setStreamingContent("");
|
|
647
|
+
setThinkingContent("");
|
|
648
|
+
setActiveToolCalls([]);
|
|
649
|
+
abortControllerRef.current = new AbortController();
|
|
650
|
+
try {
|
|
651
|
+
const adapter = adapterRef.current;
|
|
652
|
+
await adapter.connect(endpoint, {
|
|
653
|
+
headers,
|
|
654
|
+
signal: abortControllerRef.current.signal
|
|
655
|
+
});
|
|
656
|
+
adapter.onMessage(processChunk);
|
|
657
|
+
adapter.onError((err) => {
|
|
658
|
+
setError(err);
|
|
659
|
+
setIsStreaming(false);
|
|
660
|
+
setIsLoading(false);
|
|
661
|
+
onError?.(err);
|
|
662
|
+
});
|
|
663
|
+
adapter.onClose(() => {
|
|
664
|
+
setIsStreaming(false);
|
|
665
|
+
});
|
|
666
|
+
const allMessages = [...messages, userMessage];
|
|
667
|
+
await adapter.send({
|
|
668
|
+
messages: allMessages,
|
|
669
|
+
conversationId: conversationIdRef.current,
|
|
670
|
+
agentId,
|
|
671
|
+
stream: true,
|
|
672
|
+
toolApprovals: Array.from(pendingApprovalsRef.current.values())
|
|
673
|
+
});
|
|
674
|
+
pendingApprovalsRef.current.clear();
|
|
675
|
+
} catch (err) {
|
|
676
|
+
const error2 = err;
|
|
677
|
+
if (error2.name !== "AbortError") {
|
|
678
|
+
setError(error2);
|
|
679
|
+
onError?.(error2);
|
|
680
|
+
if (retryCountRef.current < maxRetries) {
|
|
681
|
+
retryCountRef.current++;
|
|
682
|
+
setMessages((prev) => prev.slice(0, -1));
|
|
683
|
+
await sendMessage(content);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
setIsStreaming(false);
|
|
688
|
+
setIsLoading(false);
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
[endpoint, headers, agentId, messages, processChunk, onError, maxRetries]
|
|
692
|
+
);
|
|
693
|
+
const approveToolCall = (0, import_react3.useCallback)((toolCallId) => {
|
|
694
|
+
pendingApprovalsRef.current.set(toolCallId, {
|
|
695
|
+
toolCallId,
|
|
696
|
+
approved: true
|
|
697
|
+
});
|
|
698
|
+
setActiveToolCalls(
|
|
699
|
+
(prev) => prev.map(
|
|
700
|
+
(tc) => tc.id === toolCallId ? { ...tc, state: "executing" } : tc
|
|
701
|
+
)
|
|
702
|
+
);
|
|
703
|
+
}, []);
|
|
704
|
+
const denyToolCall = (0, import_react3.useCallback)(
|
|
705
|
+
(toolCallId, reason) => {
|
|
706
|
+
pendingApprovalsRef.current.set(toolCallId, {
|
|
707
|
+
toolCallId,
|
|
708
|
+
approved: false,
|
|
709
|
+
reason
|
|
710
|
+
});
|
|
711
|
+
setActiveToolCalls(
|
|
712
|
+
(prev) => prev.map(
|
|
713
|
+
(tc) => tc.id === toolCallId ? { ...tc, state: "approval-denied" } : tc
|
|
714
|
+
)
|
|
715
|
+
);
|
|
716
|
+
},
|
|
717
|
+
[]
|
|
718
|
+
);
|
|
719
|
+
const stop = (0, import_react3.useCallback)(() => {
|
|
720
|
+
abortControllerRef.current?.abort();
|
|
721
|
+
adapterRef.current.close();
|
|
722
|
+
setIsStreaming(false);
|
|
723
|
+
setIsLoading(false);
|
|
724
|
+
if (streamingContent) {
|
|
725
|
+
const partialMessage = createMessage("assistant", streamingContent, {
|
|
726
|
+
toolCalls: activeToolCalls.length > 0 ? activeToolCalls : void 0,
|
|
727
|
+
thinking: thinkingContent ? { type: "thinking", content: thinkingContent, isComplete: false } : void 0,
|
|
728
|
+
metadata: { finishReason: "stopped" }
|
|
729
|
+
});
|
|
730
|
+
setMessages((prev) => [...prev, partialMessage]);
|
|
731
|
+
}
|
|
732
|
+
setStreamingContent("");
|
|
733
|
+
setThinkingContent("");
|
|
734
|
+
setActiveToolCalls([]);
|
|
735
|
+
}, [streamingContent, thinkingContent, activeToolCalls]);
|
|
736
|
+
const clear = (0, import_react3.useCallback)(() => {
|
|
737
|
+
stop();
|
|
738
|
+
setMessages([]);
|
|
739
|
+
setError(null);
|
|
740
|
+
conversationIdRef.current = generateId();
|
|
741
|
+
}, [stop]);
|
|
742
|
+
const reload = (0, import_react3.useCallback)(async () => {
|
|
743
|
+
const lastUserMessageIndex = messages.map((m) => m.role).lastIndexOf("user");
|
|
744
|
+
if (lastUserMessageIndex === -1) return;
|
|
745
|
+
const lastUserMessage = messages[lastUserMessageIndex];
|
|
746
|
+
setMessages((prev) => prev.slice(0, lastUserMessageIndex));
|
|
747
|
+
await sendMessage(lastUserMessage.content);
|
|
748
|
+
}, [messages, sendMessage]);
|
|
749
|
+
return {
|
|
750
|
+
messages,
|
|
751
|
+
streamingContent,
|
|
752
|
+
thinkingContent,
|
|
753
|
+
activeToolCalls,
|
|
754
|
+
isLoading,
|
|
755
|
+
isStreaming,
|
|
756
|
+
error,
|
|
757
|
+
sendMessage,
|
|
758
|
+
approveToolCall,
|
|
759
|
+
denyToolCall,
|
|
760
|
+
stop,
|
|
761
|
+
clear,
|
|
762
|
+
reload,
|
|
763
|
+
setMessages
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// src/useAgent.ts
|
|
768
|
+
var import_react4 = require("react");
|
|
769
|
+
function useAgent(config) {
|
|
770
|
+
const {
|
|
771
|
+
endpoint,
|
|
772
|
+
agentId,
|
|
773
|
+
headers = {},
|
|
774
|
+
onStart,
|
|
775
|
+
onContentUpdate,
|
|
776
|
+
onToolApproval,
|
|
777
|
+
onError,
|
|
778
|
+
onComplete,
|
|
779
|
+
onThinking
|
|
780
|
+
} = config;
|
|
781
|
+
const [content, setContent] = (0, import_react4.useState)("");
|
|
782
|
+
const [thinkingContent, setThinkingContent] = (0, import_react4.useState)("");
|
|
783
|
+
const [activeToolCalls, setActiveToolCalls] = (0, import_react4.useState)([]);
|
|
784
|
+
const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
|
|
785
|
+
const [isStreaming, setIsStreaming] = (0, import_react4.useState)(false);
|
|
786
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
787
|
+
const [metadata, setMetadata] = (0, import_react4.useState)(
|
|
788
|
+
null
|
|
789
|
+
);
|
|
790
|
+
const abortControllerRef = (0, import_react4.useRef)(null);
|
|
791
|
+
const pendingApprovalsRef = (0, import_react4.useRef)(
|
|
792
|
+
/* @__PURE__ */ new Map()
|
|
793
|
+
);
|
|
794
|
+
(0, import_react4.useEffect)(() => {
|
|
795
|
+
return () => {
|
|
796
|
+
abortControllerRef.current?.abort();
|
|
797
|
+
};
|
|
798
|
+
}, []);
|
|
799
|
+
async function* executeStream(input, context) {
|
|
800
|
+
onStart?.();
|
|
801
|
+
setError(null);
|
|
802
|
+
setIsLoading(true);
|
|
803
|
+
setIsStreaming(true);
|
|
804
|
+
setContent("");
|
|
805
|
+
setThinkingContent("");
|
|
806
|
+
setActiveToolCalls([]);
|
|
807
|
+
setMetadata(null);
|
|
808
|
+
abortControllerRef.current = new AbortController();
|
|
809
|
+
try {
|
|
810
|
+
const response = await fetch(endpoint, {
|
|
811
|
+
method: "POST",
|
|
812
|
+
headers: {
|
|
813
|
+
"Content-Type": "application/json",
|
|
814
|
+
Accept: "text/event-stream",
|
|
815
|
+
...headers
|
|
816
|
+
},
|
|
817
|
+
body: JSON.stringify({
|
|
818
|
+
input,
|
|
819
|
+
agentId,
|
|
820
|
+
context,
|
|
821
|
+
stream: true,
|
|
822
|
+
toolApprovals: Array.from(pendingApprovalsRef.current.values())
|
|
823
|
+
}),
|
|
824
|
+
signal: abortControllerRef.current.signal
|
|
825
|
+
});
|
|
826
|
+
if (!response.ok) {
|
|
827
|
+
throw new Error(`HTTP error: ${response.status}`);
|
|
828
|
+
}
|
|
829
|
+
const reader = response.body?.getReader();
|
|
830
|
+
if (!reader) {
|
|
831
|
+
throw new Error("No response body");
|
|
832
|
+
}
|
|
833
|
+
const decoder = new TextDecoder();
|
|
834
|
+
let buffer = "";
|
|
835
|
+
let accumulatedContent = "";
|
|
836
|
+
let accumulatedThinking = "";
|
|
837
|
+
let reading = true;
|
|
838
|
+
while (reading) {
|
|
839
|
+
const { done, value } = await reader.read();
|
|
840
|
+
if (done) {
|
|
841
|
+
reading = false;
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
buffer += decoder.decode(value, { stream: true });
|
|
845
|
+
const lines = buffer.split("\n");
|
|
846
|
+
buffer = lines.pop() || "";
|
|
847
|
+
for (const line of lines) {
|
|
848
|
+
if (line.startsWith("data: ")) {
|
|
849
|
+
const data = line.slice(6);
|
|
850
|
+
if (data === "[DONE]") {
|
|
851
|
+
setIsStreaming(false);
|
|
852
|
+
setIsLoading(false);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
try {
|
|
856
|
+
const chunk = JSON.parse(data);
|
|
857
|
+
switch (chunk.type) {
|
|
858
|
+
case "content":
|
|
859
|
+
if (chunk.delta) {
|
|
860
|
+
accumulatedContent += chunk.content;
|
|
861
|
+
} else {
|
|
862
|
+
accumulatedContent = chunk.content;
|
|
863
|
+
}
|
|
864
|
+
setContent(accumulatedContent);
|
|
865
|
+
onContentUpdate?.(chunk.content);
|
|
866
|
+
break;
|
|
867
|
+
case "thinking": {
|
|
868
|
+
if (chunk.delta) {
|
|
869
|
+
accumulatedThinking += chunk.content;
|
|
870
|
+
} else {
|
|
871
|
+
accumulatedThinking = chunk.content;
|
|
872
|
+
}
|
|
873
|
+
setThinkingContent(accumulatedThinking);
|
|
874
|
+
const thinking = {
|
|
875
|
+
type: "thinking",
|
|
876
|
+
content: chunk.content,
|
|
877
|
+
isComplete: false
|
|
878
|
+
};
|
|
879
|
+
onThinking?.(thinking);
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
882
|
+
case "tool_call":
|
|
883
|
+
setActiveToolCalls((prev) => {
|
|
884
|
+
const existing = prev.find(
|
|
885
|
+
(tc) => tc.id === chunk.toolCall.id
|
|
886
|
+
);
|
|
887
|
+
if (existing) {
|
|
888
|
+
return prev.map(
|
|
889
|
+
(tc) => tc.id === chunk.toolCall.id ? chunk.toolCall : tc
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
return [...prev, chunk.toolCall];
|
|
893
|
+
});
|
|
894
|
+
if (chunk.toolCall.needsApproval) {
|
|
895
|
+
onToolApproval?.(chunk.toolCall);
|
|
896
|
+
}
|
|
897
|
+
break;
|
|
898
|
+
case "tool_result":
|
|
899
|
+
setActiveToolCalls(
|
|
900
|
+
(prev) => prev.map(
|
|
901
|
+
(tc) => tc.id === chunk.toolCall.id ? { ...chunk.toolCall, state: "complete" } : tc
|
|
902
|
+
)
|
|
903
|
+
);
|
|
904
|
+
break;
|
|
905
|
+
case "tool_state":
|
|
906
|
+
setActiveToolCalls(
|
|
907
|
+
(prev) => prev.map(
|
|
908
|
+
(tc) => tc.id === chunk.toolCallId ? { ...tc, state: chunk.state } : tc
|
|
909
|
+
)
|
|
910
|
+
);
|
|
911
|
+
break;
|
|
912
|
+
case "done":
|
|
913
|
+
setMetadata(chunk.metadata || null);
|
|
914
|
+
setIsStreaming(false);
|
|
915
|
+
setIsLoading(false);
|
|
916
|
+
break;
|
|
917
|
+
case "error": {
|
|
918
|
+
const err = new Error(chunk.error);
|
|
919
|
+
setError(err);
|
|
920
|
+
setIsStreaming(false);
|
|
921
|
+
setIsLoading(false);
|
|
922
|
+
onError?.(err);
|
|
923
|
+
break;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
yield chunk;
|
|
927
|
+
} catch {
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
pendingApprovalsRef.current.clear();
|
|
933
|
+
} catch (err) {
|
|
934
|
+
const error2 = err;
|
|
935
|
+
if (error2.name !== "AbortError") {
|
|
936
|
+
setError(error2);
|
|
937
|
+
onError?.(error2);
|
|
938
|
+
}
|
|
939
|
+
setIsStreaming(false);
|
|
940
|
+
setIsLoading(false);
|
|
941
|
+
throw error2;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
const execute = (0, import_react4.useCallback)(
|
|
945
|
+
async (input, context) => {
|
|
946
|
+
onStart?.();
|
|
947
|
+
setError(null);
|
|
948
|
+
setIsLoading(true);
|
|
949
|
+
setContent("");
|
|
950
|
+
setThinkingContent("");
|
|
951
|
+
setActiveToolCalls([]);
|
|
952
|
+
setMetadata(null);
|
|
953
|
+
abortControllerRef.current = new AbortController();
|
|
954
|
+
try {
|
|
955
|
+
const response = await fetch(endpoint, {
|
|
956
|
+
method: "POST",
|
|
957
|
+
headers: {
|
|
958
|
+
"Content-Type": "application/json",
|
|
959
|
+
...headers
|
|
960
|
+
},
|
|
961
|
+
body: JSON.stringify({
|
|
962
|
+
input,
|
|
963
|
+
agentId,
|
|
964
|
+
context,
|
|
965
|
+
stream: false,
|
|
966
|
+
toolApprovals: Array.from(pendingApprovalsRef.current.values())
|
|
967
|
+
}),
|
|
968
|
+
signal: abortControllerRef.current.signal
|
|
969
|
+
});
|
|
970
|
+
if (!response.ok) {
|
|
971
|
+
throw new Error(`HTTP error: ${response.status}`);
|
|
972
|
+
}
|
|
973
|
+
const result = await response.json();
|
|
974
|
+
setContent(result.content);
|
|
975
|
+
setMetadata({
|
|
976
|
+
tokensUsed: result.metadata?.tokensUsed,
|
|
977
|
+
latencyMs: result.metadata?.latencyMs,
|
|
978
|
+
finishReason: result.finishReason
|
|
979
|
+
});
|
|
980
|
+
if (result.toolCalls) {
|
|
981
|
+
setActiveToolCalls(
|
|
982
|
+
result.toolCalls.map((tc) => ({
|
|
983
|
+
...tc,
|
|
984
|
+
state: "complete"
|
|
985
|
+
}))
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
setIsLoading(false);
|
|
989
|
+
onComplete?.(result);
|
|
990
|
+
pendingApprovalsRef.current.clear();
|
|
991
|
+
return result;
|
|
992
|
+
} catch (err) {
|
|
993
|
+
const error2 = err;
|
|
994
|
+
if (error2.name !== "AbortError") {
|
|
995
|
+
setError(error2);
|
|
996
|
+
onError?.(error2);
|
|
997
|
+
}
|
|
998
|
+
setIsLoading(false);
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
},
|
|
1002
|
+
[endpoint, headers, agentId, onStart, onComplete, onError]
|
|
1003
|
+
);
|
|
1004
|
+
const approveToolCall = (0, import_react4.useCallback)((toolCallId) => {
|
|
1005
|
+
pendingApprovalsRef.current.set(toolCallId, {
|
|
1006
|
+
toolCallId,
|
|
1007
|
+
approved: true
|
|
1008
|
+
});
|
|
1009
|
+
setActiveToolCalls(
|
|
1010
|
+
(prev) => prev.map(
|
|
1011
|
+
(tc) => tc.id === toolCallId ? { ...tc, state: "executing" } : tc
|
|
1012
|
+
)
|
|
1013
|
+
);
|
|
1014
|
+
}, []);
|
|
1015
|
+
const denyToolCall = (0, import_react4.useCallback)(
|
|
1016
|
+
(toolCallId, reason) => {
|
|
1017
|
+
pendingApprovalsRef.current.set(toolCallId, {
|
|
1018
|
+
toolCallId,
|
|
1019
|
+
approved: false,
|
|
1020
|
+
reason
|
|
1021
|
+
});
|
|
1022
|
+
setActiveToolCalls(
|
|
1023
|
+
(prev) => prev.map(
|
|
1024
|
+
(tc) => tc.id === toolCallId ? { ...tc, state: "approval-denied" } : tc
|
|
1025
|
+
)
|
|
1026
|
+
);
|
|
1027
|
+
},
|
|
1028
|
+
[]
|
|
1029
|
+
);
|
|
1030
|
+
const stop = (0, import_react4.useCallback)(() => {
|
|
1031
|
+
abortControllerRef.current?.abort();
|
|
1032
|
+
setIsStreaming(false);
|
|
1033
|
+
setIsLoading(false);
|
|
1034
|
+
}, []);
|
|
1035
|
+
const reset = (0, import_react4.useCallback)(() => {
|
|
1036
|
+
stop();
|
|
1037
|
+
setContent("");
|
|
1038
|
+
setThinkingContent("");
|
|
1039
|
+
setActiveToolCalls([]);
|
|
1040
|
+
setError(null);
|
|
1041
|
+
setMetadata(null);
|
|
1042
|
+
pendingApprovalsRef.current.clear();
|
|
1043
|
+
}, [stop]);
|
|
1044
|
+
return {
|
|
1045
|
+
execute,
|
|
1046
|
+
executeStream,
|
|
1047
|
+
content,
|
|
1048
|
+
thinkingContent,
|
|
1049
|
+
activeToolCalls,
|
|
1050
|
+
isLoading,
|
|
1051
|
+
isStreaming,
|
|
1052
|
+
error,
|
|
1053
|
+
metadata,
|
|
1054
|
+
approveToolCall,
|
|
1055
|
+
denyToolCall,
|
|
1056
|
+
stop,
|
|
1057
|
+
reset
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
262
1060
|
// Annotate the CommonJS export names for ESM import in node:
|
|
263
1061
|
0 && (module.exports = {
|
|
264
1062
|
AgentResponse,
|
|
265
1063
|
StreamingResponse,
|
|
1064
|
+
createHTTPStreamAdapter,
|
|
1065
|
+
createMessage,
|
|
1066
|
+
createSSEAdapter,
|
|
1067
|
+
fetchChat,
|
|
1068
|
+
generateId,
|
|
1069
|
+
getAdapter,
|
|
1070
|
+
useAgent,
|
|
1071
|
+
useChat,
|
|
266
1072
|
useFormattedContent,
|
|
267
1073
|
useStreamingContent
|
|
268
1074
|
});
|