Skip to content

Commit bebf26a

Browse files
author
Zoo (VP)
committed
fix(anthropic): keep all tool_result blocks ahead of other user-message content
1 parent e58142f commit bebf26a

3 files changed

Lines changed: 26 additions & 23 deletions

File tree

‎src/core/assistant-message/__tests__/presentAssistantMessage-images.spec.ts‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,14 @@ describe("presentAssistantMessage - Image Handling in Native Tool Calling", () =
7474
return false
7575
}
7676
mockTask.userMessageContent.push(toolResult)
77-
const images = mockTask.userMessageContent.filter((block: UserContentBlock) => block.type === "image")
78-
if (images.length > 0) {
79-
const withoutImages = mockTask.userMessageContent.filter(
80-
(block: UserContentBlock) => block.type !== "image",
77+
const results = mockTask.userMessageContent.filter(
78+
(block: UserContentBlock) => block.type === "tool_result",
79+
)
80+
if (results.length !== mockTask.userMessageContent.length) {
81+
const others = mockTask.userMessageContent.filter(
82+
(block: UserContentBlock) => block.type !== "tool_result",
8183
)
82-
withoutImages.push(...images)
83-
mockTask.userMessageContent.splice(0, mockTask.userMessageContent.length, ...withoutImages)
84+
mockTask.userMessageContent.splice(0, mockTask.userMessageContent.length, ...results, ...others)
8485
}
8586
return true
8687
})

‎src/core/task/Task.ts‎

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -469,11 +469,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
469469
* Push a tool_result block to userMessageContent, preventing duplicates.
470470
* Duplicate tool_use_ids cause API errors.
471471
*
472-
* Keeps all tool_result blocks contiguous, as the Anthropic API requires:
473-
* image blocks attached to earlier tool results must not sit between two
474-
* tool_results ("tool_use ids were found without tool_result blocks
475-
* immediately after"), so any images preceding the new result are moved
476-
* after it.
472+
* Keeps all tool_result blocks first and contiguous, as the Anthropic API
473+
* requires: a user message responding to tool_use blocks must begin with
474+
* the tool_result blocks, and any other content (images, text) between or
475+
* ahead of them is rejected ("tool_use ids were found without tool_result
476+
* blocks immediately after"). All tool_result blocks are therefore kept
477+
* ahead of other content in this array.
477478
*
478479
* @param toolResult - The tool_result block to add
479480
* @returns true if added, false if duplicate was skipped
@@ -491,15 +492,15 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
491492
}
492493
this.userMessageContent.push(toolResult)
493494

494-
// The new result is always appended at the end, so every image block
495-
// currently in the array precedes it and would split the tool_result
496-
// run once another result arrives. Move them after the new result.
497-
const images = this.userMessageContent.filter(
498-
(block): block is Anthropic.ImageBlockParam => block.type === "image",
495+
// Partition into [tool_results, other blocks], preserving order within
496+
// each group, so results stay first and contiguous regardless of what
497+
// other content (images, text) arrived between pushes.
498+
const results = this.userMessageContent.filter(
499+
(block): block is Anthropic.ToolResultBlockParam => block.type === "tool_result",
499500
)
500-
if (images.length > 0) {
501-
const withoutImages = this.userMessageContent.filter((block) => block.type !== "image")
502-
this.userMessageContent.splice(0, this.userMessageContent.length, ...withoutImages, ...images)
501+
if (results.length !== this.userMessageContent.length) {
502+
const others = this.userMessageContent.filter((block) => block.type !== "tool_result")
503+
this.userMessageContent.splice(0, this.userMessageContent.length, ...results, ...others)
503504
}
504505

505506
return true

‎src/core/task/__tests__/Task.spec.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6295,10 +6295,11 @@ describe("pushToolResultToUserContent", () => {
62956295

62966296
expect(added).toBe(true)
62976297
expect(task.userMessageContent).toHaveLength(3)
6298-
// The image must not sit between two tool_results, so it moves after
6299-
// the new result; other blocks keep their relative order.
6300-
expect(task.userMessageContent[0].type).toBe("text")
6301-
expect(task.userMessageContent[1]).toEqual(toolResult)
6298+
// Results must lead the message (Anthropic rejects other content ahead
6299+
// of or between tool_results), so the pre-existing text and image move
6300+
// after the result; blocks keep their relative order within each group.
6301+
expect(task.userMessageContent[0]).toEqual(toolResult)
6302+
expect(task.userMessageContent[1].type).toBe("text")
63026303
expect(task.userMessageContent[2]).toEqual(image)
63036304
})
63046305

0 commit comments

Comments
 (0)