Skip to content

Commit 65e510e

Browse files
authored
Merge branch 'main' into fix/empty-patch
2 parents 83387a6 + aa3ba5e commit 65e510e

File tree

8 files changed

+155
-86
lines changed

8 files changed

+155
-86
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@ enum class ModifySourceFolderErrorReason(
2525

2626
override fun toString(): String = reasonText
2727
}
28+
29+
enum class FeatureDevOperation(private val operationName: String) {
30+
StartTaskAssistCodeGeneration("StartTaskAssistCodeGenerator"),
31+
CreateConversation("CreateConversation"),
32+
CreateUploadUrl("CreateUploadUrl"),
33+
GenerateCode("GenerateCode"),
34+
GetTaskAssistCodeGeneration("GetTaskAssistCodeGenerator"),
35+
ExportTaskAssistArchiveResult("ExportTaskAssistArchiveResult"),
36+
UploadToS3("UploadToS3"),
37+
;
38+
39+
override fun toString(): String = operationName
40+
}

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

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,79 @@ package software.aws.toolkits.jetbrains.services.amazonqFeatureDev
66
import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
77
import software.aws.toolkits.resources.message
88

9-
open class FeatureDevException(override val message: String?, override val cause: Throwable? = null) : RuntimeException()
10-
11-
class ContentLengthError(override val message: String, override val cause: Throwable?) : RepoSizeError, RuntimeException()
12-
13-
class ZipFileError(override val message: String, override val cause: Throwable?) : RuntimeException()
9+
/**
10+
* FeatureDevException models failures from feature dev operations.
11+
*
12+
* - Each failure is annotated based on className, operation, and a short desc. Use the `reason()` and `reasonDesc()` members for instrumentation.
13+
* - To throw an exception without modeling, throw FeatureDevException directly.
14+
*/
15+
open class FeatureDevException(override val message: String?, val operation: String, val desc: String?, override val cause: Throwable? = null) :
16+
RuntimeException() {
17+
fun reason(): String = this.javaClass.simpleName
18+
19+
fun reasonDesc(): String =
20+
when (desc) {
21+
desc -> "$operation | Description: $desc"
22+
else -> operation
23+
}
24+
}
25+
26+
class NoChangeRequiredException(operation: String, desc: String?, cause: Throwable? = null) :
27+
FeatureDevException(message("amazonqFeatureDev.exception.no_change_required_exception"), operation, desc, cause)
1428

1529
class NoChangeRequiredException(override val message: String, override val cause: Throwable?) : RuntimeException()
1630

1731
class CodeIterationLimitError(override val message: String, override val cause: Throwable?) : RuntimeException()
1832

19-
class MonthlyConversationLimitError(override val message: String, override val cause: Throwable?) : RuntimeException()
33+
class EmptyPatchException(operation: String, desc: String?, cause: Throwable? = null) :
34+
FeatureDevException(message("amazonqFeatureDev.exception.guardrails"), operation, desc, cause)
2035

21-
class UploadURLExpired(
22-
override val message: String = message(
23-
"amazonqFeatureDev.exception.upload_url_expiry"
24-
),
25-
override val cause: Throwable? = null,
26-
) : FeatureDevException(message, cause)
36+
class ContentLengthException(
37+
override val message: String = message("amazonqFeatureDev.content_length.error_text"),
38+
operation: String,
39+
desc: String?,
40+
cause: Throwable? = null,
41+
) :
42+
RepoSizeError, FeatureDevException(message, operation, desc, cause)
2743

28-
internal fun featureDevServiceError(message: String?): Nothing =
29-
throw FeatureDevException(message)
44+
class ZipFileCorruptedException(operation: String, desc: String?, cause: Throwable? = null) :
45+
FeatureDevException("The zip file is corrupted", operation, desc, cause)
3046

31-
internal fun codeGenerationFailedError(): Nothing =
32-
throw FeatureDevException(message("amazonqFeatureDev.code_generation.failed_generation"))
47+
class UploadURLExpired(operation: String, desc: String?, cause: Throwable? = null) :
48+
FeatureDevException(message("amazonqFeatureDev.exception.upload_url_expiry"), operation, desc, cause)
49+
50+
class CodeIterationLimitException(operation: String, desc: String?, cause: Throwable? = null) :
51+
FeatureDevException(message("amazonqFeatureDev.code_generation.iteration_limit.error_text"), operation, desc, cause)
3352

3453
internal fun noChangeRequiredException(): Nothing =
3554
throw NoChangeRequiredException(message("amazonqFeatureDev.exception.no_change_required_exception"), cause = null)
3655

