Skip to content
Merged
Changes from 3 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 @@ -42,10 +42,18 @@
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.future.await
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import migration.software.aws.toolkits.jetbrains.services.codewhisperer.explorer.CodeWhispererExplorerActionManager
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException
import org.eclipse.lsp4j.jsonrpc.messages.Either
import software.amazon.awssdk.services.ssooidc.model.InvalidGrantException
import software.aws.toolkits.core.utils.debug
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.jetbrains.core.coroutines.getCoroutineBgContext
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProvider
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
import software.aws.toolkits.jetbrains.services.codewhisperer.importadder.CodeWhispererImportAdder
Expand All @@ -59,7 +67,9 @@
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService
import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererUtil
import software.aws.toolkits.jetbrains.utils.isQConnected
import software.aws.toolkits.jetbrains.utils.isQExpired
import software.aws.toolkits.resources.message
import software.aws.toolkits.telemetry.CodewhispererTriggerType
import java.awt.Dimension
Expand Down Expand Up @@ -391,8 +401,22 @@

override suspend fun getSuggestion(request: InlineCompletionRequest): InlineCompletionSuggestion {
val editor = request.editor
val document = editor.document
val project = editor.project ?: return InlineCompletionSuggestion.Empty

// try to refresh automatically if possible, otherwise ask user to login again
if (isQExpired(project)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this isQExpired should be very fast so that inline completion latency does not go up

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes this is just check on token timestamp

// consider changing to only running once a ~minute since this is relatively expensive
// say the connection is un-refreshable if refresh fails for 3 times
val shouldReauth = withContext(getCoroutineBgContext()) {
CodeWhispererUtil.promptReAuth(project)

Check warning on line 411 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L411

Added line #L411 was not covered by tests
}

if (shouldReauth) {
return InlineCompletionSuggestion.Empty

Check warning on line 415 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L415

Added line #L415 was not covered by tests
}
}

val document = editor.document

Check warning on line 419 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L419

Added line #L419 was not covered by tests
val handler = InlineCompletion.getHandlerOrNull(editor) ?: return InlineCompletionSuggestion.Empty
val session = InlineCompletionSession.getOrNull(editor) ?: return InlineCompletionSuggestion.Empty
val triggerSessionId = triggerSessionId++
Expand Down Expand Up @@ -443,31 +467,28 @@
}

try {
// Launch coroutine for background pagination progress
cs.launch {
var nextToken: Either<String, Int>? = null
do {
nextToken = startPaginationInBackground(
project,
editor,
triggerTypeInfo,
triggerSessionId,
nextToken,
sessionContext,
)
} while (nextToken != null && !nextToken.left.isNullOrEmpty())
var nextToken: Either<String, Int>? = null

Check warning on line 470 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L470

Added line #L470 was not covered by tests
do {
nextToken = startPaginationInBackground(
project,
editor,
triggerTypeInfo,
triggerSessionId,
nextToken,
sessionContext,

Check warning on line 478 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L472-L478

Added lines #L472 - L478 were not covered by tests
)
} while (nextToken != null && !nextToken.left.isNullOrEmpty())

// closing all channels since pagination for this session has finished
// closing all channels since pagination for this session has finished
logInline(triggerSessionId) {
"Pagination finished, closing all channels"

Check warning on line 484 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L483-L484

Added lines #L483 - L484 were not covered by tests
}
sessionContext.itemContexts.forEach {
it.channel.close()
}

Check warning on line 488 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L486-L488

Added lines #L486 - L488 were not covered by tests
if (session.context.isDisposed) {
logInline(triggerSessionId) {
"Pagination finished, closing all channels"
}
sessionContext.itemContexts.forEach {
it.channel.close()
}
if (session.context.isDisposed) {
logInline(triggerSessionId) {
"Current display session already disposed by a new trigger before pagination finishes, exiting"
}
"Current display session already disposed by a new trigger before pagination finishes, exiting"

Check warning on line 491 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L491

Added line #L491 was not covered by tests
}
}

Expand Down Expand Up @@ -572,6 +593,27 @@
logInline(triggerSessionId, e) {
"Error during pagination"
}
if (e is ResponseErrorException) {
// convoluted but lines up with "The bearer token included in the request is invalid"
// https://github.com/aws/language-servers/blob/1f3e93024eeb22186a34f0bd560f8d552f517300/server/aws-lsp-codewhisperer/src/language-server/chat/utils.ts#L22-L23
// error data is nullable
if (e.responseError.data?.toString()?.contains("E_AMAZON_Q_CONNECTION_EXPIRED") == true) {
// kill the session if the connection is expired
val connection = ToolkitConnectionManager
.getInstance(project)
.activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection

Check warning on line 604 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L603-L604

Added lines #L603 - L604 were not covered by tests
val tokenProvider = connection?.let { it.getConnectionSettings().tokenProvider.delegate as? BearerTokenProvider }
tokenProvider?.let {
// TODO: fragile
try {
it.refresh()
} catch (_: InvalidGrantException) {
it.invalidate()
CodeWhispererUtil.reconnectCodeWhisperer(project)
}

Check warning on line 613 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/QInlineCompletionProvider.kt#L608-L613

Added lines #L608 - L613 were not covered by tests
}
}
}
return null
}
}
Expand All @@ -591,6 +633,7 @@
val editor = request.editor
val project = editor.project ?: return false

// qExpired case handled in completion handler
if (!isQConnected(project)) return false
if (!CodeWhispererExplorerActionManager.getInstance().isAutoEnabled() && event.isManualCall()) return false
if (QRegionProfileManager.getInstance().hasValidConnectionButNoActiveProfile(project)) return false
Expand Down
Loading