Skip to content

Commit 4b5bb0a

Browse files
committed
fix: occur error when return multiple tool in once stream
1 parent b71043d commit 4b5bb0a

File tree

5 files changed

+52
-29
lines changed

5 files changed

+52
-29
lines changed

src/core/assistant-message/AssistantMessageParser.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ export class AssistantMessageParser {
9999
const currentToolValue = this.accumulator.slice(this.currentToolUseStartIndex)
100100
const toolUseClosingTag = `</${this.currentToolUse.name}>`
101101
if (currentToolValue.endsWith(toolUseClosingTag)) {
102+
if (toolCallParam?.anthropicContent && this.currentToolUse) {
103+
this.currentToolUse.toolUseParam = toolCallParam.anthropicContent
104+
}
102105
// End of a tool use.
103106
this.currentToolUse.partial = false
104107

src/core/assistant-message/parseAssistantMessage.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ export function parseAssistantMessage(
4848
const currentToolValue = accumulator.slice(currentToolUseStartIndex)
4949
const toolUseClosingTag = `</${currentToolUse.name}>`
5050
if (currentToolValue.endsWith(toolUseClosingTag)) {
51+
if (toolCallParam?.anthropicContent && currentToolUse) {
52+
currentToolUse.toolUseParam = toolCallParam.anthropicContent
53+
}
5154
// End of a tool use.
5255
currentToolUse.partial = false
5356
contentBlocks.push(currentToolUse)

src/core/assistant-message/presentAssistantMessage.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { ToolName, ClineAsk, ToolProgressStatus } from "@roo-code/types"
55
import { TelemetryService } from "@roo-code/telemetry"
66

77
import { defaultModeSlug, getModeBySlug } from "../../shared/modes"
8-
import type { ToolParamName, ToolResponse } from "../../shared/tools"
8+
import type { ToolParamName, ToolResponse, ToolUse } from "../../shared/tools"
99

1010
import { fetchInstructionsTool } from "../tools/fetchInstructionsTool"
1111
import { listFilesTool } from "../tools/listFilesTool"
@@ -245,11 +245,20 @@ export async function presentAssistantMessage(cline: Task) {
245245
}
246246

247247
if (cline.didAlreadyUseTool) {
248-
// Ignore any content after a tool has already been used.
249-
cline.userMessageContent.push({
248+
const rejectMessage: Anthropic.TextBlockParam = {
250249
type: "text",
251250
text: `Tool [${block.name}] was not executed because a tool has already been used in this message. Only one tool may be used per message. You must assess the first tool's result before proceeding to use the next tool.`,
252-
})
251+
}
252+
if (!block.toolUseId) {
253+
// Ignore any content after a tool has already been used.
254+
cline.userMessageContent.push(rejectMessage)
255+
} else {
256+
cline.userMessageContent.push({
257+
type: "tool_result",
258+
tool_use_id: block.toolUseId,
259+
content: [rejectMessage],
260+
})
261+
}
253262

254263
break
255264
}
@@ -266,9 +275,11 @@ export async function presentAssistantMessage(cline: Task) {
266275

267276
let hasSet = false
268277
if (toolCallEnabled) {
269-
const lastToolUseMessage = cline.assistantMessageContent.find((msg) => msg.type === "tool_use")
270-
if (lastToolUseMessage && lastToolUseMessage.toolUseId) {
271-
const toolUseId = lastToolUseMessage.toolUseId
278+
const lastToolUseMessage = cline.assistantMessageContent.find(
279+
(msg) => msg.type === "tool_use" && block.toolUseId && msg.toolUseId === block.toolUseId,
280+
) as ToolUse
281+
if (lastToolUseMessage) {
282+
const toolUseId = block.toolUseId!
272283
let toolResultMessage = cline.userMessageContent.find(
273284
(msg) => msg.type === "tool_result" && msg.tool_use_id === toolUseId,
274285
)

src/core/task/Task.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,27 +2125,34 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
21252125
content: [{ type: "text", text: assistantMessage }],
21262126
})
21272127
} else {
2128+
let firstTool = true
2129+
let addToolIds: Set<string> = new Set()
2130+
const newAssistantMessageContent: Array<Anthropic.ContentBlockParam> = []
21282131
for (const block of this.assistantMessageContent) {
21292132
if (block.type === "text" && block.content) {
2130-
await this.addToApiConversationHistory({
2131-
role: "assistant",
2132-
content: [{ type: "text", text: block.content }],
2133-
})
2133+
newAssistantMessageContent.push({ type: "text", text: block.content })
21342134
}
21352135
if (block.type === "tool_use" && block.toolUseId && block.toolUseParam) {
2136-
await this.addToApiConversationHistory({
2137-
role: "assistant",
2138-
content: [
2139-
{
2140-
type: "tool_use",
2141-
id: block.toolUseId,
2142-
name: block.name,
2143-
input: block.toolUseParam.input,
2144-
},
2145-
],
2136+
// ignore same tool id
2137+
if (addToolIds.has(block.toolUseId)) {
2138+
continue
2139+
}
2140+
newAssistantMessageContent.push({
2141+
type: "tool_use",
2142+
id: block.toolUseId,
2143+
name: block.name,
2144+
input: block.toolUseParam.input,
21462145
})
2146+
addToolIds.add(block.toolUseId)
2147+
firstTool = false
21472148
}
21482149
}
2150+
if (newAssistantMessageContent.length > 0) {
2151+
await this.addToApiConversationHistory({
2152+
role: "assistant",
2153+
content: newAssistantMessageContent,
2154+
})
2155+
}
21492156
}
21502157

21512158
TelemetryService.instance.captureConversationMessage(this.taskId, "assistant")

src/core/task/tool-call-helper.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ export class StreamingToolCallProcessor {
103103
*/
104104
private processChunkOpenAIFormat(chunk: any): ToolCallParam {
105105
let xmlOutput = ""
106-
106+
let index = 0
107107
for (const delta of chunk) {
108-
const index = delta.index || 0
108+
index = delta.index || 0
109109

110110
// Initialize state for a new tool call.
111111
if (!this.accumulatedToolCalls[index]) {
@@ -152,20 +152,19 @@ export class StreamingToolCallProcessor {
152152
}
153153
}
154154
// the index of GPT-5 tool_call not start by 0
155-
const firstIndex = this.accumulatedToolCalls.findIndex((call) => call?.id)
156-
const firstToolCall = this.accumulatedToolCalls[firstIndex]
155+
const toolCall = this.accumulatedToolCalls[index]
157156
const result: ToolCallParam = {
158157
providerType: "openai",
159-
toolName: firstToolCall?.function?.name,
160-
toolUserId: firstToolCall.id || undefined,
158+
toolName: toolCall?.function?.name,
159+
toolUserId: toolCall.id || undefined,
161160
chunkContent: xmlOutput,
162161
originContent: this.accumulatedToolCalls,
163162
}
164163

165-
if (this.processingStates.get(firstIndex)?.functionClosed) {
164+
if (this.processingStates.get(index)?.functionClosed) {
166165
let input
167166
try {
168-
input = JSON.parse(firstToolCall.function.arguments)
167+
input = JSON.parse(toolCall.function.arguments)
169168
} catch (e) {
170169
input = ""
171170
}

0 commit comments

Comments
 (0)