Path: blob/main/extensions/copilot/src/extension/conversation/vscode-node/userActions.ts
13399 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*--------------------------------------------------------------------------------------------*/456import * as vscode from 'vscode';7import { editsAgentName, getChatParticipantIdFromName } from '../../../platform/chat/common/chatAgents';8import { EditSurvivalResult } from '../../../platform/editSurvivalTracking/common/editSurvivalReporter';9import { IGitService } from '../../../platform/git/common/gitService';10import { ILanguageDiagnosticsService } from '../../../platform/languages/common/languageDiagnosticsService';11import { IMultiFileEditInternalTelemetryService } from '../../../platform/multiFileEdit/common/multiFileEditQualityTelemetry';12import { INotebookService } from '../../../platform/notebook/common/notebookService';13import type { EditOutcome } from '../../../platform/otel/common/genAiAttributes';14import { emitEditFeedbackEvent, emitEditHunkActionEvent, emitEditSurvivalEvent, emitInlineDoneEvent, emitUserFeedbackEvent } from '../../../platform/otel/common/genAiEvents';15import { GenAiMetrics } from '../../../platform/otel/common/genAiMetrics';16import { IOTelService } from '../../../platform/otel/common/otelService';17import { resolveWorkspaceOTelMetadata } from '../../../platform/otel/common/workspaceOTelMetadata';18import { ISurveyService } from '../../../platform/survey/common/surveyService';19import { ITelemetryService, TelemetryEventMeasurements, TelemetryEventProperties } from '../../../platform/telemetry/common/telemetry';20import { isNotebookCellOrNotebookChatInput } from '../../../util/common/notebooks';21import { createServiceIdentifier } from '../../../util/common/services';22import { Schemas } from '../../../util/vs/base/common/network';23import { Intent } from '../../common/constants';24import { IConversationStore } from '../../conversationStore/node/conversationStore';25import { findDiagnosticsTelemetry } from '../../inlineChat/node/diagnosticsTelemetry';26import { CopilotInteractiveEditorResponse, InteractionOutcome } from '../../inlineChat/node/promptCraftingTypes';27import { participantIdToModeName } from '../../intents/common/intents';28import { EditCodeStepTurnMetaData } from '../../intents/node/editCodeStep';29import { Conversation, ICopilotChatResultIn } from '../../prompt/common/conversation';30import { IFeedbackReporter } from '../../prompt/node/feedbackReporter';31import { sendUserActionTelemetry } from '../../prompt/node/telemetry';32import { resolveModelIdForTelemetry } from './resolveModelId';3334export const IUserFeedbackService = createServiceIdentifier<IUserFeedbackService>('IUserFeedbackService');35export interface IUserFeedbackService {36_serviceBrand: undefined;3738handleUserAction(e: vscode.ChatUserActionEvent, participantId: string): void;39handleFeedback(e: vscode.ChatResultFeedback, participantId: string): void;40}4142export class UserFeedbackService implements IUserFeedbackService {43_serviceBrand: undefined;4445constructor(46@ITelemetryService private readonly telemetryService: ITelemetryService,47@IConversationStore private readonly conversationStore: IConversationStore,48@IFeedbackReporter private readonly feedbackReporter: IFeedbackReporter,49@ISurveyService private readonly surveyService: ISurveyService,50@ILanguageDiagnosticsService private readonly languageDiagnosticsService: ILanguageDiagnosticsService,51@IMultiFileEditInternalTelemetryService private readonly multiFileEditTelemetryService: IMultiFileEditInternalTelemetryService,52@INotebookService private readonly notebookService: INotebookService,53@IOTelService private readonly otelService: IOTelService,54@IGitService private readonly gitService: IGitService55) { }5657handleUserAction(e: vscode.ChatUserActionEvent, agentId: string): void {58const document = vscode.window.activeTextEditor?.document;59const selection = vscode.window.activeTextEditor?.selection;6061const result = e.result as ICopilotChatResultIn;62const conversation = result.metadata?.responseId && this.conversationStore.getConversation(result.metadata.responseId);6364if (typeof conversation === 'object' && conversation.getLatestTurn().getMetadata(CopilotInteractiveEditorResponse)) {65this._handleChatUserAction(result.metadata?.sessionId, conversation, e);66return;67}6869// Don't use e.action.responseId, it will go away70switch (e.action.kind) {71case 'copy':72/* __GDPR__73"panel.action.copy" : {74"owner": "digitarald",75"comment": "Counts copied code blocks from a chat panel response",76"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Language of the currently open document." },77"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for this message request." },78"codeBlockIndex": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Index of the code block in the response." },79"copyType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "If the copy was done via the context menu or the toolbar." },80"characterCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of characters copied." },81"lineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of lines copied." },82"participant": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The name of the chat participant for this message." },83"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The command used for the chat participant." }84}85*/86this.telemetryService.sendMSFTTelemetryEvent('panel.action.copy', {87languageId: document?.languageId,88requestId: result.metadata?.responseId,89participant: agentId,90command: result.metadata?.command,91}, {92codeBlockIndex: e.action.codeBlockIndex,93copyType: e.action.copyKind,94characterCount: e.action.copiedCharacters,95lineCount: e.action.copiedText.split('\n').length,96});97GenAiMetrics.incrementUserActionCount(this.otelService, 'copy');98break;99case 'insert':100/* __GDPR__101"panel.action.insert" : {102"owner": "digitarald",103"comment": "Counts inserts on a chat panel response",104"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Language of the currently open document." },105"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for this message request." },106"codeBlockIndex": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Index of the code block in the response." },107"characterCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of characters copied." },108"participant": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The name of the chat participant for this message." },109"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The command used for the chat participant." },110"newFile": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "If the insert was done in a new file." }111}112*/113this.telemetryService.sendMSFTTelemetryEvent('panel.action.insert', {114languageId: document?.languageId,115requestId: result.metadata?.responseId,116participant: agentId,117command: result.metadata?.command,118}, {119codeBlockIndex: e.action.codeBlockIndex,120characterCount: e.action.totalCharacters,121newFile: e.action.newFile ? 1 : 0122});123GenAiMetrics.incrementUserActionCount(this.otelService, 'insert');124break;125case 'followUp':126/* __GDPR__127"panel.action.followup" : {128"owner": "digitarald",129"comment": "Counts generic actions on a chat panel response",130"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Language of the currently open document." },131"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for the message request that is being followed-up." },132"participant": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The name of the chat participant for this message." },133"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The command used for the chat participant." }134}135*/136this.telemetryService.sendMSFTTelemetryEvent('panel.action.followup', {137languageId: document?.languageId,138requestId: result.metadata?.responseId,139participant: agentId,140command: result.metadata?.command,141});142GenAiMetrics.incrementUserActionCount(this.otelService, 'followup');143break;144case 'bug':145if (conversation) {146this.feedbackReporter.reportChat(conversation.getLatestTurn());147} else {148vscode.window.showInformationMessage('Conversation not found, is it restored? Please try again.');149}150break;151case 'chatEditingSessionAction':152if (conversation instanceof Conversation) {153const editCodeStep = conversation.getLatestTurn().getMetadata(EditCodeStepTurnMetaData)?.value;154if (editCodeStep && (e.action.outcome === vscode.ChatEditingSessionActionOutcome.Accepted || e.action.outcome === vscode.ChatEditingSessionActionOutcome.Rejected)155) {156editCodeStep.setWorkingSetEntryState(e.action.uri, {157accepted: e.action.outcome === vscode.ChatEditingSessionActionOutcome.Accepted,158hasRemainingEdits: e.action.hasRemainingEdits159});160}161162/* __GDPR__163"panel.edit.feedback" : {164"owner": "joyceerhl",165"comment": "Counts accept/reject actions for a proposed edit from panel chat",166"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Language of the currently open document." },167"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for the message request that is being followed-up." },168"participant": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The name of the chat participant for this message." },169"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The command used for the chat participant." },170"outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The user decision taken for the edited file" },171"hasRemainingEdits": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether there are additional unactioned edits in the file." },172"isNotebook": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the document is a notebook." },173"isNotebookCell": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the document is a notebook cell." }174}175*/176this.telemetryService.sendMSFTTelemetryEvent('panel.edit.feedback', {177languageId: document?.languageId,178requestId: result.metadata?.responseId,179participant: agentId,180command: result.metadata?.command,181outcome: outcomes.get(e.action.outcome) ?? 'unknown',182hasRemainingEdits: String(e.action.hasRemainingEdits),183}, {184isNotebook: this.notebookService.hasSupportedNotebooks(e.action.uri) ? 1 : 0,185isNotebookCell: e.action.uri.scheme === Schemas.vscodeNotebookCell ? 1 : 0186});187188this.telemetryService.sendGHTelemetryEvent('panel.edit.feedback', {189languageId: document?.languageId,190requestId: result.metadata?.responseId,191participant: agentId,192command: result.metadata?.command,193outcome: outcomes.get(e.action.outcome) ?? 'unknown',194hasRemainingEdits: String(e.action.hasRemainingEdits),195}, {196isNotebook: this.notebookService.hasSupportedNotebooks(e.action.uri) ? 1 : 0,197isNotebookCell: e.action.uri.scheme === Schemas.vscodeNotebookCell ? 1 : 0198});199200201this.telemetryService.sendInternalMSFTTelemetryEvent('panel.edit.feedback', {202languageId: document?.languageId,203requestId: result.metadata?.responseId,204participant: agentId,205command: result.metadata?.command,206outcome: outcomes.get(e.action.outcome) ?? 'unknown',207hasRemainingEdits: String(e.action.hasRemainingEdits),208}, {209isNotebook: this.notebookService.hasSupportedNotebooks(e.action.uri) ? 1 : 0,210isNotebookCell: e.action.uri.scheme === Schemas.vscodeNotebookCell ? 1 : 0211});212213{214const otelOutcome = outcomes.get(e.action.outcome) ?? 'unknown';215const workspace = resolveWorkspaceOTelMetadata(this.gitService, e.action.uri);216emitEditFeedbackEvent(this.otelService, otelOutcome, document?.languageId ?? '', agentId, result.metadata?.responseId ?? '', 'agent', e.action.hasRemainingEdits, this.notebookService.hasSupportedNotebooks(e.action.uri), workspace);217if (e.action.outcome === vscode.ChatEditingSessionActionOutcome.Accepted218|| e.action.outcome === vscode.ChatEditingSessionActionOutcome.Rejected) {219GenAiMetrics.recordEditAcceptance(this.otelService, 'chat_editing', otelOutcome, document?.languageId);220}221GenAiMetrics.recordChatEditOutcome(this.otelService, 'chat_editing', otelOutcome, document?.languageId, e.action.hasRemainingEdits);222}223224if (result.metadata?.responseId225&& (e.action.outcome === vscode.ChatEditingSessionActionOutcome.Accepted226|| e.action.outcome === vscode.ChatEditingSessionActionOutcome.Rejected)227) {228const outcome = e.action.outcome === vscode.ChatEditingSessionActionOutcome.Accepted ? 'accept' : 'reject';229this.multiFileEditTelemetryService.sendEditPromptAndResult({ chatRequestId: result.metadata.responseId }, e.action.uri, outcome);230}231}232break;233case 'chatEditingHunkAction': {234const outcome = outcomes.get(e.action.outcome);235if (outcome) {236237const properties = {238requestId: result.metadata?.responseId ?? '',239languageId: document?.languageId ?? '',240outcome,241};242const measurements = {243hasRemainingEdits: e.action.hasRemainingEdits ? 1 : 0,244isNotebook: this.notebookService.hasSupportedNotebooks(e.action.uri) ? 1 : 0,245isNotebookCell: e.action.uri.scheme === Schemas.vscodeNotebookCell ? 1 : 0,246lineCount: e.action.lineCount,247linesAdded: e.action.linesAdded,248linesRemoved: e.action.linesRemoved,249};250251sendUserActionTelemetry(252this.telemetryService,253document ?? vscode.window.activeTextEditor?.document,254properties,255measurements,256'edit.hunk.action'257);258259emitEditHunkActionEvent(this.otelService, outcome, document?.languageId ?? '', result.metadata?.responseId ?? '', e.action.lineCount, e.action.linesAdded, e.action.linesRemoved, resolveWorkspaceOTelMetadata(this.gitService, e.action.uri));260GenAiMetrics.recordEditAcceptance(this.otelService, 'chat_editing_hunk', outcome, document?.languageId ?? '');261if (outcome === 'accepted') {262GenAiMetrics.incrementLinesOfCode(this.otelService, 'added', document?.languageId ?? '', e.action.linesAdded);263GenAiMetrics.incrementLinesOfCode(this.otelService, 'removed', document?.languageId ?? '', e.action.linesRemoved);264}265}266break;267}268}269270if (e.action.kind === 'copy' || e.action.kind === 'insert') {271let measurements = {};272273// Both copy and insert actions have a totalCharacters property274measurements = {275totalCharacters: e.action.totalCharacters,276totalLines: e.action.totalLines,277isAgent: agentId === getChatParticipantIdFromName(editsAgentName) ? 1 : 0,278};279280// Copy actions have a copiedCharacters/Lines property since this includes manual copying which can be partial281let compType: 'full' | 'partial' = 'full';282if (e.action.kind === 'copy') {283measurements = {284...measurements,285copiedCharacters: e.action.copiedCharacters,286copiedLines: e.action.copiedLines,287};288if (e.action.copiedCharacters !== e.action.totalCharacters) {289compType = 'partial';290}291}292293// If there is a document and selection, include cursor location294if (document && selection) {295measurements = {296...measurements,297cursorLocation: document.offsetAt(selection.active),298};299}300sendUserActionTelemetry(301this.telemetryService,302vscode.window.activeTextEditor?.document,303{304codeBlockIndex: e.action.codeBlockIndex.toString(),305messageId: result.metadata?.modelMessageId ?? '',306headerRequestId: result.metadata?.responseId ?? '',307participant: agentId,308languageId: e.action.languageId ?? '',309modelId: resolveModelIdForTelemetry(e.action.modelId ?? '', result.metadata?.resolvedModel),310comp_type: compType,311mode: participantIdToModeName(agentId),312},313measurements,314e.action.kind === 'copy' ? 'conversation.acceptedCopy' : 'conversation.acceptedInsert'315);316}317318if (e.action.kind === 'apply') {319// Note- this event is fired after a "keep"320this.handleApplyAction(e.action, agentId, result);321}322}323324private handleApplyAction(e: vscode.ChatApplyAction, agentId: string, result: ICopilotChatResultIn): void {325sendUserActionTelemetry(326this.telemetryService,327vscode.window.activeTextEditor?.document,328{329codeBlockIndex: e.codeBlockIndex.toString(),330messageId: result.metadata?.modelMessageId ?? '',331headerRequestId: result.metadata?.responseId ?? '',332participant: agentId,333languageId: e.languageId ?? '',334modelId: resolveModelIdForTelemetry(e.modelId, result.metadata?.resolvedModel),335mode: participantIdToModeName(agentId),336},337{338isAgent: agentId === getChatParticipantIdFromName(editsAgentName) ? 1 : 0,339totalLines: e.totalLines,340},341'conversation.appliedCodeblock'342);343GenAiMetrics.incrementUserActionCount(this.otelService, 'apply');344}345346handleFeedback(e: vscode.ChatResultFeedback, agentId: string): void {347const document = vscode.window.activeTextEditor?.document;348349const result = e.result as ICopilotChatResultIn;350351/* __GDPR__352"panel.action.vote" : {353"owner": "digitarald",354"comment": "Counts votes on a chat panel response",355"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Language of the currently open document." },356"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for this message request." },357"direction": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "If the vote was positive or negative." },358"participant": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The name of the chat participant for this message." },359"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The command used for the chat participant." },360"conversationId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the conversation." }361}362*/363this.telemetryService.sendMSFTTelemetryEvent('panel.action.vote', {364languageId: document?.languageId,365requestId: result.metadata?.responseId,366participant: agentId,367command: result.metadata?.command,368conversationId: result.metadata?.sessionId369}, {370direction: e.kind === vscode.ChatResultFeedbackKind.Helpful ? 1 : 2, // map to previous enum values371});372373sendUserActionTelemetry(374this.telemetryService,375document,376{377rating: e.kind === vscode.ChatResultFeedbackKind.Helpful ? 'positive' : 'negative',378messageId: result.metadata?.modelMessageId ?? '',379headerRequestId: result.metadata?.responseId ?? '',380},381{},382'conversation.messageRating'383);384385const otelRating = e.kind === vscode.ChatResultFeedbackKind.Helpful ? 'positive' : 'negative';386emitUserFeedbackEvent(this.otelService, otelRating, agentId, result.metadata?.sessionId ?? '', result.metadata?.responseId ?? '');387GenAiMetrics.incrementUserFeedbackCount(this.otelService, otelRating);388}389390// --- inline391392393private _handleChatUserAction(sessionId: string | undefined, conversation: Conversation, event: vscode.ChatUserActionEvent) {394395enum InteractiveEditorResponseFeedbackKind {396Undone = 2,397Accepted = 3,398Bug = 4399}400401if (!sessionId) {402return;403}404405let kind: InteractiveEditorResponseFeedbackKind | undefined;406if (event.action.kind === 'editor') {407kind = event.action.accepted ? InteractiveEditorResponseFeedbackKind.Accepted : InteractiveEditorResponseFeedbackKind.Undone;408} else if (event.action.kind === 'bug') {409kind = InteractiveEditorResponseFeedbackKind.Bug;410}411412if (kind === undefined) {413return;414}415416const response = conversation.getLatestTurn().getMetadata(CopilotInteractiveEditorResponse);417if (!response) {418return;419}420421let interactionOutcome = conversation.getLatestTurn().getMetadata(InteractionOutcome);422if (!interactionOutcome) {423interactionOutcome = new InteractionOutcome(!response.telemetry?.editCount ? 'none' : 'inlineEdit', []);424}425426if (kind === InteractiveEditorResponseFeedbackKind.Bug && conversation) {427this.feedbackReporter.reportInline(conversation, response.promptQuery, interactionOutcome);428return;429}430431const userActionProperties: { messageId: string; action?: 'undo' | 'accept' } = {432messageId: response.messageId,433};434let telemetryEventName: string;435436const { selection, wholeRange, intent, query } = response.promptQuery;437438const requestId = conversation?.getLatestTurn().id;439const intentId = intent?.id;440const languageId = response.promptQuery.document.languageId;441442// TODO: Only collect for /fix443const diagnosticsTelemetryData = (444intentId === Intent.Fix445? findDiagnosticsTelemetry(selection, this.languageDiagnosticsService.getDiagnostics(response.promptQuery.document.uri))446: undefined447);448const isNotebookDocument = isNotebookCellOrNotebookChatInput(response.promptQuery.document.uri) ? 1 : 0;449450this.surveyService.signalUsage(`inline.${intentId ?? 'default'}`, languageId);451452const sharedProps = {453languageId: languageId,454replyType: interactionOutcome.kind,455conversationId: sessionId,456requestId: requestId,457command: intentId458};459const editCount = response.telemetry?.editCount ?? 0;460const editLineCount = response.telemetry?.editLineCount ?? 0;461const sharedMeasures: TelemetryEventMeasurements = {462selectionLineCount: selection ? Math.abs(selection.end.line - selection.start.line) : -1,463wholeRangeLineCount: wholeRange ? Math.abs(wholeRange.end.line - wholeRange.start.line) : -1,464editCount: editCount > 0 ? editCount : -1,465editLineCount: editLineCount > 0 ? editLineCount : -1,466isNotebook: isNotebookDocument,467problemsCount: diagnosticsTelemetryData?.fileDiagnosticsTelemetry.problemsCount ?? 0,468selectionProblemsCount: diagnosticsTelemetryData?.selectionDiagnosticsTelemetry.problemsCount ?? 0,469diagnosticsCount: diagnosticsTelemetryData?.fileDiagnosticsTelemetry.diagnosticsCount ?? 0,470selectionDiagnosticsCount: diagnosticsTelemetryData?.selectionDiagnosticsTelemetry.diagnosticsCount ?? 0,471};472473if (kind === InteractiveEditorResponseFeedbackKind.Accepted && response.editSurvivalTracker) {474response.editSurvivalTracker.startReporter(res => reportInlineEditSurvivalEvent(res, sharedProps, sharedMeasures, this.otelService));475}476(response as any).editSurvivalTracker = undefined; // TODO@jrieken477478const accepted = (kind === InteractiveEditorResponseFeedbackKind.Accepted) ? 1 : 0;479/* __GDPR__480"inline.done" : {481"owner": "digitarald",482"comment": "Metadata about an inline code suggestion being accepted or undone",483"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The current file language." },484"replyType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "How response is shown in the interface." },485"conversationId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the inline assistant conversation." },486"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the current request turn." },487"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The command which was used in providing the response." },488"accepted": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the user accepted the suggested code or discarded it." },489"selectionLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in the current selection." },490"wholeRangeLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in the expanded whole range." },491"editCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many edits are suggested." },492"editLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in all suggested edits." },493"problemsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many problems are in the current code." },494"selectionProblemsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many problems are in the current selected code." },495"diagnosticsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many diagnostic codes are in the current code." },496"selectionDiagnosticsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many diagnostic codes are in the current code." },497"isNotebook": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the document is a notebook." }498}499*/500this.telemetryService.sendMSFTTelemetryEvent('inline.done', sharedProps, {501...sharedMeasures, accepted502});503this.telemetryService.sendGHTelemetryEvent('inline.done', sharedProps, {504...sharedMeasures, accepted505});506507emitInlineDoneEvent(this.otelService, accepted === 1, languageId, editCount, editLineCount, interactionOutcome.kind, isNotebookDocument === 1, resolveWorkspaceOTelMetadata(this.gitService, response.promptQuery.document.uri));508GenAiMetrics.recordEditAcceptance(this.otelService, 'inline_chat', accepted === 1 ? 'accepted' : 'rejected', languageId);509510this.telemetryService.sendInternalMSFTTelemetryEvent('interactiveSessionDone', {511language: languageId,512intent: intentId,513query: query,514conversationId: sessionId,515requestId: requestId,516replyType: interactionOutcome.kind,517problems: diagnosticsTelemetryData?.fileDiagnosticsTelemetry.problems ?? '',518selectionProblems: diagnosticsTelemetryData?.selectionDiagnosticsTelemetry.problems ?? '',519diagnosticCodes: diagnosticsTelemetryData?.fileDiagnosticsTelemetry.diagnosticCodes ?? '',520selectionDiagnosticCodes: diagnosticsTelemetryData?.selectionDiagnosticsTelemetry.diagnosticCodes ?? '',521}, { isNotebook: isNotebookDocument, accepted });522523switch (kind) {524case InteractiveEditorResponseFeedbackKind.Undone:525userActionProperties['action'] = 'undo';526telemetryEventName = 'inlineConversation.undo';527break;528case InteractiveEditorResponseFeedbackKind.Accepted:529userActionProperties['action'] = 'accept';530telemetryEventName = 'inlineConversation.accept';531break;532case InteractiveEditorResponseFeedbackKind.Bug:533telemetryEventName = '';534break;535}536537if (telemetryEventName) {538sendUserActionTelemetry(this.telemetryService, response.promptQuery.document, userActionProperties, {}, telemetryEventName);539}540}541}542543function reportInlineEditSurvivalEvent(res: EditSurvivalResult, sharedProps: TelemetryEventProperties | undefined, sharedMeasures: TelemetryEventMeasurements | undefined, otelService: IOTelService) {544/* __GDPR__545"inline.trackEditSurvival" : {546"owner": "hediet",547"comment": "Tracks how much percent of the AI edits surived after 5 minutes of accepting",548"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The current file language." },549"replyType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "How response is shown in the interface." },550"conversationId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the inline assistant conversation." },551"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the current request turn." },552"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The command which was used in providing the response." },553"survivalRateFourGram": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The rate between 0 and 1 of how much of the AI edit is still present in the document." },554"survivalRateNoRevert": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The rate between 0 and 1 of how much of the ranges the AI touched ended up being reverted." },555"didBranchChange": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Indicates if the branch changed in the meantime. If the branch changed (value is 1), this event should probably be ignored." },556"timeDelayMs": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The time delay between the user accepting the edit and measuring the survival rate." },557"selectionLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in the current selection." },558"wholeRangeLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in the expanded whole range." },559"editCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many edits are suggested." },560"editLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in all suggested edits." },561"problemsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many problems are in the current code." },562"selectionProblemsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many problems are in the current selected code." },563"diagnosticsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many diagnostic codes are in the current code." },564"selectionDiagnosticsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many diagnostic codes are in the current selected code." },565"isNotebook": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the document is a notebook" }566}567*/568res.telemetryService.sendMSFTTelemetryEvent('inline.trackEditSurvival', sharedProps, {569...sharedMeasures,570survivalRateFourGram: res.fourGram,571survivalRateNoRevert: res.noRevert,572timeDelayMs: res.timeDelayMs,573didBranchChange: res.didBranchChange ? 1 : 0,574});575res.telemetryService.sendGHTelemetryEvent('inline.trackEditSurvival', {576...sharedProps,577headBranchName: res.workspace?.headBranchName,578headCommitHash: res.workspace?.headCommitHash,579remoteUrl: res.workspace?.remoteUrl,580fileRelativePath: res.workspace?.fileRelativePath,581}, {582...sharedMeasures,583survivalRateFourGram: res.fourGram,584survivalRateNoRevert: res.noRevert,585timeDelayMs: res.timeDelayMs,586didBranchChange: res.didBranchChange ? 1 : 0,587});588589emitEditSurvivalEvent(otelService, 'inline_chat', res.fourGram, res.noRevert, res.timeDelayMs, res.didBranchChange, String(sharedProps?.requestId ?? ''), res.workspace);590GenAiMetrics.recordEditSurvivalFourGram(otelService, 'inline_chat', res.fourGram, res.timeDelayMs);591GenAiMetrics.recordEditSurvivalNoRevert(otelService, 'inline_chat', res.noRevert, res.timeDelayMs);592}593594const outcomes = new Map<vscode.ChatEditingSessionActionOutcome, EditOutcome>([595[vscode.ChatEditingSessionActionOutcome.Accepted, 'accepted'],596[vscode.ChatEditingSessionActionOutcome.Rejected, 'rejected'],597[vscode.ChatEditingSessionActionOutcome.Saved, 'saved']598]);599600601