Skip to content

Commit 0ed44a2

Browse files
authored
internal data collection for chat workspace (#5330)
## Problem The relevancy of workspace context retrieved from the LSP has unknown quality because of lack of data. ## Solution As required, by default enable workspace index for a fraction of amzn users and always send relevantDocument to the service. There is no customer facing changes. This change is purely for data collection and will be removed soon. To only apply this to a fraction of amzn users, we rely on the listFeatureEvaluations API in the backend. Tested with builder id login, amzn login, with and without @workspace.
1 parent 94f9f05 commit 0ed44a2

File tree

8 files changed

+52
-6
lines changed

8 files changed

+52
-6
lines changed

packages/core/src/codewhisperer/service/featureConfigProvider.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { codeWhispererClient as client } from '../client/codewhisperer'
88
import { AuthUtil } from '../util/authUtil'
99
import { getLogger } from '../../shared/logger'
1010
import { isBuilderIdConnection, isIdcSsoConnection } from '../../auth/connection'
11+
import { CodeWhispererSettings } from '../util/codewhispererSettings'
12+
import globals from '../../shared/extensionGlobals'
1113

1214
export class FeatureContext {
1315
constructor(
@@ -20,6 +22,7 @@ export class FeatureContext {
2022
const testFeatureName = 'testFeature'
2123
const customizationArnOverrideName = 'customizationArnOverride'
2224
const featureConfigPollIntervalInMs = 30 * 60 * 1000 // 30 mins
25+
const dataCollectionFeatureName = 'IDEProjectContextDataCollection'
2326

2427
// TODO: add real feature later
2528
export const featureDefinitions = new Map([
@@ -35,6 +38,8 @@ export class FeatureConfigProvider {
3538

3639
static #instance: FeatureConfigProvider
3740

41+
private _isDataCollectionGroup = false
42+
3843
constructor() {
3944
this.fetchFeatureConfigs().catch((e) => {
4045
getLogger().error('fetchFeatureConfigs failed: %s', (e as Error).message)
@@ -47,6 +52,10 @@ export class FeatureConfigProvider {
4752
return (this.#instance ??= new this())
4853
}
4954

55+
isAmznDataCollectionGroup(): boolean {
56+
return this._isDataCollectionGroup
57+
}
58+
5059
async fetchFeatureConfigs(): Promise<void> {
5160
if (AuthUtil.instance.isConnectionExpired()) {
5261
return
@@ -63,7 +72,6 @@ export class FeatureConfigProvider {
6372
new FeatureContext(evaluation.feature, evaluation.variation, evaluation.value)
6473
)
6574
})
66-
6775
const customizationArnOverride = this.featureConfigs.get(customizationArnOverrideName)?.value?.stringValue
6876
if (customizationArnOverride !== undefined) {
6977
// Double check if server-side wrongly returns a customizationArn to BID users
@@ -96,6 +104,17 @@ export class FeatureConfigProvider {
96104
}
97105
}
98106
}
107+
108+
const dataCollectionValue = this.featureConfigs.get(dataCollectionFeatureName)?.value.stringValue
109+
if (dataCollectionValue === 'data-collection') {
110+
this._isDataCollectionGroup = true
111+
// Enable local workspace index by default, for Amzn users.
112+
const isSet = globals.globalState.get<boolean>('aws.amazonq.workspaceIndexToggleOn') || false
113+
if (!isSet) {
114+
await CodeWhispererSettings.instance.enableLocalIndex()
115+
globals.globalState.tryUpdate('aws.amazonq.workspaceIndexToggleOn', true)
116+
}
117+
}
99118
} catch (e) {
100119
getLogger().error(`CodeWhisperer: Error when fetching feature configs ${e}`, e)
101120
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ export class CodeWhispererSettings extends fromExtensionManifest('amazonQ', desc
4646
return this.get('workspaceIndex', false)
4747
}
4848

49+
public async enableLocalIndex() {
50+
await this.update('workspaceIndex', true)
51+
}
52+
4953
public isLocalIndexGPUEnabled(): boolean {
5054
return this.get('workspaceIndexUseGPU', false)
5155
}

packages/core/src/codewhispererChat/controllers/chat/chatRequest/converter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): Gen
9595
const relevantDocuments: RelevantTextDocument[] = triggerPayload.relevantTextDocuments
9696
? triggerPayload.relevantTextDocuments
9797
: []
98+
const useRelevantDocuments = triggerPayload.useRelevantDocuments
9899
// service will throw validation exception if string is empty
99100
const customizationArn: string | undefined = undefinedIfEmpty(triggerPayload.customization.arn)
100101

@@ -110,6 +111,7 @@ export function triggerPayloadToChatRequest(triggerPayload: TriggerPayload): Gen
110111
document,
111112
cursorState,
112113
relevantDocuments,
114+
useRelevantDocuments,
113115
},
114116
},
115117
userIntent: triggerPayload.userIntent,

packages/core/src/codewhispererChat/controllers/chat/controller.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { randomUUID } from '../../../shared/crypto'
4747
import { LspController } from '../../../amazonq/lsp/lspController'
4848
import { CodeWhispererSettings } from '../../../codewhisperer/util/codewhispererSettings'
4949
import { getSelectedCustomization } from '../../../codewhisperer/util/customizationUtil'
50+
import { FeatureConfigProvider } from '../../../codewhisperer/service/featureConfigProvider'
5051
import { getHttpStatusCode, getReasonFromSyntaxError } from '../../../shared/errors'
5152

5253
export interface ChatControllerMessagePublishers {
@@ -557,9 +558,10 @@ export class ChatController {
557558
await this.messenger.sendAuthNeededExceptionMessage(credentialsState, tabID, triggerID)
558559
return
559560
}
561+
triggerPayload.useRelevantDocuments = false
560562
if (triggerPayload.message) {
561-
const userIntentEnableProjectContext = triggerPayload.message.includes(`@workspace`)
562-
if (userIntentEnableProjectContext) {
563+
triggerPayload.useRelevantDocuments = triggerPayload.message.includes(`@workspace`)
564+
if (triggerPayload.useRelevantDocuments) {
563565
triggerPayload.message = triggerPayload.message.replace(/@workspace/g, '')
564566
if (CodeWhispererSettings.instance.isLocalIndexEnabled()) {
565567
const start = performance.now()
@@ -575,6 +577,19 @@ export class ChatController {
575577
return
576578
}
577579
}
580+
// if user does not have @workspace in the prompt, but user is in the data collection group
581+
// If the user is in the data collection group but turned off local index to opt-out, do not collect data.
582+
// TODO: Remove this entire block of code in one month as requested
583+
else if (
584+
FeatureConfigProvider.instance.isAmznDataCollectionGroup() &&
585+
!LspController.instance.isIndexingInProgress() &&
586+
CodeWhispererSettings.instance.isLocalIndexEnabled()
587+
) {
588+
getLogger().info(`amazonq: User is in data collection group`)
589+
const start = performance.now()
590+
triggerPayload.relevantTextDocuments = await LspController.instance.query(triggerPayload.message)
591+
triggerPayload.projectContextQueryLatencyMs = performance.now() - start
592+
}
578593
}
579594

580595
const request = triggerPayloadToChatRequest(triggerPayload)

packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ export class Messenger {
128128
)
129129
}
130130
this.telemetryHelper.setResponseStreamStartTime(tabID)
131-
if (triggerPayload.relevantTextDocuments && triggerPayload.relevantTextDocuments.length > 0) {
131+
if (
132+
triggerPayload.relevantTextDocuments &&
133+
triggerPayload.relevantTextDocuments.length > 0 &&
134+
triggerPayload.useRelevantDocuments === true
135+
) {
132136
this.telemetryHelper.setResponseFromProjectContext(messageID)
133137
}
134138

packages/core/src/codewhispererChat/controllers/chat/model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export interface TriggerPayload {
142142
readonly userIntent: UserIntent | undefined
143143
readonly customization: Customization
144144
relevantTextDocuments?: RelevantTextDocument[]
145+
useRelevantDocuments?: boolean
145146
}
146147

147148
export interface InsertedCode {

packages/core/src/codewhispererChat/controllers/chat/telemetryHelper.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export class CWCTelemetryHelper {
324324
cwsprChatProgrammingLanguage: triggerPayload.fileLanguage,
325325
credentialStartUrl: AuthUtil.instance.startUrl,
326326
cwsprChatHasProjectContext: triggerPayload.relevantTextDocuments
327-
? triggerPayload.relevantTextDocuments.length > 0
327+
? triggerPayload.relevantTextDocuments.length > 0 && triggerPayload.useRelevantDocuments === true
328328
: false,
329329
cwsprChatProjectContextQueryMs: triggerPayload.projectContextQueryLatencyMs,
330330
})
@@ -357,7 +357,7 @@ export class CWCTelemetryHelper {
357357
credentialStartUrl: AuthUtil.instance.startUrl,
358358
codewhispererCustomizationArn: triggerPayload.customization.arn,
359359
cwsprChatHasProjectContext: triggerPayload.relevantTextDocuments
360-
? triggerPayload.relevantTextDocuments.length > 0
360+
? triggerPayload.relevantTextDocuments.length > 0 && triggerPayload.useRelevantDocuments === true
361361
: false,
362362
}
363363

packages/core/src/shared/globalState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type globalKey =
3737
| 'aws.redshift.connections'
3838
| 'aws.toolkit.amazonq.dismissed'
3939
| 'aws.toolkit.amazonqInstall.dismissed'
40+
| 'aws.amazonq.workspaceIndexToggleOn'
4041
| 'aws.toolkit.separationPromptCommand'
4142
| 'aws.toolkit.separationPromptDismissed'
4243
// Deprecated/legacy names. New keys should start with "aws.".

0 commit comments

Comments
 (0)