Skip to content

Commit 25ee578

Browse files
authored
fix(amazonq): do not model types that are directly passthrough to chat ui (#5702)
Due to mix of Jackson and Gson, history is not rendered correctly due to incorrect deserialization of enums
1 parent 6276aa1 commit 25ee578

File tree

14 files changed

+203
-196
lines changed

14 files changed

+203
-196
lines changed

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ import org.eclipse.lsp4j.services.LanguageClient
99
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LSPAny
1010
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_CONTEXT_COMMANDS
1111
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_UPDATE
12-
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatUpdateParams
1312
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GET_SERIALIZED_CHAT_REQUEST_METHOD
14-
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatParams
1513
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatResult
1614
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OPEN_FILE_DIFF
1715
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenFileDiffParams
18-
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabParams
1916
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabResult
2017
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SHOW_SAVE_FILE_DIALOG_REQUEST_METHOD
2118
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ShowSaveFileDialogParams
@@ -32,16 +29,16 @@ interface AmazonQLanguageClient : LanguageClient {
3229
fun getConnectionMetadata(): CompletableFuture<ConnectionMetadata>
3330

3431
@JsonRequest("aws/chat/openTab")
35-
fun openTab(params: OpenTabParams): CompletableFuture<OpenTabResult>
32+
fun openTab(params: LSPAny): CompletableFuture<OpenTabResult>
3633

3734
@JsonRequest(SHOW_SAVE_FILE_DIALOG_REQUEST_METHOD)
3835
fun showSaveFileDialog(params: ShowSaveFileDialogParams): CompletableFuture<ShowSaveFileDialogResult>
3936

4037
@JsonRequest(GET_SERIALIZED_CHAT_REQUEST_METHOD)
41-
fun getSerializedChat(params: GetSerializedChatParams): CompletableFuture<GetSerializedChatResult>
38+
fun getSerializedChat(params: LSPAny): CompletableFuture<GetSerializedChatResult>
4239

4340
@JsonNotification(CHAT_SEND_UPDATE)
44-
fun sendChatUpdate(params: ChatUpdateParams): CompletableFuture<Unit>
41+
fun sendChatUpdate(params: LSPAny): CompletableFuture<Unit>
4542

4643
@JsonNotification(OPEN_FILE_DIFF)
4744
fun openFileDiff(params: OpenFileDiffParams): CompletableFuture<Unit>

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,9 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LSPAny
3838
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_OPEN_TAB
3939
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_CONTEXT_COMMANDS
4040
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_UPDATE
41-
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatUpdateParams
4241
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GET_SERIALIZED_CHAT_REQUEST_METHOD
43-
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatParams
4442
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatResult
4543
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenFileDiffParams
46-
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabParams
4744
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.OpenTabResult
4845
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ShowSaveFileDialogParams
4946
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ShowSaveFileDialogResult
@@ -134,7 +131,7 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
134131
}
135132
}
136133

137-
override fun openTab(params: OpenTabParams): CompletableFuture<OpenTabResult> {
134+
override fun openTab(params: LSPAny): CompletableFuture<OpenTabResult> {
138135
val requestId = UUID.randomUUID().toString()
139136
val result = CompletableFuture<OpenTabResult>()
140137
val chatManager = ChatCommunicationManager.getInstance(project)
@@ -186,7 +183,7 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
186183
)
187184
}
188185

189-
override fun getSerializedChat(params: GetSerializedChatParams): CompletableFuture<GetSerializedChatResult> {
186+
override fun getSerializedChat(params: LSPAny): CompletableFuture<GetSerializedChatResult> {
190187
val requestId = UUID.randomUUID().toString()
191188
val result = CompletableFuture<GetSerializedChatResult>()
192189
val chatManager = ChatCommunicationManager.getInstance(project)
@@ -261,7 +258,7 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
261258
}
262259
}
263260

264-
override fun sendChatUpdate(params: ChatUpdateParams): CompletableFuture<Unit> {
261+
override fun sendChatUpdate(params: LSPAny): CompletableFuture<Unit> {
265262
AsyncChatUiListener.notifyPartialMessageUpdate(
266263
FlareUiMessage(
267264
command = CHAT_SEND_UPDATE,
@@ -345,7 +342,7 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
345342
chatManager.notifyUi(
346343
FlareUiMessage(
347344
command = CHAT_SEND_CONTEXT_COMMANDS,
348-
params = params ?: error("received empty payload for $CHAT_SEND_CONTEXT_COMMANDS"),
345+
params = params,
349346
)
350347
)
351348
return CompletableFuture.completedFuture(Unit)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,14 @@ class ChatCommunicationManager(private val cs: CoroutineScope) {
149149
reauthConnectionIfNeeded(project, it, isReAuth = true)
150150
}
151151
when (incomingType) {
152-
AuthFollowupType.USE_SUPPORTED_AUTH.value -> {
152+
AuthFollowupType.USE_SUPPORTED_AUTH -> {
153153
project.messageBus.syncPublisher(QRegionProfileSelectedListener.TOPIC)
154154
.onProfileSelected(project, QRegionProfileManager.getInstance().activeProfile(project))
155155
return
156156
}
157-
AuthFollowupType.RE_AUTH.value,
158-
AuthFollowupType.MISSING_SCOPES.value,
159-
AuthFollowupType.FULL_AUTH.value,
157+
AuthFollowupType.RE_AUTH,
158+
AuthFollowupType.MISSING_SCOPES,
159+
AuthFollowupType.FULL_AUTH,
160160
-> {
161161
return
162162
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
@file:Suppress("BannedImports")
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model
5+
6+
import com.fasterxml.jackson.annotation.JsonValue
7+
import com.google.gson.Gson
8+
import com.google.gson.TypeAdapter
9+
import com.google.gson.TypeAdapterFactory
10+
import com.google.gson.reflect.TypeToken
11+
12+
/**
13+
* A [Gson] [TypeAdapterFactory] that uses Jackson @[JsonValue] instead of [Enum.name] for de/serialization
14+
*/
15+
class EnumJsonValueAdapter : TypeAdapterFactory {
16+
override fun <T> create(
17+
gson: Gson,
18+
type: TypeToken<T>,
19+
): TypeAdapter<T>? {
20+
val rawType = type.getRawType()
21+
if (!rawType.isEnum) {
22+
return null
23+
}
24+
25+
val jsonField = rawType.declaredFields.firstOrNull { it.isAnnotationPresent(JsonValue::class.java) }
26+
?: return null
27+
28+
jsonField.isAccessible = true
29+
30+
return object : TypeAdapter<T>() {
31+
override fun write(out: com.google.gson.stream.JsonWriter, value: T) {
32+
val result = jsonField.get(value) as Any
33+
(gson.getAdapter(result::class.java) as TypeAdapter<Any>).write(out, result)
34+
}
35+
36+
override fun read(`in`: com.google.gson.stream.JsonReader): T {
37+
val jsonValue = `in`.nextString()
38+
return rawType.enumConstants.first { jsonField.get(it).toString() == jsonValue } as T
39+
}
40+
} as TypeAdapter<T>
41+
}
42+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ data class UpdateConfigurationParams(
1414
val settings: LSPAny,
1515
)
1616

17-
typealias LSPAny = Any?
17+
typealias LSPAny = Any

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

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat
55

6+
import com.fasterxml.jackson.annotation.JsonValue
7+
import com.google.gson.annotations.JsonAdapter
8+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.EnumJsonValueAdapter
9+
610
data class AuthFollowUpClickNotification(
711
override val command: String,
812
override val params: AuthFollowUpClickedParams,
@@ -11,21 +15,13 @@ data class AuthFollowUpClickNotification(
1115
data class AuthFollowUpClickedParams(
1216
val tabId: String,
1317
val messageId: String,
14-
val authFollowupType: String,
15-
) {
16-
companion object {
17-
fun create(tabId: String, messageId: String, authType: AuthFollowupType): AuthFollowUpClickedParams =
18-
AuthFollowUpClickedParams(tabId, messageId, authType.value)
19-
}
20-
}
18+
val authFollowupType: AuthFollowupType,
19+
)
2120

22-
enum class AuthFollowupType(val value: String) {
21+
@JsonAdapter(EnumJsonValueAdapter::class)
22+
enum class AuthFollowupType(@JsonValue val repr: String) {
2323
FULL_AUTH("full-auth"),
2424
RE_AUTH("re-auth"),
2525
MISSING_SCOPES("missing_scopes"),
2626
USE_SUPPORTED_AUTH("use-supported-auth"),
27-
;
28-
29-
override fun toString(): String =
30-
name.lowercase()
3127
}

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

Lines changed: 37 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat
55

6-
import com.fasterxml.jackson.annotation.JsonProperty
6+
import com.fasterxml.jackson.annotation.JsonValue
7+
import com.google.gson.annotations.JsonAdapter
8+
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.EnumJsonValueAdapter
79

810
data class ChatMessage(
911
val type: MessageType? = MessageType.ANSWER,
@@ -69,92 +71,44 @@ data class Changes(
6971
val total: Int? = null,
7072
)
7173

72-
enum class IconType {
73-
@JsonProperty("file")
74-
FILE,
75-
76-
@JsonProperty("folder")
77-
FOLDER,
78-
79-
@JsonProperty("code-block")
80-
CODE_BLOCK,
81-
82-
@JsonProperty("list-add")
83-
LIST_ADD,
84-
85-
@JsonProperty("magic")
86-
MAGIC,
87-
88-
@JsonProperty("help")
89-
HELP,
90-
91-
@JsonProperty("trash")
92-
TRASH,
93-
94-
@JsonProperty("search")
95-
SEARCH,
96-
97-
@JsonProperty("calendar")
98-
CALENDAR,
99-
;
100-
101-
companion object {
102-
private val stringToEnum: Map<String, IconType> = entries.associateBy { it.name.lowercase() }
103-
104-
fun fromString(value: String): IconType = stringToEnum[value] ?: throw IllegalArgumentException("Unknown IconType: $value")
105-
}
74+
@JsonAdapter(EnumJsonValueAdapter::class)
75+
enum class IconType(@JsonValue val repr: String) {
76+
FILE("file"),
77+
FOLDER("folder"),
78+
CODE_BLOCK("code-block"),
79+
LIST_ADD("list-add"),
80+
MAGIC("magic"),
81+
HELP("help"),
82+
TRASH("trash"),
83+
SEARCH("search"),
84+
CALENDAR("calendar"),
10685
}
10786

108-
enum class Status {
109-
@JsonProperty("info")
110-
INFO,
111-
112-
@JsonProperty("success")
113-
SUCCESS,
114-
115-
@JsonProperty("warning")
116-
WARNING,
117-
118-
@JsonProperty("error")
119-
ERROR,
87+
@JsonAdapter(EnumJsonValueAdapter::class)
88+
enum class Status(@JsonValue val repr: String) {
89+
INFO("info"),
90+
SUCCESS("success"),
91+
WARNING("warning"),
92+
ERROR("error"),
12093
}
12194

122-
enum class ButtonStatus {
123-
@JsonProperty("main")
124-
MAIN,
125-
126-
@JsonProperty("primary")
127-
PRIMARY,
128-
129-
@JsonProperty("clear")
130-
CLEAR,
131-
132-
@JsonProperty("info")
133-
INFO,
134-
135-
@JsonProperty("success")
136-
SUCCESS,
137-
138-
@JsonProperty("warning")
139-
WARNING,
140-
141-
@JsonProperty("error")
142-
ERROR,
95+
@JsonAdapter(EnumJsonValueAdapter::class)
96+
enum class ButtonStatus(@JsonValue val repr: String) {
97+
MAIN("main"),
98+
PRIMARY("primary"),
99+
CLEAR("clear"),
100+
INFO("info"),
101+
SUCCESS("success"),
102+
WARNING("warning"),
103+
ERROR("error"),
143104
}
144105

145-
enum class MessageType {
146-
@JsonProperty("answer")
147-
ANSWER,
148-
149-
@JsonProperty("prompt")
150-
PROMPT,
151-
152-
@JsonProperty("system-prompt")
153-
SYSTEM_PROMPT,
154-
155-
@JsonProperty("directive")
156-
DIRECTIVE,
157-
158-
@JsonProperty("tool")
159-
TOOL,
106+
// https://github.com/aws/language-server-runtimes/blame/68319c975d29a8ba9b084c9fa780ebff75b286bb/types/chat.ts#L127
107+
@JsonAdapter(EnumJsonValueAdapter::class)
108+
enum class MessageType(@JsonValue val repr: String) {
109+
ANSWER("answer"),
110+
PROMPT("prompt"),
111+
SYSTEM_PROMPT("system-prompt"),
112+
DIRECTIVE("directive"),
113+
TOOL("tool"),
160114
}

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

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)