-
Notifications
You must be signed in to change notification settings - Fork 251
feat(amazonq): Add quick actions to Flare chat #5561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
fe7738d
c09368c
9d037cf
e259071
bc6823b
b833b6c
fb9fd25
46b9a82
06dc03d
0f16cd4
21e4885
06e0966
8640432
930fff6
28a199e
98a43b5
a751f56
6b5abe1
1a3b0bd
274d1dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
import kotlinx.coroutines.flow.launchIn | ||
import kotlinx.coroutines.flow.merge | ||
import kotlinx.coroutines.flow.onEach | ||
import kotlinx.coroutines.future.await | ||
import kotlinx.coroutines.launch | ||
import org.cef.browser.CefBrowser | ||
import org.eclipse.lsp4j.Position | ||
|
@@ -27,10 +28,13 @@ | |
import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.getTextDocumentIdentifier | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_QUICK_ACTION | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatParams | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatPrompt | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CursorState | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedChatParams | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedQuickActionChatParams | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.QuickChatActionRequest | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_CHAT_COMMAND_PROMPT | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendChatPromptRequest | ||
import software.aws.toolkits.jetbrains.services.amazonq.util.command | ||
|
@@ -167,32 +171,59 @@ | |
) | ||
) | ||
|
||
val partialResultToken = chatCommunicationManager.addPartialChatMessage(requestFromUi.params.tabId) | ||
val chatParams = ChatParams( | ||
requestFromUi.params.tabId, | ||
chatPrompt, | ||
textDocumentIdentifier, | ||
cursorState | ||
) | ||
|
||
val tabId = requestFromUi.params.tabId | ||
val partialResultToken = chatCommunicationManager.addPartialChatMessage(tabId) | ||
Check warning on line 182 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
|
||
var encryptionManager: JwtEncryptionManager? = null | ||
val result = AmazonQLspService.executeIfRunning(project) { server -> | ||
encryptionManager = this.encryptionManager | ||
encryptionManager?.encrypt(chatParams)?.let { EncryptedChatParams(it, partialResultToken) }?.let { server.sendChatPrompt(it) } | ||
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running"))) | ||
showResult(result, partialResultToken, tabId, encryptionManager, browser) | ||
Check warning on line 189 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
} | ||
CHAT_QUICK_ACTION -> { | ||
val requestFromUi = serializer.deserializeChatMessages(node, QuickChatActionRequest::class.java) | ||
manodnyab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
val tabId = requestFromUi.params.tabId | ||
val quickActionParams = requestFromUi.params | ||
val partialResultToken = chatCommunicationManager.addPartialChatMessage(tabId) | ||
var encryptionManager: JwtEncryptionManager? = null | ||
Check warning on line 196 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
val result = AmazonQLspService.executeIfRunning(project) { server -> | ||
encryptionManager = this.encryptionManager | ||
Check warning on line 198 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we are extracting the encryptionManager, we might as well capture the server instance so that we don't need to fetch it on every invocation |
||
encryptionManager?.encrypt(quickActionParams)?.let { | ||
EncryptedQuickActionChatParams(it, partialResultToken) | ||
}?.let { | ||
server.sendQuickAction(it) | ||
} | ||
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running"))) | ||
Check warning on line 204 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
|
||
result.whenComplete { | ||
value, error -> | ||
chatCommunicationManager.removePartialChatMessage(partialResultToken) | ||
val messageToChat = ChatCommunicationManager.convertToJsonToSendToChat( | ||
node.command, | ||
requestFromUi.params.tabId, | ||
encryptionManager?.decrypt(value).orEmpty(), | ||
isPartialResult = false | ||
) | ||
browser.postChat(messageToChat) | ||
} | ||
showResult(result, partialResultToken, tabId, encryptionManager, browser) | ||
Check warning on line 206 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
} | ||
} | ||
} | ||
|
||
private fun showResult( | ||
result: CompletableFuture<String>, | ||
partialResultToken: String, | ||
tabId: String, | ||
encryptionManager: JwtEncryptionManager?, | ||
browser: Browser, | ||
) { | ||
result.whenComplete { value, error -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need to handle error There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
chatCommunicationManager.removePartialChatMessage(partialResultToken) | ||
val messageToChat = ChatCommunicationManager.convertToJsonToSendToChat( | ||
SEND_CHAT_COMMAND_PROMPT, | ||
tabId, | ||
Check warning on line 222 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
encryptionManager?.decrypt(value).orEmpty(), | ||
isPartialResult = false | ||
Check warning on line 224 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
) | ||
browser.postChat(messageToChat) | ||
} | ||
} | ||
Check warning on line 228 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,7 +40,10 @@ | |
import org.eclipse.lsp4j.TextDocumentClientCapabilities | ||
import org.eclipse.lsp4j.WorkspaceClientCapabilities | ||
import org.eclipse.lsp4j.jsonrpc.Launcher | ||
import org.eclipse.lsp4j.launch.LSPLauncher | ||
import org.eclipse.lsp4j.jsonrpc.Launcher.Builder | ||
Check warningCode scanning / QDJVMC Unused import directive Warning
Unused import directive
|
||
import org.eclipse.lsp4j.jsonrpc.MessageConsumer | ||
import org.eclipse.lsp4j.jsonrpc.messages.Message | ||
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage | ||
import org.slf4j.event.Level | ||
import software.aws.toolkits.core.utils.getLogger | ||
import software.aws.toolkits.core.utils.info | ||
|
@@ -50,6 +53,9 @@ | |
import software.aws.toolkits.jetbrains.services.amazonq.lsp.auth.DefaultAuthCredentialsService | ||
import software.aws.toolkits.jetbrains.services.amazonq.lsp.dependencies.DefaultModuleDependenciesService | ||
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 | ||
|
@@ -101,6 +107,10 @@ | |
|
||
@Service(Service.Level.PROJECT) | ||
class AmazonQLspService(private val project: Project, private val cs: CoroutineScope) : Disposable { | ||
private val serverStartedListener = mutableListOf<AmazonQServerStartedListener>() | ||
fun addServerStartedListener(listener: AmazonQServerStartedListener) = serverStartedListener.add(listener) | ||
fun notifyServerStarted() = serverStartedListener.forEach { it() } | ||
Check warning on line 112 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would prefer to have a flag that can be queried by consumers to handle the case where the server exits unexpectedly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has been renamed incorrectly. This listens to when we receive an initialize message and is separate from starting servers. Monitoring server state will be executed separately |
||
|
||
private var instance: Deferred<AmazonQServerInstance> | ||
val capabilities | ||
get() = instance.getCompleted().initializeResult.getCompleted().capabilities | ||
|
@@ -265,7 +275,23 @@ | |
launcherHandler.addProcessListener(inputWrapper) | ||
launcherHandler.startNotify() | ||
|
||
launcher = LSPLauncher.Builder<AmazonQLanguageServer>() | ||
class AmazonQServerBuilder : Builder<AmazonQLanguageServer>() { | ||
private val customMessageTracer = MessageTracer() | ||
Check warning on line 279 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
manodnyab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
override fun wrapMessageConsumer(consumer: MessageConsumer?): MessageConsumer = | ||
super.wrapMessageConsumer { message -> | ||
customMessageTracer.trace("INCOMING", message) | ||
Check warning on line 283 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
if (message is ResponseMessage && message.result is AwsExtendedInitializeResult) { | ||
val result = message.result as AwsExtendedInitializeResult | ||
AwsServerCapabilitiesProvider.getInstance(project).setAwsServerCapabilities(result.getAwsServerCapabilities()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we already expose capabilities on the service
? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Want to keep this in place to be able to access other agents potentially from this map |
||
AmazonQLspService.getInstance(project).notifyServerStarted() | ||
Check warning on line 287 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
} | ||
consumer?.consume(message) | ||
customMessageTracer.trace("PROCESSED", message) | ||
} | ||
Check warning on line 291 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
} | ||
|
||
launcher = AmazonQServerBuilder() | ||
Check warning on line 294 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
.setLocalService(AmazonQLanguageClientImpl(project)) | ||
.setRemoteInterface(AmazonQLanguageServer::class.java) | ||
.configureGson { | ||
|
@@ -274,6 +300,7 @@ | |
|
||
// otherwise Gson treats all numbers as double which causes deser issues | ||
it.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE) | ||
it.registerTypeAdapterFactory(AmazonQLspTypeAdapterFactory()) | ||
Check warning on line 303 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
}.traceMessages( | ||
PrintWriter( | ||
object : StringWriter() { | ||
|
@@ -348,7 +375,21 @@ | |
} | ||
} | ||
|
||
class MessageTracer { | ||
Check warning on line 378 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
Check warningCode scanning / QDJVMC Unused symbol Warning
Class "MessageTracer" is never used
|
||
private val traceLogger = LOG.atLevel(if (isDeveloperMode()) Level.INFO else Level.DEBUG) | ||
|
||
fun trace(direction: String, message: Message) { | ||
Check warningCode scanning / QDJVMC Unused symbol Warning
Function "trace" is never used
|
||
traceLogger.log { | ||
buildString { | ||
append("$direction: ") | ||
append(message.toString()) | ||
} | ||
Check warning on line 386 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
} | ||
} | ||
Check warning on line 388 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
|
||
} | ||
companion object { | ||
private val LOG = getLogger<AmazonQServerInstance>() | ||
} | ||
} | ||
|
||
typealias AmazonQServerStartedListener = () -> Unit |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
@file:Suppress("BannedImports") | ||
package software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat | ||
|
||
import com.google.gson.Gson | ||
import com.google.gson.TypeAdapter | ||
import com.google.gson.TypeAdapterFactory | ||
import com.google.gson.stream.JsonReader | ||
import com.google.gson.stream.JsonWriter | ||
import org.eclipse.lsp4j.InitializeResult | ||
import java.io.IOException | ||
|
||
class AmazonQLspTypeAdapterFactory : TypeAdapterFactory { | ||
Check warning on line 14 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
override fun <T : Any?> create(gson: Gson, type: com.google.gson.reflect.TypeToken<T>): TypeAdapter<T>? { | ||
manodnyab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (type.rawType === InitializeResult::class.java) { | ||
val delegate: TypeAdapter<InitializeResult?> = gson.getDelegateAdapter(this, type) as TypeAdapter<InitializeResult?> | ||
Check warning on line 17 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
|
||
return object : TypeAdapter<InitializeResult>() { | ||
Check warning on line 19 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
@Throws(IOException::class) | ||
override fun write(out: JsonWriter?, value: InitializeResult?) { | ||
delegate.write(out, value) | ||
} | ||
Check warning on line 23 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
|
||
@Throws(IOException::class) | ||
override fun read(`in`: JsonReader?): InitializeResult = | ||
gson.fromJson(`in`, AwsExtendedInitializeResult::class.java) | ||
Check warning on line 27 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
manodnyab marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} as TypeAdapter<T> | ||
} | ||
return null | ||
Check warning on line 30 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
} | ||
} | ||
|
||
class AwsExtendedInitializeResult : InitializeResult() { | ||
Check warning on line 34 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. data class |
||
private var awsServerCapabilities: AwsServerCapabilities? = null | ||
|
||
fun getAwsServerCapabilities(): AwsServerCapabilities? = awsServerCapabilities | ||
Check warning on line 37 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
|
||
fun setAwsServerCapabilities(awsServerCapabilities: AwsServerCapabilities?) { | ||
Check warning on line 39 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
Check warningCode scanning / QDJVMC Unused symbol Warning
Function "setAwsServerCapabilities" is never used
|
||
this.awsServerCapabilities = awsServerCapabilities | ||
} | ||
Check warning on line 41 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AmazonQLspTypeAdapterFactory.kt
|
||
} |
Check warning
Code scanning / QDJVMC
Unused import directive Warning