override suspend fun interact()

in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/CodeGenerationState.kt [51:182]


    override suspend fun interact(action: SessionStateAction): SessionStateInteraction<SessionState> {
        val startTime = System.currentTimeMillis()
        var result: MetricResult = MetricResult.Succeeded
        var failureReason: String? = null
        var failureReasonDesc: String? = null
        var codeGenerationWorkflowStatus: CodeGenerationWorkflowStatus = CodeGenerationWorkflowStatus.COMPLETE
        var numberOfReferencesGenerated: Int? = null
        var numberOfFilesGenerated: Int? = null
        try {
            val codeGenerationId = UUID.randomUUID()

            val response =
                config.featureDevService.startTaskAssistCodeGeneration(
                    conversationId = config.conversationId,
                    uploadId = uploadId,
                    message = action.msg,
                    codeGenerationId = codeGenerationId.toString(),
                    currentCodeGenerationId = currentCodeGenerationId.toString(),
                )

            if (action.token?.token?.isCancellationRequested() != true) {
                this.currentCodeGenerationId = codeGenerationId.toString()
            }

            messenger.sendAnswerPart(
                tabId = tabID,
                message = message("amazonqFeatureDev.code_generation.generating_code"),
            )
            messenger.sendUpdatePlaceholder(
                tabId = tabID,
                newPlaceholder = message("amazonqFeatureDev.code_generation.generating_code"),
            )
            val codeGenerationResult = generateCode(codeGenerationId = response.codeGenerationId(), messenger = messenger, token = action.token)
            numberOfReferencesGenerated = codeGenerationResult.references.size
            numberOfFilesGenerated = codeGenerationResult.newFiles.size
            codeGenerationRemainingIterationCount = codeGenerationResult.codeGenerationRemainingIterationCount
            codeGenerationTotalIterationCount = codeGenerationResult.codeGenerationTotalIterationCount
            currentIteration =
                if (codeGenerationRemainingIterationCount != null && codeGenerationTotalIterationCount != null) {
                    codeGenerationTotalIterationCount?.let { total -> codeGenerationRemainingIterationCount?.let { remaining -> total - remaining } }
                } else {
                    currentIteration?.plus(1)
                }

            runCatching {
                var insertedLines = 0
                var insertedCharacters = 0
                codeGenerationResult.newFiles.forEach { file ->
                    // FIXME: Ideally, the before content should be read from the uploaded context instead of from disk, to avoid drift
                    val before = config.repoContext.addressableRoot
                        .toNioPath()
                        .resolve(file.zipFilePath)
                        .toFile()
                        .let { f ->
                            if (f.exists() && f.canRead()) {
                                readFileToString(f)
                            } else {
                                ""
                            }
                        }

                    val changeIdentifier = getChangeIdentifier(file.zipFilePath, before, file.fileContent)

                    if (!diffMetricsProcessed.generated.contains(changeIdentifier)) {
                        val diffMetrics = getDiffMetrics(before, file.fileContent)
                        insertedLines += diffMetrics.insertedLines
                        insertedCharacters += diffMetrics.insertedCharacters
                        diffMetricsProcessed.generated.add(changeIdentifier)
                    }
                }
                if (insertedLines > 0) {
                    config.featureDevService.sendFeatureDevCodeGenerationEvent(
                        conversationId = config.conversationId,
                        linesOfCodeGenerated = insertedLines,
                        charactersOfCodeGenerated = insertedCharacters,
                    )
                }
            }.onFailure { /* Noop on diff telemetry failure */ }

            val nextState =
                PrepareCodeGenerationState(
                    tabID = tabID,
                    approach = approach,
                    config = config,
                    filePaths = codeGenerationResult.newFiles,
                    deletedFiles = codeGenerationResult.deletedFiles,
                    references = codeGenerationResult.references,
                    currentIteration = currentIteration,
                    uploadId = uploadId,
                    messenger = messenger,
                    codeGenerationRemainingIterationCount = codeGenerationRemainingIterationCount,
                    codeGenerationTotalIterationCount = codeGenerationTotalIterationCount,
                    token = this.token,
                    diffMetricsProcessed = diffMetricsProcessed,
                )

            // It is not needed to interact right away with the PrepareCodeGeneration.
            // returns therefore a SessionStateInteraction object to be handled by the controller.
            return SessionStateInteraction(
                nextState = nextState,
                interaction = Interaction(content = "", interactionSucceeded = true),
            )
        } catch (e: Exception) {
            logger.warn(e) { "$FEATURE_NAME: Code generation failed: ${e.message}" }
            result = MetricResult.Failed
            failureReason = e.javaClass.simpleName
            if (e is FeatureDevException) {
                failureReason = e.reason()
                failureReasonDesc = e.reasonDesc()
            }
            codeGenerationWorkflowStatus = CodeGenerationWorkflowStatus.FAILED

            throw e
        } finally {
            currentIteration?.let {
                AmazonqTelemetry.codeGenerationInvoke(
                    amazonqConversationId = config.conversationId,
                    amazonqCodeGenerationResult = codeGenerationWorkflowStatus.toString(),
                    amazonqGenerateCodeIteration = it.toDouble(),
                    amazonqNumberOfReferences = numberOfReferencesGenerated?.toDouble(),
                    amazonqGenerateCodeResponseLatency = (System.currentTimeMillis() - startTime).toDouble(),
                    amazonqNumberOfFilesGenerated = numberOfFilesGenerated?.toDouble(),
                    amazonqRepositorySize = repositorySize,
                    result = result,
                    reason = failureReason,
                    reasonDesc = failureReasonDesc,
                    duration = (System.currentTimeMillis() - startTime).toDouble(),
                    credentialStartUrl = getStartUrl(config.featureDevService.project),
                )
            }
        }
    }