Skip to content

Commit cb9abcf

Browse files
authored
Add native tool definitions (#9156)
1 parent e8ac3bf commit cb9abcf

25 files changed

+1038
-0
lines changed

src/core/prompts/tools/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,6 @@ export {
180180
getRunSlashCommandDescription,
181181
getGenerateImageDescription,
182182
}
183+
184+
// Export native tool definitions (JSON schema format for OpenAI-compatible APIs)
185+
export { nativeTools } from "./native-tools"
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { describe, it, expect } from "vitest"
2+
import type OpenAI from "openai"
3+
import type Anthropic from "@anthropic-ai/sdk"
4+
import { convertOpenAIToolToAnthropic, convertOpenAIToolsToAnthropic } from "../converters"
5+
6+
describe("converters", () => {
7+
describe("convertOpenAIToolToAnthropic", () => {
8+
it("should convert a simple OpenAI tool to Anthropic format", () => {
9+
const openAITool: OpenAI.Chat.ChatCompletionTool = {
10+
type: "function",
11+
function: {
12+
name: "get_weather",
13+
description: "Get the current weather in a location",
14+
parameters: {
15+
type: "object",
16+
properties: {
17+
location: {
18+
type: "string",
19+
description: "The city and state",
20+
},
21+
},
22+
required: ["location"],
23+
},
24+
},
25+
}
26+
27+
const result = convertOpenAIToolToAnthropic(openAITool)
28+
29+
expect(result).toEqual({
30+
name: "get_weather",
31+
description: "Get the current weather in a location",
32+
input_schema: {
33+
type: "object",
34+
properties: {
35+
location: {
36+
type: "string",
37+
description: "The city and state",
38+
},
39+
},
40+
required: ["location"],
41+
},
42+
})
43+
})
44+
45+
it("should handle tools with empty description", () => {
46+
const openAITool: OpenAI.Chat.ChatCompletionTool = {
47+
type: "function",
48+
function: {
49+
name: "test_tool",
50+
parameters: {
51+
type: "object",
52+
properties: {},
53+
},
54+
},
55+
}
56+
57+
const result = convertOpenAIToolToAnthropic(openAITool)
58+
59+
expect(result.name).toBe("test_tool")
60+
expect(result.description).toBe("")
61+
expect(result.input_schema).toEqual({
62+
type: "object",
63+
properties: {},
64+
})
65+
})
66+
67+
it("should throw error for non-function tool types", () => {
68+
const customTool = {
69+
type: "custom" as const,
70+
} as OpenAI.Chat.ChatCompletionTool
71+
72+
expect(() => convertOpenAIToolToAnthropic(customTool)).toThrow("Unsupported tool type: custom")
73+
})
74+
75+
it("should preserve complex parameter schemas", () => {
76+
const openAITool: OpenAI.Chat.ChatCompletionTool = {
77+
type: "function",
78+
function: {
79+
name: "read_file",
80+
description: "Read files",
81+
parameters: {
82+
type: "object",
83+
properties: {
84+
files: {
85+
type: "array",
86+
items: {
87+
type: "object",
88+
properties: {
89+
path: { type: "string" },
90+
line_ranges: {
91+
type: ["array", "null"],
92+
items: { type: "string", pattern: "^[0-9]+-[0-9]+$" },
93+
},
94+
},
95+
required: ["path", "line_ranges"],
96+
},
97+
},
98+
},
99+
required: ["files"],
100+
additionalProperties: false,
101+
},
102+
},
103+
}
104+
105+
const result = convertOpenAIToolToAnthropic(openAITool)
106+
107+
expect(result.input_schema).toEqual(openAITool.function.parameters)
108+
})
109+
})
110+
111+
describe("convertOpenAIToolsToAnthropic", () => {
112+
it("should convert multiple tools", () => {
113+
const openAITools: OpenAI.Chat.ChatCompletionTool[] = [
114+
{
115+
type: "function",
116+
function: {
117+
name: "tool1",
118+
description: "First tool",
119+
parameters: { type: "object", properties: {} },
120+
},
121+
},
122+
{
123+
type: "function",
124+
function: {
125+
name: "tool2",
126+
description: "Second tool",
127+
parameters: { type: "object", properties: {} },
128+
},
129+
},
130+
]
131+
132+
const results = convertOpenAIToolsToAnthropic(openAITools)
133+
134+
expect(results).toHaveLength(2)
135+
expect(results[0].name).toBe("tool1")
136+
expect(results[1].name).toBe("tool2")
137+
})
138+
139+
it("should handle empty array", () => {
140+
const results = convertOpenAIToolsToAnthropic([])
141+
expect(results).toEqual([])
142+
})
143+
})
144+
})
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import type OpenAI from "openai"
2+
3+
export const apply_diff_single_file = {
4+
type: "function",
5+
function: {
6+
name: "apply_diff",
7+
description: `
8+
Apply precise, targeted modifications to an existing file using one or more search/replace blocks. This tool is for surgical edits only; the 'SEARCH' block must exactly match the existing content, including whitespace and indentation. To make multiple targeted changes, provide multiple SEARCH/REPLACE blocks in the 'diff' parameter. Use the 'read_file' tool first if you are not confident in the exact content to search for.
9+
`,
10+
parameters: {
11+
type: "object",
12+
properties: {
13+
path: {
14+
type: "string",
15+
description: "The path of the file to modify, relative to the current workspace directory.",
16+
},
17+
diff: {
18+
type: "string",
19+
description: `
20+
A string containing one or more search/replace blocks defining the changes. The ':start_line:' is required and indicates the starting line number of the original content. You must not add a start line for the replacement content. Each block must follow this format:
21+
<<<<<<< SEARCH
22+
:start_line:[line_number]
23+
-------
24+
[exact content to find]
25+
=======
26+
[new content to replace with]
27+
>>>>>>> REPLACE
28+
`,
29+
},
30+
},
31+
required: ["path", "diff"],
32+
additionalProperties: false,
33+
},
34+
},
35+
} satisfies OpenAI.Chat.ChatCompletionTool
36+
37+
//@ts-ignore Preparing for when we enable multi-file diffs
38+
export const apply_diff_multi_file = {
39+
type: "function",
40+
function: {
41+
name: "apply_diff",
42+
description:
43+
"Apply precise, targeted modifications to one or more files by searching for specific sections of content and replacing them. This tool is for surgical edits only and supports making changes across multiple files in a single request. The 'SEARCH' block must exactly match the existing content, including whitespace and indentation. You must use this tool to edit multiple files in a single operation whenever possible.",
44+
parameters: {
45+
type: "object",
46+
properties: {
47+
files: {
48+
type: "array",
49+
description: "A list of file modification operations to perform.",
50+
items: {
51+
type: "object",
52+
properties: {
53+
path: {
54+
type: "string",
55+
description:
56+
"The path of the file to modify, relative to the current workspace directory.",
57+
},
58+
diffs: {
59+
type: "array",
60+
description:
61+
"A list of diffs to apply to the file. Each diff is a distinct search/replace operation.",
62+
items: {
63+
type: "object",
64+
properties: {
65+
content: {
66+
type: "string",
67+
description: `
68+
The search/replace block defining the changes. The SEARCH block must exactly match the content to be replaced. Format:
69+
'<<<<<<< SEARCH
70+
[content_to_find]
71+
=======
72+
[content_to_replace_with]
73+
>>>>>>> REPLACE
74+
`,
75+
},
76+
start_line: {
77+
type: "integer",
78+
description:
79+
"The line number in the original file where the SEARCH block begins.",
80+
},
81+
},
82+
required: ["content", "start_line"],
83+
},
84+
},
85+
},
86+
required: ["path", "diffs"],
87+
},
88+
},
89+
},
90+
required: ["files"],
91+
},
92+
},
93+
} satisfies OpenAI.Chat.ChatCompletionTool
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type OpenAI from "openai"
2+
3+
export default {
4+
type: "function",
5+
function: {
6+
name: "ask_followup_question",
7+
description:
8+
"Ask the user a question to gather additional information needed to complete the task. Use when clarification or more detail is required before proceeding.",
9+
strict: true,
10+
parameters: {
11+
type: "object",
12+
properties: {
13+
question: {
14+
type: "string",
15+
description: "Clear, specific question that captures the missing information you need",
16+
},
17+
follow_up: {
18+
type: "array",
19+
description:
20+
"Required list of 2-4 suggested responses; each suggestion must be a complete, actionable answer and may include a mode switch",
21+
items: {
22+
type: "object",
23+
properties: {
24+
text: {
25+
type: "string",
26+
description: "Suggested answer the user can pick",
27+
},
28+
mode: {
29+
type: ["string", "null"],
30+
description:
31+
"Optional mode slug to switch to if this suggestion is chosen (e.g., code, architect)",
32+
},
33+
},
34+
required: ["text", "mode"],
35+
additionalProperties: false,
36+
},
37+
minItems: 2,
38+
maxItems: 4,
39+
},
40+
},
41+
required: ["question", "follow_up"],
42+
additionalProperties: false,
43+
},
44+
},
45+
} satisfies OpenAI.Chat.ChatCompletionTool
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type OpenAI from "openai"
2+
3+
export default {
4+
type: "function",
5+
function: {
6+
name: "attempt_completion",
7+
description:
8+
"After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again. IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must confirm that you've received successful results from the user for any previous tool uses. If not, then DO NOT use this tool.",
9+
strict: true,
10+
parameters: {
11+
type: "object",
12+
properties: {
13+
result: {
14+
type: "string",
15+
description: "Final result message to deliver to the user once the task is complete",
16+
},
17+
},
18+
required: ["result"],
19+
additionalProperties: false,
20+
},
21+
},
22+
} satisfies OpenAI.Chat.ChatCompletionTool
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type OpenAI from "openai"
2+
3+
export default {
4+
type: "function",
5+
function: {
6+
name: "browser_action",
7+
description:
8+
"Interact with a Puppeteer-controlled browser session. Always start by launching at a URL and always finish by closing the browser. While the browser is active, do not call any other tools. Use coordinates within the viewport to hover or click, provide text for typing, and ensure actions are grounded in the latest screenshot and console logs.",
9+
strict: true,
10+
parameters: {
11+
type: "object",
12+
properties: {
13+
action: {
14+
type: "string",
15+
description: "Browser action to perform",
16+
enum: ["launch", "hover", "click", "type", "resize", "scroll_down", "scroll_up", "close"],
17+
},
18+
url: {
19+
type: ["string", "null"],
20+
description: "URL to open when performing the launch action; must include protocol",
21+
},
22+
coordinate: {
23+
type: ["object", "null"],
24+
description:
25+
"Screen coordinate for hover or click actions; target the center of the desired element",
26+
properties: {
27+
x: {
28+
type: "number",
29+
description: "Horizontal pixel position within the current viewport",
30+
},
31+
y: {
32+
type: "number",
33+
description: "Vertical pixel position within the current viewport",
34+
},
35+
},
36+
required: ["x", "y"],
37+
additionalProperties: false,
38+
},
39+
size: {
40+
type: ["object", "null"],
41+
description: "Viewport dimensions to apply when performing the resize action",
42+
properties: {
43+
width: {
44+
type: "number",
45+
description: "Viewport width in pixels",
46+
},
47+
height: {
48+
type: "number",
49+
description: "Viewport height in pixels",
50+
},
51+
},
52+
required: ["width", "height"],
53+
additionalProperties: false,
54+
},
55+
text: {
56+
type: ["string", "null"],
57+
description: "Text to type when performing the type action",
58+
},
59+
},
60+
required: ["action", "url", "coordinate", "size", "text"],
61+
additionalProperties: false,
62+
},
63+
},
64+
} satisfies OpenAI.Chat.ChatCompletionTool

0 commit comments

Comments
 (0)