@vegintech/langchain-react-agent 0.0.24 → 0.0.26

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 CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as react from "react";
2
2
  import React$1, { ReactNode } from "react";
3
3
  import { ThoughtChainItemProps } from "@ant-design/x";
4
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
4
5
  import { Components } from "streamdown";
5
- import * as _langchain_langgraph_sdk_react0 from "@langchain/langgraph-sdk/react";
6
+ import { UseStreamOptions, useStream } from "@langchain/langgraph-sdk/react";
6
7
  import { BaseNode, InsertPosition, NodeRender, SenderProps, SkillType, SlotConfigType } from "@ant-design/x/es/sender/interface.ts";
7
8
  import { Message } from "@langchain/langgraph-sdk";
8
9
 
@@ -35,8 +36,11 @@ interface InterruptConfig {
35
36
  }
36
37
  /** InterruptManager Hook 的 Props */
37
38
  interface InterruptManagerProps {
38
- /** 当前 interrupt 事件 */
39
- interrupt: InterruptEvent | null;
39
+ /** 当前 interrupt 事件 - 与 useStream 返回的 Interrupt 类型兼容 */
40
+ interrupt: {
41
+ value: unknown;
42
+ id?: string;
43
+ } | null | undefined;
40
44
  /** Interrupt 处理配置 */
41
45
  config?: InterruptConfig;
42
46
  /** 提交回调 */
@@ -46,7 +50,7 @@ interface InterruptManagerProps {
46
50
  command?: {
47
51
  resume: unknown;
48
52
  };
49
- }) => Promise<void> | void;
53
+ } | Record<string, unknown>) => Promise<void> | void;
50
54
  }
51
55
  /** Context Item,用于传递上下文信息 */
