From d5d2f737a003f6725fc9198c75e74275892e110e Mon Sep 17 00:00:00 2001 From: Valery Bugakov Date: Thu, 26 Dec 2024 19:30:00 +0800 Subject: [PATCH 1/2] feat(audoedit): ensure inline completions are also hidden on dismiss --- vscode/src/autoedits/autoedits-provider.ts | 8 ++++---- vscode/src/autoedits/renderer/manager.ts | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/vscode/src/autoedits/autoedits-provider.ts b/vscode/src/autoedits/autoedits-provider.ts index f8c131bcfbe8..bac17ca934af 100644 --- a/vscode/src/autoedits/autoedits-provider.ts +++ b/vscode/src/autoedits/autoedits-provider.ts @@ -58,20 +58,20 @@ export interface AutoeditsPrediction { */ export class AutoeditsProvider implements vscode.InlineCompletionItemProvider, vscode.Disposable { private readonly disposables: vscode.Disposable[] = [] - private readonly onSelectionChangeDebounced: DebouncedFunc /** Keeps track of the last time the text was changed in the editor. */ private lastTextChangeTimeStamp: number | undefined + private readonly onSelectionChangeDebounced: DebouncedFunc + public readonly rendererManager: AutoEditsRendererManager private readonly modelAdapter: AutoeditsModelAdapter + private readonly promptStrategy = new ShortTermPromptStrategy() + public readonly filterPrediction = new FilterPredictionBasedOnRecentEdits() private readonly contextMixer = new ContextMixer({ strategyFactory: new DefaultContextStrategyFactory(Observable.of(AUTOEDITS_CONTEXT_STRATEGY)), contextRankingStrategy: ContextRankingStrategy.TimeBased, dataCollectionEnabled: false, }) - public readonly rendererManager: AutoEditsRendererManager - public readonly filterPrediction = new FilterPredictionBasedOnRecentEdits() - constructor(chatClient: ChatClient) { this.modelAdapter = createAutoeditsModelAdapter({ providerName: autoeditsProviderConfig.provider, diff --git a/vscode/src/autoedits/renderer/manager.ts b/vscode/src/autoedits/renderer/manager.ts index 78457cef94f7..34bff20e62f9 100644 --- a/vscode/src/autoedits/renderer/manager.ts +++ b/vscode/src/autoedits/renderer/manager.ts @@ -129,6 +129,7 @@ export class AutoEditsDefaultRendererManager implements AutoEditsRendererManager decorator.dispose() } this.activeEdit = null + await vscode.commands.executeCommand('editor.action.inlineSuggest.hide') await vscode.commands.executeCommand('setContext', 'cody.supersuggest.active', false) } From a3fe6cdb0ab11544010b26f28fc6db5b00f62f0f Mon Sep 17 00:00:00 2001 From: Valery Bugakov Date: Fri, 27 Dec 2024 13:24:58 +0800 Subject: [PATCH 2/2] chore(audoedit): consistent use of the ouptut channel logger --- vscode/src/autoedits/adapters/cody-gateway.ts | 10 +- .../src/autoedits/adapters/create-adapter.ts | 7 +- vscode/src/autoedits/adapters/fireworks.ts | 13 +- vscode/src/autoedits/adapters/openai.ts | 9 +- .../autoedits/adapters/sourcegraph-chat.ts | 8 +- .../adapters/sourcegraph-completions.ts | 8 +- vscode/src/autoedits/autoedits-provider.ts | 149 +++++++++--------- .../autoedits/filter-prediction-edits.test.ts | 21 ++- .../src/autoedits/filter-prediction-edits.ts | 12 +- vscode/src/autoedits/logger.ts | 3 - vscode/src/autoedits/output-channel-logger.ts | 3 + vscode/src/autoedits/prompt/base.ts | 8 +- .../prompt/default-prompt-strategy.ts | 8 +- vscode/src/autoedits/prompt/prompt-utils.ts | 13 +- .../prompt/short-term-diff-prompt-strategy.ts | 8 +- vscode/src/autoedits/prompt/test-helper.ts | 2 +- .../src/autoedits/renderer/inline-manager.ts | 8 +- vscode/src/autoedits/renderer/manager.ts | 8 +- .../src/autoedits/renderer/mock-renderer.ts | 8 +- .../src/autoedits/shrink-prediction.test.ts | 22 +-- vscode/src/autoedits/shrink-prediction.ts | 11 +- vscode/src/output-channel-logger.ts | 4 +- 22 files changed, 196 insertions(+), 147 deletions(-) delete mode 100644 vscode/src/autoedits/logger.ts create mode 100644 vscode/src/autoedits/output-channel-logger.ts diff --git a/vscode/src/autoedits/adapters/cody-gateway.ts b/vscode/src/autoedits/adapters/cody-gateway.ts index a153127845f7..65dd5c3f652d 100644 --- a/vscode/src/autoedits/adapters/cody-gateway.ts +++ b/vscode/src/autoedits/adapters/cody-gateway.ts @@ -1,6 +1,6 @@ import { currentResolvedConfig, dotcomTokenToGatewayToken } from '@sourcegraph/cody-shared' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { AutoeditModelOptions, AutoeditsModelAdapter } from './base' import { @@ -24,7 +24,11 @@ export class CodyGatewayAdapter implements AutoeditsModelAdapter { } return response.choices[0].text } catch (error) { - autoeditsLogger.logDebug('AutoEdits', 'Error calling Cody Gateway:', error) + autoeditsOutputChannelLogger.logError( + 'getModelResponse', + 'Error calling Cody Gateway:', + error + ) throw error } } @@ -33,7 +37,7 @@ export class CodyGatewayAdapter implements AutoeditsModelAdapter { const resolvedConfig = await currentResolvedConfig() const fastPathAccessToken = dotcomTokenToGatewayToken(resolvedConfig.auth.accessToken) if (!fastPathAccessToken) { - autoeditsLogger.logError('Autoedits', 'FastPath access token is not available') + autoeditsOutputChannelLogger.logError('getApiKey', 'FastPath access token is not available') throw new Error('FastPath access token is not available') } return fastPathAccessToken diff --git a/vscode/src/autoedits/adapters/create-adapter.ts b/vscode/src/autoedits/adapters/create-adapter.ts index b76b5209c8eb..59a763af4e78 100644 --- a/vscode/src/autoedits/adapters/create-adapter.ts +++ b/vscode/src/autoedits/adapters/create-adapter.ts @@ -1,6 +1,6 @@ import type { AutoEditsModelConfig, ChatClient } from '@sourcegraph/cody-shared' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { AutoeditsModelAdapter } from './base' import { CodyGatewayAdapter } from './cody-gateway' @@ -30,7 +30,10 @@ export function createAutoeditsModelAdapter({ ? new SourcegraphChatAdapter(chatClient) : new SourcegraphCompletionsAdapter() default: - autoeditsLogger.logDebug('Config', `Provider ${providerName} not supported`) + autoeditsOutputChannelLogger.logDebug( + 'createAutoeditsModelAdapter', + `Provider ${providerName} not supported` + ) throw new Error(`Provider ${providerName} not supported`) } } diff --git a/vscode/src/autoedits/adapters/fireworks.ts b/vscode/src/autoedits/adapters/fireworks.ts index cf8571f44cc3..965ec896243c 100644 --- a/vscode/src/autoedits/adapters/fireworks.ts +++ b/vscode/src/autoedits/adapters/fireworks.ts @@ -1,5 +1,5 @@ import { autoeditsProviderConfig } from '../autoedits-config' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { AutoeditModelOptions, AutoeditsModelAdapter } from './base' import { @@ -16,7 +16,10 @@ export class FireworksAdapter implements AutoeditsModelAdapter { const apiKey = autoeditsProviderConfig.experimentalAutoeditsConfigOverride?.apiKey if (!apiKey) { - autoeditsLogger.logError('Autoedits', 'No api key provided in the config override') + autoeditsOutputChannelLogger.logError( + 'getModelResponse', + 'No api key provided in the config override' + ) throw new Error('No api key provided in the config override') } const response = await getModelResponse(option.url, body, apiKey) @@ -25,7 +28,11 @@ export class FireworksAdapter implements AutoeditsModelAdapter { } return response.choices[0].text } catch (error) { - autoeditsLogger.logDebug('AutoEdits', 'Error calling Fireworks API:', error) + autoeditsOutputChannelLogger.logError( + 'getModelResponse', + 'Error calling Fireworks API:', + error + ) throw error } } diff --git a/vscode/src/autoedits/adapters/openai.ts b/vscode/src/autoedits/adapters/openai.ts index 9654de295a7f..6327b0c6accf 100644 --- a/vscode/src/autoedits/adapters/openai.ts +++ b/vscode/src/autoedits/adapters/openai.ts @@ -1,5 +1,5 @@ import { autoeditsProviderConfig } from '../autoedits-config' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { AutoeditModelOptions, AutoeditsModelAdapter } from './base' import { getModelResponse, getOpenaiCompatibleChatPrompt } from './utils' @@ -10,7 +10,10 @@ export class OpenAIAdapter implements AutoeditsModelAdapter { const apiKey = autoeditsProviderConfig.experimentalAutoeditsConfigOverride?.apiKey if (!apiKey) { - autoeditsLogger.logError('Autoedits', 'No api key provided in the config override') + autoeditsOutputChannelLogger.logError( + 'getModelResponse', + 'No api key provided in the config override' + ) throw new Error('No api key provided in the config override') } @@ -32,7 +35,7 @@ export class OpenAIAdapter implements AutoeditsModelAdapter { ) return response.choices[0].message.content } catch (error) { - autoeditsLogger.logDebug('AutoEdits', 'Error calling OpenAI API:', error) + autoeditsOutputChannelLogger.logError('getModelResponse', 'Error calling OpenAI API:', error) throw error } } diff --git a/vscode/src/autoedits/adapters/sourcegraph-chat.ts b/vscode/src/autoedits/adapters/sourcegraph-chat.ts index 9851e1c94519..cfe136af598f 100644 --- a/vscode/src/autoedits/adapters/sourcegraph-chat.ts +++ b/vscode/src/autoedits/adapters/sourcegraph-chat.ts @@ -1,5 +1,5 @@ import type { ChatClient, Message } from '@sourcegraph/cody-shared' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { AutoeditModelOptions, AutoeditsModelAdapter } from './base' import { getMaxOutputTokensForAutoedits, getSourcegraphCompatibleChatPrompt } from './utils' @@ -38,7 +38,11 @@ export class SourcegraphChatAdapter implements AutoeditsModelAdapter { } return accumulated } catch (error) { - autoeditsLogger.logDebug('AutoEdits', 'Error calling Sourcegraph Chat:', error) + autoeditsOutputChannelLogger.logError( + 'getModelResponse', + 'Error calling Sourcegraph Chat:', + error + ) throw error } } diff --git a/vscode/src/autoedits/adapters/sourcegraph-completions.ts b/vscode/src/autoedits/adapters/sourcegraph-completions.ts index b47092f97bc3..90e9cb670357 100644 --- a/vscode/src/autoedits/adapters/sourcegraph-completions.ts +++ b/vscode/src/autoedits/adapters/sourcegraph-completions.ts @@ -5,7 +5,7 @@ import type { ModelRefStr, } from '@sourcegraph/cody-shared' import { defaultCodeCompletionsClient } from '../../completions/default-client' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { AutoeditModelOptions, AutoeditsModelAdapter } from './base' import { getMaxOutputTokensForAutoedits, getSourcegraphCompatibleChatPrompt } from './utils' @@ -48,7 +48,11 @@ export class SourcegraphCompletionsAdapter implements AutoeditsModelAdapter { } return accumulated } catch (error) { - autoeditsLogger.logDebug('AutoEdits', 'Error calling Sourcegraph Completions:', error) + autoeditsOutputChannelLogger.logError( + 'getModelResponse', + 'Error calling Sourcegraph Completions:', + error + ) throw error } } diff --git a/vscode/src/autoedits/autoedits-provider.ts b/vscode/src/autoedits/autoedits-provider.ts index ab1778842d4f..43aa27577850 100644 --- a/vscode/src/autoedits/autoedits-provider.ts +++ b/vscode/src/autoedits/autoedits-provider.ts @@ -14,11 +14,11 @@ import { ContextMixer } from '../completions/context/context-mixer' import { DefaultContextStrategyFactory } from '../completions/context/context-strategy' import { getCurrentDocContext } from '../completions/get-current-doc-context' -import type { AutoeditsModelAdapter } from './adapters/base' +import type { AutoeditsModelAdapter, AutoeditsPrompt } from './adapters/base' import { createAutoeditsModelAdapter } from './adapters/create-adapter' import { autoeditsProviderConfig } from './autoedits-config' import { FilterPredictionBasedOnRecentEdits } from './filter-prediction-edits' -import { autoeditsLogger } from './logger' +import { autoeditsOutputChannelLogger } from './output-channel-logger' import type { CodeToReplaceData } from './prompt/prompt-utils' import { ShortTermPromptStrategy } from './prompt/short-term-diff-prompt-strategy' import type { DecorationInfo } from './renderer/decorators/base' @@ -135,9 +135,10 @@ export class AutoeditsProvider implements vscode.InlineCompletionItemProvider, v public async provideInlineCompletionItems( document: vscode.TextDocument, position: vscode.Position, - context: vscode.InlineCompletionContext, + inlineCompletionContext: vscode.InlineCompletionContext, token?: vscode.CancellationToken ): Promise { + const start = Date.now() const controller = new AbortController() const abortSignal = controller.signal token?.onCancellationRequested(() => controller.abort()) @@ -154,39 +155,63 @@ export class AutoeditsProvider implements vscode.InlineCompletionItemProvider, v maxSuffixLength: tokensToChars(autoeditsProviderConfig.tokenLimit.suffixTokens), }) - const autoeditResponse = await this.inferEdit({ + const { context } = await this.contextMixer.getContext({ document, position, docContext, - abortSignal, + maxChars: 32_000, }) + if (abortSignal.aborted) { + return null + } - if (abortSignal.aborted || !autoeditResponse) { + const { codeToReplaceData, prompt } = this.promptStrategy.getPromptForModelType({ + document, + position, + docContext, + context, + tokenBudget: autoeditsProviderConfig.tokenLimit, + isChatModel: autoeditsProviderConfig.isChatModel, + }) + + const initialPrediction = await this.getPrediction({ + document, + position, + prompt, + codeToReplaceData, + }) + if (abortSignal?.aborted || !initialPrediction) { return null } - let { prediction, codeToReplaceData } = autoeditResponse + autoeditsOutputChannelLogger.logDebug( + 'provideInlineCompletionItems', + `========================== Response:\n${initialPrediction}\n` + + `========================== Time Taken: ${Date.now() - start}ms` + ) + + const prediction = shrinkPredictionUntilSuffix({ + prediction: initialPrediction, + codeToReplaceData, + }) + const { codeToRewrite } = codeToReplaceData + if (prediction === codeToRewrite) { + autoeditsOutputChannelLogger.logDebug('skip', 'prediction equals to code to rewrite') + return null + } - const shouldFilterPredictionBasedRecentEdits = this.filterPrediction.shouldFilterPrediction( - document.uri, + const shouldFilterPredictionBasedRecentEdits = this.filterPrediction.shouldFilterPrediction({ + uri: document.uri, prediction, - codeToRewrite - ) + codeToRewrite, + }) if (shouldFilterPredictionBasedRecentEdits) { - autoeditsLogger.logDebug('Autoedits', 'Skipping autoedit - based on recent edits') + autoeditsOutputChannelLogger.logDebug('skip', 'based on recent edits') return null } - prediction = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) - if (prediction === codeToRewrite) { - autoeditsLogger.logDebug( - 'Autoedits', - 'Skipping autoedit - prediction equals to code to rewrite' - ) - return null - } const decorationInfo = getDecorationInfoFromPrediction(document, prediction, codeToReplaceData) if ( @@ -196,10 +221,7 @@ export class AutoeditsProvider implements vscode.InlineCompletionItemProvider, v suffix: codeToReplaceData.suffixInArea + codeToReplaceData.suffixAfterArea, }) ) { - autoeditsLogger.logDebug( - 'Autoedits', - 'Skipping autoedit - predicted text already exists in suffix' - ) + autoeditsOutputChannelLogger.logDebug('skip', 'prediction equals to code to rewrite') return null } @@ -212,30 +234,21 @@ export class AutoeditsProvider implements vscode.InlineCompletionItemProvider, v docContext, decorationInfo, }) + return inlineCompletions } - private async inferEdit(options: AutoEditsProviderOptions): Promise { - const start = Date.now() - const { document, position, docContext, abortSignal } = options - - const { context } = await this.contextMixer.getContext({ - document, - position, - docContext, - maxChars: 32_000, - }) - - const { codeToReplace, prompt } = this.promptStrategy.getPromptForModelType({ - document, - position, - docContext, - context, - tokenBudget: autoeditsProviderConfig.tokenLimit, - isChatModel: autoeditsProviderConfig.isChatModel, - }) - - let response: string | undefined = undefined + private async getPrediction({ + document, + position, + codeToReplaceData, + prompt, + }: { + document: vscode.TextDocument + position: vscode.Position + codeToReplaceData: CodeToReplaceData + prompt: AutoeditsPrompt + }): Promise { if (autoeditsProviderConfig.isMockResponseFromCurrentDocumentTemplateEnabled) { const responseMetadata = extractAutoEditResponseFromCurrentDocumentCommentTemplate( document, @@ -243,39 +256,25 @@ export class AutoeditsProvider implements vscode.InlineCompletionItemProvider, v ) if (responseMetadata) { - response = shrinkReplacerTextToCodeToReplaceRange(responseMetadata, codeToReplace) + const prediction = shrinkReplacerTextToCodeToReplaceRange( + responseMetadata, + codeToReplaceData + ) + + if (prediction) { + return prediction + } } } - if (response === undefined) { - response = await this.modelAdapter.getModelResponse({ - url: autoeditsProviderConfig.url, - model: autoeditsProviderConfig.model, - prompt, - codeToRewrite: codeToReplace.codeToRewrite, - userId: (await currentResolvedConfig()).clientState.anonymousUserID, - isChatModel: autoeditsProviderConfig.isChatModel, - }) - } - - if (abortSignal?.aborted || !response) { - return null - } - - autoeditsLogger.logDebug( - 'Autoedits', - '========================== Response:\n', - response, - '\n', - '========================== Time Taken For LLM (Msec): ', - (Date.now() - start).toString(), - '\n' - ) - - return { - codeToReplaceData: codeToReplace, - prediction: response, - } + return this.modelAdapter.getModelResponse({ + url: autoeditsProviderConfig.url, + model: autoeditsProviderConfig.model, + prompt, + codeToRewrite: codeToReplaceData.codeToRewrite, + userId: (await currentResolvedConfig()).clientState.anonymousUserID, + isChatModel: autoeditsProviderConfig.isChatModel, + }) } public dispose(): void { diff --git a/vscode/src/autoedits/filter-prediction-edits.test.ts b/vscode/src/autoedits/filter-prediction-edits.test.ts index b2b8dbfa154f..59d2e44f99ab 100644 --- a/vscode/src/autoedits/filter-prediction-edits.test.ts +++ b/vscode/src/autoedits/filter-prediction-edits.test.ts @@ -37,13 +37,18 @@ describe('FilterPredictionBasedOnRecentEdits', () => { filterStrategy.dispose() }) - const assertShouldFilterPrediction = (param: { + const assertShouldFilterPrediction = ({ + documentTextWithChanges, + codeToRewrite, + prediction, + expectedFilterValue, + }: { documentTextWithChanges: string codeToRewrite: string prediction: string expectedFilterValue: boolean }) => { - const { originalText, changes } = getTextDocumentChangesForText(param.documentTextWithChanges) + const { originalText, changes } = getTextDocumentChangesForText(documentTextWithChanges) const doc = document(originalText) onDidOpenTextDocument(doc) @@ -55,12 +60,12 @@ describe('FilterPredictionBasedOnRecentEdits', () => { reason: undefined, }) } - const result = filterStrategy.shouldFilterPrediction( - doc.uri, - param.prediction, - param.codeToRewrite - ) - expect(result).toBe(param.expectedFilterValue) + const result = filterStrategy.shouldFilterPrediction({ + uri: doc.uri, + prediction, + codeToRewrite, + }) + expect(result).toBe(expectedFilterValue) } it('should filter prediction if most recent addition is predicted for deletion', () => { diff --git a/vscode/src/autoedits/filter-prediction-edits.ts b/vscode/src/autoedits/filter-prediction-edits.ts index 2c8bf0cc2276..4d643433917a 100644 --- a/vscode/src/autoedits/filter-prediction-edits.ts +++ b/vscode/src/autoedits/filter-prediction-edits.ts @@ -2,7 +2,7 @@ import { createTwoFilesPatch } from 'diff' import * as vscode from 'vscode' import { applyTextDocumentChanges } from '../completions/context/retrievers/recent-user-actions/recent-edits-diff-helpers/utils' import { RecentEditsTracker } from '../completions/context/retrievers/recent-user-actions/recent-edits-tracker' -import { autoeditsLogger } from './logger' +import { autoeditsOutputChannelLogger } from './output-channel-logger' const MAX_FILTER_AGE_MS = 1000 * 30 // 30 seconds @@ -23,7 +23,11 @@ export class FilterPredictionBasedOnRecentEdits implements vscode.Disposable { * The function compares diffs between document states and the prediction vs code to re-write * to determine if the same edit was recently reverted. */ - public shouldFilterPrediction(uri: vscode.Uri, prediction: string, codeToRewrite: string): boolean { + public shouldFilterPrediction({ + uri, + prediction, + codeToRewrite, + }: { uri: vscode.Uri; prediction: string; codeToRewrite: string }): boolean { const trackedDocument = this.recentEditsTracker.getTrackedDocumentForUri(uri) if (!trackedDocument) { return false @@ -72,8 +76,8 @@ export class FilterPredictionBasedOnRecentEdits implements vscode.Disposable { const diff2 = this.createGitDiffForSnapshotComparison(prediction, codeToRewrite) if (diff1 === diff2) { if (diff1.length > 0) { - autoeditsLogger.logDebug( - 'Autoedits', + autoeditsOutputChannelLogger.logDebug( + 'isTextDocumentChangeReverted', 'Filtered the prediction based on recent edits match', 'Diff calculated for filtering based on recent edits\n', diff1 diff --git a/vscode/src/autoedits/logger.ts b/vscode/src/autoedits/logger.ts deleted file mode 100644 index 17662e34b644..000000000000 --- a/vscode/src/autoedits/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Logger } from '../output-channel-logger' - -export const autoeditsLogger = new Logger('AutoEdits Testing') diff --git a/vscode/src/autoedits/output-channel-logger.ts b/vscode/src/autoedits/output-channel-logger.ts new file mode 100644 index 000000000000..bff2c18c9386 --- /dev/null +++ b/vscode/src/autoedits/output-channel-logger.ts @@ -0,0 +1,3 @@ +import { Logger } from '../output-channel-logger' + +export const autoeditsOutputChannelLogger = new Logger('AutoEdits') diff --git a/vscode/src/autoedits/prompt/base.ts b/vscode/src/autoedits/prompt/base.ts index a500e6105d09..af0bb0b604dd 100644 --- a/vscode/src/autoedits/prompt/base.ts +++ b/vscode/src/autoedits/prompt/base.ts @@ -21,7 +21,7 @@ export interface UserPromptArgs { } export interface UserPromptResponse { - codeToReplace: CodeToReplaceData + codeToReplaceData: CodeToReplaceData prompt: PromptString } @@ -30,7 +30,7 @@ export interface UserPromptForModelArgs extends UserPromptArgs { } export interface UserPromptForModelResponse { - codeToReplace: CodeToReplaceData + codeToReplaceData: CodeToReplaceData prompt: AutoeditsPrompt } @@ -45,14 +45,14 @@ export abstract class AutoeditsUserPromptStrategy { isChatModel, ...userPromptArgs }: UserPromptForModelArgs): UserPromptForModelResponse { - const { codeToReplace, prompt } = this.getUserPrompt(userPromptArgs) + const { codeToReplaceData, prompt } = this.getUserPrompt(userPromptArgs) const adjustedPrompt: AutoeditsPrompt = isChatModel ? { systemMessage: SYSTEM_PROMPT, userMessage: prompt } : { userMessage: getCompletionsPromptWithSystemPrompt(SYSTEM_PROMPT, prompt) } return { - codeToReplace, + codeToReplaceData, prompt: adjustedPrompt, } } diff --git a/vscode/src/autoedits/prompt/default-prompt-strategy.ts b/vscode/src/autoedits/prompt/default-prompt-strategy.ts index 2843beba809d..1aa6cb3cedd0 100644 --- a/vscode/src/autoedits/prompt/default-prompt-strategy.ts +++ b/vscode/src/autoedits/prompt/default-prompt-strategy.ts @@ -1,7 +1,7 @@ import { ps } from '@sourcegraph/cody-shared' import { RetrieverIdentifier } from '../../completions/context/utils' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import { AutoeditsUserPromptStrategy, type UserPromptArgs, type UserPromptResponse } from './base' import * as constants from './constants' @@ -30,7 +30,7 @@ export class DefaultUserPromptStrategy extends AutoeditsUserPromptStrategy { context, tokenBudget.contextSpecificTokenLimit ) - const { fileWithMarkerPrompt, areaPrompt, codeToReplace } = getCurrentFilePromptComponents({ + const { fileWithMarkerPrompt, areaPrompt, codeToReplaceData } = getCurrentFilePromptComponents({ docContext, document, position, @@ -83,9 +83,9 @@ export class DefaultUserPromptStrategy extends AutoeditsUserPromptStrategy { constants.FINAL_USER_PROMPT ) - autoeditsLogger.logDebug('AutoEdits', 'Prompt\n', finalPrompt) + autoeditsOutputChannelLogger.logDebug('getUserPrompt', 'Prompt\n', finalPrompt) return { - codeToReplace: codeToReplace, + codeToReplaceData, prompt: finalPrompt, } } diff --git a/vscode/src/autoedits/prompt/prompt-utils.ts b/vscode/src/autoedits/prompt/prompt-utils.ts index 83f0fc4222b4..3958c4c151b4 100644 --- a/vscode/src/autoedits/prompt/prompt-utils.ts +++ b/vscode/src/autoedits/prompt/prompt-utils.ts @@ -8,7 +8,7 @@ import { import { Uri } from 'vscode' import * as vscode from 'vscode' import { RetrieverIdentifier } from '../../completions/context/utils' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import { clip, splitLinesKeepEnds } from '../utils' import * as constants from './constants' @@ -36,7 +36,7 @@ export interface CodeToReplaceData { export interface CurrentFilePromptResponse { fileWithMarkerPrompt: PromptString areaPrompt: PromptString - codeToReplace: CodeToReplaceData + codeToReplaceData: CodeToReplaceData } interface CurrentFileContext { @@ -82,7 +82,7 @@ export function getCurrentFilePromptComponents( options: CurrentFilePromptOptions ): CurrentFilePromptResponse { const currentFileContext = getCurrentFileContext(options) - const codeToReplace = { + const codeToReplaceData = { codeToRewrite: currentFileContext.codeToRewrite.toString(), range: currentFileContext.range, codeToRewritePrefix: currentFileContext.codeToRewritePrefix.toString(), @@ -118,7 +118,7 @@ export function getCurrentFilePromptComponents( constants.AREA_FOR_CODE_MARKER_CLOSE ) - return { fileWithMarkerPrompt: filePrompt, areaPrompt: areaPrompt, codeToReplace: codeToReplace } + return { fileWithMarkerPrompt: filePrompt, areaPrompt, codeToReplaceData } } export function getCurrentFileContext(options: CurrentFilePromptOptions): CurrentFileContext { @@ -350,7 +350,10 @@ export function getContextItemMappingWithTokenLimit( if (tokenLimit !== undefined) { contextItemMapping.set(identifier, getContextItemsInTokenBudget(items, tokenLimit)) } else { - autoeditsLogger.logDebug('AutoEdits', `No token limit for ${identifier}`) + autoeditsOutputChannelLogger.logDebug( + 'getContextItemMappingWithTokenLimit', + `No token limit for ${identifier}` + ) contextItemMapping.set(identifier, []) } } diff --git a/vscode/src/autoedits/prompt/short-term-diff-prompt-strategy.ts b/vscode/src/autoedits/prompt/short-term-diff-prompt-strategy.ts index a36693cd5453..13d558bfaeff 100644 --- a/vscode/src/autoedits/prompt/short-term-diff-prompt-strategy.ts +++ b/vscode/src/autoedits/prompt/short-term-diff-prompt-strategy.ts @@ -2,7 +2,7 @@ import { type AutocompleteContextSnippet, type PromptString, ps } from '@sourceg import { groupConsecutiveItemsByPredicate } from '../../completions/context/retrievers/recent-user-actions/recent-edits-diff-helpers/utils' import { RetrieverIdentifier } from '../../completions/context/utils' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import { AutoeditsUserPromptStrategy, type UserPromptArgs, type UserPromptResponse } from './base' import * as constants from './constants' @@ -34,7 +34,7 @@ export class ShortTermPromptStrategy extends AutoeditsUserPromptStrategy { context, tokenBudget.contextSpecificTokenLimit ) - const { fileWithMarkerPrompt, areaPrompt, codeToReplace } = getCurrentFilePromptComponents({ + const { fileWithMarkerPrompt, areaPrompt, codeToReplaceData } = getCurrentFilePromptComponents({ docContext, document, position, @@ -82,9 +82,9 @@ export class ShortTermPromptStrategy extends AutoeditsUserPromptStrategy { constants.FINAL_USER_PROMPT ) - autoeditsLogger.logDebug('AutoEdits', 'Prompt\n', finalPrompt) + autoeditsOutputChannelLogger.logDebug('getUserPrompt', 'Prompt\n', finalPrompt) return { - codeToReplace: codeToReplace, + codeToReplaceData, prompt: finalPrompt, } } diff --git a/vscode/src/autoedits/prompt/test-helper.ts b/vscode/src/autoedits/prompt/test-helper.ts index 860fa5265340..0f2457871f13 100644 --- a/vscode/src/autoedits/prompt/test-helper.ts +++ b/vscode/src/autoedits/prompt/test-helper.ts @@ -33,5 +33,5 @@ export function createCodeToReplaceDataForTest( maxSuffixLinesInArea: options.maxSuffixLinesInArea, codeToRewritePrefixLines: options.codeToRewritePrefixLines, codeToRewriteSuffixLines: options.codeToRewriteSuffixLines, - }).codeToReplace + }).codeToReplaceData } diff --git a/vscode/src/autoedits/renderer/inline-manager.ts b/vscode/src/autoedits/renderer/inline-manager.ts index 248bd85c02c3..8cef94346e51 100644 --- a/vscode/src/autoedits/renderer/inline-manager.ts +++ b/vscode/src/autoedits/renderer/inline-manager.ts @@ -2,7 +2,7 @@ import { isFileURI } from '@sourcegraph/cody-shared' import * as vscode from 'vscode' import { completionMatchesSuffix } from '../../completions/is-completion-visible' import { getNewLineChar } from '../../completions/text-processing' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { AutoeditRendererManagerArgs } from './manager' import type { @@ -86,7 +86,11 @@ export class AutoEditsInlineRendererManager ), ] - autoeditsLogger.logDebug('Autocomplete Inline Response: ', completionText) + autoeditsOutputChannelLogger.logDebug( + 'maybeRenderDecorationsAndTryMakeInlineCompletionResponse', + 'Autocomplete Inline Response: ', + completionText + ) } function withoutUsedChanges(array: T[]): T[] { diff --git a/vscode/src/autoedits/renderer/manager.ts b/vscode/src/autoedits/renderer/manager.ts index 560dd85c9457..bec17051c8e3 100644 --- a/vscode/src/autoedits/renderer/manager.ts +++ b/vscode/src/autoedits/renderer/manager.ts @@ -3,7 +3,7 @@ import * as vscode from 'vscode' import type { DocumentContext } from '@sourcegraph/cody-shared' import { completionMatchesSuffix } from '../../completions/is-completion-visible' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import type { CodeToReplaceData } from '../prompt/prompt-utils' import { adjustPredictionIfInlineCompletionPossible, @@ -229,7 +229,11 @@ export class AutoEditsDefaultRendererManager implements AutoEditsRendererManager document.lineAt(position).range.end ) ) - autoeditsLogger.logDebug('Autocomplete Inline Response: ', autocompleteResponse) + autoeditsOutputChannelLogger.logDebug( + 'maybeRenderDecorationsAndTryMakeInlineCompletionResponse', + 'Autocomplete Inline Response: ', + autocompleteResponse + ) return { inlineCompletions: [inlineCompletionItem], updatedDecorationInfo: decorationInfo } } diff --git a/vscode/src/autoedits/renderer/mock-renderer.ts b/vscode/src/autoedits/renderer/mock-renderer.ts index 3a09e7d0db69..72a39b6aa852 100644 --- a/vscode/src/autoedits/renderer/mock-renderer.ts +++ b/vscode/src/autoedits/renderer/mock-renderer.ts @@ -3,7 +3,7 @@ import * as vscode from 'vscode' import { getNewLineChar } from '../../completions/text-processing' import type { CodeToReplaceData } from '../prompt/prompt-utils' -import { autoeditsLogger } from '../logger' +import { autoeditsOutputChannelLogger } from '../output-channel-logger' import { DefaultDecorator } from './decorators/default-decorator' import { getDecorationInfo } from './diff-utils' @@ -59,9 +59,9 @@ export function shrinkReplacerTextToCodeToReplaceRange( const rewriteStartLineNumber = offsetToLineNumber(initial.text, rewriteStartOffset) if (rewriteStartLineNumber === -1) { - autoeditsLogger.logError( - 'Autoedits', - '`shrinkReplacerTextToCodeToReplaceRange` unable to find `codeToRewrite` start offset' + autoeditsOutputChannelLogger.logError( + 'shrinkReplacerTextToCodeToReplaceRange', + 'unable to find `codeToRewrite` start offset' ) return undefined } diff --git a/vscode/src/autoedits/shrink-prediction.test.ts b/vscode/src/autoedits/shrink-prediction.test.ts index 4ab01c6426a6..9c3a455cb094 100644 --- a/vscode/src/autoedits/shrink-prediction.test.ts +++ b/vscode/src/autoedits/shrink-prediction.test.ts @@ -33,7 +33,7 @@ describe('shrinkPredictionUntilSuffix', () => { pred_line_2\n ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result).toBe(prediction) }) @@ -56,7 +56,7 @@ describe('shrinkPredictionUntilSuffix', () => { this.recentEditsTracker = recentEditsTracker }\n\n` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result).toBe(prediction) }) @@ -76,7 +76,7 @@ describe('shrinkPredictionUntilSuffix', () => { self.email = email ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) // We expect that last line to be removed (overlap is 1 line). expect(result.trimEnd()).toBe(withoutLastLines(prediction, 1)) }) @@ -93,7 +93,7 @@ describe('shrinkPredictionUntilSuffix', () => { extra_line3 ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) // No overlap to remove, so the prediction remains. expect(result.trimEnd()).toBe(prediction.trimEnd()) }) @@ -113,7 +113,7 @@ describe('shrinkPredictionUntilSuffix', () => { console.log("end") ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result.trimEnd()).toBe(withoutLastLines(prediction, 1)) }) @@ -130,7 +130,7 @@ describe('shrinkPredictionUntilSuffix', () => { console.log(a + b); }` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result).toBe(codeToReplaceData.codeToRewrite) }) @@ -141,7 +141,7 @@ describe('shrinkPredictionUntilSuffix', () => { more lines\n ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result).toBe(prediction) }) @@ -153,7 +153,7 @@ describe('shrinkPredictionUntilSuffix', () => { const prediction = '' - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result).toBe(prediction) }) @@ -167,7 +167,7 @@ describe('shrinkPredictionUntilSuffix', () => { console.log("barbaz")\n ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result).toBe(prediction) }) @@ -187,7 +187,7 @@ describe('shrinkPredictionUntilSuffix', () => { line2\n ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) // Entire prediction is removed => only a single newline remains. expect(result).toBe('\n') }) @@ -209,7 +209,7 @@ describe('shrinkPredictionUntilSuffix', () => { line4\n ` - const result = shrinkPredictionUntilSuffix(prediction, codeToReplaceData) + const result = shrinkPredictionUntilSuffix({ prediction, codeToReplaceData }) expect(result).toBe(withoutLastLines(prediction, 3)) }) }) diff --git a/vscode/src/autoedits/shrink-prediction.ts b/vscode/src/autoedits/shrink-prediction.ts index 6dd601049f59..283b8493bda4 100644 --- a/vscode/src/autoedits/shrink-prediction.ts +++ b/vscode/src/autoedits/shrink-prediction.ts @@ -5,10 +5,13 @@ import type { CodeToReplaceData } from './prompt/prompt-utils' /** * Shrinks the prediction by removing overlapping lines with the suffix. */ -export function shrinkPredictionUntilSuffix( - prediction: string, - { suffixInArea, suffixAfterArea, codeToRewrite }: CodeToReplaceData -): string { +export function shrinkPredictionUntilSuffix({ + prediction, + codeToReplaceData: { suffixInArea, suffixAfterArea, codeToRewrite }, +}: { + prediction: string + codeToReplaceData: CodeToReplaceData +}): string { if (prediction.length === 0) { return prediction } diff --git a/vscode/src/output-channel-logger.ts b/vscode/src/output-channel-logger.ts index eb2fff354aac..ae14c1da1ddd 100644 --- a/vscode/src/output-channel-logger.ts +++ b/vscode/src/output-channel-logger.ts @@ -136,7 +136,9 @@ function formatMessage({ debugVerbose: boolean }): string { const featureLabel = feature ? `${feature}:` : '' - const messageParts: string[] = [`${prefix}${featureLabel}${filterLabel} ${text}:`] + const messageParts: string[] = [ + `${prefix}${featureLabel}${filterLabel} ${text}${args.length > 0 ? ':' : ''}`, + ] if (args.length > 0) { const lastArg = args.at(-1)