3756
internal fun uploadCodeError(): Nothing =
3857
throw FeatureDevException(message("amazonqFeatureDev.exception.upload_code"))
58+
59+
class MonthlyConversationLimitError(message: String, operation: String, desc: String?, cause: Throwable? = null) :
60+
FeatureDevException(message, operation, desc, cause)
61+
62+
class GuardrailsException(operation: String, desc: String?, cause: Throwable? = null) :
63+
FeatureDevException(message("amazonqFeatureDev.exception.guardrails"), operation, desc, cause)
64+
65+
class PromptRefusalException(operation: String, desc: String?, cause: Throwable? = null) :
66+
FeatureDevException(message("amazonqFeatureDev.exception.prompt_refusal"), operation, desc, cause)
67+
68+
class ThrottlingException(operation: String, desc: String?, cause: Throwable? = null) :
69+
FeatureDevException(message("amazonqFeatureDev.exception.throttling"), operation, desc, cause)
70+
71+
class ExportParseException(operation: String, desc: String?, cause: Throwable? = null) :
72+
FeatureDevException(message("amazonqFeatureDev.exception.export_parsing_error"), operation, desc, cause)
3973

40-
internal fun conversationIdNotFound(): Nothing =
41-
throw FeatureDevException(message("amazonqFeatureDev.exception.conversation_not_found"))
74+
class CodeGenerationException(operation: String, desc: String?, cause: Throwable? = null) :
75+
FeatureDevException(message("amazonqFeatureDev.code_generation.failed_generation"), operation, desc, cause)
4276

43-
internal fun apiError(message: String?, cause: Throwable?): Nothing =
44-
throw FeatureDevException(message, cause)
77+
class UploadCodeException(operation: String, desc: String?, cause: Throwable? = null) :
78+
FeatureDevException(message("amazonqFeatureDev.exception.upload_code"), operation, desc, cause)
4579

46-
internal fun exportParseError(): Nothing =
47-
throw FeatureDevException(message("amazonqFeatureDev.exception.export_parsing_error"))
80+
class ConversationIdNotFoundException(operation: String, desc: String?, cause: Throwable? = null) :
81+
FeatureDevException(message("amazonqFeatureDev.exception.conversation_not_found"), operation, desc, cause)
4882

