|
|
@@ -3,6 +3,7 @@
|
|
|
import { convertToBedrockConverseMessages } from "../bedrock-converse-format"
|
|
|
import { Anthropic } from "@anthropic-ai/sdk"
|
|
|
import { ContentBlock, ToolResultContentBlock } from "@aws-sdk/client-bedrock-runtime"
|
|
|
+import { OPENAI_CALL_ID_MAX_LENGTH } from "../../../utils/tool-id"
|
|
|
|
|
|
describe("convertToBedrockConverseMessages", () => {
|
|
|
it("converts simple text messages correctly", () => {
|
|
|
@@ -341,4 +342,218 @@ describe("convertToBedrockConverseMessages", () => {
|
|
|
const textBlock = result[0].content[0] as ContentBlock
|
|
|
expect(textBlock).toEqual({ text: "Hello world" })
|
|
|
})
|
|
|
+
|
|
|
+ describe("toolUseId sanitization for Bedrock 64-char limit", () => {
|
|
|
+ it("truncates toolUseId longer than 64 characters in tool_use blocks", () => {
|
|
|
+ const longId = "a".repeat(100)
|
|
|
+ const messages: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "assistant",
|
|
|
+ content: [
|
|
|
+ {
|
|
|
+ type: "tool_use",
|
|
|
+ id: longId,
|
|
|
+ name: "read_file",
|
|
|
+ input: { path: "test.txt" },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const result = convertToBedrockConverseMessages(messages)
|
|
|
+ const toolBlock = result[0]?.content?.[0] as ContentBlock
|
|
|
+
|
|
|
+ if ("toolUse" in toolBlock && toolBlock.toolUse && toolBlock.toolUse.toolUseId) {
|
|
|
+ expect(toolBlock.toolUse.toolUseId.length).toBeLessThanOrEqual(OPENAI_CALL_ID_MAX_LENGTH)
|
|
|
+ expect(toolBlock.toolUse.toolUseId.length).toBe(OPENAI_CALL_ID_MAX_LENGTH)
|
|
|
+ expect(toolBlock.toolUse.toolUseId).toContain("_")
|
|
|
+ } else {
|
|
|
+ expect.fail("Expected tool use block not found")
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it("truncates toolUseId longer than 64 characters in tool_result blocks with string content", () => {
|
|
|
+ const longId = "b".repeat(100)
|
|
|
+ const messages: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "user",
|
|
|
+ content: [
|
|
|
+ {
|
|
|
+ type: "tool_result",
|
|
|
+ tool_use_id: longId,
|
|
|
+ content: "Result content",
|
|
|
+ } as any,
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const result = convertToBedrockConverseMessages(messages)
|
|
|
+ const resultBlock = result[0]?.content?.[0] as ContentBlock
|
|
|
+
|
|
|
+ if ("toolResult" in resultBlock && resultBlock.toolResult && resultBlock.toolResult.toolUseId) {
|
|
|
+ expect(resultBlock.toolResult.toolUseId.length).toBeLessThanOrEqual(OPENAI_CALL_ID_MAX_LENGTH)
|
|
|
+ expect(resultBlock.toolResult.toolUseId.length).toBe(OPENAI_CALL_ID_MAX_LENGTH)
|
|
|
+ expect(resultBlock.toolResult.toolUseId).toContain("_")
|
|
|
+ } else {
|
|
|
+ expect.fail("Expected tool result block not found")
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it("truncates toolUseId longer than 64 characters in tool_result blocks with array content", () => {
|
|
|
+ const longId = "c".repeat(100)
|
|
|
+ const messages: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "user",
|
|
|
+ content: [
|
|
|
+ {
|
|
|
+ type: "tool_result",
|
|
|
+ tool_use_id: longId,
|
|
|
+ content: [{ type: "text", text: "Result content" }],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const result = convertToBedrockConverseMessages(messages)
|
|
|
+ const resultBlock = result[0]?.content?.[0] as ContentBlock
|
|
|
+
|
|
|
+ if ("toolResult" in resultBlock && resultBlock.toolResult && resultBlock.toolResult.toolUseId) {
|
|
|
+ expect(resultBlock.toolResult.toolUseId.length).toBeLessThanOrEqual(OPENAI_CALL_ID_MAX_LENGTH)
|
|
|
+ expect(resultBlock.toolResult.toolUseId.length).toBe(OPENAI_CALL_ID_MAX_LENGTH)
|
|
|
+ } else {
|
|
|
+ expect.fail("Expected tool result block not found")
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it("keeps toolUseId unchanged when under 64 characters", () => {
|
|
|
+ const shortId = "short-id-123"
|
|
|
+ const messages: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "assistant",
|
|
|
+ content: [
|
|
|
+ {
|
|
|
+ type: "tool_use",
|
|
|
+ id: shortId,
|
|
|
+ name: "read_file",
|
|
|
+ input: { path: "test.txt" },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const result = convertToBedrockConverseMessages(messages)
|
|
|
+ const toolBlock = result[0]?.content?.[0] as ContentBlock
|
|
|
+
|
|
|
+ if ("toolUse" in toolBlock && toolBlock.toolUse) {
|
|
|
+ expect(toolBlock.toolUse.toolUseId).toBe(shortId)
|
|
|
+ } else {
|
|
|
+ expect.fail("Expected tool use block not found")
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it("produces consistent truncated IDs for the same input", () => {
|
|
|
+ const longId = "d".repeat(100)
|
|
|
+ const messages: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "assistant",
|
|
|
+ content: [
|
|
|
+ {
|
|
|
+ type: "tool_use",
|
|
|
+ id: longId,
|
|
|
+ name: "read_file",
|
|
|
+ input: { path: "test.txt" },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const result1 = convertToBedrockConverseMessages(messages)
|
|
|
+ const result2 = convertToBedrockConverseMessages(messages)
|
|
|
+
|
|
|
+ const toolBlock1 = result1[0]?.content?.[0] as ContentBlock
|
|
|
+ const toolBlock2 = result2[0]?.content?.[0] as ContentBlock
|
|
|
+
|
|
|
+ if ("toolUse" in toolBlock1 && toolBlock1.toolUse && "toolUse" in toolBlock2 && toolBlock2.toolUse) {
|
|
|
+ expect(toolBlock1.toolUse.toolUseId).toBe(toolBlock2.toolUse.toolUseId)
|
|
|
+ } else {
|
|
|
+ expect.fail("Expected tool use blocks not found")
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it("produces different truncated IDs for different long inputs", () => {
|
|
|
+ const longId1 = "e".repeat(100)
|
|
|
+ const longId2 = "f".repeat(100)
|
|
|
+
|
|
|
+ const messages1: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "assistant",
|
|
|
+ content: [{ type: "tool_use", id: longId1, name: "read_file", input: {} }],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ const messages2: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "assistant",
|
|
|
+ content: [{ type: "tool_use", id: longId2, name: "read_file", input: {} }],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const result1 = convertToBedrockConverseMessages(messages1)
|
|
|
+ const result2 = convertToBedrockConverseMessages(messages2)
|
|
|
+
|
|
|
+ const toolBlock1 = result1[0]?.content?.[0] as ContentBlock
|
|
|
+ const toolBlock2 = result2[0]?.content?.[0] as ContentBlock
|
|
|
+
|
|
|
+ if ("toolUse" in toolBlock1 && toolBlock1.toolUse && "toolUse" in toolBlock2 && toolBlock2.toolUse) {
|
|
|
+ expect(toolBlock1.toolUse.toolUseId).not.toBe(toolBlock2.toolUse.toolUseId)
|
|
|
+ } else {
|
|
|
+ expect.fail("Expected tool use blocks not found")
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it("matching tool_use and tool_result IDs are both truncated consistently", () => {
|
|
|
+ const longId = "g".repeat(100)
|
|
|
+ const messages: Anthropic.Messages.MessageParam[] = [
|
|
|
+ {
|
|
|
+ role: "assistant",
|
|
|
+ content: [
|
|
|
+ {
|
|
|
+ type: "tool_use",
|
|
|
+ id: longId,
|
|
|
+ name: "read_file",
|
|
|
+ input: { path: "test.txt" },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ role: "user",
|
|
|
+ content: [
|
|
|
+ {
|
|
|
+ type: "tool_result",
|
|
|
+ tool_use_id: longId,
|
|
|
+ content: "File contents",
|
|
|
+ } as any,
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ const result = convertToBedrockConverseMessages(messages)
|
|
|
+
|
|
|
+ const toolUseBlock = result[0]?.content?.[0] as ContentBlock
|
|
|
+ const toolResultBlock = result[1]?.content?.[0] as ContentBlock
|
|
|
+
|
|
|
+ if (
|
|
|
+ "toolUse" in toolUseBlock &&
|
|
|
+ toolUseBlock.toolUse &&
|
|
|
+ toolUseBlock.toolUse.toolUseId &&
|
|
|
+ "toolResult" in toolResultBlock &&
|
|
|
+ toolResultBlock.toolResult &&
|
|
|
+ toolResultBlock.toolResult.toolUseId
|
|
|
+ ) {
|
|
|
+ expect(toolUseBlock.toolUse.toolUseId).toBe(toolResultBlock.toolResult.toolUseId)
|
|
|
+ expect(toolUseBlock.toolUse.toolUseId.length).toBeLessThanOrEqual(OPENAI_CALL_ID_MAX_LENGTH)
|
|
|
+ } else {
|
|
|
+ expect.fail("Expected tool use and result blocks not found")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
})
|