langchain-ai / langchainjs

πŸ¦œπŸ”— Build context-aware reasoning applications πŸ¦œπŸ”—
https://js.langchain.com/docs/
MIT License
12.32k stars 2.08k forks source link

ChatBedrockConverse, ValidationException #6724

Open rossanodr opened 1 week ago

rossanodr commented 1 week ago

Checked other resources

Example Code

Basically its the same code you can find here but using ChatBedrockConverse: https://github.com/bracesproul/gen-ui/blob/main/ai/graph.tsx

llm = new ChatBedrockConverse({ model: "anthropic.claude-3-sonnet-20240229-v1:0", temperature: state.temperature, }).bindTools(tools);

Error Message and Stack Trace (if applicable)

No response

Description

I'm experiencing the following issue while using ChatBedrockConverse: "ValidationException: A conversation must alternate between user and assistant roles. Make sure the conversation alternates between user and assistant roles and try again."

The issue occurs when, for example, a tool's response is followed by an AI message, or when there are two consecutive user messages.

Could you check if there's a specific setting or configuration to handle this issue? I tried something like the following, but it didn't work:

const convertChatHistoryToMessages = (
    chat_history: [role: string, content: string][]
) => {
    let previousRole: any = null;

    const a = chat_history.flatMap(([role, content]) => {
        const messages = [];

        if (previousRole === role) {
            if (role === "human" || role === "user") {
                messages.push(new AIMessage("..."));
            } else {
                messages.push(new HumanMessage("..."));
            }
        }

        // Adds the current message
        switch (role) {
            case "human":
            case "user":
                messages.push(new HumanMessage(content));
                break;
            case "assistant":
            case "ai":
                messages.push(new AIMessage(content));
                break;
            default:
                messages.push(new HumanMessage(content));
        }

        previousRole = role;

        return messages;
    });

    return a;
};

Is there any adjustment or alternative solution to resolve the issue when consecutive messages come from the same role?

System Info

langchain@npm:0.2.17 β”œβ”€ Instances: 1 β”œβ”€ Version: 0.2.17 β”‚ └─ Dependencies β”œβ”€ @langchain/core@npm:^0.2.18 β†’ npm:0.2.31 β”œβ”€ @langchain/openai@npm:>=0.1.0 <0.3.0 β†’ npm:0.2.5 β”œβ”€ @langchain/textsplitters@npm:~0.0.0 β†’ npm:0.0.3 β”œβ”€ binary-extensions@npm:^2.2.0 β†’ npm:2.3.0 β”œβ”€ js-tiktoken@npm:^1.0.12 β†’ npm:1.0.12 β”œβ”€ js-yaml@npm:^4.1.0 β†’ npm:4.1.0 β”œβ”€ jsonpointer@npm:^5.0.1 β†’ npm:5.0.1 β”œβ”€ langsmith@npm:~0.1.40 β†’ npm:0.1.48 β”œβ”€ openapi-types@npm:^12.1.3 β†’ npm:12.1.3 β”œβ”€ p-retry@npm:4 β†’ npm:4.6.2 β”œβ”€ uuid@npm:^10.0.0 β†’ npm:10.0.0 β”œβ”€ yaml@npm:^2.2.1 β†’ npm:2.4.5 β”œβ”€ zod-to-json-schema@npm:^3.22.3 β†’ npm:3.23.1 └─ zod@npm:^3.22.4 β†’ npm:3.23.8

dosubot[bot] commented 1 week ago

To resolve the "ValidationException: A conversation must alternate between user and assistant roles" issue when using ChatBedrockConverse, you need to ensure that the conversation messages alternate between user and assistant roles. The provided code already includes a mechanism to combine consecutive user tool result messages into a single message. This helps in maintaining the required alternation between user and assistant roles.

Here is the relevant part of the code that handles this:

const combinedConverseMessages = converseMessages.reduce<BedrockMessage[]>(
  (acc, curr) => {
    const lastMessage = acc[acc.length - 1];

    if (
      lastMessage &&
      lastMessage.role === "user" &&
      lastMessage.content?.some((c) => "toolResult" in c) &&
      curr.role === "user" &&
      curr.content?.some((c) => "toolResult" in c)
    ) {
      lastMessage.content = lastMessage.content.concat(curr.content);
    } else {
      acc.push(curr);
    }

    return acc;
  },
  []
);

return { converseMessages: combinedConverseMessages, converseSystem };

Additionally, ensure that the roles are correctly assigned when converting messages to the required format. The messageToGroqRole function and the convertMessagesToGroqParams function from the libs/langchain-groq/src/chat_models.ts file can help in correctly mapping the roles:

export function messageToGroqRole(message: BaseMessage): GroqRoleEnum {
  const type = message._getType();
  switch (type) {
    case "system":
      return "system";
    case "ai":
      return "assistant";
    case "human":
      return "user";
    case "function":
      return "function";
    case "tool":
      return "tool" as GroqRoleEnum;
    default:
      throw new Error(`Unknown message type: ${type}`);
  }
}

function convertMessagesToGroqParams(
  messages: BaseMessage[]
): Array<ChatCompletionsAPI.ChatCompletionMessage> {
  return messages.map((message): ChatCompletionsAPI.ChatCompletionMessage => {
    if (typeof message.content !== "string") {
      throw new Error("Non string message content not supported");
    }
    const completionParam: Record<string, any> = {
      role: messageToGroqRole(message),
      content: message.content,
      name: message.name,
      function_call: message.additional_kwargs.function_call,
      tool_calls: message.additional_kwargs.tool_calls,
      tool_call_id: (message as ToolMessage).tool_call_id,
    };
    if (isAIMessage(message) && !!message.tool_calls?.length) {
      completionParam.tool_calls = message.tool_calls.map(
        convertLangChainToolCallToOpenAI
      );
    } else {
      if (message.additional_kwargs.tool_calls != null) {
        completionParam.tool_calls = message.additional_kwargs.tool_calls;
      }
      if ((message as ToolMessage).tool_call_id != null) {
        completionParam.tool_call_id = (message as ToolMessage).tool_call_id;
      }
    }
    return completionParam as ChatCompletionsAPI.ChatCompletionMessage;
  });
}

This code ensures that the roles are correctly assigned and the messages are formatted properly, which helps in maintaining the required alternation between user and assistant roles [1][2][3].

To continue talking to Dosu, mention @dosu.

rossanodr commented 1 week ago

The problem does not occour using BedrockChat, but I can't use the tools with it

rossanodr commented 1 week ago

This fix the issue: The problem is that the you can't send the input + the last message with 'user' role. `const convertChatHistoryToMessages = ( chat_history: [role: string, content: string][] ) => { let previousRole: any = null;

const messages = chat_history.flatMap(([role, content], index) => {
    const msgArray = [];

    if (previousRole === role) {
        if (role === "human" || role === "user") {
            msgArray.push(new AIMessage("..."));
        } else {
            msgArray.push(new HumanMessage("..."));
        }
    }

    switch (role) {
        case "human":
        case "user":
            msgArray.push(new HumanMessage(content));
            break;
        case "assistant":
        case "ai":
            msgArray.push(new AIMessage(content));
            break;
        default:
            msgArray.push(new HumanMessage(content));
    }

    previousRole = role;
    return msgArray;
});

const lastMessage = chat_history[chat_history.length - 1];
if (
    lastMessage &&
    (lastMessage[0] === "user" || lastMessage[0] === "human")
) {
    messages.push(new AIMessage("..."));
}

return messages;

};`