Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/otel/common/genAiMetrics.ts
13401 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 { CopilotChatAttr, type EditOutcome, type EditSource, GenAiAttr, StdAttr } from './genAiAttributes';
7
import type { IOTelService } from './otelService';
8
9
/**
10
* Pre-configured OTel GenAI metric instruments.
11
* All methods are static to avoid per-call allocations (aligned with gemini-cli pattern).
12
*/
13
export class GenAiMetrics {
14
15
// ── GenAI Convention Metrics ──
16
17
static recordOperationDuration(
18
otel: IOTelService,
19
durationSec: number,
20
attrs: {
21
operationName: string;
22
providerName: string;
23
requestModel: string;
24
responseModel?: string;
25
serverAddress?: string;
26
serverPort?: number;
27
errorType?: string;
28
},
29
): void {
30
otel.recordMetric('gen_ai.client.operation.duration', durationSec, {
31
[GenAiAttr.OPERATION_NAME]: attrs.operationName,
32
[GenAiAttr.PROVIDER_NAME]: attrs.providerName,
33
[GenAiAttr.REQUEST_MODEL]: attrs.requestModel,
34
...(attrs.responseModel ? { [GenAiAttr.RESPONSE_MODEL]: attrs.responseModel } : {}),
35
...(attrs.serverAddress ? { [StdAttr.SERVER_ADDRESS]: attrs.serverAddress } : {}),
36
...(attrs.serverPort ? { [StdAttr.SERVER_PORT]: attrs.serverPort } : {}),
37
...(attrs.errorType ? { [StdAttr.ERROR_TYPE]: attrs.errorType } : {}),
38
});
39
}
40
41
static recordTokenUsage(
42
otel: IOTelService,
43
tokenCount: number,
44
tokenType: 'input' | 'output',
45
attrs: {
46
operationName: string;
47
providerName: string;
48
requestModel: string;
49
responseModel?: string;
50
serverAddress?: string;
51
},
52
): void {
53
otel.recordMetric('gen_ai.client.token.usage', tokenCount, {
54
[GenAiAttr.OPERATION_NAME]: attrs.operationName,
55
[GenAiAttr.PROVIDER_NAME]: attrs.providerName,
56
[GenAiAttr.TOKEN_TYPE]: tokenType,
57
[GenAiAttr.REQUEST_MODEL]: attrs.requestModel,
58
...(attrs.responseModel ? { [GenAiAttr.RESPONSE_MODEL]: attrs.responseModel } : {}),
59
...(attrs.serverAddress ? { [StdAttr.SERVER_ADDRESS]: attrs.serverAddress } : {}),
60
});
61
}
62
63
// ── Extension-Specific Metrics ──
64
65
static recordToolCallCount(otel: IOTelService, toolName: string, success: boolean): void {
66
otel.incrementCounter('copilot_chat.tool.call.count', 1, {
67
[GenAiAttr.TOOL_NAME]: toolName,
68
success,
69
});
70
}
71
72
static recordToolCallDuration(otel: IOTelService, toolName: string, durationMs: number): void {
73
otel.recordMetric('copilot_chat.tool.call.duration', durationMs, {
74
[GenAiAttr.TOOL_NAME]: toolName,
75
});
76
}
77
78
static recordAgentDuration(otel: IOTelService, agentName: string, durationSec: number): void {
79
otel.recordMetric('copilot_chat.agent.invocation.duration', durationSec, {
80
[GenAiAttr.AGENT_NAME]: agentName,
81
});
82
}
83
84
static recordAgentTurnCount(otel: IOTelService, agentName: string, turnCount: number): void {
85
otel.recordMetric('copilot_chat.agent.turn.count', turnCount, {
86
[GenAiAttr.AGENT_NAME]: agentName,
87
});
88
}
89
90
static recordTimeToFirstToken(otel: IOTelService, model: string, ttftSec: number): void {
91
otel.recordMetric('copilot_chat.time_to_first_token', ttftSec, {
92
[GenAiAttr.REQUEST_MODEL]: model,
93
});
94
}
95
96
static incrementSessionCount(otel: IOTelService): void {
97
otel.incrementCounter('copilot_chat.session.count');
98
}
99
100
// ── Agent Activity & Outcome Metrics ──
101
102
/** Accept/reject counter for inline chat and chat editing edits */
103
static recordEditAcceptance(otel: IOTelService, source: EditSource, outcome: EditOutcome, languageId?: string): void {
104
otel.incrementCounter('copilot_chat.edit.acceptance.count', 1, {
105
[CopilotChatAttr.EDIT_SOURCE]: source,
106
[CopilotChatAttr.EDIT_OUTCOME]: outcome,
107
...(languageId ? { [CopilotChatAttr.LANGUAGE_ID]: languageId } : {}),
108
});
109
}
110
111
/** File-level chat editing session outcome (accepted/rejected/saved) */
112
static recordChatEditOutcome(otel: IOTelService, source: EditSource, outcome: EditOutcome, languageId?: string, hasRemainingEdits?: boolean): void {
113
otel.incrementCounter('copilot_chat.chat_edit.outcome.count', 1, {
114
[CopilotChatAttr.EDIT_SOURCE]: source,
115
[CopilotChatAttr.EDIT_OUTCOME]: outcome,
116
...(languageId ? { [CopilotChatAttr.LANGUAGE_ID]: languageId } : {}),
117
...(hasRemainingEdits !== undefined ? { [CopilotChatAttr.HAS_REMAINING_EDITS]: hasRemainingEdits } : {}),
118
});
119
}
120
121
/** 4-gram text similarity survival score */
122
static recordEditSurvivalFourGram(otel: IOTelService, source: EditSource, score: number, timeDelayMs: number): void {
123
otel.recordMetric('copilot_chat.edit.survival.four_gram', score, {
124
[CopilotChatAttr.EDIT_SOURCE]: source,
125
[CopilotChatAttr.TIME_DELAY_MS]: timeDelayMs,
126
});
127
}
128
129
/** No-revert survival score */
130
static recordEditSurvivalNoRevert(otel: IOTelService, source: EditSource, score: number, timeDelayMs: number): void {
131
otel.recordMetric('copilot_chat.edit.survival.no_revert', score, {
132
[CopilotChatAttr.EDIT_SOURCE]: source,
133
[CopilotChatAttr.TIME_DELAY_MS]: timeDelayMs,
134
});
135
}
136
137
/** Lines of code added/removed by accepted agent edits */
138
static incrementLinesOfCode(otel: IOTelService, type: 'added' | 'removed', languageId: string | undefined, count: number): void {
139
otel.incrementCounter('copilot_chat.lines_of_code.count', count, {
140
'type': type,
141
...(languageId ? { [CopilotChatAttr.LANGUAGE_ID]: languageId } : {}),
142
});
143
}
144
145
// ── User Engagement Metrics ──
146
147
static incrementUserActionCount(otel: IOTelService, action: string): void {
148
otel.incrementCounter('copilot_chat.user.action.count', 1, {
149
'action': action,
150
});
151
}
152
153
static incrementUserFeedbackCount(otel: IOTelService, rating: string): void {
154
otel.incrementCounter('copilot_chat.user.feedback.count', 1, {
155
'rating': rating,
156
});
157
}
158
159
// ── Agent Internals Metrics ──
160
161
static incrementAgentEditResponseCount(otel: IOTelService, outcome: string): void {
162
otel.incrementCounter('copilot_chat.agent.edit_response.count', 1, {
163
'outcome': outcome,
164
});
165
}
166
167
static incrementAgentSummarizationCount(otel: IOTelService, outcome: string): void {
168
otel.incrementCounter('copilot_chat.agent.summarization.count', 1, {
169
'outcome': outcome,
170
});
171
}
172
173
// ── Background/Cloud Metrics ──
174
175
static incrementPullRequestCount(otel: IOTelService): void {
176
otel.incrementCounter('copilot_chat.pull_request.count');
177
}
178
179
static incrementCloudSessionCount(otel: IOTelService, partnerAgent: string): void {
180
otel.incrementCounter('copilot_chat.cloud.session.count', 1, {
181
'partner_agent': partnerAgent,
182
});
183
}
184
185
static incrementCloudPrReadyCount(otel: IOTelService): void {
186
otel.incrementCounter('copilot_chat.cloud.pr_ready.count');
187
}
188
}
189
190