@vegintech/langchain-react-agent 0.0.8 → 0.0.10
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/README.md +1 -1
- package/dist/index.d.mts +5 -3
- package/dist/index.mjs +582 -59
- package/package.json +8 -9
package/README.md
CHANGED
|
@@ -137,7 +137,7 @@ const searchTool: BackendTool = {
|
|
|
137
137
|
render: (props) => (
|
|
138
138
|
<div className="tool-search">
|
|
139
139
|
{props.status === "pending" && "等待执行..."}
|
|
140
|
-
{props.status === "running" && `搜索: ${props.
|
|
140
|
+
{props.status === "running" && `搜索: ${props.args.query}`}
|
|
141
141
|
{props.status === "success" && `找到 ${props.result?.count || 0} 条结果`}
|
|
142
142
|
</div>
|
|
143
143
|
),
|
package/dist/index.d.mts
CHANGED
|
@@ -150,7 +150,7 @@ interface ChatMessage {
|
|
|
150
150
|
interface ToolCallInput {
|
|
151
151
|
id: string;
|
|
152
152
|
name: string;
|
|
153
|
-
|
|
153
|
+
args: Record<string, unknown>;
|
|
154
154
|
/** 后端工具执行结果(来自 ToolMessage) */
|
|
155
155
|
result?: unknown;
|
|
156
156
|
}
|
|
@@ -160,7 +160,7 @@ type ToolExecutionStatus = "pending" | "running" | "success" | "error";
|
|
|
160
160
|
interface ToolExecutionRecord {
|
|
161
161
|
callId: string;
|
|
162
162
|
name: string;
|
|
163
|
-
|
|
163
|
+
args: Record<string, unknown>;
|
|
164
164
|
status: ToolExecutionStatus;
|
|
165
165
|
result?: unknown;
|
|
166
166
|
error?: string;
|
|
@@ -168,7 +168,7 @@ interface ToolExecutionRecord {
|
|
|
168
168
|
/** 渲染函数的 props */
|
|
169
169
|
interface ToolRenderProps<TArgs = Record<string, unknown>> {
|
|
170
170
|
name: string;
|
|
171
|
-
|
|
171
|
+
args: TArgs;
|
|
172
172
|
result?: unknown;
|
|
173
173
|
status: ToolExecutionStatus;
|
|
174
174
|
error?: string;
|
|
@@ -241,6 +241,8 @@ interface AgentChatProps {
|
|
|
241
241
|
agentState?: Record<string, unknown>;
|
|
242
242
|
/** Agent 状态变化回调 */
|
|
243
243
|
onAgentStateChange?: (state: Record<string, unknown>) => void;
|
|
244
|
+
/** 是否显示调试面板,默认 false。仅在开发环境生效 */
|
|
245
|
+
showDebug?: boolean;
|
|
244
246
|
}
|
|
245
247
|
/** AgentChat 组件对外暴露的方法 */
|
|
246
248
|
interface AgentChatRef {
|
package/dist/index.mjs
CHANGED
|
@@ -65,7 +65,7 @@ const ToolCallRenderer = ({ tool, record }) => {
|
|
|
65
65
|
className: "tool-call-wrapper",
|
|
66
66
|
children: tool.render({
|
|
67
67
|
name: record.name,
|
|
68
|
-
|
|
68
|
+
args: record.args,
|
|
69
69
|
result: record.result,
|
|
70
70
|
status: record.status,
|
|
71
71
|
error: record.error
|
|
@@ -226,7 +226,7 @@ const renderMessageContent = (message, isLastMessage, isLoading, tools, toolExec
|
|
|
226
226
|
children: /* @__PURE__ */ jsx(ReasoningContent, { content: message.reasoningContent })
|
|
227
227
|
}),
|
|
228
228
|
message.content && /* @__PURE__ */ jsx("div", {
|
|
229
|
-
style: { marginTop: message.reasoningContent ? "8px" :
|
|
229
|
+
style: { marginTop: message.reasoningContent ? "8px" : "3px" },
|
|
230
230
|
children: /* @__PURE__ */ jsx(Streamdown, {
|
|
231
231
|
components: {
|
|
232
232
|
p: CustomParagraph,
|
|
@@ -262,16 +262,20 @@ const renderToolCalls = (toolCalls, tools, toolExecutions) => {
|
|
|
262
262
|
return toolCalls.map((call) => {
|
|
263
263
|
const tool = findTool(tools, call.name);
|
|
264
264
|
let record;
|
|
265
|
-
if (tool && isFrontendTool(tool))
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
265
|
+
if (tool && isFrontendTool(tool)) {
|
|
266
|
+
const execution = toolExecutions.get(call.id);
|
|
267
|
+
record = {
|
|
268
|
+
callId: call.id,
|
|
269
|
+
name: call.name,
|
|
270
|
+
args: call.args,
|
|
271
|
+
status: execution?.status || "pending",
|
|
272
|
+
result: execution?.result,
|
|
273
|
+
error: execution?.error
|
|
274
|
+
};
|
|
275
|
+
} else record = {
|
|
272
276
|
callId: call.id,
|
|
273
277
|
name: call.name,
|
|
274
|
-
|
|
278
|
+
args: call.args,
|
|
275
279
|
status: call.result !== void 0 ? "success" : "pending",
|
|
276
280
|
result: call.result
|
|
277
281
|
};
|
|
@@ -373,6 +377,443 @@ const MessageList = ({ messages, isLoading = false, className = "", tools, toolE
|
|
|
373
377
|
});
|
|
374
378
|
};
|
|
375
379
|
//#endregion
|
|
380
|
+
//#region src/components/DebugPanel.tsx
|
|
381
|
+
const isDevelopment = () => {
|
|
382
|
+
if (typeof process !== "undefined" && process.env) return process.env.NODE_ENV === "development";
|
|
383
|
+
const viteEnv = import.meta.env;
|
|
384
|
+
if (typeof import.meta !== "undefined" && viteEnv) return viteEnv.DEV === true || viteEnv.MODE === "development";
|
|
385
|
+
return false;
|
|
386
|
+
};
|
|
387
|
+
function DebugPanel({ messages, streamState, visible = true }) {
|
|
388
|
+
if (!isDevelopment() || !visible) return null;
|
|
389
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
390
|
+
const [activeTab, setActiveTab] = useState("messages");
|
|
391
|
+
const [expandedRows, setExpandedRows] = useState(/* @__PURE__ */ new Set());
|
|
392
|
+
const [buttonPos, setButtonPos] = useState({
|
|
393
|
+
x: 0,
|
|
394
|
+
y: 0
|
|
395
|
+
});
|
|
396
|
+
const [isDraggingButton, setIsDraggingButton] = useState(false);
|
|
397
|
+
const buttonDragRef = useRef(null);
|
|
398
|
+
const [dialogPos, setDialogPos] = useState({
|
|
399
|
+
x: -400,
|
|
400
|
+
y: -300
|
|
401
|
+
});
|
|
402
|
+
const [isDraggingDialog, setIsDraggingDialog] = useState(false);
|
|
403
|
+
const dialogDragRef = useRef(null);
|
|
404
|
+
const handleButtonMouseDown = useCallback((e) => {
|
|
405
|
+
e.preventDefault();
|
|
406
|
+
buttonDragRef.current = {
|
|
407
|
+
startX: e.clientX,
|
|
408
|
+
startY: e.clientY,
|
|
409
|
+
initialX: buttonPos.x,
|
|
410
|
+
initialY: buttonPos.y
|
|
411
|
+
};
|
|
412
|
+
setIsDraggingButton(true);
|
|
413
|
+
}, [buttonPos]);
|
|
414
|
+
const handleDialogMouseDown = useCallback((e) => {
|
|
415
|
+
if (!e.target.closest(".debug-panel-header")) return;
|
|
416
|
+
e.preventDefault();
|
|
417
|
+
dialogDragRef.current = {
|
|
418
|
+
startX: e.clientX,
|
|
419
|
+
startY: e.clientY,
|
|
420
|
+
initialX: dialogPos.x,
|
|
421
|
+
initialY: dialogPos.y
|
|
422
|
+
};
|
|
423
|
+
setIsDraggingDialog(true);
|
|
424
|
+
}, [dialogPos]);
|
|
425
|
+
useEffect(() => {
|
|
426
|
+
const handleMouseMove = (e) => {
|
|
427
|
+
if (isDraggingButton && buttonDragRef.current) {
|
|
428
|
+
const dx = e.clientX - buttonDragRef.current.startX;
|
|
429
|
+
const dy = e.clientY - buttonDragRef.current.startY;
|
|
430
|
+
setButtonPos({
|
|
431
|
+
x: buttonDragRef.current.initialX + dx,
|
|
432
|
+
y: buttonDragRef.current.initialY + dy
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (isDraggingDialog && dialogDragRef.current) {
|
|
436
|
+
const dx = e.clientX - dialogDragRef.current.startX;
|
|
437
|
+
const dy = e.clientY - dialogDragRef.current.startY;
|
|
438
|
+
setDialogPos({
|
|
439
|
+
x: dialogDragRef.current.initialX + dx,
|
|
440
|
+
y: dialogDragRef.current.initialY + dy
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const handleMouseUp = () => {
|
|
445
|
+
setIsDraggingButton(false);
|
|
446
|
+
setIsDraggingDialog(false);
|
|
447
|
+
buttonDragRef.current = null;
|
|
448
|
+
dialogDragRef.current = null;
|
|
449
|
+
};
|
|
450
|
+
if (isDraggingButton || isDraggingDialog) {
|
|
451
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
452
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
453
|
+
}
|
|
454
|
+
return () => {
|
|
455
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
456
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
457
|
+
};
|
|
458
|
+
}, [isDraggingButton, isDraggingDialog]);
|
|
459
|
+
const formatJson = (data) => {
|
|
460
|
+
try {
|
|
461
|
+
return JSON.stringify(data, null, 2);
|
|
462
|
+
} catch {
|
|
463
|
+
return String(data);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
const toggleRow = (index) => {
|
|
467
|
+
setExpandedRows((prev) => {
|
|
468
|
+
const next = new Set(prev);
|
|
469
|
+
if (next.has(index)) next.delete(index);
|
|
470
|
+
else next.add(index);
|
|
471
|
+
return next;
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
const getTypeTagStyle = (type) => {
|
|
475
|
+
const color = {
|
|
476
|
+
human: {
|
|
477
|
+
bg: "#e6f4ff",
|
|
478
|
+
color: "#0958d9"
|
|
479
|
+
},
|
|
480
|
+
ai: {
|
|
481
|
+
bg: "#f6ffed",
|
|
482
|
+
color: "#389e0d"
|
|
483
|
+
},
|
|
484
|
+
tool: {
|
|
485
|
+
bg: "#fff7e6",
|
|
486
|
+
color: "#d46b08"
|
|
487
|
+
},
|
|
488
|
+
system: {
|
|
489
|
+
bg: "#f9f0ff",
|
|
490
|
+
color: "#722ed1"
|
|
491
|
+
},
|
|
492
|
+
function: {
|
|
493
|
+
bg: "#fff2f0",
|
|
494
|
+
color: "#cf1322"
|
|
495
|
+
}
|
|
496
|
+
}[type || ""] || {
|
|
497
|
+
bg: "#f5f5f5",
|
|
498
|
+
color: "#666"
|
|
499
|
+
};
|
|
500
|
+
return {
|
|
501
|
+
display: "inline-block",
|
|
502
|
+
padding: "2px 8px",
|
|
503
|
+
borderRadius: "4px",
|
|
504
|
+
fontSize: "12px",
|
|
505
|
+
fontWeight: 500,
|
|
506
|
+
background: color.bg,
|
|
507
|
+
color: color.color
|
|
508
|
+
};
|
|
509
|
+
};
|
|
510
|
+
const formatContent = (content) => {
|
|
511
|
+
if (!content) return "-";
|
|
512
|
+
return content;
|
|
513
|
+
};
|
|
514
|
+
const buttonStyle = {
|
|
515
|
+
position: "fixed",
|
|
516
|
+
right: `${20 - buttonPos.x}px`,
|
|
517
|
+
bottom: `${20 - buttonPos.y}px`,
|
|
518
|
+
width: "48px",
|
|
519
|
+
height: "48px",
|
|
520
|
+
borderRadius: "50%",
|
|
521
|
+
background: "#1677ff",
|
|
522
|
+
color: "#fff",
|
|
523
|
+
border: "none",
|
|
524
|
+
cursor: isDraggingButton ? "grabbing" : "grab",
|
|
525
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
526
|
+
display: "flex",
|
|
527
|
+
alignItems: "center",
|
|
528
|
+
justifyContent: "center",
|
|
529
|
+
fontSize: "20px",
|
|
530
|
+
zIndex: 9998,
|
|
531
|
+
transition: isDraggingButton ? "none" : "box-shadow 0.2s",
|
|
532
|
+
userSelect: "none"
|
|
533
|
+
};
|
|
534
|
+
const dialogStyle = {
|
|
535
|
+
position: "fixed",
|
|
536
|
+
right: `${20 - dialogPos.x}px`,
|
|
537
|
+
bottom: `${80 - dialogPos.y}px`,
|
|
538
|
+
width: "800px",
|
|
539
|
+
maxWidth: "90vw",
|
|
540
|
+
height: "600px",
|
|
541
|
+
maxHeight: "85vh",
|
|
542
|
+
background: "#fff",
|
|
543
|
+
borderRadius: "12px",
|
|
544
|
+
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
|
|
545
|
+
zIndex: 9999,
|
|
546
|
+
display: "flex",
|
|
547
|
+
flexDirection: "column",
|
|
548
|
+
overflow: "hidden",
|
|
549
|
+
cursor: isDraggingDialog ? "grabbing" : "default",
|
|
550
|
+
userSelect: "none"
|
|
551
|
+
};
|
|
552
|
+
const headerStyle = {
|
|
553
|
+
padding: "12px 16px",
|
|
554
|
+
background: "#f5f5f5",
|
|
555
|
+
borderBottom: "1px solid #e8e8e8",
|
|
556
|
+
display: "flex",
|
|
557
|
+
alignItems: "center",
|
|
558
|
+
justifyContent: "space-between",
|
|
559
|
+
cursor: isDraggingDialog ? "grabbing" : "grab"
|
|
560
|
+
};
|
|
561
|
+
const tabContainerStyle = {
|
|
562
|
+
display: "flex",
|
|
563
|
+
gap: "8px"
|
|
564
|
+
};
|
|
565
|
+
const getTabStyle = (isActive) => ({
|
|
566
|
+
padding: "6px 16px",
|
|
567
|
+
borderRadius: "6px",
|
|
568
|
+
border: "none",
|
|
569
|
+
background: isActive ? "#1677ff" : "transparent",
|
|
570
|
+
color: isActive ? "#fff" : "#666",
|
|
571
|
+
cursor: "pointer",
|
|
572
|
+
fontSize: "14px",
|
|
573
|
+
transition: "all 0.2s"
|
|
574
|
+
});
|
|
575
|
+
const closeButtonStyle = {
|
|
576
|
+
width: "28px",
|
|
577
|
+
height: "28px",
|
|
578
|
+
borderRadius: "50%",
|
|
579
|
+
border: "none",
|
|
580
|
+
background: "#ff4d4f",
|
|
581
|
+
color: "#fff",
|
|
582
|
+
cursor: "pointer",
|
|
583
|
+
display: "flex",
|
|
584
|
+
alignItems: "center",
|
|
585
|
+
justifyContent: "center",
|
|
586
|
+
fontSize: "16px",
|
|
587
|
+
transition: "background 0.2s"
|
|
588
|
+
};
|
|
589
|
+
const contentStyle = {
|
|
590
|
+
flex: 1,
|
|
591
|
+
overflow: "auto",
|
|
592
|
+
padding: "16px",
|
|
593
|
+
background: "#fafafa"
|
|
594
|
+
};
|
|
595
|
+
const preStyle = {
|
|
596
|
+
margin: 0,
|
|
597
|
+
padding: "12px",
|
|
598
|
+
background: "#f8f9fa",
|
|
599
|
+
color: "#333",
|
|
600
|
+
borderRadius: "8px",
|
|
601
|
+
fontSize: "12px",
|
|
602
|
+
fontFamily: "\"Fira Code\", \"Monaco\", \"Consolas\", monospace",
|
|
603
|
+
lineHeight: 1.5,
|
|
604
|
+
overflow: "auto",
|
|
605
|
+
whiteSpace: "pre-wrap",
|
|
606
|
+
wordBreak: "break-word",
|
|
607
|
+
border: "1px solid #e8e8e8"
|
|
608
|
+
};
|
|
609
|
+
const statsStyle = {
|
|
610
|
+
padding: "8px 12px",
|
|
611
|
+
background: "#e6f4ff",
|
|
612
|
+
borderRadius: "6px",
|
|
613
|
+
marginBottom: "12px",
|
|
614
|
+
fontSize: "13px",
|
|
615
|
+
color: "#0958d9"
|
|
616
|
+
};
|
|
617
|
+
const tableStyle = {
|
|
618
|
+
width: "100%",
|
|
619
|
+
borderCollapse: "collapse",
|
|
620
|
+
fontSize: "13px",
|
|
621
|
+
background: "#fff",
|
|
622
|
+
borderRadius: "8px",
|
|
623
|
+
overflow: "hidden",
|
|
624
|
+
boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)"
|
|
625
|
+
};
|
|
626
|
+
const tableHeaderStyle = {
|
|
627
|
+
background: "#f5f5f5",
|
|
628
|
+
fontWeight: 600,
|
|
629
|
+
color: "#333",
|
|
630
|
+
textAlign: "left",
|
|
631
|
+
padding: "10px 12px",
|
|
632
|
+
borderBottom: "1px solid #e8e8e8",
|
|
633
|
+
whiteSpace: "nowrap"
|
|
634
|
+
};
|
|
635
|
+
const tableCellStyle = {
|
|
636
|
+
padding: "10px 12px",
|
|
637
|
+
borderBottom: "1px solid #f0f0f0",
|
|
638
|
+
verticalAlign: "top"
|
|
639
|
+
};
|
|
640
|
+
const expandButtonStyle = {
|
|
641
|
+
width: "20px",
|
|
642
|
+
height: "20px",
|
|
643
|
+
border: "none",
|
|
644
|
+
background: "transparent",
|
|
645
|
+
cursor: "pointer",
|
|
646
|
+
display: "flex",
|
|
647
|
+
alignItems: "center",
|
|
648
|
+
justifyContent: "center",
|
|
649
|
+
fontSize: "12px",
|
|
650
|
+
color: "#666",
|
|
651
|
+
borderRadius: "4px",
|
|
652
|
+
transition: "background 0.2s"
|
|
653
|
+
};
|
|
654
|
+
const expandedRowStyle = { background: "#fafafa" };
|
|
655
|
+
const jsonContainerStyle = {
|
|
656
|
+
padding: "12px",
|
|
657
|
+
background: "#fff",
|
|
658
|
+
borderRadius: "6px",
|
|
659
|
+
margin: "8px 0",
|
|
660
|
+
border: "1px solid #e8e8e8"
|
|
661
|
+
};
|
|
662
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
663
|
+
style: buttonStyle,
|
|
664
|
+
onMouseDown: handleButtonMouseDown,
|
|
665
|
+
onClick: () => !isDraggingButton && setIsOpen(true),
|
|
666
|
+
title: "打开调试面板",
|
|
667
|
+
children: "🐛"
|
|
668
|
+
}), isOpen && /* @__PURE__ */ jsxs("div", {
|
|
669
|
+
style: dialogStyle,
|
|
670
|
+
onMouseDown: handleDialogMouseDown,
|
|
671
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
672
|
+
className: "debug-panel-header",
|
|
673
|
+
style: headerStyle,
|
|
674
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
675
|
+
style: tabContainerStyle,
|
|
676
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
677
|
+
style: getTabStyle(activeTab === "messages"),
|
|
678
|
+
onClick: () => setActiveTab("messages"),
|
|
679
|
+
children: [
|
|
680
|
+
"Messages (",
|
|
681
|
+
messages.length,
|
|
682
|
+
")"
|
|
683
|
+
]
|
|
684
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
685
|
+
style: getTabStyle(activeTab === "state"),
|
|
686
|
+
onClick: () => setActiveTab("state"),
|
|
687
|
+
children: "State"
|
|
688
|
+
})]
|
|
689
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
690
|
+
style: closeButtonStyle,
|
|
691
|
+
onClick: () => setIsOpen(false),
|
|
692
|
+
title: "关闭",
|
|
693
|
+
children: "×"
|
|
694
|
+
})]
|
|
695
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
696
|
+
style: contentStyle,
|
|
697
|
+
children: activeTab === "messages" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
698
|
+
style: statsStyle,
|
|
699
|
+
children: [
|
|
700
|
+
"共 ",
|
|
701
|
+
messages.length,
|
|
702
|
+
" 条消息"
|
|
703
|
+
]
|
|
704
|
+
}), /* @__PURE__ */ jsxs("table", {
|
|
705
|
+
style: tableStyle,
|
|
706
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
707
|
+
/* @__PURE__ */ jsx("th", { style: {
|
|
708
|
+
...tableHeaderStyle,
|
|
709
|
+
width: "30px"
|
|
710
|
+
} }),
|
|
711
|
+
/* @__PURE__ */ jsx("th", {
|
|
712
|
+
style: {
|
|
713
|
+
...tableHeaderStyle,
|
|
714
|
+
width: "60px"
|
|
715
|
+
},
|
|
716
|
+
children: "类型"
|
|
717
|
+
}),
|
|
718
|
+
/* @__PURE__ */ jsx("th", {
|
|
719
|
+
style: tableHeaderStyle,
|
|
720
|
+
children: "内容"
|
|
721
|
+
}),
|
|
722
|
+
/* @__PURE__ */ jsx("th", {
|
|
723
|
+
style: {
|
|
724
|
+
...tableHeaderStyle,
|
|
725
|
+
width: "100px"
|
|
726
|
+
},
|
|
727
|
+
children: "ID"
|
|
728
|
+
})
|
|
729
|
+
] }) }), /* @__PURE__ */ jsx("tbody", { children: messages.map((msg, index) => {
|
|
730
|
+
const message = msg;
|
|
731
|
+
const isExpanded = expandedRows.has(index);
|
|
732
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("tr", {
|
|
733
|
+
style: {
|
|
734
|
+
cursor: "pointer",
|
|
735
|
+
transition: "background 0.2s"
|
|
736
|
+
},
|
|
737
|
+
onClick: () => toggleRow(index),
|
|
738
|
+
onMouseEnter: (e) => {
|
|
739
|
+
e.currentTarget.style.background = "#f5f5f5";
|
|
740
|
+
},
|
|
741
|
+
onMouseLeave: (e) => {
|
|
742
|
+
e.currentTarget.style.background = "transparent";
|
|
743
|
+
},
|
|
744
|
+
children: [
|
|
745
|
+
/* @__PURE__ */ jsx("td", {
|
|
746
|
+
style: tableCellStyle,
|
|
747
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
748
|
+
style: expandButtonStyle,
|
|
749
|
+
onClick: (e) => {
|
|
750
|
+
e.stopPropagation();
|
|
751
|
+
toggleRow(index);
|
|
752
|
+
},
|
|
753
|
+
children: isExpanded ? "▼" : "▶"
|
|
754
|
+
})
|
|
755
|
+
}),
|
|
756
|
+
/* @__PURE__ */ jsx("td", {
|
|
757
|
+
style: tableCellStyle,
|
|
758
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
759
|
+
style: getTypeTagStyle(message.type),
|
|
760
|
+
children: message.type || "unknown"
|
|
761
|
+
})
|
|
762
|
+
}),
|
|
763
|
+
/* @__PURE__ */ jsx("td", {
|
|
764
|
+
style: tableCellStyle,
|
|
765
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
766
|
+
style: {
|
|
767
|
+
color: "#333",
|
|
768
|
+
lineHeight: 1.5,
|
|
769
|
+
wordBreak: "break-all"
|
|
770
|
+
},
|
|
771
|
+
children: formatContent(message.content)
|
|
772
|
+
})
|
|
773
|
+
}),
|
|
774
|
+
/* @__PURE__ */ jsx("td", {
|
|
775
|
+
style: {
|
|
776
|
+
...tableCellStyle,
|
|
777
|
+
color: "#999",
|
|
778
|
+
fontSize: "12px"
|
|
779
|
+
},
|
|
780
|
+
children: message.id?.slice(-8) || "-"
|
|
781
|
+
})
|
|
782
|
+
]
|
|
783
|
+
}, index), isExpanded && /* @__PURE__ */ jsx("tr", {
|
|
784
|
+
style: expandedRowStyle,
|
|
785
|
+
children: /* @__PURE__ */ jsx("td", {
|
|
786
|
+
colSpan: 4,
|
|
787
|
+
style: {
|
|
788
|
+
padding: 0,
|
|
789
|
+
borderBottom: "1px solid #e8e8e8"
|
|
790
|
+
},
|
|
791
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
792
|
+
style: jsonContainerStyle,
|
|
793
|
+
children: /* @__PURE__ */ jsx("pre", {
|
|
794
|
+
style: {
|
|
795
|
+
...preStyle,
|
|
796
|
+
margin: 0,
|
|
797
|
+
maxHeight: "300px",
|
|
798
|
+
overflow: "auto"
|
|
799
|
+
},
|
|
800
|
+
children: formatJson(message)
|
|
801
|
+
})
|
|
802
|
+
})
|
|
803
|
+
})
|
|
804
|
+
})] });
|
|
805
|
+
}) })]
|
|
806
|
+
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
807
|
+
style: statsStyle,
|
|
808
|
+
children: ["State 键数量: ", Object.keys(streamState).length]
|
|
809
|
+
}), /* @__PURE__ */ jsx("pre", {
|
|
810
|
+
style: preStyle,
|
|
811
|
+
children: formatJson(streamState)
|
|
812
|
+
})] })
|
|
813
|
+
})]
|
|
814
|
+
})] });
|
|
815
|
+
}
|
|
816
|
+
//#endregion
|
|
376
817
|
//#region src/hooks/useInterrupt.tsx
|
|
377
818
|
/**
|
|
378
819
|
* InterruptManager - 管理 Interrupt 状态的 Hook
|
|
@@ -426,12 +867,17 @@ function useInterrupt({ interrupt, config, onSubmit }) {
|
|
|
426
867
|
* 1. 管理工具执行状态
|
|
427
868
|
* 2. 自动执行前端工具(避免重复执行)
|
|
428
869
|
* 3. 通知外部执行状态变化
|
|
870
|
+
* 4. 支持批量提交工具结果
|
|
429
871
|
*/
|
|
430
|
-
function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChange,
|
|
872
|
+
function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChange, onToolResultsBatch, completedToolResults }) {
|
|
431
873
|
const executedCallsRef = useRef(/* @__PURE__ */ new Set());
|
|
432
874
|
const executingCallsRef = useRef(/* @__PURE__ */ new Set());
|
|
433
875
|
const pendingNotifiedRef = useRef(/* @__PURE__ */ new Set());
|
|
434
876
|
const initializedRef = useRef(false);
|
|
877
|
+
const batchCallIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
878
|
+
const batchResultsRef = useRef(/* @__PURE__ */ new Map());
|
|
879
|
+
const isProcessingRef = useRef(false);
|
|
880
|
+
const batchSubmittedRef = useRef(false);
|
|
435
881
|
useEffect(() => {
|
|
436
882
|
if (initializedRef.current || !completedToolResults || completedToolResults.size === 0) return;
|
|
437
883
|
initializedRef.current = true;
|
|
@@ -441,13 +887,26 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
441
887
|
if (call) onExecutionChange?.({
|
|
442
888
|
callId,
|
|
443
889
|
name: call.name,
|
|
444
|
-
|
|
890
|
+
args: call.args,
|
|
445
891
|
status: "success",
|
|
446
892
|
result
|
|
447
893
|
});
|
|
448
894
|
});
|
|
449
895
|
}, [completedToolResults]);
|
|
450
896
|
/**
|
|
897
|
+
* 检查批次是否完成并提交结果
|
|
898
|
+
*/
|
|
899
|
+
const checkAndSubmitBatch = useCallback(() => {
|
|
900
|
+
if (batchSubmittedRef.current) return;
|
|
901
|
+
if (Array.from(batchCallIdsRef.current).every((callId) => executedCallsRef.current.has(callId)) && batchCallIdsRef.current.size > 0) {
|
|
902
|
+
batchSubmittedRef.current = true;
|
|
903
|
+
const results = Array.from(batchResultsRef.current.values());
|
|
904
|
+
if (results.length > 0) onToolResultsBatch?.(results);
|
|
905
|
+
batchCallIdsRef.current.clear();
|
|
906
|
+
batchResultsRef.current.clear();
|
|
907
|
+
}
|
|
908
|
+
}, [onToolResultsBatch]);
|
|
909
|
+
/**
|
|
451
910
|
* 执行单个前端工具
|
|
452
911
|
*/
|
|
453
912
|
const executeFrontendTool = useCallback(async (tool, call) => {
|
|
@@ -456,38 +915,50 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
456
915
|
onExecutionChange?.({
|
|
457
916
|
callId,
|
|
458
917
|
name: call.name,
|
|
459
|
-
|
|
918
|
+
args: call.args,
|
|
460
919
|
status: "running"
|
|
461
920
|
});
|
|
462
921
|
try {
|
|
463
|
-
const result = await tool.execute(call.
|
|
922
|
+
const result = await tool.execute(call.args);
|
|
464
923
|
onExecutionChange?.({
|
|
465
924
|
callId,
|
|
466
925
|
name: call.name,
|
|
467
|
-
|
|
926
|
+
args: call.args,
|
|
468
927
|
status: "success",
|
|
469
928
|
result
|
|
470
929
|
});
|
|
471
|
-
|
|
930
|
+
batchResultsRef.current.set(callId, {
|
|
931
|
+
callId,
|
|
932
|
+
name: call.name,
|
|
933
|
+
result
|
|
934
|
+
});
|
|
472
935
|
} catch (error) {
|
|
473
936
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
474
937
|
onExecutionChange?.({
|
|
475
938
|
callId,
|
|
476
939
|
name: call.name,
|
|
477
|
-
|
|
940
|
+
args: call.args,
|
|
478
941
|
status: "error",
|
|
479
942
|
error: errorMessage
|
|
480
943
|
});
|
|
944
|
+
batchResultsRef.current.set(callId, {
|
|
945
|
+
callId,
|
|
946
|
+
name: call.name,
|
|
947
|
+
result: { error: errorMessage }
|
|
948
|
+
});
|
|
481
949
|
} finally {
|
|
482
950
|
executingCallsRef.current.delete(callId);
|
|
483
951
|
executedCallsRef.current.add(callId);
|
|
952
|
+
checkAndSubmitBatch();
|
|
484
953
|
}
|
|
485
|
-
}, [onExecutionChange,
|
|
954
|
+
}, [onExecutionChange, checkAndSubmitBatch]);
|
|
486
955
|
/**
|
|
487
956
|
* 处理工具调用
|
|
488
957
|
*/
|
|
489
958
|
const processToolCalls = useCallback(async () => {
|
|
490
959
|
if (!tools) return;
|
|
960
|
+
if (isProcessingRef.current) return;
|
|
961
|
+
const frontendCalls = [];
|
|
491
962
|
for (const call of toolCalls) {
|
|
492
963
|
const callId = call.id;
|
|
493
964
|
if (executedCallsRef.current.has(callId) || executingCallsRef.current.has(callId)) continue;
|
|
@@ -498,7 +969,7 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
498
969
|
onExecutionChange?.({
|
|
499
970
|
callId,
|
|
500
971
|
name: call.name,
|
|
501
|
-
|
|
972
|
+
args: call.args,
|
|
502
973
|
status: "pending"
|
|
503
974
|
});
|
|
504
975
|
}
|
|
@@ -508,19 +979,33 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
508
979
|
onExecutionChange?.({
|
|
509
980
|
callId,
|
|
510
981
|
name: call.name,
|
|
511
|
-
|
|
982
|
+
args: call.args,
|
|
512
983
|
status: "pending"
|
|
513
984
|
});
|
|
514
985
|
continue;
|
|
515
986
|
}
|
|
516
|
-
if (isFrontendTool(tool))
|
|
987
|
+
if (isFrontendTool(tool)) frontendCalls.push({
|
|
988
|
+
tool,
|
|
989
|
+
call
|
|
990
|
+
});
|
|
517
991
|
else onExecutionChange?.({
|
|
518
992
|
callId,
|
|
519
993
|
name: call.name,
|
|
520
|
-
|
|
994
|
+
args: call.args,
|
|
521
995
|
status: "pending"
|
|
522
996
|
});
|
|
523
997
|
}
|
|
998
|
+
if (frontendCalls.length > 0) {
|
|
999
|
+
batchSubmittedRef.current = false;
|
|
1000
|
+
isProcessingRef.current = true;
|
|
1001
|
+
batchCallIdsRef.current = new Set(frontendCalls.map(({ call }) => call.id));
|
|
1002
|
+
batchResultsRef.current = /* @__PURE__ */ new Map();
|
|
1003
|
+
try {
|
|
1004
|
+
await Promise.all(frontendCalls.map(({ tool, call }) => executeFrontendTool(tool, call)));
|
|
1005
|
+
} finally {
|
|
1006
|
+
isProcessingRef.current = false;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
524
1009
|
}, [
|
|
525
1010
|
tools,
|
|
526
1011
|
toolCalls,
|
|
@@ -543,7 +1028,7 @@ function extractToolCalls(message) {
|
|
|
543
1028
|
if ("tool_calls" in message && Array.isArray(message.tool_calls)) return message.tool_calls.map((tc) => ({
|
|
544
1029
|
id: tc.id || crypto.randomUUID(),
|
|
545
1030
|
name: tc.name || tc.function?.name || "",
|
|
546
|
-
|
|
1031
|
+
args: typeof tc.function?.args === "string" ? JSON.parse(tc.function.args) : tc.args || {}
|
|
547
1032
|
}));
|
|
548
1033
|
}
|
|
549
1034
|
/**
|
|
@@ -632,10 +1117,33 @@ const styles = `
|
|
|
632
1117
|
justify-content: center;
|
|
633
1118
|
}
|
|
634
1119
|
|
|
1120
|
+
|
|
1121
|
+
|
|
635
1122
|
.agent-message-list .ant-think-status-text {
|
|
636
1123
|
font-size: 13px;
|
|
637
1124
|
}
|
|
638
1125
|
|
|
1126
|
+
.agent-message-list table {
|
|
1127
|
+
border-collapse: collapse;
|
|
1128
|
+
width: 100%;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
.agent-message-list th,
|
|
1132
|
+
.agent-message-list td {
|
|
1133
|
+
border: 1px solid rgba(128, 128, 128, 0.3);
|
|
1134
|
+
padding: 8px 12px;
|
|
1135
|
+
text-align: left;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
.agent-message-list th {
|
|
1139
|
+
background-color: rgba(128, 128, 128, 0.12);
|
|
1140
|
+
font-weight: 500;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
.agent-message-list tr:nth-child(even) {
|
|
1144
|
+
background-color: rgba(128, 128, 128, 0.06);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
639
1147
|
.agent-message-empty {
|
|
640
1148
|
color: #8b8d91;
|
|
641
1149
|
font-size: 14px;
|
|
@@ -715,7 +1223,7 @@ function injectStyles() {
|
|
|
715
1223
|
//#endregion
|
|
716
1224
|
//#region src/components/AgentChat.tsx
|
|
717
1225
|
injectStyles();
|
|
718
|
-
const AgentChat = forwardRef(({ apiUrl, assistantId, headers, threadId: externalThreadId, onThreadIdChange, className = "", tools, contexts, messageConfig, inputConfig, onError, interruptConfig, agentState, onAgentStateChange }, ref) => {
|
|
1226
|
+
const AgentChat = forwardRef(({ apiUrl, assistantId, headers, threadId: externalThreadId, onThreadIdChange, className = "", tools, contexts, messageConfig, inputConfig, onError, interruptConfig, agentState, onAgentStateChange, showDebug }, ref) => {
|
|
719
1227
|
const [internalThreadId, setInternalThreadId] = useState(externalThreadId);
|
|
720
1228
|
useEffect(() => {
|
|
721
1229
|
setInternalThreadId(externalThreadId);
|
|
@@ -774,7 +1282,9 @@ const AgentChat = forwardRef(({ apiUrl, assistantId, headers, threadId: external
|
|
|
774
1282
|
context: contexts
|
|
775
1283
|
}
|
|
776
1284
|
}, {
|
|
1285
|
+
streamSubgraphs: true,
|
|
777
1286
|
streamResumable: true,
|
|
1287
|
+
config: { recursion_limit: 1e3 },
|
|
778
1288
|
optimisticValues(prev) {
|
|
779
1289
|
const newMessages = [...prev.messages ?? [], ...submitMessages];
|
|
780
1290
|
return {
|
|
@@ -789,13 +1299,18 @@ const AgentChat = forwardRef(({ apiUrl, assistantId, headers, threadId: external
|
|
|
789
1299
|
contexts,
|
|
790
1300
|
agentState
|
|
791
1301
|
]);
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
1302
|
+
const handleToolResultsBatch = useCallback((results) => {
|
|
1303
|
+
const toolMessages = results.map(({ callId, name, result }) => {
|
|
1304
|
+
return {
|
|
1305
|
+
type: "tool",
|
|
1306
|
+
content: typeof result === "string" ? result : JSON.stringify(result),
|
|
1307
|
+
tool_call_id: callId,
|
|
1308
|
+
name
|
|
1309
|
+
};
|
|
1310
|
+
});
|
|
1311
|
+
setTimeout(() => {
|
|
1312
|
+
submitToStream(toolMessages);
|
|
1313
|
+
}, 100);
|
|
799
1314
|
}, [submitToStream]);
|
|
800
1315
|
const handleSend = useCallback(async (params) => {
|
|
801
1316
|
let messages = [];
|
|
@@ -818,38 +1333,46 @@ const AgentChat = forwardRef(({ apiUrl, assistantId, headers, threadId: external
|
|
|
818
1333
|
toolCalls: allToolCalls,
|
|
819
1334
|
isLoading: stream.isLoading,
|
|
820
1335
|
onExecutionChange: handleExecutionChange,
|
|
821
|
-
|
|
1336
|
+
onToolResultsBatch: handleToolResultsBatch,
|
|
822
1337
|
completedToolResults: toolResults
|
|
823
1338
|
});
|
|
824
1339
|
return /* @__PURE__ */ jsxs("div", {
|
|
825
1340
|
className: `agent-chat-container ${className}`,
|
|
826
|
-
children: [
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1341
|
+
children: [
|
|
1342
|
+
/* @__PURE__ */ jsx(MessageList, {
|
|
1343
|
+
messages,
|
|
1344
|
+
isLoading: stream.isLoading,
|
|
1345
|
+
className: "agent-chat-messages",
|
|
1346
|
+
tools,
|
|
1347
|
+
toolExecutions,
|
|
1348
|
+
components: messageConfig?.components,
|
|
1349
|
+
securityConfig: {
|
|
1350
|
+
allowedTags: messageConfig?.allowedTags,
|
|
1351
|
+
literalTagContent: messageConfig?.literalTagContent
|
|
1352
|
+
},
|
|
1353
|
+
loadingColor: messageConfig?.loadingColor,
|
|
1354
|
+
interruptRender,
|
|
1355
|
+
emptyState: messageConfig?.emptyState
|
|
1356
|
+
}),
|
|
1357
|
+
/* @__PURE__ */ jsx(ChatInput, {
|
|
1358
|
+
ref: chatInputRef,
|
|
1359
|
+
onSend: handleSend,
|
|
1360
|
+
onStop: handleStop,
|
|
1361
|
+
isLoading: stream.isLoading,
|
|
1362
|
+
className: "agent-chat-input",
|
|
1363
|
+
footer,
|
|
1364
|
+
skill,
|
|
1365
|
+
slotConfig,
|
|
1366
|
+
header,
|
|
1367
|
+
prefix,
|
|
1368
|
+
placeholder
|
|
1369
|
+
}),
|
|
1370
|
+
/* @__PURE__ */ jsx(DebugPanel, {
|
|
1371
|
+
messages: stream.messages,
|
|
1372
|
+
streamState: stream.values || {},
|
|
1373
|
+
visible: showDebug
|
|
1374
|
+
})
|
|
1375
|
+
]
|
|
853
1376
|
});
|
|
854
1377
|
});
|
|
855
1378
|
AgentChat.displayName = "AgentChat";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vegintech/langchain-react-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "LangChain Agent UI component library for React",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -15,13 +15,6 @@
|
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "vp pack",
|
|
20
|
-
"dev": "vp pack --watch",
|
|
21
|
-
"test": "vp test",
|
|
22
|
-
"check": "vp check",
|
|
23
|
-
"prepublishOnly": "vp run build"
|
|
24
|
-
},
|
|
25
18
|
"dependencies": {
|
|
26
19
|
"@ant-design/x": "^2.4.0",
|
|
27
20
|
"@langchain/core": "^1.1.36",
|
|
@@ -43,5 +36,11 @@
|
|
|
43
36
|
"peerDependencies": {
|
|
44
37
|
"react": ">=18.0.0",
|
|
45
38
|
"react-dom": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "vp pack",
|
|
42
|
+
"dev": "vp pack --watch",
|
|
43
|
+
"test": "vp test",
|
|
44
|
+
"check": "vp check"
|
|
46
45
|
}
|
|
47
|
-
}
|
|
46
|
+
}
|