Skip to content
Draft
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 packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ const openAiSchema = baseProviderSettingsSchema.extend({
openAiApiKey: z.string().optional(),
openAiLegacyFormat: z.boolean().optional(),
openAiR1FormatEnabled: z.boolean().optional(),
openAiDisableNativeTools: z.boolean().optional(),
openAiModelId: z.string().optional(),
openAiCustomModelInfo: modelInfoSchema.nullish(),
openAiUseAzure: z.boolean().optional(),
Expand Down
50 changes: 50 additions & 0 deletions src/api/providers/__tests__/openai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,56 @@ describe("OpenAiHandler", () => {
expect(model.id).toBe("")
expect(model.info).toBeDefined()
})

it("should default to supportsNativeTools true for standard models", () => {
const model = handler.getModel()
expect(model.info.supportsNativeTools).toBe(true)
})

it("should disable native tools when openAiDisableNativeTools is true", () => {
const handlerWithDisabledTools = new OpenAiHandler({
...mockOptions,
openAiDisableNativeTools: true,
})
const model = handlerWithDisabledTools.getModel()
expect(model.info.supportsNativeTools).toBe(false)
})

it("should disable native tools for deepseek-r1 model", () => {
const handlerWithDeepSeekR1 = new OpenAiHandler({
...mockOptions,
openAiModelId: "deepseek-r1",
})
const model = handlerWithDeepSeekR1.getModel()
expect(model.info.supportsNativeTools).toBe(false)
})

it("should disable native tools for deepseek-reasoner model", () => {
const handlerWithDeepSeekReasoner = new OpenAiHandler({
...mockOptions,
openAiModelId: "deepseek-reasoner",
})
const model = handlerWithDeepSeekReasoner.getModel()
expect(model.info.supportsNativeTools).toBe(false)
})

it("should disable native tools when openAiR1FormatEnabled is true", () => {
const handlerWithR1Format = new OpenAiHandler({
...mockOptions,
openAiR1FormatEnabled: true,
})
const model = handlerWithR1Format.getModel()
expect(model.info.supportsNativeTools).toBe(false)
})

it("should disable native tools for deepseek-r1 variants (e.g., deepseek-r1-distill-qwen-32b)", () => {
const handlerWithDeepSeekR1Variant = new OpenAiHandler({
...mockOptions,
openAiModelId: "deepseek-r1-distill-qwen-32b",
})
const model = handlerWithDeepSeekR1Variant.getModel()
expect(model.info.supportsNativeTools).toBe(false)
})
})

describe("Azure AI Inference Service", () => {
Expand Down
11 changes: 11 additions & 0 deletions src/api/providers/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,23 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl

override getModel() {
const id = this.options.openAiModelId ?? ""
const enabledR1Format = this.options.openAiR1FormatEnabled ?? false
const disableNativeTools = this.options.openAiDisableNativeTools ?? false

// Detect DeepSeek reasoner models that don't support native tool calling
const isDeepSeekReasoner =
id.includes("deepseek-reasoner") || id.includes("deepseek-r1") || enabledR1Format

// Ensure OpenAI-compatible models default to supporting native tool calling.
// This is required for [`Task.attemptApiRequest()`](src/core/task/Task.ts:3817) to
// include tool definitions in the request.
// However, disable native tools for DeepSeek reasoner models or when explicitly disabled
// by the user, as these models don't support function calling and will return 400 errors.
const info: ModelInfo = {
...NATIVE_TOOL_DEFAULTS,
...(this.options.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults),
// Override: Disable native tools if user setting is enabled or model is a DeepSeek reasoner
...((disableNativeTools || isDeepSeekReasoner) && { supportsNativeTools: false }),
}
const params = getModelParams({ format: "openai", modelId: id, model: info, settings: this.options })
return { id, info, ...params }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,18 @@ export const OpenAICompatible = ({
onChange={handleInputChange("openAiR1FormatEnabled", noTransform)}
openAiR1FormatEnabled={apiConfiguration?.openAiR1FormatEnabled ?? false}
/>
<div>
<div className="flex items-center gap-1">
<Checkbox
checked={apiConfiguration?.openAiDisableNativeTools ?? false}
onChange={handleInputChange("openAiDisableNativeTools", noTransform)}>
<span className="font-medium">{t("settings:modelInfo.disableNativeTools")}</span>
</Checkbox>
</div>
<p className="text-vscode-descriptionForeground text-sm mt-0 ml-6">
{t("settings:modelInfo.disableNativeToolsTips")}
</p>
</div>
<div>
<Checkbox
checked={openAiLegacyFormatSelected}
Expand Down
2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/ca/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/de/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,8 @@
"enableStreaming": "Enable streaming",
"enableR1Format": "Enable R1 model parameters",
"enableR1FormatTips": "Must be enabled when using R1 models such as QWQ to prevent 400 errors",
"disableNativeTools": "Disable native tool calling",
"disableNativeToolsTips": "Enable this if your model does not support function calling (tool use). When disabled, Roo will use XML-based tool definitions instead. Required for DeepSeek-R1 and similar models.",
"useAzure": "Use Azure",
"azureApiVersion": "Set Azure API version",
"gemini": {
Expand Down
2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/es/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/fr/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/hi/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/id/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/it/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/ja/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/ko/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/nl/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/pl/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/pt-BR/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/ru/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/tr/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/vi/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/zh-CN/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/zh-TW/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading