-
Notifications
You must be signed in to change notification settings - Fork 273
feat(amazonQFlareChat): Set up Flare chat connection #5545
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 1 commit
fe7738d
c09368c
9d037cf
e259071
bc6823b
b833b6c
fb9fd25
46b9a82
06dc03d
0f16cd4
21e4885
06e0966
ebba74e
57600f5
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 |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
| import com.intellij.openapi.util.Disposer | ||
| import com.intellij.ui.jcef.JBCefJSQuery | ||
| import org.cef.CefApp | ||
| import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactHelper | ||
| import software.aws.toolkits.jetbrains.services.amazonq.util.HighlightCommand | ||
| import software.aws.toolkits.jetbrains.services.amazonq.util.createBrowser | ||
| import software.aws.toolkits.jetbrains.settings.MeetQSettings | ||
|
|
@@ -46,6 +47,13 @@ | |
|
|
||
| fun component() = jcefBrowser.component | ||
|
|
||
| fun postChat(message: String) { | ||
| jcefBrowser | ||
| .cefBrowser | ||
| .executeJavaScript("window.postMessage($message)", jcefBrowser.cefBrowser.url, 0) | ||
| } | ||
|
|
||
| // TODO: Remove this once chat has been integrated with agents | ||
| fun post(message: String) = | ||
| jcefBrowser | ||
| .cefBrowser | ||
|
|
@@ -82,32 +90,86 @@ | |
| highlightCommand: HighlightCommand?, | ||
| ): String { | ||
| val postMessageToJavaJsCode = receiveMessageQuery.inject("JSON.stringify(message)") | ||
|
|
||
| val jsScripts = """ | ||
| <script type="text/javascript" src="$WEB_SCRIPT_URI" defer onload="init()"></script> | ||
| <script type="text/javascript"> | ||
| const init = () => { | ||
| mynahUI.createMynahUI( | ||
| amazonQChat.createChat( | ||
| { | ||
| postMessage: message => { | ||
| $postMessageToJavaJsCode | ||
| } | ||
| }, | ||
| ${MeetQSettings.getInstance().reinvent2024OnboardingCount < MAX_ONBOARDING_PAGE_COUNT}, | ||
| ${MeetQSettings.getInstance().disclaimerAcknowledged}, | ||
| $isFeatureDevAvailable, // whether /dev is available | ||
| $isCodeTransformAvailable, // whether /transform is available | ||
| $isDocAvailable, // whether /doc is available | ||
| $isCodeScanAvailable, // whether /scan is available | ||
| $isCodeTestAvailable, // whether /test is available | ||
| ${OBJECT_MAPPER.writeValueAsString(highlightCommand)} | ||
| }, | ||
| { | ||
| quickActionCommands: [], | ||
| disclaimerAcknowledged: ${MeetQSettings.getInstance().disclaimerAcknowledged} | ||
| } | ||
| ); | ||
| } | ||
| </script> | ||
| """.trimIndent() | ||
|
|
||
| addQuickActionCommands(isCodeTransformAvailable, isFeatureDevAvailable, isDocAvailable, isCodeTestAvailable, isCodeScanAvailable, highlightCommand) | ||
| return """ | ||
| <!DOCTYPE html> | ||
| <style> | ||
| body, | ||
| html { | ||
| background-color: var(--mynah-color-bg); | ||
| color: var(--mynah-color-text-default); | ||
| height: 100vh; | ||
| width: 100%%; | ||
| overflow: hidden; | ||
| margin: 0; | ||
| padding: 0; | ||
| } | ||
| .mynah-ui-icon-plus, | ||
| .mynah-ui-icon-cancel { | ||
| -webkit-mask-size: 155% !important; | ||
| mask-size: 155% !important; | ||
| mask-position: center; | ||
| scale: 60%; | ||
| } | ||
| .code-snippet-close-button i.mynah-ui-icon-cancel, | ||
| .mynah-chat-item-card-related-content-show-more i.mynah-ui-icon-down-open { | ||
| -webkit-mask-size: 195.5% !important; | ||
| mask-size: 195.5% !important; | ||
| mask-position: center; | ||
| aspect-ratio: 1/1; | ||
| width: 15px; | ||
| height: 15px; | ||
| scale: 50% | ||
| } | ||
| .mynah-ui-icon-tabs { | ||
| -webkit-mask-size: 102% !important; | ||
| mask-size: 102% !important; | ||
| mask-position: center; | ||
| } | ||
| textarea:placeholder-shown { | ||
| line-height: 1.5rem; | ||
| } | ||
| .mynah-ui-spinner-container { | ||
| contain: layout !important; | ||
| } | ||
| .mynah-ui-spinner-container > span.mynah-ui-spinner-logo-part { | ||
| position: static !important; | ||
| will-change: transform !important; | ||
| } | ||
| .mynah-ui-spinner-container, | ||
| .mynah-ui-spinner-container > span.mynah-ui-spinner-logo-part, | ||
| .mynah-ui-spinner-container > span.mynah-ui-spinner-logo-part > .mynah-ui-spinner-logo-mask.text { | ||
| border: 0 !important; | ||
| outline: none !important; | ||
| box-shadow: none !important; | ||
| border-radius: 0 !important; | ||
| } | ||
| .mynah-ui-spinner-container > span.mynah-ui-spinner-logo-part > .mynah-ui-spinner-logo-mask.text { | ||
| will-change: transform !important; | ||
| transform: translateZ(0) !important; | ||
| } | ||
| </style> | ||
| <html> | ||
| <head> | ||
| <title>AWS Q</title> | ||
|
|
@@ -119,8 +181,28 @@ | |
| """.trimIndent() | ||
| } | ||
|
|
||
| fun addQuickActionCommands( | ||
|
Check notice on line 184 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt
|
||
| isCodeTransformAvailable: Boolean, | ||
| isFeatureDevAvailable: Boolean, | ||
| isDocAvailable: Boolean, | ||
| isCodeTestAvailable: Boolean, | ||
| isCodeScanAvailable: Boolean, | ||
| highlightCommand: HighlightCommand?, | ||
| ) { | ||
| // TODO: Remove this once chat has been integrated with agents. This is added temporarily to keep detekt happy. | ||
| isCodeScanAvailable | ||
| isCodeTestAvailable | ||
| isDocAvailable | ||
| isFeatureDevAvailable | ||
| isCodeTransformAvailable | ||
| MAX_ONBOARDING_PAGE_COUNT | ||
| OBJECT_MAPPER | ||
| highlightCommand | ||
| } | ||
|
|
||
| companion object { | ||
| private const val WEB_SCRIPT_URI = "http://mynah/js/mynah-ui.js" | ||
| // TODO: Switch this to respect the overriden paths too | ||
| private val WEB_SCRIPT_URI = ArtifactHelper().getLatestLocalLspArtifact().resolve("amazonq-ui.js").toUri() | ||
|
||
| private const val MAX_ONBOARDING_PAGE_COUNT = 3 | ||
| private val OBJECT_MAPPER = jacksonObjectMapper() | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,11 @@ | |
|
|
||
| package software.aws.toolkits.jetbrains.services.amazonq.webview | ||
|
|
||
| import com.fasterxml.jackson.databind.JsonNode | ||
| import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper | ||
| import com.intellij.ide.BrowserUtil | ||
| import com.intellij.ide.util.RunOnceUtil | ||
| import com.intellij.openapi.project.Project | ||
| import com.intellij.ui.jcef.JBCefJSQuery.Response | ||
| import kotlinx.coroutines.CompletableDeferred | ||
| import kotlinx.coroutines.channels.awaitClose | ||
|
|
@@ -17,22 +20,36 @@ | |
| import kotlinx.coroutines.flow.onEach | ||
| import kotlinx.coroutines.launch | ||
| import org.cef.browser.CefBrowser | ||
| import org.eclipse.lsp4j.Position | ||
| import org.eclipse.lsp4j.Range | ||
| import software.aws.toolkits.jetbrains.services.amazonq.apps.AppConnection | ||
| import software.aws.toolkits.jetbrains.services.amazonq.commands.MessageSerializer | ||
| import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService | ||
| 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.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.SendChatPromptRequest | ||
| import software.aws.toolkits.jetbrains.services.amazonq.util.command | ||
| import software.aws.toolkits.jetbrains.services.amazonq.util.tabType | ||
| import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.AmazonQTheme | ||
| import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.ThemeBrowserAdapter | ||
| import software.aws.toolkits.jetbrains.settings.MeetQSettings | ||
| import software.aws.toolkits.telemetry.MetricResult | ||
| import software.aws.toolkits.telemetry.Telemetry | ||
| import java.util.concurrent.CompletableFuture | ||
| import java.util.function.Function | ||
|
|
||
| class BrowserConnector( | ||
| private val serializer: MessageSerializer = MessageSerializer.getInstance(), | ||
| private val themeBrowserAdapter: ThemeBrowserAdapter = ThemeBrowserAdapter(), | ||
| private val project: Project, | ||
| ) { | ||
| var uiReady = CompletableDeferred<Boolean>() | ||
| val chatCommunicationManager = ChatCommunicationManager.getInstance(project) | ||
|
Check notice on line 52 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt
|
||
|
||
|
|
||
| suspend fun connect( | ||
| browser: Browser, | ||
|
|
@@ -77,7 +94,10 @@ | |
| } | ||
| } | ||
|
|
||
| val tabType = node.tabType ?: return@onEach | ||
| val tabType = node.tabType | ||
| if (tabType == null) { | ||
| handleFlareChatMessages(browser, node) | ||
| } | ||
| connections.filter { connection -> connection.app.tabTypes.contains(tabType) }.forEach { connection -> | ||
| launch { | ||
| val message = serializer.deserialize(node, connection.messageTypeRegistry) | ||
|
|
@@ -123,4 +143,55 @@ | |
| browser.receiveMessageQuery.removeHandler(handler) | ||
| } | ||
| } | ||
|
|
||
| private suspend fun handleFlareChatMessages(browser: Browser, node: JsonNode) { | ||
|
||
| when (node.command) { | ||
| "aws/chat/sendChatPrompt" -> { | ||
manodnyab marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| val requestFromUi = jacksonObjectMapper().readValue(node.toString(), SendChatPromptRequest::class.java) | ||
manodnyab marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| val chatPrompt = ChatPrompt( | ||
| requestFromUi.params.prompt.prompt, | ||
| requestFromUi.params.prompt.escapedPrompt, | ||
| node.command | ||
| ) | ||
| val textDocumentIdentifier = getTextDocumentIdentifier(project) | ||
| val cursorState = CursorState( | ||
| Range( | ||
| Position( | ||
| 0, | ||
| 0 | ||
| ), | ||
| Position( | ||
| 1, | ||
| 1 | ||
| ) | ||
|
Comment on lines
+158
to
+166
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. use real values? 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. Adding this as a follow-up |
||
| ) | ||
| ) | ||
| val chatParams = ChatParams( | ||
| requestFromUi.params.tabId, | ||
| chatPrompt, | ||
| textDocumentIdentifier, | ||
| cursorState | ||
| ) | ||
| val partialResultToken = chatCommunicationManager.addPartialChatMessage(requestFromUi.params.tabId) | ||
|
|
||
| var encryptionManager: JwtEncryptionManager? = null | ||
| val result = AmazonQLspService.executeIfRunning(project) { server -> | ||
| encryptionManager = this.encryptionManager | ||
| server.sendChatPrompt(EncryptedChatParams(encryptionManager!!.encrypt(chatParams))) | ||
| } ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running"))) | ||
|
|
||
| result.whenComplete { | ||
| value, error -> | ||
| chatCommunicationManager.removePartialChatMessage(partialResultToken) | ||
| val messageToChat = ChatCommunicationManager.convertToJsonToSendToChat( | ||
| node.command, | ||
| requestFromUi.params.tabId, | ||
| encryptionManager?.decrypt(value) ?: "", | ||
| isPartialResult = false | ||
| ) | ||
| browser.postChat(messageToChat) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.