feat: implement experimental features system

- Add experiments.ts to manage experimental features
- Refactor experimental diff strategy into experiments system
- Add UI components for managing experimental features
- Add tests for experimental tools
- Update system prompts to handle experiments
This commit is contained in:
sam hoang
2025-01-27 03:48:24 +07:00
parent 2c97b59ed1
commit ad552ea026
14 changed files with 429 additions and 69 deletions

View File

@@ -11,6 +11,7 @@ import { defaultModeSlug, modes } from "../../../shared/modes"
import "../../../utils/path"
import { addCustomInstructions } from "../sections/custom-instructions"
import * as modesSection from "../sections/modes"
import { EXPERIMENT_IDS } from "../../../shared/experiments"
// Mock the sections
jest.mock("../sections/modes", () => ({
@@ -121,6 +122,7 @@ const createMockMcpHub = (): McpHub =>
describe("SYSTEM_PROMPT", () => {
let mockMcpHub: McpHub
let experiments: Record<string, boolean>
beforeAll(() => {
// Ensure fs mock is properly initialized
@@ -140,6 +142,10 @@ describe("SYSTEM_PROMPT", () => {
"/mock/mcp/path",
]
dirs.forEach((dir) => mockFs._mockDirectories.add(dir))
experiments = {
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: false,
[EXPERIMENT_IDS.INSERT_BLOCK]: false,
}
})
beforeEach(() => {
@@ -164,6 +170,10 @@ describe("SYSTEM_PROMPT", () => {
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
expect(prompt).toMatchSnapshot()
@@ -179,7 +189,11 @@ describe("SYSTEM_PROMPT", () => {
"1280x800", // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // customModes,
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
expect(prompt).toMatchSnapshot()
@@ -197,7 +211,11 @@ describe("SYSTEM_PROMPT", () => {
undefined, // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // customModes,
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
expect(prompt).toMatchSnapshot()
@@ -213,7 +231,11 @@ describe("SYSTEM_PROMPT", () => {
undefined, // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // customModes,
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
expect(prompt).toMatchSnapshot()
@@ -229,7 +251,11 @@ describe("SYSTEM_PROMPT", () => {
"900x600", // different viewport size
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // customModes,
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
expect(prompt).toMatchSnapshot()
@@ -249,6 +275,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // globalCustomInstructions
undefined, // preferredLanguage
true, // diffEnabled
experiments,
)
expect(prompt).toContain("apply_diff")
@@ -269,6 +296,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // globalCustomInstructions
undefined, // preferredLanguage
false, // diffEnabled
experiments,
)
expect(prompt).not.toContain("apply_diff")
@@ -289,6 +317,7 @@ describe("SYSTEM_PROMPT", () => {
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
expect(prompt).not.toContain("apply_diff")
@@ -308,6 +337,8 @@ describe("SYSTEM_PROMPT", () => {
undefined, // customModes
undefined, // globalCustomInstructions
"Spanish", // preferredLanguage
undefined, // diffEnabled
experiments,
)
expect(prompt).toContain("Language Preference:")
@@ -337,6 +368,9 @@ describe("SYSTEM_PROMPT", () => {
undefined, // customModePrompts
customModes, // customModes
"Global instructions", // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
// Role definition should be at the top
@@ -368,6 +402,10 @@ describe("SYSTEM_PROMPT", () => {
defaultModeSlug,
customModePrompts,
undefined,
undefined,
undefined,
undefined,
experiments,
)
// Role definition from promptComponent should be at the top
@@ -394,18 +432,101 @@ describe("SYSTEM_PROMPT", () => {
defaultModeSlug,
customModePrompts,
undefined,
undefined,
undefined,
undefined,
experiments,
)
// Should use the default mode's role definition
expect(prompt.indexOf(modes[0].roleDefinition)).toBeLessThan(prompt.indexOf("TOOL USE"))
})
describe("experimental tools", () => {
it("should disable experimental tools by default", async () => {
const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false, // supportsComputerUse
undefined, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments, // experiments - undefined should disable all experimental tools
)
// Verify experimental tools are not included in the prompt
expect(prompt).not.toContain(EXPERIMENT_IDS.SEARCH_AND_REPLACE)
expect(prompt).not.toContain(EXPERIMENT_IDS.INSERT_BLOCK)
})
it("should enable experimental tools when explicitly enabled", async () => {
const experiments = {
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: true,
[EXPERIMENT_IDS.INSERT_BLOCK]: true,
}
const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false, // supportsComputerUse
undefined, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
// Verify experimental tools are included in the prompt when enabled
expect(prompt).toContain(EXPERIMENT_IDS.SEARCH_AND_REPLACE)
expect(prompt).toContain(EXPERIMENT_IDS.INSERT_BLOCK)
})
it("should selectively enable experimental tools", async () => {
const experiments = {
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: true,
[EXPERIMENT_IDS.INSERT_BLOCK]: false,
}
const prompt = await SYSTEM_PROMPT(
mockContext,
"/test/path",
false, // supportsComputerUse
undefined, // mcpHub
undefined, // diffStrategy
undefined, // browserViewportSize
defaultModeSlug, // mode
undefined, // customModePrompts
undefined, // customModes
undefined, // globalCustomInstructions
undefined, // preferredLanguage
undefined, // diffEnabled
experiments,
)
// Verify only enabled experimental tools are included
expect(prompt).toContain(EXPERIMENT_IDS.SEARCH_AND_REPLACE)
expect(prompt).not.toContain(EXPERIMENT_IDS.INSERT_BLOCK)
})
})
afterAll(() => {
jest.restoreAllMocks()
})
})
describe("addCustomInstructions", () => {
let experiments: Record<string, boolean>
beforeAll(() => {
// Ensure fs mock is properly initialized
const mockFs = jest.requireMock("fs/promises")
@@ -417,6 +538,11 @@ describe("addCustomInstructions", () => {
}
throw new Error(`ENOENT: no such file or directory, mkdir '${path}'`)
})
experiments = {
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: false,
[EXPERIMENT_IDS.INSERT_BLOCK]: false,
}
})
beforeEach(() => {
@@ -434,6 +560,10 @@ describe("addCustomInstructions", () => {
"architect", // mode
undefined, // customModePrompts
undefined, // customModes
undefined,
undefined,
undefined,
experiments,
)
expect(prompt).toMatchSnapshot()
@@ -450,6 +580,10 @@ describe("addCustomInstructions", () => {
"ask", // mode
undefined, // customModePrompts
undefined, // customModes
undefined,
undefined,
undefined,
experiments,
)
expect(prompt).toMatchSnapshot()

View File

@@ -39,6 +39,7 @@ async function generatePrompt(
globalCustomInstructions?: string,
preferredLanguage?: string,
diffEnabled?: boolean,
experiments?: Record<string, boolean>,
): Promise<string> {
if (!context) {
throw new Error("Extension context is required for generating system prompt")
@@ -68,6 +69,7 @@ ${getToolDescriptionsForMode(
browserViewportSize,
mcpHub,
customModeConfigs,
experiments,
)}
${getToolUseGuidelinesSection()}
@@ -102,6 +104,7 @@ export const SYSTEM_PROMPT = async (
globalCustomInstructions?: string,
preferredLanguage?: string,
diffEnabled?: boolean,
experiments?: Record<string, boolean>,
): Promise<string> => {
if (!context) {
throw new Error("Extension context is required for generating system prompt")
@@ -135,5 +138,6 @@ export const SYSTEM_PROMPT = async (
globalCustomInstructions,
preferredLanguage,
diffEnabled,
experiments,
)
}

View File

@@ -46,6 +46,7 @@ export function getToolDescriptionsForMode(
browserViewportSize?: string,
mcpHub?: McpHub,
customModes?: ModeConfig[],
experiments?: Record<string, boolean>,
): string {
const config = getModeConfig(mode, customModes)
const args: ToolArgs = {
@@ -64,7 +65,7 @@ export function getToolDescriptionsForMode(
const toolGroup = TOOL_GROUPS[groupName]
if (toolGroup) {
toolGroup.forEach((tool) => {
if (isToolAllowedForMode(tool as ToolName, mode, customModes ?? [])) {
if (isToolAllowedForMode(tool as ToolName, mode, customModes ?? [], experiments ?? {})) {
tools.add(tool)
}
})