Skip to content

Commit 6afc59b

Browse files
fix(openai): send native tool definitions by default (#10314)
Co-authored-by: Roo Code <[email protected]>
1 parent dfa7c35 commit 6afc59b

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import OpenAI from "openai"
2+
3+
import { OpenAiHandler } from "../openai"
4+
5+
describe("OpenAiHandler native tools", () => {
6+
it("includes tools in request when custom model info lacks supportsNativeTools (regression test)", async () => {
7+
const mockCreate = vi.fn().mockImplementationOnce(() => ({
8+
[Symbol.asyncIterator]: async function* () {
9+
yield {
10+
choices: [{ delta: { content: "Test response" } }],
11+
}
12+
},
13+
}))
14+
15+
// Set openAiCustomModelInfo WITHOUT supportsNativeTools to simulate
16+
// a user-provided custom model info that doesn't specify native tool support.
17+
// The getModel() fix should merge NATIVE_TOOL_DEFAULTS to ensure
18+
// supportsNativeTools defaults to true.
19+
const handler = new OpenAiHandler({
20+
openAiApiKey: "test-key",
21+
openAiBaseUrl: "https://example.com/v1",
22+
openAiModelId: "test-model",
23+
openAiCustomModelInfo: {
24+
maxTokens: 4096,
25+
contextWindow: 128000,
26+
},
27+
} as unknown as import("../../../shared/api").ApiHandlerOptions)
28+
29+
// Patch the OpenAI client call
30+
const mockClient = {
31+
chat: {
32+
completions: {
33+
create: mockCreate,
34+
},
35+
},
36+
} as unknown as OpenAI
37+
;(handler as unknown as { client: OpenAI }).client = mockClient
38+
39+
const tools: OpenAI.Chat.ChatCompletionTool[] = [
40+
{
41+
type: "function",
42+
function: {
43+
name: "test_tool",
44+
description: "test",
45+
parameters: { type: "object", properties: {} },
46+
},
47+
},
48+
]
49+
50+
// Mimic the behavior in Task.attemptApiRequest() where tools are only
51+
// included when modelInfo.supportsNativeTools is true. This is the
52+
// actual regression path being tested - without the getModel() fix,
53+
// supportsNativeTools would be undefined and tools wouldn't be passed.
54+
const modelInfo = handler.getModel().info
55+
const supportsNativeTools = modelInfo.supportsNativeTools ?? false
56+
57+
const stream = handler.createMessage("system", [], {
58+
taskId: "test-task-id",
59+
...(supportsNativeTools && { tools }),
60+
...(supportsNativeTools && { toolProtocol: "native" as const }),
61+
})
62+
await stream.next()
63+
64+
expect(mockCreate).toHaveBeenCalledWith(
65+
expect.objectContaining({
66+
tools: expect.arrayContaining([
67+
expect.objectContaining({
68+
type: "function",
69+
function: expect.objectContaining({ name: "test_tool" }),
70+
}),
71+
]),
72+
parallel_tool_calls: false,
73+
}),
74+
expect.anything(),
75+
)
76+
})
77+
})

src/api/providers/openai.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type ModelInfo,
77
azureOpenAiDefaultApiVersion,
88
openAiModelInfoSaneDefaults,
9+
NATIVE_TOOL_DEFAULTS,
910
DEEP_SEEK_DEFAULT_TEMPERATURE,
1011
OPENAI_AZURE_AI_INFERENCE_PATH,
1112
} from "@roo-code/types"
@@ -291,7 +292,13 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl
291292

292293
override getModel() {
293294
const id = this.options.openAiModelId ?? ""
294-
const info = this.options.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults
295+
// Ensure OpenAI-compatible models default to supporting native tool calling.
296+
// This is required for [`Task.attemptApiRequest()`](src/core/task/Task.ts:3817) to
297+
// include tool definitions in the request.
298+
const info: ModelInfo = {
299+
...NATIVE_TOOL_DEFAULTS,
300+
...(this.options.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults),
301+
}
295302
const params = getModelParams({ format: "openai", modelId: id, model: info, settings: this.options })
296303
return { id, info, ...params }
297304
}

0 commit comments

Comments
 (0)