Skip to content

Commit 3202b6e

Browse files
suprajavenSupraja Venkatesh
andauthored
feat: adding a check before invoking workspace context (#1227)
Co-authored-by: Supraja Venkatesh <[email protected]>
1 parent 823b199 commit 3202b6e

File tree

8 files changed

+224
-22
lines changed

8 files changed

+224
-22
lines changed

server/aws-lsp-codewhisperer/src/language-server/chat/chatController.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export class ChatController implements ChatHandlers {
8686
) {
8787
this.#features = features
8888
this.#chatSessionManagementService = chatSessionManagementService
89-
this.#triggerContext = new QChatTriggerContext(features.workspace, features.logging)
89+
this.#triggerContext = new QChatTriggerContext(features.workspace, features.logging, serviceManager)
9090
this.#telemetryController = new ChatTelemetryController(features, telemetryService)
9191
this.#telemetryService = telemetryService
9292
this.#serviceManager = serviceManager

server/aws-lsp-codewhisperer/src/language-server/chat/contexts/triggerContext.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DocumentContext, DocumentContextExtractor } from './documentContext'
66
import { SendMessageCommandInput } from '../../../shared/streamingClientService'
77
import { LocalProjectContextController } from '../../../shared/localProjectContextController'
88
import { convertChunksToRelevantTextDocuments } from '../tools/relevantTextDocuments'
9+
import { AmazonQBaseServiceManager as AmazonQServiceManager } from '../../../shared/amazonQServiceManager/BaseAmazonQServiceManager'
910

1011
export interface TriggerContext extends Partial<DocumentContext> {
1112
userIntent?: UserIntent
@@ -21,7 +22,11 @@ export class QChatTriggerContext {
2122
#documentContextExtractor: DocumentContextExtractor
2223
#logger: Features['logging']
2324

24-
constructor(workspace: Features['workspace'], logger: Features['logging']) {
25+
constructor(
26+
workspace: Features['workspace'],
27+
logger: Features['logging'],
28+
private amazonQServiceManager?: AmazonQServiceManager
29+
) {
2530
this.#workspace = workspace
2631
this.#documentContextExtractor = new DocumentContextExtractor({ logger, workspace })
2732
this.#logger = logger
@@ -120,6 +125,19 @@ export class QChatTriggerContext {
120125
async extractProjectContext(query?: string): Promise<RelevantTextDocument[]> {
121126
if (query) {
122127
try {
128+
let enableWorkspaceContext = true
129+
130+
if (this.amazonQServiceManager) {
131+
const config = this.amazonQServiceManager.getConfiguration()
132+
if (config.projectContext?.enableLocalIndexing === false) {
133+
enableWorkspaceContext = false
134+
}
135+
}
136+
137+
if (!enableWorkspaceContext) {
138+
this.#logger.debug('Workspace context is disabled, skipping project context extraction')
139+
return []
140+
}
123141
const contextController = await LocalProjectContextController.getInstance()
124142
const resp = await contextController.queryVectorIndex({ query })
125143
return convertChunksToRelevantTextDocuments(resp)

server/aws-lsp-codewhisperer/src/language-server/chat/contexts/triggerContexts.test.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import assert = require('assert')
44
import { TextDocument } from 'vscode-languageserver-textdocument'
55
import { DocumentContext, DocumentContextExtractor } from './documentContext'
66
import sinon = require('sinon')
7+
import { LocalProjectContextController } from '../../../shared/localProjectContextController'
78

89
describe('QChatTriggerContext', () => {
910
let testFeatures: TestFeatures
11+
let amazonQServiceManager: any
1012

1113
const filePath = 'file://test.ts'
1214
const mockTSDocument = TextDocument.create(filePath, 'typescript', 1, '')
@@ -20,6 +22,13 @@ describe('QChatTriggerContext', () => {
2022

2123
beforeEach(() => {
2224
testFeatures = new TestFeatures()
25+
amazonQServiceManager = {
26+
getConfiguration: sinon.stub().returns({
27+
projectContext: {
28+
enableLocalIndexing: true,
29+
},
30+
}),
31+
}
2332
sinon.stub(DocumentContextExtractor.prototype, 'extractDocumentContext').resolves(mockDocumentContext)
2433
})
2534

@@ -28,7 +37,11 @@ describe('QChatTriggerContext', () => {
2837
})
2938

3039
it('returns null if text document is not defined in params', async () => {
31-
const triggerContext = new QChatTriggerContext(testFeatures.workspace, testFeatures.logging)
40+
const triggerContext = new QChatTriggerContext(
41+
testFeatures.workspace,
42+
testFeatures.logging,
43+
amazonQServiceManager
44+
)
3245

3346
const documentContext = await triggerContext.extractDocumentContext({
3447
cursorState: [
@@ -46,7 +59,11 @@ describe('QChatTriggerContext', () => {
4659
})
4760

4861
it('returns null if text document is not found', async () => {
49-
const triggerContext = new QChatTriggerContext(testFeatures.workspace, testFeatures.logging)
62+
const triggerContext = new QChatTriggerContext(
63+
testFeatures.workspace,
64+
testFeatures.logging,
65+
amazonQServiceManager
66+
)
5067

5168
const documentContext = await triggerContext.extractDocumentContext({
5269
cursorState: [
@@ -66,7 +83,11 @@ describe('QChatTriggerContext', () => {
6683
})
6784

6885
it('passes default cursor state if no cursor is found', async () => {
69-
const triggerContext = new QChatTriggerContext(testFeatures.workspace, testFeatures.logging)
86+
const triggerContext = new QChatTriggerContext(
87+
testFeatures.workspace,
88+
testFeatures.logging,
89+
amazonQServiceManager
90+
)
7091

7192
const documentContext = await triggerContext.extractDocumentContext({
7293
cursorState: [],
@@ -79,7 +100,11 @@ describe('QChatTriggerContext', () => {
79100
})
80101

81102
it('includes cursor state from the parameters and text document if found', async () => {
82-
const triggerContext = new QChatTriggerContext(testFeatures.workspace, testFeatures.logging)
103+
const triggerContext = new QChatTriggerContext(
104+
testFeatures.workspace,
105+
testFeatures.logging,
106+
amazonQServiceManager
107+
)
83108

84109
testFeatures.openDocument(mockTSDocument)
85110
const documentContext = await triggerContext.extractDocumentContext({
@@ -91,4 +116,26 @@ describe('QChatTriggerContext', () => {
91116

92117
assert.deepStrictEqual(documentContext, mockDocumentContext)
93118
})
119+
120+
it('should not extract project context when workspace context is disabled', async () => {
121+
amazonQServiceManager.getConfiguration.returns({
122+
projectContext: {
123+
enableLocalIndexing: false,
124+
},
125+
})
126+
127+
const triggerContext = new QChatTriggerContext(
128+
testFeatures.workspace,
129+
testFeatures.logging,
130+
amazonQServiceManager
131+
)
132+
133+
const getInstanceStub = sinon.stub(LocalProjectContextController, 'getInstance')
134+
135+
const result = await triggerContext.extractProjectContext('test query')
136+
137+
sinon.assert.notCalled(getInstanceStub)
138+
139+
assert.deepStrictEqual(result, [])
140+
})
94141
})

server/aws-lsp-codewhisperer/src/language-server/inline-completion/codeWhispererServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ export const CodewhispererServerFactory =
336336
// supplementalContext available only via token authentication
337337
const supplementalContextPromise =
338338
codeWhispererService instanceof CodeWhispererServiceToken
339-
? fetchSupplementalContext(textDocument, params.position, workspace, logging, token)
339+
? fetchSupplementalContext(textDocument, params.position, workspace, logging, token, amazonQServiceManager)
340340
: Promise.resolve(undefined)
341341

342342
let requestContext: GenerateSuggestionsRequest = {

server/aws-lsp-codewhisperer/src/shared/supplementalContextUtil/crossFileContextUtil.test.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,16 @@ describe('crossFileContextUtil', function () {
249249
})
250250
describe('codemapContext', () => {
251251
let sandbox: sinon.SinonSandbox
252+
let amazonQServiceManager: any
252253
beforeEach(() => {
253254
sandbox = sinon.createSandbox()
255+
amazonQServiceManager = {
256+
getConfiguration: sandbox.stub().returns({
257+
projectContext: {
258+
enableLocalIndexing: true,
259+
},
260+
}),
261+
}
254262
})
255263
afterEach(() => {
256264
sandbox.restore()
@@ -271,13 +279,47 @@ describe('crossFileContextUtil', function () {
271279
document,
272280
{ line: 0, character: 0 },
273281
features.workspace,
274-
fakeCancellationToken
282+
fakeCancellationToken,
283+
amazonQServiceManager
284+
)
285+
assert.deepStrictEqual(result, {
286+
supplementalContextItems: [],
287+
strategy: 'Empty',
288+
})
289+
})
290+
291+
it('should return Empty strategy when workspace context is disabled', async () => {
292+
const document = TextDocument.create(
293+
'file:///testfile.java',
294+
'java',
295+
1,
296+
'line_1\nline_2\nline_3\nline_4\nline_5\nline_6\nline_7'
275297
)
298+
299+
amazonQServiceManager.getConfiguration.returns({
300+
projectContext: {
301+
enableLocalIndexing: false,
302+
},
303+
})
304+
305+
const instanceStub = sandbox.stub(LocalProjectContextController, 'getInstance')
306+
307+
const result = await crossFile.codemapContext(
308+
document,
309+
{ line: 0, character: 0 },
310+
features.workspace,
311+
fakeCancellationToken,
312+
amazonQServiceManager
313+
)
314+
315+
sinon.assert.notCalled(instanceStub)
316+
276317
assert.deepStrictEqual(result, {
277318
supplementalContextItems: [],
278319
strategy: 'Empty',
279320
})
280321
})
322+
281323
it('should return codemap strategy when project context exists', async () => {
282324
const document = TextDocument.create(
283325
'file:///testfile.java',
@@ -296,7 +338,8 @@ describe('crossFileContextUtil', function () {
296338
document,
297339
{ line: 0, character: 0 },
298340
features.workspace,
299-
fakeCancellationToken
341+
fakeCancellationToken,
342+
amazonQServiceManager
300343
)
301344
assert.deepStrictEqual(result, {
302345
supplementalContextItems: [{ content: 'someOtherContet', filePath: '/path/', score: 29.879 }],
@@ -321,7 +364,8 @@ describe('crossFileContextUtil', function () {
321364
document,
322365
{ line: 0, character: 0 },
323366
features.workspace,
324-
fakeCancellationToken
367+
fakeCancellationToken,
368+
amazonQServiceManager
325369
)
326370
assert.deepStrictEqual(result, {
327371
supplementalContextItems: [

server/aws-lsp-codewhisperer/src/shared/supplementalContextUtil/crossFileContextUtil.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { LocalProjectContextController } from '../../shared/localProjectContextC
2929
import { QueryInlineProjectContextRequestV2 } from 'local-indexing'
3030
import { URI } from 'vscode-uri'
3131
import { waitUntil } from '@aws/lsp-core/out/util/timeoutUtils'
32+
import { AmazonQBaseServiceManager } from '../amazonQServiceManager/BaseAmazonQServiceManager'
3233

3334
type CrossFileSupportedLanguage =
3435
| 'java'
@@ -65,7 +66,8 @@ export async function fetchSupplementalContextForSrc(
6566
document: TextDocument,
6667
position: Position,
6768
workspace: Workspace,
68-
cancellationToken: CancellationToken
69+
cancellationToken: CancellationToken,
70+
amazonQServiceManager?: AmazonQBaseServiceManager
6971
): Promise<Pick<CodeWhispererSupplementalContext, 'supplementalContextItems' | 'strategy'> | undefined> {
7072
const supplementalContextConfig = getSupplementalContextConfig(document.languageId)
7173

@@ -74,15 +76,17 @@ export async function fetchSupplementalContextForSrc(
7476
}
7577
//TODO: add logic for other strategies once available
7678
if (supplementalContextConfig === 'codemap') {
77-
return await codemapContext(document, position, workspace, cancellationToken)
79+
return await codemapContext(document, position, workspace, cancellationToken, amazonQServiceManager)
7880
}
81+
return { supplementalContextItems: [], strategy: 'Empty' }
7982
}
8083

8184
export async function codemapContext(
8285
document: TextDocument,
8386
position: Position,
8487
workspace: Workspace,
85-
cancellationToken: CancellationToken
88+
cancellationToken: CancellationToken,
89+
amazonQServiceManager?: AmazonQBaseServiceManager
8690
): Promise<Pick<CodeWhispererSupplementalContext, 'supplementalContextItems' | 'strategy'> | undefined> {
8791
let strategy: SupplementalContextStrategy = 'Empty'
8892

@@ -95,7 +99,7 @@ export async function codemapContext(
9599

96100
const projectContextPromise = waitUntil(
97101
async function () {
98-
return await fetchProjectContext(document, position, 'codemap')
102+
return await fetchProjectContext(document, position, 'codemap', amazonQServiceManager)
99103
},
100104
{ timeout: supplementalContextTimeoutInMs, interval: 5, truthy: false }
101105
)
@@ -133,7 +137,8 @@ export async function codemapContext(
133137
export async function fetchProjectContext(
134138
document: TextDocument,
135139
position: Position,
136-
target: 'default' | 'codemap' | 'bm25'
140+
target: 'default' | 'codemap' | 'bm25',
141+
amazonQServiceManager?: AmazonQBaseServiceManager
137142
): Promise<CodeWhispererSupplementalContextItem[]> {
138143
const inputChunk: Chunk = getInputChunk(document, position, crossFileContextConfig.numberOfLinesEachChunk)
139144
const fsPath = URI.parse(document.uri).fsPath
@@ -143,6 +148,17 @@ export async function fetchProjectContext(
143148
filePath: fsPath,
144149
target,
145150
}
151+
let enableWorkspaceContext = true
152+
153+
if (amazonQServiceManager) {
154+
const config = amazonQServiceManager.getConfiguration()
155+
if (config.projectContext?.enableLocalIndexing === false) {
156+
enableWorkspaceContext = false
157+
}
158+
}
159+
if (!enableWorkspaceContext) {
160+
return []
161+
}
146162

147163
try {
148164
controller = await LocalProjectContextController.getInstance()

0 commit comments

Comments
 (0)