Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/editTelemetry/browser/telemetry/arcTelemetrySender.ts
5266 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { onUnexpectedError } from '../../../../../base/common/errors.js';
7
import { Disposable } from '../../../../../base/common/lifecycle.js';
8
import { IObservable, runOnChange } from '../../../../../base/common/observable.js';
9
import { AnnotatedStringEdit } from '../../../../../editor/common/core/edits/stringEdit.js';
10
import { EditDeltaInfo, EditSuggestionId, ITextModelEditSourceMetadata } from '../../../../../editor/common/textModelEditSource.js';
11
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
12
import { EditSourceData, IDocumentWithAnnotatedEdits, createDocWithJustReason } from '../helpers/documentWithAnnotatedEdits.js';
13
import { IAiEditTelemetryService } from './aiEditTelemetry/aiEditTelemetryService.js';
14
import type { ScmRepoAdapter } from './scmAdapter.js';
15
import { forwardToChannelIf, isCopilotLikeExtension } from '../../../../../platform/dataChannel/browser/forwardingTelemetryService.js';
16
import { ProviderId } from '../../../../../editor/common/languages.js';
17
import { ArcTelemetryReporter } from './arcTelemetryReporter.js';
18
import { IRandomService } from '../randomService.js';
19
20
export class EditTelemetryReportInlineEditArcSender extends Disposable {
21
constructor(
22
docWithAnnotatedEdits: IDocumentWithAnnotatedEdits<EditSourceData>,
23
scmRepoBridge: IObservable<ScmRepoAdapter | undefined>,
24
@IInstantiationService private readonly _instantiationService: IInstantiationService
25
) {
26
super();
27
28
this._register(runOnChange(docWithAnnotatedEdits.value, (_val, _prev, changes) => {
29
const edit = AnnotatedStringEdit.compose(changes.map(c => c.edit));
30
31
if (!edit.replacements.some(r => r.data.editSource.metadata.source === 'inlineCompletionAccept')) {
32
return;
33
}
34
if (!edit.replacements.every(r => r.data.editSource.metadata.source === 'inlineCompletionAccept')) {
35
onUnexpectedError(new Error('ArcTelemetrySender: Not all edits are inline completion accept edits!'));
36
return;
37
}
38
if (edit.replacements[0].data.editSource.metadata.source !== 'inlineCompletionAccept') {
39
return;
40
}
41
const data = edit.replacements[0].data.editSource.metadata;
42
43
const docWithJustReason = createDocWithJustReason(docWithAnnotatedEdits, this._store);
44
const reporter = this._store.add(this._instantiationService.createInstance(ArcTelemetryReporter, [0, 30, 120, 300, 600, 900].map(s => s * 1000), _prev, docWithJustReason, scmRepoBridge, edit, res => {
45
res.telemetryService.publicLog2<{
46
extensionId: string;
47
extensionVersion: string;
48
opportunityId: string;
49
languageId: string;
50
correlationId: string | undefined;
51
didBranchChange: number;
52
timeDelayMs: number;
53
54
originalCharCount: number;
55
originalLineCount: number;
56
originalDeletedLineCount: number;
57
arc: number;
58
currentLineCount: number;
59
currentDeletedLineCount: number;
60
}, {
61
owner: 'hediet';
62
comment: 'Reports for each accepted inline suggestion (= inline completions + next edit suggestions) the accumulated retained character count after a certain time delay. This event is sent 0s, 30s, 120s, 300s, 600s and 900s after acceptance. @sentToGitHub';
63
64
extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension id which provided this inline suggestion.' };
65
extensionVersion: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version of the extension.' };
66
opportunityId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Unique identifier for an opportunity to show an inline suggestion.' };
67
languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The language id of the document.' };
68
correlationId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The correlation id of the inline suggestion.' };
69
70
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.' };
71
timeDelayMs: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The time delay between the user accepting the edit and measuring the survival rate.' };
72
73
originalCharCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The original character count before any edits.' };
74
originalLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The original line count before any edits.' };
75
originalDeletedLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The original deleted line count before any edits.' };
76
arc: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The accepted and retained character count.' };
77
currentLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The current line count after edits.' };
78
currentDeletedLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The current deleted line count after edits.' };
79
}>('editTelemetry.reportInlineEditArc', {
80
extensionId: data.$extensionId ?? '',
81
extensionVersion: data.$extensionVersion ?? '',
82
opportunityId: data.$$requestUuid ?? 'unknown',
83
languageId: data.$$languageId,
84
correlationId: data.$$correlationId,
85
didBranchChange: res.didBranchChange ? 1 : 0,
86
timeDelayMs: res.timeDelayMs,
87
88
originalCharCount: res.originalCharCount,
89
originalLineCount: res.originalLineCount,
90
originalDeletedLineCount: res.originalDeletedLineCount,
91
arc: res.arc,
92
currentLineCount: res.currentLineCount,
93
currentDeletedLineCount: res.currentDeletedLineCount,
94
95
...forwardToChannelIf(isCopilotLikeExtension(data.$extensionId)),
96
});
97
}, () => {
98
this._store.delete(reporter);
99
}));
100
}));
101
}
102
}
103
104
export class CreateSuggestionIdForChatOrInlineChatCaller extends Disposable {
105
constructor(
106
docWithAnnotatedEdits: IDocumentWithAnnotatedEdits<EditSourceData>,
107
@IAiEditTelemetryService private readonly _aiEditTelemetryService: IAiEditTelemetryService,
108
) {
109
super();
110
111
this._register(runOnChange(docWithAnnotatedEdits.value, (_val, _prev, changes) => {
112
const edit = AnnotatedStringEdit.compose(changes.map(c => c.edit));
113
114
const supportedSource = new Set(['Chat.applyEdits', 'inlineChat.applyEdits'] as ITextModelEditSourceMetadata['source'][]);
115
116
if (!edit.replacements.some(r => supportedSource.has(r.data.editSource.metadata.source))) {
117
return;
118
}
119
if (!edit.replacements.every(r => supportedSource.has(r.data.editSource.metadata.source))) {
120
onUnexpectedError(new Error(`ArcTelemetrySender: Not all edits are ${edit.replacements[0].data.editSource.metadata.source}!`));
121
return;
122
}
123
let applyCodeBlockSuggestionId: EditSuggestionId | undefined = undefined;
124
const data = edit.replacements[0].data.editSource;
125
let feature: 'inlineChat' | 'sideBarChat';
126
if (data.metadata.source === 'Chat.applyEdits') {
127
feature = 'sideBarChat';
128
if (data.metadata.$$mode === 'applyCodeBlock') {
129
applyCodeBlockSuggestionId = data.metadata.$$codeBlockSuggestionId;
130
}
131
} else {
132
feature = 'inlineChat';
133
}
134
135
const providerId = new ProviderId(data.props.$extensionId, data.props.$extensionVersion, data.props.$providerId);
136
137
// TODO@hediet tie this suggestion id to hunks, so acceptance can be correlated.
138
this._aiEditTelemetryService.createSuggestionId({
139
applyCodeBlockSuggestionId,
140
languageId: data.props.$$languageId,
141
presentation: 'highlightedEdit',
142
feature,
143
source: providerId,
144
modelId: data.props.$modelId,
145
// eslint-disable-next-line local/code-no-any-casts
146
modeId: data.props.$$mode as any,
147
editDeltaInfo: EditDeltaInfo.fromEdit(edit, _prev),
148
});
149
}));
150
}
151
}
152
153
export class EditTelemetryReportEditArcForChatOrInlineChatSender extends Disposable {
154
constructor(
155
docWithAnnotatedEdits: IDocumentWithAnnotatedEdits<EditSourceData>,
156
scmRepoBridge: IObservable<ScmRepoAdapter | undefined>,
157
@IInstantiationService private readonly _instantiationService: IInstantiationService,
158
@IRandomService private readonly _randomService: IRandomService,
159
) {
160
super();
161
162
this._register(runOnChange(docWithAnnotatedEdits.value, (_val, _prev, changes) => {
163
const edit = AnnotatedStringEdit.compose(changes.map(c => c.edit));
164
165
const supportedSource = new Set(['Chat.applyEdits', 'inlineChat.applyEdits'] as ITextModelEditSourceMetadata['source'][]);
166
167
if (!edit.replacements.some(r => supportedSource.has(r.data.editSource.metadata.source))) {
168
return;
169
}
170
if (!edit.replacements.every(r => supportedSource.has(r.data.editSource.metadata.source))) {
171
onUnexpectedError(new Error(`ArcTelemetrySender: Not all edits are ${edit.replacements[0].data.editSource.metadata.source}!`));
172
return;
173
}
174
const data = edit.replacements[0].data.editSource;
175
176
const uniqueEditId = this._randomService.generateUuid();
177
178
const docWithJustReason = createDocWithJustReason(docWithAnnotatedEdits, this._store);
179
const reporter = this._store.add(this._instantiationService.createInstance(ArcTelemetryReporter, [0, 60, 300].map(s => s * 1000), _prev, docWithJustReason, scmRepoBridge, edit, res => {
180
res.telemetryService.publicLog2<{
181
sourceKeyCleaned: string;
182
extensionId: string | undefined;
183
extensionVersion: string | undefined;
184
opportunityId: string | undefined;
185
editSessionId: string | undefined;
186
requestId: string | undefined;
187
modelId: string | undefined;
188
languageId: string | undefined;
189
mode: string | undefined;
190
uniqueEditId: string | undefined;
191
192
didBranchChange: number;
193
timeDelayMs: number;
194
195
originalCharCount: number;
196
originalLineCount: number;
197
originalDeletedLineCount: number;
198
arc: number;
199
currentLineCount: number;
200
currentDeletedLineCount: number;
201
}, {
202
owner: 'hediet';
203
comment: 'Reports the accepted and retained character count for an inline completion/edit. @sentToGitHub';
204
205
sourceKeyCleaned: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The key of the edit source.' };
206
extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension id (copilot or copilot-chat); which provided this inline completion.' };
207
extensionVersion: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version of the extension.' };
208
opportunityId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Unique identifier for an opportunity to show an inline completion or NES.' };
209
editSessionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The session id.' };
210
requestId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The request id.' };
211
modelId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The model id.' };
212
languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The language id of the document.' };
213
mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The mode chat was in.' };
214
uniqueEditId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The unique identifier for the edit.' };
215
216
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.' };
217
timeDelayMs: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The time delay between the user accepting the edit and measuring the survival rate.' };
218
219
originalCharCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The original character count before any edits.' };
220
originalLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The original line count before any edits.' };
221
originalDeletedLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The original deleted line count before any edits.' };
222
arc: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The accepted and restrained character count.' };
223
currentLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The current line count after edits.' };
224
currentDeletedLineCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The current deleted line count after edits.' };
225
}>('editTelemetry.reportEditArc', {
226
sourceKeyCleaned: data.toKey(Number.MAX_SAFE_INTEGER, {
227
$extensionId: false,
228
$extensionVersion: false,
229
$$requestUuid: false,
230
$$sessionId: false,
231
$$requestId: false,
232
$$languageId: false,
233
$modelId: false,
234
}),
235
extensionId: data.props.$extensionId,
236
extensionVersion: data.props.$extensionVersion,
237
opportunityId: data.props.$$requestUuid,
238
editSessionId: data.props.$$sessionId,
239
requestId: data.props.$$requestId,
240
modelId: data.props.$modelId,
241
languageId: data.props.$$languageId,
242
mode: data.props.$$mode,
243
uniqueEditId,
244
245
didBranchChange: res.didBranchChange ? 1 : 0,
246
timeDelayMs: res.timeDelayMs,
247
248
originalCharCount: res.originalCharCount,
249
originalLineCount: res.originalLineCount,
250
originalDeletedLineCount: res.originalDeletedLineCount,
251
arc: res.arc,
252
currentLineCount: res.currentLineCount,
253
currentDeletedLineCount: res.currentDeletedLineCount,
254
255
...forwardToChannelIf(isCopilotLikeExtension(data.props.$extensionId)),
256
});
257
}, () => {
258
this._store.delete(reporter);
259
}));
260
}));
261
}
262
}
263
264