@vegintech/langchain-react-agent 0.0.35 → 0.0.37

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
@@ -154,7 +154,7 @@ interface ToolCallInput {
154
154
  status?: "success" | "error";
155
155
  }
156
156
  /** 工具执行状态 */
157
- type ToolExecutionStatus = "pending" | "running" | "success" | "error";
157
+ type ToolExecutionStatus = "pending" | "running" | "success" | "error" | "cancelled";
158
158
  /** 工具执行结果 */
159
159
  interface ToolExecutionRecord {
160
160
  callId: string;
package/dist/index.mjs CHANGED
@@ -2,6 +2,7 @@ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo
2
2
  import { Actions, Bubble, Sender, Think, ThoughtChain } from "@ant-design/x";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  import { Streamdown } from "streamdown";
5
+ import { code } from "@streamdown/code";
5
6
  import { useStream } from "@langchain/langgraph-sdk/react";
6
7
  //#region src/components/ChatInput.tsx
7
8
  const slotConfig = [];
@@ -180,6 +181,7 @@ const ContentBlock = ({ block, index, components, securityConfig }) => {
180
181
  },
181
182
  allowedTags: securityConfig?.allowedTags,
182
183
  literalTagContent: securityConfig?.literalTagContent,
184
+ plugins: { code },
183
185
  controls: { table: {
184
186
  copy: false,
185
187
  download: false,
@@ -563,10 +565,16 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
563
565
  const batchResultsRef = useRef(/* @__PURE__ */ new Map());
564
566
  const isProcessingRef = useRef(false);
565
567
  const batchSubmittedRef = useRef(false);
568
+ const cancelledCallIdsRef = useRef(/* @__PURE__ */ new Set());
569
+ const cancelledExecutionsRef = useRef(/* @__PURE__ */ new Set());
566
570
  useEffect(() => {
567
571
  if (!completedToolResults || completedToolResults.size === 0) return;
568
572
  completedToolResults.forEach(({ result, status }, callId) => {
569
573
  if (notifiedCompletedRef.current.has(callId)) return;
574
+ if (cancelledExecutionsRef.current.has(callId)) {
575
+ notifiedCompletedRef.current.add(callId);
576
+ return;
577
+ }
570
578
  if (executedCallsRef.current.has(callId)) {
571
579
  notifiedCompletedRef.current.add(callId);
572
580
  return;
@@ -630,7 +638,12 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
630
638
  batchResultsRef.current.set(callId, createToolMessage(callId, call.name, null, "error", errorMessage));
631
639
  } finally {
632
640
  executingCallsRef.current.delete(callId);
633
- executedCallsRef.current.add(callId);
641
+ if (cancelledCallIdsRef.current.has(callId)) {
642
+ cancelledCallIdsRef.current.delete(callId);
643
+ cancelledExecutionsRef.current.add(callId);
644
+ batchResultsRef.current.delete(callId);
645
+ batchCallIdsRef.current.delete(callId);
646
+ } else executedCallsRef.current.add(callId);
634
647
  checkAndSubmitBatch();
635
648
  }
636
649
  }, [onExecutionChange, checkAndSubmitBatch]);
@@ -643,7 +656,7 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
643
656
  const frontendCalls = [];
644
657
  for (const call of toolCalls) {
645
658
  const callId = call.id;
646
- if (executedCallsRef.current.has(callId) || executingCallsRef.current.has(callId)) continue;
659
+ if (executedCallsRef.current.has(callId) || executingCallsRef.current.has(callId) || cancelledExecutionsRef.current.has(callId)) continue;
647
660
  const tool = findTool(tools, call.name);
648
661
  if (isLoading) {
649
662
  if (!pendingNotifiedRef.current.has(callId)) {
@@ -695,11 +708,36 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
695
708
  executeFrontendTool,
696
709
  onExecutionChange
697
710
  ]);
711
+ /**
712
+ * 取消当前正在执行或待执行的前端工具
713
+ *
714
+ * 由于 JavaScript 无法真正中断异步执行,取消仅做标记:
715
+ * 1. 正在执行的工具完成后会被无视(不提交结果)
716
+ * 2. 尚未执行的工具被直接移除,不再执行
717
+ */
718
+ const cancel = useCallback(() => {
719
+ const callIdsToCancel = new Set([...executingCallsRef.current, ...batchCallIdsRef.current]);
720
+ for (const callId of callIdsToCancel) {
721
+ cancelledCallIdsRef.current.add(callId);
722
+ if (!executingCallsRef.current.has(callId)) {
723
+ cancelledExecutionsRef.current.add(callId);
724
+ batchCallIdsRef.current.delete(callId);
725
+ }
726
+ const call = toolCalls.find((c) => c.id === callId);
727
+ if (call) onExecutionChange?.({
728
+ callId,
729
+ name: call.name,
730
+ args: call.args,
731
+ status: "cancelled"
732
+ });
733
+ }
734
+ }, [toolCalls, onExecutionChange]);
698
735
  useEffect(() => {
699
736
  processToolCalls();
700
737
  }, [useMemo(() => {
701
- return toolCalls.filter((call) => !executedCallsRef.current.has(call.id) && !executingCallsRef.current.has(call.id)).map((call) => call.id).sort().join(",");
738
+ return toolCalls.filter((call) => !executedCallsRef.current.has(call.id) && !executingCallsRef.current.has(call.id) && !cancelledExecutionsRef.current.has(call.id)).map((call) => call.id).sort().join(",");
702
739
  }, [toolCalls]), isLoading]);
740
+ return { cancel };
703
741
  }
704
742
  //#endregion
705
743
  //#region src/utils/messageUtils.ts
@@ -951,7 +989,6 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
951
989
  onSubmit: stream.submit
952
990
  });
953
991
  const [toolExecutions, setToolExecutions] = useState(/* @__PURE__ */ new Map());
954
- const isLoading = stream.isLoading;
955
992
  const { messages, toolResults } = useMemo(() => {
956
993
  return processMessages(stream.messages);
957
994
  }, [stream.messages]);
@@ -968,6 +1005,12 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
968
1005
  const frontendTools = useMemo(() => {
969
1006
  return tools?.filter(isFrontendTool) || [];
970
1007
  }, [tools]);
1008
+ const frontendToolNames = useMemo(() => new Set(frontendTools.map((t) => t.name)), [frontendTools]);
1009
+ const hasToolExecuting = useMemo(() => {
1010
+ for (const record of toolExecutions.values()) if (frontendToolNames.has(record.name) && (record.status === "running" || record.status === "pending")) return true;
1011
+ return false;
1012
+ }, [toolExecutions, frontendToolNames]);
1013
+ const isLoading = stream.isLoading || hasToolExecuting;
971
1014
  const submitToStream = useCallback(async (submitMessages) => {
972
1015
  await stream.submit({
973
1016
  ...agentState,
@@ -1012,10 +1055,7 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
1012
1055
  if (messages.length === 0) return;
1013
1056
  await submitToStream(messages);
1014
1057
  }, [onPreSend, submitToStream]);
1015
- const handleStop = useCallback(async () => {
1016
- await stream.stop();
1017
- }, [stream]);
1018
- useToolExecution({
1058
+ const { cancel: cancelToolExecution } = useToolExecution({
1019
1059
  tools,
1020
1060
  toolCalls: allToolCalls,
1021
1061
  isLoading: stream.isLoading,
@@ -1023,6 +1063,10 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
1023
1063
  onToolResultsBatch: handleToolResultsBatch,
1024
1064
  completedToolResults: toolResults
1025
1065
  });
1066
+ const handleStop = useCallback(async () => {
1067
+ cancelToolExecution();
1068
+ await stream.stop();
1069
+ }, [stream, cancelToolExecution]);
1026
1070
  const shouldRenderWelcome = messages.length === 0 && !isLoading && welcome;
1027
1071
  return /* @__PURE__ */ jsxs("div", {
1028
1072
  className: `agent-chat-container ${className}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegintech/langchain-react-agent",
3
- "version": "0.0.35",
3
+ "version": "0.0.37",
4
4
  "description": "LangChain Agent UI component library for React",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -25,6 +25,7 @@
25
25
  "dependencies": {
26
26
  "@ant-design/x": "^2.4.0",
27
27
  "@langchain/langgraph-sdk": "^1.8.7",
28
+ "@streamdown/code": "catalog:",
28
29
  "streamdown": "^2.5.0"
29
30
  },
30
31
  "devDependencies": {