|
@@ -27,6 +27,7 @@ import {
|
|
|
type ToolProgressStatus,
|
|
type ToolProgressStatus,
|
|
|
type HistoryItem,
|
|
type HistoryItem,
|
|
|
type CreateTaskOptions,
|
|
type CreateTaskOptions,
|
|
|
|
|
+ type ModelInfo,
|
|
|
RooCodeEventName,
|
|
RooCodeEventName,
|
|
|
TelemetryEventName,
|
|
TelemetryEventName,
|
|
|
TaskStatus,
|
|
TaskStatus,
|
|
@@ -305,6 +306,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|
|
assistantMessageParser?: AssistantMessageParser
|
|
assistantMessageParser?: AssistantMessageParser
|
|
|
private providerProfileChangeListener?: (config: { name: string; provider?: string }) => void
|
|
private providerProfileChangeListener?: (config: { name: string; provider?: string }) => void
|
|
|
|
|
|
|
|
|
|
+ // Cached model info for current streaming session (set at start of each API request)
|
|
|
|
|
+ // This prevents excessive getModel() calls during tool execution
|
|
|
|
|
+ cachedStreamingModel?: { id: string; info: ModelInfo }
|
|
|
|
|
+
|
|
|
// Token Usage Cache
|
|
// Token Usage Cache
|
|
|
private tokenUsageSnapshot?: TokenUsage
|
|
private tokenUsageSnapshot?: TokenUsage
|
|
|
private tokenUsageSnapshotAt?: number
|
|
private tokenUsageSnapshotAt?: number
|
|
@@ -412,7 +417,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|
|
// Initialize the assistant message parser only for XML protocol.
|
|
// Initialize the assistant message parser only for XML protocol.
|
|
|
// For native protocol, tool calls come as tool_call chunks, not XML.
|
|
// For native protocol, tool calls come as tool_call chunks, not XML.
|
|
|
// experiments is always provided via TaskOptions (defaults to experimentDefault in provider)
|
|
// experiments is always provided via TaskOptions (defaults to experimentDefault in provider)
|
|
|
- const toolProtocol = resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
|
|
|
|
|
|
|
+ const modelInfo = this.api.getModel().info
|
|
|
|
|
+ const toolProtocol = resolveToolProtocol(this.apiConfiguration, modelInfo)
|
|
|
this.assistantMessageParser = toolProtocol !== "native" ? new AssistantMessageParser() : undefined
|
|
this.assistantMessageParser = toolProtocol !== "native" ? new AssistantMessageParser() : undefined
|
|
|
|
|
|
|
|
this.messageQueueService = new MessageQueueService()
|
|
this.messageQueueService = new MessageQueueService()
|
|
@@ -1094,15 +1100,17 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|
|
*/
|
|
*/
|
|
|
public async updateApiConfiguration(newApiConfiguration: ProviderSettings): Promise<void> {
|
|
public async updateApiConfiguration(newApiConfiguration: ProviderSettings): Promise<void> {
|
|
|
// Determine the previous protocol before updating
|
|
// Determine the previous protocol before updating
|
|
|
|
|
+ const prevModelInfo = this.api.getModel().info
|
|
|
const previousProtocol = this.apiConfiguration
|
|
const previousProtocol = this.apiConfiguration
|
|
|
- ? resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
|
|
|
|
|
|
|
+ ? resolveToolProtocol(this.apiConfiguration, prevModelInfo)
|
|
|
: undefined
|
|
: undefined
|
|
|
|
|
|
|
|
this.apiConfiguration = newApiConfiguration
|
|
this.apiConfiguration = newApiConfiguration
|
|
|
this.api = buildApiHandler(newApiConfiguration)
|
|
this.api = buildApiHandler(newApiConfiguration)
|
|
|
|
|
|
|
|
// Determine the new tool protocol
|
|
// Determine the new tool protocol
|
|
|
- const newProtocol = resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
|
|
|
|
|
|
|
+ const newModelInfo = this.api.getModel().info
|
|
|
|
|
+ const newProtocol = resolveToolProtocol(this.apiConfiguration, newModelInfo)
|
|
|
const shouldUseXmlParser = newProtocol === "xml"
|
|
const shouldUseXmlParser = newProtocol === "xml"
|
|
|
|
|
|
|
|
// Only make changes if the protocol actually changed
|
|
// Only make changes if the protocol actually changed
|
|
@@ -2071,14 +2079,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|
|
const costResult =
|
|
const costResult =
|
|
|
apiProtocol === "anthropic"
|
|
apiProtocol === "anthropic"
|
|
|
? calculateApiCostAnthropic(
|
|
? calculateApiCostAnthropic(
|
|
|
- this.api.getModel().info,
|
|
|
|
|
|
|
+ streamModelInfo,
|
|
|
inputTokens,
|
|
inputTokens,
|
|
|
outputTokens,
|
|
outputTokens,
|
|
|
cacheWriteTokens,
|
|
cacheWriteTokens,
|
|
|
cacheReadTokens,
|
|
cacheReadTokens,
|
|
|
)
|
|
)
|
|
|
: calculateApiCostOpenAI(
|
|
: calculateApiCostOpenAI(
|
|
|
- this.api.getModel().info,
|
|
|
|
|
|
|
+ streamModelInfo,
|
|
|
inputTokens,
|
|
inputTokens,
|
|
|
outputTokens,
|
|
outputTokens,
|
|
|
cacheWriteTokens,
|
|
cacheWriteTokens,
|
|
@@ -2137,8 +2145,12 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|
|
|
|
|
|
|
await this.diffViewProvider.reset()
|
|
await this.diffViewProvider.reset()
|
|
|
|
|
|
|
|
- // Determine protocol once per API request to avoid repeated calls in the streaming loop
|
|
|
|
|
- const streamProtocol = resolveToolProtocol(this.apiConfiguration, this.api.getModel().info)
|
|
|
|
|
|
|
+ // Cache model info once per API request to avoid repeated calls during streaming
|
|
|
|
|
+ // This is especially important for tools and background usage collection
|
|
|
|
|
+ this.cachedStreamingModel = this.api.getModel()
|
|
|
|
|
+ const streamModelInfo = this.cachedStreamingModel.info
|
|
|
|
|
+ const cachedModelId = this.cachedStreamingModel.id
|
|
|
|
|
+ const streamProtocol = resolveToolProtocol(this.apiConfiguration, streamModelInfo)
|
|
|
const shouldUseXmlParser = streamProtocol === "xml"
|
|
const shouldUseXmlParser = streamProtocol === "xml"
|
|
|
|
|
|
|
|
// Yields only if the first chunk is successful, otherwise will
|
|
// Yields only if the first chunk is successful, otherwise will
|
|
@@ -2359,14 +2371,14 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|
|
const costResult =
|
|
const costResult =
|
|
|
apiProtocol === "anthropic"
|
|
apiProtocol === "anthropic"
|
|
|
? calculateApiCostAnthropic(
|
|
? calculateApiCostAnthropic(
|
|
|
- this.api.getModel().info,
|
|
|
|
|
|
|
+ streamModelInfo,
|
|
|
tokens.input,
|
|
tokens.input,
|
|
|
tokens.output,
|
|
tokens.output,
|
|
|
tokens.cacheWrite,
|
|
tokens.cacheWrite,
|
|
|
tokens.cacheRead,
|
|
tokens.cacheRead,
|
|
|
)
|
|
)
|
|
|
: calculateApiCostOpenAI(
|
|
: calculateApiCostOpenAI(
|
|
|
- this.api.getModel().info,
|
|
|
|
|
|
|
+ streamModelInfo,
|
|
|
tokens.input,
|
|
tokens.input,
|
|
|
tokens.output,
|
|
tokens.output,
|
|
|
tokens.cacheWrite,
|
|
tokens.cacheWrite,
|
|
@@ -2616,7 +2628,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
|
|
|
|
|
|
|
|
// Check if we should preserve reasoning in the assistant message
|
|
// Check if we should preserve reasoning in the assistant message
|
|
|
let finalAssistantMessage = assistantMessage
|
|
let finalAssistantMessage = assistantMessage
|
|
|
- if (reasoningMessage && this.api.getModel().info.preserveReasoning) {
|
|
|
|
|
|
|
+ if (reasoningMessage && streamModelInfo.preserveReasoning) {
|
|
|
// Prepend reasoning in XML tags to the assistant message so it's included in API history
|
|
// Prepend reasoning in XML tags to the assistant message so it's included in API history
|
|
|
finalAssistantMessage = `<think>${reasoningMessage}</think>\n${assistantMessage}`
|
|
finalAssistantMessage = `<think>${reasoningMessage}</think>\n${assistantMessage}`
|
|
|
}
|
|
}
|