fix: restore tool protocol selector for OpenAI Compatible provider

Re-adds the Tool Protocol selector for OpenAI Compatible providers that
was removed in PR #10281. This allows users with third-party API proxies
to select XML protocol as a workaround when their provider does not fully
support native tool calling.

Changes:
- Add Tool Protocol selector to OpenAICompatible.tsx
- Update resolveToolProtocol() to respect user preference for openai provider
- Add tests for OpenAI Compatible provider tool protocol selection

Fixes #10246
This commit is contained in:
Roo Code 2025-12-23 07:35:11 +00:00
parent 30090de2e9
commit fda4984d34
3 changed files with 98 additions and 13 deletions

View file

@ -8,12 +8,15 @@ describe("resolveToolProtocol", () => {
/**
* XML Protocol Deprecation:
*
* XML tool protocol has been fully deprecated. All models now use Native
* tool calling. User preferences and model defaults are ignored.
* XML tool protocol has been deprecated for most providers. All models now
* use Native tool calling by default. However, for OpenAI Compatible providers,
* user preferences are still respected because third-party API proxies may not
* fully support native tool calling.
*
* Precedence:
* 1. Locked Protocol (for resumed tasks that used XML)
* 2. Native (always, for all new tasks)
* 2. User preference for OpenAI Compatible provider (allows XML for compatibility)
* 3. Native (default for all other providers and new tasks)
*/
describe("Locked Protocol (Precedence Level 0 - Highest Priority)", () => {
@ -66,7 +69,7 @@ describe("resolveToolProtocol", () => {
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
})
it("should use native for OpenAI compatible provider", () => {
it("should use native for OpenAI compatible provider without user preference", () => {
const settings: ProviderSettings = {
apiProvider: "openai",
}
@ -75,6 +78,44 @@ describe("resolveToolProtocol", () => {
})
})
describe("OpenAI Compatible Provider - User Preference Respected", () => {
it("should use XML when user explicitly sets toolProtocol to xml for openai provider", () => {
const settings: ProviderSettings = {
apiProvider: "openai",
toolProtocol: "xml",
}
const result = resolveToolProtocol(settings)
expect(result).toBe(TOOL_PROTOCOL.XML)
})
it("should use native when user explicitly sets toolProtocol to native for openai provider", () => {
const settings: ProviderSettings = {
apiProvider: "openai",
toolProtocol: "native",
}
const result = resolveToolProtocol(settings)
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
})
it("should default to native when no toolProtocol is set for openai provider", () => {
const settings: ProviderSettings = {
apiProvider: "openai",
}
const result = resolveToolProtocol(settings)
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
})
it("should respect locked protocol over user preference for openai provider", () => {
const settings: ProviderSettings = {
apiProvider: "openai",
toolProtocol: "xml",
}
// Locked protocol takes precedence
const result = resolveToolProtocol(settings, undefined, "native")
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
})
})
describe("Edge Cases", () => {
it("should handle missing provider name gracefully", () => {
const settings: ProviderSettings = {}

View file

@ -15,21 +15,23 @@ type ApiMessageForDetection = Anthropic.MessageParam & {
* Resolve the effective tool protocol.
*
* **Deprecation Note (XML Protocol):**
* XML tool protocol has been deprecated. All models now use Native tool calling.
* User/profile preferences (`providerSettings.toolProtocol`) and model defaults
* (`modelInfo.defaultToolProtocol`) are ignored.
* XML tool protocol has been deprecated for most providers. All models now use
* Native tool calling by default. However, for OpenAI Compatible providers,
* user preferences are still respected because third-party API proxies may not
* fully support native tool calling.
*
* Precedence:
* 1. Locked Protocol (task-level lock for resumed tasks - highest priority)
* 2. Native (always, for all new tasks)
* 2. User preference for OpenAI Compatible provider (allows XML for compatibility)
* 3. Native (default for all other providers and new tasks)
*
* @param _providerSettings - The provider settings (toolProtocol field is ignored)
* @param providerSettings - The provider settings (toolProtocol field is respected for openai provider)
* @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,
providerSettings: ProviderSettings,
_modelInfo?: unknown,
lockedProtocol?: ToolProtocol,
): ToolProtocol {
@ -39,8 +41,15 @@ export function resolveToolProtocol(
return lockedProtocol
}
// 2. Always return Native protocol for new tasks
// All models now support native tools; XML is deprecated
// 2. For OpenAI Compatible provider, respect user preference
// Third-party API proxies may not fully support native tool calling,
// so users need the ability to select XML protocol as a workaround.
if (providerSettings.apiProvider === "openai" && providerSettings.toolProtocol) {
return providerSettings.toolProtocol
}
// 3. Default to Native protocol for new tasks
// All models now support native tools; XML is deprecated for standard providers
return TOOL_PROTOCOL.NATIVE
}

View file

@ -8,14 +8,24 @@ import {
type ModelInfo,
type ReasoningEffort,
type OrganizationAllowList,
type ToolProtocol,
azureOpenAiDefaultApiVersion,
openAiModelInfoSaneDefaults,
TOOL_PROTOCOL,
} from "@roo-code/types"
import { ExtensionMessage } from "@roo/ExtensionMessage"
import { useAppTranslation } from "@src/i18n/TranslationContext"
import { Button, StandardTooltip } from "@src/components/ui"
import {
Button,
StandardTooltip,
Select,
SelectTrigger,
SelectValue,
SelectContent,
SelectItem,
} from "@src/components/ui"
import { convertHeadersToObject } from "../utils/headers"
import { inputEventTransform, noTransform } from "../transforms"
@ -246,6 +256,31 @@ export const OpenAICompatible = ({
)}
</div>
{/* Tool Protocol Selector - for third-party API providers that may not support native tool calling */}
<div>
<label className="block font-medium mb-1">{t("settings:toolProtocol.label")}</label>
<Select
value={apiConfiguration.toolProtocol || "default"}
onValueChange={(value) => {
const newValue = value === "default" ? undefined : (value as ToolProtocol)
setApiConfigurationField("toolProtocol", newValue)
}}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">
{t("settings:toolProtocol.default")} ({t("settings:toolProtocol.native")})
</SelectItem>
<SelectItem value={TOOL_PROTOCOL.XML}>{t("settings:toolProtocol.xml")}</SelectItem>
<SelectItem value={TOOL_PROTOCOL.NATIVE}>{t("settings:toolProtocol.native")}</SelectItem>
</SelectContent>
</Select>
<div className="text-sm text-vscode-descriptionForeground mt-1">
{t("settings:toolProtocol.description")}
</div>
</div>
<div className="flex flex-col gap-1">
<Checkbox
checked={apiConfiguration.enableReasoningEffort ?? false}