@@ -16,10 +16,13 @@ import software.amazon.awssdk.services.codewhisperer.model.GetCodeScanResponse
16
16
import software.amazon.awssdk.services.codewhisperer.model.ListCodeScanFindingsRequest
17
17
import software.amazon.awssdk.services.codewhisperer.model.ListCodeScanFindingsResponse
18
18
import software.amazon.awssdk.services.codewhispererruntime.CodeWhispererRuntimeClient
19
+ import software.amazon.awssdk.services.codewhispererruntime.model.CompletionType
19
20
import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlRequest
20
21
import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlResponse
21
22
import software.amazon.awssdk.services.codewhispererruntime.model.GenerateCompletionsRequest
22
23
import software.amazon.awssdk.services.codewhispererruntime.model.GenerateCompletionsResponse
24
+ import software.amazon.awssdk.services.codewhispererruntime.model.SendTelemetryEventResponse
25
+ import software.amazon.awssdk.services.codewhispererruntime.model.SuggestionState
23
26
import software.aws.toolkits.core.utils.getLogger
24
27
import software.aws.toolkits.core.utils.warn
25
28
import software.aws.toolkits.jetbrains.core.AwsClientManager
@@ -31,8 +34,15 @@ import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
31
34
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManagerListener
32
35
import software.aws.toolkits.jetbrains.core.credentials.pinning.CodeWhispererConnection
33
36
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager
37
+ import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
38
+ import software.aws.toolkits.jetbrains.services.codewhisperer.service.RequestContext
39
+ import software.aws.toolkits.jetbrains.services.codewhisperer.service.ResponseContext
34
40
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
35
41
import software.aws.toolkits.jetbrains.services.codewhisperer.util.transform
42
+ import software.aws.toolkits.telemetry.CodewhispererCompletionType
43
+ import software.aws.toolkits.telemetry.CodewhispererSuggestionState
44
+ import java.time.Instant
45
+ import java.util.concurrent.TimeUnit
36
46
import kotlin.reflect.KProperty0
37
47
import kotlin.reflect.jvm.isAccessible
38
48
@@ -64,11 +74,40 @@ interface CodeWhispererClientAdaptor : Disposable {
64
74
isSigv4 : Boolean = shouldUseSigv4Client(project)
65
75
): ListCodeScanFindingsResponse
66
76
77
+ fun putUserTriggerDecisionTelemetry (
78
+ requestContext : RequestContext ,
79
+ responseContext : ResponseContext ,
80
+ completionType : CodewhispererCompletionType ,
81
+ suggestionState : CodewhispererSuggestionState ,
82
+ suggestionReferenceCount : Int ,
83
+ lineCount : Int
84
+ ): SendTelemetryEventResponse
85
+
86
+ fun putCodePercentageTelemetry (
87
+ language : CodeWhispererProgrammingLanguage ,
88
+ acceptedTokenCount : Int ,
89
+ totalTokenCount : Int
90
+ ): SendTelemetryEventResponse
91
+
92
+ fun sendUserModificationTelemetry (
93
+ sessionId : String ,
94
+ requestId : String ,
95
+ language : CodeWhispererProgrammingLanguage ,
96
+ modificationPercentage : Double
97
+ ): SendTelemetryEventResponse
98
+
99
+ fun sendCodeScanTelemetry (
100
+ language : CodeWhispererProgrammingLanguage ,
101
+ codeScanJobId : String?
102
+ ): SendTelemetryEventResponse
103
+
67
104
companion object {
68
105
fun getInstance (project : Project ): CodeWhispererClientAdaptor = project.service()
69
106
70
107
private fun shouldUseSigv4Client (project : Project ) =
71
108
CodeWhispererExplorerActionManager .getInstance().checkActiveCodeWhispererConnectionType(project) == CodeWhispererLoginType .Accountless
109
+
110
+ const val INVALID_CODESCANJOBID = " Invalid_CodeScanJobID"
72
111
}
73
112
}
74
113
@@ -139,6 +178,91 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
139
178
bearerClient().listCodeAnalysisFindings(request.transform()).transform()
140
179
}
141
180
181
+ override fun putUserTriggerDecisionTelemetry (
182
+ requestContext : RequestContext ,
183
+ responseContext : ResponseContext ,
184
+ completionType : CodewhispererCompletionType ,
185
+ suggestionState : CodewhispererSuggestionState ,
186
+ suggestionReferenceCount : Int ,
187
+ lineCount : Int
188
+ ): SendTelemetryEventResponse {
189
+ val fileContext = requestContext.fileContextInfo
190
+ val programmingLanguage = fileContext.programmingLanguage
191
+ var e2eLatency = requestContext.latencyContext.getCodeWhispererEndToEndLatency()
192
+
193
+ // When we send a userTriggerDecision of Empty or Discard, we set the time users see the first
194
+ // suggestion to be now.
195
+ if (e2eLatency < 0 ) {
196
+ e2eLatency = TimeUnit .NANOSECONDS .toMillis(
197
+ System .nanoTime() - requestContext.latencyContext.codewhispererEndToEndStart
198
+ ).toDouble()
199
+ }
200
+ return bearerClient().sendTelemetryEvent { requestBuilder ->
201
+ requestBuilder.telemetryEvent { telemetryEventBuilder ->
202
+ telemetryEventBuilder.userTriggerDecisionEvent {
203
+ it.requestId(requestContext.latencyContext.firstRequestId)
204
+ it.completionType(completionType.toCodeWhispererSdkType())
205
+ it.programmingLanguage { builder -> builder.languageName(programmingLanguage.languageId) }
206
+ it.sessionId(responseContext.sessionId)
207
+ it.recommendationLatencyMilliseconds(e2eLatency)
208
+ it.suggestionState(suggestionState.toCodeWhispererSdkType())
209
+ it.timestamp(Instant .now())
210
+ it.suggestionReferenceCount(suggestionReferenceCount)
211
+ it.generatedLine(lineCount)
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ override fun putCodePercentageTelemetry (
218
+ language : CodeWhispererProgrammingLanguage ,
219
+ acceptedTokenCount : Int ,
220
+ totalTokenCount : Int
221
+ ): SendTelemetryEventResponse = bearerClient().sendTelemetryEvent { requestBuilder ->
222
+ requestBuilder.telemetryEvent { telemetryEventBuilder ->
223
+ telemetryEventBuilder.codeCoverageEvent {
224
+ it.programmingLanguage { languageBuilder -> languageBuilder.languageName(language.languageId) }
225
+ it.acceptedCharacterCount(acceptedTokenCount)
226
+ it.totalCharacterCount(totalTokenCount)
227
+ it.timestamp(Instant .now())
228
+ }
229
+ }
230
+ }
231
+
232
+ override fun sendUserModificationTelemetry (
233
+ sessionId : String ,
234
+ requestId : String ,
235
+ language : CodeWhispererProgrammingLanguage ,
236
+ modificationPercentage : Double
237
+ ): SendTelemetryEventResponse = bearerClient().sendTelemetryEvent { requestBuilder ->
238
+ requestBuilder.telemetryEvent { telemetryEventBuilder ->
239
+ telemetryEventBuilder.userModificationEvent {
240
+ it.sessionId(sessionId)
241
+ it.requestId(requestId)
242
+ it.programmingLanguage { languageBuilder ->
243
+ languageBuilder.languageName(language.languageId)
244
+ }
245
+ it.modificationPercentage(modificationPercentage)
246
+ it.timestamp(Instant .now())
247
+ }
248
+ }
249
+ }
250
+
251
+ override fun sendCodeScanTelemetry (
252
+ language : CodeWhispererProgrammingLanguage ,
253
+ codeScanJobId : String?
254
+ ): SendTelemetryEventResponse = bearerClient().sendTelemetryEvent { requestBuilder ->
255
+ requestBuilder.telemetryEvent { telemetryEventBuilder ->
256
+ telemetryEventBuilder.codeScanEvent {
257
+ it.programmingLanguage { languageBuilder ->
258
+ languageBuilder.languageName(language.languageId)
259
+ }
260
+ it.codeScanJobId(if (codeScanJobId.isNullOrEmpty()) CodeWhispererClientAdaptor .INVALID_CODESCANJOBID else codeScanJobId)
261
+ it.timestamp(Instant .now())
262
+ }
263
+ }
264
+ }
265
+
142
266
override fun dispose () {
143
267
if (this ::mySigv4Client.isLazyInitialized) {
144
268
mySigv4Client.close()
@@ -184,3 +308,17 @@ class MockCodeWhispererClientAdaptor(override val project: Project) : CodeWhispe
184
308
185
309
override fun dispose () {}
186
310
}
311
+
312
+ private fun CodewhispererSuggestionState.toCodeWhispererSdkType () = when {
313
+ this == CodewhispererSuggestionState .Accept -> SuggestionState .ACCEPT
314
+ this == CodewhispererSuggestionState .Reject -> SuggestionState .REJECT
315
+ this == CodewhispererSuggestionState .Empty -> SuggestionState .EMPTY
316
+ this == CodewhispererSuggestionState .Discard -> SuggestionState .DISCARD
317
+ else -> SuggestionState .UNKNOWN_TO_SDK_VERSION
318
+ }
319
+
320
+ private fun CodewhispererCompletionType.toCodeWhispererSdkType () = when {
321
+ this == CodewhispererCompletionType .Line -> CompletionType .LINE
322
+ this == CodewhispererCompletionType .Block -> CompletionType .BLOCK
323
+ else -> CompletionType .UNKNOWN_TO_SDK_VERSION
324
+ }
0 commit comments