Skip to content

Commit fdcae23

Browse files
authored
fix(amazonq): Enable stop button of chat (#5671)
* stop response cancel * avoid get throw * code * endpoint * detekt * feedback * update * update * msg * fix ci
1 parent bee5206 commit fdcae23

File tree

6 files changed

+90
-5
lines changed

6 files changed

+90
-5
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
1818
import kotlinx.coroutines.flow.launchIn
1919
import kotlinx.coroutines.flow.merge
2020
import kotlinx.coroutines.flow.onEach
21-
import kotlinx.coroutines.future.await
2221
import kotlinx.coroutines.launch
2322
import org.cef.browser.CefBrowser
2423
import org.eclipse.lsp4j.Position
@@ -32,6 +31,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
3231
import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager
3332
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsServerCapabilitiesProvider
3433
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager
34+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager.Companion.convertToJsonToSendToChat
3535
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.getTextDocumentIdentifier
3636
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ButtonClickNotification
3737
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ButtonClickParams
@@ -61,6 +61,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatN
6161
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatParams
6262
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatPrompt
6363
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatReadyNotification
64+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatUiMessageParams
6465
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ConversationClickRequest
6566
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CopyCodeToClipboardNotification
6667
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CopyCodeToClipboardParams
@@ -90,9 +91,11 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.Promp
9091
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.PromptInputOptionChangeParams
9192
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.QuickChatActionRequest
9293
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_CHAT_COMMAND_PROMPT
94+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.STOP_CHAT_RESPONSE
9395
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendChatPromptRequest
9496
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SourceLinkClickNotification
9597
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SourceLinkClickParams
98+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.StopResponseMessage
9699
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TabBarActionParams
97100
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TabBarActionRequest
98101
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TabEventParams
@@ -102,6 +105,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.util.tabType
102105
import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.AmazonQTheme
103106
import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.ThemeBrowserAdapter
104107
import software.aws.toolkits.jetbrains.settings.MeetQSettings
108+
import software.aws.toolkits.resources.AwsCoreBundle
105109
import software.aws.toolkits.telemetry.MetricResult
106110
import software.aws.toolkits.telemetry.Telemetry
107111
import java.util.concurrent.CompletableFuture
@@ -245,6 +249,10 @@ class BrowserConnector(
245249
encryptionManager = this.encryptionManager
246250
encryptionManager?.encrypt(chatParams)?.let { EncryptedChatParams(it, partialResultToken) }?.let { server.sendChatPrompt(it) }
247251
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running")))
252+
253+
// We assume there is only one outgoing request per tab because the input is
254+
// blocked when there is an outgoing request
255+
chatCommunicationManager.setInflightRequestForTab(tabId, result)
248256
showResult(result, partialResultToken, tabId, encryptionManager, browser)
249257
}
250258
CHAT_QUICK_ACTION -> {
@@ -262,6 +270,10 @@ class BrowserConnector(
262270
}
263271
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running")))
264272

273+
// We assume there is only one outgoing request per tab because the input is
274+
// blocked when there is an outgoing request
275+
chatCommunicationManager.setInflightRequestForTab(tabId, result)
276+
265277
showResult(result, partialResultToken, tabId, encryptionManager, browser)
266278
}
267279
CHAT_LIST_CONVERSATIONS -> {
@@ -318,6 +330,7 @@ class BrowserConnector(
318330
CHAT_TAB_REMOVE -> {
319331
handleChatNotification<TabEventRequest, TabEventParams>(node) { server, params ->
320332
chatCommunicationManager.removePartialChatMessage(params.tabId)
333+
cancelInflightRequests(params.tabId)
321334
server.tabRemove(params)
322335
}
323336
}
@@ -414,6 +427,30 @@ class BrowserConnector(
414427
server.createPrompt(params)
415428
}
416429
}
430+
STOP_CHAT_RESPONSE -> {
431+
val stopResponseRequest = serializer.deserializeChatMessages<StopResponseMessage>(node)
432+
if (!chatCommunicationManager.hasInflightRequest(stopResponseRequest.params.tabId)) {
433+
return
434+
}
435+
cancelInflightRequests(stopResponseRequest.params.tabId)
436+
chatCommunicationManager.removePartialChatMessage(stopResponseRequest.params.tabId)
437+
438+
val paramsJson = Gson().toJson(
439+
// https://github.com/aws/language-servers/blob/1c0d88806087125b6fc561f610cc15e98127c6bf/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts#L403
440+
ChatUiMessageParams(
441+
title = AwsCoreBundle.message("amazonqChat.stopChatResponse"),
442+
body = ""
443+
)
444+
)
445+
446+
val uiMessage = convertToJsonToSendToChat(
447+
command = SEND_CHAT_COMMAND_PROMPT,
448+
tabId = stopResponseRequest.params.tabId,
449+
params = paramsJson.toString(),
450+
isPartialResult = false
451+
)
452+
browser.postChat(uiMessage)
453+
}
417454
}
418455
}
419456

