fun createConversation()

in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/common/util/AmazonQCodeGenService.kt [41:248]


    fun createConversation(): String {
        val startTime = System.currentTimeMillis()
        var failureReason: String? = null
        var failureReasonDesc: String? = null
        var result: MetricResult = MetricResult.Succeeded
        var conversationId: String? = null
        try {
            logger.debug { "Executing createTaskAssistConversation" }
            val taskAssistConversationResult = proxyClient.createTaskAssistConversation()
            conversationId = taskAssistConversationResult.conversationId()
            logger.debug {
                "$FEATURE_NAME: Created conversation: {conversationId: $conversationId, requestId: ${
                    taskAssistConversationResult.responseMetadata().requestId()
                }"
            }

            return conversationId
        } catch (e: Exception) {
            logger.warn(e) { "$FEATURE_NAME: Failed to start conversation: ${e.message}" }
            result = MetricResult.Failed
            failureReason = e.javaClass.simpleName
            if (e is FeatureDevException) {
                failureReason = e.reason()
                failureReasonDesc = e.reasonDesc()
            }
            var errMssg = e.message
            if (e is CodeWhispererRuntimeException) {
                errMssg = e.awsErrorDetails().errorMessage()
                logger.warn(e) { "Start conversation failed for request: ${e.requestId()}" }

                if (e is ServiceQuotaExceededException) {
                    throw MonthlyConversationLimitError(errMssg, operation = FeatureDevOperation.CreateConversation.toString(), desc = null, cause = e.cause)
                }
                throw ApiException.of(e.statusCode(), errMssg, operation = FeatureDevOperation.CreateConversation.toString(), desc = null, e.cause)
            }
            throw ServiceException(
                errMssg ?: "CreateTaskAssistConversation failed",
                operation = FeatureDevOperation.CreateConversation.toString(),
                desc = null,
                e.cause
            )
        } finally {
            AmazonqTelemetry.startConversationInvoke(
                amazonqConversationId = conversationId,
                result = result,
                reason = failureReason,
                reasonDesc = failureReasonDesc,
                duration = (System.currentTimeMillis() - startTime).toDouble(),
                credentialStartUrl = getStartUrl(project = this.project),
            )
        }
    }

    fun createUploadUrl(conversationId: String, contentChecksumSha256: String, contentLength: Long, uploadId: String, featureName: String? = null):
        CreateUploadUrlResponse {
        try {
            logger.debug { "Executing createUploadUrl with conversationId $conversationId" }
            val uploadUrlResponse = proxyClient.createTaskAssistUploadUrl(
                conversationId,
                contentChecksumSha256,
                contentLength,
            )
            logger.debug {
                "$FEATURE_NAME: Created upload url: {uploadId: $uploadId, requestId: ${uploadUrlResponse.responseMetadata().requestId()}}"
            }
            return uploadUrlResponse
        } catch (e: Exception) {
            logger.warn(e) { "$FEATURE_NAME: Failed to generate presigned url: ${e.message}" }

            var errMssg = e.message
            if (e is CodeWhispererRuntimeException) {
                errMssg = e.awsErrorDetails().errorMessage()
                logger.warn(e) { "Create UploadUrl failed for request: ${e.requestId()}" }

                if (e is ValidationException && e.message?.contains("Invalid contentLength") == true) {
                    if (featureName?.equals("docGeneration") == true) {
                        throw DocContentLengthException(operation = FeatureDevOperation.CreateUploadUrl.toString(), desc = null, cause = e.cause)
                    }
                    throw ContentLengthException(operation = FeatureDevOperation.CreateUploadUrl.toString(), desc = null, cause = e.cause)
                }

                throw ApiException.of(e.statusCode(), errMssg, operation = FeatureDevOperation.CreateUploadUrl.toString(), desc = null, e.cause)
            }
            throw ServiceException(errMssg ?: "CreateUploadUrl failed", operation = FeatureDevOperation.CreateUploadUrl.toString(), desc = null, e.cause)
        }
    }

    open fun startTaskAssistCodeGeneration(conversationId: String, uploadId: String, message: String, intent: Intent):
        StartTaskAssistCodeGenerationResponse {
        try {
            logger.debug { "Executing startTaskAssistCodeGeneration with conversationId: $conversationId , uploadId: $uploadId" }
            val startCodeGenerationResponse = proxyClient.startTaskAssistCodeGeneration(
                conversationId,
                uploadId,
                message,
                intent
            )

            logger.debug { "$FEATURE_NAME: Started code generation with requestId: ${startCodeGenerationResponse.responseMetadata().requestId()}" }
            return startCodeGenerationResponse
        } catch (e: Exception) {
            logger.warn(e) { "$FEATURE_NAME: Failed to execute startTaskAssistCodeGeneration ${e.message}" }

            var errMssg = e.message
            if (e is CodeWhispererRuntimeException) {
                errMssg = e.awsErrorDetails().errorMessage()
                logger.warn(e) { "StartTaskAssistCodeGeneration failed for request: ${e.requestId()}" }

                // API Front-end will throw Throttling if conversation limit is reached. API Front-end monitors StartCodeGeneration for throttling
                if (e is software.amazon.awssdk.services.codewhispererruntime.model.ThrottlingException &&
                    e.message?.contains("StartTaskAssistCodeGeneration reached for this month.") == true
                ) {
                    throw MonthlyConversationLimitError(errMssg, operation = FeatureDevOperation.StartTaskAssistCodeGeneration.toString(), desc = null, e.cause)
                }

                if (e is ServiceQuotaExceededException || (
                        e is ThrottlingException && e.message?.contains(
                            "limit for number of iterations on a code generation"
                        ) == true
                        )
                ) {
                    throw CodeIterationLimitException(operation = FeatureDevOperation.StartTaskAssistCodeGeneration.toString(), desc = null, e.cause)
                } else if (e is ValidationException && e.message?.contains("repo size is exceeding the limits") == true) {
                    if (intent == Intent.DOC) {
                        throw DocContentLengthException(operation = FeatureDevOperation.CreateUploadUrl.toString(), desc = null, cause = e.cause)
                    }
                    throw ContentLengthException(operation = FeatureDevOperation.StartTaskAssistCodeGeneration.toString(), desc = null, cause = e.cause)
                } else if (e is ValidationException && e.message?.contains("zipped file is corrupted") == true) {
                    throw ZipFileCorruptedException(operation = FeatureDevOperation.StartTaskAssistCodeGeneration.toString(), desc = null, e.cause)
                }
                throw ApiException.of(e.statusCode(), errMssg, operation = FeatureDevOperation.StartTaskAssistCodeGeneration.toString(), desc = null, e.cause)
            }
            throw ServiceException(
                errMssg ?: "StartTaskAssistCodeGeneration failed",
                operation = FeatureDevOperation.StartTaskAssistCodeGeneration.toString(),
                desc = null,
                e.cause
            )
        }
    }

    fun getTaskAssistCodeGeneration(conversationId: String, codeGenerationId: String): GetTaskAssistCodeGenerationResponse {
        try {
            logger.debug { "Executing GetTaskAssistCodeGeneration with conversationId: $conversationId , codeGenerationId: $codeGenerationId" }
            val getCodeGenerationResponse = proxyClient.getTaskAssistCodeGeneration(conversationId, codeGenerationId)

            logger.debug {
                "$FEATURE_NAME: Received code generation status $getCodeGenerationResponse with requestId ${
                    getCodeGenerationResponse.responseMetadata()
                        .requestId()
                }"
            }
            return getCodeGenerationResponse
        } catch (e: Exception) {
            logger.warn(e) { "$FEATURE_NAME: Failed to execute GetTaskAssistCodeGeneration ${e.message}" }

            var errMssg = e.message
            if (e is CodeWhispererRuntimeException) {
                errMssg = e.awsErrorDetails().errorMessage()
                logger.warn(e) { "GetTaskAssistCodeGeneration failed for request:  ${e.requestId()}" }
                throw ApiException.of(e.statusCode(), errMssg, operation = FeatureDevOperation.GetTaskAssistCodeGeneration.toString(), desc = null, e.cause)
            }
            throw ServiceException(
                errMssg ?: "GetTaskAssistCodeGeneration failed",
                operation = FeatureDevOperation.GetTaskAssistCodeGeneration.toString(),
                desc = null,
                e.cause
            )
        }
    }

    suspend fun exportTaskAssistArchiveResult(conversationId: String): DocGenerationStreamResult {
        val exportResponse: MutableList<ByteArray>
        try {
            exportResponse = proxyClient.exportTaskAssistResultArchive(conversationId)
            logger.debug { "$FEATURE_NAME: Received export task assist result archive response" }
        } catch (e: Exception) {
            logger.warn(e) { "$FEATURE_NAME: Failed to export archive result: ${e.message}" }

            var errMssg = e.message
            if (e is CodeWhispererStreamingException) {
                errMssg = e.awsErrorDetails().errorMessage()
                logger.warn(e) { "ExportTaskAssistArchiveResult failed for request: ${e.requestId()}" }
            }
            throw ServiceException(
                errMssg ?: "ExportTaskAssistArchive failed",
                operation = FeatureDevOperation.ExportTaskAssistArchiveResult.toString(),
                desc = null,
                e.cause
            )
        }

        val parsedResult: ExportDocTaskAssistResultArchiveStreamResult
        try {
            val result = exportResponse.reduce { acc, next -> acc + next } // To map the result it is needed to combine the  full byte array
            parsedResult = jacksonObjectMapper().readValue(result)
        } catch (e: Exception) {
            logger.error(e) { "Failed to parse downloaded code results" }
            throw ExportParseException(operation = FeatureDevOperation.ExportTaskAssistArchiveResult.toString(), desc = null, e.cause)
        }

        return parsedResult.codeGenerationResult
    }

    companion object {
        private val logger = getLogger<AmazonQCodeGenerateClient>()
    }
}