Skip to content

Commit b72f78b

Browse files
telemetry(amazonq) add feedback functionality with thumbs up/down (#5336)
Previously, UTG did not have telemetry to collect user feedback on generated unit tests. This PR introduces telemetry support to address that gap. Additionally, it enables users to submit comments, which will be routed to a private SIM folder.
1 parent fd7eb31 commit b72f78b

File tree

8 files changed

+110
-4
lines changed

8 files changed

+110
-4
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ mockitoKotlin = "5.4.0"
2727
mockk = "1.13.10"
2828
nimbus-jose-jwt = "9.40"
2929
node-gradle = "7.0.2"
30-
telemetryGenerator = "1.0.295"
30+
telemetryGenerator = "1.0.297"
3131
testLogger = "4.0.0"
3232
testRetry = "1.5.10"
3333
# test-only; platform provides slf4j transitively at runtime

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeTestChatApp.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class CodeTestChatApp(private val scope: CoroutineScope) : AmazonQApp {
3939
"start-test-gen" to IncomingCodeTestMessage.StartTestGen::class,
4040
"response-body-link-click" to IncomingCodeTestMessage.ClickedLink::class,
4141
"button-click" to IncomingCodeTestMessage.ButtonClicked::class,
42+
"chat-item-voted" to IncomingCodeTestMessage.ChatItemVoted::class,
43+
"chat-item-feedback" to IncomingCodeTestMessage.ChatItemFeedback::class,
44+
"button-click" to IncomingCodeTestMessage.ButtonClicked::class,
4245
"auth-follow-up-was-clicked" to IncomingCodeTestMessage.AuthFollowUpWasClicked::class
4346
)
4447

@@ -80,6 +83,8 @@ class CodeTestChatApp(private val scope: CoroutineScope) : AmazonQApp {
8083
is IncomingCodeTestMessage.StartTestGen -> inboundAppMessagesHandler.processStartTestGen(message)
8184
is IncomingCodeTestMessage.ClickedLink -> inboundAppMessagesHandler.processLinkClick(message)
8285
is IncomingCodeTestMessage.ButtonClicked -> inboundAppMessagesHandler.processButtonClickedMessage(message)
86+
is IncomingCodeTestMessage.ChatItemVoted -> inboundAppMessagesHandler.processChatItemVoted(message)
87+
is IncomingCodeTestMessage.ChatItemFeedback -> inboundAppMessagesHandler.processChatItemFeedBack(message)
8388
is IncomingCodeTestMessage.AuthFollowUpWasClicked -> inboundAppMessagesHandler.processAuthFollowUpClick(message)
8489
}
8590
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/InboundAppMessagesHandler.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,9 @@ interface InboundAppMessagesHandler {
2222

2323
suspend fun processButtonClickedMessage(message: IncomingCodeTestMessage.ButtonClicked)
2424

25+
suspend fun processChatItemVoted(message: IncomingCodeTestMessage.ChatItemVoted)
26+
27+
suspend fun processChatItemFeedBack(message: IncomingCodeTestMessage.ChatItemFeedback)
28+
2529
suspend fun processAuthFollowUpClick(message: IncomingCodeTestMessage.AuthFollowUpWasClicked)
2630
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
package software.aws.toolkits.jetbrains.services.amazonqCodeTest.controller
5-
5+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
66
import com.intellij.diff.DiffContentFactory
77
import com.intellij.diff.DiffManager
88
import com.intellij.diff.DiffManagerEx
@@ -44,8 +44,11 @@ import software.amazon.awssdk.services.codewhispererstreaming.model.TextDocument
4444
import software.amazon.awssdk.services.codewhispererstreaming.model.UserInputMessage
4545
import software.amazon.awssdk.services.codewhispererstreaming.model.UserInputMessageContext
4646
import software.amazon.awssdk.services.codewhispererstreaming.model.UserIntent
47+
import software.amazon.awssdk.services.toolkittelemetry.model.Sentiment
4748
import software.aws.toolkits.core.utils.debug
4849
import software.aws.toolkits.core.utils.getLogger
50+
import software.aws.toolkits.core.utils.info
51+
import software.aws.toolkits.core.utils.warn
4952
import software.aws.toolkits.jetbrains.core.AwsClientManager
5053
import software.aws.toolkits.jetbrains.core.coroutines.EDT
5154
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
@@ -81,14 +84,19 @@ import software.aws.toolkits.jetbrains.services.cwc.clients.chat.model.TriggerTy
8184
import software.aws.toolkits.jetbrains.services.cwc.clients.chat.v1.ChatSessionV1.Companion.validLanguages
8285
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.StaticPrompt
8386
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.StaticTextResponse
87+
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.FeedbackComment
8488
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
8589
import software.aws.toolkits.jetbrains.services.cwc.editor.context.ActiveFileContext
8690
import software.aws.toolkits.jetbrains.services.cwc.editor.context.ActiveFileContextExtractor
8791
import software.aws.toolkits.jetbrains.services.cwc.editor.context.ExtractionTriggerType
8892
import software.aws.toolkits.jetbrains.services.cwc.editor.context.file.FileContext
8993
import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType
94+
import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService
95+
import software.aws.toolkits.jetbrains.utils.notifyError
9096
import software.aws.toolkits.resources.message
9197
import software.aws.toolkits.telemetry.AmazonqTelemetry
98+
import software.aws.toolkits.telemetry.FeatureId
99+
import software.aws.toolkits.telemetry.InteractionType
92100
import software.aws.toolkits.telemetry.MetricResult
93101
import software.aws.toolkits.telemetry.UiTelemetry
94102
import java.io.File
@@ -403,6 +411,57 @@ class CodeTestChatController(
403411
.build()
404412
}
405413

414+
override suspend fun processChatItemFeedBack(message: IncomingCodeTestMessage.ChatItemFeedback) {
415+
LOG.debug { "$FEATURE_NAME: Processing ChatItemFeedBackMessage: ${message.comment}" }
416+
417+
val session = codeTestChatHelper.getActiveSession()
418+
419+
val comment = FeedbackComment(
420+
conversationId = session.startTestGenerationRequestId,
421+
userComment = message.comment.orEmpty(),
422+
reason = message.selectedOption,
423+
type = "testgen-chat-answer-feedback",
424+
messageId = "",
425+
)
426+
427+
try {
428+
TelemetryService.getInstance().sendFeedback(
429+
sentiment = Sentiment.NEGATIVE,
430+
comment = objectMapper.writeValueAsString(comment),
431+
)
432+
LOG.info { "$FEATURE_NAME answer feedback sent: \"Negative\"" }
433+
} catch (e: Throwable) {
434+
e.notifyError(message("feedback.submit_failed", e))
435+
LOG.warn(e) { "Failed to submit feedback" }
436+
return
437+
}
438+
}
439+
440+
override suspend fun processChatItemVoted(message: IncomingCodeTestMessage.ChatItemVoted) {
441+
LOG.debug { "$FEATURE_NAME: Processing ChatItemVotedMessage: $message" }
442+
443+
val session = codeTestChatHelper.getActiveSession()
444+
when (message.vote) {
445+
"upvote" -> {
446+
AmazonqTelemetry.feedback(
447+
featureId = FeatureId.AmazonQTest,
448+
interactionType = InteractionType.Upvote,
449+
credentialStartUrl = getStartUrl(project = context.project),
450+
amazonqConversationId = session.startTestGenerationRequestId
451+
452+
)
453+
}
454+
"downvote" -> {
455+
AmazonqTelemetry.feedback(
456+
featureId = FeatureId.AmazonQTest,
457+
interactionType = InteractionType.Downvote,
458+
credentialStartUrl = getStartUrl(project = context.project),
459+
amazonqConversationId = session.startTestGenerationRequestId
460+
)
461+
}
462+
}
463+
}
464+
406465
override suspend fun processNewTabCreatedMessage(message: IncomingCodeTestMessage.NewTabCreated) {
407466
newTabOpened(message.tabId)
408467
LOG.debug { "$FEATURE_NAME: New tab created: $message" }
@@ -1312,5 +1371,7 @@ class CodeTestChatController(
13121371

13131372
companion object {
13141373
private val LOG = getLogger<CodeTestChatController>()
1374+
1375+
private val objectMapper = jacksonObjectMapper()
13151376
}
13161377
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/messages/CodeTestMessage.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ sealed interface IncomingCodeTestMessage : CodeTestBaseMessage {
9595
@JsonProperty("actionID") val actionID: String,
9696
) : IncomingCodeTestMessage
9797

98+
data class ChatItemVoted(
99+
@JsonProperty("tabID") val tabId: String,
100+
val vote: String,
101+
) : IncomingCodeTestMessage
102+
103+
data class ChatItemFeedback(
104+
@JsonProperty("tabID") val tabId: String,
105+
val selectedOption: String,
106+
val comment: String?,
107+
) : IncomingCodeTestMessage
108+
98109
data class AuthFollowUpWasClicked(
99110
@JsonProperty("tabID") val tabId: String,
100111
val authType: AuthFollowUpType,

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/telemetry/TelemetryHelper.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ class TelemetryHelper(private val project: Project, private val sessionStorage:
444444

445445
data class FeedbackComment(
446446
val conversationId: String,
447-
val messageId: String,
447+
val messageId: String?,
448448
val reason: String,
449449
val userComment: String,
450450
val type: String = "codewhisperer-chat-answer-feedback",

plugins/amazonq/mynah-ui/src/mynah-ui/ui/apps/codeTestChatConnector.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import {ExtensionMessage} from "../commands";
55
import {ChatPayload, ConnectorProps} from "../connector";
66
import {FormButtonIds} from "../forms/constants";
7-
import {ChatItem, ChatItemAction, ChatItemType, MynahIcons, MynahUIDataModel} from '@aws/mynah-ui-chat'
7+
import {ChatItem, ChatItemAction, ChatItemType, FeedbackPayload, MynahIcons, MynahUIDataModel} from '@aws/mynah-ui-chat'
88
import {CodeReference} from "./amazonqCommonsConnector";
99
import {Status} from "@aws/mynah-ui-chat/dist/static";
1010
import {EmptyMynahUIDataModel} from "@aws/mynah-ui-chat/dist/helper/store";
@@ -13,6 +13,7 @@ import {doesNotMatch} from "node:assert";
1313
export interface ICodeTestChatConnectorProps {
1414
sendMessageToExtension: (message: ExtensionMessage) => void
1515
onChatAnswerReceived?: (tabID: string, message: ChatItem) => void
16+
sendFeedback?: (tabId: string, feedbackPayload: FeedbackPayload) => void | undefined
1617
onUpdateAuthentication: (
1718
featureDevEnabled: boolean,
1819
codeTransformEnabled: boolean,
@@ -571,6 +572,24 @@ export class CodeTestChatConnector {
571572
})
572573
}
573574

575+
onChatItemVoted = (tabId: string, messageId: string, vote: string): void | undefined => {
576+
this.sendMessageToExtension({
577+
tabID: tabId,
578+
vote: vote,
579+
command: 'chat-item-voted',
580+
tabType: 'codetest',
581+
})
582+
}
583+
584+
sendFeedback = (tabId: string, feedbackPayload: FeedbackPayload): void | undefined => {
585+
this.sendMessageToExtension({
586+
command: 'chat-item-feedback',
587+
...feedbackPayload,
588+
tabType: 'codetest',
589+
tabID: tabId,
590+
})
591+
}
592+
574593
onResponseBodyLinkClick = (tabID: string, messageId: string, link: string): void => {
575594
this.sendMessageToExtension({
576595
command: 'response-body-link-click',

plugins/amazonq/mynah-ui/src/mynah-ui/ui/connector.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,9 @@ export class Connector {
593593
case 'cwc':
594594
this.cwChatConnector.onSendFeedback(tabId, feedbackPayload)
595595
break
596+
case 'codetest':
597+
this.codeTestChatConnector.sendFeedback(tabId,feedbackPayload)
598+
break
596599
}
597600
}
598601

@@ -604,6 +607,9 @@ export class Connector {
604607
case 'featuredev':
605608
this.featureDevChatConnector.onChatItemVoted(tabId, messageId, vote)
606609
break
610+
case 'codetest' :
611+
this.codeTestChatConnector.onChatItemVoted(tabId,messageId,vote)
612+
break
607613
}
608614
}
609615
}

0 commit comments

Comments
 (0)