From cc93702d589e5edc60e7f82ee142cfc9ea669539 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 29 Oct 2025 22:24:29 -0400 Subject: [PATCH] Per-model config for supporting native tool calling --- packages/types/src/model.ts | 2 ++ packages/types/src/providers/anthropic.ts | 11 +++++++++ packages/types/src/providers/openai.ts | 23 +++++++++++++++++++ src/api/providers/anthropic.ts | 4 ---- src/api/providers/base-provider.ts | 23 +++++++------------ .../providers/fetchers/__tests__/roo.spec.ts | 3 +++ src/api/providers/fetchers/openrouter.ts | 1 + src/api/providers/fetchers/roo.ts | 4 ++++ src/api/providers/openai-native.ts | 4 ---- src/api/providers/openrouter.ts | 7 ------ src/api/providers/roo.ts | 8 ------- src/api/providers/router-provider.ts | 8 ------- src/api/providers/vercel-ai-gateway.ts | 7 ------ 13 files changed, 52 insertions(+), 53 deletions(-) diff --git a/packages/types/src/model.ts b/packages/types/src/model.ts index 705f17039e..a7b850d670 100644 --- a/packages/types/src/model.ts +++ b/packages/types/src/model.ts @@ -58,6 +58,8 @@ export const modelInfoSchema = z.object({ contextWindow: z.number(), supportsImages: z.boolean().optional(), supportsPromptCache: z.boolean(), + // Capability flag to indicate whether the model supports native tool calling + supportsNativeToolCalling: z.boolean().optional(), // Capability flag to indicate whether the model supports an output verbosity parameter supportsVerbosity: z.boolean().optional(), supportsReasoningBudget: z.boolean().optional(), diff --git a/packages/types/src/providers/anthropic.ts b/packages/types/src/providers/anthropic.ts index 5fbf62d507..d842da85f5 100644 --- a/packages/types/src/providers/anthropic.ts +++ b/packages/types/src/providers/anthropic.ts @@ -11,6 +11,7 @@ export const anthropicModels = { contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07' supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 3.0, // $3 per million input tokens (≤200K context) outputPrice: 15.0, // $15 per million output tokens (≤200K context) cacheWritesPrice: 3.75, // $3.75 per million tokens @@ -32,6 +33,7 @@ export const anthropicModels = { contextWindow: 200_000, // Default 200K, extendable to 1M with beta flag 'context-1m-2025-08-07' supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 3.0, // $3 per million input tokens (≤200K context) outputPrice: 15.0, // $15 per million output tokens (≤200K context) cacheWritesPrice: 3.75, // $3.75 per million tokens @@ -53,6 +55,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 15.0, // $15 per million input tokens outputPrice: 75.0, // $75 per million output tokens cacheWritesPrice: 18.75, // $18.75 per million tokens @@ -64,6 +67,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 15.0, // $15 per million input tokens outputPrice: 75.0, // $75 per million output tokens cacheWritesPrice: 18.75, // $18.75 per million tokens @@ -75,6 +79,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 3.0, // $3 per million input tokens outputPrice: 15.0, // $15 per million output tokens cacheWritesPrice: 3.75, // $3.75 per million tokens @@ -87,6 +92,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 3.0, // $3 per million input tokens outputPrice: 15.0, // $15 per million output tokens cacheWritesPrice: 3.75, // $3.75 per million tokens @@ -97,6 +103,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 3.0, // $3 per million input tokens outputPrice: 15.0, // $15 per million output tokens cacheWritesPrice: 3.75, // $3.75 per million tokens @@ -107,6 +114,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: false, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.0, outputPrice: 5.0, cacheWritesPrice: 1.25, @@ -117,6 +125,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 15.0, outputPrice: 75.0, cacheWritesPrice: 18.75, @@ -127,6 +136,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 0.25, outputPrice: 1.25, cacheWritesPrice: 0.3, @@ -137,6 +147,7 @@ export const anthropicModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.0, outputPrice: 5.0, cacheWritesPrice: 1.25, diff --git a/packages/types/src/providers/openai.ts b/packages/types/src/providers/openai.ts index a3eed1b57c..6010a3f42f 100644 --- a/packages/types/src/providers/openai.ts +++ b/packages/types/src/providers/openai.ts @@ -11,6 +11,7 @@ export const openAiNativeModels = { contextWindow: 400000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, supportsReasoningEffort: false, inputPrice: 1.25, outputPrice: 10.0, @@ -23,6 +24,7 @@ export const openAiNativeModels = { contextWindow: 400000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, supportsReasoningEffort: true, reasoningEffort: "medium", inputPrice: 1.25, @@ -42,6 +44,7 @@ export const openAiNativeModels = { contextWindow: 400000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, supportsReasoningEffort: true, reasoningEffort: "medium", inputPrice: 0.25, @@ -60,6 +63,7 @@ export const openAiNativeModels = { contextWindow: 400000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, supportsReasoningEffort: true, reasoningEffort: "medium", inputPrice: 0.05, @@ -75,6 +79,7 @@ export const openAiNativeModels = { contextWindow: 400000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, supportsReasoningEffort: true, reasoningEffort: "medium", inputPrice: 1.25, @@ -89,6 +94,7 @@ export const openAiNativeModels = { contextWindow: 1_047_576, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 2, outputPrice: 8, cacheReadsPrice: 0.5, @@ -102,6 +108,7 @@ export const openAiNativeModels = { contextWindow: 1_047_576, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 0.4, outputPrice: 1.6, cacheReadsPrice: 0.1, @@ -115,6 +122,7 @@ export const openAiNativeModels = { contextWindow: 1_047_576, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 0.1, outputPrice: 0.4, cacheReadsPrice: 0.025, @@ -128,6 +136,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 2.0, outputPrice: 8.0, cacheReadsPrice: 0.5, @@ -144,6 +153,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 2.0, outputPrice: 8.0, cacheReadsPrice: 0.5, @@ -155,6 +165,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 2.0, outputPrice: 8.0, cacheReadsPrice: 0.5, @@ -166,6 +177,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.1, outputPrice: 4.4, cacheReadsPrice: 0.275, @@ -182,6 +194,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.1, outputPrice: 4.4, cacheReadsPrice: 0.275, @@ -193,6 +206,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.1, outputPrice: 4.4, cacheReadsPrice: 0.275, @@ -204,6 +218,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: false, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.1, outputPrice: 4.4, cacheReadsPrice: 0.55, @@ -216,6 +231,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: false, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.1, outputPrice: 4.4, cacheReadsPrice: 0.55, @@ -227,6 +243,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: false, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.1, outputPrice: 4.4, cacheReadsPrice: 0.55, @@ -238,6 +255,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 15, outputPrice: 60, cacheReadsPrice: 7.5, @@ -248,6 +266,7 @@ export const openAiNativeModels = { contextWindow: 128_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 15, outputPrice: 60, cacheReadsPrice: 7.5, @@ -258,6 +277,7 @@ export const openAiNativeModels = { contextWindow: 128_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 1.1, outputPrice: 4.4, cacheReadsPrice: 0.55, @@ -268,6 +288,7 @@ export const openAiNativeModels = { contextWindow: 128_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 2.5, outputPrice: 10, cacheReadsPrice: 1.25, @@ -281,6 +302,7 @@ export const openAiNativeModels = { contextWindow: 128_000, supportsImages: true, supportsPromptCache: true, + supportsNativeToolCalling: true, inputPrice: 0.15, outputPrice: 0.6, cacheReadsPrice: 0.075, @@ -294,6 +316,7 @@ export const openAiNativeModels = { contextWindow: 200_000, supportsImages: false, supportsPromptCache: false, + supportsNativeToolCalling: true, inputPrice: 1.5, outputPrice: 6, cacheReadsPrice: 0, diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index b1a751b4f5..7872eb9423 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -361,8 +361,4 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa return super.countTokens(content) } } - - protected override hasNativeToolCapability(): boolean { - return true - } } diff --git a/src/api/providers/base-provider.ts b/src/api/providers/base-provider.ts index 9c7739628c..8d05da8970 100644 --- a/src/api/providers/base-provider.ts +++ b/src/api/providers/base-provider.ts @@ -35,26 +35,19 @@ export abstract class BaseProvider implements ApiHandler { } /** - * Returns whether this provider has the capability to support native tool calling. - * Default implementation returns false (no native tool support). - * Providers that DO support native tools should override this to return true. + * Returns whether native tool calling is supported for the current model AND enabled by user. + * Checks the model's supportsNativeToolCalling property and the user setting. * - * @returns false by default - */ - protected hasNativeToolCapability(): boolean { - return false - } - - /** - * Returns whether this provider supports native tool calling AND the user has enabled it. - * Combines provider capability with user setting. - * - * @returns true if provider supports it AND setting is enabled, false otherwise + * @returns true if model supports it AND setting is enabled, false otherwise */ supportsNativeTools(): boolean { - if (!this.hasNativeToolCapability()) { + const model = this.getModel() + const modelSupportsNativeTools = model.info.supportsNativeToolCalling ?? false + + if (!modelSupportsNativeTools) { return false } + return vscode.workspace.getConfiguration("roo-cline").get("nativeToolCalling", false) } } diff --git a/src/api/providers/fetchers/__tests__/roo.spec.ts b/src/api/providers/fetchers/__tests__/roo.spec.ts index dcc79e941f..edef63bc8a 100644 --- a/src/api/providers/fetchers/__tests__/roo.spec.ts +++ b/src/api/providers/fetchers/__tests__/roo.spec.ts @@ -69,6 +69,7 @@ describe("getRooModels", () => { supportsReasoningEffort: true, requiredReasoningEffort: false, supportsPromptCache: true, + supportsNativeToolCalling: false, inputPrice: 100, // 0.0001 * 1_000_000 outputPrice: 200, // 0.0002 * 1_000_000 cacheWritesPrice: 100, // 0.0001 * 1_000_000 @@ -117,6 +118,7 @@ describe("getRooModels", () => { supportsReasoningEffort: true, requiredReasoningEffort: true, supportsPromptCache: false, + supportsNativeToolCalling: false, inputPrice: 100, // 0.0001 * 1_000_000 outputPrice: 200, // 0.0002 * 1_000_000 cacheWritesPrice: undefined, @@ -163,6 +165,7 @@ describe("getRooModels", () => { supportsReasoningEffort: false, requiredReasoningEffort: false, supportsPromptCache: false, + supportsNativeToolCalling: false, inputPrice: 100, // 0.0001 * 1_000_000 outputPrice: 200, // 0.0002 * 1_000_000 cacheWritesPrice: undefined, diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index b546c40a3c..7af2c2a678 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -210,6 +210,7 @@ export const parseOpenRouterModel = ({ contextWindow: model.context_length, supportsImages: inputModality?.includes("image") ?? false, supportsPromptCache, + supportsNativeToolCalling: supportedParameters?.includes("tools") ?? false, inputPrice: parseApiPrice(model.pricing?.prompt), outputPrice: parseApiPrice(model.pricing?.completion), cacheWritesPrice, diff --git a/src/api/providers/fetchers/roo.ts b/src/api/providers/fetchers/roo.ts index 17aec4253b..a4ececfc52 100644 --- a/src/api/providers/fetchers/roo.ts +++ b/src/api/providers/fetchers/roo.ts @@ -83,6 +83,9 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise { }, } } - - protected override hasNativeToolCapability(): boolean { - // TODO: Read this from Roo model metadata instead of hardcoding - // For now, only enable for well-tested providers - const modelId = this.options.apiModelId ?? rooDefaultModelId - - return modelId.startsWith("anthropic/") || modelId.startsWith("openai/") - } } diff --git a/src/api/providers/router-provider.ts b/src/api/providers/router-provider.ts index 336e48a999..6500413f84 100644 --- a/src/api/providers/router-provider.ts +++ b/src/api/providers/router-provider.ts @@ -72,12 +72,4 @@ export abstract class RouterProvider extends BaseProvider { protected supportsTemperature(modelId: string): boolean { return !modelId.startsWith("openai/o3-mini") } - - protected override hasNativeToolCapability(): boolean { - // TODO: Read this from router model metadata instead of hardcoding - // For now, only enable for well-tested providers - const modelId = this.modelId ?? this.defaultModelId - - return modelId.startsWith("anthropic/") || modelId.startsWith("openai/") - } } diff --git a/src/api/providers/vercel-ai-gateway.ts b/src/api/providers/vercel-ai-gateway.ts index 61e3ac7bcd..57b8eef25c 100644 --- a/src/api/providers/vercel-ai-gateway.ts +++ b/src/api/providers/vercel-ai-gateway.ts @@ -139,11 +139,4 @@ export class VercelAiGatewayHandler extends RouterProvider implements SingleComp throw error } } - - protected override hasNativeToolCapability(): boolean { - // Enable for Anthropic and OpenAI models routed through Vercel AI Gateway - const modelId = this.options.vercelAiGatewayModelId ?? vercelAiGatewayDefaultModelId - - return modelId.startsWith("anthropic/") || modelId.startsWith("openai/") - } }