Per-model config for supporting native tool calling

This commit is contained in:
Matt Rubens 2025-10-29 22:24:29 -04:00
parent dc2baaf809
commit cc93702d58
13 changed files with 52 additions and 53 deletions

View file

@ -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(),

View file

@ -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,

View file

@ -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,

View file

@ -361,8 +361,4 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
return super.countTokens(content)
}
}
protected override hasNativeToolCapability(): boolean {
return true
}
}

View file

@ -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<boolean>("nativeToolCalling", false)
}
}

View file

@ -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,

View file

@ -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,

View file

@ -83,6 +83,9 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
const cacheReadPrice = pricing.input_cache_read ? parseApiPrice(pricing.input_cache_read) : undefined
const cacheWritePrice = pricing.input_cache_write ? parseApiPrice(pricing.input_cache_write) : undefined
// Determine if the model supports native tools based on tags
const supportsNativeToolCalling = tags.includes("tool-use")
models[modelId] = {
maxTokens,
contextWindow,
@ -90,6 +93,7 @@ export async function getRooModels(baseUrl: string, apiKey?: string): Promise<Mo
supportsReasoningEffort,
requiredReasoningEffort,
supportsPromptCache: Boolean(cacheReadPrice !== undefined),
supportsNativeToolCalling,
inputPrice,
outputPrice,
cacheWritesPrice: cacheWritePrice,

View file

@ -1413,8 +1413,4 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
throw error
}
}
protected override hasNativeToolCapability(): boolean {
return true
}
}

View file

@ -427,11 +427,4 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
}
}
}
protected override hasNativeToolCapability(): boolean {
// TODO: Read this from OpenRouter model metadata instead of hardcoding
// For now, only enable for well-tested providers
const modelId = this.options.openRouterModelId ?? openRouterDefaultModelId
return modelId.startsWith("anthropic/") || modelId.startsWith("openai/")
}
}

View file

@ -262,12 +262,4 @@ export class RooHandler extends BaseOpenAiCompatibleProvider<string> {
},
}
}
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/")
}
}

View file

@ -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/")
}
}

View file

@ -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/")
}
}