From ae6c87e5cb6fa944b95cd14ae49f97c599a63032 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Mon, 16 Jun 2025 13:22:28 -0700 Subject: [PATCH] fix(amazonq): delete AwsServerCapabilitiesProvider --- .../services/amazonq/webview/Browser.kt | 9 ++-- .../amazonq/webview/BrowserConnector.kt | 10 +++-- .../services/amazonq/lsp/AmazonQLspService.kt | 43 ++++++++----------- .../flareChat/AmazonQLspTypeAdapterFactory.kt | 11 +---- .../lsp/model/ExtendedClientMetadata.kt | 12 +++--- .../aws/AwsServerCapabilities.kt} | 37 +--------------- .../amazonq/lsp/model/aws/chat/ChatPrompt.kt | 2 +- .../lsp/model/aws/chat/SendChatPrompt.kt | 2 +- 8 files changed, 40 insertions(+), 86 deletions(-) rename plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/{flareChat/AwsServerCapabilitiesProvider.kt => model/aws/AwsServerCapabilities.kt} (51%) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt index fc88293038e..d4a03928148 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt @@ -9,9 +9,10 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.ui.jcef.JBCefJSQuery +import kotlinx.coroutines.flow.first import org.cef.CefApp import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService -import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsServerCapabilitiesProvider +import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile import software.aws.toolkits.jetbrains.services.amazonq.util.HighlightCommand @@ -114,7 +115,7 @@ class Browser(parent: Disposable, private val webUri: URI, val project: Project) ): String { val postMessageToJavaJsCode = receiveMessageQuery.inject("JSON.stringify(message)") val connectorAdapterPath = "http://mynah/js/connectorAdapter.js" - generateQuickActionConfig() + // https://github.com/highlightjs/highlight.js/issues/1387 // language=HTML val jsScripts = """ @@ -257,10 +258,6 @@ class Browser(parent: Disposable, private val webUri: URI, val project: Project) activeProfile } - private fun generateQuickActionConfig() = AwsServerCapabilitiesProvider.getInstance(project).getChatOptions().quickActions.quickActionsCommandGroups - .let { OBJECT_MAPPER.writeValueAsString(it) } - ?: "[]" - companion object { private const val MAX_ONBOARDING_PAGE_COUNT = 3 private val OBJECT_MAPPER = jacksonObjectMapper() diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt index f030d69aa3d..2376899a6c0 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach @@ -40,7 +41,6 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.JsonRpcMethod import software.aws.toolkits.jetbrains.services.amazonq.lsp.JsonRpcNotification import software.aws.toolkits.jetbrains.services.amazonq.lsp.JsonRpcRequest import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager -import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsServerCapabilitiesProvider import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatAsyncResultManager import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage @@ -535,15 +535,19 @@ class BrowserConnector( browser.postChat(cancelMessage) } - private fun updateQuickActionsInBrowser(browser: Browser) { + private suspend fun updateQuickActionsInBrowser(browser: Browser) { val isFeatureDevAvailable = isFeatureDevAvailable(project) val isCodeTransformAvailable = isCodeTransformAvailable(project) val isDocAvailable = isDocAvailable(project) val isCodeScanAvailable = isCodeScanAvailable(project) val isCodeTestAvailable = isCodeTestAvailable(project) + val serverCapabilities = AmazonQLspService.getInstance(project).instanceFlow.first().initializeResult.await().awsServerCapabilities + + // language=JavaScript val script = """ try { + // hack to create the list of actions across all tab types const tempConnector = connectorAdapter.initiateAdapter( false, true, // the two values are not used here, needed for constructor @@ -556,7 +560,7 @@ class BrowserConnector( ); const commands = tempConnector.initialQuickActions?.slice(0, 2) || []; - const options = ${Gson().toJson(AwsServerCapabilitiesProvider.getInstance(project).getChatOptions())}; + const options = ${Gson().toJson(serverCapabilities.chatOptions)}; options.quickActions.quickActionsCommandGroups = [ ...commands, ...options.quickActions.quickActionsCommandGroups diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt index 5b3b4dd107b..bf30c1803b2 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt @@ -34,6 +34,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.map @@ -59,7 +60,6 @@ import org.eclipse.lsp4j.jsonrpc.Launcher import org.eclipse.lsp4j.jsonrpc.MessageConsumer import org.eclipse.lsp4j.jsonrpc.RemoteEndpoint import org.eclipse.lsp4j.jsonrpc.json.JsonRpcMethod -import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage import org.eclipse.lsp4j.launch.LSPLauncher import org.slf4j.event.Level import software.aws.toolkits.core.utils.debug @@ -74,7 +74,6 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.Default import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AmazonQLspTypeAdapterFactory import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsExtendedInitializeResult -import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsServerCapabilitiesProvider import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.createExtendedClientMetadata import software.aws.toolkits.jetbrains.services.amazonq.lsp.textdocument.TextDocumentServiceHandler import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders @@ -138,11 +137,9 @@ internal class LSPProcessListener : ProcessListener { @Service(Service.Level.PROJECT) class AmazonQLspService(private val project: Project, private val cs: CoroutineScope) : Disposable { private val _flowInstance = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) - val instanceFlow = _flowInstance.asSharedFlow().map { it.languageServer } + val instanceFlow: Flow = _flowInstance.asSharedFlow() private var instance: Deferred - val capabilities - get() = instance.getCompleted().initializeResult.getCompleted().capabilities val encryptionManager get() = instance.getCompleted().encryptionManager @@ -312,12 +309,17 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS } } -private class AmazonQServerInstance(private val project: Project, private val cs: CoroutineScope) : Disposable { +interface AmazonQServerInstanceFacade { + val languageServer: AmazonQLanguageServer + val initializeResult: Deferred +} + +private class AmazonQServerInstance(private val project: Project, private val cs: CoroutineScope) : Disposable, AmazonQServerInstanceFacade { val encryptionManager = JwtEncryptionManager() private val launcher: Launcher - val languageServer: AmazonQLanguageServer + override val languageServer: AmazonQLanguageServer get() = launcher.remoteProxy val rawEndpoint: RemoteEndpoint @@ -326,7 +328,7 @@ private class AmazonQServerInstance(private val project: Project, private val cs @Suppress("ForbiddenVoid") val launcherFuture: Future private val launcherHandler: KillableProcessHandler - val initializeResult: Deferred + override val initializeResult: Deferred private fun createClientCapabilities(): ClientCapabilities = ClientCapabilities().apply { @@ -479,10 +481,12 @@ private class AmazonQServerInstance(private val project: Project, private val cs } .wrapMessages { consumer -> MessageConsumer { message -> - if (message is ResponseMessage && message.result is AwsExtendedInitializeResult) { - val result = message.result as AwsExtendedInitializeResult - AwsServerCapabilitiesProvider.getInstance(project).setAwsServerCapabilities(result.getAwsServerCapabilities()) - } + // logging + val traceLogger = LOG.atLevel(if (isDeveloperMode()) Level.INFO else Level.DEBUG) + val direction = if (consumer is RemoteEndpoint) "Sent" else "Received" + traceLogger.log { "$direction: $message" } + + // required consumer?.consume(message) } } @@ -495,18 +499,7 @@ private class AmazonQServerInstance(private val project: Project, private val cs // otherwise Gson treats all numbers as double which causes deser issues it.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) it.registerTypeAdapterFactory(AmazonQLspTypeAdapterFactory()) - }.traceMessages( - PrintWriter( - object : StringWriter() { - private val traceLogger = LOG.atLevel(if (isDeveloperMode()) Level.INFO else Level.DEBUG) - - override fun flush() { - traceLogger.log { buffer.toString() } - buffer.setLength(0) - } - } - ) - ) + } .setInput(inputWrapper.inputStream) .setOutput(launcherHandler.process.outputStream) .create() @@ -531,7 +524,7 @@ private class AmazonQServerInstance(private val project: Project, private val cs } languageServer.initialized(InitializedParams()) - initializeResult + initializeResult as AwsExtendedInitializeResult } // invokeOnCompletion results in weird lock/timeout error diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt index 89d76c205c3..c26ca85d37a 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt @@ -10,6 +10,7 @@ import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter import org.eclipse.lsp4j.InitializeResult +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.AwsServerCapabilities import java.io.IOException class AmazonQLspTypeAdapterFactory : TypeAdapterFactory { @@ -32,12 +33,4 @@ class AmazonQLspTypeAdapterFactory : TypeAdapterFactory { } } -class AwsExtendedInitializeResult(awsServerCapabilities: AwsServerCapabilities? = null) : InitializeResult() { - private var awsServerCapabilities: AwsServerCapabilities? = null - - fun getAwsServerCapabilities(): AwsServerCapabilities? = awsServerCapabilities - - fun setAwsServerCapabilities(awsServerCapabilities: AwsServerCapabilities?) { - this.awsServerCapabilities = awsServerCapabilities - } -} +data class AwsExtendedInitializeResult(val awsServerCapabilities: AwsServerCapabilities) : InitializeResult() diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt index db49b158c41..74e8cf38343 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/ExtendedClientMetadata.kt @@ -17,16 +17,16 @@ data class AwsMetadata( ) data class AwsClientCapabilities( - val q: DeveloperProfiles, - val window: WindowSettings, + val q: QCapabilities, + val window: WindowCapabilities, ) -data class DeveloperProfiles( +data class QCapabilities( val developerProfiles: Boolean, val mcp: Boolean, ) -data class WindowSettings( +data class WindowCapabilities( val showSaveFileDialog: Boolean, ) @@ -60,11 +60,11 @@ fun createExtendedClientMetadata(project: Project): ExtendedClientMetadata { name = metadata.parentProduct ), awsClientCapabilities = AwsClientCapabilities( - q = DeveloperProfiles( + q = QCapabilities( developerProfiles = true, mcp = true ), - window = WindowSettings( + window = WindowCapabilities( showSaveFileDialog = true ) ), diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AwsServerCapabilitiesProvider.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/AwsServerCapabilities.kt similarity index 51% rename from plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AwsServerCapabilitiesProvider.kt rename to plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/AwsServerCapabilities.kt index 4055b8381a5..faf3639109b 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AwsServerCapabilitiesProvider.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/AwsServerCapabilities.kt @@ -1,44 +1,10 @@ // Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat +package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws -import com.intellij.openapi.components.Service -import com.intellij.openapi.components.service -import com.intellij.openapi.project.Project import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.IconType -@Service(Service.Level.PROJECT) -class AwsServerCapabilitiesProvider { - private var serverCapabilities: AwsServerCapabilities? = null - - fun setAwsServerCapabilities(serverCapabilities: AwsServerCapabilities?) { - this.serverCapabilities = serverCapabilities - } - - fun getChatOptions() = serverCapabilities?.chatOptions ?: DEFAULT_CHAT_OPTIONS - - companion object { - fun getInstance(project: Project) = project.service() - - private val DEFAULT_CHAT_OPTIONS: ChatOptions = ChatOptions( - QuickActions( - listOf( - QuickActionsCommandGroups( - listOf( - QuickActionCommand("/help", "Learn more about Amazon Q then"), - QuickActionCommand("/clear", "Clear this session") - ) - ) - ) - ), - history = true, - export = true, - mcpServers = true - ) - } -} - data class AwsServerCapabilities( val chatOptions: ChatOptions, ) @@ -48,6 +14,7 @@ data class ChatOptions( val history: Boolean, val export: Boolean, val mcpServers: Boolean, + val modelSelection: Boolean, ) data class QuickActions( diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt index 80e39191891..0d22f7bee64 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt @@ -4,7 +4,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat import com.fasterxml.jackson.annotation.JsonProperty -import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ContextCommand +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.ContextCommand data class ChatPrompt( val prompt: String, diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/SendChatPrompt.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/SendChatPrompt.kt index 136f9adc336..8a20f696a97 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/SendChatPrompt.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/SendChatPrompt.kt @@ -4,7 +4,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat import org.eclipse.lsp4j.TextDocumentIdentifier -import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ContextCommand +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.ContextCommand data class ChatParams( val tabId: String,