@vegintech/langchain-react-agent 0.0.34 → 0.0.36
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 +3 -1
- package/dist/index.mjs +71 -18
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -150,9 +150,11 @@ interface ToolCallInput {
|
|
|
150
150
|
args: Record<string, unknown>;
|
|
151
151
|
/** 后端工具执行结果(来自 ToolMessage) */
|
|
152
152
|
result?: unknown;
|
|
153
|
+
/** 后端工具执行状态(来自 ToolMessage.status) */
|
|
154
|
+
status?: "success" | "error";
|
|
153
155
|
}
|
|
154
156
|
/** 工具执行状态 */
|
|
155
|
-
type ToolExecutionStatus = "pending" | "running" | "success" | "error";
|
|
157
|
+
type ToolExecutionStatus = "pending" | "running" | "success" | "error" | "cancelled";
|
|
156
158
|
/** 工具执行结果 */
|
|
157
159
|
interface ToolExecutionRecord {
|
|
158
160
|
callId: string;
|
package/dist/index.mjs
CHANGED
|
@@ -356,7 +356,7 @@ const renderToolCalls = (toolCalls, tools, toolExecutions, isLoading) => {
|
|
|
356
356
|
callId: call.id,
|
|
357
357
|
name: call.name,
|
|
358
358
|
args: call.args,
|
|
359
|
-
status: call.result !== void 0 ? "success" : "pending",
|
|
359
|
+
status: call.status || (call.result !== void 0 ? "success" : "pending"),
|
|
360
360
|
result: call.result
|
|
361
361
|
};
|
|
362
362
|
return /* @__PURE__ */ jsx(ToolCallRenderer, {
|
|
@@ -563,10 +563,16 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
563
563
|
const batchResultsRef = useRef(/* @__PURE__ */ new Map());
|
|
564
564
|
const isProcessingRef = useRef(false);
|
|
565
565
|
const batchSubmittedRef = useRef(false);
|
|
566
|
+
const cancelledCallIdsRef = useRef(/* @__PURE__ */ new Set());
|
|
567
|
+
const cancelledExecutionsRef = useRef(/* @__PURE__ */ new Set());
|
|
566
568
|
useEffect(() => {
|
|
567
569
|
if (!completedToolResults || completedToolResults.size === 0) return;
|
|
568
|
-
completedToolResults.forEach((result, callId) => {
|
|
570
|
+
completedToolResults.forEach(({ result, status }, callId) => {
|
|
569
571
|
if (notifiedCompletedRef.current.has(callId)) return;
|
|
572
|
+
if (cancelledExecutionsRef.current.has(callId)) {
|
|
573
|
+
notifiedCompletedRef.current.add(callId);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
570
576
|
if (executedCallsRef.current.has(callId)) {
|
|
571
577
|
notifiedCompletedRef.current.add(callId);
|
|
572
578
|
return;
|
|
@@ -578,7 +584,7 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
578
584
|
callId,
|
|
579
585
|
name: call.name,
|
|
580
586
|
args: call.args,
|
|
581
|
-
status: "success",
|
|
587
|
+
status: call.status || "success",
|
|
582
588
|
result
|
|
583
589
|
});
|
|
584
590
|
});
|
|
@@ -630,7 +636,12 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
630
636
|
batchResultsRef.current.set(callId, createToolMessage(callId, call.name, null, "error", errorMessage));
|
|
631
637
|
} finally {
|
|
632
638
|
executingCallsRef.current.delete(callId);
|
|
633
|
-
|
|
639
|
+
if (cancelledCallIdsRef.current.has(callId)) {
|
|
640
|
+
cancelledCallIdsRef.current.delete(callId);
|
|
641
|
+
cancelledExecutionsRef.current.add(callId);
|
|
642
|
+
batchResultsRef.current.delete(callId);
|
|
643
|
+
batchCallIdsRef.current.delete(callId);
|
|
644
|
+
} else executedCallsRef.current.add(callId);
|
|
634
645
|
checkAndSubmitBatch();
|
|
635
646
|
}
|
|
636
647
|
}, [onExecutionChange, checkAndSubmitBatch]);
|
|
@@ -643,7 +654,7 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
643
654
|
const frontendCalls = [];
|
|
644
655
|
for (const call of toolCalls) {
|
|
645
656
|
const callId = call.id;
|
|
646
|
-
if (executedCallsRef.current.has(callId) || executingCallsRef.current.has(callId)) continue;
|
|
657
|
+
if (executedCallsRef.current.has(callId) || executingCallsRef.current.has(callId) || cancelledExecutionsRef.current.has(callId)) continue;
|
|
647
658
|
const tool = findTool(tools, call.name);
|
|
648
659
|
if (isLoading) {
|
|
649
660
|
if (!pendingNotifiedRef.current.has(callId)) {
|
|
@@ -695,11 +706,36 @@ function useToolExecution({ tools, toolCalls, isLoading = false, onExecutionChan
|
|
|
695
706
|
executeFrontendTool,
|
|
696
707
|
onExecutionChange
|
|
697
708
|
]);
|
|
709
|
+
/**
|
|
710
|
+
* 取消当前正在执行或待执行的前端工具
|
|
711
|
+
*
|
|
712
|
+
* 由于 JavaScript 无法真正中断异步执行,取消仅做标记:
|
|
713
|
+
* 1. 正在执行的工具完成后会被无视(不提交结果)
|
|
714
|
+
* 2. 尚未执行的工具被直接移除,不再执行
|
|
715
|
+
*/
|
|
716
|
+
const cancel = useCallback(() => {
|
|
717
|
+
const callIdsToCancel = new Set([...executingCallsRef.current, ...batchCallIdsRef.current]);
|
|
718
|
+
for (const callId of callIdsToCancel) {
|
|
719
|
+
cancelledCallIdsRef.current.add(callId);
|
|
720
|
+
if (!executingCallsRef.current.has(callId)) {
|
|
721
|
+
cancelledExecutionsRef.current.add(callId);
|
|
722
|
+
batchCallIdsRef.current.delete(callId);
|
|
723
|
+
}
|
|
724
|
+
const call = toolCalls.find((c) => c.id === callId);
|
|
725
|
+
if (call) onExecutionChange?.({
|
|
726
|
+
callId,
|
|
727
|
+
name: call.name,
|
|
728
|
+
args: call.args,
|
|
729
|
+
status: "cancelled"
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}, [toolCalls, onExecutionChange]);
|
|
698
733
|
useEffect(() => {
|
|
699
734
|
processToolCalls();
|
|
700
735
|
}, [useMemo(() => {
|
|
701
|
-
return toolCalls.filter((call) => !executedCallsRef.current.has(call.id) && !executingCallsRef.current.has(call.id)).map((call) => call.id).sort().join(",");
|
|
736
|
+
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
737
|
}, [toolCalls]), isLoading]);
|
|
738
|
+
return { cancel };
|
|
703
739
|
}
|
|
704
740
|
//#endregion
|
|
705
741
|
//#region src/utils/messageUtils.ts
|
|
@@ -749,10 +785,14 @@ function toChatMessage(message, toolResults) {
|
|
|
749
785
|
if (message.type === "human") msgType = "human";
|
|
750
786
|
const toolCalls = extractToolCalls(message);
|
|
751
787
|
let toolCallsWithResult = toolCalls;
|
|
752
|
-
if (toolCalls) toolCallsWithResult = toolCalls.map((tc) =>
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
788
|
+
if (toolCalls) toolCallsWithResult = toolCalls.map((tc) => {
|
|
789
|
+
const toolResult = toolResults.get(tc.id);
|
|
790
|
+
return {
|
|
791
|
+
...tc,
|
|
792
|
+
result: toolResult?.result,
|
|
793
|
+
status: toolResult?.status
|
|
794
|
+
};
|
|
795
|
+
});
|
|
756
796
|
const reasoningContent = additionalKwargs.reasoning_content;
|
|
757
797
|
return {
|
|
758
798
|
id: message.id || crypto.randomUUID(),
|
|
@@ -765,7 +805,7 @@ function toChatMessage(message, toolResults) {
|
|
|
765
805
|
};
|
|
766
806
|
}
|
|
767
807
|
/**
|
|
768
|
-
* 预处理消息列表:建立 tool_call_id -> result 映射,并过滤 ToolMessage
|
|
808
|
+
* 预处理消息列表:建立 tool_call_id -> { result, status } 映射,并过滤 ToolMessage
|
|
769
809
|
*/
|
|
770
810
|
function processMessages(rawMessages) {
|
|
771
811
|
const toolResults = /* @__PURE__ */ new Map();
|
|
@@ -773,10 +813,17 @@ function processMessages(rawMessages) {
|
|
|
773
813
|
const toolCallId = message.tool_call_id || message.additional_kwargs?.tool_call_id;
|
|
774
814
|
if (toolCallId) {
|
|
775
815
|
const textContent = extractTextFromContent(extractContent(message));
|
|
816
|
+
const status = message.status;
|
|
776
817
|
try {
|
|
777
|
-
toolResults.set(toolCallId,
|
|
818
|
+
toolResults.set(toolCallId, {
|
|
819
|
+
result: JSON.parse(textContent),
|
|
820
|
+
status
|
|
821
|
+
});
|
|
778
822
|
} catch {
|
|
779
|
-
toolResults.set(toolCallId,
|
|
823
|
+
toolResults.set(toolCallId, {
|
|
824
|
+
result: textContent,
|
|
825
|
+
status
|
|
826
|
+
});
|
|
780
827
|
}
|
|
781
828
|
}
|
|
782
829
|
}
|
|
@@ -940,7 +987,6 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
|
|
|
940
987
|
onSubmit: stream.submit
|
|
941
988
|
});
|
|
942
989
|
const [toolExecutions, setToolExecutions] = useState(/* @__PURE__ */ new Map());
|
|
943
|
-
const isLoading = stream.isLoading;
|
|
944
990
|
const { messages, toolResults } = useMemo(() => {
|
|
945
991
|
return processMessages(stream.messages);
|
|
946
992
|
}, [stream.messages]);
|
|
@@ -957,6 +1003,12 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
|
|
|
957
1003
|
const frontendTools = useMemo(() => {
|
|
958
1004
|
return tools?.filter(isFrontendTool) || [];
|
|
959
1005
|
}, [tools]);
|
|
1006
|
+
const frontendToolNames = useMemo(() => new Set(frontendTools.map((t) => t.name)), [frontendTools]);
|
|
1007
|
+
const hasToolExecuting = useMemo(() => {
|
|
1008
|
+
for (const record of toolExecutions.values()) if (frontendToolNames.has(record.name) && (record.status === "running" || record.status === "pending")) return true;
|
|
1009
|
+
return false;
|
|
1010
|
+
}, [toolExecutions, frontendToolNames]);
|
|
1011
|
+
const isLoading = stream.isLoading || hasToolExecuting;
|
|
960
1012
|
const submitToStream = useCallback(async (submitMessages) => {
|
|
961
1013
|
await stream.submit({
|
|
962
1014
|
...agentState,
|
|
@@ -1001,10 +1053,7 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
|
|
|
1001
1053
|
if (messages.length === 0) return;
|
|
1002
1054
|
await submitToStream(messages);
|
|
1003
1055
|
}, [onPreSend, submitToStream]);
|
|
1004
|
-
const
|
|
1005
|
-
await stream.stop();
|
|
1006
|
-
}, [stream]);
|
|
1007
|
-
useToolExecution({
|
|
1056
|
+
const { cancel: cancelToolExecution } = useToolExecution({
|
|
1008
1057
|
tools,
|
|
1009
1058
|
toolCalls: allToolCalls,
|
|
1010
1059
|
isLoading: stream.isLoading,
|
|
@@ -1012,6 +1061,10 @@ const AgentChat = forwardRef(({ stream, className = "", tools, contexts, message
|
|
|
1012
1061
|
onToolResultsBatch: handleToolResultsBatch,
|
|
1013
1062
|
completedToolResults: toolResults
|
|
1014
1063
|
});
|
|
1064
|
+
const handleStop = useCallback(async () => {
|
|
1065
|
+
cancelToolExecution();
|
|
1066
|
+
await stream.stop();
|
|
1067
|
+
}, [stream, cancelToolExecution]);
|
|
1015
1068
|
const shouldRenderWelcome = messages.length === 0 && !isLoading && welcome;
|
|
1016
1069
|
return /* @__PURE__ */ jsxs("div", {
|
|
1017
1070
|
className: `agent-chat-container ${className}`,
|