Skip to content

Commit 47fadd3

Browse files
committed
init commit of ai code gen %
1 parent 892c494 commit 47fadd3

File tree

1 file changed

+149
-0
lines changed
  • plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry

1 file changed

+149
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codewhisperer.telemetry
5+
6+
import com.intellij.openapi.Disposable
7+
import com.intellij.openapi.application.runReadAction
8+
import com.intellij.openapi.components.Service
9+
import com.intellij.openapi.components.service
10+
import com.intellij.openapi.editor.event.DocumentEvent
11+
import com.intellij.openapi.fileEditor.FileDocumentManager
12+
import com.intellij.openapi.project.Project
13+
import com.intellij.openapi.util.Key
14+
import com.intellij.openapi.util.TextRange
15+
import com.intellij.util.Alarm
16+
import com.intellij.util.AlarmFactory
17+
import info.debatty.java.stringsimilarity.Levenshtein
18+
import org.assertj.core.util.VisibleForTesting
19+
import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException
20+
import software.aws.toolkits.core.utils.debug
21+
import software.aws.toolkits.core.utils.getLogger
22+
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
23+
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererModelConfigurator
24+
import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererLanguageManager
25+
import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererUnknownLanguage
26+
import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage
27+
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererCodeCoverageTracker.Companion
28+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getConnectionStartUrl
29+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.getUnmodifiedAcceptedCharsCount
30+
import software.aws.toolkits.jetbrains.services.codewhisperer.util.runIfIdcConnectionOrTelemetryEnabled
31+
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.InsertedCodeModificationEntry
32+
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
33+
import software.aws.toolkits.jetbrains.settings.AwsSettings
34+
import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings
35+
import software.aws.toolkits.telemetry.AmazonqTelemetry
36+
import software.aws.toolkits.telemetry.CodewhispererLanguage
37+
import software.aws.toolkits.telemetry.CodewhispererTelemetry
38+
import java.time.Duration
39+
import java.time.Instant
40+
import java.util.concurrent.LinkedBlockingDeque
41+
import java.util.concurrent.atomic.AtomicBoolean
42+
43+
44+
@Service(Service.Level.PROJECT)
45+
class UserWrittenCodeTracker(private val project: Project) : Disposable {
46+
private val userWrittenCodePerLanguage = mutableMapOf<String, Int>()
47+
private val alarm = AlarmFactory.getInstance().create(Alarm.ThreadToUse.POOLED_THREAD, this)
48+
49+
private val isShuttingDown = AtomicBoolean(false)
50+
51+
init {
52+
scheduleTracker()
53+
}
54+
55+
private fun scheduleTracker() {
56+
if (!alarm.isDisposed && !isShuttingDown.get()) {
57+
alarm.addRequest({ flush() }, DEFAULT_CHECK_INTERVAL.toMillis())
58+
}
59+
}
60+
61+
private fun isTelemetryEnabled(): Boolean = AwsSettings.getInstance().isTelemetryEnabled
62+
63+
64+
private fun flush() {
65+
try {
66+
if (!isTelemetryEnabled()) {
67+
return
68+
}
69+
for ((language, userWrittenCode) in userWrittenCodePerLanguage) {
70+
71+
}
72+
73+
} finally {
74+
scheduleTracker()
75+
}
76+
}
77+
78+
internal fun documentChanged(event: DocumentEvent) {
79+
// When open a file for the first time, IDE will also emit DocumentEvent for loading with `isWholeTextReplaced = true`
80+
// Added this condition to filter out those events
81+
if (event.isWholeTextReplaced) {
82+
LOG.debug { "event with isWholeTextReplaced flag: $event" }
83+
if (event.oldTimeStamp == 0L) return
84+
}
85+
// only count total tokens when it is a user keystroke input
86+
// do not count doc changes from copy & paste of >=50 characters
87+
// do not count other changes from formatter, git command, etc
88+
// edge case: event can be from user hit enter with indentation where change is \n\t\t, count as 1 char increase in total chars
89+
// when event is auto closing [{(', there will be 2 separated events, both count as 1 char increase in total chars
90+
val text = event.newFragment.toString()
91+
if (event.newLength < DOCUMENT_COPY_THRESSHOLD && text.trim().isNotEmpty()) {
92+
// count doc changes from <50 multi character input as total user written code
93+
// ignore all white space changes, this usually comes from IntelliJ formatting
94+
val language = ""
95+
val newUserWrittenCode = userWrittenCodePerLanguage.getOrDefault(language, 0) + event.newLength
96+
userWrittenCodePerLanguage.set(language, newUserWrittenCode)
97+
}
98+
}
99+
100+
private fun sendUserModificationTelemetryToServiceAPI(
101+
suggestion: AcceptedSuggestionEntry,
102+
) {
103+
runIfIdcConnectionOrTelemetryEnabled(project) {
104+
try {
105+
// should be impossible from the caller logic
106+
if (suggestion.vFile == null) return@runIfIdcConnectionOrTelemetryEnabled
107+
val document = runReadAction {
108+
FileDocumentManager.getInstance().getDocument(suggestion.vFile)
109+
}
110+
val modifiedSuggestion = document?.getText(
111+
TextRange(suggestion.range.startOffset, suggestion.range.endOffset)
112+
).orEmpty()
113+
val response = CodeWhispererClientAdaptor.getInstance(project)
114+
.sendUserModificationTelemetry(
115+
suggestion.sessionId,
116+
suggestion.requestId,
117+
CodeWhispererLanguageManager.getInstance().getLanguage(suggestion.vFile),
118+
CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn.orEmpty(),
119+
suggestion.suggestion.length,
120+
getUnmodifiedAcceptedCharsCount(suggestion.suggestion, modifiedSuggestion)
121+
)
122+
LOG.debug { "Successfully sent user modification telemetry. RequestId: ${response.responseMetadata().requestId()}" }
123+
} catch (e: Exception) {
124+
val requestId = if (e is CodeWhispererRuntimeException) e.requestId() else null
125+
LOG.debug {
126+
"Failed to send user modification telemetry. RequestId: $requestId, ErrorMessage: ${e.message}"
127+
}
128+
}
129+
}
130+
}
131+
132+
companion object {
133+
private val DEFAULT_CHECK_INTERVAL = Duration.ofMinutes(1)
134+
private const val DEFAULT_MODIFICATION_INTERVAL_IN_SECONDS = 300 // 5 minutes
135+
136+
private const val DOCUMENT_COPY_THRESSHOLD = 50
137+
private val LOG = getLogger<UserWrittenCodeTracker>()
138+
139+
fun getInstance(project: Project) = project.service<UserWrittenCodeTracker>()
140+
}
141+
142+
override fun dispose() {
143+
if (isShuttingDown.getAndSet(true)) {
144+
return
145+
}
146+
147+
flush()
148+
}
149+
}

0 commit comments

Comments
 (0)