mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-17 23:51:08 +00:00
Merge b92cd76e6d into c4547d25c5
This commit is contained in:
commit
b6e145098c
2 changed files with 133 additions and 4 deletions
|
|
@ -166,16 +166,27 @@ export class NativeToolCallParser {
|
|||
/**
|
||||
* Process stream finish reason.
|
||||
* Emits end events when finish_reason is 'tool_calls'.
|
||||
* Only emits events for tool calls that have been properly started (have a name).
|
||||
*/
|
||||
public static processFinishReason(finishReason: string | null | undefined): ToolCallStreamEvent[] {
|
||||
const events: ToolCallStreamEvent[] = []
|
||||
|
||||
if (finishReason === "tool_calls" && this.rawChunkTracker.size > 0) {
|
||||
for (const [, tracked] of this.rawChunkTracker.entries()) {
|
||||
events.push({
|
||||
type: "tool_call_end",
|
||||
id: tracked.id,
|
||||
})
|
||||
// Only emit end event if the tool call was properly started
|
||||
// (i.e., we received a name and emitted tool_call_start)
|
||||
if (tracked.hasStarted) {
|
||||
events.push({
|
||||
type: "tool_call_end",
|
||||
id: tracked.id,
|
||||
})
|
||||
} else {
|
||||
// Log diagnostic warning for unstarted tool calls
|
||||
console.warn(
|
||||
`[NativeToolCallParser] Skipping tool_call_end for unstarted tool call: ${tracked.id} ` +
|
||||
`(no name received, hasStarted=false)`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -343,4 +343,122 @@ describe("NativeToolCallParser", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("processFinishReason", () => {
|
||||
describe("hasStarted check", () => {
|
||||
it("should emit tool_call_end for started tool calls", () => {
|
||||
// Simulate a tool call that has been properly started
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 0,
|
||||
id: "call_started",
|
||||
name: "read_file",
|
||||
arguments: '{"path":"test.ts"}',
|
||||
})
|
||||
|
||||
const events = NativeToolCallParser.processFinishReason("tool_calls")
|
||||
|
||||
expect(events).toHaveLength(1)
|
||||
expect(events[0]).toEqual({
|
||||
type: "tool_call_end",
|
||||
id: "call_started",
|
||||
})
|
||||
})
|
||||
|
||||
it("should NOT emit tool_call_end for unstarted tool calls", () => {
|
||||
// Simulate a tool call that received an ID but no name (not started)
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 0,
|
||||
id: "call_unstarted",
|
||||
// No name provided - tool call is tracked but not started
|
||||
})
|
||||
|
||||
const events = NativeToolCallParser.processFinishReason("tool_calls")
|
||||
|
||||
// Should not emit any events because the tool call wasn't started
|
||||
expect(events).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should handle mixed started and unstarted tool calls", () => {
|
||||
// First tool call: properly started
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 0,
|
||||
id: "call_started_1",
|
||||
name: "read_file",
|
||||
arguments: '{"path":"test1.ts"}',
|
||||
})
|
||||
|
||||
// Second tool call: tracked but not started (no name)
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 1,
|
||||
id: "call_unstarted",
|
||||
// No name - won't be started
|
||||
})
|
||||
|
||||
// Third tool call: properly started
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 2,
|
||||
id: "call_started_2",
|
||||
name: "write_to_file",
|
||||
arguments: '{"path":"output.ts"}',
|
||||
})
|
||||
|
||||
const events = NativeToolCallParser.processFinishReason("tool_calls")
|
||||
|
||||
// Should only emit end events for the two started tool calls
|
||||
expect(events).toHaveLength(2)
|
||||
expect(events[0]).toEqual({
|
||||
type: "tool_call_end",
|
||||
id: "call_started_1",
|
||||
})
|
||||
expect(events[1]).toEqual({
|
||||
type: "tool_call_end",
|
||||
id: "call_started_2",
|
||||
})
|
||||
})
|
||||
|
||||
it("should not emit events when finish_reason is not tool_calls", () => {
|
||||
// Set up a started tool call
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 0,
|
||||
id: "call_started",
|
||||
name: "read_file",
|
||||
arguments: '{"path":"test.ts"}',
|
||||
})
|
||||
|
||||
// Process with different finish reason
|
||||
const events = NativeToolCallParser.processFinishReason("stop")
|
||||
|
||||
expect(events).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("should handle tool call that receives name in a separate chunk", () => {
|
||||
// First chunk: ID only
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 0,
|
||||
id: "call_delayed_name",
|
||||
})
|
||||
|
||||
// At this point, tool call is tracked but not started
|
||||
let events = NativeToolCallParser.processFinishReason("tool_calls")
|
||||
expect(events).toHaveLength(0)
|
||||
|
||||
// Clear state and try again with name
|
||||
NativeToolCallParser.clearRawChunkState()
|
||||
|
||||
// Simulate proper sequence with name
|
||||
NativeToolCallParser.processRawChunk({
|
||||
index: 0,
|
||||
id: "call_delayed_name",
|
||||
name: "read_file",
|
||||
})
|
||||
|
||||
events = NativeToolCallParser.processFinishReason("tool_calls")
|
||||
expect(events).toHaveLength(1)
|
||||
expect(events[0]).toEqual({
|
||||
type: "tool_call_end",
|
||||
id: "call_delayed_name",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue