Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/task/__tests__/Task.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ describe("Cline", () => {
getState: vi.fn().mockResolvedValue({
apiConfiguration: mockApiConfig,
}),
getMcpHub: vi.fn().mockReturnValue(undefined),
say: vi.fn(),
postStateToWebview: vi.fn().mockResolvedValue(undefined),
postMessageToWebview: vi.fn().mockResolvedValue(undefined),
Expand Down
30 changes: 18 additions & 12 deletions src/utils/__tests__/resolveToolProtocol.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,24 @@ describe("resolveToolProtocol", () => {
})
})

describe("Precedence Level 3: XML Fallback", () => {
it("should use XML fallback when no model default is specified", () => {
describe("Precedence Level 3: Native Fallback", () => {
it("should use Native fallback when no model default is specified and model supports native", () => {
const settings: ProviderSettings = {
apiProvider: "anthropic",
}
const result = resolveToolProtocol(settings, undefined)
expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback
const modelInfo: ModelInfo = {
maxTokens: 4096,
contextWindow: 128000,
supportsPromptCache: false,
supportsNativeTools: true,
}
const result = resolveToolProtocol(settings, modelInfo)
expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Native fallback
})
})

describe("Complete Precedence Chain", () => {
it("should respect full precedence: Profile > Model Default > XML Fallback", () => {
it("should respect full precedence: Profile > Model Default > Native Fallback", () => {
// Set up a scenario with all levels defined
const settings: ProviderSettings = {
toolProtocol: "native", // Level 1: User profile setting
Expand Down Expand Up @@ -186,7 +192,7 @@ describe("resolveToolProtocol", () => {
expect(result).toBe(TOOL_PROTOCOL.XML) // Model default wins
})

it("should skip to XML fallback when profile and model default are undefined", () => {
it("should skip to Native fallback when profile and model default are undefined", () => {
const settings: ProviderSettings = {
apiProvider: "openai-native",
}
Expand All @@ -199,7 +205,7 @@ describe("resolveToolProtocol", () => {
}

const result = resolveToolProtocol(settings, modelInfo)
expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback
expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Native fallback
})

it("should skip to XML fallback when model info is unavailable", () => {
Expand All @@ -208,23 +214,23 @@ describe("resolveToolProtocol", () => {
}

const result = resolveToolProtocol(settings, undefined)
expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback
expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback (no model info means no native support)
})
})

describe("Edge Cases", () => {
it("should handle missing provider name gracefully", () => {
const settings: ProviderSettings = {}
const result = resolveToolProtocol(settings)
expect(result).toBe(TOOL_PROTOCOL.XML) // Falls back to global
expect(result).toBe(TOOL_PROTOCOL.XML) // Falls back to XML (no model info)
})

it("should handle undefined model info gracefully", () => {
const settings: ProviderSettings = {
apiProvider: "openai-native",
}
const result = resolveToolProtocol(settings, undefined)
expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback
expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback (no model info)
})

it("should fall back to XML when model doesn't support native", () => {
Expand All @@ -243,7 +249,7 @@ describe("resolveToolProtocol", () => {
})

describe("Real-world Scenarios", () => {
it("should use XML fallback for models without defaultToolProtocol", () => {
it("should use Native fallback for models without defaultToolProtocol", () => {
const settings: ProviderSettings = {
apiProvider: "openai-native",
}
Expand All @@ -254,7 +260,7 @@ describe("resolveToolProtocol", () => {
supportsNativeTools: true,
}
const result = resolveToolProtocol(settings, modelInfo)
expect(result).toBe(TOOL_PROTOCOL.XML) // XML fallback
expect(result).toBe(TOOL_PROTOCOL.NATIVE) // Native fallback
})

it("should use XML for Claude models with Anthropic provider", () => {
Expand Down
6 changes: 3 additions & 3 deletions src/utils/resolveToolProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { ProviderSettings, ModelInfo } from "@roo-code/types"
*
* 1. User Preference - Per-Profile (explicit profile setting)
* 2. Model Default (defaultToolProtocol in ModelInfo)
* 3. XML Fallback (final fallback)
* 3. Native Fallback (final fallback)
*
* Then check support: if protocol is "native" but model doesn't support it, use XML.
*
Expand All @@ -31,6 +31,6 @@ export function resolveToolProtocol(providerSettings: ProviderSettings, modelInf
return modelInfo.defaultToolProtocol
}

// 3. XML Fallback
return TOOL_PROTOCOL.XML
// 3. Native Fallback
return TOOL_PROTOCOL.NATIVE
}
4 changes: 2 additions & 2 deletions webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,8 @@ const ApiOptions = ({
// Mirrors the simplified logic in resolveToolProtocol.ts:
// 1. User preference (toolProtocol) - handled by the select value binding
// 2. Model default - use if available
// 3. XML fallback
const defaultProtocol = selectedModelInfo?.defaultToolProtocol || TOOL_PROTOCOL.XML
// 3. Native fallback
const defaultProtocol = selectedModelInfo?.defaultToolProtocol || TOOL_PROTOCOL.NATIVE

// Show the tool protocol selector when model supports native tools
const showToolProtocolSelector = selectedModelInfo?.supportsNativeTools === true
Expand Down
Loading