Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,16 @@ enum class ModifySourceFolderErrorReason(

override fun toString(): String = reasonText
}

enum class FeatureDevOperation(private val operationName: String) {
StartTaskAssistCodeGeneration("StartTaskAssistCodeGenerator"),
CreateConversation("CreateConversation"),
CreateUploadUrl("CreateUploadUrl"),
GenerateCode("GenerateCode"),
GetTaskAssistCodeGeneration("GetTaskAssistCodeGenerator"),
ExportTaskAssistArchiveResult("ExportTaskAssistArchiveResult"),
UploadToS3("UploadToS3"),
;

override fun toString(): String = operationName
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for cleaning this up

Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,69 @@ package software.aws.toolkits.jetbrains.services.amazonqFeatureDev
import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
import software.aws.toolkits.resources.message

open class FeatureDevException(override val message: String?, override val cause: Throwable? = null) : RuntimeException()
/**
* FeatureDevException models failures from feature dev operations.
*
* - Each failure is annotated based on className, operation, and a short desc. Use the `reason()` and `reasonDesc()` members for instrumentation.
* - To throw an exception without modeling, throw FeatureDevException directly.
*/
open class FeatureDevException(override val message: String?, val operation: String, val desc: String?, override val cause: Throwable? = null) :
RuntimeException() {
fun reason(): String = this.javaClass.simpleName

class ContentLengthError(override val message: String, override val cause: Throwable?) : RepoSizeError, RuntimeException()
fun reasonDesc(): String =
when (desc) {
desc -> "$operation | Description: $desc"
else -> operation
}
}

class ZipFileError(override val message: String, override val cause: Throwable?) : RuntimeException()
class NoChangeRequiredException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.no_change_required_exception"), operation, desc, cause)

class CodeIterationLimitError(override val message: String, override val cause: Throwable?) : RuntimeException()
class EmptyPatchException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.guardrails"), operation, desc, cause)

class MonthlyConversationLimitError(override val message: String, override val cause: Throwable?) : RuntimeException()
class ContentLengthException(
override val message: String = message("amazonqFeatureDev.content_length.error_text"),
operation: String,
desc: String?,
cause: Throwable? = null,
) :
RepoSizeError, FeatureDevException(message, operation, desc, cause)

class UploadURLExpired(
override val message: String = message(
"amazonqFeatureDev.exception.upload_url_expiry"
),
override val cause: Throwable? = null,
) : FeatureDevException(message, cause)
class ZipFileCorruptedException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException("The zip file is corrupted", operation, desc, cause)

internal fun featureDevServiceError(message: String?): Nothing =
throw FeatureDevException(message)
class UploadURLExpired(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.upload_url_expiry"), operation, desc, cause)

internal fun codeGenerationFailedError(): Nothing =
throw FeatureDevException(message("amazonqFeatureDev.code_generation.failed_generation"))
class CodeIterationLimitException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.code_generation.iteration_limit.error_text"), operation, desc, cause)

internal fun uploadCodeError(): Nothing =
throw FeatureDevException(message("amazonqFeatureDev.exception.upload_code"))
class MonthlyConversationLimitError(message: String, operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message, operation, desc, cause)

internal fun conversationIdNotFound(): Nothing =
throw FeatureDevException(message("amazonqFeatureDev.exception.conversation_not_found"))
class GuardrailsException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.guardrails"), operation, desc, cause)

internal fun apiError(message: String?, cause: Throwable?): Nothing =
throw FeatureDevException(message, cause)
class PromptRefusalException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.prompt_refusal"), operation, desc, cause)

internal fun exportParseError(): Nothing =
throw FeatureDevException(message("amazonqFeatureDev.exception.export_parsing_error"))
class ThrottlingException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.throttling"), operation, desc, cause)

class ExportParseException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.export_parsing_error"), operation, desc, cause)

class CodeGenerationException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.code_generation.failed_generation"), operation, desc, cause)

class UploadCodeException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.upload_code"), operation, desc, cause)

class ConversationIdNotFoundException(operation: String, desc: String?, cause: Throwable? = null) :
FeatureDevException(message("amazonqFeatureDev.exception.conversation_not_found"), operation, desc, cause)

val denyListedErrors = arrayOf("Deserialization error", "Inaccessible host", "UnknownHost")
fun createUserFacingErrorMessage(message: String?): String? =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ import software.aws.toolkits.jetbrains.services.amazonq.RepoSizeError
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindowFactory
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CodeIterationLimitError
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CodeIterationLimitException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.DEFAULT_RETRY_LIMIT
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.InboundAppMessagesHandler
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ModifySourceFolderErrorReason
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.MonthlyConversationLimitError
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.UploadURLExpired
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ZipFileError
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ZipFileCorruptedException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.createUserFacingErrorMessage
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.denyListedErrors
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.FeatureDevMessageType
Expand Down Expand Up @@ -433,7 +433,7 @@ class FeatureDevController(
),
)
}
is ZipFileError -> {
is ZipFileCorruptedException -> {
messenger.sendError(
tabId = tabId,
errMessage = err.message,
Expand All @@ -451,15 +451,7 @@ class FeatureDevController(
messageType = FeatureDevMessageType.Answer,
canBeVoted = true
)
is FeatureDevException -> {
messenger.sendError(
tabId = tabId,
errMessage = err.message,
retries = retriesRemaining(session),
conversationId = session?.conversationIdUnsafe
)
}
is CodeIterationLimitError -> {
is CodeIterationLimitException -> {
messenger.sendError(
tabId = tabId,
errMessage = err.message,
Expand All @@ -479,24 +471,36 @@ class FeatureDevController(
)
}
else -> {
var msg = createUserFacingErrorMessage("$FEATURE_NAME request failed: ${err.message ?: err.cause?.message}")
val isDenyListedError = denyListedErrors.any { msg?.contains(it) ?: false }
val defaultMessage: String = when (session?.sessionState?.phase) {
SessionStatePhase.CODEGEN -> {
if (isDenyListedError || retriesRemaining(session) > 0) {
message("amazonqFeatureDev.code_generation.error_message")
} else {
message("amazonqFeatureDev.code_generation.no_retries.error_message")
when (err) {
is FeatureDevException -> {
messenger.sendError(
tabId = tabId,
errMessage = err.message,
retries = retriesRemaining(session),
conversationId = session?.conversationIdUnsafe
)
}
else -> {
val msg = createUserFacingErrorMessage("$FEATURE_NAME request failed: ${err.message ?: err.cause?.message}")
val isDenyListedError = denyListedErrors.any { msg?.contains(it) ?: false }
val defaultMessage: String = when (session?.sessionState?.phase) {
SessionStatePhase.CODEGEN -> {
if (isDenyListedError || retriesRemaining(session) > 0) {
message("amazonqFeatureDev.code_generation.error_message")
} else {
message("amazonqFeatureDev.code_generation.no_retries.error_message")
}
}
else -> message("amazonqFeatureDev.error_text")
}
messenger.sendError(
tabId = tabId,
errMessage = defaultMessage,
retries = retriesRemaining(session),
conversationId = session?.conversationIdUnsafe
)
}
else -> message("amazonqFeatureDev.error_text")
}
messenger.sendError(
tabId = tabId,
errMessage = defaultMessage,
retries = retriesRemaining(session),
conversationId = session?.conversationIdUnsafe
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import software.amazon.awssdk.services.codewhispererruntime.model.CodeGeneration
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CodeGenerationException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.EmptyPatchException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.codeGenerationFailedError
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.featureDevServiceError
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FeatureDevOperation
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.GuardrailsException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.PromptRefusalException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ThrottlingException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAnswerPart
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendUpdatePlaceholder
import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
Expand All @@ -37,6 +42,7 @@ class CodeGenerationState(
val startTime = System.currentTimeMillis()
var result: Result = Result.Succeeded
var failureReason: String? = null
var failureReasonDesc: String? = null
var codeGenerationWorkflowStatus: CodeGenerationWorkflowStatus = CodeGenerationWorkflowStatus.COMPLETE
var numberOfReferencesGenerated: Int? = null
var numberOfFilesGenerated: Int? = null
Expand Down Expand Up @@ -86,6 +92,10 @@ class CodeGenerationState(
logger.warn(e) { "$FEATURE_NAME: Code generation failed: ${e.message}" }
result = Result.Failed
failureReason = e.javaClass.simpleName
if (e is FeatureDevException) {
failureReason = e.reason()
failureReasonDesc = e.reasonDesc()
}
codeGenerationWorkflowStatus = CodeGenerationWorkflowStatus.FAILED

throw e
Expand All @@ -100,6 +110,7 @@ class CodeGenerationState(
amazonqRepositorySize = repositorySize,
result = result,
reason = failureReason,
reasonDesc = failureReasonDesc,
duration = (System.currentTimeMillis() - startTime).toDouble(),
credentialStartUrl = getStartUrl(config.featureDevService.project)
)
Expand Down Expand Up @@ -149,20 +160,20 @@ private suspend fun CodeGenerationState.generateCode(codeGenerationId: String, m
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
"Guardrails"
),
-> featureDevServiceError(message("amazonqFeatureDev.exception.guardrails"))
-> throw GuardrailsException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Failed guardrails")
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
"PromptRefusal"
),
-> featureDevServiceError(message("amazonqFeatureDev.exception.prompt_refusal"))
-> throw PromptRefusalException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Prompt refusal")
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
"EmptyPatch"
),
-> featureDevServiceError(message("amazonqFeatureDev.exception.guardrails"))
-> throw EmptyPatchException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Empty patch")
codeGenerationResultState.codeGenerationStatusDetail()?.contains(
"Throttling"
),
-> featureDevServiceError(message("amazonqFeatureDev.exception.throttling"))
else -> codeGenerationFailedError()
-> throw ThrottlingException(operation = FeatureDevOperation.GenerateCode.toString(), desc = "Request throttled")
else -> throw CodeGenerationException(operation = FeatureDevOperation.GenerateCode.toString(), desc = null)
}
}
else -> error("Unknown status: ${codeGenerationResultState.codeGenerationStatus().status()}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import com.intellij.openapi.vfs.VfsUtil
import software.aws.toolkits.jetbrains.services.amazonq.FeatureDevSessionContext
import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CODE_GENERATION_RETRY_LIMIT
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.ConversationIdNotFoundException
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.FEATURE_NAME
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.MAX_PROJECT_SIZE_BYTES
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.clients.FeatureDevClient
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.conversationIdNotFound
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAsyncEventProgress
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.FeatureDevService
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.resolveAndCreateOrUpdateFile
Expand Down Expand Up @@ -138,7 +138,7 @@ class Session(val tabID: String, val project: Project) {
val conversationId: String
get() {
if (_conversationId == null) {
conversationIdNotFound()
throw ConversationIdNotFoundException(operation = "Session", desc = "Conversation ID not found")
} else {
return _conversationId as String
}
Expand Down
Loading