mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Filter tools
This commit is contained in:
parent
a840ec4979
commit
2749c6e9bd
3 changed files with 278 additions and 26 deletions
218
src/core/prompts/__tests__/system-native-tools.spec.ts
Normal file
218
src/core/prompts/__tests__/system-native-tools.spec.ts
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest"
|
||||
import { SYSTEM_PROMPT } from "../system"
|
||||
import { CodeIndexManager } from "../../../services/code-index/manager"
|
||||
import type { SystemPromptSettings } from "../types"
|
||||
|
||||
vi.mock("../../../services/code-index/manager")
|
||||
vi.mock("../../../utils/storage", () => ({
|
||||
getSettingsDirectoryPath: vi.fn().mockResolvedValue("/test/settings"),
|
||||
}))
|
||||
vi.mock("../../../utils/globalContext", () => ({
|
||||
ensureSettingsDirectoryExists: vi.fn().mockResolvedValue("/test/settings"),
|
||||
}))
|
||||
|
||||
describe("SYSTEM_PROMPT with native tools", () => {
|
||||
const mockContext = {
|
||||
extensionUri: { fsPath: "/test/path" },
|
||||
globalStorageUri: { fsPath: "/test/global-storage" },
|
||||
globalState: {
|
||||
get: vi.fn(),
|
||||
update: vi.fn(),
|
||||
},
|
||||
workspaceState: {
|
||||
get: vi.fn(),
|
||||
update: vi.fn(),
|
||||
},
|
||||
} as any
|
||||
|
||||
const defaultSettings: SystemPromptSettings = {
|
||||
maxConcurrentFileReads: 5,
|
||||
todoListEnabled: true,
|
||||
useAgentRules: true,
|
||||
newTaskRequireTodos: true,
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should filter out update_todo_list when todoListEnabled is false", async () => {
|
||||
const mockCodeIndexManager = {
|
||||
isFeatureEnabled: true,
|
||||
isFeatureConfigured: true,
|
||||
isInitialized: true,
|
||||
}
|
||||
vi.mocked(CodeIndexManager.getInstance).mockReturnValue(mockCodeIndexManager as any)
|
||||
|
||||
const result = await SYSTEM_PROMPT(
|
||||
mockContext,
|
||||
"/test/cwd",
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
"code",
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
{ ...defaultSettings, todoListEnabled: false },
|
||||
undefined,
|
||||
undefined,
|
||||
true, // useNativeTools
|
||||
)
|
||||
|
||||
expect(result.tools).toBeDefined()
|
||||
const toolNames = result.tools?.map((t) => t.name) || []
|
||||
expect(toolNames).not.toContain("update_todo_list")
|
||||
})
|
||||
|
||||
it("should include update_todo_list when todoListEnabled is true", async () => {
|
||||
const mockCodeIndexManager = {
|
||||
isFeatureEnabled: true,
|
||||
isFeatureConfigured: true,
|
||||
isInitialized: true,
|
||||
}
|
||||
vi.mocked(CodeIndexManager.getInstance).mockReturnValue(mockCodeIndexManager as any)
|
||||
|
||||
const result = await SYSTEM_PROMPT(
|
||||
mockContext,
|
||||
"/test/cwd",
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
"code",
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
{ ...defaultSettings, todoListEnabled: true },
|
||||
undefined,
|
||||
undefined,
|
||||
true, // useNativeTools
|
||||
)
|
||||
|
||||
expect(result.tools).toBeDefined()
|
||||
const toolNames = result.tools?.map((t) => t.name) || []
|
||||
expect(toolNames).toContain("update_todo_list")
|
||||
})
|
||||
|
||||
it("should filter out codebase_search when feature is not configured", async () => {
|
||||
const mockCodeIndexManager = {
|
||||
isFeatureEnabled: false,
|
||||
isFeatureConfigured: false,
|
||||
isInitialized: false,
|
||||
}
|
||||
vi.mocked(CodeIndexManager.getInstance).mockReturnValue(mockCodeIndexManager as any)
|
||||
|
||||
const result = await SYSTEM_PROMPT(
|
||||
mockContext,
|
||||
"/test/cwd",
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
"code",
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true, // useNativeTools
|
||||
)
|
||||
|
||||
expect(result.tools).toBeDefined()
|
||||
const toolNames = result.tools?.map((t) => t.name) || []
|
||||
expect(toolNames).not.toContain("codebase_search")
|
||||
})
|
||||
|
||||
it("should filter out generate_image when experiment is disabled", async () => {
|
||||
const mockCodeIndexManager = {
|
||||
isFeatureEnabled: true,
|
||||
isFeatureConfigured: true,
|
||||
isInitialized: true,
|
||||
}
|
||||
vi.mocked(CodeIndexManager.getInstance).mockReturnValue(mockCodeIndexManager as any)
|
||||
|
||||
const result = await SYSTEM_PROMPT(
|
||||
mockContext,
|
||||
"/test/cwd",
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
"code",
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
{ imageGeneration: false },
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true, // useNativeTools
|
||||
)
|
||||
|
||||
expect(result.tools).toBeDefined()
|
||||
const toolNames = result.tools?.map((t) => t.name) || []
|
||||
expect(toolNames).not.toContain("generate_image")
|
||||
})
|
||||
|
||||
it("should filter out run_slash_command when experiment is disabled", async () => {
|
||||
const mockCodeIndexManager = {
|
||||
isFeatureEnabled: true,
|
||||
isFeatureConfigured: true,
|
||||
isInitialized: true,
|
||||
}
|
||||
vi.mocked(CodeIndexManager.getInstance).mockReturnValue(mockCodeIndexManager as any)
|
||||
|
||||
const result = await SYSTEM_PROMPT(
|
||||
mockContext,
|
||||
"/test/cwd",
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
"code",
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
{ runSlashCommand: false },
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true, // useNativeTools
|
||||
)
|
||||
|
||||
expect(result.tools).toBeDefined()
|
||||
const toolNames = result.tools?.map((t) => t.name) || []
|
||||
expect(toolNames).not.toContain("run_slash_command")
|
||||
})
|
||||
})
|
||||
|
|
@ -17,7 +17,7 @@ import { getToolSpecs } from "./tool-specs"
|
|||
|
||||
import { PromptVariables, loadSystemPromptFile } from "./sections/custom-system-prompt"
|
||||
|
||||
import { getToolDescriptionsForMode } from "./tools"
|
||||
import { getToolDescriptionsForMode, filterToolsByAvailability } from "./tools"
|
||||
import {
|
||||
getRulesSection,
|
||||
getSystemInfoSection,
|
||||
|
|
@ -252,8 +252,12 @@ ${customInstructions}`,
|
|||
// Remove duplicates
|
||||
const uniqueToolNames = [...new Set(toolNames)]
|
||||
|
||||
// Apply centralized filtering to ensure consistency with XML tool mode
|
||||
const codeIndexManager = CodeIndexManager.getInstance(context, cwd)
|
||||
const filteredTools = filterToolsByAvailability(uniqueToolNames, codeIndexManager, settings, experiments)
|
||||
|
||||
// Get tool specifications
|
||||
const tools = getToolSpecs(uniqueToolNames)
|
||||
const tools = getToolSpecs(filteredTools)
|
||||
|
||||
return {
|
||||
systemPrompt: basePrompt,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { ToolName, ModeConfig } from "@roo-code/types"
|
|||
import { TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS, DiffStrategy } from "../../../shared/tools"
|
||||
import { McpHub } from "../../../services/mcp/McpHub"
|
||||
import { Mode, getModeConfig, isToolAllowedForMode, getGroupName } from "../../../shared/modes"
|
||||
import { CodeIndexManager } from "../../../services/code-index/manager"
|
||||
|
||||
import { ToolArgs } from "./types"
|
||||
import { getExecuteCommandDescription } from "./execute-command"
|
||||
|
|
@ -27,7 +28,50 @@ import { getCodebaseSearchDescription } from "./codebase-search"
|
|||
import { getUpdateTodoListDescription } from "./update-todo-list"
|
||||
import { getRunSlashCommandDescription } from "./run-slash-command"
|
||||
import { getGenerateImageDescription } from "./generate-image"
|
||||
import { CodeIndexManager } from "../../../services/code-index/manager"
|
||||
|
||||
/**
|
||||
* Filters a list of tool names based on feature flags, settings, and experiments.
|
||||
* This ensures consistent tool availability across XML and native tool modes.
|
||||
*/
|
||||
export function filterToolsByAvailability(
|
||||
tools: ToolName[],
|
||||
codeIndexManager: CodeIndexManager | undefined,
|
||||
settings?: Record<string, any>,
|
||||
experiments?: Record<string, boolean>,
|
||||
): ToolName[] {
|
||||
return tools.filter((tool) => {
|
||||
// Conditionally exclude codebase_search if feature is disabled or not configured
|
||||
if (tool === "codebase_search") {
|
||||
if (
|
||||
!codeIndexManager ||
|
||||
!(
|
||||
codeIndexManager.isFeatureEnabled &&
|
||||
codeIndexManager.isFeatureConfigured &&
|
||||
codeIndexManager.isInitialized
|
||||
)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Conditionally exclude update_todo_list if disabled in settings
|
||||
if (tool === "update_todo_list" && settings?.todoListEnabled === false) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Conditionally exclude generate_image if experiment is not enabled
|
||||
if (tool === "generate_image" && !experiments?.imageGeneration) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Conditionally exclude run_slash_command if experiment is not enabled
|
||||
if (tool === "run_slash_command" && !experiments?.runSlashCommand) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Map of tool names to their description functions
|
||||
const toolDescriptionMap: Record<string, (args: ToolArgs) => string | undefined> = {
|
||||
|
|
@ -120,31 +164,17 @@ export function getToolDescriptionsForMode(
|
|||
// Add always available tools
|
||||
ALWAYS_AVAILABLE_TOOLS.forEach((tool) => tools.add(tool))
|
||||
|
||||
// Conditionally exclude codebase_search if feature is disabled or not configured
|
||||
if (
|
||||
!codeIndexManager ||
|
||||
!(codeIndexManager.isFeatureEnabled && codeIndexManager.isFeatureConfigured && codeIndexManager.isInitialized)
|
||||
) {
|
||||
tools.delete("codebase_search")
|
||||
}
|
||||
|
||||
// Conditionally exclude update_todo_list if disabled in settings
|
||||
if (settings?.todoListEnabled === false) {
|
||||
tools.delete("update_todo_list")
|
||||
}
|
||||
|
||||
// Conditionally exclude generate_image if experiment is not enabled
|
||||
if (!experiments?.imageGeneration) {
|
||||
tools.delete("generate_image")
|
||||
}
|
||||
|
||||
// Conditionally exclude run_slash_command if experiment is not enabled
|
||||
if (!experiments?.runSlashCommand) {
|
||||
tools.delete("run_slash_command")
|
||||
}
|
||||
// Apply consistent filtering across all tool modes
|
||||
const filteredTools = filterToolsByAvailability(
|
||||
Array.from(tools) as ToolName[],
|
||||
codeIndexManager,
|
||||
settings,
|
||||
experiments,
|
||||
)
|
||||
const filteredToolsSet = new Set(filteredTools)
|
||||
|
||||
// Map tool descriptions for allowed tools
|
||||
const descriptions = Array.from(tools).map((toolName) => {
|
||||
const descriptions = Array.from(filteredToolsSet).map((toolName) => {
|
||||
const descriptionFn = toolDescriptionMap[toolName]
|
||||
if (!descriptionFn) {
|
||||
return undefined
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue