Path: blob/main/extensions/copilot/src/platform/editSurvivalTracking/common/editSurvivalReporter.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import type vscode from 'vscode';6import { IGitService } from '../../../platform/git/common/gitService';7import { resolveWorkspaceOTelMetadata, type WorkspaceOTelMetadata } from '../../../platform/otel/common/workspaceOTelMetadata';8import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';9import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';10import { TimeoutTimer } from '../../../util/vs/base/common/async';11import { DisposableStore } from '../../../util/vs/base/common/lifecycle';12import { StringEdit } from '../../../util/vs/editor/common/core/edits/stringEdit';13import { stringEditFromTextContentChange } from '../../editing/common/edit';14import { ArcTracker } from './arcTracker';15import { EditSurvivalTracker } from './editSurvivalTracker';1617export interface EditSurvivalResult {18readonly telemetryService: ITelemetryService;19readonly fourGram: number;20readonly noRevert: number;21readonly timeDelayMs: number;22readonly didBranchChange: boolean;23readonly currentFileContent?: string;24readonly workspace?: WorkspaceOTelMetadata;2526/**27* Set includeArc to get this!28* See ArcTracker.29*/30readonly arc?: number;3132/**33* Text states for each edit region34*/35readonly textBeforeAiEdits?: string[];36readonly textAfterAiEdits?: string[];37readonly textAfterUserEdits?: string[];38}3940export class EditSurvivalReporter {41private readonly _store = new DisposableStore();42private readonly _editSurvivalTracker = new EditSurvivalTracker(this._documentTextBeforeMarkedEdits, this._markedEdits);43private readonly _arcTracker = this._options.includeArc === true ? new ArcTracker(this._documentTextBeforeMarkedEdits, this._markedEdits) : undefined;44private readonly _initialBranchName: string | undefined;4546/**47* ```48* _documentTextBeforeMarkedEdits49* ----markedEdits---->50* ----editsOnTop---->51* _document.getText()52* ----onDidChangeTextDocument edits---->53* [30sec] -> telemetry event of survival rate of markedEdits54* [2min] -> ...55* [5min] -> ...56* [10min] -> ...57* ```58*/59constructor(60private readonly _document: vscode.TextDocument,61private readonly _documentTextBeforeMarkedEdits: string,62private readonly _markedEdits: StringEdit,63editsOnTop: StringEdit,64private readonly _options: { includeArc?: boolean },65private readonly _sendTelemetryEvent: (res: EditSurvivalResult) => void,66@IWorkspaceService workspaceService: IWorkspaceService,67@IGitService private readonly _gitService: IGitService,68@ITelemetryService private readonly _telemetryService: ITelemetryService69) {70this._store.add(workspaceService.onDidChangeTextDocument(e => {71if (e.document !== this._document) {72return;73}74const edits = stringEditFromTextContentChange(e.contentChanges);75this._editSurvivalTracker.handleEdits(edits);76this._arcTracker?.handleEdits(edits);77}));7879this._editSurvivalTracker.handleEdits(editsOnTop);80this._arcTracker?.handleEdits(editsOnTop);8182this._initialBranchName = this._gitService.activeRepository.get()?.headBranchName;8384// This aligns with github inline completions85this._reportAfter(0);86this._reportAfter(5 * 1000);87this._reportAfter(30 * 1000);88this._reportAfter(120 * 1000);89this._reportAfter(300 * 1000);90this._reportAfter(600 * 1000);91// track up to 15min to allow for slower edit responses from legacy SD endpoint92this._reportAfter(900 * 1000, () => {93this._store.dispose();94});95}9697private _getCurrentBranchName() {98return this._gitService.activeRepository.get()?.headBranchName;99}100101private _reportAfter(timeoutMs: number, cb?: () => void) {102const timer = new TimeoutTimer(() => {103this._report(timeoutMs);104timer.dispose();105if (cb) {106cb();107}108}, timeoutMs);109this._store.add(timer);110}111112private _report(timeMs: number): void {113const survivalRate = this._editSurvivalTracker.computeTrackedEditsSurvivalScore();114115const currentBranch = this._getCurrentBranchName();116const didBranchChange = currentBranch !== this._initialBranchName;117const workspace = resolveWorkspaceOTelMetadata(this._gitService, this._document.uri);118this._sendTelemetryEvent({119telemetryService: this._telemetryService,120fourGram: survivalRate.fourGram,121noRevert: survivalRate.noRevert,122timeDelayMs: timeMs,123didBranchChange,124currentFileContent: this._document.getText(),125workspace,126arc: this._arcTracker?.getAcceptedRestrainedCharactersCount(),127textBeforeAiEdits: survivalRate.textBeforeAiEdits,128textAfterAiEdits: survivalRate.textAfterAiEdits,129textAfterUserEdits: survivalRate.textAfterUserEdits,130});131}132133public cancel(): void {134this._store.dispose();135}136}137138139