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/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..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" @@ -79,7 +85,11 @@ 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 = { ...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 eaf58530e97..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" @@ -157,23 +158,23 @@ 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 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 info = routerModels.unbound?.[id] + const routerInfo = routerModels.unbound?.[id] + // 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": {