Skip to content

Commit b4de24f

Browse files
authored
feat(amazonq): remote workspace context (#6894)
## Problem We did not utilize server side compute to help improve the quality of inline completion, chat, etc. ## Solution 1. Add server side project context support. --- - 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 6ca54fc commit b4de24f

File tree

5 files changed

+135
-26
lines changed

5 files changed

+135
-26
lines changed

aws-toolkit-vscode.code-workspace

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
{
2-
"folders": [
3-
{
4-
"path": "."
5-
},
6-
{
7-
"path": "packages/toolkit"
8-
},
9-
{
10-
"path": "packages/core"
11-
},
12-
{
13-
"path": "packages/amazonq"
14-
}
15-
],
16-
"settings": {
17-
"typescript.tsdk": "node_modules/typescript/lib"
18-
}
19-
}
2+
"folders": [
3+
{
4+
"path": ".",
5+
},
6+
{
7+
"path": "packages/toolkit",
8+
},
9+
{
10+
"path": "packages/core",
11+
},
12+
{
13+
"path": "packages/amazonq",
14+
},
15+
],
16+
"settings": {
17+
"typescript.tsdk": "node_modules/typescript/lib",
18+
},
19+
}

packages/amazonq/src/extension.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import { AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth'
6+
import { Auth, AuthUtils, CredentialsStore, LoginManager, initializeAuth } from 'aws-core-vscode/auth'
77
import { activate as activateCodeWhisperer, shutdown as shutdownCodeWhisperer } from 'aws-core-vscode/codewhisperer'
88
import { makeEndpointsProvider, registerGenericCommands } from 'aws-core-vscode'
99
import { CommonAuthWebview } from 'aws-core-vscode/login'
@@ -119,10 +119,10 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is
119119
}
120120
// This contains every lsp agnostic things (auth, security scan, code scan)
121121
await activateCodeWhisperer(extContext as ExtContext)
122-
if (Experiments.instance.get('amazonqLSP', false)) {
122+
if (Experiments.instance.get('amazonqLSP', false) || Auth.instance.isInternalAmazonUser()) {
123+
// start the Amazon Q LSP for internal users first
123124
await activateAmazonqLsp(context)
124125
}
125-
126126
if (!Experiments.instance.get('amazonqLSPInline', false)) {
127127
await activateInlineCompletion()
128128
}

packages/amazonq/src/lsp/client.ts

+94-3
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,30 @@
66
import vscode, { env, version } from 'vscode'
77
import * as nls from 'vscode-nls'
88
import * as crypto from 'crypto'
9-
import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient'
9+
import { LanguageClient, LanguageClientOptions, RequestType } from 'vscode-languageclient'
1010
import { InlineCompletionManager } from '../app/inline/completion'
1111
import { AmazonQLspAuth, encryptionKey, notificationTypes } from './auth'
1212
import { AuthUtil } from 'aws-core-vscode/codewhisperer'
13-
import { ConnectionMetadata } from '@aws/language-server-runtimes/protocol'
13+
import {
14+
ConnectionMetadata,
15+
CreateFilesParams,
16+
DeleteFilesParams,
17+
DidChangeWorkspaceFoldersParams,
18+
DidSaveTextDocumentParams,
19+
GetConfigurationFromServerParams,
20+
RenameFilesParams,
21+
ResponseMessage,
22+
updateConfigurationRequestType,
23+
WorkspaceFolder,
24+
} from '@aws/language-server-runtimes/protocol'
1425
import {
1526
Settings,
1627
oidcClientName,
1728
createServerOptions,
1829
globals,
1930
Experiments,
2031
Commands,
32+
oneSecond,
2133
validateNodeExe,
2234
getLogger,
2335
} from 'aws-core-vscode/shared'
@@ -75,6 +87,9 @@ export async function startLanguageServer(
7587
window: {
7688
notifications: true,
7789
},
90+
q: {
91+
developerProfiles: true,
92+
},
7893
},
7994
},
8095
credentials: {
@@ -134,7 +149,27 @@ export async function startLanguageServer(
134149
activate(client, encryptionKey, resourcePaths.ui)
135150
}
136151

137-
const refreshInterval = auth.startTokenRefreshInterval()
152+
const refreshInterval = auth.startTokenRefreshInterval(10 * oneSecond)
153+
154+
const sendProfileToLsp = async () => {
155+
try {
156+
const result = await client.sendRequest(updateConfigurationRequestType.method, {
157+
section: 'aws.q',
158+
settings: {
159+
profileArn: AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn,
160+
},
161+
})
162+
client.info(
163+
`Client: Updated Amazon Q Profile ${AuthUtil.instance.regionProfileManager.activeRegionProfile?.arn} to Amazon Q LSP`,
164+
result
165+
)
166+
} catch (err) {
167+
client.error('Error when setting Q Developer Profile to Amazon Q LSP', err)
168+
}
169+
}
170+
171+
// send profile to lsp once.
172+
void sendProfileToLsp()
138173

139174
toDispose.push(
140175
AuthUtil.instance.auth.onDidChangeActiveConnection(async () => {
@@ -143,6 +178,62 @@ export async function startLanguageServer(
143178
AuthUtil.instance.auth.onDidDeleteConnection(async () => {
144179
client.sendNotification(notificationTypes.deleteBearerToken.method)
145180
}),
181+
AuthUtil.instance.regionProfileManager.onDidChangeRegionProfile(sendProfileToLsp),
182+
vscode.commands.registerCommand('aws.amazonq.getWorkspaceId', async () => {
183+
const requestType = new RequestType<GetConfigurationFromServerParams, ResponseMessage, Error>(
184+
'aws/getConfigurationFromServer'
185+
)
186+
const workspaceIdResp = await client.sendRequest(requestType.method, {
187+
section: 'aws.q.workspaceContext',
188+
})
189+
return workspaceIdResp
190+
}),
191+
vscode.workspace.onDidCreateFiles((e) => {
192+
client.sendNotification('workspace/didCreateFiles', {
193+
files: e.files.map((it) => {
194+
return { uri: it.fsPath }
195+
}),
196+
} as CreateFilesParams)
197+
}),
198+
vscode.workspace.onDidDeleteFiles((e) => {
199+
client.sendNotification('workspace/didDeleteFiles', {
200+
files: e.files.map((it) => {
201+
return { uri: it.fsPath }
202+
}),
203+
} as DeleteFilesParams)
204+
}),
205+
vscode.workspace.onDidRenameFiles((e) => {
206+
client.sendNotification('workspace/didRenameFiles', {
207+
files: e.files.map((it) => {
208+
return { oldUri: it.oldUri.fsPath, newUri: it.newUri.fsPath }
209+
}),
210+
} as RenameFilesParams)
211+
}),
212+
vscode.workspace.onDidSaveTextDocument((e) => {
213+
client.sendNotification('workspace/didSaveTextDocument', {
214+
textDocument: {
215+
uri: e.uri.fsPath,
216+
},
217+
} as DidSaveTextDocumentParams)
218+
}),
219+
vscode.workspace.onDidChangeWorkspaceFolders((e) => {
220+
client.sendNotification('workspace/didChangeWorkspaceFolder', {
221+
event: {
222+
added: e.added.map((it) => {
223+
return {
224+
name: it.name,
225+
uri: it.uri.fsPath,
226+
} as WorkspaceFolder
227+
}),
228+
removed: e.removed.map((it) => {
229+
return {
230+
name: it.name,
231+
uri: it.uri.fsPath,
232+
} as WorkspaceFolder
233+
}),
234+
},
235+
} as DidChangeWorkspaceFoldersParams)
236+
}),
146237
{ dispose: () => clearInterval(refreshInterval) }
147238
)
148239
})

packages/amazonq/src/lsp/config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ export interface ExtendedAmazonQLSPConfig extends LspConfig {
1111
}
1212

1313
export const defaultAmazonQLspConfig: ExtendedAmazonQLSPConfig = {
14-
manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/codewhisperer/0/manifest.json',
15-
supportedVersions: '^3.1.1',
14+
manifestUrl: 'https://aws-toolkit-language-servers.amazonaws.com/remoteWorkspaceContext/0/manifest.json',
15+
supportedVersions: '^1.0.0',
1616
id: 'AmazonQ', // used across IDEs for identifying global storage/local disk locations. Do not change.
1717
suppressPromptPrefix: 'amazonQ',
1818
path: undefined,

packages/core/src/codewhisperer/util/editorContext.ts

+18
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { checkLeftContextKeywordsForJson } from './commonUtil'
1818
import { CodeWhispererSupplementalContext } from '../models/model'
1919
import { getOptOutPreference } from '../../shared/telemetry/util'
2020
import { indent } from '../../shared/utilities/textUtilities'
21+
import { isInDirectory } from '../../shared'
2122
import { AuthUtil } from './authUtil'
2223

2324
let tabSize: number = getTabSizeSetting()
@@ -83,6 +84,22 @@ export function getFileRelativePath(editor: vscode.TextEditor): string {
8384
return relativePath.substring(0, CodeWhispererConstants.filenameCharsLimit)
8485
}
8586

87+
async function getWorkspaceId(editor: vscode.TextEditor): Promise<string | undefined> {
88+
try {
89+
const workspaceIds: { workspaces: { workspaceRoot: string; workspaceId: string }[] } =
90+
await vscode.commands.executeCommand('aws.amazonq.getWorkspaceId')
91+
for (const item of workspaceIds.workspaces) {
92+
const path = vscode.Uri.parse(item.workspaceRoot).fsPath
93+
if (isInDirectory(path, editor.document.uri.fsPath)) {
94+
return item.workspaceId
95+
}
96+
}
97+
} catch (err) {
98+
getLogger().warn(`No workspace id found ${err}`)
99+
}
100+
return undefined
101+
}
102+
86103
export async function buildListRecommendationRequest(
87104
editor: vscode.TextEditor,
88105
nextToken: string,
@@ -121,6 +138,7 @@ export async function buildListRecommendationRequest(
121138
supplementalContexts: supplementalContext,
122139
customizationArn: selectedCustomization.arn === '' ? undefined : selectedCustomization.arn,
123140
optOutPreference: getOptOutPreference(),
141+
workspaceId: await getWorkspaceId(editor),
124142
profileArn: profile?.arn,
125143
},
126144
supplementalMetadata: supplementalContexts,

0 commit comments

Comments
 (0)