mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
feat: present codex edit tool as apply_patch
This commit is contained in:
parent
d2f5dfd24c
commit
0d5bd7ac4e
4 changed files with 139 additions and 19 deletions
|
|
@ -7,6 +7,26 @@ describe("NativeToolCallParser", () => {
|
|||
})
|
||||
|
||||
describe("parseToolCall", () => {
|
||||
it("should resolve apply_patch to edit_file and preserve originalName", () => {
|
||||
const toolCall = {
|
||||
id: "toolu_apply_patch_123",
|
||||
name: "apply_patch" as any,
|
||||
arguments: JSON.stringify({
|
||||
patch: "*** Begin Patch\n*** End Patch",
|
||||
}),
|
||||
}
|
||||
|
||||
const result = NativeToolCallParser.parseToolCall(toolCall)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.type).toBe("tool_use")
|
||||
if (result?.type === "tool_use") {
|
||||
expect(result.name).toBe("edit_file")
|
||||
expect(result.originalName).toBe("apply_patch")
|
||||
expect(result.nativeArgs).toEqual({ patch: "*** Begin Patch\n*** End Patch" })
|
||||
}
|
||||
})
|
||||
|
||||
describe("read_file tool", () => {
|
||||
it("should handle line_ranges as tuples (new format)", () => {
|
||||
const toolCall = {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import * as toolsModule from "../../../../shared/tools"
|
|||
// and replaced with the unified "edit_file" tool.
|
||||
|
||||
describe("filterNativeToolsForMode", () => {
|
||||
// Use edit_file_roo as the default edit tool variant in mock tools
|
||||
// Include all edit tool variants to mirror the production native tools array.
|
||||
const mockNativeTools: OpenAI.Chat.ChatCompletionTool[] = [
|
||||
{
|
||||
type: "function",
|
||||
|
|
@ -37,6 +37,38 @@ describe("filterNativeToolsForMode", () => {
|
|||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_anthropic",
|
||||
description: "Edit file (Anthropic variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_grok",
|
||||
description: "Edit file (Grok variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_gemini",
|
||||
description: "Edit file (Gemini variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_codex",
|
||||
description: "Edit file (Codex variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
|
|
@ -123,10 +155,11 @@ describe("filterNativeToolsForMode", () => {
|
|||
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
|
||||
|
||||
// Should include all tools (code mode has all groups)
|
||||
// Note: edit_file_roo gets renamed to edit_file
|
||||
// Note: edit tool variants get renamed to a single display name.
|
||||
// Default is "edit_file" (roo variant), but codex can be presented as "apply_patch".
|
||||
expect(toolNames).toContain("read_file")
|
||||
expect(toolNames).toContain("write_to_file")
|
||||
expect(toolNames).toContain("edit_file") // Unified edit tool
|
||||
expect(toolNames).toContain("edit_file") // Unified edit tool (default)
|
||||
expect(toolNames).not.toContain("edit_file_roo") // Variant name should be renamed
|
||||
expect(toolNames).toContain("execute_command")
|
||||
expect(toolNames).toContain("browser_action")
|
||||
|
|
@ -134,6 +167,29 @@ describe("filterNativeToolsForMode", () => {
|
|||
expect(toolNames).toContain("attempt_completion")
|
||||
})
|
||||
|
||||
it("should present codex edit tool variant as apply_patch when editToolVariant is codex", () => {
|
||||
const codeMode: ModeConfig = {
|
||||
slug: "code",
|
||||
name: "Code",
|
||||
roleDefinition: "Test",
|
||||
groups: ["read", "edit", "browser", "command", "mcp"] as const,
|
||||
}
|
||||
|
||||
const filtered = filterNativeToolsForMode(mockNativeTools, "code", [codeMode], {}, undefined, {
|
||||
modelInfo: {
|
||||
contextWindow: 100000,
|
||||
supportsPromptCache: false,
|
||||
editToolVariant: "codex",
|
||||
},
|
||||
})
|
||||
|
||||
const toolNames = filtered.map((t) => ("function" in t ? t.function.name : ""))
|
||||
|
||||
expect(toolNames).toContain("apply_patch")
|
||||
expect(toolNames).not.toContain("edit_file")
|
||||
expect(toolNames).not.toContain("edit_file_codex")
|
||||
})
|
||||
|
||||
it("should always include always-available tools regardless of mode groups", () => {
|
||||
const restrictiveMode: ModeConfig = {
|
||||
slug: "restrictive",
|
||||
|
|
@ -718,6 +774,38 @@ describe("filterMcpToolsForMode", () => {
|
|||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_anthropic",
|
||||
description: "Edit file (Anthropic variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_grok",
|
||||
description: "Edit file (Grok variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_gemini",
|
||||
description: "Edit file (Gemini variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "edit_file_codex",
|
||||
description: "Edit file (Codex variant)",
|
||||
parameters: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
|
|
|
|||
|
|
@ -8,22 +8,30 @@ import type { McpHub } from "../../../services/mcp/McpHub"
|
|||
import { isToolAllowedForMode } from "../../../core/tools/validateToolUse"
|
||||
|
||||
/**
|
||||
* Mapping from edit tool variant to internal tool name.
|
||||
* These are the tools that will be selected based on modelInfo.editToolVariant.
|
||||
* Central edit tool configuration.
|
||||
*
|
||||
* `toolName` is the internal native tool name.
|
||||
* `displayName` is the name shown to (and called by) the model.
|
||||
*
|
||||
* By default, edit tool variants are presented as "edit_file".
|
||||
* The codex variant is presented as "apply_patch" so:
|
||||
* - the model calls `apply_patch`
|
||||
* - the tool is stored in API conversation history as `apply_patch`
|
||||
* - execution is still routed through the canonical `edit_file` tool via aliases
|
||||
*/
|
||||
const EDIT_TOOL_VARIANT_MAP: Record<EditToolVariant, string> = {
|
||||
roo: "edit_file_roo",
|
||||
anthropic: "edit_file_anthropic",
|
||||
grok: "edit_file_grok",
|
||||
gemini: "edit_file_gemini",
|
||||
codex: "edit_file_codex",
|
||||
const EDIT_TOOL_VARIANTS: Record<EditToolVariant, { toolName: string; displayName: string }> = {
|
||||
roo: { toolName: "edit_file_roo", displayName: "edit_file" },
|
||||
anthropic: { toolName: "edit_file_anthropic", displayName: "edit_file" },
|
||||
grok: { toolName: "edit_file_grok", displayName: "edit_file" },
|
||||
gemini: { toolName: "edit_file_gemini", displayName: "edit_file" },
|
||||
codex: { toolName: "edit_file_codex", displayName: "apply_patch" },
|
||||
}
|
||||
|
||||
/**
|
||||
* All edit tool variant names that should be filtered.
|
||||
* Only one of these (based on editToolVariant) will be included and renamed to "edit_file".
|
||||
* Only one of these (based on editToolVariant) will be included and renamed to the configured display name.
|
||||
*/
|
||||
const ALL_EDIT_TOOL_VARIANTS = new Set(Object.values(EDIT_TOOL_VARIANT_MAP))
|
||||
const ALL_EDIT_TOOL_VARIANTS = new Set(Object.values(EDIT_TOOL_VARIANTS).map((variant) => variant.toolName))
|
||||
|
||||
/**
|
||||
* Reverse lookup map - maps alias name to canonical tool name.
|
||||
|
|
@ -326,7 +334,8 @@ export function filterNativeToolsForMode(
|
|||
|
||||
// Determine which edit tool variant to use (default: "roo")
|
||||
const editToolVariant: EditToolVariant = modelInfo?.editToolVariant ?? "roo"
|
||||
const selectedEditToolName = EDIT_TOOL_VARIANT_MAP[editToolVariant]
|
||||
const selectedEditToolName = EDIT_TOOL_VARIANTS[editToolVariant].toolName
|
||||
const editToolDisplayName = EDIT_TOOL_VARIANTS[editToolVariant].displayName
|
||||
|
||||
// Check if diffs are disabled - if so, skip edit tool entirely
|
||||
const diffEnabled = settings?.diffEnabled !== false
|
||||
|
|
@ -358,8 +367,9 @@ export function filterNativeToolsForMode(
|
|||
// 3. Mode has "edit" group
|
||||
// 4. edit_file is not excluded by model config
|
||||
if (toolName === selectedEditToolName && diffEnabled && modeHasEditGroup && !isEditFileExcluded) {
|
||||
// Rename the selected variant to "edit_file" so LLM always sees that name
|
||||
filteredTools.push(getOrCreateRenamedTool(tool, "edit_file"))
|
||||
// Rename the selected variant to the configured display name.
|
||||
// The tool will still execute via the unified `edit_file` tool name through aliases.
|
||||
filteredTools.push(getOrCreateRenamedTool(tool, editToolDisplayName))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -339,12 +339,14 @@ export const ALWAYS_AVAILABLE_TOOLS: ToolName[] = [
|
|||
* When a model calls a tool by its alias, the system resolves it to the canonical name for execution,
|
||||
* but preserves the alias in API conversation history for consistency.
|
||||
*
|
||||
* Note: Legacy edit tool aliases (apply_diff, search_and_replace, search_replace, apply_patch)
|
||||
* have been removed. Native protocol now uses editToolVariant to select the edit tool schema,
|
||||
* and all edit tools are presented to the LLM as "edit_file". XML protocol still uses apply_diff directly.
|
||||
* Note: Legacy edit tool aliases (apply_diff, search_and_replace, search_replace)
|
||||
* have been removed. Native protocol uses editToolVariant to select the edit tool schema.
|
||||
* The codex variant can optionally be presented to the LLM as "apply_patch".
|
||||
* XML protocol still uses apply_diff directly.
|
||||
*/
|
||||
export const TOOL_ALIASES: Record<string, ToolName> = {
|
||||
write_file: "write_to_file",
|
||||
apply_patch: "edit_file",
|
||||
} as const
|
||||
|
||||
export type DiffResult =
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue