refactor: streamline edit result validation and format code

This commit is contained in:
Daniel Riccio
2025-01-17 01:40:42 -05:00
parent fa49bd804b
commit fa9e055ea9

View File

@@ -66,33 +66,29 @@ export function getDMPSimilarity(original: string, modified: string): number {
} }
// Helper function to validate edit results using hunk information // Helper function to validate edit results using hunk information
export function validateEditResult(hunk: Hunk, result: string, confidenceThreshold: number): number { export function validateEditResult(hunk: Hunk, result: string): number {
const hunkDeepCopy: Hunk = JSON.parse(JSON.stringify(hunk)) // Build the expected text from the hunk
const expectedText = hunk.changes
.filter(change => change.type === "context" || change.type === "add")
.map(change => change.indent ? change.indent + change.content : change.content)
.join("\n");
const originalSkeleton = hunkDeepCopy.changes // Calculate similarity between the result and expected text
.filter((change) => change.type === "context" || change.type === "remove") const similarity = getDMPSimilarity(expectedText, result);
.map((change) => change.content)
.join("\n")
const expectedSkeleton = hunkDeepCopy.changes // If the result is unchanged from original, return low confidence
.filter((change) => change.type === "context" || change.type === "add") const originalText = hunk.changes
.map((change) => change.content) .filter(change => change.type === "context" || change.type === "remove")
.join("\n") .map(change => change.indent ? change.indent + change.content : change.content)
.join("\n");
const originalSimilarity = evaluateSimilarity(originalSkeleton, result) const originalSimilarity = getDMPSimilarity(originalText, result);
const expectedSimilarity = evaluateSimilarity(expectedSkeleton, result) if (originalSimilarity > 0.97 && similarity !== 1) {
return 0.8 * similarity; // Some confidence since we found the right location
if (originalSimilarity > 0.97 && expectedSimilarity !== 1) {
if (originalSimilarity === 1) {
return 0.5
} else {
return 0.8
}
} }
const multiplier = expectedSimilarity < confidenceThreshold ? expectedSimilarity : 1 // For partial matches, scale the confidence but keep it high if we're close
return similarity;
return multiplier
} }
// Helper function to validate context lines against original content // Helper function to validate context lines against original content
@@ -157,7 +153,9 @@ function combineOverlappingMatches(
const usedIndices = new Set<number>() const usedIndices = new Set<number>()
for (const match of matches) { for (const match of matches) {
if (usedIndices.has(match.windowIndex)) {continue} if (usedIndices.has(match.windowIndex)) {
continue
}
// Find overlapping matches // Find overlapping matches
const overlapping = matches.filter( const overlapping = matches.filter(
@@ -194,7 +192,12 @@ function combineOverlappingMatches(
return combinedMatches return combinedMatches
} }
export function findExactMatch(searchStr: string, content: string[], startIndex: number = 0, confidenceThreshold: number = 0.97): SearchResult { export function findExactMatch(
searchStr: string,
content: string[],
startIndex: number = 0,
confidenceThreshold: number = 0.97
): SearchResult {
const searchLines = searchStr.split("\n") const searchLines = searchStr.split("\n")
const windows = createOverlappingWindows(content.slice(startIndex), searchLines.length) const windows = createOverlappingWindows(content.slice(startIndex), searchLines.length)
const matches: (SearchResult & { windowIndex: number })[] = [] const matches: (SearchResult & { windowIndex: number })[] = []
@@ -229,7 +232,12 @@ export function findExactMatch(searchStr: string, content: string[], startIndex:
} }
// String similarity strategy // String similarity strategy
export function findSimilarityMatch(searchStr: string, content: string[], startIndex: number = 0, confidenceThreshold: number = 0.97): SearchResult { export function findSimilarityMatch(
searchStr: string,
content: string[],
startIndex: number = 0,
confidenceThreshold: number = 0.97
): SearchResult {
const searchLines = searchStr.split("\n") const searchLines = searchStr.split("\n")
let bestScore = 0 let bestScore = 0
let bestIndex = -1 let bestIndex = -1
@@ -257,7 +265,12 @@ export function findSimilarityMatch(searchStr: string, content: string[], startI
} }
// Levenshtein strategy // Levenshtein strategy
export function findLevenshteinMatch(searchStr: string, content: string[], startIndex: number = 0, confidenceThreshold: number = 0.97): SearchResult { export function findLevenshteinMatch(
searchStr: string,
content: string[],
startIndex: number = 0,
confidenceThreshold: number = 0.97
): SearchResult {
const searchLines = searchStr.split("\n") const searchLines = searchStr.split("\n")
const candidates = [] const candidates = []
@@ -271,7 +284,6 @@ export function findLevenshteinMatch(searchStr: string, content: string[], start
const similarity = getDMPSimilarity(searchStr, closestMatch) const similarity = getDMPSimilarity(searchStr, closestMatch)
const contextSimilarity = validateContextLines(searchStr, closestMatch, confidenceThreshold) const contextSimilarity = validateContextLines(searchStr, closestMatch, confidenceThreshold)
const confidence = Math.min(similarity, contextSimilarity) const confidence = Math.min(similarity, contextSimilarity)
console.log(searchStr, closestMatch, index, confidence)
return { return {
index: confidence === 0 ? -1 : index, index: confidence === 0 ? -1 : index,
confidence: index !== -1 ? confidence : 0, confidence: index !== -1 ? confidence : 0,
@@ -284,99 +296,104 @@ export function findLevenshteinMatch(searchStr: string, content: string[], start
// Helper function to identify anchor lines // Helper function to identify anchor lines
function identifyAnchors(searchStr: string): { first: string | null; last: string | null } { function identifyAnchors(searchStr: string): { first: string | null; last: string | null } {
const searchLines = searchStr.split("\n"); const searchLines = searchStr.split("\n")
let first: string | null = null; let first: string | null = null
let last: string | null = null; let last: string | null = null
// Find the first non-empty line // Find the first non-empty line
for (const line of searchLines) { for (const line of searchLines) {
if (line.trim()) { if (line.trim()) {
first = line; first = line
break; break
} }
} }
// Find the last non-empty line // Find the last non-empty line
for (let i = searchLines.length - 1; i >= 0; i--) { for (let i = searchLines.length - 1; i >= 0; i--) {
if (searchLines[i].trim()) { if (searchLines[i].trim()) {
last = searchLines[i]; last = searchLines[i]
break; break
} }
} }
return { first, last }; return { first, last }
} }
// Anchor-based search strategy // Anchor-based search strategy
export function findAnchorMatch(searchStr: string, content: string[], startIndex: number = 0, confidenceThreshold: number = 0.97): SearchResult { export function findAnchorMatch(
const searchLines = searchStr.split("\n"); searchStr: string,
const { first, last } = identifyAnchors(searchStr); content: string[],
startIndex: number = 0,
confidenceThreshold: number = 0.97
): SearchResult {
const searchLines = searchStr.split("\n")
const { first, last } = identifyAnchors(searchStr)
if (!first || !last) { if (!first || !last) {
return { index: -1, confidence: 0, strategy: "anchor" }; return { index: -1, confidence: 0, strategy: "anchor" }
} }
let firstIndex = -1; let firstIndex = -1
let lastIndex = -1; let lastIndex = -1
// Check if the first anchor is unique // Check if the first anchor is unique
let firstOccurrences = 0; let firstOccurrences = 0
for (const contentLine of content) { for (const contentLine of content) {
if (contentLine === first) { if (contentLine === first) {
firstOccurrences++; firstOccurrences++
} }
} }
if (firstOccurrences !== 1) { if (firstOccurrences !== 1) {
return { index: -1, confidence: 0, strategy: "anchor" }; return { index: -1, confidence: 0, strategy: "anchor" }
} }
// Find the first anchor // Find the first anchor
for (let i = startIndex; i < content.length; i++) { for (let i = startIndex; i < content.length; i++) {
if (content[i] === first) { if (content[i] === first) {
firstIndex = i; firstIndex = i
break; break
} }
} }
// Find the last anchor // Find the last anchor
for (let i = content.length - 1; i >= startIndex; i--) { for (let i = content.length - 1; i >= startIndex; i--) {
if (content[i] === last) { if (content[i] === last) {
lastIndex = i; lastIndex = i
break; break
} }
} }
if (firstIndex === -1 || lastIndex === -1 || lastIndex <= firstIndex) { if (firstIndex === -1 || lastIndex === -1 || lastIndex <= firstIndex) {
return { index: -1, confidence: 0, strategy: "anchor" }; return { index: -1, confidence: 0, strategy: "anchor" }
} }
// Validate the context // Validate the context
const expectedContext = searchLines.slice(searchLines.indexOf(first) + 1, searchLines.indexOf(last)).join("\n"); const expectedContext = searchLines.slice(searchLines.indexOf(first) + 1, searchLines.indexOf(last)).join("\n")
const actualContext = content.slice(firstIndex + 1, lastIndex).join("\n"); const actualContext = content.slice(firstIndex + 1, lastIndex).join("\n")
const contextSimilarity = evaluateSimilarity(expectedContext, actualContext); const contextSimilarity = evaluateSimilarity(expectedContext, actualContext)
if (contextSimilarity < getAdaptiveThreshold(content.length, confidenceThreshold)) { if (contextSimilarity < getAdaptiveThreshold(content.length, confidenceThreshold)) {
return { index: -1, confidence: 0, strategy: "anchor" }; return { index: -1, confidence: 0, strategy: "anchor" }
} }
const confidence = 1; const confidence = 1
return { return {
index: firstIndex, index: firstIndex,
confidence: confidence, confidence: confidence,
strategy: "anchor", strategy: "anchor",
}; }
} }
// Main search function that tries all strategies // Main search function that tries all strategies
export function findBestMatch(searchStr: string, content: string[], startIndex: number = 0, confidenceThreshold: number = 0.97): SearchResult { export function findBestMatch(
const strategies = [ searchStr: string,
findExactMatch, content: string[],
findAnchorMatch, startIndex: number = 0,
findSimilarityMatch, confidenceThreshold: number = 0.97
findLevenshteinMatch ): SearchResult {
] const strategies = [findExactMatch, findAnchorMatch, findSimilarityMatch, findLevenshteinMatch]
let bestResult: SearchResult = { index: -1, confidence: 0, strategy: "none" } let bestResult: SearchResult = { index: -1, confidence: 0, strategy: "none" }