Skip to content

Commit 1c91037

Browse files
authored
feat(amazonq): Chat history provided through flare (#7056)
## Problem - In order to enable chat history in Flare the: listConversationsRequestType, conversationClickRequestType, tabBarActionRequestType events need to be implemented ## Solution - Handle listConversationsRequestType, conversationClickRequestType, tabBarActionRequestType events --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent d6d839a commit 1c91037

File tree

4 files changed

+144
-40
lines changed

4 files changed

+144
-40
lines changed

package-lock.json

Lines changed: 13 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/amazonq/src/lsp/chat/messages.ts

Lines changed: 128 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
COPY_TO_CLIPBOARD,
1212
AuthFollowUpType,
1313
DISCLAIMER_ACKNOWLEDGED,
14+
UiMessageResultParams,
1415
} from '@aws/chat-client-ui-types'
1516
import {
1617
ChatResult,
@@ -21,30 +22,42 @@ import {
2122
QuickActionResult,
2223
QuickActionParams,
2324
insertToCursorPositionNotificationType,
25+
ErrorCodes,
26+
ResponseError,
27+
openTabRequestType,
28+
getSerializedChatRequestType,
29+
listConversationsRequestType,
30+
conversationClickRequestType,
31+
ShowSaveFileDialogRequestType,
32+
ShowSaveFileDialogParams,
33+
LSPErrorCodes,
34+
tabBarActionRequestType,
2435
} from '@aws/language-server-runtimes/protocol'
2536
import { v4 as uuidv4 } from 'uuid'
2637
import * as vscode from 'vscode'
27-
import { Disposable, LanguageClient, Position, State, TextDocumentIdentifier } from 'vscode-languageclient'
38+
import { Disposable, LanguageClient, Position, TextDocumentIdentifier } from 'vscode-languageclient'
2839
import * as jose from 'jose'
2940
import { AmazonQChatViewProvider } from './webviewProvider'
3041
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
3142
import { AmazonQPromptSettings, messages } from 'aws-core-vscode/shared'
3243

3344
export function registerLanguageServerEventListener(languageClient: LanguageClient, provider: AmazonQChatViewProvider) {
34-
languageClient.onDidChangeState(({ oldState, newState }) => {
35-
if (oldState === State.Starting && newState === State.Running) {
36-
languageClient.info(
37-
'Language client received initializeResult from server:',
38-
JSON.stringify(languageClient.initializeResult)
39-
)
45+
languageClient.info(
46+
'Language client received initializeResult from server:',
47+
JSON.stringify(languageClient.initializeResult)
48+
)
4049

41-
const chatOptions = languageClient.initializeResult?.awsServerCapabilities?.chatOptions
50+
const chatOptions = languageClient.initializeResult?.awsServerCapabilities?.chatOptions
4251

43-
void provider.webview?.postMessage({
44-
command: CHAT_OPTIONS,
45-
params: chatOptions,
46-
})
47-
}
52+
// Enable the history/export feature flags
53+
chatOptions.history = true
54+
chatOptions.export = true
55+
56+
provider.onDidResolveWebview(() => {
57+
void provider.webview?.postMessage({
58+
command: CHAT_OPTIONS,
59+
params: chatOptions,
60+
})
4861
})
4962

5063
languageClient.onTelemetry((e) => {
@@ -60,6 +73,7 @@ export function registerMessageListeners(
6073
provider.webview?.onDidReceiveMessage(async (message) => {
6174
languageClient.info(`[VSCode Client] Received ${JSON.stringify(message)} from chat`)
6275

76+
const webview = provider.webview
6377
switch (message.command) {
6478
case COPY_TO_CLIPBOARD:
6579
languageClient.info('[VSCode Client] Copy to clipboard event received')
@@ -172,6 +186,11 @@ export function registerMessageListeners(
172186
)
173187
break
174188
}
189+
case listConversationsRequestType.method:
190+
case conversationClickRequestType.method:
191+
case tabBarActionRequestType.method:
192+
await resolveChatResponse(message.command, message.params, languageClient, webview)
193+
break
175194
case followUpClickNotificationType.method:
176195
if (!isValidAuthFollowUpType(message.params.followUp.type)) {
177196
languageClient.sendNotification(followUpClickNotificationType.method, message.params)
@@ -184,6 +203,89 @@ export function registerMessageListeners(
184203
break
185204
}
186205
}, undefined)
206+
207+
const registerHandlerWithResponseRouter = (command: string) => {
208+
const handler = async (params: any, _: any) => {
209+
const mapErrorType = (type: string | undefined): number => {
210+
switch (type) {
211+
case 'InvalidRequest':
212+
return ErrorCodes.InvalidRequest
213+
case 'InternalError':
214+
return ErrorCodes.InternalError
215+
case 'UnknownError':
216+
default:
217+
return ErrorCodes.UnknownErrorCode
218+
}
219+
}
220+
const requestId = uuidv4()
221+
222+
void provider.webview?.postMessage({
223+
requestId: requestId,
224+
command: command,
225+
params: params,
226+
})
227+
const responsePromise = new Promise<UiMessageResultParams | undefined>((resolve, reject) => {
228+
const timeout = setTimeout(() => {
229+
disposable?.dispose()
230+
reject(new Error('Request timed out'))
231+
}, 30000)
232+
233+
const disposable = provider.webview?.onDidReceiveMessage((message: any) => {
234+
if (message.requestId === requestId) {
235+
clearTimeout(timeout)
236+
disposable?.dispose()
237+
resolve(message.params)
238+
}
239+
})
240+
})
241+
242+
const result = await responsePromise
243+
244+
if (result?.success) {
245+
return result.result
246+
} else {
247+
return new ResponseError(
248+
mapErrorType(result?.error.type),
249+
result?.error.message ?? 'No response from client'
250+
)
251+
}
252+
}
253+
254+
languageClient.onRequest(command, handler)
255+
}
256+
257+
registerHandlerWithResponseRouter(openTabRequestType.method)
258+
registerHandlerWithResponseRouter(getSerializedChatRequestType.method)
259+
260+
languageClient.onRequest(ShowSaveFileDialogRequestType.method, async (params: ShowSaveFileDialogParams) => {
261+
const filters: Record<string, string[]> = {}
262+
const formatMappings = [
263+
{ format: 'markdown', key: 'Markdown', extensions: ['md'] },
264+
{ format: 'html', key: 'HTML', extensions: ['html'] },
265+
]
266+
267+
for (const format of params.supportedFormats ?? []) {
268+
const mapping = formatMappings.find((m) => m.format === format)
269+
if (mapping) {
270+
filters[mapping.key] = mapping.extensions
271+
}
272+
}
273+
274+
const saveAtUri = params.defaultUri ? vscode.Uri.parse(params.defaultUri) : vscode.Uri.file('export-chat.md')
275+
const targetUri = await vscode.window.showSaveDialog({
276+
filters,
277+
defaultUri: saveAtUri,
278+
title: 'Export',
279+
})
280+
281+
if (!targetUri) {
282+
return new ResponseError(LSPErrorCodes.RequestFailed, 'Export failed')
283+
}
284+
285+
return {
286+
targetUri: targetUri.toString(),
287+
}
288+
})
187289
}
188290

189291
function isServerEvent(command: string) {
@@ -258,3 +360,16 @@ async function handleCompleteResult<T>(
258360
})
259361
disposable.dispose()
260362
}
363+
364+
async function resolveChatResponse(
365+
requestMethod: string,
366+
params: any,
367+
languageClient: LanguageClient,
368+
webview: vscode.Webview | undefined
369+
) {
370+
const result = await languageClient.sendRequest(requestMethod, params)
371+
void webview?.postMessage({
372+
command: requestMethod,
373+
params: result,
374+
})
375+
}

packages/amazonq/test/unit/amazonq/lsp/chat/messages.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe('registerMessageListeners', () => {
2626
info: sandbox.stub(),
2727
error: errorStub,
2828
sendNotification: sandbox.stub(),
29+
onRequest: sandbox.stub(),
2930
} as unknown as LanguageClient
3031

3132
provider = {

packages/core/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,8 @@
441441
"devDependencies": {
442442
"@aws-sdk/types": "^3.13.1",
443443
"@aws/chat-client-ui-types": "^0.1.12",
444-
"@aws/language-server-runtimes": "^0.2.49",
445-
"@aws/language-server-runtimes-types": "^0.1.10",
444+
"@aws/language-server-runtimes": "^0.2.58",
445+
"@aws/language-server-runtimes-types": "^0.1.13",
446446
"@cspotcode/source-map-support": "^0.8.1",
447447
"@sinonjs/fake-timers": "^10.0.2",
448448
"@types/adm-zip": "^0.4.34",

0 commit comments

Comments
 (0)