@@ -101,7 +101,7 @@ import { getMessagesSinceLastSummary, summarizeConversation } from "../condense"
101
101
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
102
102
import { restoreTodoListForTask } from "../tools/updateTodoListTool"
103
103
import { AutoApprovalHandler } from "./AutoApprovalHandler"
104
- import { handleOpenaiToolCall } from "./tool-call-helper"
104
+ import { StreamingToolCallProcessor , handleOpenaiToolCallStreaming } from "./tool-call-helper"
105
105
import { ToolArgs } from "../prompts/tools/types"
106
106
107
107
const MAX_EXPONENTIAL_BACKOFF_SECONDS = 600 // 10 minutes
@@ -236,6 +236,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
236
236
consecutiveMistakeCountForApplyDiff : Map < string , number > = new Map ( )
237
237
toolUsage : ToolUsage = { }
238
238
239
+ // Streaming Tool Call Processing
240
+ streamingToolCallProcessor : StreamingToolCallProcessor = new StreamingToolCallProcessor ( )
241
+
239
242
// Checkpoints
240
243
enableCheckpoints : boolean
241
244
checkpointService ?: RepoPerTaskCheckpointService
@@ -1564,6 +1567,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
1564
1567
this . assistantMessageParser . reset ( )
1565
1568
}
1566
1569
1570
+ // Reset streaming tool call processor
1571
+ this . streamingToolCallProcessor . reset ( )
1572
+
1567
1573
await this . diffViewProvider . reset ( )
1568
1574
1569
1575
// Yields only if the first chunk is successful, otherwise will
@@ -1572,7 +1578,6 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
1572
1578
const stream = this . attemptApiRequest ( )
1573
1579
let assistantMessage = ""
1574
1580
let reasoningMessage = ""
1575
- let accumulatedToolCalls : any [ ] = [ ]
1576
1581
this . isStreaming = true
1577
1582
1578
1583
try {
@@ -1595,13 +1600,25 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
1595
1600
cacheReadTokens += chunk . cacheReadTokens ?? 0
1596
1601
totalCost = chunk . totalCost
1597
1602
break
1598
- case "text" : {
1599
- assistantMessage += chunk . text
1603
+ case "text" :
1604
+ case "tool_call" : {
1605
+ let chunkContent
1606
+ if ( chunk . type == "tool_call" ) {
1607
+ chunkContent =
1608
+ handleOpenaiToolCallStreaming (
1609
+ this . streamingToolCallProcessor ,
1610
+ chunk . toolCalls ,
1611
+ chunk . toolCallType ,
1612
+ ) ?? ""
1613
+ } else {
1614
+ chunkContent = chunk . text
1615
+ }
1616
+ assistantMessage += chunkContent
1600
1617
1601
1618
// Parse raw assistant message chunk into content blocks.
1602
1619
const prevLength = this . assistantMessageContent . length
1603
1620
if ( this . isAssistantMessageParserEnabled && this . assistantMessageParser ) {
1604
- this . assistantMessageContent = this . assistantMessageParser . processChunk ( chunk . text )
1621
+ this . assistantMessageContent = this . assistantMessageParser . processChunk ( chunkContent )
1605
1622
} else {
1606
1623
// Use the old parsing method when experiment is disabled
1607
1624
this . assistantMessageContent = parseAssistantMessage ( assistantMessage )
@@ -1617,34 +1634,6 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
1617
1634
presentAssistantMessage ( this )
1618
1635
break
1619
1636
}
1620
- case "tool_call" : {
1621
- if ( chunk . toolCallType === "openai" ) {
1622
- const xmlContent = handleOpenaiToolCall ( accumulatedToolCalls , chunk )
1623
- if ( xmlContent ) {
1624
- assistantMessage += xmlContent
1625
-
1626
- // Parse raw assistant message chunk into content blocks.
1627
- const prevLength = this . assistantMessageContent . length
1628
- if ( this . isAssistantMessageParserEnabled && this . assistantMessageParser ) {
1629
- this . assistantMessageContent =
1630
- this . assistantMessageParser . processChunk ( xmlContent )
1631
- } else {
1632
- // Use the old parsing method when experiment is disabled
1633
- this . assistantMessageContent = parseAssistantMessage ( assistantMessage )
1634
- }
1635
-
1636
- if ( this . assistantMessageContent . length > prevLength ) {
1637
- // New content we need to present, reset to
1638
- // false in case previous content set this to true.
1639
- this . userMessageContentReady = false
1640
- }
1641
-
1642
- // Present content to user.
1643
- presentAssistantMessage ( this )
1644
- break
1645
- }
1646
- }
1647
- }
1648
1637
}
1649
1638
1650
1639
if ( this . abort ) {
@@ -1760,6 +1749,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
1760
1749
}
1761
1750
// When using old parser, no finalization needed - parsing already happened during streaming
1762
1751
1752
+ this . streamingToolCallProcessor . reset ( )
1753
+
1763
1754
if ( partialBlocks . length > 0 ) {
1764
1755
// If there is content to update then it will complete and
1765
1756
// update `this.userMessageContentReady` to true, which we
@@ -1917,7 +1908,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
1917
1908
{
1918
1909
maxConcurrentFileReads : maxConcurrentFileReads ?? 5 ,
1919
1910
todoListEnabled : apiConfiguration ?. todoListEnabled ?? true ,
1920
- toolCallEnabled : apiConfiguration ?. toolCallEnabled ?? false ,
1911
+ toolCallEnabled :
1912
+ ( apiConfiguration ?. toolCallEnabled ?? false ) && supportToolCall ( apiConfiguration ?. apiProvider ) ,
1921
1913
useAgentRules : vscode . workspace . getConfiguration ( "roo-cline" ) . get < boolean > ( "useAgentRules" ) ?? true ,
1922
1914
} ,
1923
1915
)
@@ -2111,7 +2103,9 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
2111
2103
...{
2112
2104
maxConcurrentFileReads : maxConcurrentFileReads ?? 5 ,
2113
2105
todoListEnabled : apiConfiguration ?. todoListEnabled ?? true ,
2114
- toolCallEnabled : apiConfiguration ?. toolCallEnabled ?? false ,
2106
+ toolCallEnabled :
2107
+ ( apiConfiguration ?. toolCallEnabled ?? false ) &&
2108
+ supportToolCall ( apiConfiguration ?. apiProvider ) ,
2115
2109
useAgentRules :
2116
2110
vscode . workspace . getConfiguration ( "roo-cline" ) . get < boolean > ( "useAgentRules" ) ?? true ,
2117
2111
} ,
0 commit comments