Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/prompt/node/chatParticipantTelemetry.ts
13399 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 { PromptReference, Raw } from '@vscode/prompt-tsx';
7
import type * as vscode from 'vscode';
8
import { ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes';
9
import { getTextPart, roleToString } from '../../../platform/chat/common/globalStringUtils';
10
import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
11
import { isAutoModel } from '../../../platform/endpoint/node/autoChatEndpoint';
12
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
13
import { ILanguageDiagnosticsService } from '../../../platform/languages/common/languageDiagnosticsService';
14
import { IChatEndpoint } from '../../../platform/networking/common/networking';
15
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry';
16
import { TelemetryData as PlatformTelemetryData } from '../../../platform/telemetry/common/telemetryData';
17
import { getCachedSha256Hash } from '../../../util/common/crypto';
18
import { isNotebookCellOrNotebookChatInput } from '../../../util/common/notebooks';
19
import { extUriBiasedIgnorePathCase } from '../../../util/vs/base/common/resources';
20
import { URI } from '../../../util/vs/base/common/uri';
21
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
22
import { isBYOKModel } from '../../byok/node/openAIEndpoint';
23
import { Intent, agentsToCommands } from '../../common/constants';
24
import { DiagnosticsTelemetryData, findDiagnosticsTelemetry } from '../../inlineChat/node/diagnosticsTelemetry';
25
import { InteractionOutcome } from '../../inlineChat/node/promptCraftingTypes';
26
import { AgentIntent } from '../../intents/node/agentIntent';
27
import { EditCodeIntent } from '../../intents/node/editCodeIntent';
28
import { getCustomInstructionTelemetry } from '../../prompts/node/panel/customInstructions';
29
import { PATCH_PREFIX } from '../../tools/node/applyPatch/parseApplyPatch';
30
import { ChatVariablesCollection, parseSlashCommand } from '../common/chatVariablesCollection';
31
import { Conversation } from '../common/conversation';
32
import { IToolCall, IToolCallRound } from '../common/intents';
33
import { IDocumentContext } from './documentContext';
34
import { IIntent, TelemetryData } from './intents';
35
import { RepoInfoTelemetry } from './repoInfoTelemetry';
36
import { ConversationalBaseTelemetryData, ConversationalTelemetryData, createTelemetryWithId, extendUserMessageTelemetryData, getCodeBlocks, sendModelMessageTelemetry, sendOffTopicMessageTelemetry, sendUserActionTelemetry, sendUserMessageTelemetry } from './telemetry';
37
38
// #region: internal telemetry for responses
39
40
type ResponseInternalTelemetryProperties = {
41
chatLocation: 'inline' | 'panel';
42
intent: string;
43
request: string;
44
response: string;
45
baseModel: string;
46
apiType: string | undefined;
47
};
48
49
// EVENT: interactiveSessionResponse
50
type ResponseInternalPanelTelemetryProperties = ResponseInternalTelemetryProperties & {
51
chatLocation: 'panel';
52
requestId: string;
53
54
// shareable but NOT
55
isParticipantDetected: string;
56
sessionId: string;
57
};
58
59
// EVENT: interactiveSessionResponse
60
type ResponseInternalPanelTelemetryMeasurements = {
61
turnNumber: number;
62
};
63
64
// EVENT: interactiveSessionResponse
65
type ResponseInternalInlineTelemetryProperties = ResponseInternalTelemetryProperties & {
66
chatLocation: 'inline';
67
68
// shareable but NOT
69
conversationId: string;
70
requestId: string;
71
responseType: ChatFetchResponseType;
72
73
// editor-specific
74
problems: string;
75
selectionProblems: string;
76
diagnosticCodes: string;
77
selectionDiagnosticCodes: string;
78
diagnosticsProvider: string;
79
language: string;
80
};
81
82
// EVENT: interactiveSessionResponse
83
type ResponseInternalInlineTelemetryMeasurements = {
84
isNotebook: number;
85
turnNumber: number;
86
};
87
88
// #endregion
89
90
// #region: internal telemetry for requests
91
92
// EVENT: interactiveSessionMessage
93
94
type RequestInternalPanelTelemetryProperties = {
95
chatLocation: 'panel';
96
sessionId: string;
97
requestId: string;
98
baseModel: string;
99
apiType: string | undefined;
100
intent: string;
101
isParticipantDetected: string;
102
detectedIntent: string;
103
contextTypes: string;
104
query: string;
105
};
106
107
// EVENT: interactiveSessionRequest
108
109
type RequestInternalInlineTelemetryProperties = {
110
chatLocation: 'inline';
111
conversationId: string;
112
requestId: string;
113
intent: string;
114
language: string;
115
prompt: string;
116
model: string;
117
apiType: string | undefined;
118
};
119
120
type RequestInternalInlineTelemetryMeasurements = {
121
isNotebook: number;
122
turnNumber: number;
123
};
124
125
// #endregion
126
127
128
//#region public telemetry for requests
129
130
// EVENT: panel.request
131
132
type RequestTelemetryProperties = {
133
command: string;
134
contextTypes: string;
135
promptTypes: string;
136
conversationId: string;
137
requestId: string;
138
139
responseType: string;
140
languageId: string | undefined;
141
model: string;
142
apiType: string | undefined;
143
toolCounts: string;
144
};
145
146
type RequestPanelTelemetryProperties = RequestTelemetryProperties & {
147
responseId: string;
148
codeBlocks: string;
149
isParticipantDetected: string;
150
mode: string;
151
parentRequestId: string | undefined;
152
vscodeRequestId: string | undefined;
153
slashCommand: string;
154
isSystemInitiated: string;
155
};
156
157
type RequestTelemetryMeasurements = {
158
promptTokenCount: number;
159
timeToRequest: number;
160
timeToFirstToken: number;
161
timeToComplete: number;
162
responseTokenCount: number;
163
messageTokenCount: number;
164
numToolCalls: number;
165
availableToolCount: number;
166
toolTokenCount: number;
167
isBYOK: number;
168
isAuto: number;
169
};
170
171
type RequestPanelTelemetryMeasurements = RequestTelemetryMeasurements & {
172
turn: number;
173
round: number;
174
textBlocks: number;
175
links: number;
176
maybeOffTopic: number;
177
userPromptCount: number;
178
summarizationEnabled: number;
179
};
180
181
// EVENT: inline.request
182
183
type RequestInlineTelemetryProperties = RequestTelemetryProperties & {
184
languageId: string;
185
replyType: string;
186
diagnosticsProvider: string;
187
diagnosticCodes: string;
188
selectionDiagnosticCodes: string;
189
outcomeAnnotations: string;
190
};
191
192
type RequestInlineTelemetryMeasurements = RequestTelemetryMeasurements & {
193
firstTurn: number;
194
isNotebook: number;
195
withIntentDetection: number;
196
implicitCommand: number;
197
attemptCount: number;
198
selectionLineCount: number;
199
wholeRangeLineCount: number;
200
editCount: number;
201
editLineCount: number;
202
markdownCharCount: number;
203
problemsCount: number;
204
selectionProblemsCount: number;
205
diagnosticsCount: number;
206
selectionDiagnosticsCount: number;
207
};
208
209
//#endregion
210
211
const builtinSlashCommands = new Set(
212
Object.values(agentsToCommands).flatMap(commands => commands ? Object.keys(commands) : [])
213
);
214
215
function getSlashCommandForTelemetry(request: vscode.ChatRequest, extensionUri: URI): string {
216
// Built-in slash commands (explain, fix, tests, etc.) are safe to send as plain text
217
if (request.command && builtinSlashCommands.has(request.command)) {
218
return request.command;
219
}
220
221
// Parse the query for /command and match against prompt file references
222
const match = parseSlashCommand(request.prompt, new ChatVariablesCollection(request.references));
223
if (!match) {
224
return '';
225
}
226
227
// Extension-provided prompt files are safe to send as plain text
228
if (URI.isUri(match.variable.value) && extUriBiasedIgnorePathCase.isEqualOrParent(match.variable.value, extensionUri)) {
229
return match.command;
230
}
231
232
// User-defined prompt file slash commands may contain PII — hash them
233
return getCachedSha256Hash(match.command);
234
}
235
236
export class ChatTelemetryBuilder {
237
238
public readonly baseUserTelemetry: ConversationalBaseTelemetryData;
239
240
private readonly _repoInfoTelemetry: RepoInfoTelemetry;
241
242
public get telemetryMessageId() {
243
return this.baseUserTelemetry.properties.messageId;
244
}
245
246
constructor(
247
private readonly _startTime: number,
248
private readonly _sessionId: string,
249
private readonly _documentContext: IDocumentContext | undefined,
250
private readonly _firstTurn: boolean,
251
private readonly _request: vscode.ChatRequest,
252
telemetryMessageId: string | undefined,
253
@IInstantiationService private readonly instantiationService: IInstantiationService,
254
) {
255
this.baseUserTelemetry = telemetryMessageId
256
? new ConversationalTelemetryData(PlatformTelemetryData.createAndMarkAsIssued({ messageId: telemetryMessageId }))
257
: createTelemetryWithId();
258
// Repo info telemetry is held here as the begin event should be sent only by the first PanelChatTelemetry instance created for a user request.
259
// and a new PanelChatTelemetry instance is created per step in the request.
260
this._repoInfoTelemetry = this.instantiationService.createInstance(RepoInfoTelemetry, this.baseUserTelemetry.properties.messageId);
261
}
262
263
public makeRequest(intent: IIntent, location: ChatLocation.Editor, conversation: Conversation, messages: Raw.ChatMessage[], promptTokenLength: number, references: readonly PromptReference[], endpoint: IChatEndpoint, telemetryData: readonly TelemetryData[], availableToolCount: number, toolTokenCount: number): InlineChatTelemetry;
264
public makeRequest(intent: IIntent, location: ChatLocation, conversation: Conversation, messages: Raw.ChatMessage[], promptTokenLength: number, references: readonly PromptReference[], endpoint: IChatEndpoint, telemetryData: readonly TelemetryData[], availableToolCount: number, toolTokenCount: number): PanelChatTelemetry;
265
public makeRequest(intent: IIntent, location: ChatLocation, conversation: Conversation, messages: Raw.ChatMessage[], promptTokenLength: number, references: readonly PromptReference[], endpoint: IChatEndpoint, telemetryData: readonly TelemetryData[], availableToolCount: number, toolTokenCount: number): InlineChatTelemetry | PanelChatTelemetry {
266
267
if (location === ChatLocation.Editor) {
268
return this.instantiationService.createInstance(InlineChatTelemetry,
269
this._sessionId,
270
this._documentContext!,
271
this._firstTurn,
272
this._request,
273
this._startTime,
274
this.baseUserTelemetry,
275
conversation,
276
intent,
277
messages,
278
references,
279
endpoint,
280
promptTokenLength,
281
telemetryData,
282
availableToolCount,
283
toolTokenCount,
284
this._repoInfoTelemetry
285
);
286
} else {
287
return this.instantiationService.createInstance(PanelChatTelemetry,
288
this._sessionId,
289
this._documentContext!,
290
this._firstTurn,
291
this._request,
292
this._startTime,
293
this.baseUserTelemetry,
294
conversation,
295
intent,
296
messages,
297
references,
298
endpoint,
299
promptTokenLength,
300
telemetryData,
301
availableToolCount,
302
toolTokenCount,
303
this._repoInfoTelemetry
304
);
305
}
306
}
307
}
308
309
export abstract class ChatTelemetry<C extends IDocumentContext | undefined = IDocumentContext | undefined> {
310
311
protected readonly _userTelemetry: ConversationalBaseTelemetryData;
312
313
protected readonly _requestStartTime: number = Date.now();
314
protected _firstTokenTime: number = 0;
315
316
protected _addedLinkCount = 0;
317
protected _markdownCharCount: number = 0;
318
protected _editCount: number = 0;
319
protected _editLineCount: number = 0;
320
321
// todo@connor4312: temporary event to track occurences of patches in response
322
// text, ref https://github.com/microsoft/vscode-copilot/issues/16608
323
private _didSeePatchInResponse = false;
324
private _lastMarkdownLine = '';
325
326
public get telemetryMessageId(): string {
327
return this._userTelemetry.properties.messageId;
328
}
329
330
public get editCount(): number {
331
return this._editCount;
332
}
333
334
public get editLineCount(): number {
335
return this._editLineCount;
336
}
337
338
public get sessionId(): string {
339
return this._sessionId;
340
}
341
342
constructor(
343
protected readonly _location: ChatLocation,
344
protected readonly _sessionId: string,
345
protected readonly _documentContext: C,
346
protected readonly _firstTurn: boolean,
347
protected readonly _request: vscode.ChatRequest,
348
protected readonly _startTime: number,
349
baseUserTelemetry: ConversationalBaseTelemetryData,
350
protected readonly _conversation: Conversation,
351
protected readonly _intent: IIntent,
352
protected readonly _messages: Raw.ChatMessage[],
353
protected readonly _references: readonly PromptReference[],
354
protected readonly _endpoint: IChatEndpoint,
355
promptTokenLength: number,
356
protected readonly _genericTelemetryData: readonly TelemetryData[],
357
protected readonly _availableToolCount: number,
358
protected readonly _toolTokenCount: number,
359
protected readonly _repoInfoTelemetry: RepoInfoTelemetry,
360
@ITelemetryService protected readonly _telemetryService: ITelemetryService,
361
) {
362
// Extend the base user telemetry with message and prompt information.
363
// We don't send this telemetry yet, but we will need it later to include the off topic scores.
364
this._userTelemetry = extendUserMessageTelemetryData(
365
this._conversation,
366
this._sessionId,
367
this._location,
368
this._request.prompt,
369
promptTokenLength,
370
// this._tokenizer.countMessagesTokens(this._messages),
371
this._intent.id,
372
baseUserTelemetry
373
);
374
375
// we are in a super-ctor and use a microtask to give sub-classes a change to initialize properties
376
// that might be used in their _sendInternalRequestTelemetryEvent-method
377
queueMicrotask(() => this._sendInternalRequestTelemetryEvent());
378
}
379
380
public markReceivedToken(): void {
381
if (this._firstTokenTime === 0) {
382
this._firstTokenTime = Date.now();
383
}
384
}
385
386
public markAddedLinks(n: number): void {
387
this._addedLinkCount += n;
388
}
389
390
public markEmittedMarkdown(str: vscode.MarkdownString) {
391
this._markdownCharCount += str.value.length;
392
this._lastMarkdownLine += str.value;
393
if (this._lastMarkdownLine.includes(PATCH_PREFIX.trim())) {
394
this._didSeePatchInResponse = true;
395
}
396
397
const i = this._lastMarkdownLine.lastIndexOf('\n');
398
this._lastMarkdownLine = this._lastMarkdownLine.slice(i + 1);
399
}
400
401
public markEmittedEdits(uri: vscode.Uri, edits: vscode.TextEdit[]) {
402
this._editCount += edits.length;
403
this._editLineCount += edits.reduce((acc, edit) => acc + edit.newText.split('\n').length, 0);
404
}
405
406
public async sendTelemetry(requestId: string, responseType: ChatFetchResponseType, response: string, interactionOutcome: InteractionOutcome, toolCalls: IToolCall[]): Promise<void> {
407
// We can send the user message telemetry event now that the response is returned, including off-topic prediction.
408
sendUserMessageTelemetry(
409
this._telemetryService,
410
this._location,
411
requestId,
412
this._request.prompt,
413
responseType === ChatFetchResponseType.OffTopic ? true : false,
414
this._documentContext?.document,
415
this._userTelemetry,
416
this._getModeNameForTelemetry(),
417
);
418
419
if (responseType === ChatFetchResponseType.OffTopic) {
420
sendOffTopicMessageTelemetry(
421
this._telemetryService,
422
this._conversation,
423
this._location,
424
this._request.prompt,
425
this.telemetryMessageId, // That's the message id of the user message
426
this._documentContext?.document,
427
this._userTelemetry
428
);
429
}
430
431
if (responseType === ChatFetchResponseType.Success) {
432
sendModelMessageTelemetry(
433
this._telemetryService,
434
this._conversation,
435
this._location,
436
response,
437
this.telemetryMessageId, // That's the message id of the user message
438
this._documentContext?.document,
439
this._userTelemetry.extendedBy({ replyType: interactionOutcome.kind }),
440
this._getModeNameForTelemetry()
441
);
442
}
443
444
await this._sendResponseTelemetryEvent(responseType, response, interactionOutcome, toolCalls);
445
this._sendResponseInternalTelemetryEvent(responseType, response);
446
447
448
// todo@connor4312: temporary event to track occurences of patches in response
449
// text, ref https://github.com/microsoft/vscode-copilot/issues/16608
450
if (this._didSeePatchInResponse) {
451
/* __GDPR__
452
"applyPatch.inResponse" : {
453
"owner": "digitarald",
454
"comment": "Metadata about an inline response from the model",
455
"model": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The model that is used in the endpoint." }
456
}
457
*/
458
this._telemetryService.sendMSFTTelemetryEvent('applyPatch.inResponse', {
459
model: this._endpoint.model
460
});
461
}
462
}
463
464
protected _getModeNameForTelemetry(): string {
465
return this._request.modeInstructions2 ? (this._request.modeInstructions2.isBuiltin ? this._request.modeInstructions2.name.toLowerCase() : 'custom') :
466
this._intent.id === AgentIntent.ID ? 'agent' :
467
(this._intent.id === EditCodeIntent.ID) ? 'edit' :
468
(this._intent.id === Intent.InlineChat) ? 'inlineChatIntent' :
469
'ask';
470
}
471
472
public sendToolCallingTelemetry(toolCallRounds: IToolCallRound[], availableTools: readonly vscode.LanguageModelToolInformation[], responseType: ChatFetchResponseType | 'cancelled' | 'maxToolCalls'): void {
473
if (availableTools.length === 0) {
474
return;
475
}
476
477
const toolCounts = toolCallRounds.reduce((acc, round) => {
478
round.toolCalls.forEach(call => {
479
acc[call.name] = (acc[call.name] || 0) + 1;
480
});
481
return acc;
482
}, {} as Record<string, number>);
483
484
const invalidToolCallCount = toolCallRounds.reduce((acc, round) => {
485
if (round.toolInputRetry > 0) {
486
acc++;
487
}
488
return acc;
489
}, 0);
490
491
let totalToolCalls = 0;
492
let parallelToolCallRounds = 0;
493
let parallelToolCallsTotal = 0;
494
for (const round of toolCallRounds) {
495
const count = round.toolCalls.length;
496
totalToolCalls += count;
497
if (count > 1) {
498
parallelToolCallRounds++;
499
parallelToolCallsTotal += count;
500
}
501
}
502
503
const toolCallProperties = {
504
intentId: this._intent.id,
505
conversationId: this._conversation.sessionId,
506
requestId: this.telemetryMessageId,
507
responseType,
508
toolCounts: JSON.stringify(toolCounts),
509
model: this._endpoint.model
510
};
511
512
const toolCallMeasurements = {
513
numRequests: toolCallRounds.length, // This doesn't include cancelled requests
514
turnIndex: this._conversation.turns.length,
515
sessionDuration: Date.now() - this._conversation.turns[0].startTime,
516
turnDuration: Date.now() - this._conversation.getLatestTurn().startTime,
517
promptTokenCount: this._userTelemetry.measurements.promptTokenLen,
518
messageCharLen: this._userTelemetry.measurements.messageCharLen,
519
availableToolCount: availableTools.length,
520
toolTokenCount: this._toolTokenCount,
521
invalidToolCallCount,
522
totalToolCalls,
523
parallelToolCallRounds,
524
parallelToolCallsTotal
525
};
526
527
/* __GDPR__
528
"toolCallDetails" : {
529
"owner": "roblourens",
530
"comment": "Records information about tool calls during a request.",
531
"intentId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for the invoked intent." },
532
"conversationId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for the current chat conversation." },
533
"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for the current turn request." },
534
"numRequests": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The total number of requests made" },
535
"turnIndex": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The conversation turn index" },
536
"toolCounts": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The number of times each tool was used" },
537
"sessionDuration": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The time since the session started" },
538
"turnDuration": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The time since the turn started" },
539
"promptTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many tokens were in the last generated prompt." },
540
"messageCharLen": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many characters were in the user message." },
541
"availableToolCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How number of tools that were available." },
542
"toolTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many tokens were used by tool definitions." },
543
"responseType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the final response was successful or how it failed." },
544
"invalidToolCallCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The number of tool call rounds that had an invalid tool call." },
545
"model": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The model used for the request." },
546
"totalToolCalls": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Total number of tool calls across all rounds." },
547
"parallelToolCallRounds": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Number of rounds where the model called multiple tools in parallel." },
548
"parallelToolCallsTotal": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Total number of tool calls that were part of a parallel round." }
549
}
550
*/
551
this._telemetryService.sendMSFTTelemetryEvent('toolCallDetails', toolCallProperties, toolCallMeasurements);
552
553
this._telemetryService.sendInternalMSFTTelemetryEvent('toolCallDetailsInternal', {
554
...toolCallProperties,
555
messageId: this.telemetryMessageId,
556
availableTools: JSON.stringify(availableTools.map(tool => tool.name))
557
}, toolCallMeasurements);
558
559
this._telemetryService.sendEnhancedGHTelemetryEvent('toolCallDetailsExternal', {
560
...toolCallProperties,
561
messageId: this.telemetryMessageId,
562
availableTools: JSON.stringify(availableTools.map(tool => tool.name))
563
}, toolCallMeasurements);
564
565
// Send internal repo info telemetry at the end of the tool loop
566
this._repoInfoTelemetry.sendEndTelemetry();
567
}
568
569
protected abstract _sendInternalRequestTelemetryEvent(): void;
570
571
protected abstract _sendResponseTelemetryEvent(responseType: ChatFetchResponseType, response: string, interactionOutcome: InteractionOutcome, toolCalls?: IToolCall[]): Promise<void>;
572
573
protected abstract _sendResponseInternalTelemetryEvent(responseType: ChatFetchResponseType, response: string): void;
574
575
protected _getTelemetryData<T extends TelemetryData>(ctor: new (...args: any[]) => T): T | undefined {
576
return <T>this._genericTelemetryData.find(d => d instanceof ctor);
577
}
578
579
}
580
581
export class PanelChatTelemetry extends ChatTelemetry<IDocumentContext | undefined> {
582
583
constructor(
584
sessionId: string,
585
documentContext: IDocumentContext | undefined,
586
firstTurn: boolean,
587
request: vscode.ChatRequest,
588
startTime: number,
589
baseUserTelemetry: ConversationalBaseTelemetryData,
590
conversation: Conversation,
591
intent: IIntent,
592
messages: Raw.ChatMessage[],
593
references: readonly PromptReference[],
594
endpoint: IChatEndpoint,
595
promptTokenLength: number,
596
genericTelemetryData: readonly TelemetryData[],
597
availableToolCount: number,
598
toolTokenCount: number,
599
repoInfoTelemetry: RepoInfoTelemetry,
600
@ITelemetryService telemetryService: ITelemetryService,
601
@IConfigurationService private readonly _configurationService: IConfigurationService,
602
@IVSCodeExtensionContext private readonly _extensionContext: IVSCodeExtensionContext,
603
) {
604
super(ChatLocation.Panel,
605
sessionId,
606
documentContext,
607
firstTurn,
608
request,
609
startTime,
610
baseUserTelemetry,
611
conversation,
612
intent,
613
messages,
614
references,
615
endpoint,
616
promptTokenLength,
617
genericTelemetryData,
618
availableToolCount,
619
toolTokenCount,
620
repoInfoTelemetry,
621
telemetryService
622
);
623
}
624
625
protected override _sendInternalRequestTelemetryEvent(): void {
626
627
628
// Capture the created prompt in internal telemetry
629
this._telemetryService.sendInternalMSFTTelemetryEvent('interactiveSessionMessage', {
630
chatLocation: 'panel',
631
sessionId: this._sessionId,
632
requestId: this.telemetryMessageId,
633
baseModel: this._endpoint.model,
634
apiType: this._endpoint.apiType,
635
intent: this._intent.id,
636
isParticipantDetected: String(this._request.isParticipantDetected),
637
detectedIntent: this._request.enableCommandDetection ? this._intent?.id : 'none',
638
contextTypes: 'none', // TODO this is defunct
639
query: this._request.prompt
640
} satisfies RequestInternalPanelTelemetryProperties, {
641
turnNumber: this._conversation.turns.length,
642
} satisfies ResponseInternalPanelTelemetryMeasurements);
643
644
// Send the begin telemetry for repo info, this uses the same repo info telemetry instance held by the builder class
645
// as the begin event need to be sent only once per user request and PanelChatTelemetry is recreated per step. The class is
646
// guarded to only send one time.
647
this._repoInfoTelemetry.sendBeginTelemetryIfNeeded();
648
}
649
650
protected override async _sendResponseTelemetryEvent(responseType: ChatFetchResponseType, response: string, interactionOutcome: InteractionOutcome, toolCalls: IToolCall[] = []): Promise<void> {
651
652
653
const turn = this._conversation.getLatestTurn();
654
const roundIndex = turn.rounds.length - 1;
655
656
const codeBlocks = response ? getCodeBlocks(response) : [];
657
const codeBlockLanguages = codeBlocks.map(block => block.languageId);
658
659
// TBD@digitarald: This is a first cheap way to detect off-topic LLM responses.
660
const offTopicHints = ['programming-related tasks', 'programming related questions', 'software development topics', 'related to programming', 'expertise is limited', 'sorry, i can\'t assist with that'];
661
let maybeOffTopic = 0;
662
if (responseType === ChatFetchResponseType.Success && !response.trim().includes('\n')) {
663
// Check responseMessage
664
if (offTopicHints.some(flag => response.toLowerCase().includes(flag))) {
665
maybeOffTopic = 1;
666
}
667
}
668
669
const toolCounts = toolCalls.reduce((acc, call) => {
670
acc[call.name] = (acc[call.name] || 0) + 1;
671
return acc;
672
}, {} as Record<string, number>);
673
674
const messageTokenCount = await this._endpoint.acquireTokenizer().tokenLength(turn.request.message);
675
const promptTokenCount = await this._endpoint.acquireTokenizer().countMessagesTokens(this._messages);
676
const responseTokenCount = await this._endpoint.acquireTokenizer().tokenLength(response) ?? 0;
677
678
/* __GDPR__
679
"panel.request" : {
680
"owner": "digitarald",
681
"comment": "Metadata about one message turn in a chat conversation.",
682
"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The command which was used in providing the response." },
683
"contextTypes": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The context parts which were used in providing the response." },
684
"promptTypes": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The prompt types and their length which were used in providing the response." },
685
"conversationId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for the current chat conversation." },
686
"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for this message request." },
687
"responseId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for this message response." },
688
"responseType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "If the response was successful or how it failed." },
689
"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The language of the active editor." },
690
"codeBlocks": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Code block languages in the response." },
691
"model": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The model that is used in the endpoint." },
692
"apiType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The API type used in the endpoint- responses or chatCompletions" },
693
"turn": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many turns have been made in the conversation." },
694
"round": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The current round index of the turn." },
695
"textBlocks": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "For text-only responses (no code), how many paragraphs were in the response." },
696
"links": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Symbol and file links in the response.", "isMeasurement": true },
697
"maybeOffTopic": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "If the response sounds like it got rejected due to the request being off-topic." },
698
"messageTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many characters were in the user message." },
699
"promptTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many characters were in the generated prompt." },
700
"userPromptCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many user messages were in the generated prompt." },
701
"responseTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many characters were in the response." },
702
"timeToRequest": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to start the final request." },
703
"timeToFirstToken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to get the first token." },
704
"timeToComplete": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to complete the request." },
705
"codeGenInstructionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instructions are in the request." },
706
"codeGenInstructionsLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whats the length of the code generation instructions that were added to request." },
707
"codeGenInstructionsFilteredCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instructions were filtered." },
708
"codeGenInstructionFileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instruction files were read." },
709
"codeGenInstructionSettingsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instructions originated from settings." },
710
"toolCounts": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The number of times each tool was used" },
711
"numToolCalls": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The total number of tool calls" },
712
"availableToolCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How number of tools that were available." },
713
"toolTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many tokens were used by tool definitions." },
714
"summarizationEnabled" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "Whether summarization is enabled (the default) or disabled (via user setting)" },
715
"isBYOK": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the request was for a BYOK model" },
716
"isAuto": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the request was for an Auto model" },
717
"mode": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The chat mode used for this request (e.g., ask, edit, agent, custom)." },
718
"parentRequestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The parent request id if this request is from a subagent." },
719
"vscodeRequestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The VS Code chat request id, for joining with VS Code telemetry events." },
720
"slashCommand": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The slash command used by the user, if any (e.g. troubleshoot, explain). Empty if no slash command was used." },
721
"isSystemInitiated": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Whether the request was system-initiated (e.g. terminal completion notification) rather than user-typed." }
722
}
723
*/
724
this._telemetryService.sendMSFTTelemetryEvent('panel.request', {
725
command: this._intent.id,
726
contextTypes: 'none', // TODO this is defunct
727
promptTypes: this._messages.map(msg => `${msg.role}${'name' in msg && msg.name ? `-${msg.name}` : ''}:${getTextPart(msg.content).length}`).join(','),
728
conversationId: this._sessionId,
729
requestId: turn.id,
730
responseId: turn.id, // SAME as fetchResult.requestId ,
731
responseType,
732
languageId: this._documentContext?.document.languageId,
733
codeBlocks: codeBlockLanguages.join(','),
734
model: this._endpoint.model,
735
apiType: this._endpoint.apiType,
736
isParticipantDetected: String(this._request.isParticipantDetected),
737
toolCounts: JSON.stringify(toolCounts),
738
mode: this._getModeNameForTelemetry(),
739
parentRequestId: this._request.parentRequestId,
740
vscodeRequestId: this._request.id,
741
slashCommand: getSlashCommandForTelemetry(this._request, URI.from(this._extensionContext.extensionUri)),
742
isSystemInitiated: String(!!this._request.isSystemInitiated)
743
} satisfies RequestPanelTelemetryProperties, {
744
turn: this._conversation.turns.length,
745
round: roundIndex,
746
textBlocks: codeBlocks.length ? -1 : response.split(/\n{2,}/).length ?? 0,
747
links: this._addedLinkCount,
748
maybeOffTopic: maybeOffTopic,
749
messageTokenCount,
750
promptTokenCount,
751
userPromptCount: this._messages.filter(msg => msg.role === Raw.ChatRole.User).length,
752
responseTokenCount,
753
timeToRequest: this._requestStartTime - this._startTime,
754
timeToFirstToken: this._firstTokenTime ? this._firstTokenTime - this._startTime : -1,
755
timeToComplete: Date.now() - this._startTime,
756
...getCustomInstructionTelemetry(turn.references),
757
numToolCalls: toolCalls.length,
758
availableToolCount: this._availableToolCount,
759
toolTokenCount: this._toolTokenCount,
760
summarizationEnabled: this._configurationService.getConfig(ConfigKey.SummarizeAgentConversationHistory) ? 1 : 0,
761
isBYOK: isBYOKModel(this._endpoint),
762
isAuto: isAutoModel(this._endpoint)
763
} satisfies RequestPanelTelemetryMeasurements);
764
765
const modeName = this._getModeNameForTelemetry();
766
sendUserActionTelemetry(
767
this._telemetryService,
768
undefined,
769
{
770
command: this._intent.id,
771
conversationId: this._sessionId,
772
requestId: turn.id,
773
responseType,
774
languageId: this._documentContext?.document.languageId ?? '',
775
model: this._endpoint.model,
776
isParticipantDetected: String(this._request.isParticipantDetected),
777
toolCounts: JSON.stringify(toolCounts),
778
mode: modeName,
779
codeBlocks: JSON.stringify(codeBlocks),
780
vscodeRequestId: this._request.id,
781
},
782
{
783
isAgent: this._intent.id === AgentIntent.ID ? 1 : 0,
784
turn: this._conversation.turns.length,
785
round: roundIndex,
786
textBlocks: codeBlocks.length ? -1 : response.split(/\n{2,}/).length ?? 0,
787
links: this._addedLinkCount,
788
maybeOffTopic,
789
messageTokenCount,
790
promptTokenCount,
791
userPromptCount: this._messages.filter(msg => msg.role === Raw.ChatRole.User).length,
792
responseTokenCount,
793
timeToRequest: this._requestStartTime - this._startTime,
794
timeToFirstToken: this._firstTokenTime ? this._firstTokenTime - this._startTime : -1,
795
timeToComplete: Date.now() - this._startTime,
796
numToolCalls: toolCalls.length,
797
availableToolCount: this._availableToolCount,
798
},
799
'panel_request'
800
);
801
}
802
803
protected override _sendResponseInternalTelemetryEvent(_responseType: ChatFetchResponseType, response: string): void {
804
805
this._telemetryService.sendInternalMSFTTelemetryEvent('interactiveSessionResponse', {
806
// shared
807
chatLocation: 'panel',
808
requestId: this.telemetryMessageId,
809
intent: this._intent.id,
810
request: this._request.prompt,
811
response: response ?? '',
812
baseModel: this._endpoint.model,
813
apiType: this._endpoint.apiType,
814
815
// shareable but NOT
816
isParticipantDetected: String(this._request.isParticipantDetected),
817
sessionId: this._sessionId,
818
} satisfies ResponseInternalPanelTelemetryProperties, {
819
turnNumber: this._conversation.turns.length,
820
} satisfies ResponseInternalPanelTelemetryMeasurements);
821
}
822
}
823
824
export class InlineChatTelemetry extends ChatTelemetry<IDocumentContext> {
825
826
private readonly _diagnosticsTelemetryData: {
827
fileDiagnosticsTelemetry: DiagnosticsTelemetryData;
828
selectionDiagnosticsTelemetry: DiagnosticsTelemetryData;
829
diagnosticsProvider: string;
830
};
831
832
private get _isNotebookDocument(): number {
833
return isNotebookCellOrNotebookChatInput(this._documentContext.document.uri) ? 1 : 0;
834
}
835
836
constructor(
837
sessionId: string,
838
documentContext: IDocumentContext,
839
firstTurn: boolean,
840
request: vscode.ChatRequest,
841
startTime: number,
842
baseUserTelemetry: ConversationalBaseTelemetryData,
843
conversation: Conversation,
844
intent: IIntent,
845
messages: Raw.ChatMessage[],
846
references: readonly PromptReference[],
847
endpoint: IChatEndpoint,
848
promptTokenLength: number,
849
genericTelemetryData: readonly TelemetryData[],
850
availableToolCount: number,
851
toolTokenCount: number,
852
repoInfoTelemetry: RepoInfoTelemetry,
853
@ITelemetryService telemetryService: ITelemetryService,
854
@ILanguageDiagnosticsService private readonly _languageDiagnosticsService: ILanguageDiagnosticsService,
855
) {
856
super(ChatLocation.Editor,
857
sessionId,
858
documentContext,
859
firstTurn,
860
request,
861
startTime,
862
baseUserTelemetry,
863
conversation,
864
intent,
865
messages,
866
references,
867
endpoint,
868
promptTokenLength,
869
genericTelemetryData,
870
availableToolCount,
871
toolTokenCount,
872
repoInfoTelemetry,
873
telemetryService
874
);
875
876
this._diagnosticsTelemetryData = findDiagnosticsTelemetry(this._documentContext.selection, this._languageDiagnosticsService.getDiagnostics(this._documentContext.document.uri));
877
}
878
879
protected override _sendInternalRequestTelemetryEvent(): void {
880
// Capture the created prompt in internal telemetry
881
this._telemetryService.sendInternalMSFTTelemetryEvent('interactiveSessionRequest', {
882
conversationId: this._sessionId,
883
requestId: this.telemetryMessageId,
884
chatLocation: 'inline',
885
intent: this._intent.id,
886
language: this._documentContext.document.languageId,
887
prompt: this._messages.map(m => `${roleToString(m.role).toUpperCase()}:\n${m.content}`).join('\n---\n'),
888
model: this._endpoint.model,
889
apiType: this._endpoint.apiType
890
} satisfies RequestInternalInlineTelemetryProperties, {
891
isNotebook: this._isNotebookDocument,
892
turnNumber: this._conversation.turns.length,
893
} satisfies RequestInternalInlineTelemetryMeasurements);
894
}
895
896
protected override async _sendResponseTelemetryEvent(responseType: ChatFetchResponseType, response: string, interactionOutcome: InteractionOutcome, toolCalls: IToolCall[] = []): Promise<void> {
897
898
const toolCounts = toolCalls.reduce((acc, call) => {
899
acc[call.name] = (acc[call.name] || 0) + 1;
900
return acc;
901
}, {} as Record<string, number>);
902
903
/* __GDPR__
904
"inline.request" : {
905
"owner": "digitarald",
906
"comment": "Metadata about an inline response from the model",
907
"command": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The command which was used in providing the response." },
908
"contextTypes": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The context parts which were used in providing the response." },
909
"promptTypes": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The prompt types and their length which were used in providing the response." },
910
"conversationId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The id of the conversation." },
911
"requestId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Id for this message request." },
912
"languageId": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The language of the current document." },
913
"responseType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The result type of the response." },
914
"replyType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "How response is shown in the interface." },
915
"model": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The model that is used in the endpoint." },
916
"apiType": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The API type used in the endpoint- responses or chatCompletions" },
917
"diagnosticsProvider": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The diagnostics provider." },
918
"diagnosticCodes": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The diagnostics codes in the file." },
919
"selectionDiagnosticCodes": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "The selected diagnostics codes." },
920
"firstTurn": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether this is the first turn in the conversation." },
921
"isNotebook": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether this is a notebook document." },
922
"messageTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many tokens are in the rest of the query, without the command." },
923
"promptTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many tokens are in the overall prompt." },
924
"responseTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many tokens were in the response." },
925
"implicitCommand": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the command was implictly detected or provided by the user." },
926
"attemptCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many times the user has retried." },
927
"selectionLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in the current selection." },
928
"wholeRangeLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in the expanded whole range." },
929
"editCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many edits are suggested." },
930
"editLineCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many lines are in all suggested edits." },
931
"markdownCharCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many characters were emitted as markdown to vscode in the response stream." },
932
"problemsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many problems are in the current document." },
933
"selectionProblemsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many problems are in the current selected code." },
934
"diagnosticsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many diagnostic codes are in the current ." },
935
"selectionDiagnosticsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many diagnostic codes are in the code at the selection." },
936
"outcomeAnnotations": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "comment": "Annotations about the outcome of the request." },
937
"timeToRequest": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to start the final request." },
938
"timeToFirstToken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to get the first token." },
939
"timeToComplete": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true, "comment": "How long it took to complete the request." },
940
"codeGenInstructionsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instructions are in the request." },
941
"codeGenInstructionsLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The length of the code generation instructions that were added to request." },
942
"codeGenInstructionsFilteredCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instructions were filtered." },
943
"codeGenInstructionFileCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instruction files were read." },
944
"codeGenInstructionSettingsCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many code generation instructions originated from settings." },
945
"toolCounts": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": false, "comment": "The number of times each tool was used" },
946
"numToolCalls": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "The total number of tool calls" },
947
"availableToolCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How number of tools that were available." },
948
"toolTokenCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "How many tokens were used by tool definitions." },
949
"isBYOK": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the request was for a BYOK model" },
950
"isAuto": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Whether the request was for an Auto model" }
951
}
952
*/
953
this._telemetryService.sendMSFTTelemetryEvent('inline.request', {
954
command: this._intent.id,
955
contextTypes: 'none',// TODO@jrieken intentResult.contexts.map(part => part.kind).join(',') ?? 'none',
956
promptTypes: this._messages.map(msg => `${msg.role}${'name' in msg && msg.name ? `-${msg.name}` : ''}:${getTextPart(msg.content).length}`).join(','),
957
conversationId: this._sessionId,
958
requestId: this.telemetryMessageId,
959
languageId: this._documentContext.document.languageId,
960
responseType: responseType,
961
replyType: interactionOutcome.kind,
962
model: this._endpoint.model,
963
apiType: this._endpoint.apiType,
964
diagnosticsProvider: this._diagnosticsTelemetryData.diagnosticsProvider,
965
diagnosticCodes: this._diagnosticsTelemetryData.fileDiagnosticsTelemetry.diagnosticCodes,
966
selectionDiagnosticCodes: this._diagnosticsTelemetryData.selectionDiagnosticsTelemetry.diagnosticCodes,
967
outcomeAnnotations: interactionOutcome.annotations?.map(a => a.label).join(','),
968
toolCounts: JSON.stringify(toolCounts),
969
} satisfies RequestInlineTelemetryProperties, {
970
firstTurn: this._firstTurn ? 1 : 0,
971
isNotebook: this._isNotebookDocument,
972
withIntentDetection: this._request.enableCommandDetection ? 1 : 0,
973
messageTokenCount: await this._endpoint.acquireTokenizer().tokenLength(this._request.prompt),
974
promptTokenCount: await this._endpoint.acquireTokenizer().countMessagesTokens(this._messages),
975
responseTokenCount: responseType === ChatFetchResponseType.Success ? await this._endpoint.acquireTokenizer().tokenLength(response) : -1,
976
implicitCommand: (!this._request.prompt.trim().startsWith(`/${this._intent.id}`) ? 1 : 0),
977
attemptCount: this._request.attempt || 0,
978
selectionLineCount: Math.abs(this._documentContext.selection.end.line - this._documentContext.selection.start.line) + 1,
979
wholeRangeLineCount: Math.abs(this._documentContext.wholeRange.end.line - this._documentContext.wholeRange.start.line) + 1,
980
editCount: this._editCount > 0 ? this._editCount : -1,
981
editLineCount: this._editLineCount > 0 ? this._editLineCount : -1,
982
markdownCharCount: this._markdownCharCount,
983
problemsCount: this._diagnosticsTelemetryData.fileDiagnosticsTelemetry.problemsCount,
984
selectionProblemsCount: this._diagnosticsTelemetryData.selectionDiagnosticsTelemetry.problemsCount,
985
diagnosticsCount: this._diagnosticsTelemetryData.fileDiagnosticsTelemetry.diagnosticsCount,
986
selectionDiagnosticsCount: this._diagnosticsTelemetryData.selectionDiagnosticsTelemetry.diagnosticsCount,
987
timeToRequest: this._requestStartTime - this._startTime,
988
timeToFirstToken: this._firstTokenTime ? this._firstTokenTime - this._startTime : -1,
989
timeToComplete: Date.now() - this._startTime,
990
...getCustomInstructionTelemetry(this._references),
991
numToolCalls: toolCalls.length,
992
availableToolCount: this._availableToolCount,
993
toolTokenCount: this._toolTokenCount,
994
isBYOK: isBYOKModel(this._endpoint),
995
isAuto: isAutoModel(this._endpoint)
996
} satisfies RequestInlineTelemetryMeasurements);
997
}
998
999
protected override _sendResponseInternalTelemetryEvent(responseType: ChatFetchResponseType, response: string): void {
1000
this._telemetryService.sendInternalMSFTTelemetryEvent('interactiveSessionResponse', {
1001
chatLocation: 'inline',
1002
intent: this._intent.id,
1003
request: this._request.prompt,
1004
response,
1005
conversationId: this._sessionId,
1006
requestId: this.telemetryMessageId,
1007
baseModel: this._endpoint.model,
1008
apiType: this._endpoint.apiType,
1009
responseType,
1010
problems: this._diagnosticsTelemetryData.fileDiagnosticsTelemetry.problems,
1011
selectionProblems: this._diagnosticsTelemetryData.selectionDiagnosticsTelemetry.problems,
1012
diagnosticCodes: this._diagnosticsTelemetryData.fileDiagnosticsTelemetry.diagnosticCodes,
1013
selectionDiagnosticCodes: this._diagnosticsTelemetryData.selectionDiagnosticsTelemetry.diagnosticCodes,
1014
diagnosticsProvider: this._diagnosticsTelemetryData.diagnosticsProvider,
1015
language: this._documentContext.document.languageId,
1016
} satisfies ResponseInternalInlineTelemetryProperties, {
1017
isNotebook: this._isNotebookDocument,
1018
turnNumber: this._conversation.turns.length,
1019
} satisfies ResponseInternalInlineTelemetryMeasurements);
1020
}
1021
}
1022
1023