From 3970acfd27ae0e7071a3a76b41c1e4fdc78cc30f Mon Sep 17 00:00:00 2001 From: daniel-lxs Date: Fri, 19 Dec 2025 11:26:31 -0500 Subject: [PATCH 1/2] fix: enable native tool calls for Requesty provider (ROO-235) - Add supportsNativeTools and defaultToolProtocol to Requesty model fetcher - Merge native tool defaults in RequestyHandler.getModel() for cached models - Apply same defaults merge in useSelectedModel for UI consistency - Also applied to Unbound provider for consistency The issue was that cached model data lacked the supportsNativeTools field, causing resolveToolProtocol() to return XML instead of native protocol. --- src/api/providers/fetchers/requesty.ts | 2 ++ src/api/providers/requesty.ts | 10 +++++++++- .../src/components/ui/hooks/useSelectedModel.ts | 16 ++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/api/providers/fetchers/requesty.ts b/src/api/providers/fetchers/requesty.ts index 64c7de66892..4623c47b250 100644 --- a/src/api/providers/fetchers/requesty.ts +++ b/src/api/providers/fetchers/requesty.ts @@ -38,6 +38,8 @@ export async function getRequestyModels(baseUrl?: string, apiKey?: string): Prom supportsImages: rawModel.supports_vision, supportsReasoningBudget: reasoningBudget, supportsReasoningEffort: reasoningEffort, + supportsNativeTools: true, + defaultToolProtocol: "native", inputPrice: parseApiPrice(rawModel.input_price), outputPrice: parseApiPrice(rawModel.output_price), description: rawModel.description, diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index b84a36bcc16..c120cd65ece 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -79,7 +79,15 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan override getModel() { const id = this.options.requestyModelId ?? requestyDefaultModelId - let info = this.models[id] ?? requestyDefaultModelInfo + const cachedInfo = this.models[id] ?? requestyDefaultModelInfo + + // Merge native tool defaults for cached models that may lack these fields + // The order ensures that cached values (if present) override the defaults + let info: ModelInfo = { + supportsNativeTools: true, + defaultToolProtocol: TOOL_PROTOCOL.NATIVE, + ...cachedInfo, + } // Apply tool preferences for models accessed through routers (OpenAI, Gemini) info = applyRouterToolPreferences(id, info) diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index eaf58530e97..55169393ad9 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -157,12 +157,24 @@ function getSelectedModel({ } case "requesty": { const id = getValidatedModelId(apiConfiguration.requestyModelId, routerModels.requesty, defaultModelId) - const info = routerModels.requesty?.[id] + const routerInfo = routerModels.requesty?.[id] + // Merge native tool call defaults for cached models that may lack these fields + const nativeToolDefaults = { + supportsNativeTools: true, + defaultToolProtocol: "native" as const, + } + const info = routerInfo ? { ...nativeToolDefaults, ...routerInfo } : undefined return { id, info } } case "unbound": { const id = getValidatedModelId(apiConfiguration.unboundModelId, routerModels.unbound, defaultModelId) - const info = routerModels.unbound?.[id] + const routerInfo = routerModels.unbound?.[id] + // Merge native tool call defaults for cached models that may lack these fields + const nativeToolDefaults = { + supportsNativeTools: true, + defaultToolProtocol: "native" as const, + } + const info = routerInfo ? { ...nativeToolDefaults, ...routerInfo } : undefined return { id, info } } case "litellm": { From 660365e46e9c933c246b84c245e15cf21be5dbe1 Mon Sep 17 00:00:00 2001 From: daniel-lxs Date: Fri, 19 Dec 2025 11:37:56 -0500 Subject: [PATCH 2/2] refactor: extract NATIVE_TOOL_DEFAULTS to shared constant - Add NATIVE_TOOL_DEFAULTS constant to @roo-code/types/tool.ts - Update RouterProvider.getModel() to merge defaults for all router providers (LiteLLM, Unbound) - Update RequestyHandler to use shared constant - Update useSelectedModel to use shared constant for requesty, unbound, litellm --- packages/types/src/tool.ts | 10 ++++++++ src/api/providers/requesty.ts | 14 ++++++----- src/api/providers/router-provider.ts | 7 +++--- .../components/ui/hooks/useSelectedModel.ts | 25 ++++++------------- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/types/src/tool.ts b/packages/types/src/tool.ts index 658ebf4450b..be3f49c40f2 100644 --- a/packages/types/src/tool.ts +++ b/packages/types/src/tool.ts @@ -71,6 +71,16 @@ export const TOOL_PROTOCOL = { */ export type ToolProtocol = (typeof TOOL_PROTOCOL)[keyof typeof TOOL_PROTOCOL] +/** + * Default model info properties for native tool support. + * Used to merge with cached model info that may lack these fields. + * Router providers (Requesty, Unbound, LiteLLM) assume all models support native tools. + */ +export const NATIVE_TOOL_DEFAULTS = { + supportsNativeTools: true, + defaultToolProtocol: TOOL_PROTOCOL.NATIVE, +} as const + /** * Checks if the protocol is native (non-XML). * diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index c120cd65ece..85efeb800f1 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -1,7 +1,13 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" -import { type ModelInfo, requestyDefaultModelId, requestyDefaultModelInfo, TOOL_PROTOCOL } from "@roo-code/types" +import { + type ModelInfo, + requestyDefaultModelId, + requestyDefaultModelInfo, + TOOL_PROTOCOL, + NATIVE_TOOL_DEFAULTS, +} from "@roo-code/types" import type { ApiHandlerOptions, ModelRecord } from "../../shared/api" import { resolveToolProtocol } from "../../utils/resolveToolProtocol" @@ -83,11 +89,7 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan // Merge native tool defaults for cached models that may lack these fields // The order ensures that cached values (if present) override the defaults - let info: ModelInfo = { - supportsNativeTools: true, - defaultToolProtocol: TOOL_PROTOCOL.NATIVE, - ...cachedInfo, - } + let info: ModelInfo = { ...NATIVE_TOOL_DEFAULTS, ...cachedInfo } // Apply tool preferences for models accessed through routers (OpenAI, Gemini) info = applyRouterToolPreferences(id, info) diff --git a/src/api/providers/router-provider.ts b/src/api/providers/router-provider.ts index e43f49aa2c2..01942e21723 100644 --- a/src/api/providers/router-provider.ts +++ b/src/api/providers/router-provider.ts @@ -1,6 +1,6 @@ import OpenAI from "openai" -import type { ModelInfo } from "@roo-code/types" +import { type ModelInfo, NATIVE_TOOL_DEFAULTS } from "@roo-code/types" import { ApiHandlerOptions, RouterName, ModelRecord } from "../../shared/api" @@ -64,8 +64,9 @@ export abstract class RouterProvider extends BaseProvider { const id = this.modelId ?? this.defaultModelId // First check instance models (populated by fetchModel) + // Merge native tool defaults for cached models that may lack these fields if (this.models[id]) { - return { id, info: this.models[id] } + return { id, info: { ...NATIVE_TOOL_DEFAULTS, ...this.models[id] } } } // Fall back to global cache (synchronous disk/memory cache) @@ -74,7 +75,7 @@ export abstract class RouterProvider extends BaseProvider { if (cachedModels?.[id]) { // Also populate instance models for future calls this.models = cachedModels - return { id, info: cachedModels[id] } + return { id, info: { ...NATIVE_TOOL_DEFAULTS, ...cachedModels[id] } } } // Last resort: return default model diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 55169393ad9..f5f71156396 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -33,6 +33,7 @@ import { BEDROCK_1M_CONTEXT_MODEL_IDS, isDynamicProvider, getProviderDefaultModelId, + NATIVE_TOOL_DEFAULTS, } from "@roo-code/types" import type { ModelRecord, RouterModels } from "@roo/api" @@ -158,34 +159,22 @@ function getSelectedModel({ case "requesty": { const id = getValidatedModelId(apiConfiguration.requestyModelId, routerModels.requesty, defaultModelId) const routerInfo = routerModels.requesty?.[id] - // Merge native tool call defaults for cached models that may lack these fields - const nativeToolDefaults = { - supportsNativeTools: true, - defaultToolProtocol: "native" as const, - } - const info = routerInfo ? { ...nativeToolDefaults, ...routerInfo } : undefined + // Merge native tool defaults for cached models that may lack these fields + const info = routerInfo ? { ...NATIVE_TOOL_DEFAULTS, ...routerInfo } : undefined return { id, info } } case "unbound": { const id = getValidatedModelId(apiConfiguration.unboundModelId, routerModels.unbound, defaultModelId) const routerInfo = routerModels.unbound?.[id] - // Merge native tool call defaults for cached models that may lack these fields - const nativeToolDefaults = { - supportsNativeTools: true, - defaultToolProtocol: "native" as const, - } - const info = routerInfo ? { ...nativeToolDefaults, ...routerInfo } : undefined + // Merge native tool defaults for cached models that may lack these fields + const info = routerInfo ? { ...NATIVE_TOOL_DEFAULTS, ...routerInfo } : undefined return { id, info } } case "litellm": { const id = getValidatedModelId(apiConfiguration.litellmModelId, routerModels.litellm, defaultModelId) const routerInfo = routerModels.litellm?.[id] - // Only merge native tool call defaults, not prices or other model-specific info - const nativeToolDefaults = { - supportsNativeTools: litellmDefaultModelInfo.supportsNativeTools, - defaultToolProtocol: litellmDefaultModelInfo.defaultToolProtocol, - } - const info = routerInfo ? { ...nativeToolDefaults, ...routerInfo } : litellmDefaultModelInfo + // Merge native tool defaults for cached models that may lack these fields + const info = routerInfo ? { ...NATIVE_TOOL_DEFAULTS, ...routerInfo } : litellmDefaultModelInfo return { id, info } } case "xai": {