4983
val denyListedErrors = arrayOf("Deserialization error", "Inaccessible host", "UnknownHost")
5084
fun createUserFacingErrorMessage(message: String?): String? =

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

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
2929
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
3030
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
3131
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindowFactory
32-
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CodeIterationLimitError
32+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CodeIterationLimitException
3333
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.DEFAULT_RETRY_LIMIT
3434
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
3535
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevException
@@ -38,7 +38,7 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ModifySourceFo
3838
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.MonthlyConversationLimitError
3939
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.NoChangeRequiredException
4040
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.UploadURLExpired
41-
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ZipFileError
41+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ZipFileCorruptedException
4242
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.createUserFacingErrorMessage
4343
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.denyListedErrors
4444
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.FeatureDevMessageType
@@ -450,7 +450,7 @@ class FeatureDevController(
450450
)
451451
return this.newTask(message, isException)
452452
}
453-
is ZipFileError -> {
453+
is ZipFileCorruptedException -> {
454454
messenger.sendError(
455455
tabId = tabId,
456456
errMessage = err.message,
@@ -468,15 +468,7 @@ class FeatureDevController(
468468
messageType = FeatureDevMessageType.Answer,
469469
canBeVoted = true
470470
)
471-
is FeatureDevException -> {
472-
messenger.sendError(
473-
tabId = tabId,
474-
errMessage = err.message,
475-
retries = retriesRemaining(session),
476-
conversationId = session?.conversationIdUnsafe
477-
)
478-
}
479-
is CodeIterationLimitError -> {
471+
is CodeIterationLimitException -> {
480472
messenger.sendError(
481473
tabId = tabId,
482474
errMessage = err.message,
@@ -496,24 +488,36 @@ class FeatureDevController(
496488
)
497489
}
498490
else -> {
499-
var msg = createUserFacingErrorMessage("$FEATURE_NAME request failed: ${err.message ?: err.cause?.message}")
500-
val isDenyListedError = denyListedErrors.any { msg?.contains(it) ?: false }
501-
val defaultMessage: String = when (session?.sessionState?.phase) {
502-
SessionStatePhase.CODEGEN -> {
503-
if (isDenyListedError || retriesRemaining(session) > 0) {
504-
message("amazonqFeatureDev.code_generation.error_message")
505-
} else {
506-
message("amazonqFeatureDev.code_generation.no_retries.error_message")
491+
when (err) {
492+
is FeatureDevException -> {
493+
messenger.sendError(
494+
tabId = tabId,
495+
errMessage = err.message,
496+
retries = retriesRemaining(session),
497+
conversationId = session?.conversationIdUnsafe
498+
)
499+
}
500+
else -> {
501+
val msg = createUserFacingErrorMessage("$FEATURE_NAME request failed: ${err.message ?: err.cause?.message}")
502+
val isDenyListedError = denyListedErrors.any { msg?.contains(it) ?: false }
503+
val defaultMessage: String = when (session?.sessionState?.phase) {
504+
SessionStatePhase.CODEGEN -> {
505+
if (isDenyListedError || retriesRemaining(session) > 0) {
506+
message("amazonqFeatureDev.code_generation.error_message")
507+
} else {
508+
message("amazonqFeatureDev.code_generation.no_retries.error_message")
509+
}
510+
}
511+
else -> message("amazonqFeatureDev.error_text")
507512
}
513+
messenger.sendError(
514+
tabId = tabId,
515+
errMessage = defaultMessage,
516+
retries = retriesRemaining(session),
517+
conversationId = session?.conversationIdUnsafe
518+
)
508519
}
509-
else -> message("amazonqFeatureDev.error_text")
510520
}
511-
messenger.sendError(
512-
tabId = tabId,
513-
errMessage = defaultMessage,
514-
retries = retriesRemaining(session),
515-
conversationId = session?.conversationIdUnsafe
516-
)
517521
}
518522
}
519523
}

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import software.amazon.awssdk.services.codewhispererruntime.model.CodeGeneration
88
import software.aws.toolkits.core.utils.getLogger
99
import software.aws.toolkits.core.utils.warn
1010
import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher
11+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CodeGenerationException
12+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.EmptyPatchException
1113
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
12-
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.codeGenerationFailedError
13-
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.featureDevServiceError
14+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevException
15+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevOperation
16+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.GuardrailsException
17+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.PromptRefusalException
18+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ThrottlingException
1419
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAnswerPart
1520
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendUpdatePlaceholder
1621
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.noChangeRequiredException
@@ -38,6 +43,7 @@ class CodeGenerationState(
3843
val startTime = System.currentTimeMillis()
3944
var result: Result = Result.Succeeded
4045
var failureReason: String? = null
46+
var failureReasonDesc: String? = null
4147
var codeGenerationWorkflowStatus: CodeGenerationWorkflowStatus = CodeGenerationWorkflowStatus.COMPLETE
4248
var numberOfReferencesGenerated: Int? = null
4349
var numberOfFilesGenerated: Int? = null
@@ -87,6 +93,10 @@ class CodeGenerationState(
8793
logger.warn(e) { "$FEATURE_NAME: Code generation failed: ${e.message}" }
8894
result = Result.Failed
8995
failureReason = e.javaClass.simpleName
96+
if (e is FeatureDevException) {
97+
failureReason = e.reason()
98+
failureReasonDesc = e.reasonDesc()
99+
}
90100
codeGenerationWorkflowStatus = CodeGenerationWorkflowStatus.FAILED
91101

92102
throw e
@@ -101,6 +111,7 @@ class CodeGenerationState(
101111
amazonqRepositorySize = repositorySize,
102112
result = result,
103113
reason = failureReason,
114+
reasonDesc = failureReasonDesc,
104115
duration = (System.currentTimeMillis() - startTime).toDouble(),
105116
credentialStartUrl = getStartUrl(config.featureDevService.project)
106117
)
@@ -150,25 +161,25 @@ private suspend fun CodeGenerationState.generateCode(codeGenerationId: String, m
150161
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
151162
"Guardrails"
152163
),
153-
-> featureDevServiceError(message("amazonqFeatureDev.exception.guardrails"))
164+
-> throw GuardrailsException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Failed guardrails")
154165
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
155166
"PromptRefusal"
156167
),
157-
-> featureDevServiceError(message("amazonqFeatureDev.exception.prompt_refusal"))
168+
-> throw PromptRefusalException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Prompt refusal")
158169
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
159170
"EmptyPatch"
160171
),
161172
-> {
162173
if (codeGenerationResultState.codeGenerationStatusDetail().contains("NO_CHANGE_REQUIRED")) {
163174
noChangeRequiredException()
164175
}
165-
featureDevServiceError(message("amazonqFeatureDev.exception.guardrails"))
176+
throw EmptyPatchException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Empty patch")
166177
}
167178
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
168179
"Throttling"
169180
),
170-
-> featureDevServiceError(message("amazonqFeatureDev.exception.throttling"))
171-
else -> codeGenerationFailedError()
181+
-> throw ThrottlingException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Request throttled")
182+
else -> throw CodeGenerationException(operation = FeatureDevOperation.GenerateCode.toString(), desc = null)
172183
}
173184
}
174185
else -> error("Unknown status: ${codeGenerationResultState.codeGenerationStatus().status()}")

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import com.intellij.openapi.vfs.VfsUtil
99
import software.aws.toolkits.jetbrains.services.amazonq.FeatureDevSessionContext
1010
import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher
1111
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CODE_GENERATION_RETRY_LIMIT
12+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ConversationIdNotFoundException
1213
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
1314
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.MAX_PROJECT_SIZE_BYTES
1415
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.clients.FeatureDevClient
15-
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.conversationIdNotFound
1616
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAsyncEventProgress
1717
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.FeatureDevService
1818
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.resolveAndCreateOrUpdateFile
@@ -138,7 +138,7 @@ class Session(val tabID: String, val project: Project) {
138138
val conversationId: String
139139
get() {
140140
if (_conversationId == null) {
141-
conversationIdNotFound()
141+
throw ConversationIdNotFoundException(operation = "Session", desc = "Conversation ID not found")
142142
} else {
143143
return _conversationId as String
144144
}

0 commit comments

Comments
 (0)