@@ -433,6 +470,14 @@ class BrowserConnector(
433470
isPartialResult = false
434471
)
435472
browser.postChat(messageToChat)
473+
chatCommunicationManager.removeInflightRequestForTab(tabId)
474+
}
475+
}
476+
477+
private fun cancelInflightRequests(tabId: String) {
478+
chatCommunicationManager.getInflightRequestForTab(tabId)?.let { request ->
479+
request.cancel(true)
480+
chatCommunicationManager.removeInflightRequestForTab(tabId)
436481
}
437482
}
438483

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,21 @@ import java.util.concurrent.ConcurrentHashMap
2020
@Service(Service.Level.PROJECT)
2121
class ChatCommunicationManager {
2222
private val chatPartialResultMap = ConcurrentHashMap<String, String>()
23-
private fun getPartialChatMessage(partialResultToken: String): String =
24-
chatPartialResultMap.getValue(partialResultToken)
23+
private fun getPartialChatMessage(partialResultToken: String): String? =
24+
chatPartialResultMap.getOrDefault(partialResultToken, null)
25+
26+
private val inflightRequestByTabId = ConcurrentHashMap<String, CompletableFuture<String>>()
27+
28+
fun setInflightRequestForTab(tabId: String, result: CompletableFuture<String>) {
29+
inflightRequestByTabId[tabId] = result
30+
}
31+
fun removeInflightRequestForTab(tabId: String) {
32+
inflightRequestByTabId.remove(tabId)
33+
}
34+
35+
fun getInflightRequestForTab(tabId: String): CompletableFuture<String>? = inflightRequestByTabId[tabId]
36+
37+
fun hasInflightRequest(tabId: String): Boolean = inflightRequestByTabId.containsKey(tabId)
2538

2639
fun addPartialChatMessage(tabId: String): String {
2740
val partialResultToken: String = UUID.randomUUID().toString()
@@ -35,7 +48,7 @@ class ChatCommunicationManager {
3548
fun handlePartialResultProgressNotification(project: Project, params: ProgressParams) {
3649
val token = ProgressNotificationUtils.getToken(params)
3750
val tabId = getPartialChatMessage(token)
38-
if (tabId == null || tabId.isEmpty()) {
51+
if (tabId.isNullOrEmpty()) {
3952
return
4053
}
4154
if (params.value.isLeft || params.value.right == null) {
@@ -47,7 +60,6 @@ class ChatCommunicationManager {
4760
val encryptedPartialChatResult = getObject(params, String::class.java)
4861
if (encryptedPartialChatResult != null) {
4962
val partialChatResult = AmazonQLspService.getInstance(project).encryptionManager.decrypt(encryptedPartialChatResult)
50-
5163
val uiMessage = convertToJsonToSendToChat(
5264
command = SEND_CHAT_COMMAND_PROMPT,
5365
tabId = tabId,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat
5+
import java.util.UUID
6+
7+
data class ChatUiMessageParams(
8+
val title: String,
9+
val additionalMessages: List<String> = emptyList(),
10+
val messageId: String = UUID.randomUUID().toString(),
11+
val buttons: List<String> = emptyList(),
12+
val codeReference: List<String> = emptyList(),
13+
val body: String = "",
14+
)

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ const val CHAT_SEND_UPDATE = "aws/chat/sendChatUpdate"
3131
const val CHAT_CREATE_PROMPT = "aws/chat/createPrompt"
3232
const val SHOW_SAVE_FILE_DIALOG_REQUEST_METHOD = "aws/showSaveFileDialog"
3333
const val GET_SERIALIZED_CHAT_REQUEST_METHOD = "aws/chat/getSerializedChat"
34+
const val STOP_CHAT_RESPONSE = "stopChatResponse"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat
5+
6+
data class StopResponseMessage(
7+
val params: StopResponseParams,
8+
)
9+
10+
data class StopResponseParams(
11+
val tabId: String,
12+
)

plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ action.aws.toolkit.toolwindow.newConnection.text=Add Another Connection...
3838
action.dynamic.open.text=Open Resource...
3939
action.q.openchat.text=Open Chat Panel
4040
amazonqChat.project_context.index_in_progress=By the way, I'm still indexing this project for full context from your workspace. I may have a better response in a few minutes when it's complete if you'd like to try again then.
41+
amazonqChat.stopChatResponse=You stopped your current work, please provide additional examples or ask another question.
4142
amazonqDoc.answer.codeResult=You can accept the changes to your files, or describe any additional changes you'd like me to make.
4243
amazonqDoc.answer.readmeCreated=I've created a README for your code.
4344
amazonqDoc.answer.readmeUpdated=I've updated your README.

0 commit comments

Comments
 (0)