mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-06 02:47:56 +00:00
feat: restore XML tool protocol option for OpenAI-Compatible provider
This restores the ability for users of the OpenAI-Compatible provider to
explicitly select XML tool protocol, fixing compatibility issues with
backends like SGLang/vLLM that do not support native tool calling.
Changes:
- Modified resolveToolProtocol.ts to respect user toolProtocol preference
for the OpenAI-Compatible provider ("openai")
- Added Tool Protocol selector to OpenAICompatible.tsx component
- Added tests for the new behavior in resolveToolProtocol.spec.ts
- Updated OpenAICompatible.spec.tsx to mock Select components
Fixes RooCodeInc#10319
This commit is contained in:
parent
343d5e9dcb
commit
89fd5a695f
4 changed files with 101 additions and 16 deletions
|
|
@ -48,8 +48,47 @@ describe("resolveToolProtocol", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("Native Protocol Always Used For New Tasks", () => {
|
||||
it("should always use native for new tasks", () => {
|
||||
describe("OpenAI-Compatible Provider Tool Protocol", () => {
|
||||
it("should respect XML preference for OpenAI-Compatible provider", () => {
|
||||
const settings: ProviderSettings = {
|
||||
apiProvider: "openai",
|
||||
toolProtocol: "xml",
|
||||
}
|
||||
// OpenAI-Compatible provider should respect user preference
|
||||
const result = resolveToolProtocol(settings)
|
||||
expect(result).toBe(TOOL_PROTOCOL.XML)
|
||||
})
|
||||
|
||||
it("should respect native preference for OpenAI-Compatible 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 preference set for OpenAI-Compatible provider", () => {
|
||||
const settings: ProviderSettings = {
|
||||
apiProvider: "openai",
|
||||
}
|
||||
const result = resolveToolProtocol(settings, openAiModelInfoSaneDefaults)
|
||||
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
|
||||
})
|
||||
|
||||
it("should still honor locked protocol over user preference for OpenAI-Compatible", () => {
|
||||
const settings: ProviderSettings = {
|
||||
apiProvider: "openai",
|
||||
toolProtocol: "xml", // User wants XML
|
||||
}
|
||||
// Locked protocol takes precedence
|
||||
const result = resolveToolProtocol(settings, undefined, "native")
|
||||
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Native Protocol Always Used For Other Providers", () => {
|
||||
it("should always use native for new tasks with other providers", () => {
|
||||
const settings: ProviderSettings = {
|
||||
apiProvider: "anthropic",
|
||||
}
|
||||
|
|
@ -57,20 +96,21 @@ describe("resolveToolProtocol", () => {
|
|||
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
|
||||
})
|
||||
|
||||
it("should use native even when user preference is XML (user prefs ignored)", () => {
|
||||
it("should use native even when user preference is XML for non-OpenAI-Compatible providers", () => {
|
||||
const settings: ProviderSettings = {
|
||||
toolProtocol: "xml", // User wants XML - ignored
|
||||
toolProtocol: "xml", // User wants XML - ignored for non-OpenAI-Compatible
|
||||
apiProvider: "openai-native",
|
||||
}
|
||||
const result = resolveToolProtocol(settings)
|
||||
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
|
||||
})
|
||||
|
||||
it("should use native for OpenAI compatible provider", () => {
|
||||
it("should ignore XML preference for Anthropic provider", () => {
|
||||
const settings: ProviderSettings = {
|
||||
apiProvider: "openai",
|
||||
toolProtocol: "xml", // User preference - ignored for Anthropic
|
||||
apiProvider: "anthropic",
|
||||
}
|
||||
const result = resolveToolProtocol(settings, openAiModelInfoSaneDefaults)
|
||||
const result = resolveToolProtocol(settings)
|
||||
expect(result).toBe(TOOL_PROTOCOL.NATIVE)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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 the OpenAI-Compatible provider,
|
||||
* users can explicitly opt for XML protocol when their backend servers (like
|
||||
* SGLang, vLLM, etc.) don't 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 (respects toolProtocol setting)
|
||||
* 3. Native (default for all new tasks)
|
||||
*
|
||||
* @param _providerSettings - The provider settings (toolProtocol field is ignored)
|
||||
* @param providerSettings - The provider settings (toolProtocol respected for OpenAI-Compatible)
|
||||
* @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
|
||||
// Some backends (SGLang, vLLM, etc.) don't support native tool calling
|
||||
// and require XML protocol to work properly
|
||||
if (providerSettings.apiProvider === "openai" && providerSettings.toolProtocol) {
|
||||
return providerSettings.toolProtocol
|
||||
}
|
||||
|
||||
// 3. Default to Native protocol for new tasks
|
||||
// Most providers support native tools; XML is deprecated for other providers
|
||||
return TOOL_PROTOCOL.NATIVE
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
type ModelInfo,
|
||||
type ReasoningEffort,
|
||||
type OrganizationAllowList,
|
||||
type ToolProtocol,
|
||||
azureOpenAiDefaultApiVersion,
|
||||
openAiModelInfoSaneDefaults,
|
||||
} from "@roo-code/types"
|
||||
|
|
@ -15,7 +16,15 @@ import {
|
|||
import { ExtensionMessage } from "@roo/ExtensionMessage"
|
||||
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
import { Button, StandardTooltip } from "@src/components/ui"
|
||||
import {
|
||||
Button,
|
||||
StandardTooltip,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@src/components/ui"
|
||||
|
||||
import { convertHeadersToObject } from "../utils/headers"
|
||||
import { inputEventTransform, noTransform } from "../transforms"
|
||||
|
|
@ -170,6 +179,24 @@ export const OpenAICompatible = ({
|
|||
onChange={handleInputChange("openAiStreamingEnabled", noTransform)}>
|
||||
{t("settings:modelInfo.enableStreaming")}
|
||||
</Checkbox>
|
||||
{/* Tool Protocol selector for backends that don't support native tool calling */}
|
||||
<div>
|
||||
<label className="block font-medium mb-1">{t("settings:toolProtocol.label")}</label>
|
||||
<Select
|
||||
value={apiConfiguration?.toolProtocol || "native"}
|
||||
onValueChange={(value) => setApiConfigurationField("toolProtocol", value as ToolProtocol)}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder={t("settings:common.select")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="native">{t("settings:toolProtocol.native")}</SelectItem>
|
||||
<SelectItem value="xml">{t("settings:toolProtocol.xml")}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-sm text-vscode-descriptionForeground mt-1">
|
||||
{t("settings:toolProtocol.description")}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox
|
||||
checked={apiConfiguration?.includeMaxTokens ?? true}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,15 @@ vi.mock("@src/i18n/TranslationContext", () => ({
|
|||
vi.mock("@src/components/ui", () => ({
|
||||
Button: ({ children, onClick }: any) => <button onClick={onClick}>{children}</button>,
|
||||
StandardTooltip: ({ children, content }: any) => <div title={content}>{children}</div>,
|
||||
Select: ({ children, value, onValueChange: _onValueChange }: any) => (
|
||||
<div data-testid="select" data-value={value}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
SelectContent: ({ children }: any) => <div data-testid="select-content">{children}</div>,
|
||||
SelectItem: ({ children, value }: any) => <div data-testid={`select-item-${value}`}>{children}</div>,
|
||||
SelectTrigger: ({ children }: any) => <button data-testid="select-trigger">{children}</button>,
|
||||
SelectValue: ({ placeholder }: any) => <span data-testid="select-value">{placeholder}</span>,
|
||||
}))
|
||||
|
||||
// Mock other components
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue