Skip to content

Commit 24adda9

Browse files
authored
feat(/dev): send FeatureDevEvent telemetry event (#4602)
1 parent f544307 commit 24adda9

File tree

7 files changed

+82
-2
lines changed

7 files changed

+82
-2
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/FeatureDevConstants.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonqFeatureDev
55

6+
const val FEATURE_EVALUATION_PRODUCT_NAME = "FeatureDev"
7+
68
const val FEATURE_NAME = "Amazon Q Developer Agent for software development"
79

810
// Max number of times a user can attempt to retry an approach request if it fails

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/clients/FeatureDevClient.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package software.aws.toolkits.jetbrains.services.amazonqFeatureDev.clients
66
import com.intellij.openapi.components.Service
77
import com.intellij.openapi.components.service
88
import com.intellij.openapi.project.Project
9+
import com.intellij.openapi.util.SystemInfo
910
import kotlinx.coroutines.future.await
1011
import software.amazon.awssdk.services.codewhispererruntime.CodeWhispererRuntimeClient
1112
import software.amazon.awssdk.services.codewhispererruntime.model.ArtifactType
@@ -14,10 +15,15 @@ import software.amazon.awssdk.services.codewhispererruntime.model.CreateTaskAssi
1415
import software.amazon.awssdk.services.codewhispererruntime.model.CreateTaskAssistConversationResponse
1516
import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlResponse
1617
import software.amazon.awssdk.services.codewhispererruntime.model.GetTaskAssistCodeGenerationResponse
18+
import software.amazon.awssdk.services.codewhispererruntime.model.IdeCategory
19+
import software.amazon.awssdk.services.codewhispererruntime.model.OperatingSystem
20+
import software.amazon.awssdk.services.codewhispererruntime.model.OptOutPreference
21+
import software.amazon.awssdk.services.codewhispererruntime.model.SendTelemetryEventResponse
1722
import software.amazon.awssdk.services.codewhispererruntime.model.StartTaskAssistCodeGenerationResponse
1823
import software.amazon.awssdk.services.codewhispererruntime.model.TaskAssistPlanningUploadContext
1924
import software.amazon.awssdk.services.codewhispererruntime.model.UploadContext
2025
import software.amazon.awssdk.services.codewhispererruntime.model.UploadIntent
26+
import software.amazon.awssdk.services.codewhispererruntime.model.UserContext
2127
import software.amazon.awssdk.services.codewhispererstreaming.CodeWhispererStreamingAsyncClient
2228
import software.amazon.awssdk.services.codewhispererstreaming.model.ChatMessage
2329
import software.amazon.awssdk.services.codewhispererstreaming.model.ChatTriggerType
@@ -35,12 +41,40 @@ import software.aws.toolkits.jetbrains.core.awsClient
3541
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
3642
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
3743
import software.aws.toolkits.jetbrains.services.amazonq.clients.AmazonQStreamingClient
44+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_EVALUATION_PRODUCT_NAME
3845
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.calculateTotalLatency
46+
import software.aws.toolkits.jetbrains.services.telemetry.ClientMetadata
47+
import software.aws.toolkits.jetbrains.settings.AwsSettings
3948
import java.time.Instant
4049
import software.amazon.awssdk.services.codewhispererruntime.model.ChatTriggerType as SyncChatTriggerType
4150

4251
@Service(Service.Level.PROJECT)
4352
class FeatureDevClient(private val project: Project) {
53+
fun getTelemetryOptOutPreference() =
54+
if (AwsSettings.getInstance().isTelemetryEnabled) {
55+
OptOutPreference.OPTIN
56+
} else {
57+
OptOutPreference.OPTOUT
58+
}
59+
60+
private val featureDevUserContext = ClientMetadata.getDefault().let {
61+
val osForFeatureDev: OperatingSystem =
62+
when {
63+
SystemInfo.isWindows -> OperatingSystem.WINDOWS
64+
SystemInfo.isMac -> OperatingSystem.MAC
65+
// For now, categorize everything else as "Linux" (Linux/FreeBSD/Solaris/etc)
66+
else -> OperatingSystem.LINUX
67+
}
68+
69+
UserContext.builder()
70+
.ideCategory(IdeCategory.JETBRAINS)
71+
.operatingSystem(osForFeatureDev)
72+
.product(FEATURE_EVALUATION_PRODUCT_NAME)
73+
.clientId(it.clientId)
74+
.ideVersion(it.awsVersion)
75+
.build()
76+
}
77+
4478
private fun connection() = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance())
4579
?: error("Attempted to use connection while one does not exist")
4680

@@ -51,6 +85,16 @@ class FeatureDevClient(private val project: Project) {
5185
private val amazonQStreamingClient
5286
get() = AmazonQStreamingClient.getInstance(project)
5387

88+
fun sendFeatureDevTelemetryEvent(conversationId: String): SendTelemetryEventResponse = bearerClient().sendTelemetryEvent { requestBuilder ->
89+
requestBuilder.telemetryEvent { telemetryEventBuilder ->
90+
telemetryEventBuilder.featureDevEvent {
91+
it.conversationId(conversationId)
92+
}
93+
}
94+
requestBuilder.optOutPreference(getTelemetryOptOutPreference())
95+
requestBuilder.userContext(featureDevUserContext)
96+
}
97+
5498
fun createTaskAssistConversation(): CreateTaskAssistConversationResponse = bearerClient().createTaskAssistConversation(
5599
CreateTaskAssistConversationRequest.builder().build()
56100
)

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/Session.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ class Session(val tabID: String, val project: Project) {
5656
if (!preloaderFinished) {
5757
setupConversation(msg)
5858
preloaderFinished = true
59-
6059
messenger.sendAsyncEventProgress(tabId = this.tabID, inProgress = true)
60+
featureDevService.sendFeatureDevEvent(this.conversationId)
6161
}
6262
}
6363

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/util/FeatureDevService.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.intellij.openapi.project.Project
99
import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException
1010
import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlResponse
1111
import software.amazon.awssdk.services.codewhispererruntime.model.GetTaskAssistCodeGenerationResponse
12+
import software.amazon.awssdk.services.codewhispererruntime.model.SendTelemetryEventResponse
1213
import software.amazon.awssdk.services.codewhispererruntime.model.StartTaskAssistCodeGenerationResponse
1314
import software.amazon.awssdk.services.codewhispererruntime.model.ValidationException
1415
import software.amazon.awssdk.services.codewhispererstreaming.model.CodeWhispererStreamingException
@@ -237,4 +238,17 @@ class FeatureDevService(val proxyClient: FeatureDevClient, val project: Project)
237238

238239
return parsedResult.code_generation_result
239240
}
241+
242+
fun sendFeatureDevEvent(conversationId: String) {
243+
val sendFeatureDevTelemetryEventResponse: SendTelemetryEventResponse
244+
try {
245+
sendFeatureDevTelemetryEventResponse = proxyClient.sendFeatureDevTelemetryEvent(conversationId)
246+
val requestId = sendFeatureDevTelemetryEventResponse.responseMetadata().requestId()
247+
logger.debug {
248+
"$FEATURE_NAME: succesfully sent feature dev telemetry: ConversationId: $conversationId RequestId: $requestId"
249+
}
250+
} catch (e: Exception) {
251+
logger.warn(e) { "$FEATURE_NAME: failed to send feature dev telemetry" }
252+
}
253+
}
240254
}

plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/FeatureDevTestBase.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.CodeGeneration
2424
import software.amazon.awssdk.services.codewhispererruntime.model.CreateTaskAssistConversationResponse
2525
import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlResponse
2626
import software.amazon.awssdk.services.codewhispererruntime.model.GetTaskAssistCodeGenerationResponse
27+
import software.amazon.awssdk.services.codewhispererruntime.model.SendTelemetryEventResponse
2728
import software.amazon.awssdk.services.codewhispererruntime.model.StartTaskAssistCodeGenerationResponse
2829
import software.aws.toolkits.core.TokenConnectionSettings
2930
import software.aws.toolkits.core.credentials.ToolkitBearerTokenProvider
@@ -70,6 +71,10 @@ open class FeatureDevTestBase(
7071
internal val testChecksumSha = "test-sha"
7172
internal val testContentLength: Long = 40
7273

74+
internal val exampleSendTelemetryEventResponse = SendTelemetryEventResponse.builder()
75+
.responseMetadata(DefaultAwsResponseMetadata.create(mapOf(AwsHeader.AWS_REQUEST_ID to testRequestId)))
76+
.build() as SendTelemetryEventResponse
77+
7378
internal val exampleCreateTaskAssistConversationResponse = CreateTaskAssistConversationResponse.builder()
7479
.conversationId(testConversationId)
7580
.responseMetadata(DefaultAwsResponseMetadata.create(mapOf(AwsHeader.AWS_REQUEST_ID to testRequestId)))

plugins/amazonq/chat/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevControllerTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class FeatureDevControllerTest : FeatureDevTestBase() {
143143
doReturn(testAuth).`when`(authController).getAuthNeededStates(any())
144144
whenever(chatSessionStorage.getSession(any(), any())).thenReturn(spySession)
145145
whenever(featureDevClient.createTaskAssistConversation()).thenReturn(exampleCreateTaskAssistConversationResponse)
146+
whenever(featureDevClient.sendFeatureDevTelemetryEvent(any())).thenReturn(exampleSendTelemetryEventResponse)
146147
whenever(featureDevClient.createTaskAssistUploadUrl(any(), any(), any())).thenReturn(exampleCreateUploadUrlResponse)
147148

148149
runTest {
@@ -168,6 +169,7 @@ class FeatureDevControllerTest : FeatureDevTestBase() {
168169
val message = IncomingFeatureDevMessage.FollowupClicked(followUp, testTabId, "", "test-command")
169170

170171
whenever(featureDevClient.createTaskAssistConversation()).thenReturn(exampleCreateTaskAssistConversationResponse)
172+
whenever(featureDevClient.sendFeatureDevTelemetryEvent(any())).thenReturn(exampleSendTelemetryEventResponse)
171173
whenever(chatSessionStorage.getSession(any(), any())).thenReturn(spySession)
172174
doNothing().`when`(chatSessionStorage).deleteSession(any())
173175

@@ -216,6 +218,7 @@ class FeatureDevControllerTest : FeatureDevTestBase() {
216218
val message = IncomingFeatureDevMessage.FollowupClicked(followUp, testTabId, "", "test-command")
217219

218220
whenever(featureDevClient.createTaskAssistConversation()).thenReturn(exampleCreateTaskAssistConversationResponse)
221+
whenever(featureDevClient.sendFeatureDevTelemetryEvent(any())).thenReturn(exampleSendTelemetryEventResponse)
219222
whenever(chatSessionStorage.getSession(any(), any())).thenReturn(spySession)
220223

221224
mockkObject(AmazonqTelemetry)
@@ -243,6 +246,7 @@ class FeatureDevControllerTest : FeatureDevTestBase() {
243246
} just runs
244247

245248
whenever(featureDevClient.createTaskAssistConversation()).thenReturn(exampleCreateTaskAssistConversationResponse)
249+
whenever(featureDevClient.sendFeatureDevTelemetryEvent(any())).thenReturn(exampleSendTelemetryEventResponse)
246250
whenever(chatSessionStorage.getSession(any(), any())).thenReturn(spySession)
247251
whenever(spySession.sessionState).thenReturn(
248252
PrepareCodeGenerationState(
@@ -403,6 +407,7 @@ class FeatureDevControllerTest : FeatureDevTestBase() {
403407
val message = IncomingFeatureDevMessage.FollowupClicked(followUp, testTabId, "", "test-command")
404408

405409
whenever(featureDevClient.createTaskAssistConversation()).thenReturn(exampleCreateTaskAssistConversationResponse)
410+
whenever(featureDevClient.sendFeatureDevTelemetryEvent(any())).thenReturn(exampleSendTelemetryEventResponse)
406411
whenever(chatSessionStorage.getSession(any(), any())).thenReturn(spySession)
407412

408413
mockkStatic("software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.FileUtilsKt")
@@ -431,6 +436,7 @@ class FeatureDevControllerTest : FeatureDevTestBase() {
431436
val message = IncomingFeatureDevMessage.FollowupClicked(followUp, testTabId, "", "test-command")
432437

433438
whenever(featureDevClient.createTaskAssistConversation()).thenReturn(exampleCreateTaskAssistConversationResponse)
439+
whenever(featureDevClient.sendFeatureDevTelemetryEvent(any())).thenReturn(exampleSendTelemetryEventResponse)
434440
whenever(chatSessionStorage.getSession(any(), any())).thenReturn(spySession)
435441

436442
mockkStatic("software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.FileUtilsKt")
@@ -464,6 +470,7 @@ class FeatureDevControllerTest : FeatureDevTestBase() {
464470
val message = IncomingFeatureDevMessage.FollowupClicked(followUp, testTabId, "", "test-command")
465471

466472
whenever(featureDevClient.createTaskAssistConversation()).thenReturn(exampleCreateTaskAssistConversationResponse)
473+
whenever(featureDevClient.sendFeatureDevTelemetryEvent(any())).thenReturn(exampleSendTelemetryEventResponse)
467474
whenever(chatSessionStorage.getSession(any(), any())).thenReturn(spySession)
468475

469476
val folder = LightVirtualFile("${spySession.context.projectRoot.name}/path/to/sub/folder")

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,13 @@
456456
"DOWNVOTE"
457457
]
458458
},
459+
"FeatureDevEvent": {
460+
"type": "structure",
461+
"required": ["conversationId"],
462+
"members": {
463+
"conversationId": { "shape": "ConversationId" }
464+
}
465+
},
459466
"ChatUserModificationEvent": {
460467
"type": "structure",
461468
"required": ["conversationId", "messageId", "modificationPercentage"],
@@ -1593,7 +1600,8 @@
15931600
"metricData":{"shape":"MetricData"},
15941601
"chatAddMessageEvent": { "shape": "ChatAddMessageEvent" },
15951602
"chatInteractWithMessageEvent": { "shape": "ChatInteractWithMessageEvent" },
1596-
"chatUserModificationEvent": { "shape": "ChatUserModificationEvent" }
1603+
"chatUserModificationEvent": { "shape": "ChatUserModificationEvent" },
1604+
"featureDevEvent": { "shape": "FeatureDevEvent" }
15971605
},
15981606
"union":true
15991607
},

0 commit comments

Comments
 (0)