52
56
  interface ContextItem {
@@ -219,25 +223,42 @@ interface InputConfig extends SenderCustomizationProps {
219
223
  /** 输入框占位符文本 */
220
224
  placeholder?: string;
221
225
  }
226
+ /**
227
+ * AgentChat 需要的 Stream 属性
228
+ * 由外部通过 useStream/useAgentStream hook 创建并传入
229
+ */
230
+ interface AgentChatStreamProps {
231
+ /** 消息列表 */
232
+ messages: Message[];
233
+ /** 是否正在加载 */
234
+ isLoading: boolean;
235
+ /** 提交消息 */
236
+ submit: (values: {
237
+ [x: string]: unknown;
238
+ } | null | undefined, options?: {
239
+ command?: {
240
+ resume: unknown;
241
+ };
242
+ } | Record<string, unknown>) => Promise<void> | void;
243
+ /** 中断事件 - 与 useStream 返回的 Interrupt 类型兼容 */
244
+ interrupt: unknown;
245
+ /** 停止流 */
246
+ stop: () => Promise<void> | void;
247
+ }
222
248
  interface AgentChatProps {
223
- apiUrl?: string;
224
- assistantId: string;
225
- threadId?: string;
226
- headers?: Record<string, string | undefined | null>;
227
- onThreadIdChange?: (threadId: string) => void;
249
+ /**
250
+ * Stream 相关属性
251
+ * 由外部通过 useAgentStream/useStream hook 创建并解构传入
252
+ */
253
+ stream: AgentChatStreamProps;
228
254
  className?: string;
229
255
  tools?: ToolDefinition<any>[];
230
256
  contexts?: ContextItem[];
231
257
  messageConfig?: MessageConfig;
232
258
  inputConfig?: InputConfig;
233
- onError?: (error: Error) => void;
234
259
  interruptConfig?: InterruptConfig;
235
260
  /** Agent 状态值 */
236
261
  agentState?: Record<string, unknown>;
237
- /** Agent 状态变化回调 */
238
- onAgentStateChange?: (state: Record<string, unknown>) => void;
239
- /** 是否显示调试面板,默认 false。仅在开发环境生效 */
240
- showDebug?: boolean;
241
262
  /** 欢迎组件,当消息列表为空且非加载状态时显示 */
242
263
  welcome?: ReactNode;
243
264
  }
@@ -251,19 +272,6 @@ interface AgentChatRef {
251
272
  clearInput: () => void;
252
273
  /** 聚焦输入框 */
253
274
  focusInput: () => void;
254
- /**
255
- * LangGraph Stream 实例
256
- * 提供对底层流式连接的完整访问,包括:
257
- * - messages: 当前消息列表
258
- * - values: Agent 状态值
259
- * - isLoading: 是否正在加载
260
- * - submit: 提交消息
261
- * - stop: 停止流
262
- * - interrupt: 中断事件
263
- * - client: LangGraph 客户端(可通过 client.threads.create() 创建新线程)
264
- * 等完整功能
265
- */
266
- stream: ReturnType<typeof _langchain_langgraph_sdk_react0.useStream> | null;
267
275
  }
268
276
  /** Streamdown 安全过滤配置 */
269
277
  interface StreamdownSecurityConfig {
@@ -309,4 +317,34 @@ interface MessageContentRendererProps {
309
317
  /** 消息内容渲染组件 - 支持字符串和多模态内容块 */
310
318
  declare const MessageContentRenderer: React$1.FC<MessageContentRendererProps>;
311
319
  //#endregion
312
- export { AgentChat, type AgentChatInputRef, type AgentChatProps, type AgentChatRef, type BackendTool, type ChatMessage, type ContextItem, type EmptyStateConfig, type FrontendTool, type InputConfig, type InterruptConfig, type InterruptEvent, type InterruptManagerProps, type InterruptRenderProps, type MessageConfig, type MessageContent, type MessageContentBlock, MessageContentRenderer, type MessageContentRendererProps, type MessageType, type SenderCustomizationProps, type SenderSlotConfig, type SenderSubmitParams, type ToolCallInput, ToolCard, type ToolCardProps, type ToolDefinition, type ToolExecutionRecord, type ToolExecutionStatus, type ToolParameterSchema, type ToolRenderProps };
320
+ //#region src/components/DebugPanel.d.ts
321
+ interface DebugPanelProps {
322
+ /** Stream 状态数据(包含 messages) */
323
+ streamState: Record<string, unknown>;
324
+ /** 是否显示调试面板 */
325
+ visible?: boolean;
326
+ }
327
+ declare function DebugPanel({
328
+ streamState,
329
+ visible
330
+ }: DebugPanelProps): react_jsx_runtime0.JSX.Element | null;
331
+ //#endregion
332
+ //#region src/hooks/useAgentStream.d.ts
333
+ type AgentStreamOptions = UseStreamOptions;
334
+ type AgentStream = ReturnType<typeof useStream>;
335
+ /**
336
+ * 包装 useStream hook,为 AgentChat 提供统一的 stream 实例创建方式
337
+ *
338
+ * @example
339
+ * ```tsx
340
+ * const stream = useAgentStream({
341
+ * apiUrl: "http://localhost:2026",
342
+ * assistantId: "demo",
343
+ * });
344
+ *
345
+ * <AgentChat stream={stream} />
346
+ * ```
347
+ */
348
+ declare function useAgentStream(options: AgentStreamOptions): AgentStream;
349
+ //#endregion
350
+ export { AgentChat, type AgentChatInputRef, type AgentChatProps, type AgentChatRef, type AgentChatStreamProps, type AgentStream, type AgentStreamOptions, type BackendTool, type ChatMessage, type ContextItem, DebugPanel, type DebugPanelProps, type EmptyStateConfig, type FrontendTool, type InputConfig, type InterruptConfig, type InterruptEvent, type InterruptManagerProps, type InterruptRenderProps, type MessageConfig, type MessageContent, type MessageContentBlock, MessageContentRenderer, type MessageContentRendererProps, type MessageType, type SenderCustomizationProps, type SenderSlotConfig, type SenderSubmitParams, type ToolCallInput, ToolCard, type ToolCardProps, type ToolDefinition, type ToolExecutionRecord, type ToolExecutionStatus, type ToolParameterSchema, type ToolRenderProps, useAgentStream };
package/dist/index.mjs CHANGED
@@ -456,551 +456,84 @@ const MessageList = ({ messages, isLoading = false, className = "", tools, toolE
456
456
  });
457
457
  };
458
458
  //#endregion
459
- //#region src/components/DebugPanel.tsx
460
- const isDevelopment = () => {
461
- if (typeof process !== "undefined" && process.env) return process.env.NODE_ENV === "development";
462
- const viteEnv = import.meta.env;
463
- if (typeof import.meta !== "undefined" && viteEnv) return viteEnv.DEV === true || viteEnv.MODE === "development";
464
- return false;
465
- };
466
- function DebugPanel({ messages, streamState, visible = true }) {
467
- if (!isDevelopment() || !visible) return null;
468
- const [isOpen, setIsOpen] = useState(false);
469
- const [activeTab, setActiveTab] = useState("messages");
470
- const [expandedRows, setExpandedRows] = useState(/* @__PURE__ */ new Set());
471
- const [buttonPos, setButtonPos] = useState({
472
- x: 0,
473
- y: 0
474
- });
475
- const [isDraggingButton, setIsDraggingButton] = useState(false);
476
- const buttonDragRef = useRef(null);
477
- const [dialogPos, setDialogPos] = useState({
478
- x: 0,
479
- y: 0
480
- });
481
- const [isDraggingDialog, setIsDraggingDialog] = useState(false);
482
- const dialogDragRef = useRef(null);
483
- const handleButtonMouseDown = useCallback((e) => {
484
- e.preventDefault();
485
- buttonDragRef.current = {
486
- startX: e.clientX,
487
- startY: e.clientY,
488
- initialX: buttonPos.x,
489
- initialY: buttonPos.y
490
- };
491
- setIsDraggingButton(true);
492
- }, [buttonPos]);
493
- const handleDialogMouseDown = useCallback((e) => {
494
- if (!e.target.closest(".debug-panel-header")) return;
495
- e.preventDefault();
496
- dialogDragRef.current = {
497
- startX: e.clientX,
498
- startY: e.clientY,
499
- initialX: dialogPos.x,
500
- initialY: dialogPos.y
501
- };
502
- setIsDraggingDialog(true);
503
- }, [dialogPos]);
459
+ //#region src/hooks/useInterrupt.tsx
460
+ /**
461
+ * InterruptManager - 管理 Interrupt 状态的 Hook
462
+ *
463
+ * 封装 Interrupt 状态管理和处理逻辑,避免在 AgentChat 组件中臃肿
464
+ */
465
+ function useInterrupt({ interrupt, config, onSubmit }) {
466
+ const [activeInterrupt, setActiveInterrupt] = useState(null);
467
+ const processedInterruptIdRef = useRef(null);
504
468
  useEffect(() => {
505
- const handleMouseMove = (e) => {
506
- if (isDraggingButton && buttonDragRef.current) {
507
- const dx = e.clientX - buttonDragRef.current.startX;
508
- const dy = e.clientY - buttonDragRef.current.startY;
509
- setButtonPos({
510
- x: buttonDragRef.current.initialX + dx,
511
- y: buttonDragRef.current.initialY + dy
512
- });
513
- }
514
- if (isDraggingDialog && dialogDragRef.current) {
515
- const dx = e.clientX - dialogDragRef.current.startX;
516
- const dy = e.clientY - dialogDragRef.current.startY;
517
- setDialogPos({
518
- x: dialogDragRef.current.initialX + dx,
519
- y: dialogDragRef.current.initialY + dy
520
- });
469
+ if (interrupt) {
470
+ const interruptEvent = interrupt;
471
+ if (interruptEvent.id !== processedInterruptIdRef.current) {
472
+ processedInterruptIdRef.current = interruptEvent.id;
473
+ setActiveInterrupt(interruptEvent);
521
474
  }
522
- };
523
- const handleMouseUp = () => {
524
- setIsDraggingButton(false);
525
- setIsDraggingDialog(false);
526
- buttonDragRef.current = null;
527
- dialogDragRef.current = null;
528
- };
529
- if (isDraggingButton || isDraggingDialog) {
530
- document.addEventListener("mousemove", handleMouseMove);
531
- document.addEventListener("mouseup", handleMouseUp);
532
- }
533
- return () => {
534
- document.removeEventListener("mousemove", handleMouseMove);
535
- document.removeEventListener("mouseup", handleMouseUp);
536
- };
537
- }, [isDraggingButton, isDraggingDialog]);
538
- const formatJson = (data) => {
539
- try {
540
- return JSON.stringify(data, null, 2);
541
- } catch {
542
- return String(data);
475
+ } else {
476
+ processedInterruptIdRef.current = null;
477
+ setActiveInterrupt(null);
543
478
  }
479
+ }, [interrupt?.id]);
480
+ const handleResolveInterrupt = useCallback((response) => {
481
+ onSubmit(null, { command: { resume: response } });
482
+ setActiveInterrupt(null);
483
+ }, [onSubmit]);
484
+ return {
485
+ activeInterrupt,
486
+ handleResolveInterrupt,
487
+ renderInterrupt: useCallback(() => {
488
+ if (!activeInterrupt || !config?.render) return null;
489
+ return /* @__PURE__ */ jsx("div", {
490
+ className: "agent-chat-interrupt",
491
+ children: config.render({
492
+ event: activeInterrupt,
493
+ resolve: handleResolveInterrupt
494
+ })
495
+ });
496
+ }, [
497
+ activeInterrupt,
498
+ config,
499
+ handleResolveInterrupt
500
+ ])
544
501
  };
545
- const toggleRow = (index) => {
546
- setExpandedRows((prev) => {
547
- const next = new Set(prev);
548
- if (next.has(index)) next.delete(index);
549
- else next.add(index);
550
- return next;
551
- });
552
- };
553
- const getTypeTagStyle = (type) => {
554
- const color = {
555
- human: {
556
- bg: "#e6f4ff",
557
- color: "#0958d9"
558
- },
559
- ai: {
560
- bg: "#f6ffed",
561
- color: "#389e0d"
562
- },
563
- tool: {
564
- bg: "#fff7e6",
565
- color: "#d46b08"
566
- },
567
- system: {
568
- bg: "#f9f0ff",
569
- color: "#722ed1"
570
- },
571
- function: {
572
- bg: "#fff2f0",
573
- color: "#cf1322"
574
- }
575
- }[type || ""] || {
576
- bg: "#f5f5f5",
577
- color: "#666"
578
- };
579
- return {
580
- display: "inline-block",
581
- padding: "2px 8px",
582
- borderRadius: "4px",
583
- fontSize: "12px",
584
- fontWeight: 500,
585
- background: color.bg,
586
- color: color.color
587
- };
588
- };
589
- const formatContent = (content) => {
590
- if (!content) return "";
591
- if (Array.isArray(content)) return content.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join("");
592
- return String(content);
593
- };
594
- const renderMessageContent = (message) => {
595
- const hasToolCalls = message.tool_calls && Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
596
- return /* @__PURE__ */ jsxs("div", {
597
- style: {
598
- display: "flex",
599
- flexDirection: "column",
600
- gap: "8px"
601
- },
602
- children: [message.content && /* @__PURE__ */ jsx("div", {
603
- style: {
604
- color: "#333",
605
- lineHeight: 1.5,
606
- wordBreak: "break-all"
607
- },
608
- children: formatContent(message.content)
609
- }), hasToolCalls && /* @__PURE__ */ jsxs("div", {
610
- style: {
611
- marginTop: "4px",
612
- padding: "8px",
613
- background: "#f6ffed",
614
- border: "1px solid #b7eb8f",
615
- borderRadius: "6px"
616
- },
617
- children: [/* @__PURE__ */ jsxs("div", {
618
- style: {
619
- fontSize: "11px",
620
- fontWeight: 600,
621
- color: "#389e0d",
622
- marginBottom: "6px",
623
- display: "flex",
624
- alignItems: "center",
625
- gap: "4px"
626
- },
627
- children: [/* @__PURE__ */ jsx("span", { children: "🔧" }), /* @__PURE__ */ jsxs("span", { children: [
628
- "Tool Calls (",
629
- message.tool_calls.length,
630
- ")"
631
- ] })]
632
- }), /* @__PURE__ */ jsx("pre", {
633
- style: {
634
- ...preStyle,
635
- background: "#fff",
636
- border: "1px solid #d9f7be",
637
- maxHeight: "200px",
638
- fontSize: "11px"
639
- },
640
- children: formatJson(message.tool_calls)
641
- })]
642
- })]
643
- });
644
- };
645
- const buttonStyle = {
646
- position: "fixed",
647
- right: `${20 - buttonPos.x}px`,
648
- bottom: `${20 - buttonPos.y}px`,
649
- width: "48px",
650
- height: "48px",
651
- borderRadius: "50%",
652
- background: "#1677ff",
653
- color: "#fff",
654
- border: "none",
655
- cursor: isDraggingButton ? "grabbing" : "grab",
656
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
657
- display: "flex",
658
- alignItems: "center",
659
- justifyContent: "center",
660
- fontSize: "20px",
661
- zIndex: 9998,
662
- transition: isDraggingButton ? "none" : "box-shadow 0.2s",
663
- userSelect: "none"
664
- };
665
- const dialogStyle = {
666
- position: "fixed",
667
- left: "50%",
668
- top: "50%",
669
- transform: `translate(calc(-50% + ${dialogPos.x}px), calc(-50% + ${dialogPos.y}px))`,
670
- width: "1000px",
671
- maxWidth: "90vw",
672
- height: "650px",
673
- maxHeight: "85vh",
674
- background: "#fff",
675
- borderRadius: "12px",
676
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
677
- zIndex: 9999,
678
- display: "flex",
679
- flexDirection: "column",
680
- overflow: "hidden",
681
- cursor: isDraggingDialog ? "grabbing" : "default"
682
- };
683
- const headerStyle = {
684
- padding: "12px 16px",
685
- background: "#f5f5f5",
686
- borderBottom: "1px solid #e8e8e8",
687
- display: "flex",
688
- alignItems: "center",
689
- justifyContent: "space-between",
690
- cursor: isDraggingDialog ? "grabbing" : "grab"
691
- };
692
- const tabContainerStyle = {
693
- display: "flex",
694
- gap: "8px"
695
- };
696
- const getTabStyle = (isActive) => ({
697
- padding: "6px 16px",
698
- borderRadius: "6px",
699
- border: "none",
700
- background: isActive ? "#1677ff" : "transparent",
701
- color: isActive ? "#fff" : "#666",
702
- cursor: "pointer",
703
- fontSize: "14px",
704
- transition: "all 0.2s"
705
- });
706
- const closeButtonStyle = {
707
- width: "28px",
708
- height: "28px",
709
- borderRadius: "50%",
710
- border: "none",
711
- background: "#ff4d4f",
712
- color: "#fff",
713
- cursor: "pointer",
714
- display: "flex",
715
- alignItems: "center",
716
- justifyContent: "center",
717
- fontSize: "16px",
718
- transition: "background 0.2s"
719
- };
720
- const contentStyle = {
721
- flex: 1,
722
- overflow: "auto",
723
- padding: "16px",
724
- background: "#fafafa"
725
- };
726
- const preStyle = {
727
- margin: 0,
728
- padding: "12px",
729
- background: "#f8f9fa",
730
- color: "#333",
731
- borderRadius: "8px",
732
- fontSize: "12px",
733
- fontFamily: "\"Fira Code\", \"Monaco\", \"Consolas\", monospace",
734
- lineHeight: 1.5,
735
- overflow: "auto",
736
- whiteSpace: "pre-wrap",
737
- wordBreak: "break-word",
738
- border: "1px solid #e8e8e8"
739
- };
740
- const statsStyle = {
741
- padding: "8px 12px",
742
- background: "#e6f4ff",
743
- borderRadius: "6px",
744
- marginBottom: "12px",
745
- fontSize: "13px",
746
- color: "#0958d9"
747
- };
748
- const tableStyle = {
749
- width: "100%",
750
- borderCollapse: "collapse",
751
- fontSize: "13px",
752
- background: "#fff",
753
- borderRadius: "8px",
754
- overflow: "hidden",
755
- boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)"
756
- };
757
- const tableHeaderStyle = {
758
- background: "#f5f5f5",
759
- fontWeight: 600,
760
- color: "#333",
761
- textAlign: "left",
762
- padding: "10px 12px",
763
- borderBottom: "1px solid #e8e8e8",
764
- whiteSpace: "nowrap"
765
- };
766
- const tableCellStyle = {
767
- padding: "10px 12px",
768
- borderBottom: "1px solid #f0f0f0",
769
- verticalAlign: "top"
770
- };
771
- const expandButtonStyle = {
772
- width: "20px",
773
- height: "20px",
774
- border: "none",
775
- background: "transparent",
776
- cursor: "pointer",
777
- display: "flex",
778
- alignItems: "center",
779
- justifyContent: "center",
780
- fontSize: "12px",
781
- color: "#666",
782
- borderRadius: "4px",
783
- transition: "background 0.2s"
784
- };
785
- const expandedRowStyle = { background: "#fafafa" };
786
- const jsonContainerStyle = {
787
- padding: "12px",
788
- background: "#fff",
789
- borderRadius: "6px",
790
- margin: "8px 0",
791
- border: "1px solid #e8e8e8"
792
- };
793
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
794
- style: buttonStyle,
795
- onMouseDown: handleButtonMouseDown,
796
- onClick: () => !isDraggingButton && setIsOpen(true),
797
- title: "打开调试面板",
798
- children: "🐛"
799
- }), isOpen && /* @__PURE__ */ jsxs("div", {
800
- style: dialogStyle,
801
- onMouseDown: handleDialogMouseDown,
802
- children: [/* @__PURE__ */ jsxs("div", {
803
- className: "debug-panel-header",
804
- style: headerStyle,
805
- children: [/* @__PURE__ */ jsxs("div", {
806
- style: tabContainerStyle,
807
- children: [/* @__PURE__ */ jsxs("button", {
808
- style: getTabStyle(activeTab === "messages"),
809
- onClick: () => setActiveTab("messages"),
810
- children: [
811
- "Messages (",
812
- messages.length,
813
- ")"
814
- ]
815
- }), /* @__PURE__ */ jsx("button", {
816
- style: getTabStyle(activeTab === "state"),
817
- onClick: () => setActiveTab("state"),
818
- children: "State"
819
- })]
820
- }), /* @__PURE__ */ jsx("button", {
821
- style: closeButtonStyle,
822
- onClick: () => setIsOpen(false),
823
- title: "关闭",
824
- children: "×"
825
- })]
826
- }), /* @__PURE__ */ jsx("div", {
827
- style: contentStyle,
828
- children: activeTab === "messages" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
829
- style: statsStyle,
830
- children: [
831
- "共 ",
832
- messages.length,
833
- " 条消息"
834
- ]
835
- }), /* @__PURE__ */ jsxs("table", {
836
- style: tableStyle,
837
- children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
838
- /* @__PURE__ */ jsx("th", { style: {
839
- ...tableHeaderStyle,
840
- width: "30px"
841
- } }),
842
- /* @__PURE__ */ jsx("th", {
843
- style: {
844
- ...tableHeaderStyle,
845
- width: "60px"
846
- },
847
- children: "类型"
848
- }),
849
- /* @__PURE__ */ jsx("th", {
850
- style: tableHeaderStyle,
851
- children: "内容"
852
- })
853
- ] }) }), /* @__PURE__ */ jsx("tbody", { children: messages.map((msg, index) => {
854
- const message = msg;
855
- const isExpanded = expandedRows.has(index);
856
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("tr", {
857
- style: {
858
- cursor: "pointer",
859
- transition: "background 0.2s"
860
- },
861
- onClick: () => toggleRow(index),
862
- onMouseEnter: (e) => {
863
- e.currentTarget.style.background = "#f5f5f5";
864
- },
865
- onMouseLeave: (e) => {
866
- e.currentTarget.style.background = "transparent";
867
- },
868
- children: [
869
- /* @__PURE__ */ jsx("td", {
870
- style: tableCellStyle,
871
- children: /* @__PURE__ */ jsx("button", {
872
- style: expandButtonStyle,
873
- onClick: (e) => {
874
- e.stopPropagation();
875
- toggleRow(index);
876
- },
877
- children: isExpanded ? "▼" : "▶"
878
- })
879
- }),
880
- /* @__PURE__ */ jsx("td", {
881
- style: tableCellStyle,
882
- children: /* @__PURE__ */ jsx("span", {
883
- style: getTypeTagStyle(message.type),
884
- children: message.type || "unknown"
885
- })
886
- }),
887
- /* @__PURE__ */ jsx("td", {
888
- style: tableCellStyle,
889
- children: renderMessageContent(message)
890
- })
891
- ]
892
- }, index), isExpanded && /* @__PURE__ */ jsx("tr", {
893
- style: expandedRowStyle,
894
- children: /* @__PURE__ */ jsx("td", {
895
- colSpan: 3,
896
- style: {
897
- padding: 0,
898
- borderBottom: "1px solid #e8e8e8"
899
- },
900
- children: /* @__PURE__ */ jsx("div", {
901
- style: jsonContainerStyle,
902
- children: /* @__PURE__ */ jsx("pre", {
903
- style: {
904
- ...preStyle,
905
- margin: 0,
906
- maxHeight: "300px",
907
- overflow: "auto"
908
- },
909
- children: formatJson(message)
910
- })
911
- })
912
- })
913
- })] });
914
- }) })]
915
- })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
916
- style: statsStyle,
917
- children: ["State 键数量: ", Object.keys(streamState).length]
918
- }), /* @__PURE__ */ jsx("pre", {
919
- style: preStyle,
920
- children: formatJson(streamState)
921
- })] })
922
- })]
923
- })] });
924
- }
925
- //#endregion
926
- //#region src/hooks/useInterrupt.tsx
927
- /**
928
- * InterruptManager - 管理 Interrupt 状态的 Hook
929
- *
930
- * 封装 Interrupt 状态管理和处理逻辑,避免在 AgentChat 组件中臃肿
931
- */
932
- function useInterrupt({ interrupt, config, onSubmit }) {
933
- const [activeInterrupt, setActiveInterrupt] = useState(null);
934
- const processedInterruptIdRef = useRef(null);
935
- useEffect(() => {
936
- if (interrupt) {
937
- const interruptEvent = interrupt;
938
- if (interruptEvent.id !== processedInterruptIdRef.current) {
939
- processedInterruptIdRef.current = interruptEvent.id;
940
- setActiveInterrupt(interruptEvent);
941
- }
942
- } else {
943
- processedInterruptIdRef.current = null;
944
- setActiveInterrupt(null);
945
- }
946
- }, [interrupt?.id]);
947
- const handleResolveInterrupt = useCallback((response) => {
948
- onSubmit(null, { command: { resume: response } });
949
- setActiveInterrupt(null);
950
- }, [onSubmit]);
951
- return {
952
- activeInterrupt,
953
- handleResolveInterrupt,
954
- renderInterrupt: useCallback(() => {
955
- if (!activeInterrupt || !config?.render) return null;
956
- return /* @__PURE__ */ jsx("div", {
957
- className: "agent-chat-interrupt",
958
- children: config.render({
959
- event: activeInterrupt,
960
- resolve: handleResolveInterrupt
961
- })
962
- });
963
- }, [
964
- activeInterrupt,
965
- config,
966
- handleResolveInterrupt
967
- ])
968
- };
969
- }
970
- //#endregion
971
- //#region src/hooks/useToolExecution.ts
972
- /**
973
- * useToolExecution - 管理前端工具执行的 Hook
974
- *
975
- * 职责:
976
- * 1. 管理工具执行状态
977
- * 2. 自动执行前端工具(避免重复执行)
978
- * 3. 通知外部执行状态变化
979
- * 4. 支持批量提交工具结果
980
- */
981
- function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChange, onToolResultsBatch, completedToolResults }) {
982
- const executedCallsRef = useRef(/* @__PURE__ */ new Set());
983
- const executingCallsRef = useRef(/* @__PURE__ */ new Set());
984
- const pendingNotifiedRef = useRef(/* @__PURE__ */ new Set());
985
- const notifiedCompletedRef = useRef(/* @__PURE__ */ new Set());
986
- const batchCallIdsRef = useRef(/* @__PURE__ */ new Set());
987
- const batchResultsRef = useRef(/* @__PURE__ */ new Map());
988
- const isProcessingRef = useRef(false);
989
- const batchSubmittedRef = useRef(false);
990
- useEffect(() => {
991
- if (!completedToolResults || completedToolResults.size === 0) return;
992
- completedToolResults.forEach((result, callId) => {
993
- if (notifiedCompletedRef.current.has(callId)) return;
994
- notifiedCompletedRef.current.add(callId);
995
- executedCallsRef.current.add(callId);
996
- const call = toolCalls.find((c) => c.id === callId);
997
- if (call) onExecutionChange?.({
998
- callId,
999
- name: call.name,
1000
- args: call.args,
1001
- status: "success",
1002
- result
1003
- });
502
+ }
503
+ //#endregion
504
+ //#region src/hooks/useToolExecution.ts
505
+ /**
506
+ * useToolExecution - 管理前端工具执行的 Hook
507
+ *
508
+ * 职责:
509
+ * 1. 管理工具执行状态
510
+ * 2. 自动执行前端工具(避免重复执行)
511
+ * 3. 通知外部执行状态变化
512
+ * 4. 支持批量提交工具结果
513
+ */
514
+ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChange, onToolResultsBatch, completedToolResults }) {
515
+ const executedCallsRef = useRef(/* @__PURE__ */ new Set());
516
+ const executingCallsRef = useRef(/* @__PURE__ */ new Set());
517
+ const pendingNotifiedRef = useRef(/* @__PURE__ */ new Set());
518
+ const notifiedCompletedRef = useRef(/* @__PURE__ */ new Set());
519
+ const batchCallIdsRef = useRef(/* @__PURE__ */ new Set());
520
+ const batchResultsRef = useRef(/* @__PURE__ */ new Map());
521
+ const isProcessingRef = useRef(false);
522
+ const batchSubmittedRef = useRef(false);
523
+ useEffect(() => {
524
+ if (!completedToolResults || completedToolResults.size === 0) return;
525
+ completedToolResults.forEach((result, callId) => {
526
+ if (notifiedCompletedRef.current.has(callId)) return;
527
+ notifiedCompletedRef.current.add(callId);
528
+ executedCallsRef.current.add(callId);
529
+ const call = toolCalls.find((c) => c.id === callId);
530
+ if (call) onExecutionChange?.({
531
+ callId,
532
+ name: call.name,
533
+ args: call.args,
534
+ status: "success",
535
+ result
536
+ });
1004
537
  });
1005
538
  }, [completedToolResults, toolCalls]);
1006
539
  /**
@@ -1349,35 +882,21 @@ function injectStyles() {
1349
882
  //#endregion
1350
883
  //#region src/components/AgentChat.tsx
1351
884
  injectStyles();
1352
- const AgentChat = forwardRef(({ apiUrl, assistantId, headers, threadId, onThreadIdChange, className = "", tools, contexts, messageConfig, inputConfig, onError, interruptConfig, agentState, onAgentStateChange, showDebug, welcome }, ref) => {
885
+ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, messageConfig, inputConfig, interruptConfig, agentState, welcome }, ref) => {
1353
886
  const { onPreSend, ...chatInputConfig } = inputConfig || {};
1354
887
  const chatInputRef = useRef(null);
1355
- const stream = useStream({
1356
- apiUrl,
1357
- assistantId,
1358
- threadId,
1359
- onThreadId: (newThreadId) => {
1360
- onThreadIdChange?.(newThreadId);
1361
- },
1362
- defaultHeaders: headers,
1363
- messagesKey: "messages",
1364
- onError: (error) => {
1365
- const err = error instanceof Error ? error : new Error(String(error));
1366
- onError?.(err);
1367
- }
1368
- });
1369
888
  useImperativeHandle(ref, () => ({
1370
889
  input: chatInputRef.current,
1371
890
  setSkill: (skill) => chatInputRef.current?.setSkill?.(skill),
1372
891
  clearInput: () => chatInputRef.current?.clear?.(),
1373
- focusInput: () => chatInputRef.current?.focus?.(),
1374
- stream
892
+ focusInput: () => chatInputRef.current?.focus?.()
1375
893
  }));
1376
- useEffect(() => {
1377
- if (stream.values && onAgentStateChange) onAgentStateChange(stream.values);
1378
- }, [stream.values, onAgentStateChange]);
894
+ const interruptEvent = stream.interrupt;
1379
895
  const { renderInterrupt: interruptRender } = useInterrupt({
1380
- interrupt: stream.interrupt,
896
+ interrupt: interruptEvent ? {
897
+ value: interruptEvent.value,
898
+ id: interruptEvent.id
899
+ } : null,
1381
900
  config: interruptConfig,
1382
901
  onSubmit: stream.submit
1383
902
  });
@@ -1469,55 +988,533 @@ const AgentChat = forwardRef(({ apiUrl, assistantId, headers, threadId, onThread
1469
988
  const shouldRenderWelcome = messages.length === 0 && !isLoading && welcome;
1470
989
  return /* @__PURE__ */ jsxs("div", {
1471
990
  className: `agent-chat-container ${className}`,
1472
- children: [
1473
- shouldRenderWelcome ? /* @__PURE__ */ jsx("div", {
1474
- className: "agent-message-list agent-chat-welcome",
1475
- children: welcome
1476
- }) : /* @__PURE__ */ jsx(MessageList, {
1477
- messages,
1478
- isLoading: stream.isLoading,
1479
- tools,
1480
- toolExecutions,
1481
- components: messageConfig?.components,
1482
- securityConfig: {
1483
- allowedTags: messageConfig?.allowedTags,
1484
- literalTagContent: messageConfig?.literalTagContent
991
+ children: [shouldRenderWelcome ? /* @__PURE__ */ jsx("div", {
992
+ className: "agent-message-list agent-chat-welcome",
993
+ children: welcome
994
+ }) : /* @__PURE__ */ jsx(MessageList, {
995
+ messages,
996
+ isLoading: stream.isLoading,
997
+ tools,
998
+ toolExecutions,
999
+ components: messageConfig?.components,
1000
+ securityConfig: {
1001
+ allowedTags: messageConfig?.allowedTags,
1002
+ literalTagContent: messageConfig?.literalTagContent
1003
+ },
1004
+ loadingColor: messageConfig?.loadingColor,
1005
+ interruptRender
1006
+ }), /* @__PURE__ */ jsx(ChatInput, {
1007
+ ref: chatInputRef,
1008
+ onSend: handleSend,
1009
+ onStop: handleStop,
1010
+ isLoading,
1011
+ className: "agent-chat-input",
1012
+ ...chatInputConfig
1013
+ })]
1014
+ });
1015
+ });
1016
+ AgentChat.displayName = "AgentChat";
1017
+ //#endregion
1018
+ //#region src/components/ToolCard.tsx
1019
+ /**
1020
+ * ToolCard 组件 - 用于展示工具调用结果
1021
+ * 样式:圆角矩形框,左侧 icon,label 分两行显示(prefix 小字体,content 默认字体)
1022
+ */
1023
+ const ToolCard = ({ children, style, ...rest }) => {
1024
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(ThoughtChain.Item, {
1025
+ variant: "text",
1026
+ ...rest,
1027
+ style: {
1028
+ paddingLeft: 0,
1029
+ ...style
1030
+ }
1031
+ }), children] });
1032
+ };
1033
+ //#endregion
1034
+ //#region src/components/DebugPanel.tsx
1035
+ const isDevelopment = () => {
1036
+ if (typeof process !== "undefined" && process.env) return process.env.NODE_ENV === "development";
1037
+ const viteEnv = import.meta.env;
1038
+ if (typeof import.meta !== "undefined" && viteEnv) return viteEnv.DEV === true || viteEnv.MODE === "development";
1039
+ return false;
1040
+ };
1041
+ function DebugPanel({ streamState, visible = true }) {
1042
+ if (!isDevelopment() || !visible) return null;
1043
+ const streamMessages = streamState.messages || [];
1044
+ const [isOpen, setIsOpen] = useState(false);
1045
+ const [activeTab, setActiveTab] = useState("messages");
1046
+ const [expandedRows, setExpandedRows] = useState(/* @__PURE__ */ new Set());
1047
+ const [buttonPos, setButtonPos] = useState({
1048
+ x: 0,
1049
+ y: 0
1050
+ });
1051
+ const [isDraggingButton, setIsDraggingButton] = useState(false);
1052
+ const buttonDragRef = useRef(null);
1053
+ const [dialogPos, setDialogPos] = useState({
1054
+ x: 0,
1055
+ y: 0
1056
+ });
1057
+ const [isDraggingDialog, setIsDraggingDialog] = useState(false);
1058
+ const dialogDragRef = useRef(null);
1059
+ const handleButtonMouseDown = useCallback((e) => {
1060
+ e.preventDefault();
1061
+ buttonDragRef.current = {
1062
+ startX: e.clientX,
1063
+ startY: e.clientY,
1064
+ initialX: buttonPos.x,
1065
+ initialY: buttonPos.y
1066
+ };
1067
+ setIsDraggingButton(true);
1068
+ }, [buttonPos]);
1069
+ const handleDialogMouseDown = useCallback((e) => {
1070
+ if (!e.target.closest(".debug-panel-header")) return;
1071
+ e.preventDefault();
1072
+ dialogDragRef.current = {
1073
+ startX: e.clientX,
1074
+ startY: e.clientY,
1075
+ initialX: dialogPos.x,
1076
+ initialY: dialogPos.y
1077
+ };
1078
+ setIsDraggingDialog(true);
1079
+ }, [dialogPos]);
1080
+ useEffect(() => {
1081
+ const handleMouseMove = (e) => {
1082
+ if (isDraggingButton && buttonDragRef.current) {
1083
+ const dx = e.clientX - buttonDragRef.current.startX;
1084
+ const dy = e.clientY - buttonDragRef.current.startY;
1085
+ setButtonPos({
1086
+ x: buttonDragRef.current.initialX + dx,
1087
+ y: buttonDragRef.current.initialY + dy
1088
+ });
1089
+ }
1090
+ if (isDraggingDialog && dialogDragRef.current) {
1091
+ const dx = e.clientX - dialogDragRef.current.startX;
1092
+ const dy = e.clientY - dialogDragRef.current.startY;
1093
+ setDialogPos({
1094
+ x: dialogDragRef.current.initialX + dx,
1095
+ y: dialogDragRef.current.initialY + dy
1096
+ });
1097
+ }
1098
+ };
1099
+ const handleMouseUp = () => {
1100
+ setIsDraggingButton(false);
1101
+ setIsDraggingDialog(false);
1102
+ buttonDragRef.current = null;
1103
+ dialogDragRef.current = null;
1104
+ };
1105
+ if (isDraggingButton || isDraggingDialog) {
1106
+ document.addEventListener("mousemove", handleMouseMove);
1107
+ document.addEventListener("mouseup", handleMouseUp);
1108
+ }
1109
+ return () => {
1110
+ document.removeEventListener("mousemove", handleMouseMove);
1111
+ document.removeEventListener("mouseup", handleMouseUp);
1112
+ };
1113
+ }, [isDraggingButton, isDraggingDialog]);
1114
+ const formatJson = (data) => {
1115
+ try {
1116
+ return JSON.stringify(data, null, 2);
1117
+ } catch {
1118
+ return String(data);
1119
+ }
1120
+ };
1121
+ const toggleRow = (index) => {
1122
+ setExpandedRows((prev) => {
1123
+ const next = new Set(prev);
1124
+ if (next.has(index)) next.delete(index);
1125
+ else next.add(index);
1126
+ return next;
1127
+ });
1128
+ };
1129
+ const getTypeTagStyle = (type) => {
1130
+ const color = {
1131
+ human: {
1132
+ bg: "#e6f4ff",
1133
+ color: "#0958d9"
1134
+ },
1135
+ ai: {
1136
+ bg: "#f6ffed",
1137
+ color: "#389e0d"
1138
+ },
1139
+ tool: {
1140
+ bg: "#fff7e6",
1141
+ color: "#d46b08"
1142
+ },
1143
+ system: {
1144
+ bg: "#f9f0ff",
1145
+ color: "#722ed1"
1146
+ },
1147
+ function: {
1148
+ bg: "#fff2f0",
1149
+ color: "#cf1322"
1150
+ }
1151
+ }[type || ""] || {
1152
+ bg: "#f5f5f5",
1153
+ color: "#666"
1154
+ };
1155
+ return {
1156
+ display: "inline-block",
1157
+ padding: "2px 8px",
1158
+ borderRadius: "4px",
1159
+ fontSize: "12px",
1160
+ fontWeight: 500,
1161
+ background: color.bg,
1162
+ color: color.color
1163
+ };
1164
+ };
1165
+ const formatContent = (content) => {
1166
+ if (!content) return "";
1167
+ if (Array.isArray(content)) return content.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join("");
1168
+ return String(content);
1169
+ };
1170
+ const renderMessageContent = (message) => {
1171
+ const hasToolCalls = message.tool_calls && Array.isArray(message.tool_calls) && message.tool_calls.length > 0;
1172
+ return /* @__PURE__ */ jsxs("div", {
1173
+ style: {
1174
+ display: "flex",
1175
+ flexDirection: "column",
1176
+ gap: "8px"
1177
+ },
1178
+ children: [message.content && /* @__PURE__ */ jsx("div", {
1179
+ style: {
1180
+ color: "#333",
1181
+ lineHeight: 1.5,
1182
+ wordBreak: "break-all"
1485
1183
  },
1486
- loadingColor: messageConfig?.loadingColor,
1487
- interruptRender
1488
- }),
1489
- /* @__PURE__ */ jsx(ChatInput, {
1490
- ref: chatInputRef,
1491
- onSend: handleSend,
1492
- onStop: handleStop,
1493
- isLoading,
1494
- className: "agent-chat-input",
1495
- ...chatInputConfig
1496
- }),
1497
- /* @__PURE__ */ jsx(DebugPanel, {
1498
- messages: stream.messages,
1499
- streamState: stream.values || {},
1500
- visible: showDebug
1501
- })
1502
- ]
1184
+ children: formatContent(message.content)
1185
+ }), hasToolCalls && /* @__PURE__ */ jsxs("div", {
1186
+ style: {
1187
+ marginTop: "4px",
1188
+ padding: "8px",
1189
+ background: "#f6ffed",
1190
+ border: "1px solid #b7eb8f",
1191
+ borderRadius: "6px"
1192
+ },
1193
+ children: [/* @__PURE__ */ jsxs("div", {
1194
+ style: {
1195
+ fontSize: "11px",
1196
+ fontWeight: 600,
1197
+ color: "#389e0d",
1198
+ marginBottom: "6px",
1199
+ display: "flex",
1200
+ alignItems: "center",
1201
+ gap: "4px"
1202
+ },
1203
+ children: [/* @__PURE__ */ jsx("span", { children: "🔧" }), /* @__PURE__ */ jsxs("span", { children: [
1204
+ "Tool Calls (",
1205
+ message.tool_calls.length,
1206
+ ")"
1207
+ ] })]
1208
+ }), /* @__PURE__ */ jsx("pre", {
1209
+ style: {
1210
+ ...preStyle,
1211
+ background: "#fff",
1212
+ border: "1px solid #d9f7be",
1213
+ maxHeight: "200px",
1214
+ fontSize: "11px"
1215
+ },
1216
+ children: formatJson(message.tool_calls)
1217
+ })]
1218
+ })]
1219
+ });
1220
+ };
1221
+ const buttonStyle = {
1222
+ position: "fixed",
1223
+ right: `${20 - buttonPos.x}px`,
1224
+ bottom: `${20 - buttonPos.y}px`,
1225
+ width: "48px",
1226
+ height: "48px",
1227
+ borderRadius: "50%",
1228
+ background: "#1677ff",
1229
+ color: "#fff",
1230
+ border: "none",
1231
+ cursor: isDraggingButton ? "grabbing" : "grab",
1232
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
1233
+ display: "flex",
1234
+ alignItems: "center",
1235
+ justifyContent: "center",
1236
+ fontSize: "20px",
1237
+ zIndex: 9998,
1238
+ transition: isDraggingButton ? "none" : "box-shadow 0.2s",
1239
+ userSelect: "none"
1240
+ };
1241
+ const dialogStyle = {
1242
+ position: "fixed",
1243
+ left: "50%",
1244
+ top: "50%",
1245
+ transform: `translate(calc(-50% + ${dialogPos.x}px), calc(-50% + ${dialogPos.y}px))`,
1246
+ width: "1000px",
1247
+ maxWidth: "90vw",
1248
+ height: "650px",
1249
+ maxHeight: "85vh",
1250
+ background: "#fff",
1251
+ borderRadius: "12px",
1252
+ boxShadow: "0 8px 32px rgba(0, 0, 0, 0.2)",
1253
+ zIndex: 9999,
1254
+ display: "flex",
1255
+ flexDirection: "column",
1256
+ overflow: "hidden",
1257
+ cursor: isDraggingDialog ? "grabbing" : "default"
1258
+ };
1259
+ const headerStyle = {
1260
+ padding: "12px 16px",
1261
+ background: "#f5f5f5",
1262
+ borderBottom: "1px solid #e8e8e8",
1263
+ display: "flex",
1264
+ alignItems: "center",
1265
+ justifyContent: "space-between",
1266
+ cursor: isDraggingDialog ? "grabbing" : "grab"
1267
+ };
1268
+ const tabContainerStyle = {
1269
+ display: "flex",
1270
+ gap: "8px"
1271
+ };
1272
+ const getTabStyle = (isActive) => ({
1273
+ padding: "6px 16px",
1274
+ borderRadius: "6px",
1275
+ border: "none",
1276
+ background: isActive ? "#1677ff" : "transparent",
1277
+ color: isActive ? "#fff" : "#666",
1278
+ cursor: "pointer",
1279
+ fontSize: "14px",
1280
+ transition: "all 0.2s"
1503
1281
  });
