mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-14 04:08:28 +00:00
112 lines
4 KiB
TypeScript
112 lines
4 KiB
TypeScript
import { ToolProtocol, TOOL_PROTOCOL } from "@roo-code/types"
|
|
import type { ProviderSettings, ModelInfo } from "@roo-code/types"
|
|
import type { Anthropic } from "@anthropic-ai/sdk"
|
|
import { findLast, findLastIndex } from "../shared/array"
|
|
|
|
/**
|
|
* Represents an API message in the conversation history.
|
|
* This is a minimal type definition for the detection function.
|
|
*/
|
|
type ApiMessageForDetection = Anthropic.MessageParam & {
|
|
ts?: number
|
|
}
|
|
|
|
/**
|
|
* Resolve the effective tool protocol based on the precedence hierarchy:
|
|
*
|
|
* 0. Locked Protocol (task-level lock, if provided - highest priority)
|
|
* 1. User Preference - Per-Profile (explicit profile setting)
|
|
* 2. Model Default (defaultToolProtocol in ModelInfo)
|
|
* 3. Native Fallback (final fallback)
|
|
*
|
|
* Then check support: if protocol is "native" but model doesn't support it, use XML.
|
|
*
|
|
* @param providerSettings - The provider settings for the current profile
|
|
* @param modelInfo - Optional model information containing capabilities
|
|
* @param lockedProtocol - Optional task-locked protocol that takes absolute precedence
|
|
* @returns The resolved tool protocol (either "xml" or "native")
|
|
*/
|
|
export function resolveToolProtocol(
|
|
providerSettings: ProviderSettings,
|
|
modelInfo?: ModelInfo,
|
|
lockedProtocol?: ToolProtocol,
|
|
): ToolProtocol {
|
|
// 0. Locked Protocol - task-level lock takes absolute precedence
|
|
// This ensures tasks continue using their original protocol even if settings change
|
|
if (lockedProtocol) {
|
|
return lockedProtocol
|
|
}
|
|
|
|
// If model doesn't support native tools, return XML immediately
|
|
// Treat undefined as unsupported (only allow native when explicitly true)
|
|
if (modelInfo?.supportsNativeTools !== true) {
|
|
return TOOL_PROTOCOL.XML
|
|
}
|
|
|
|
// 1. User Preference - Per-Profile (explicit profile setting, highest priority)
|
|
if (providerSettings.toolProtocol) {
|
|
return providerSettings.toolProtocol
|
|
}
|
|
|
|
// 2. Model Default - model's preferred protocol
|
|
if (modelInfo?.defaultToolProtocol) {
|
|
return modelInfo.defaultToolProtocol
|
|
}
|
|
|
|
// 3. Native Fallback
|
|
return TOOL_PROTOCOL.NATIVE
|
|
}
|
|
|
|
/**
|
|
* Detect the tool protocol used in an existing conversation history.
|
|
*
|
|
* This function scans the API conversation history for tool_use blocks
|
|
* and determines which protocol was used based on their structure:
|
|
*
|
|
* - Native protocol: tool_use blocks ALWAYS have an `id` field
|
|
* - XML protocol: tool_use blocks NEVER have an `id` field
|
|
*
|
|
* This is critical for task resumption: if a task previously used tools
|
|
* with a specific protocol, we must continue using that protocol even
|
|
* if the user's NTC settings have changed.
|
|
*
|
|
* The function searches from the most recent message backwards to find
|
|
* the last tool call, which represents the task's current protocol state.
|
|
*
|
|
* @param messages - The API conversation history to scan
|
|
* @returns The detected protocol, or undefined if no tool calls were found
|
|
*/
|
|
export function detectToolProtocolFromHistory(messages: ApiMessageForDetection[]): ToolProtocol | undefined {
|
|
// Find the last assistant message that contains a tool_use block
|
|
const lastAssistantWithTool = findLast(messages, (message) => {
|
|
if (message.role !== "assistant") {
|
|
return false
|
|
}
|
|
const content = message.content
|
|
if (!Array.isArray(content)) {
|
|
return false
|
|
}
|
|
return content.some((block) => block.type === "tool_use")
|
|
})
|
|
|
|
if (!lastAssistantWithTool) {
|
|
return undefined
|
|
}
|
|
|
|
// Find the last tool_use block in that message's content
|
|
const content = lastAssistantWithTool.content as Anthropic.ContentBlock[]
|
|
const lastToolUseIndex = findLastIndex(content, (block) => block.type === "tool_use")
|
|
|
|
if (lastToolUseIndex === -1) {
|
|
return undefined
|
|
}
|
|
|
|
const lastToolUse = content[lastToolUseIndex]
|
|
|
|
// The presence or absence of `id` determines the protocol:
|
|
// - Native protocol tool calls ALWAYS have an ID (set when parsed from tool_call chunks)
|
|
// - XML protocol tool calls NEVER have an ID (parsed from XML text)
|
|
// This pattern is used in presentAssistantMessage.ts:497-500
|
|
const hasId = "id" in lastToolUse && !!lastToolUse.id
|
|
return hasId ? TOOL_PROTOCOL.NATIVE : TOOL_PROTOCOL.XML
|
|
}
|