mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-24 22:03:54 +00:00
fix: tool choice run same tool will error (#3502)
This commit is contained in:
@@ -27,9 +27,10 @@ import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools
|
|||||||
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||||
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
|
|
||||||
type ToolRunResponseType = {
|
type ToolRunResponseType = {
|
||||||
toolRunResponse: DispatchFlowResponse;
|
toolRunResponse?: DispatchFlowResponse;
|
||||||
toolMsgParams: ChatCompletionToolMessageParam;
|
toolMsgParams: ChatCompletionToolMessageParam;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
@@ -344,59 +345,87 @@ export const runToolWithToolChoice = async (
|
|||||||
return Promise.reject(getEmptyResponseTip());
|
return Promise.reject(getEmptyResponseTip());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the selected tool by LLM.
|
/* Run the selected tool by LLM.
|
||||||
const toolsRunResponse = (
|
Since only reference parameters are passed, if the same tool is run in parallel, it will get the same run parameters
|
||||||
await Promise.all(
|
*/
|
||||||
toolCalls.map(async (tool) => {
|
const toolsRunResponse: ToolRunResponseType = [];
|
||||||
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
for await (const tool of toolCalls) {
|
||||||
|
try {
|
||||||
|
const toolNode = toolNodes.find((item) => item.nodeId === tool.function?.name);
|
||||||
|
|
||||||
if (!toolNode) return;
|
if (!toolNode) continue;
|
||||||
|
|
||||||
const startParams = (() => {
|
const startParams = (() => {
|
||||||
try {
|
try {
|
||||||
return json5.parse(tool.function.arguments);
|
return json5.parse(tool.function.arguments);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
||||||
|
const toolRunResponse = await dispatchWorkFlow({
|
||||||
|
...workflowProps,
|
||||||
|
isToolCall: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
||||||
|
|
||||||
|
const toolMsgParams: ChatCompletionToolMessageParam = {
|
||||||
|
tool_call_id: tool.id,
|
||||||
|
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
||||||
|
name: tool.function.name,
|
||||||
|
content: stringToolResponse
|
||||||
|
};
|
||||||
|
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.toolResponse,
|
||||||
|
data: {
|
||||||
|
tool: {
|
||||||
|
id: tool.id,
|
||||||
|
toolName: '',
|
||||||
|
toolAvatar: '',
|
||||||
|
params: '',
|
||||||
|
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
||||||
}
|
}
|
||||||
})();
|
}
|
||||||
|
});
|
||||||
|
|
||||||
initToolNodes(runtimeNodes, [toolNode.nodeId], startParams);
|
toolsRunResponse.push({
|
||||||
const toolRunResponse = await dispatchWorkFlow({
|
toolRunResponse,
|
||||||
...workflowProps,
|
toolMsgParams
|
||||||
isToolCall: true
|
});
|
||||||
});
|
} catch (error) {
|
||||||
|
const err = getErrText(error);
|
||||||
|
workflowStreamResponse?.({
|
||||||
|
event: SseResponseEventEnum.toolResponse,
|
||||||
|
data: {
|
||||||
|
tool: {
|
||||||
|
id: tool.id,
|
||||||
|
toolName: '',
|
||||||
|
toolAvatar: '',
|
||||||
|
params: '',
|
||||||
|
response: sliceStrStartEnd(err, 5000, 5000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const stringToolResponse = formatToolResponse(toolRunResponse.toolResponses);
|
toolsRunResponse.push({
|
||||||
|
toolRunResponse: undefined,
|
||||||
const toolMsgParams: ChatCompletionToolMessageParam = {
|
toolMsgParams: {
|
||||||
tool_call_id: tool.id,
|
tool_call_id: tool.id,
|
||||||
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
role: ChatCompletionRequestMessageRoleEnum.Tool,
|
||||||
name: tool.function.name,
|
name: tool.function.name,
|
||||||
content: stringToolResponse
|
content: sliceStrStartEnd(err, 5000, 5000)
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
workflowStreamResponse?.({
|
const flatToolsResponseData = toolsRunResponse
|
||||||
event: SseResponseEventEnum.toolResponse,
|
.map((item) => item.toolRunResponse)
|
||||||
data: {
|
.flat()
|
||||||
tool: {
|
.filter(Boolean) as DispatchFlowResponse[];
|
||||||
id: tool.id,
|
|
||||||
toolName: '',
|
|
||||||
toolAvatar: '',
|
|
||||||
params: '',
|
|
||||||
response: sliceStrStartEnd(stringToolResponse, 5000, 5000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
toolRunResponse,
|
|
||||||
toolMsgParams
|
|
||||||
};
|
|
||||||
})
|
|
||||||
)
|
|
||||||
).filter(Boolean) as ToolRunResponseType;
|
|
||||||
|
|
||||||
const flatToolsResponseData = toolsRunResponse.map((item) => item.toolRunResponse).flat();
|
|
||||||
// concat tool responses
|
// concat tool responses
|
||||||
const dispatchFlowResponse = response
|
const dispatchFlowResponse = response
|
||||||
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
? response.dispatchFlowResponse.concat(flatToolsResponseData)
|
||||||
@@ -434,22 +463,22 @@ export const runToolWithToolChoice = async (
|
|||||||
const outputTokens = await countGptMessagesTokens(assistantToolMsgParams);
|
const outputTokens = await countGptMessagesTokens(assistantToolMsgParams);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
...
|
...
|
||||||
user
|
user
|
||||||
assistant: tool data
|
assistant: tool data
|
||||||
tool: tool response
|
tool: tool response
|
||||||
*/
|
*/
|
||||||
const completeMessages = [
|
const completeMessages = [
|
||||||
...concatToolMessages,
|
...concatToolMessages,
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get tool node assistant response
|
Get tool node assistant response
|
||||||
history assistant
|
history assistant
|
||||||
current tool assistant
|
current tool assistant
|
||||||
tool child assistant
|
tool child assistant
|
||||||
*/
|
*/
|
||||||
const toolNodeAssistant = GPTMessages2Chats([
|
const toolNodeAssistant = GPTMessages2Chats([
|
||||||
...assistantToolMsgParams,
|
...assistantToolMsgParams,
|
||||||
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
...toolsRunResponse.map((item) => item?.toolMsgParams)
|
||||||
@@ -478,12 +507,12 @@ export const runToolWithToolChoice = async (
|
|||||||
);
|
);
|
||||||
// Check interactive response(Only 1 interaction is reserved)
|
// Check interactive response(Only 1 interaction is reserved)
|
||||||
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
const workflowInteractiveResponseItem = toolsRunResponse.find(
|
||||||
(item) => item.toolRunResponse.workflowInteractiveResponse
|
(item) => item.toolRunResponse?.workflowInteractiveResponse
|
||||||
);
|
);
|
||||||
if (hasStopSignal || workflowInteractiveResponseItem) {
|
if (hasStopSignal || workflowInteractiveResponseItem) {
|
||||||
// Get interactive tool data
|
// Get interactive tool data
|
||||||
const workflowInteractiveResponse =
|
const workflowInteractiveResponse =
|
||||||
workflowInteractiveResponseItem?.toolRunResponse.workflowInteractiveResponse;
|
workflowInteractiveResponseItem?.toolRunResponse?.workflowInteractiveResponse;
|
||||||
|
|
||||||
// Flashback traverses completeMessages, intercepting messages that know the first user
|
// Flashback traverses completeMessages, intercepting messages that know the first user
|
||||||
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
const firstUserIndex = completeMessages.findLastIndex((item) => item.role === 'user');
|
||||||
|
@@ -72,6 +72,7 @@ import { dispatchLoopEnd } from './loop/runLoopEnd';
|
|||||||
import { dispatchLoopStart } from './loop/runLoopStart';
|
import { dispatchLoopStart } from './loop/runLoopStart';
|
||||||
import { dispatchFormInput } from './interactive/formInput';
|
import { dispatchFormInput } from './interactive/formInput';
|
||||||
import { dispatchToolParams } from './agent/runTool/toolParams';
|
import { dispatchToolParams } from './agent/runTool/toolParams';
|
||||||
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
|
|
||||||
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
const callbackMap: Record<FlowNodeTypeEnum, Function> = {
|
||||||
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
[FlowNodeTypeEnum.workflowStart]: dispatchWorkflowStart,
|
||||||
@@ -231,9 +232,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
|
|
||||||
if (toolResponses !== undefined) {
|
if (toolResponses !== undefined) {
|
||||||
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
if (Array.isArray(toolResponses) && toolResponses.length === 0) return;
|
||||||
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) {
|
if (typeof toolResponses === 'object' && Object.keys(toolResponses).length === 0) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
toolRunResponse = toolResponses;
|
toolRunResponse = toolResponses;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -565,6 +564,8 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
|
const targetEdges = runtimeEdges.filter((item) => item.source === node.nodeId);
|
||||||
const skipHandleIds = targetEdges.map((item) => item.sourceHandle);
|
const skipHandleIds = targetEdges.map((item) => item.sourceHandle);
|
||||||
|
|
||||||
|
toolRunResponse = getErrText(error);
|
||||||
|
|
||||||
// Skip all edges and return error
|
// Skip all edges and return error
|
||||||
return {
|
return {
|
||||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||||
|
Reference in New Issue
Block a user