Make fuzzy diff matching configurable (and default to off)

This commit is contained in:
Matt Rubens
2024-12-18 12:25:57 -05:00
parent 1beb3a3cf6
commit 3aca5e813e
10 changed files with 137 additions and 21 deletions

View File

@@ -67,6 +67,7 @@ export class Cline {
private didEditFile: boolean = false
customInstructions?: string
diffStrategy?: DiffStrategy
diffEnabled: boolean = false
apiConversationHistory: Anthropic.MessageParam[] = []
clineMessages: ClineMessage[] = []
@@ -97,10 +98,11 @@ export class Cline {
provider: ClineProvider,
apiConfiguration: ApiConfiguration,
customInstructions?: string,
diffEnabled?: boolean,
task?: string,
images?: string[],
historyItem?: HistoryItem,
enableDiff?: boolean,
fuzzyMatchThreshold?: number,
task?: string | undefined,
images?: string[] | undefined,
historyItem?: HistoryItem | undefined,
) {
this.providerRef = new WeakRef(provider)
this.api = buildApiHandler(apiConfiguration)
@@ -109,8 +111,9 @@ export class Cline {
this.browserSession = new BrowserSession(provider.context)
this.diffViewProvider = new DiffViewProvider(cwd)
this.customInstructions = customInstructions
if (diffEnabled && this.api.getModel().id) {
this.diffStrategy = getDiffStrategy(this.api.getModel().id)
this.diffEnabled = enableDiff ?? false
if (this.diffEnabled && this.api.getModel().id) {
this.diffStrategy = getDiffStrategy(this.api.getModel().id, fuzzyMatchThreshold ?? 1.0)
}
if (historyItem) {
this.taskId = historyItem.id

View File

@@ -248,7 +248,7 @@ describe('Cline', () => {
// Setup mock API configuration
mockApiConfig = {
apiProvider: 'anthropic',
apiModelId: 'claude-3-sonnet'
apiModelId: 'claude-3-5-sonnet-20241022'
};
// Mock provider methods
@@ -278,20 +278,77 @@ describe('Cline', () => {
mockProvider,
mockApiConfig,
'custom instructions',
false, // diffEnabled
'test task', // task
undefined, // images
undefined // historyItem
false,
0.95, // 95% threshold
'test task'
);
expect(cline.customInstructions).toBe('custom instructions');
expect(cline.diffEnabled).toBe(false);
});
it('should use default fuzzy match threshold when not provided', () => {
const cline = new Cline(
mockProvider,
mockApiConfig,
'custom instructions',
true,
undefined,
'test task'
);
expect(cline.diffEnabled).toBe(true);
// The diff strategy should be created with default threshold (1.0)
expect(cline.diffStrategy).toBeDefined();
});
it('should use provided fuzzy match threshold', () => {
const getDiffStrategySpy = jest.spyOn(require('../diff/DiffStrategy'), 'getDiffStrategy');
const cline = new Cline(
mockProvider,
mockApiConfig,
'custom instructions',
true,
0.9, // 90% threshold
'test task'
);
expect(cline.diffEnabled).toBe(true);
expect(cline.diffStrategy).toBeDefined();
expect(getDiffStrategySpy).toHaveBeenCalledWith('claude-3-5-sonnet-20241022', 0.9);
getDiffStrategySpy.mockRestore();
});
it('should pass default threshold to diff strategy when not provided', () => {
const getDiffStrategySpy = jest.spyOn(require('../diff/DiffStrategy'), 'getDiffStrategy');
const cline = new Cline(
mockProvider,
mockApiConfig,
'custom instructions',
true,
undefined,
'test task'
);
expect(cline.diffEnabled).toBe(true);
expect(cline.diffStrategy).toBeDefined();
expect(getDiffStrategySpy).toHaveBeenCalledWith('claude-3-5-sonnet-20241022', 1.0);
getDiffStrategySpy.mockRestore();
});
it('should require either task or historyItem', () => {
expect(() => {
new Cline(
mockProvider,
mockApiConfig
mockApiConfig,
undefined, // customInstructions
false, // diffEnabled
undefined, // fuzzyMatchThreshold
undefined // task
);
}).toThrow('Either historyItem or task/images must be provided');
});

View File

@@ -6,10 +6,10 @@ import { SearchReplaceDiffStrategy } from './strategies/search-replace'
* @param model The name of the model being used (e.g., 'gpt-4', 'claude-3-opus')
* @returns The appropriate diff strategy for the model
*/
export function getDiffStrategy(model: string): DiffStrategy {
// For now, return SearchReplaceDiffStrategy for all models (with a fuzzy threshold of 0.9)
export function getDiffStrategy(model: string, fuzzyMatchThreshold?: number): DiffStrategy {
// For now, return SearchReplaceDiffStrategy for all models
// This architecture allows for future optimizations based on model capabilities
return new SearchReplaceDiffStrategy(0.9)
return new SearchReplaceDiffStrategy(fuzzyMatchThreshold ?? 1.0)
}
export type { DiffStrategy }

View File

@@ -58,7 +58,9 @@ export class SearchReplaceDiffStrategy implements DiffStrategy {
private bufferLines: number;
constructor(fuzzyThreshold?: number, bufferLines?: number) {
// Default to exact matching (1.0) unless fuzzy threshold specified
// 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;
}

View File

@@ -70,6 +70,7 @@ type GlobalStateKey =
| "diffEnabled"
| "alwaysAllowMcp"
| "browserLargeViewport"
| "fuzzyMatchThreshold"
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",
@@ -217,7 +218,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
const {
apiConfiguration,
customInstructions,
diffEnabled
diffEnabled,
fuzzyMatchThreshold
} = await this.getState()
this.cline = new Cline(
@@ -225,6 +227,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
apiConfiguration,
customInstructions,
diffEnabled,
fuzzyMatchThreshold,
task,
images
)
@@ -235,7 +238,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
const {
apiConfiguration,
customInstructions,
diffEnabled
diffEnabled,
fuzzyMatchThreshold
} = await this.getState()
this.cline = new Cline(
@@ -243,6 +247,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
apiConfiguration,
customInstructions,
diffEnabled,
fuzzyMatchThreshold,
undefined,
undefined,
historyItem
@@ -613,6 +618,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("browserLargeViewport", browserLargeViewport)
await this.postStateToWebview()
break
case "fuzzyMatchThreshold":
await this.updateGlobalState("fuzzyMatchThreshold", message.value)
await this.postStateToWebview()
break
}
},
null,
@@ -1062,6 +1071,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
diffEnabled,
soundVolume,
browserLargeViewport,
fuzzyMatchThreshold,
] = await Promise.all([
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
this.getGlobalState("apiModelId") as Promise<string | undefined>,
@@ -1101,6 +1111,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("diffEnabled") as Promise<boolean | undefined>,
this.getGlobalState("soundVolume") as Promise<number | undefined>,
this.getGlobalState("browserLargeViewport") as Promise<boolean | undefined>,
this.getGlobalState("fuzzyMatchThreshold") as Promise<number | undefined>,
])
let apiProvider: ApiProvider
@@ -1158,6 +1169,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
diffEnabled: diffEnabled ?? false,
soundVolume,
browserLargeViewport: browserLargeViewport ?? false,
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
}
}

View File

@@ -54,6 +54,7 @@ export interface ExtensionState {
soundVolume?: number
diffEnabled?: boolean
browserLargeViewport?: boolean
fuzzyMatchThreshold?: number
}
export interface ClineMessage {

View File

@@ -39,6 +39,7 @@ export interface WebviewMessage {
| "restartMcpServer"
| "toggleToolAlwaysAllow"
| "toggleMcpServer"
| "fuzzyMatchThreshold"
text?: string
disabled?: boolean
askResponse?: ClineAskResponse