-
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 18 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,12 +16,12 @@ | |
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 | ||
import software.amazon.awssdk.services.codewhispererruntime.model.TargetCode | ||
import software.amazon.awssdk.services.codewhispererruntime.model.TestGenerationJobStatus | ||
import software.amazon.awssdk.services.codewhispererruntime.model.ThrottlingException | ||
import software.amazon.awssdk.services.codewhispererstreaming.model.ExportContext | ||
import software.amazon.awssdk.services.codewhispererstreaming.model.ExportIntent | ||
import software.aws.toolkits.core.utils.debug | ||
|
@@ -38,9 +38,13 @@ | |
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.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")) | ||
testGenStoppedError() | ||
} | ||
} | ||
|
||
|
@@ -104,24 +108,39 @@ | |
} | ||
|
||
// 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) { | ||
val statusCode = when { | ||
e is ThrottlingException -> e.statusCode() | ||
else -> 500 | ||
} | ||
LOG.error(e) { "Unexpected error while creating test generation job" } | ||
val errorMessage = getTelemetryErrorMessage(e, CodeWhispererConstants.FeatureName.TEST_GENERATION) | ||
throw CodeTestException( | ||
"CreateTestJobError: $errorMessage", | ||
statusCode, | ||
"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 +192,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 CodeTestException( | ||
"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 { | ||
|
@@ -183,12 +208,17 @@ | |
if (testGenerationResponse.testGenerationJob().shortAnswer() != null) { | ||
shortAnswer = parseShortAnswerString(testGenerationResponse.testGenerationJob().shortAnswer()) | ||
if (shortAnswer.stopIteration == "true") { | ||
throw Exception("${shortAnswer.planSummary}") | ||
throw CodeTestException("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 CodeTestException( | ||
"TestGenFailedError: " + message("testgen.message.failed"), | ||
|
||
500, | ||
"TestGenFailedError", | ||
message("testgen.error.generic_technical_error_message") | ||
|
||
) | ||
} else { | ||
// In progress | ||
LOG.debug { | ||
|
@@ -200,7 +230,7 @@ | |
if (previousIterationContext == null && testGenerationResponse.testGenerationJob().shortAnswer() != null) { | ||
shortAnswer = parseShortAnswerString(testGenerationResponse.testGenerationJob().shortAnswer()) | ||
if (shortAnswer.stopIteration == "true") { | ||
throw Exception("${shortAnswer.planSummary}") | ||
throw CodeTestException("TestGenFailedError: ${shortAnswer.planSummary}", 400, "TestGenFailedError", shortAnswer.planSummary) | ||
} | ||
codeTestChatHelper.updateAnswer( | ||
CodeTestChatMessageContent( | ||
|
@@ -232,6 +262,12 @@ | |
}, | ||
{ e -> | ||
LOG.error(e) { "ExportResultArchive failed: ${e.message}" } | ||
throw CodeTestException( | ||
"ExportResultsArchiveError: ${e.message}", | ||
500, | ||
"ExportResultsArchiveError", | ||
message("testgen.error.generic_technical_error_message") | ||
|
||
) | ||
}, | ||
{ startTime -> | ||
LOG.info { "ExportResultArchive latency: ${calculateTotalLatency(startTime, Instant.now())}" } | ||
|
@@ -493,14 +529,16 @@ | |
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") == 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 -> message("testgen.error.generic_error_message") | ||
Check warning on line 539 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
|
||
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
|
||
} | ||
|
||
if (e is CodeWhispererRuntimeException) { | ||
errorMessage = message("testgen.error.maximum_generations_reach") | ||
} | ||
codeTestChatHelper.addAnswer( | ||
CodeTestChatMessageContent( | ||
message = errorMessage, | ||
|
@@ -517,8 +555,9 @@ | |
jobGroup = session.testGenerationJobGroupName, | ||
jobId = session.testGenerationJob, | ||
result = if (e.message == message("testgen.message.cancelled")) MetricResult.Cancelled else MetricResult.Failed, | ||
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.toString(), | ||
perfClientLatency = (Instant.now().toEpochMilli() - session.startTimeOfTestGeneration), | ||
isCodeBlockSelected = session.isCodeBlockSelected, | ||
artifactsUploadDuration = session.artifactUploadDuration, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// 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: Int = 400, | ||
val code: String? = "DefaultError", | ||
val uiMessage: String? = message( | ||
Check warning on line 12 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_error_message" | ||
), | ||
) : RuntimeException() | ||
|
||
internal fun noFileOpenError(): Nothing = | ||
throw CodeTestException(message("codewhisperer.codescan.no_file_open"), 400, "ProjectZipError") | ||
Check warning on line 18 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 fileTooLarge(): Nothing = | ||
throw CodeTestException(message("codewhisperer.codescan.file_too_large_telemetry"), 400, "ProjectZipError") | ||
|
||
|
||
internal fun cannotFindFile(errorMessage: String, filepath: String): Nothing = | ||
error(message("codewhisperer.codescan.file_not_found", filepath, errorMessage)) | ||
Check warning on line 24 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
'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") | ||
|
||
|
||
fun testGenStoppedError(): Nothing = | ||
throw CodeTestException(message("testgen.message.cancelled"), 400, "TestGenCancelled", message("testgen.message.cancelled")) | ||
Check warning on line 36 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.