-
Notifications
You must be signed in to change notification settings - Fork 234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(amazonq): Error handling and telemetry for Unit test generation. #5192
base: main
Are you sure you want to change the base?
Changes from 12 commits
b733179
bc08f99
9ee9b13
c04de5e
b22eca6
10b39bb
7fc0959
ee4de19
95b7f1b
75a6254
3a87313
4461636
143c8c7
d893719
4698590
a0c6fc1
0bab6b6
1c89a6e
f200c7c
f86d1cb
d62ae9c
584547b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,6 @@ | |
import kotlinx.coroutines.Job | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.launch | ||
import software.amazon.awssdk.services.codewhispererruntime.model.CodeWhispererRuntimeException | ||
import software.amazon.awssdk.services.codewhispererruntime.model.GetTestGenerationResponse | ||
import software.amazon.awssdk.services.codewhispererruntime.model.Range | ||
import software.amazon.awssdk.services.codewhispererruntime.model.StartTestGenerationResponse | ||
|
@@ -38,9 +37,14 @@ | |
import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.Session | ||
import software.aws.toolkits.jetbrains.services.amazonqCodeTest.utils.combineBuildAndExecuteLogFiles | ||
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.calculateTotalLatency | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.CodeTestException | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.codeTestServerException | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.sessionconfig.CodeTestSessionConfig | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.testGenStoppedError | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil.promptReAuth | ||
import software.aws.toolkits.jetbrains.services.codewhisperer.util.getTelemetryErrorMessage | ||
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl | ||
import software.aws.toolkits.jetbrains.services.cwc.messages.ChatMessageType | ||
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference | ||
|
@@ -67,7 +71,7 @@ | |
|
||
private fun throwIfCancelled(session: Session) { | ||
if (!session.isGeneratingTests) { | ||
error(message("testgen.message.cancelled")) | ||
throw testGenStoppedError() | ||
} | ||
} | ||
|
||
|
@@ -104,24 +108,37 @@ | |
} | ||
|
||
// 2nd API call: StartTestGeneration | ||
val startTestGenerationResponse = startTestGeneration( | ||
uploadId = createUploadUrlResponse.uploadId(), | ||
targetCode = listOf( | ||
TargetCode.builder() | ||
.relativeTargetPath(codeTestResponseContext.currentFileRelativePath.toString()) | ||
.targetLineRangeList( | ||
if (selectionRange != null) { | ||
listOf( | ||
selectionRange | ||
) | ||
} else { | ||
emptyList() | ||
} | ||
) | ||
.build() | ||
), | ||
userInput = prompt | ||
) | ||
val startTestGenerationResponse = try { | ||
startTestGeneration( | ||
uploadId = createUploadUrlResponse.uploadId(), | ||
targetCode = listOf( | ||
TargetCode.builder() | ||
.relativeTargetPath(codeTestResponseContext.currentFileRelativePath.toString()) | ||
.targetLineRangeList( | ||
if (selectionRange != null) { | ||
listOf( | ||
selectionRange | ||
) | ||
} else { | ||
emptyList() | ||
} | ||
) | ||
.build() | ||
), | ||
userInput = prompt | ||
) | ||
} catch (e: Exception) { | ||
LOG.error(e) { "Failed to create test generation job" } | ||
// TODO: Not able to emit e.statusCode directly as statusCode is private property | ||
// Cannot access 'statusCode': it is invisible (private in a supertype) in 'ThrottlingException' | ||
val errorMessage = getTelemetryErrorMessage(e, CodeWhispererConstants.FeatureName.TEST_GENERATION) | ||
throw codeTestServerException( | ||
"CreateTestJobError: $errorMessage", | ||
"400", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be renamed? It mentions server exception but uses 4xx There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah this needs to be e.statusCode() but for now this can be 5XX. |
||
"CreateTestJobError", | ||
message("testgen.error.generic_technical_error_message") | ||
Check warning Code scanning / QDJVMC Usage of redundant or deprecated syntax or deprecated symbols Warning
'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead
|
||
) | ||
} | ||
|
||
val job = startTestGenerationResponse.testGenerationJob() | ||
session.startTestGenerationRequestId = startTestGenerationResponse.responseMetadata().requestId() | ||
|
@@ -173,7 +190,13 @@ | |
} | ||
// update test summary card | ||
} else { | ||
throw Exception(message("testgen.message.failed")) | ||
// If job status is Completed and has no ShortAnswer then there might be some issue in the backend. | ||
throw codeTestServerException( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you verified that this works? It looks like you are throwing a function which throws an exception There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verified in Kibana and its working fine. Attached related links in Slack. |
||
"TestGenFailedError: " + message("testgen.message.failed"), | ||
"500", | ||
"TestGenFailedError", | ||
message("testgen.error.generic_technical_error_message") | ||
Check warning Code scanning / QDJVMC Usage of redundant or deprecated syntax or deprecated symbols Warning
'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead
|
||
) | ||
} | ||
} else if (status == TestGenerationJobStatus.FAILED) { | ||
LOG.debug { | ||
|
@@ -184,11 +207,17 @@ | |
shortAnswer = parseShortAnswerString(testGenerationResponse.testGenerationJob().shortAnswer()) | ||
if (shortAnswer.stopIteration == "true") { | ||
throw Exception("${shortAnswer.planSummary}") | ||
throw codeTestServerException("TestGenFailedError: ${shortAnswer.planSummary}", "400", "TestGenFailedError", shortAnswer.planSummary) | ||
} | ||
} | ||
|
||
// TODO: Modify text according to FnF | ||
throw Exception(message("testgen.message.failed")) | ||
// If job status is Failed and has no ShortAnswer then there might be some issue in the backend. | ||
throw codeTestServerException( | ||
"TestGenFailedError: " + message("testgen.message.failed"), | ||
|
||
"500", | ||
"TestGenFailedError", | ||
message("testgen.error.generic_technical_error_message") | ||
|
||
) | ||
} else { | ||
// In progress | ||
LOG.debug { | ||
|
@@ -200,7 +229,7 @@ | |
if (previousIterationContext == null && testGenerationResponse.testGenerationJob().shortAnswer() != null) { | ||
shortAnswer = parseShortAnswerString(testGenerationResponse.testGenerationJob().shortAnswer()) | ||
if (shortAnswer.stopIteration == "true") { | ||
throw Exception("${shortAnswer.planSummary}") | ||
throw codeTestServerException("TestGenFailedError: ${shortAnswer.planSummary}", "400", "TestGenFailedError", shortAnswer.planSummary) | ||
} | ||
codeTestChatHelper.updateAnswer( | ||
CodeTestChatMessageContent( | ||
|
@@ -232,6 +261,12 @@ | |
}, | ||
{ e -> | ||
LOG.error(e) { "ExportResultArchive failed: ${e.message}" } | ||
throw codeTestServerException( | ||
"ExportResultsArchiveError: ${e.message}", | ||
"500", | ||
"ExportResultsArchiveError", | ||
message("testgen.error.generic_technical_error_message") | ||
|
||
) | ||
}, | ||
{ startTime -> | ||
LOG.info { "ExportResultArchive latency: ${calculateTotalLatency(startTime, Instant.now())}" } | ||
|
@@ -493,14 +528,19 @@ | |
launchTestGenFlow(prompt, codeTestChatHelper, previousIterationContext, selectionRange) | ||
} catch (e: Exception) { | ||
// Add an answer for displaying error message | ||
var errorMessage = e.message | ||
if (e is JsonParseException) { | ||
errorMessage = message("testgen.error.generic_error_message") | ||
val errorMessage = when { | ||
e is CodeTestException && e.statusCode == "400" && | ||
|
||
e.message?.startsWith( | ||
"CreateTestJobError: " + | ||
"Maximum com.amazon.aws.codewhisperer.runtime.StartTestGeneration reached for this month." | ||
) == true -> | ||
message("testgen.error.maximum_generations_reach") | ||
|
||
|
||
e is CodeTestException -> e.uiMessage | ||
|
||
e is JsonParseException -> message("testgen.error.generic_technical_error_message") | ||
Check warning Code scanning / QDJVMC Usage of redundant or deprecated syntax or deprecated symbols Warning
'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead
|
||
else -> e.message | ||
} | ||
|
||
if (e is CodeWhispererRuntimeException) { | ||
errorMessage = message("testgen.error.maximum_generations_reach") | ||
} | ||
codeTestChatHelper.addAnswer( | ||
CodeTestChatMessageContent( | ||
message = errorMessage, | ||
|
@@ -516,11 +556,12 @@ | |
credentialStartUrl = getStartUrl(project), | ||
jobGroup = session.testGenerationJobGroupName, | ||
jobId = session.testGenerationJob, | ||
result = if (e.message == message("testgen.message.cancelled")) MetricResult.Cancelled else MetricResult.Failed, | ||
Check warning on line 559 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeWhispererUTGChatManager.kt GitHub Actions / qodanaUsage of redundant or deprecated syntax or deprecated symbols
|
||
reason = e.javaClass.name, | ||
reasonDesc = e.message, | ||
reason = (e as CodeTestException).code ?: "DefaultError", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why don't we just let it be null? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can be but placing this under the bucket of DefaultError to be consistent in Kibana. |
||
reasonDesc = if (e.message == message("testgen.message.cancelled")) "${e.code}: ${e.message}" else e.message, | ||
|
||
httpStatusCode = e.statusCode ?: "400", | ||
perfClientLatency = (Instant.now().toEpochMilli() - session.startTimeOfTestGeneration), | ||
isCodeBlockSelected = session.isCodeBlockSelected, | ||
artifactsUploadDuration = session.artifactUploadDuration, | ||
buildPayloadBytes = session.srcPayloadSize, | ||
buildZipFileBytes = session.srcZipFileSize, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package software.aws.toolkits.jetbrains.services.codewhisperer.codetest | ||
|
||
import software.aws.toolkits.resources.message | ||
Check warning on line 6 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/CodeTestException.kt GitHub Actions / qodanaUsage of redundant or deprecated syntax or deprecated symbols
|
||
Check warning Code scanning / QDJVMC Usage of redundant or deprecated syntax or deprecated symbols Warning
Remove deprecated symbol import
|
||
|
||
open class CodeTestException( | ||
override val message: String?, | ||
val statusCode: String? = "400", | ||
val code: String? = "DefaultError", | ||
val uiMessage: String? = message( | ||
|
||
"testgen.error.generic_error_message" | ||
), | ||
) : RuntimeException() | ||
|
||
open class CodeTestServerException( | ||
override val message: String?, | ||
val statusCode: String? = "400", | ||
val code: String? = "DefaultError", | ||
val uiMessage: String? = message( | ||
Check warning on line 21 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/CodeTestException.kt GitHub Actions / qodanaUsage of redundant or deprecated syntax or deprecated symbols
|
||
|
||
"testgen.error.generic_technical_error_message" | ||
), | ||
) : RuntimeException() | ||
|
||
internal fun noFileOpenError(): Nothing = | ||
throw CodeTestException(message("codewhisperer.codescan.no_file_open"), "400", "ProjectZipError") | ||
|
||
|
||
internal fun fileTooLarge(): Nothing = | ||
throw CodeTestException(message("codewhisperer.codescan.file_too_large_telemetry"), "400", "ProjectZipError") | ||
Check warning on line 30 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/CodeTestException.kt GitHub Actions / qodanaUsage of redundant or deprecated syntax or deprecated symbols
|
||
|
||
|
||
internal fun cannotFindFile(errorMessage: String, filepath: String): Nothing = | ||
error(message("codewhisperer.codescan.file_not_found", filepath, errorMessage)) | ||
Check warning Code scanning / QDJVMC Usage of redundant or deprecated syntax or deprecated symbols Warning
'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead
|
||
|
||
internal fun cannotFindValidFile(errorMessage: String): Nothing = | ||
throw CodeTestException(errorMessage, "400", "ProjectZipError") | ||
|
||
internal fun cannotFindBuildArtifacts(errorMessage: String): Nothing = | ||
throw CodeTestException(errorMessage, "400", "ProjectZipError") | ||
|
||
internal fun invalidSourceZipError(): Nothing = | ||
throw CodeTestException(message("codewhisperer.codescan.invalid_source_zip_telemetry"), "400", "InvalidSourceZipError") | ||
Check warning on line 42 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/CodeTestException.kt GitHub Actions / qodanaUsage of redundant or deprecated syntax or deprecated symbols
|
||
|
||
|
||
fun codeTestServerException(errorMessage: String, statusCode: String?, code: String?, uiMessage: String?): Nothing = | ||
throw CodeTestServerException(errorMessage, statusCode, code, uiMessage) | ||
|
||
fun testGenStoppedError(): Nothing = | ||
throw CodeTestException(message("testgen.message.cancelled"), "400", "TestGenCancelled", message("testgen.message.cancelled")) | ||
Check warning on line 48 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/CodeTestException.kt GitHub Actions / qodanaUsage of redundant or deprecated syntax or deprecated symbols
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try
statusCode()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I tried statusCode(), no luck.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.