1504
- });
1505
- AgentChat.displayName = "AgentChat";
1282
+ const closeButtonStyle = {
1283
+ width: "28px",
1284
+ height: "28px",
1285
+ borderRadius: "50%",
1286
+ border: "none",
1287
+ background: "#ff4d4f",
1288
+ color: "#fff",
1289
+ cursor: "pointer",
1290
+ display: "flex",
1291
+ alignItems: "center",
1292
+ justifyContent: "center",
1293
+ fontSize: "16px",
1294
+ transition: "background 0.2s"
1295
+ };
1296
+ const contentStyle = {
1297
+ flex: 1,
1298
+ overflow: "auto",
1299
+ padding: "16px",
1300
+ background: "#fafafa"
1301
+ };
1302
+ const preStyle = {
1303
+ margin: 0,
1304
+ padding: "12px",
1305
+ background: "#f8f9fa",
1306
+ color: "#333",
1307
+ borderRadius: "8px",
1308
+ fontSize: "12px",
1309
+ fontFamily: "\"Fira Code\", \"Monaco\", \"Consolas\", monospace",
1310
+ lineHeight: 1.5,
1311
+ overflow: "auto",
1312
+ whiteSpace: "pre-wrap",
1313
+ wordBreak: "break-word",
1314
+ border: "1px solid #e8e8e8"
1315
+ };
1316
+ const statsStyle = {
1317
+ padding: "8px 12px",
1318
+ background: "#e6f4ff",
1319
+ borderRadius: "6px",
1320
+ marginBottom: "12px",
1321
+ fontSize: "13px",
1322
+ color: "#0958d9"
1323
+ };
1324
+ const tableStyle = {
1325
+ width: "100%",
1326
+ borderCollapse: "collapse",
1327
+ fontSize: "13px",
1328
+ background: "#fff",
1329
+ borderRadius: "8px",
1330
+ overflow: "hidden",
1331
+ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)"
1332
+ };
1333
+ const tableHeaderStyle = {
1334
+ background: "#f5f5f5",
1335
+ fontWeight: 600,
1336
+ color: "#333",
1337
+ textAlign: "left",
1338
+ padding: "10px 12px",
1339
+ borderBottom: "1px solid #e8e8e8",
1340
+ whiteSpace: "nowrap"
1341
+ };
1342
+ const tableCellStyle = {
1343
+ padding: "10px 12px",
1344
+ borderBottom: "1px solid #f0f0f0",
1345
+ verticalAlign: "top"
1346
+ };
1347
+ const expandButtonStyle = {
1348
+ width: "20px",
1349
+ height: "20px",
1350
+ border: "none",
1351
+ background: "transparent",
1352
+ cursor: "pointer",
1353
+ display: "flex",
1354
+ alignItems: "center",
1355
+ justifyContent: "center",
1356
+ fontSize: "12px",
1357
+ color: "#666",
1358
+ borderRadius: "4px",
1359
+ transition: "background 0.2s"
1360
+ };
1361
+ const expandedRowStyle = { background: "#fafafa" };
1362
+ const jsonContainerStyle = {
1363
+ padding: "12px",
1364
+ background: "#fff",
1365
+ borderRadius: "6px",
1366
+ margin: "8px 0",
1367
+ border: "1px solid #e8e8e8"
1368
+ };
1369
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
1370
+ style: buttonStyle,
1371
+ onMouseDown: handleButtonMouseDown,
1372
+ onClick: () => !isDraggingButton && setIsOpen(true),
1373
+ title: "打开调试面板",
1374
+ children: "🐛"
1375
+ }), isOpen && /* @__PURE__ */ jsxs("div", {
1376
+ style: dialogStyle,
1377
+ onMouseDown: handleDialogMouseDown,
1378
+ children: [/* @__PURE__ */ jsxs("div", {
1379
+ className: "debug-panel-header",
1380
+ style: headerStyle,
1381
+ children: [/* @__PURE__ */ jsxs("div", {
1382
+ style: tabContainerStyle,
1383
+ children: [/* @__PURE__ */ jsxs("button", {
1384
+ style: getTabStyle(activeTab === "messages"),
1385
+ onClick: () => setActiveTab("messages"),
1386
+ children: [
1387
+ "Messages (",
1388
+ streamMessages.length,
1389
+ ")"
1390
+ ]
1391
+ }), /* @__PURE__ */ jsx("button", {
1392
+ style: getTabStyle(activeTab === "state"),
1393
+ onClick: () => setActiveTab("state"),
1394
+ children: "State"
1395
+ })]
1396
+ }), /* @__PURE__ */ jsx("button", {
1397
+ style: closeButtonStyle,
1398
+ onClick: () => setIsOpen(false),
1399
+ title: "关闭",
1400
+ children: "×"
1401
+ })]
1402
+ }), /* @__PURE__ */ jsx("div", {
1403
+ style: contentStyle,
1404
+ children: activeTab === "messages" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
1405
+ style: statsStyle,
1406
+ children: [
1407
+ "共 ",
1408
+ streamMessages.length,
1409
+ " 条消息"
1410
+ ]
1411
+ }), /* @__PURE__ */ jsxs("table", {
1412
+ style: tableStyle,
1413
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
1414
+ /* @__PURE__ */ jsx("th", { style: {
1415
+ ...tableHeaderStyle,
1416
+ width: "30px"
1417
+ } }),
1418
+ /* @__PURE__ */ jsx("th", {
1419
+ style: {
1420
+ ...tableHeaderStyle,
1421
+ width: "60px"
1422
+ },
1423
+ children: "类型"
1424
+ }),
1425
+ /* @__PURE__ */ jsx("th", {
1426
+ style: tableHeaderStyle,
1427
+ children: "内容"
1428
+ })
1429
+ ] }) }), /* @__PURE__ */ jsx("tbody", { children: streamMessages.map((msg, index) => {
1430
+ const message = msg;
1431
+ const isExpanded = expandedRows.has(index);
1432
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("tr", {
1433
+ style: {
1434
+ cursor: "pointer",
1435
+ transition: "background 0.2s"
1436
+ },
1437
+ onClick: () => toggleRow(index),
1438
+ onMouseEnter: (e) => {
1439
+ e.currentTarget.style.background = "#f5f5f5";
1440
+ },
1441
+ onMouseLeave: (e) => {
1442
+ e.currentTarget.style.background = "transparent";
1443
+ },
1444
+ children: [
1445
+ /* @__PURE__ */ jsx("td", {
1446
+ style: tableCellStyle,
1447
+ children: /* @__PURE__ */ jsx("button", {
1448
+ style: expandButtonStyle,
1449
+ onClick: (e) => {
1450
+ e.stopPropagation();
1451
+ toggleRow(index);
1452
+ },
1453
+ children: isExpanded ? "▼" : "▶"
1454
+ })
1455
+ }),
1456
+ /* @__PURE__ */ jsx("td", {
1457
+ style: tableCellStyle,
1458
+ children: /* @__PURE__ */ jsx("span", {
1459
+ style: getTypeTagStyle(message.type),
1460
+ children: message.type || "unknown"
1461
+ })
1462
+ }),
1463
+ /* @__PURE__ */ jsx("td", {
1464
+ style: tableCellStyle,
1465
+ children: renderMessageContent(message)
1466
+ })
1467
+ ]
1468
+ }, index), isExpanded && /* @__PURE__ */ jsx("tr", {
1469
+ style: expandedRowStyle,
1470
+ children: /* @__PURE__ */ jsx("td", {
1471
+ colSpan: 3,
1472
+ style: {
1473
+ padding: 0,
1474
+ borderBottom: "1px solid #e8e8e8"
1475
+ },
1476
+ children: /* @__PURE__ */ jsx("div", {
1477
+ style: jsonContainerStyle,
1478
+ children: /* @__PURE__ */ jsx("pre", {
1479
+ style: {
1480
+ ...preStyle,
1481
+ margin: 0,
1482
+ maxHeight: "300px",
1483
+ overflow: "auto"
1484
+ },
1485
+ children: formatJson(message)
1486
+ })
1487
+ })
1488
+ })
1489
+ })] });
1490
+ }) })]
1491
+ })] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
1492
+ style: statsStyle,
1493
+ children: ["State 键数量: ", Object.keys(streamState).length]
1494
+ }), /* @__PURE__ */ jsx("pre", {
1495
+ style: preStyle,
1496
+ children: formatJson(streamState)
1497
+ })] })
1498
+ })]
1499
+ })] });
1500
+ }
1506
1501
  //#endregion
