From eb575d5394ed90bcb230b871907907ed709aff55 Mon Sep 17 00:00:00 2001 From: bracesproul Date: Mon, 29 Jul 2024 16:43:03 -0700 Subject: [PATCH] community[patch]: Fix handling parallel tool call results in bedrock --- .../tests/chatbedrock.standard.int.test.ts | 8 ++++ .../src/utils/bedrock/anthropic.ts | 38 +++++++++++++------ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/libs/langchain-community/src/chat_models/tests/chatbedrock.standard.int.test.ts b/libs/langchain-community/src/chat_models/tests/chatbedrock.standard.int.test.ts index 0fc9e92e7ec8..4edd4a1ebdcc 100644 --- a/libs/langchain-community/src/chat_models/tests/chatbedrock.standard.int.test.ts +++ b/libs/langchain-community/src/chat_models/tests/chatbedrock.standard.int.test.ts @@ -15,6 +15,7 @@ class BedrockChatStandardIntegrationTests extends ChatModelIntegrationTests< Cls: BedrockChat, chatModelHasToolCalling: true, chatModelHasStructuredOutput: true, + supportsParallelToolCalls: true, constructorArgs: { region, model: "anthropic.claude-3-sonnet-20240229-v1:0", @@ -57,6 +58,13 @@ class BedrockChatStandardIntegrationTests extends ChatModelIntegrationTests< "Flaky test with Bedrock not consistently returning tool calls. TODO: Fix prompting." ); } + + async testParallelToolCalling() { + // Anthropic is very flaky when calling multiple tools in parallel. + // Because of this, we pass `true` as the second arg to only verify + // it can handle parallel tools in the message history. + await super.testParallelToolCalling(undefined, true); + } } const testClass = new BedrockChatStandardIntegrationTests(); diff --git a/libs/langchain-community/src/utils/bedrock/anthropic.ts b/libs/langchain-community/src/utils/bedrock/anthropic.ts index 33a7d0b48fb1..4565a2f1615d 100644 --- a/libs/langchain-community/src/utils/bedrock/anthropic.ts +++ b/libs/langchain-community/src/utils/bedrock/anthropic.ts @@ -55,17 +55,33 @@ function _mergeMessages( for (const message of messages) { if (message._getType() === "tool") { if (typeof message.content === "string") { - merged.push( - new HumanMessage({ - content: [ - { - type: "tool_result", - content: message.content, - tool_use_id: (message as ToolMessage).tool_call_id, - }, - ], - }) - ); + const previousMessage = merged[merged.length - 1]; + if ( + previousMessage?._getType() === "human" && + Array.isArray(previousMessage.content) && + "type" in previousMessage.content[0] && + previousMessage.content[0].type === "tool_result" + ) { + // If the previous message was a tool result, we merge this tool message into it. + previousMessage.content.push({ + type: "tool_result", + content: message.content, + tool_use_id: (message as ToolMessage).tool_call_id, + }); + } else { + // If not, we create a new human message with the tool result. + merged.push( + new HumanMessage({ + content: [ + { + type: "tool_result", + content: message.content, + tool_use_id: (message as ToolMessage).tool_call_id, + }, + ], + }) + ); + } } else { merged.push(new HumanMessage({ content: message.content })); }