Skip to content

Commit 397328c

Browse files
feat: merge native tool defaults for openai-compatible provider (#10213)
1 parent bb358fb commit 397328c

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.spec.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
55
import { renderHook } from "@testing-library/react"
66
import type { Mock } from "vitest"
77

8-
import { ProviderSettings, ModelInfo, BEDROCK_1M_CONTEXT_MODEL_IDS, litellmDefaultModelInfo } from "@roo-code/types"
8+
import {
9+
ProviderSettings,
10+
ModelInfo,
11+
BEDROCK_1M_CONTEXT_MODEL_IDS,
12+
litellmDefaultModelInfo,
13+
openAiModelInfoSaneDefaults,
14+
} from "@roo-code/types"
915

1016
import { useSelectedModel } from "../useSelectedModel"
1117
import { useRouterModels } from "../useRouterModels"
@@ -661,4 +667,100 @@ describe("useSelectedModel", () => {
661667
expect(result.current.info?.defaultToolProtocol).toBe("native")
662668
})
663669
})
670+
671+
describe("openai provider", () => {
672+
beforeEach(() => {
673+
mockUseRouterModels.mockReturnValue({
674+
data: {
675+
openrouter: {},
676+
requesty: {},
677+
unbound: {},
678+
litellm: {},
679+
"io-intelligence": {},
680+
},
681+
isLoading: false,
682+
isError: false,
683+
} as any)
684+
685+
mockUseOpenRouterModelProviders.mockReturnValue({
686+
data: {},
687+
isLoading: false,
688+
isError: false,
689+
} as any)
690+
})
691+
692+
it("should use openAiModelInfoSaneDefaults when no custom model info is provided", () => {
693+
const apiConfiguration: ProviderSettings = {
694+
apiProvider: "openai",
695+
openAiModelId: "gpt-4o",
696+
}
697+
698+
const wrapper = createWrapper()
699+
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
700+
701+
expect(result.current.provider).toBe("openai")
702+
expect(result.current.id).toBe("gpt-4o")
703+
expect(result.current.info).toEqual(openAiModelInfoSaneDefaults)
704+
expect(result.current.info?.supportsNativeTools).toBe(true)
705+
expect(result.current.info?.defaultToolProtocol).toBe("native")
706+
})
707+
708+
it("should merge native tool defaults with custom model info", () => {
709+
const customModelInfo: ModelInfo = {
710+
maxTokens: 16384,
711+
contextWindow: 128000,
712+
supportsImages: true,
713+
supportsPromptCache: false,
714+
inputPrice: 0.01,
715+
outputPrice: 0.03,
716+
description: "Custom OpenAI-compatible model",
717+
}
718+
719+
const apiConfiguration: ProviderSettings = {
720+
apiProvider: "openai",
721+
openAiModelId: "custom-model",
722+
openAiCustomModelInfo: customModelInfo,
723+
}
724+
725+
const wrapper = createWrapper()
726+
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
727+
728+
expect(result.current.provider).toBe("openai")
729+
expect(result.current.id).toBe("custom-model")
730+
// Should merge native tool defaults with custom model info
731+
const nativeToolDefaults = {
732+
supportsNativeTools: openAiModelInfoSaneDefaults.supportsNativeTools,
733+
defaultToolProtocol: openAiModelInfoSaneDefaults.defaultToolProtocol,
734+
}
735+
expect(result.current.info).toEqual({ ...nativeToolDefaults, ...customModelInfo })
736+
expect(result.current.info?.supportsNativeTools).toBe(true)
737+
expect(result.current.info?.defaultToolProtocol).toBe("native")
738+
})
739+
740+
it("should allow custom model info to override native tool defaults", () => {
741+
const customModelInfo: ModelInfo = {
742+
maxTokens: 8192,
743+
contextWindow: 32000,
744+
supportsImages: false,
745+
supportsPromptCache: false,
746+
supportsNativeTools: false, // Explicitly disable
747+
defaultToolProtocol: "xml", // Override default to use XML instead of native
748+
}
749+
750+
const apiConfiguration: ProviderSettings = {
751+
apiProvider: "openai",
752+
openAiModelId: "custom-model-no-tools",
753+
openAiCustomModelInfo: customModelInfo,
754+
}
755+
756+
const wrapper = createWrapper()
757+
const { result } = renderHook(() => useSelectedModel(apiConfiguration), { wrapper })
758+
759+
expect(result.current.provider).toBe("openai")
760+
expect(result.current.id).toBe("custom-model-no-tools")
761+
// Custom model info should override the native tool defaults
762+
expect(result.current.info?.supportsNativeTools).toBe(false)
763+
expect(result.current.info?.defaultToolProtocol).toBe("xml")
764+
})
765+
})
664766
})

webview-ui/src/components/ui/hooks/useSelectedModel.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,13 @@ function getSelectedModel({
280280
}
281281
case "openai": {
282282
const id = apiConfiguration.openAiModelId ?? ""
283-
const info = apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults
283+
const customInfo = apiConfiguration?.openAiCustomModelInfo
284+
// Only merge native tool call defaults, not prices or other model-specific info
285+
const nativeToolDefaults = {
286+
supportsNativeTools: openAiModelInfoSaneDefaults.supportsNativeTools,
287+
defaultToolProtocol: openAiModelInfoSaneDefaults.defaultToolProtocol,
288+
}
289+
const info = customInfo ? { ...nativeToolDefaults, ...customInfo } : openAiModelInfoSaneDefaults
284290
return { id, info }
285291
}
286292
case "ollama": {

0 commit comments

Comments
 (0)