Open thiagotps opened 3 months ago
this is part of the anthropic API — you're not allowed to have adjacent Tool and Human messages, you need an AI message in between them. Not sure why ChatBedrock isn't raising an error here, will look into it.
If you add an AIMessage after your last ToolMessage and before the human message does that resolve the issue?
this is part of the anthropic API — you're not allowed to have adjacent Tool and Human messages, you need an AI message in between them. Not sure why ChatBedrock isn't raising an error here, will look into it.
If you add an AIMessage after your last ToolMessage and before the human message does that resolve the issue?
Adding the AIMessage
after the last ToolMessage
and before the human message didn't help:
from langchain_core.prompts import ChatPromptTemplate
from langchain_aws import ChatBedrockConverse
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.messages import AIMessage, ToolMessage
def rag_query(query: str) -> str:
"""Do a RAG search in a internal knowledge base. """
return "No results found !!!"
def web_query(query: str) -> str:
"""Search the web for the query."""
return "Connection error"
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant with tools."),
("human", "Tell me who was Mozart ?"),
AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Mozart"}, "id": 1}]),
ToolMessage("You correctly called the tool", tool_call_id="1"),
("human", "Who was Bethoveen ?"),
AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Bethoveen"}, "id": 2}]),
ToolMessage("You correctly called the tool", tool_call_id="2"),
("human", "How to install linux ?"),
AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "Tutorial on how to install Linux"}, "id": 3}]),
ToolMessage("You correctly called the tool", tool_call_id="3"),
("human", "What is nmap"),
AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "nmap documentation"}, "id": 4}]),
ToolMessage("You correctly called the tool", tool_call_id="4"),
AIMessage("Everything seems ok."),
("human", "{question}")
])
bedrock = boto3.client('bedrock-runtime')
llm = ChatBedrockConverse(model_id="anthropic.claude-3-haiku-20240307-v1:0", client=bedrock)
chain = {"question": RunnablePassthrough()}| prompt | llm.bind_tools([rag_query, web_query])
chain.invoke("How to use docker ?")
ValidationException: An error occurred (ValidationException) when calling the Converse operation: messages.2.content: Conversation blocks and tool result blocks cannot be provided in the same turn.
this is part of the anthropic API — you're not allowed to have adjacent Tool and Human messages, you need an AI message in between them. Not sure why ChatBedrock isn't raising an error here, will look into it. If you add an AIMessage after your last ToolMessage and before the human message does that resolve the issue?
Adding the
AIMessage
after the lastToolMessage
and before the human message didn't help:from langchain_core.prompts import ChatPromptTemplate from langchain_aws import ChatBedrockConverse from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain_core.messages import AIMessage, ToolMessage def rag_query(query: str) -> str: """Do a RAG search in a internal knowledge base. """ return "No results found !!!" def web_query(query: str) -> str: """Search the web for the query.""" return "Connection error" prompt = ChatPromptTemplate.from_messages([ ("system", "You are a helpful assistant with tools."), ("human", "Tell me who was Mozart ?"), AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Mozart"}, "id": 1}]), ToolMessage("You correctly called the tool", tool_call_id="1"), ("human", "Who was Bethoveen ?"), AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Bethoveen"}, "id": 2}]), ToolMessage("You correctly called the tool", tool_call_id="2"), ("human", "How to install linux ?"), AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "Tutorial on how to install Linux"}, "id": 3}]), ToolMessage("You correctly called the tool", tool_call_id="3"), ("human", "What is nmap"), AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "nmap documentation"}, "id": 4}]), ToolMessage("You correctly called the tool", tool_call_id="4"), AIMessage("Everything seems ok."), ("human", "{question}") ]) bedrock = boto3.client('bedrock-runtime') llm = ChatBedrockConverse(model_id="anthropic.claude-3-haiku-20240307-v1:0", client=bedrock) chain = {"question": RunnablePassthrough()}| prompt | llm.bind_tools([rag_query, web_query]) chain.invoke("How to use docker ?")
ValidationException: An error occurred (ValidationException) when calling the Converse operation: messages.2.content: Conversation blocks and tool result blocks cannot be provided in the same turn.
ah sorry, didn't look closely. looks like you've got a number of adjacent tool/human messages. you'd actually need to do this for every tool message / human message adjacent pair.
@baskaryan I made a test with _merge_messages
from bedrock.py
:
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage, SystemMessage
from typing import Sequence, List, Union
def _merge_messages(
messages: Sequence[BaseMessage],
) -> List[Union[SystemMessage, AIMessage, HumanMessage]]:
"""Merge runs of human/tool messages into single human messages with content blocks.""" # noqa: E501
merged: list = []
for curr in messages:
curr = curr.copy(deep=True)
if isinstance(curr, ToolMessage):
if isinstance(curr.content, list) and all(
isinstance(block, dict) and block.get("type") == "tool_result"
for block in curr.content
):
curr = HumanMessage(curr.content) # type: ignore[misc]
else:
curr = HumanMessage( # type: ignore[misc]
[
{
"type": "tool_result",
"content": curr.content,
"tool_use_id": curr.tool_call_id,
}
]
)
last = merged[-1] if merged else None
if isinstance(last, HumanMessage) and isinstance(curr, HumanMessage):
if isinstance(last.content, str):
new_content: List = [{"type": "text", "text": last.content}]
else:
new_content = last.content
if isinstance(curr.content, str):
new_content.append({"type": "text", "text": curr.content})
else:
new_content.extend(curr.content)
last.content = new_content
else:
merged.append(curr)
return merged
messages = [
SystemMessage("You are a helpful assistant with tools."),
HumanMessage("Tell me who was Mozart ?"),
AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Mozart"}, "id": 1}]),
ToolMessage("You correctly called the tool", tool_call_id="1"),
HumanMessage("Who was Bethoveen ?"),
AIMessage("", tool_calls=[{"name": "rag_query", "args": {"query": "Who was Bethoveen"}, "id": 2}]),
ToolMessage("You correctly called the tool", tool_call_id="2"),
HumanMessage("How to install linux ?"),
AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "Tutorial on how to install Linux"}, "id": 3}]),
ToolMessage("You correctly called the tool", tool_call_id="3"),
HumanMessage("What is nmap"),
AIMessage("", tool_calls=[{"name": "web_query", "args": {"query": "nmap documentation"}, "id": 4}]),
ToolMessage("You correctly called the tool", tool_call_id="4"),
HumanMessage("{question}"),
]
_merge_messages(messages)
And the result was:
[SystemMessage(content='You are a helpful assistant with tools.'),
HumanMessage(content='Tell me who was Mozart ?'),
AIMessage(content='', tool_calls=[{'name': 'rag_query', 'args': {'query': 'Who was Mozart'}, 'id': '1', 'type': 'tool_call'}]),
HumanMessage(content=[{'type': 'tool_result', 'content': 'You correctly called the tool', 'tool_use_id': '1'}, {'type': 'text', 'text': 'Who was Bethoveen ?'}]),
AIMessage(content='', tool_calls=[{'name': 'rag_query', 'args': {'query': 'Who was Bethoveen'}, 'id': '2', 'type': 'tool_call'}]),
HumanMessage(content=[{'type': 'tool_result', 'content': 'You correctly called the tool', 'tool_use_id': '2'}, {'type': 'text', 'text': 'How to install linux ?'}]),
AIMessage(content='', tool_calls=[{'name': 'web_query', 'args': {'query': 'Tutorial on how to install Linux'}, 'id': '3', 'type': 'tool_call'}]),
HumanMessage(content=[{'type': 'tool_result', 'content': 'You correctly called the tool', 'tool_use_id': '3'}, {'type': 'text', 'text': 'What is nmap'}]),
AIMessage(content='', tool_calls=[{'name': 'web_query', 'args': {'query': 'nmap documentation'}, 'id': '4', 'type': 'tool_call'}]),
HumanMessage(content=[{'type': 'tool_result', 'content': 'You correctly called the tool', 'tool_use_id': '4'}, {'type': 'text', 'text': '{question}'}])]
All intermediaries human questions were removed by the _merge_messages
.
It's probably the reason why ChatBedrock
is not raising any error.
I am experiencing this too with the Converse API. It doesn't matter if you remove adjacent human and tool messages. For me as soon as I add memory i.e a list of messages. I start getting the error.
Everything only works if you are doing single-turn runs
Any updates over here?
With the
ChatBedrock
class, the following code works as expected:Result:
But when using the
ChatBedrockConverse
class:I receive the following exception:
@baskaryan