mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
This restores the ability for users to override the tool call protocol in the Advanced Settings. This is useful for OpenAI-compatible models (like Qwen3 and Kimi2) that do not handle native tool calling well. Changes: - Updated resolveToolProtocol.ts to respect user toolProtocol preference - Re-added Tool Call Protocol dropdown in Advanced Settings (ApiOptions.tsx) - Updated tests to reflect new behavior Fixes #10459
102 lines
3.8 KiB
TypeScript
102 lines
3.8 KiB
TypeScript
import { ToolProtocol, TOOL_PROTOCOL } from "@roo-code/types"
|
|
import type { ProviderSettings } 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.
|
|
*
|
|
* Precedence:
|
|
* 1. Locked Protocol (task-level lock for resumed tasks - highest priority)
|
|
* 2. User/Profile Preference (providerSettings.toolProtocol) - allows forcing XML for models
|
|
* that don't handle native tool calling well (e.g., some OpenAI-compatible models like Qwen3, Kimi2)
|
|
* 3. Native (default for all new tasks without explicit preference)
|
|
*
|
|
* @param providerSettings - The provider settings (toolProtocol field used for user preference)
|
|
* @param _modelInfo - Unused, kept for API compatibility
|
|
* @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?: unknown,
|
|
lockedProtocol?: ToolProtocol,
|
|
): ToolProtocol {
|
|
// 1. Locked Protocol - task-level lock takes absolute precedence
|
|
// This ensures resumed tasks continue using their original protocol
|
|
if (lockedProtocol) {
|
|
return lockedProtocol
|
|
}
|
|
|
|
// 2. User/Profile Preference - allow users to explicitly force XML protocol
|
|
// This is useful for models that don't handle native tool calling well
|
|
// (e.g., some OpenAI-compatible models like Qwen3, Kimi2)
|
|
if (providerSettings.toolProtocol) {
|
|
return providerSettings.toolProtocol
|
|
}
|
|
|
|
// 3. Default to Native protocol for new tasks
|
|
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
|
|
}
|