1507
- //#region src/components/ToolCard.tsx
1502
+ //#region src/hooks/useAgentStream.ts
1508
1503
  /**
1509
- * ToolCard 组件 - 用于展示工具调用结果
1510
- * 样式:圆角矩形框,左侧 icon,label 分两行显示(prefix 小字体,content 默认字体)
1504
+ * 包装 useStream hook,为 AgentChat 提供统一的 stream 实例创建方式
1505
+ *
1506
+ * @example
1507
+ * ```tsx
1508
+ * const stream = useAgentStream({
1509
+ * apiUrl: "http://localhost:2026",
1510
+ * assistantId: "demo",
1511
+ * });
1512
+ *
1513
+ * <AgentChat stream={stream} />
1514
+ * ```
1511
1515
  */
1512
- const ToolCard = ({ children, style, ...rest }) => {
1513
- return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(ThoughtChain.Item, {
1514
- variant: "text",
1515
- ...rest,
1516
- style: {
1517
- paddingLeft: 0,
1518
- ...style
1519
- }
1520
- }), children] });
1521
- };
1516
+ function useAgentStream(options) {
1517
+ return useStream(options);
1518
+ }
1522
1519
  //#endregion
1523
- export { AgentChat, MessageContentRenderer, ToolCard };
1520
+ export { AgentChat, DebugPanel, MessageContentRenderer, ToolCard, useAgentStream };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegintech/langchain-react-agent",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "description": "LangChain Agent UI component library for React",
5
5
  "license": "MIT",
6
6
  "files": [