Skip to content

Commit f4ae707

Browse files
authored
Merge branch 'main' into main
2 parents 3aacba4 + d18218c commit f4ae707

File tree

18 files changed

+268
-250
lines changed

18 files changed

+268
-250
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Amazon Q (/dev): provide error messaging when no code changes are required for the prompt"
4+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.intellij.ui.jcef.JBCefJSQuery
2020
import org.cef.CefApp
2121
import software.aws.toolkits.core.utils.error
2222
import software.aws.toolkits.core.utils.getLogger
23+
import software.aws.toolkits.core.utils.warn
2324
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
2425
import software.aws.toolkits.jetbrains.core.credentials.ToolkitAuthManager
2526
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevExce
3636
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.InboundAppMessagesHandler
3737
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ModifySourceFolderErrorReason
3838
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.MonthlyConversationLimitError
39+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.NoChangeRequiredException
3940
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.UploadURLExpired
4041
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ZipFileCorruptedException
4142
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.createUserFacingErrorMessage
@@ -351,7 +352,7 @@ class FeatureDevController(
351352
}
352353
}
353354

354-
private suspend fun newTask(tabId: String) {
355+
private suspend fun newTask(tabId: String, isException: Boolean? = false) {
355356
val session = getSessionInfo(tabId)
356357
val sessionLatency = System.currentTimeMillis() - session.sessionStartTime
357358
AmazonqTelemetry.endChat(
@@ -362,9 +363,15 @@ class FeatureDevController(
362363
chatSessionStorage.deleteSession(tabId)
363364

364365
newTabOpened(tabId)
365-
366-
messenger.sendAnswer(tabId = tabId, messageType = FeatureDevMessageType.Answer, message = message("amazonqFeatureDev.chat_message.ask_for_new_task"))
366+
if (isException != null && !isException) {
367+
messenger.sendAnswer(
368+
tabId = tabId,
369+
messageType = FeatureDevMessageType.Answer,
370+
message = message("amazonqFeatureDev.chat_message.ask_for_new_task")
371+
)
372+
}
367373
messenger.sendUpdatePlaceholder(tabId = tabId, newPlaceholder = message("amazonqFeatureDev.placeholder.new_plan"))
374+
messenger.sendChatInputEnabledMessage(tabId = tabId, enabled = true)
368375
}
369376

370377
private suspend fun closeSession(tabId: String) {
@@ -433,6 +440,16 @@ class FeatureDevController(
433440
),
434441
)
435442
}
443+
is NoChangeRequiredException -> {
444+
val isException = true
445+
messenger.sendAnswer(
446+
tabId = tabId,
447+
message = err.message,
448+
messageType = FeatureDevMessageType.Answer,
449+
canBeVoted = true
450+
)
451+
return this.newTask(message, isException)
452+
}
436453
is ZipFileCorruptedException -> {
437454
messenger.sendError(
438455
tabId = tabId,

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
1414
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevException
1515
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevOperation
1616
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.GuardrailsException
17+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.NoChangeRequiredException
1718
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.PromptRefusalException
1819
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ThrottlingException
1920
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAnswerPart
@@ -168,7 +169,12 @@ private suspend fun CodeGenerationState.generateCode(codeGenerationId: String, m
168169
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
169170
"EmptyPatch"
170171
),
171-
-> throw EmptyPatchException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Empty patch")
172+
-> {
173+
if (codeGenerationResultState.codeGenerationStatusDetail().contains("NO_CHANGE_REQUIRED")) {
174+
throw NoChangeRequiredException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "No change required")
175+
}
176+
throw EmptyPatchException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Empty patch")
177+
}
172178
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
173179
"Throttling"
174180
),

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import software.aws.toolkits.jetbrains.services.telemetry.ClientMetadata
5656
import software.aws.toolkits.telemetry.CodewhispererCompletionType
5757
import software.aws.toolkits.telemetry.CodewhispererSuggestionState
5858
import java.time.Instant
59-
import java.util.concurrent.TimeUnit
6059
import kotlin.reflect.KProperty0
6160
import kotlin.reflect.jvm.isAccessible
6261

@@ -114,7 +113,8 @@ interface CodeWhispererClientAdaptor : Disposable {
114113
requestId: String,
115114
language: CodeWhispererProgrammingLanguage,
116115
customizationArn: String,
117-
modificationPercentage: Double,
116+
acceptedCharacterCount: Int,
117+
unmodifiedAcceptedTokenCount: Int,
118118
): SendTelemetryEventResponse
119119

120120
fun sendCodeScanTelemetry(
@@ -305,13 +305,14 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
305305
val programmingLanguage = fileContext.programmingLanguage
306306
var e2eLatency = requestContext.latencyContext.getCodeWhispererEndToEndLatency()
307307

308-
// When we send a userTriggerDecision of Empty or Discard, we set the time users see the first
309-
// suggestion to be now.
310-
if (e2eLatency < 0) {
311-
e2eLatency = TimeUnit.NANOSECONDS.toMillis(
312-
System.nanoTime() - requestContext.latencyContext.codewhispererEndToEndStart
313-
).toDouble()
308+
// When we send a userTriggerDecision for neither Accept nor Reject, service side should not use this value
309+
// and client side will set this value to 0.0.
310+
if (suggestionState != CodewhispererSuggestionState.Accept &&
311+
suggestionState != CodewhispererSuggestionState.Reject
312+
) {
313+
e2eLatency = 0.0
314314
}
315+
315316
return bearerClient().sendTelemetryEvent { requestBuilder ->
316317
requestBuilder.telemetryEvent { telemetryEventBuilder ->
317318
telemetryEventBuilder.userTriggerDecisionEvent {
@@ -321,6 +322,9 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
321322
it.sessionId(responseContext.sessionId)
322323
it.recommendationLatencyMilliseconds(e2eLatency)
323324
it.triggerToResponseLatencyMilliseconds(requestContext.latencyContext.paginationFirstCompletionTime)
325+
it.perceivedLatencyMilliseconds(
326+
requestContext.latencyContext.getPerceivedLatency(requestContext.triggerTypeInfo.triggerType)
327+
)
324328
it.suggestionState(suggestionState.toCodeWhispererSdkType())
325329
it.timestamp(Instant.now())
326330
it.suggestionReferenceCount(suggestionReferenceCount)
@@ -360,7 +364,8 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
360364
requestId: String,
361365
language: CodeWhispererProgrammingLanguage,
362366
customizationArn: String,
363-
modificationPercentage: Double,
367+
acceptedCharacterCount: Int,
368+
unmodifiedAcceptedTokenCount: Int,
364369
): SendTelemetryEventResponse = bearerClient().sendTelemetryEvent { requestBuilder ->
365370
requestBuilder.telemetryEvent { telemetryEventBuilder ->
366371
telemetryEventBuilder.userModificationEvent {
@@ -370,8 +375,11 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW
370375
languageBuilder.languageName(language.toCodeWhispererRuntimeLanguage().languageId)
371376
}
372377
it.customizationArn(customizationArn)
373-
it.modificationPercentage(modificationPercentage)
378+
// deprecated field, service side should not use this % anymore
379+
it.modificationPercentage(0.0)
374380
it.timestamp(Instant.now())
381+
it.acceptedCharacterCount(acceptedCharacterCount)
382+
it.unmodifiedAcceptedCharacterCount(unmodifiedAcceptedTokenCount)
375383
}
376384
}
377385
requestBuilder.optOutPreference(getTelemetryOptOutPreference())

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import software.amazon.awssdk.services.codewhispererruntime.model.GenerateComple
1313
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
1414
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.sessionconfig.PayloadContext
1515
import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage
16+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutoTriggerService
1617
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererAutomatedTriggerType
1718
import software.aws.toolkits.jetbrains.services.codewhisperer.service.RequestContext
1819
import software.aws.toolkits.jetbrains.services.codewhisperer.service.ResponseContext
@@ -198,6 +199,18 @@ data class LatencyContext(
198199
fun getCodeWhispererPreprocessingLatency() = TimeUnit.NANOSECONDS.toMillis(
199200
codewhispererPreprocessingEnd - codewhispererPreprocessingStart
200201
).toDouble()
202+
203+
// For auto-trigger it's from the time when last char typed
204+
// for manual-trigger it's from the time when last trigger action happened(alt + c)
205+
fun getPerceivedLatency(triggerType: CodewhispererTriggerType) =
206+
if (triggerType == CodewhispererTriggerType.OnDemand) {
207+
getCodeWhispererEndToEndLatency()
208+
} else {
209+
(
210+
TimeUnit.NANOSECONDS.toMillis(codewhispererEndToEndEnd) -
211+
CodeWhispererAutoTriggerService.getInstance().timeAtLastCharTyped.toEpochMilli()
212+
).toDouble()
213+
}
201214
}
202215

203216
data class TryExampleRowContext(

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererAutoTriggerService.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ class CodeWhispererAutoTriggerService : CodeWhispererAutoTriggerHandler, Disposa
4242

4343
private var lastInvocationTime: Instant? = null
4444
private var lastInvocationLineNum: Int? = null
45+
var timeAtLastCharTyped: Instant = Instant.now()
46+
private set
4547

4648
init {
4749
scheduleReset()
@@ -54,6 +56,7 @@ class CodeWhispererAutoTriggerService : CodeWhispererAutoTriggerHandler, Disposa
5456
// a util wrapper
5557
fun tryInvokeAutoTrigger(editor: Editor, triggerType: CodeWhispererAutomatedTriggerType): Job? {
5658
// only needed for Classifier group, thus calculate it lazily
59+
timeAtLastCharTyped = Instant.now()
5760
val classifierResult: ClassifierResult by lazy { shouldTriggerClassifier(editor, triggerType.telemetryType) }
5861
val language = runReadAction {
5962
FileDocumentManager.getInstance().getFile(editor.document)?.programmingLanguage()

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererFeatureConfigService.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ class CodeWhispererFeatureConfigService {
3232
featureConfigs[it.feature()] = FeatureContext(it.feature(), it.variation(), it.value())
3333
}
3434

35+
// Only apply new auto-trigger UX to BID users
36+
val isNewAutoTriggerUX = getNewAutoTriggerUX()
37+
if (isNewAutoTriggerUX) {
38+
calculateIfIamIdentityCenterConnection(project) {
39+
featureConfigs.remove(NEW_AUTO_TRIGGER_UX)
40+
}
41+
}
42+
3543
val customizationArnOverride = featureConfigs[CUSTOMIZATION_ARN_OVERRIDE_NAME]?.value?.stringValue()
3644
if (customizationArnOverride != null) {
3745
// Double check if server-side wrongly returns a customizationArn to BID users
@@ -84,20 +92,24 @@ class CodeWhispererFeatureConfigService {
8492

8593
fun getCustomizationArnOverride(): String = getFeatureValueForKey(CUSTOMIZATION_ARN_OVERRIDE_NAME).stringValue()
8694

95+
fun getNewAutoTriggerUX(): Boolean = getFeatureValueForKey(NEW_AUTO_TRIGGER_UX).boolValue()
96+
8797
// Get the feature value for the given key.
88-
// In case of a misconfiguration, it will return a default feature value of Boolean true.
98+
// In case of a misconfiguration, it will return a default feature value of Boolean false.
8999
private fun getFeatureValueForKey(name: String): FeatureValue =
90100
featureConfigs[name]?.value ?: FEATURE_DEFINITIONS[name]?.value
91-
?: FeatureValue.builder().boolValue(true).build()
101+
?: FeatureValue.builder().boolValue(false).build()
92102

93103
companion object {
94104
fun getInstance(): CodeWhispererFeatureConfigService = service()
95105
private const val TEST_FEATURE_NAME = "testFeature"
96106
private const val DATA_COLLECTION_FEATURE = "IDEProjectContextDataCollection"
97107
const val CUSTOMIZATION_ARN_OVERRIDE_NAME = "customizationArnOverride"
108+
private const val NEW_AUTO_TRIGGER_UX = "newAutoTriggerUX"
98109
private val LOG = getLogger<CodeWhispererFeatureConfigService>()
99110

100111
// TODO: add real feature later
112+
// Also serve as default values in case server-side config isn't there yet
101113
internal val FEATURE_DEFINITIONS = mapOf(
102114
TEST_FEATURE_NAME to FeatureContext(
103115
TEST_FEATURE_NAME,
@@ -109,7 +121,12 @@ class CodeWhispererFeatureConfigService {
109121
CUSTOMIZATION_ARN_OVERRIDE_NAME,
110122
"customizationARN",
111123
FeatureValue.builder().stringValue("").build()
112-
)
124+
),
125+
NEW_AUTO_TRIGGER_UX to FeatureContext(
126+
NEW_AUTO_TRIGGER_UX,
127+
"CONTROL",
128+
FeatureValue.builder().boolValue(false).build()
129+
),
113130
)
114131
}
115132
}

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,8 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable {
244244
val sessionId = response.sdkHttpResponse().headers().getOrDefault(KET_SESSION_ID, listOf(requestId))[0]
245245
if (requestCount == 1) {
246246
requestContext.latencyContext.codewhispererPostprocessingStart = System.nanoTime()
247-
requestContext.latencyContext.paginationFirstCompletionTime = latency
247+
requestContext.latencyContext.paginationFirstCompletionTime =
248+
(endTime - requestContext.latencyContext.codewhispererEndToEndStart).toDouble()
248249
requestContext.latencyContext.firstRequestId = requestId
249250
CodeWhispererInvocationStatus.getInstance().setInvocationSessionId(sessionId)
250251
}

0 commit comments

Comments
 (0)