mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-14 23:21:19 +00:00
Remove the single diff strategy and make multi-diff the default (#2133)
This commit is contained in:
parent
520326648b
commit
9a13041186
27 changed files with 71 additions and 1972 deletions
|
|
@ -1,5 +1,4 @@
|
|||
import type { DiffStrategy } from "./types"
|
||||
import { SearchReplaceDiffStrategy } from "./strategies/search-replace"
|
||||
import { NewUnifiedDiffStrategy } from "./strategies/new-unified"
|
||||
import { MultiSearchReplaceDiffStrategy } from "./strategies/multi-search-replace"
|
||||
import { EXPERIMENT_IDS, ExperimentId } from "../../shared/experiments"
|
||||
|
|
@ -12,7 +11,7 @@ export type { DiffStrategy }
|
|||
* @returns The appropriate diff strategy for the model
|
||||
*/
|
||||
|
||||
export type DiffStrategyName = "unified" | "multi-search-and-replace" | "search-and-replace"
|
||||
export type DiffStrategyName = "unified" | "multi-search-and-replace"
|
||||
|
||||
type GetDiffStrategyOptions = {
|
||||
model: string
|
||||
|
|
@ -23,6 +22,4 @@ type GetDiffStrategyOptions = {
|
|||
export const getDiffStrategy = ({ fuzzyMatchThreshold, experiments }: GetDiffStrategyOptions): DiffStrategy =>
|
||||
experiments[EXPERIMENT_IDS.DIFF_STRATEGY_UNIFIED]
|
||||
? new NewUnifiedDiffStrategy(fuzzyMatchThreshold)
|
||||
: experiments[EXPERIMENT_IDS.DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE]
|
||||
? new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold)
|
||||
: new SearchReplaceDiffStrategy(fuzzyMatchThreshold)
|
||||
: new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,306 +0,0 @@
|
|||
import { DiffStrategy, DiffResult } from "../types"
|
||||
import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text"
|
||||
import { distance } from "fastest-levenshtein"
|
||||
|
||||
const BUFFER_LINES = 20 // Number of extra context lines to show before and after matches
|
||||
|
||||
function getSimilarity(original: string, search: string): number {
|
||||
if (search === "") {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Normalize strings by removing extra whitespace but preserve case
|
||||
const normalizeStr = (str: string) => str.replace(/\s+/g, " ").trim()
|
||||
|
||||
const normalizedOriginal = normalizeStr(original)
|
||||
const normalizedSearch = normalizeStr(search)
|
||||
|
||||
if (normalizedOriginal === normalizedSearch) {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Calculate Levenshtein distance using fastest-levenshtein's distance function
|
||||
const dist = distance(normalizedOriginal, normalizedSearch)
|
||||
|
||||
// Calculate similarity ratio (0 to 1, where 1 is an exact match)
|
||||
const maxLength = Math.max(normalizedOriginal.length, normalizedSearch.length)
|
||||
return 1 - dist / maxLength
|
||||
}
|
||||
|
||||
export class SearchReplaceDiffStrategy implements DiffStrategy {
|
||||
private fuzzyThreshold: number
|
||||
private bufferLines: number
|
||||
|
||||
getName(): string {
|
||||
return "SearchReplace"
|
||||
}
|
||||
|
||||
constructor(fuzzyThreshold?: number, bufferLines?: number) {
|
||||
// Use provided threshold or default to exact matching (1.0)
|
||||
// Note: fuzzyThreshold is inverted in UI (0% = 1.0, 10% = 0.9)
|
||||
// so we use it directly here
|
||||
this.fuzzyThreshold = fuzzyThreshold ?? 1.0
|
||||
this.bufferLines = bufferLines ?? BUFFER_LINES
|
||||
}
|
||||
|
||||
getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string {
|
||||
return `## apply_diff
|
||||
Description: Request to replace existing code using a search and replace block.
|
||||
This tool allows for precise, surgical replaces to files by specifying exactly what content to search for and what to replace it with.
|
||||
The tool will maintain proper indentation and formatting while making changes.
|
||||
Only a single operation is allowed per tool use.
|
||||
The SEARCH section must exactly match existing content including whitespace and indentation.
|
||||
If you're not confident in the exact content to search for, use the read_file tool first to get the exact content.
|
||||
When applying the diffs, be extra careful to remember to change any closing brackets or other syntax that may be affected by the diff farther down in the file.
|
||||
|
||||
Parameters:
|
||||
- path: (required) The path of the file to modify (relative to the current working directory ${args.cwd})
|
||||
- diff: (required) The search/replace block defining the changes.
|
||||
- start_line: (required) The line number where the search block starts.
|
||||
- end_line: (required) The line number where the search block ends.
|
||||
|
||||
Diff format:
|
||||
\`\`\`
|
||||
<<<<<<< SEARCH
|
||||
[exact content to find including whitespace]
|
||||
=======
|
||||
[new content to replace with]
|
||||
>>>>>>> REPLACE
|
||||
\`\`\`
|
||||
|
||||
Example:
|
||||
|
||||
Original file:
|
||||
\`\`\`
|
||||
1 | def calculate_total(items):
|
||||
2 | total = 0
|
||||
3 | for item in items:
|
||||
4 | total += item
|
||||
5 | return total
|
||||
\`\`\`
|
||||
|
||||
Search/Replace content:
|
||||
\`\`\`
|
||||
<<<<<<< SEARCH
|
||||
def calculate_total(items):
|
||||
total = 0
|
||||
for item in items:
|
||||
total += item
|
||||
return total
|
||||
=======
|
||||
def calculate_total(items):
|
||||
"""Calculate total with 10% markup"""
|
||||
return sum(item * 1.1 for item in items)
|
||||
>>>>>>> REPLACE
|
||||
\`\`\`
|
||||
|
||||
Usage:
|
||||
<apply_diff>
|
||||
<path>File path here</path>
|
||||
<diff>
|
||||
Your search/replace content here
|
||||
</diff>
|
||||
<start_line>1</start_line>
|
||||
<end_line>5</end_line>
|
||||
</apply_diff>`
|
||||
}
|
||||
|
||||
async applyDiff(
|
||||
originalContent: string,
|
||||
diffContent: string,
|
||||
startLine?: number,
|
||||
endLine?: number,
|
||||
): Promise<DiffResult> {
|
||||
// Extract the search and replace blocks
|
||||
const match = diffContent.match(/<<<<<<< SEARCH\n([\s\S]*?)\n?=======\n([\s\S]*?)\n?>>>>>>> REPLACE/)
|
||||
if (!match) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Invalid diff format - missing required SEARCH/REPLACE sections\n\nDebug Info:\n- Expected Format: <<<<<<< SEARCH\\n[search content]\\n=======\\n[replace content]\\n>>>>>>> REPLACE\n- Tip: Make sure to include both SEARCH and REPLACE sections with correct markers`,
|
||||
}
|
||||
}
|
||||
|
||||
let [_, searchContent, replaceContent] = match
|
||||
|
||||
// Detect line ending from original content
|
||||
const lineEnding = originalContent.includes("\r\n") ? "\r\n" : "\n"
|
||||
|
||||
// Strip line numbers from search and replace content if every line starts with a line number
|
||||
if (everyLineHasLineNumbers(searchContent) && everyLineHasLineNumbers(replaceContent)) {
|
||||
searchContent = stripLineNumbers(searchContent)
|
||||
replaceContent = stripLineNumbers(replaceContent)
|
||||
}
|
||||
|
||||
// Split content into lines, handling both \n and \r\n
|
||||
const searchLines = searchContent === "" ? [] : searchContent.split(/\r?\n/)
|
||||
const replaceLines = replaceContent === "" ? [] : replaceContent.split(/\r?\n/)
|
||||
const originalLines = originalContent.split(/\r?\n/)
|
||||
|
||||
// Validate that empty search requires start line
|
||||
if (searchLines.length === 0 && !startLine) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Empty search content requires start_line to be specified\n\nDebug Info:\n- Empty search content is only valid for insertions at a specific line\n- For insertions, specify the line number where content should be inserted`,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that empty search requires same start and end line
|
||||
if (searchLines.length === 0 && startLine && endLine && startLine !== endLine) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Empty search content requires start_line and end_line to be the same (got ${startLine}-${endLine})\n\nDebug Info:\n- Empty search content is only valid for insertions at a specific line\n- For insertions, use the same line number for both start_line and end_line`,
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize search variables
|
||||
let matchIndex = -1
|
||||
let bestMatchScore = 0
|
||||
let bestMatchContent = ""
|
||||
const searchChunk = searchLines.join("\n")
|
||||
|
||||
// Determine search bounds
|
||||
let searchStartIndex = 0
|
||||
let searchEndIndex = originalLines.length
|
||||
|
||||
// Validate and handle line range if provided
|
||||
if (startLine && endLine) {
|
||||
// Convert to 0-based index
|
||||
const exactStartIndex = startLine - 1
|
||||
const exactEndIndex = endLine - 1
|
||||
|
||||
if (exactStartIndex < 0 || exactEndIndex > originalLines.length || exactStartIndex > exactEndIndex) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Line range ${startLine}-${endLine} is invalid (file has ${originalLines.length} lines)\n\nDebug Info:\n- Requested Range: lines ${startLine}-${endLine}\n- File Bounds: lines 1-${originalLines.length}`,
|
||||
}
|
||||
}
|
||||
|
||||
// Try exact match first
|
||||
const originalChunk = originalLines.slice(exactStartIndex, exactEndIndex + 1).join("\n")
|
||||
const similarity = getSimilarity(originalChunk, searchChunk)
|
||||
if (similarity >= this.fuzzyThreshold) {
|
||||
matchIndex = exactStartIndex
|
||||
bestMatchScore = similarity
|
||||
bestMatchContent = originalChunk
|
||||
} else {
|
||||
// Set bounds for buffered search
|
||||
searchStartIndex = Math.max(0, startLine - (this.bufferLines + 1))
|
||||
searchEndIndex = Math.min(originalLines.length, endLine + this.bufferLines)
|
||||
}
|
||||
}
|
||||
|
||||
// If no match found yet, try middle-out search within bounds
|
||||
if (matchIndex === -1) {
|
||||
const midPoint = Math.floor((searchStartIndex + searchEndIndex) / 2)
|
||||
let leftIndex = midPoint
|
||||
let rightIndex = midPoint + 1
|
||||
|
||||
// Search outward from the middle within bounds
|
||||
while (leftIndex >= searchStartIndex || rightIndex <= searchEndIndex - searchLines.length) {
|
||||
// Check left side if still in range
|
||||
if (leftIndex >= searchStartIndex) {
|
||||
const originalChunk = originalLines.slice(leftIndex, leftIndex + searchLines.length).join("\n")
|
||||
const similarity = getSimilarity(originalChunk, searchChunk)
|
||||
if (similarity > bestMatchScore) {
|
||||
bestMatchScore = similarity
|
||||
matchIndex = leftIndex
|
||||
bestMatchContent = originalChunk
|
||||
}
|
||||
leftIndex--
|
||||
}
|
||||
|
||||
// Check right side if still in range
|
||||
if (rightIndex <= searchEndIndex - searchLines.length) {
|
||||
const originalChunk = originalLines.slice(rightIndex, rightIndex + searchLines.length).join("\n")
|
||||
const similarity = getSimilarity(originalChunk, searchChunk)
|
||||
if (similarity > bestMatchScore) {
|
||||
bestMatchScore = similarity
|
||||
matchIndex = rightIndex
|
||||
bestMatchContent = originalChunk
|
||||
}
|
||||
rightIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Require similarity to meet threshold
|
||||
if (matchIndex === -1 || bestMatchScore < this.fuzzyThreshold) {
|
||||
const searchChunk = searchLines.join("\n")
|
||||
const originalContentSection =
|
||||
startLine !== undefined && endLine !== undefined
|
||||
? `\n\nOriginal Content:\n${addLineNumbers(
|
||||
originalLines
|
||||
.slice(
|
||||
Math.max(0, startLine - 1 - this.bufferLines),
|
||||
Math.min(originalLines.length, endLine + this.bufferLines),
|
||||
)
|
||||
.join("\n"),
|
||||
Math.max(1, startLine - this.bufferLines),
|
||||
)}`
|
||||
: `\n\nOriginal Content:\n${addLineNumbers(originalLines.join("\n"))}`
|
||||
|
||||
const bestMatchSection = bestMatchContent
|
||||
? `\n\nBest Match Found:\n${addLineNumbers(bestMatchContent, matchIndex + 1)}`
|
||||
: `\n\nBest Match Found:\n(no match)`
|
||||
|
||||
const lineRange =
|
||||
startLine || endLine
|
||||
? ` at ${startLine ? `start: ${startLine}` : "start"} to ${endLine ? `end: ${endLine}` : "end"}`
|
||||
: ""
|
||||
return {
|
||||
success: false,
|
||||
error: `No sufficiently similar match found${lineRange} (${Math.floor(bestMatchScore * 100)}% similar, needs ${Math.floor(this.fuzzyThreshold * 100)}%)\n\nDebug Info:\n- Similarity Score: ${Math.floor(bestMatchScore * 100)}%\n- Required Threshold: ${Math.floor(this.fuzzyThreshold * 100)}%\n- Search Range: ${startLine && endLine ? `lines ${startLine}-${endLine}` : "start to end"}\n- Tip: Use read_file to get the latest content of the file before attempting the diff again, as the file content may have changed\n\nSearch Content:\n${searchChunk}${bestMatchSection}${originalContentSection}`,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the matched lines from the original content
|
||||
const matchedLines = originalLines.slice(matchIndex, matchIndex + searchLines.length)
|
||||
|
||||
// Get the exact indentation (preserving tabs/spaces) of each line
|
||||
const originalIndents = matchedLines.map((line) => {
|
||||
const match = line.match(/^[\t ]*/)
|
||||
return match ? match[0] : ""
|
||||
})
|
||||
|
||||
// Get the exact indentation of each line in the search block
|
||||
const searchIndents = searchLines.map((line) => {
|
||||
const match = line.match(/^[\t ]*/)
|
||||
return match ? match[0] : ""
|
||||
})
|
||||
|
||||
// Apply the replacement while preserving exact indentation
|
||||
const indentedReplaceLines = replaceLines.map((line, i) => {
|
||||
// Get the matched line's exact indentation
|
||||
const matchedIndent = originalIndents[0] || ""
|
||||
|
||||
// Get the current line's indentation relative to the search content
|
||||
const currentIndentMatch = line.match(/^[\t ]*/)
|
||||
const currentIndent = currentIndentMatch ? currentIndentMatch[0] : ""
|
||||
const searchBaseIndent = searchIndents[0] || ""
|
||||
|
||||
// Calculate the relative indentation level
|
||||
const searchBaseLevel = searchBaseIndent.length
|
||||
const currentLevel = currentIndent.length
|
||||
const relativeLevel = currentLevel - searchBaseLevel
|
||||
|
||||
// If relative level is negative, remove indentation from matched indent
|
||||
// If positive, add to matched indent
|
||||
const finalIndent =
|
||||
relativeLevel < 0
|
||||
? matchedIndent.slice(0, Math.max(0, matchedIndent.length + relativeLevel))
|
||||
: matchedIndent + currentIndent.slice(searchBaseLevel)
|
||||
|
||||
return finalIndent + line.trim()
|
||||
})
|
||||
|
||||
// Construct the final content
|
||||
const beforeMatch = originalLines.slice(0, matchIndex)
|
||||
const afterMatch = originalLines.slice(matchIndex + searchLines.length)
|
||||
|
||||
const finalContent = [...beforeMatch, ...indentedReplaceLines, ...afterMatch].join(lineEnding)
|
||||
return {
|
||||
success: true,
|
||||
content: finalContent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4031,22 +4031,26 @@ Only a single operation is allowed per tool use.
|
|||
The SEARCH section must exactly match existing content including whitespace and indentation.
|
||||
If you're not confident in the exact content to search for, use the read_file tool first to get the exact content.
|
||||
When applying the diffs, be extra careful to remember to change any closing brackets or other syntax that may be affected by the diff farther down in the file.
|
||||
ALWAYS make as many changes in a single 'apply_diff' request as possible using multiple SEARCH/REPLACE blocks
|
||||
|
||||
Parameters:
|
||||
- path: (required) The path of the file to modify (relative to the current working directory /test/path)
|
||||
- diff: (required) The search/replace block defining the changes.
|
||||
- start_line: (required) The line number where the search block starts.
|
||||
- end_line: (required) The line number where the search block ends.
|
||||
|
||||
Diff format:
|
||||
\`\`\`
|
||||
<<<<<<< SEARCH
|
||||
:start_line: (required) The line number of original content where the search block starts.
|
||||
:end_line: (required) The line number of original content where the search block ends.
|
||||
-------
|
||||
[exact content to find including whitespace]
|
||||
=======
|
||||
[new content to replace with]
|
||||
>>>>>>> REPLACE
|
||||
|
||||
\`\`\`
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
Original file:
|
||||
|
|
@ -4061,6 +4065,9 @@ Original file:
|
|||
Search/Replace content:
|
||||
\`\`\`
|
||||
<<<<<<< SEARCH
|
||||
:start_line:1
|
||||
:end_line:5
|
||||
-------
|
||||
def calculate_total(items):
|
||||
total = 0
|
||||
for item in items:
|
||||
|
|
@ -4071,16 +4078,43 @@ def calculate_total(items):
|
|||
"""Calculate total with 10% markup"""
|
||||
return sum(item * 1.1 for item in items)
|
||||
>>>>>>> REPLACE
|
||||
|
||||
\`\`\`
|
||||
|
||||
Search/Replace content with multi edits:
|
||||
\`\`\`
|
||||
<<<<<<< SEARCH
|
||||
:start_line:1
|
||||
:end_line:2
|
||||
-------
|
||||
def calculate_sum(items):
|
||||
sum = 0
|
||||
=======
|
||||
def calculate_sum(items):
|
||||
sum = 0
|
||||
>>>>>>> REPLACE
|
||||
|
||||
<<<<<<< SEARCH
|
||||
:start_line:4
|
||||
:end_line:5
|
||||
-------
|
||||
total += item
|
||||
return total
|
||||
=======
|
||||
sum += item
|
||||
return sum
|
||||
>>>>>>> REPLACE
|
||||
\`\`\`
|
||||
|
||||
|
||||
Usage:
|
||||
<apply_diff>
|
||||
<path>File path here</path>
|
||||
<diff>
|
||||
Your search/replace content here
|
||||
You can use multi search/replace block in one diff block, but make sure to include the line numbers for each block.
|
||||
Only use a single line of '=======' between search and replacement content, because multiple '=======' will corrupt the file.
|
||||
</diff>
|
||||
<start_line>1</start_line>
|
||||
<end_line>5</end_line>
|
||||
</apply_diff>
|
||||
|
||||
## write_to_file
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import * as vscode from "vscode"
|
|||
import { SYSTEM_PROMPT } from "../system"
|
||||
import { McpHub } from "../../../services/mcp/McpHub"
|
||||
import { ClineProvider } from "../../../core/webview/ClineProvider"
|
||||
import { SearchReplaceDiffStrategy } from "../../../core/diff/strategies/search-replace"
|
||||
import { defaultModeSlug, modes, Mode, ModeConfig } from "../../../shared/modes"
|
||||
import "../../../utils/path" // Import path utils to get access to toPosix string extension.
|
||||
import { addCustomInstructions } from "../sections/custom-instructions"
|
||||
import { EXPERIMENT_IDS } from "../../../shared/experiments"
|
||||
import { MultiSearchReplaceDiffStrategy } from "../../diff/strategies/multi-search-replace"
|
||||
|
||||
// Mock the sections
|
||||
jest.mock("../sections/modes", () => ({
|
||||
|
|
@ -171,7 +171,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
beforeEach(() => {
|
||||
// Reset experiments before each test to ensure they're disabled by default
|
||||
experiments = {
|
||||
[EXPERIMENT_IDS.DIFF_STRATEGY_SEARCH_AND_REPLACE]: false,
|
||||
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: false,
|
||||
[EXPERIMENT_IDS.INSERT_BLOCK]: false,
|
||||
}
|
||||
})
|
||||
|
|
@ -295,7 +295,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
"/test/path",
|
||||
false, // supportsComputerUse
|
||||
undefined, // mcpHub
|
||||
new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
|
||||
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
|
||||
undefined, // browserViewportSize
|
||||
defaultModeSlug, // mode
|
||||
undefined, // customModePrompts
|
||||
|
|
@ -316,7 +316,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
"/test/path",
|
||||
false, // supportsComputerUse
|
||||
undefined, // mcpHub
|
||||
new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
|
||||
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
|
||||
undefined, // browserViewportSize
|
||||
defaultModeSlug, // mode
|
||||
undefined, // customModePrompts
|
||||
|
|
@ -337,7 +337,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
"/test/path",
|
||||
false, // supportsComputerUse
|
||||
undefined, // mcpHub
|
||||
new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
|
||||
new MultiSearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
|
||||
undefined, // browserViewportSize
|
||||
defaultModeSlug, // mode
|
||||
undefined, // customModePrompts
|
||||
|
|
@ -482,7 +482,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
it("should disable experimental tools by default", async () => {
|
||||
// Set experiments to explicitly disable experimental tools
|
||||
const experimentsConfig = {
|
||||
[EXPERIMENT_IDS.DIFF_STRATEGY_SEARCH_AND_REPLACE]: false,
|
||||
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: false,
|
||||
[EXPERIMENT_IDS.INSERT_BLOCK]: false,
|
||||
}
|
||||
|
||||
|
|
@ -516,7 +516,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
it("should enable experimental tools when explicitly enabled", async () => {
|
||||
// Set experiments for testing experimental features
|
||||
const experimentsEnabled = {
|
||||
[EXPERIMENT_IDS.DIFF_STRATEGY_SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.INSERT_BLOCK]: true,
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +552,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
it("should selectively enable experimental tools", async () => {
|
||||
// Set experiments for testing selective enabling
|
||||
const experimentsSelective = {
|
||||
[EXPERIMENT_IDS.DIFF_STRATEGY_SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.INSERT_BLOCK]: false,
|
||||
}
|
||||
|
||||
|
|
@ -587,7 +587,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
|
||||
it("should list all available editing tools in base instruction", async () => {
|
||||
const experiments = {
|
||||
[EXPERIMENT_IDS.DIFF_STRATEGY_SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.INSERT_BLOCK]: true,
|
||||
}
|
||||
|
||||
|
|
@ -596,7 +596,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
"/test/path",
|
||||
false,
|
||||
undefined,
|
||||
new SearchReplaceDiffStrategy(),
|
||||
new MultiSearchReplaceDiffStrategy(),
|
||||
undefined,
|
||||
defaultModeSlug,
|
||||
undefined,
|
||||
|
|
@ -615,7 +615,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
})
|
||||
it("should provide detailed instructions for each enabled tool", async () => {
|
||||
const experiments = {
|
||||
[EXPERIMENT_IDS.DIFF_STRATEGY_SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.SEARCH_AND_REPLACE]: true,
|
||||
[EXPERIMENT_IDS.INSERT_BLOCK]: true,
|
||||
}
|
||||
|
||||
|
|
@ -624,7 +624,7 @@ describe("SYSTEM_PROMPT", () => {
|
|||
"/test/path",
|
||||
false,
|
||||
undefined,
|
||||
new SearchReplaceDiffStrategy(),
|
||||
new MultiSearchReplaceDiffStrategy(),
|
||||
undefined,
|
||||
defaultModeSlug,
|
||||
undefined,
|
||||
|
|
|
|||
1
src/exports/roo-code.d.ts
vendored
1
src/exports/roo-code.d.ts
vendored
|
|
@ -254,7 +254,6 @@ type GlobalSettings = {
|
|||
| {
|
||||
search_and_replace: boolean
|
||||
experimentalDiffStrategy: boolean
|
||||
multi_search_and_replace: boolean
|
||||
insert_content: boolean
|
||||
powerSteering: boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,7 +257,6 @@ type GlobalSettings = {
|
|||
| {
|
||||
search_and_replace: boolean
|
||||
experimentalDiffStrategy: boolean
|
||||
multi_search_and_replace: boolean
|
||||
insert_content: boolean
|
||||
powerSteering: boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -277,7 +277,6 @@ export type CustomSupportPrompts = z.infer<typeof customSupportPromptsSchema>
|
|||
export const experimentIds = [
|
||||
"search_and_replace",
|
||||
"experimentalDiffStrategy",
|
||||
"multi_search_and_replace",
|
||||
"insert_content",
|
||||
"powerSteering",
|
||||
] as const
|
||||
|
|
@ -293,7 +292,6 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
|
|||
const experimentsSchema = z.object({
|
||||
search_and_replace: z.boolean(),
|
||||
experimentalDiffStrategy: z.boolean(),
|
||||
multi_search_and_replace: z.boolean(),
|
||||
insert_content: z.boolean(),
|
||||
powerSteering: z.boolean(),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ describe("experiments", () => {
|
|||
experimentalDiffStrategy: false,
|
||||
search_and_replace: false,
|
||||
insert_content: false,
|
||||
multi_search_and_replace: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
|
||||
})
|
||||
|
|
@ -28,7 +27,6 @@ describe("experiments", () => {
|
|||
experimentalDiffStrategy: false,
|
||||
search_and_replace: false,
|
||||
insert_content: false,
|
||||
multi_search_and_replace: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
|
||||
})
|
||||
|
|
@ -39,7 +37,6 @@ describe("experiments", () => {
|
|||
search_and_replace: false,
|
||||
insert_content: false,
|
||||
powerSteering: false,
|
||||
multi_search_and_replace: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@ import { AssertEqual, Equals, Keys, Values } from "../utils/type-fu"
|
|||
export type { ExperimentId }
|
||||
|
||||
export const EXPERIMENT_IDS = {
|
||||
DIFF_STRATEGY_SEARCH_AND_REPLACE: "search_and_replace",
|
||||
DIFF_STRATEGY_UNIFIED: "experimentalDiffStrategy",
|
||||
DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE: "multi_search_and_replace",
|
||||
INSERT_BLOCK: "insert_content",
|
||||
SEARCH_AND_REPLACE: "search_and_replace",
|
||||
POWER_STEERING: "powerSteering",
|
||||
} as const satisfies Record<string, ExperimentId>
|
||||
|
||||
|
|
@ -20,10 +19,9 @@ interface ExperimentConfig {
|
|||
}
|
||||
|
||||
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
|
||||
DIFF_STRATEGY_SEARCH_AND_REPLACE: { enabled: false },
|
||||
DIFF_STRATEGY_UNIFIED: { enabled: false },
|
||||
DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE: { enabled: false },
|
||||
INSERT_BLOCK: { enabled: false },
|
||||
SEARCH_AND_REPLACE: { enabled: false },
|
||||
POWER_STEERING: { enabled: false },
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Cog } from "lucide-react"
|
|||
import { EXPERIMENT_IDS, ExperimentId } from "../../../../src/shared/experiments"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui"
|
||||
import { Slider } from "@/components/ui"
|
||||
|
||||
import { SetCachedStateField, SetExperimentEnabled } from "./types"
|
||||
import { SectionHeader } from "./SectionHeader"
|
||||
|
|
@ -67,9 +67,8 @@ export const AdvancedSettings = ({
|
|||
onChange={(e: any) => {
|
||||
setCachedStateField("diffEnabled", e.target.checked)
|
||||
if (!e.target.checked) {
|
||||
// Reset both experimental strategies when diffs are disabled.
|
||||
// Reset experimental strategies when diffs are disabled.
|
||||
setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY_UNIFIED, false)
|
||||
setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE, false)
|
||||
}
|
||||
}}>
|
||||
<span className="font-medium">{t("settings:advanced.diff.label")}</span>
|
||||
|
|
@ -81,63 +80,6 @@ export const AdvancedSettings = ({
|
|||
|
||||
{diffEnabled && (
|
||||
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
|
||||
<div>
|
||||
<label className="block font-medium mb-1">
|
||||
{t("settings:advanced.diff.strategy.label")}
|
||||
</label>
|
||||
<Select
|
||||
value={
|
||||
experiments[EXPERIMENT_IDS.DIFF_STRATEGY_UNIFIED]
|
||||
? "unified"
|
||||
: experiments[EXPERIMENT_IDS.DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE]
|
||||
? "multiBlock"
|
||||
: "standard"
|
||||
}
|
||||
onValueChange={(value) => {
|
||||
if (value === "standard") {
|
||||
setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY_UNIFIED, false)
|
||||
setExperimentEnabled(
|
||||
EXPERIMENT_IDS.DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE,
|
||||
false,
|
||||
)
|
||||
} else if (value === "unified") {
|
||||
setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY_UNIFIED, true)
|
||||
setExperimentEnabled(
|
||||
EXPERIMENT_IDS.DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE,
|
||||
false,
|
||||
)
|
||||
} else if (value === "multiBlock") {
|
||||
setExperimentEnabled(EXPERIMENT_IDS.DIFF_STRATEGY_UNIFIED, false)
|
||||
setExperimentEnabled(
|
||||
EXPERIMENT_IDS.DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE,
|
||||
true,
|
||||
)
|
||||
}
|
||||
}}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="standard">
|
||||
{t("settings:advanced.diff.strategy.options.standard")}
|
||||
</SelectItem>
|
||||
<SelectItem value="multiBlock">
|
||||
{t("settings:advanced.diff.strategy.options.multiBlock")}
|
||||
</SelectItem>
|
||||
<SelectItem value="unified">
|
||||
{t("settings:advanced.diff.strategy.options.unified")}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1">
|
||||
{experiments[EXPERIMENT_IDS.DIFF_STRATEGY_UNIFIED]
|
||||
? t("settings:advanced.diff.strategy.descriptions.unified")
|
||||
: experiments[EXPERIMENT_IDS.DIFF_STRATEGY_MULTI_SEARCH_AND_REPLACE]
|
||||
? t("settings:advanced.diff.strategy.descriptions.multiBlock")
|
||||
: t("settings:advanced.diff.strategy.descriptions.standard")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block font-medium mb-1">
|
||||
{t("settings:advanced.diff.matchPrecision.label")}
|
||||
|
|
|
|||
|
|
@ -222,7 +222,6 @@ describe("mergeExtensionState", () => {
|
|||
apiConfiguration: { modelMaxThinkingTokens: 456, modelTemperature: 0.3 },
|
||||
experiments: {
|
||||
powerSteering: true,
|
||||
multi_search_and_replace: true,
|
||||
} as Record<ExperimentId, boolean>,
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +237,6 @@ describe("mergeExtensionState", () => {
|
|||
search_and_replace: true,
|
||||
insert_content: true,
|
||||
powerSteering: true,
|
||||
multi_search_and_replace: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Utilitzar estratègia diff unificada experimental",
|
||||
"description": "Activar l'estratègia diff unificada experimental. Aquesta estratègia podria reduir el nombre de reintents causats per errors del model, però pot causar comportaments inesperats o edicions incorrectes. Activeu-la només si enteneu els riscos i esteu disposats a revisar acuradament tots els canvis."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Experimentelle einheitliche Diff-Strategie verwenden",
|
||||
"description": "Aktiviert die experimentelle einheitliche Diff-Strategie. Diese Strategie könnte die Anzahl der durch Modellfehler verursachten Wiederholungen reduzieren, kann aber unerwartetes Verhalten oder falsche Bearbeitungen verursachen. Nur aktivieren, wenn du die Risiken verstehst und bereit bist, alle Änderungen sorgfältig zu überprüfen."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Use experimental unified diff strategy",
|
||||
"description": "Enable the experimental unified diff strategy. This strategy might reduce the number of retries caused by model errors but may cause unexpected behavior or incorrect edits. Only enable if you understand the risks and are willing to carefully review all changes."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Usar estrategia de diff unificada experimental",
|
||||
"description": "Habilitar la estrategia de diff unificada experimental. Esta estrategia podría reducir el número de reintentos causados por errores del modelo, pero puede causar comportamientos inesperados o ediciones incorrectas. Habilítela solo si comprende los riesgos y está dispuesto a revisar cuidadosamente todos los cambios."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Utiliser la stratégie diff unifiée expérimentale",
|
||||
"description": "Activer la stratégie diff unifiée expérimentale. Cette stratégie pourrait réduire le nombre de tentatives causées par des erreurs de modèle, mais peut provoquer des comportements inattendus ou des modifications incorrectes. Activez-la uniquement si vous comprenez les risques et êtes prêt à examiner attentivement tous les changements."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "प्रायोगिक एकीकृत diff रणनीति का उपयोग करें",
|
||||
"description": "प्रायोगिक एकीकृत diff रणनीति सक्षम करें। यह रणनीति मॉडल त्रुटियों के कारण पुनः प्रयासों की संख्या को कम कर सकती है, लेकिन अप्रत्याशित व्यवहार या गलत संपादन का कारण बन सकती है। केवल तभी सक्षम करें जब आप जोखिमों को समझते हों और सभी परिवर्तनों की सावधानीपूर्वक समीक्षा करने के लिए तैयार हों।"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Usa strategia diff unificata sperimentale",
|
||||
"description": "Abilita la strategia diff unificata sperimentale. Questa strategia potrebbe ridurre il numero di tentativi causati da errori del modello, ma può causare comportamenti imprevisti o modifiche errate. Abilitala solo se comprendi i rischi e sei disposto a rivedere attentamente tutte le modifiche."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "実験的な統合diff戦略を使用する",
|
||||
"description": "実験的な統合diff戦略を有効にします。この戦略はモデルエラーによる再試行の回数を減らす可能性がありますが、予期しない動作や不正確な編集を引き起こす可能性があります。リスクを理解し、すべての変更を注意深く確認する準備がある場合にのみ有効にしてください。"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "실험적 통합 diff 전략 사용",
|
||||
"description": "실험적 통합 diff 전략을 활성화합니다. 이 전략은 모델 오류로 인한 재시도 횟수를 줄일 수 있지만 예기치 않은 동작이나 잘못된 편집을 일으킬 수 있습니다. 위험을 이해하고 모든 변경 사항을 신중하게 검토할 의향이 있는 경우에만 활성화하십시오."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Użyj eksperymentalnej ujednoliconej strategii diff",
|
||||
"description": "Włącz eksperymentalną ujednoliconą strategię diff. Ta strategia może zmniejszyć liczbę ponownych prób spowodowanych błędami modelu, ale może powodować nieoczekiwane zachowanie lub nieprawidłowe edycje. Włącz tylko jeśli rozumiesz ryzyko i jesteś gotów dokładnie przeglądać wszystkie zmiany."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Usar estratégia diff unificada experimental",
|
||||
"description": "Ativar a estratégia diff unificada experimental. Esta estratégia pode reduzir o número de novas tentativas causadas por erros do modelo, mas pode causar comportamento inesperado ou edições incorretas. Ative apenas se compreender os riscos e estiver disposto a revisar cuidadosamente todas as alterações."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Deneysel birleştirilmiş diff stratejisini kullan",
|
||||
"description": "Deneysel birleştirilmiş diff stratejisini etkinleştir. Bu strateji, model hatalarından kaynaklanan yeniden deneme sayısını azaltabilir, ancak beklenmeyen davranışlara veya hatalı düzenlemelere neden olabilir. Yalnızca riskleri anlıyorsanız ve tüm değişiklikleri dikkatlice incelemeye istekliyseniz etkinleştirin."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "Sử dụng chiến lược diff thống nhất thử nghiệm",
|
||||
"description": "Bật chiến lược diff thống nhất thử nghiệm. Chiến lược này có thể giảm số lần thử lại do lỗi mô hình nhưng có thể gây ra hành vi không mong muốn hoặc chỉnh sửa không chính xác. Chỉ bật nếu bạn hiểu rõ các rủi ro và sẵn sàng xem xét cẩn thận tất cả các thay đổi."
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "使用实验性统一差异策略",
|
||||
"description": "启用实验性统一差异策略。此策略可能减少由模型错误引起的重试次数,但可能导致意外行为或不正确的编辑。仅在您了解风险并愿意仔细审查所有更改时才启用。"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@
|
|||
},
|
||||
"experimental": {
|
||||
"warning": "⚠️",
|
||||
"DIFF_STRATEGY": {
|
||||
"DIFF_STRATEGY_UNIFIED": {
|
||||
"name": "使用實驗性統一 diff 策略",
|
||||
"description": "此實驗性策略可能減少模型錯誤導致的重試次數,但需謹慎使用"
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue