Skip to content

Commit b0b5e3a

Browse files
committed
more code
1 parent 47fadd3 commit b0b5e3a

File tree

3 files changed

+66
-55
lines changed

3 files changed

+66
-55
lines changed

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ interface CodeWhispererClientAdaptor : Disposable {
133133
acceptedTokenCount: Long,
134134
totalTokenCount: Long,
135135
unmodifiedAcceptedTokenCount: Long?,
136+
userWrittenCodeCharacterCount: Long?,
137+
userWrittenCodeLineCount: Long?,
136138
): SendTelemetryEventResponse
137139

138140
fun sendUserModificationTelemetry(
@@ -479,6 +481,8 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
479481
acceptedTokenCount: Long,
480482
totalTokenCount: Long,
481483
unmodifiedAcceptedTokenCount: Long?,
484+
userWrittenCodeCharacterCount: Long?,
485+
userWrittenCodeLineCount: Long?,
482486
): SendTelemetryEventResponse = bearerClient().sendTelemetryEvent { requestBuilder ->
483487
requestBuilder.telemetryEvent { telemetryEventBuilder ->
484488
telemetryEventBuilder.codeCoverageEvent {
@@ -488,6 +492,8 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
488492
it.totalCharacterCount(totalTokenCount.toInt())
489493
it.timestamp(Instant.now())
490494
it.unmodifiedAcceptedCharacterCount(unmodifiedAcceptedTokenCount?.toInt())
495+
it.userWrittenCodeCharacterCount(userWrittenCodeLineCount?.toInt())
496+
it.userWrittenCodeLineCount(userWrittenCodeLineCount?.toInt())
491497
}
492498
}
493499
requestBuilder.optOutPreference(getTelemetryOptOutPreference())

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/telemetry/UserWrittenCodeTracker.kt

Lines changed: 57 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,36 @@
44
package software.aws.toolkits.jetbrains.services.codewhisperer.telemetry
55

66
import com.intellij.openapi.Disposable
7-
import com.intellij.openapi.application.runReadAction
87
import com.intellij.openapi.components.Service
98
import com.intellij.openapi.components.service
109
import com.intellij.openapi.editor.event.DocumentEvent
11-
import com.intellij.openapi.fileEditor.FileDocumentManager
1210
import com.intellij.openapi.project.Project
13-
import com.intellij.openapi.util.Key
14-
import com.intellij.openapi.util.TextRange
11+
import com.intellij.psi.PsiDocumentManager
1512
import com.intellij.util.Alarm
1613
import com.intellij.util.AlarmFactory
17-
import info.debatty.java.stringsimilarity.Levenshtein
18-
import org.assertj.core.util.VisibleForTesting
1914
import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException
2015
import software.aws.toolkits.core.utils.debug
2116
import software.aws.toolkits.core.utils.getLogger
2217
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
2318
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
19+
import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
2620
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
3021
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
3322
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
3823
import java.time.Duration
39-
import java.time.Instant
40-
import java.util.concurrent.LinkedBlockingDeque
4124
import java.util.concurrent.atomic.AtomicBoolean
25+
import java.util.concurrent.atomic.AtomicInteger
4226

4327

4428
@Service(Service.Level.PROJECT)
4529
class UserWrittenCodeTracker(private val project: Project) : Disposable {
46-
private val userWrittenCodePerLanguage = mutableMapOf<String, Int>()
30+
private val userWrittenCodeLineCount = mutableMapOf<CodeWhispererProgrammingLanguage, Long>()
31+
private val userWrittenCodeCharacterCount = mutableMapOf<CodeWhispererProgrammingLanguage, Long>()
4732
private val alarm = AlarmFactory.getInstance().create(Alarm.ThreadToUse.POOLED_THREAD, this)
4833

4934
private val isShuttingDown = AtomicBoolean(false)
35+
private val qInvocationCount: AtomicInteger = AtomicInteger(0)
36+
private val isQMakingEdits = AtomicBoolean(false)
5037

5138
init {
5239
scheduleTracker()
@@ -60,22 +47,35 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable {
6047

6148
private fun isTelemetryEnabled(): Boolean = AwsSettings.getInstance().isTelemetryEnabled
6249

50+
fun onQFeatureInvoked() {
51+
qInvocationCount.incrementAndGet()
52+
}
53+
54+
fun onQStartsMakingEdits() {
55+
isQMakingEdits.set(true)
56+
}
57+
58+
fun onQFinishesMakingEdits() {
59+
isQMakingEdits.set(false)
60+
}
6361

6462
private fun flush() {
6563
try {
66-
if (!isTelemetryEnabled()) {
64+
if (!isTelemetryEnabled() || qInvocationCount.get() <= 0) {
6765
return
6866
}
69-
for ((language, userWrittenCode) in userWrittenCodePerLanguage) {
70-
71-
}
72-
67+
emitCodeWhispererCodeContribution()
7368
} finally {
7469
scheduleTracker()
7570
}
7671
}
7772

7873
internal fun documentChanged(event: DocumentEvent) {
74+
// do not listen to document changed made by Amazon Q itself
75+
if (isQMakingEdits.get()) {
76+
return
77+
}
78+
7979
// When open a file for the first time, IDE will also emit DocumentEvent for loading with `isWholeTextReplaced = true`
8080
// Added this condition to filter out those events
8181
if (event.isWholeTextReplaced) {
@@ -88,45 +88,48 @@ class UserWrittenCodeTracker(private val project: Project) : Disposable {
8888
// 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
8989
// when event is auto closing [{(', there will be 2 separated events, both count as 1 char increase in total chars
9090
val text = event.newFragment.toString()
91+
val lines = text.split('\n').size - 1
9192
if (event.newLength < DOCUMENT_COPY_THRESSHOLD && text.trim().isNotEmpty()) {
9293
// count doc changes from <50 multi character input as total user written code
9394
// 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)
95+
val language = PsiDocumentManager.getInstance(project).getPsiFile(event.document)?.programmingLanguage()
96+
if (language != null) {
97+
userWrittenCodeLineCount.set(language, userWrittenCodeLineCount.getOrDefault(language, 0) + lines)
98+
userWrittenCodeCharacterCount.set(language, userWrittenCodeCharacterCount.getOrDefault(language, 0) + event.newLength)
99+
}
97100
}
98101
}
99102

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)
103+
104+
internal fun emitCodeWhispererCodeContribution() {
105+
val customizationArn: String? = CodeWhispererModelConfigurator.getInstance().activeCustomization(project)?.arn
106+
for ((language, _) in userWrittenCodeCharacterCount) {
107+
if (userWrittenCodeCharacterCount.getOrDefault(language, 0) <= 0 ) {
108+
continue
109+
}
110+
runIfIdcConnectionOrTelemetryEnabled(project) {
111+
// here acceptedTokensSize is the count of accepted chars post user modification
112+
try {
113+
val response = CodeWhispererClientAdaptor.getInstance(project).sendCodePercentageTelemetry(
114+
language,
115+
customizationArn,
116+
0,
117+
0,
118+
0,
119+
userWrittenCodeCharacterCount = userWrittenCodeCharacterCount.get(language),
120+
userWrittenCodeLineCount = userWrittenCodeLineCount.get((language))
121121
)
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}"
122+
LOG.debug { "Successfully sent code percentage 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 code percentage telemetry. RequestId: $requestId, ErrorMessage: ${e.message}"
127+
}
127128
}
128129
}
129130
}
131+
132+
130133
}
131134

132135
companion object {

plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,9 @@
579579
"timestamp": { "shape": "Timestamp" },
580580
"unmodifiedAcceptedCharacterCount": { "shape": "PrimitiveInteger" },
581581
"totalNewCodeCharacterCount": { "shape": "PrimitiveInteger" },
582-
"totalNewCodeLineCount": { "shape": "PrimitiveInteger" }
582+
"totalNewCodeLineCount": { "shape": "PrimitiveInteger" },
583+
"userWrittenCodeCharacterCount": { "shape": "PrimitiveInteger" },
584+
"userWrittenCodeLineCount": { "shape": "PrimitiveInteger" }
583585
}
584586
},
585587
"CodeFixAcceptanceEvent": {

0 commit comments

Comments
 (0)