From 39f4bc445d7cf5559456816686c3582d39fca10c Mon Sep 17 00:00:00 2001 From: Wilson Huang Date: Fri, 25 Oct 2024 11:46:12 -0400 Subject: [PATCH] feat(feature dev): Add setting to opt in/out of QSDA auto build feature --- ...-10418729-4b46-4126-b1af-623a08f0223c.json | 4 ++ packages/amazonq/package.json | 5 +++ .../amazonqFeatureDev/session/session.test.ts | 3 ++ packages/core/package.nls.json | 1 + .../controllers/chat/controller.ts | 19 ++++++++- .../src/amazonqFeatureDev/session/session.ts | 41 ++++++++++++++++++- packages/core/src/amazonqFeatureDev/types.ts | 2 + .../core/src/amazonqFeatureDev/util/files.ts | 8 +++- .../util/codewhispererSettings.ts | 13 ++++++ packages/core/src/shared/filetypes.ts | 8 ++++ .../core/src/shared/settings-amazonq.gen.ts | 1 + 11 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 packages/amazonq/.changes/next-release/Feature-10418729-4b46-4126-b1af-623a08f0223c.json diff --git a/packages/amazonq/.changes/next-release/Feature-10418729-4b46-4126-b1af-623a08f0223c.json b/packages/amazonq/.changes/next-release/Feature-10418729-4b46-4126-b1af-623a08f0223c.json new file mode 100644 index 00000000000..ad3575896ff --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-10418729-4b46-4126-b1af-623a08f0223c.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Amazon Q Feature Dev: Add new setting to opt in/out auto build feature" +} diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index fbb68da3fde..bd3e0f0f2ac 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -128,6 +128,11 @@ "markdownDescription": "%AWS.configuration.description.amazonq%", "default": true }, + "amazonQ.autoBuildFeatureProjects": { + "type": "object", + "markdownDescription": "%AWS.configuration.description.autoBuildFeatureProjects%", + "default": {} + }, "amazonQ.importRecommendationForInlineCodeSuggestions": { "type": "boolean", "description": "%AWS.configuration.description.amazonq.importRecommendation%", diff --git a/packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts b/packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts index f2a08348d23..a3f1470b7ca 100644 --- a/packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts +++ b/packages/amazonq/test/unit/amazonqFeatureDev/session/session.test.ts @@ -17,10 +17,12 @@ import { sessionRegisterProvider, sessionWriteFile, assertTelemetry, + getTestWorkspaceFolder, } from 'aws-core-vscode/test' import { CurrentWsFolders, CodeGenState, FeatureDevClient, Messenger } from 'aws-core-vscode/amazonqFeatureDev' import path from 'path' import { fs } from 'aws-core-vscode/shared' +import { CodeWhispererSettings } from 'aws-core-vscode/codewhisperer' describe('session', () => { const conversationID = '12345' @@ -36,6 +38,7 @@ describe('session', () => { describe('preloader', () => { it('emits start chat telemetry', async () => { + await CodeWhispererSettings.instance.updateToAutoBuildFeatureProjects(getTestWorkspaceFolder(), false) const session = await createSession({ messenger, conversationID }) await session.preloader('implement twosum in typescript') diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 0f334478532..a003f61fa12 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -21,6 +21,7 @@ "AWS.configuration.enableCodeLenses": "Enable SAM hints in source code and template.yaml files", "AWS.configuration.description.resources.enabledResources": "AWS resources to display in the 'Resources' portion of the explorer.", "AWS.configuration.description.experiments": "Try experimental features and give feedback. Note that experimental features may be removed at any time.\n * `jsonResourceModification` - Enables basic create, update, and delete support for cloud resources via the JSON Resources explorer component.\n * `samSyncCode` - Adds an additional code-only option when synchronizing SAM applications. Code-only synchronizations are faster but can cause drift in the CloudFormation stack. Does nothing when using the legacy SAM deploy feature.\n * `iamPolicyChecks` - Enables IAM Policy Checks feature, allowing users to validate IAM policies against IAM policy grammar, AWS best practices, and specified security standards.", + "AWS.configuration.description.autoBuildFeatureProjects": "Projects with auto build feature enabled or disabled", "AWS.stepFunctions.asl.format.enable.desc": "Enables the default formatter used with Amazon States Language files", "AWS.stepFunctions.asl.maxItemsComputed.desc": "The maximum number of outline symbols and folding regions computed (limited for performance reasons).", "AWS.configuration.description.awssam.debug.api": "API Gateway configuration", diff --git a/packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts b/packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts index aa300b9ddba..9a700a51f25 100644 --- a/packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts +++ b/packages/core/src/amazonqFeatureDev/controllers/chat/controller.ts @@ -46,6 +46,7 @@ import { getWorkspaceFoldersByPrefixes } from '../../../shared/utilities/workspa import { openDeletedDiff, openDiff } from '../../../amazonq/commons/diff' import { i18n } from '../../../shared/i18n-helper' import globals from '../../../shared/extensionGlobals' +import { CodeWhispererSettings } from '../../../codewhisperer' export const TotalSteps = 3 @@ -122,7 +123,7 @@ export class FeatureDevController { getLogger().error('processChatItemFeedbackMessage failed: %s', (e as Error).message) }) }) - this.chatControllerMessageListeners.followUpClicked.event((data) => { + this.chatControllerMessageListeners.followUpClicked.event(async (data) => { switch (data.followUp.type) { case FollowUpTypes.InsertCode: return this.insertCode(data) @@ -147,6 +148,12 @@ export class FeatureDevController { case FollowUpTypes.SendFeedback: this.sendFeedback() break + case FollowUpTypes.AcceptAutoBuild: + await this.processAutoBuildSetting(true) + return this.retryRequest(data) + case FollowUpTypes.DenyAutoBuild: + await this.processAutoBuildSetting(false) + return this.retryRequest(data) } }) this.chatControllerMessageListeners.openDiff.event((data) => { @@ -537,6 +544,16 @@ export class FeatureDevController { i18n('AWS.amazonq.featureDev.placeholder.additionalImprovements') ) } + + private async processAutoBuildSetting(setting: boolean) { + const workspaceFolders = vscode.workspace.workspaceFolders + const workspaceRoots = workspaceFolders?.map((f) => f.uri.fsPath) + + if (workspaceRoots) { + await CodeWhispererSettings.instance.updateToAutoBuildFeatureProjects(workspaceRoots[0], setting) + } + } + // TODO add type private async insertCode(message: any) { let session diff --git a/packages/core/src/amazonqFeatureDev/session/session.ts b/packages/core/src/amazonqFeatureDev/session/session.ts index 204e974eee0..2aa323ea70d 100644 --- a/packages/core/src/amazonqFeatureDev/session/session.ts +++ b/packages/core/src/amazonqFeatureDev/session/session.ts @@ -7,6 +7,7 @@ import * as path from 'path' import { ConversationNotStartedState, PrepareCodeGenState } from './sessionState' import { + FollowUpTypes, type DeletedFileInfo, type Interaction, type NewFileInfo, @@ -26,6 +27,8 @@ import { ReferenceLogViewProvider } from '../../codewhisperer/service/referenceL import { AuthUtil } from '../../codewhisperer/util/authUtil' import { getLogger } from '../../shared' import { logWithConversationId } from '../userFacingText' +import { CodeWhispererSettings } from '../../codewhisperer' + export class Session { private _state?: SessionState | Omit private task: string = '' @@ -59,14 +62,50 @@ export class Session { * Preload any events that have to run before a chat message can be sent */ async preloader(msg: string) { - if (!this.preloaderFinished) { + const root = this.config.workspaceRoots[0] + const projects = CodeWhispererSettings.instance.getAutoBuildFeatureProjects() + + if (Object.keys(projects).includes(root) && !this.preloaderFinished) { await this.setupConversation(msg) this.preloaderFinished = true this.messenger.sendAsyncEventProgress(this.tabID, true, undefined) await this.proxyClient.sendFeatureDevTelemetryEvent(this.conversationId) // send the event only once per conversation. + } else { + await this.promptUserConsent() } } + /** + * + */ + + private async promptUserConsent() { + this.messenger.sendAnswer({ + tabID: this.tabID, + message: + 'Would you like to use the auto build feature that will allow Amazon Q feature development agent to build and test your project?', + type: 'answer', + }) + + this.messenger.sendAnswer({ + message: undefined, + type: 'system-prompt', + followUps: [ + { + pillText: 'Accept for this project', + type: FollowUpTypes.AcceptAutoBuild, + status: 'info', + }, + { + pillText: 'Deny for this project', + type: FollowUpTypes.DenyAutoBuild, + status: 'info', + }, + ], + tabID: this.tabID, + }) + } + /** * setupConversation * diff --git a/packages/core/src/amazonqFeatureDev/types.ts b/packages/core/src/amazonqFeatureDev/types.ts index cf046743425..3f9391c719e 100644 --- a/packages/core/src/amazonqFeatureDev/types.ts +++ b/packages/core/src/amazonqFeatureDev/types.ts @@ -49,6 +49,8 @@ export enum FollowUpTypes { NewTask = 'NewTask', CloseSession = 'CloseSession', SendFeedback = 'SendFeedback', + AcceptAutoBuild = 'AcceptAutoBuild', + DenyAutoBuild = 'DenyAutoBuild', } export type SessionStatePhase = DevPhase.INIT | DevPhase.CODEGEN diff --git a/packages/core/src/amazonqFeatureDev/util/files.ts b/packages/core/src/amazonqFeatureDev/util/files.ts index 1b83bdbe2b5..d7a089e309a 100644 --- a/packages/core/src/amazonqFeatureDev/util/files.ts +++ b/packages/core/src/amazonqFeatureDev/util/files.ts @@ -19,6 +19,7 @@ import { TelemetryHelper } from './telemetryHelper' import { maxRepoSizeBytes } from '../constants' import { isCodeFile } from '../../shared/filetypes' import { fs } from '../../shared' +import { CodeWhispererSettings } from '../../codewhisperer' const getSha256 = (file: Buffer) => createHash('sha256').update(file).digest('base64') @@ -34,6 +35,9 @@ export async function prepareRepoData( ) { try { const files = await collectFiles(repoRootPaths, workspaceFolders, true, maxRepoSizeBytes) + const zip = new AdmZip() + const autoBuildFeatureProjects = CodeWhispererSettings.instance.getAutoBuildFeatureProjects() + const useAutoBuildFeature = autoBuildFeatureProjects[repoRootPaths[0]] ?? false let totalBytes = 0 const ignoredExtensionMap = new Map() @@ -41,8 +45,10 @@ export async function prepareRepoData( for (const file of files) { const fileSize = (await fs.stat(file.fileUri)).size const isCodeFile_ = isCodeFile(file.relativeFilePath) + // exclude user's devfile if `useAutoBuildFeature` is set to false + const excludeDevFile = useAutoBuildFeature ? false : file.relativeFilePath === 'devfile.yaml' - if (fileSize >= maxFileSizeBytes || !isCodeFile_) { + if (fileSize >= maxFileSizeBytes || !isCodeFile_ || excludeDevFile) { if (!isCodeFile_) { const re = /(?:\.([^.]+))?$/ const extensionArray = re.exec(file.relativeFilePath) diff --git a/packages/core/src/codewhisperer/util/codewhispererSettings.ts b/packages/core/src/codewhisperer/util/codewhispererSettings.ts index 09c7e2657bd..cc84766227d 100644 --- a/packages/core/src/codewhisperer/util/codewhispererSettings.ts +++ b/packages/core/src/codewhisperer/util/codewhispererSettings.ts @@ -12,6 +12,7 @@ const description = { workspaceIndexWorkerThreads: Number, workspaceIndexUseGPU: Boolean, workspaceIndexMaxSize: Number, + autoBuildFeatureProjects: Object, } export class CodeWhispererSettings extends fromExtensionManifest('amazonQ', description) { @@ -64,6 +65,18 @@ export class CodeWhispererSettings extends fromExtensionManifest('amazonQ', desc return Math.max(this.get('workspaceIndexMaxSize', 250), 1) } + public getAutoBuildFeatureProjects(): { [key: string]: boolean } { + return this.get('autoBuildFeatureProjects', {}) + } + + public async updateToAutoBuildFeatureProjects(projectName: string, setting: boolean) { + const projects = this.getAutoBuildFeatureProjects() + + projects[projectName] = setting + + await this.update('autoBuildFeatureProjects', projects) + } + static #instance: CodeWhispererSettings public static get instance() { diff --git a/packages/core/src/shared/filetypes.ts b/packages/core/src/shared/filetypes.ts index 446cabe0a2a..2a2c508e987 100644 --- a/packages/core/src/shared/filetypes.ts +++ b/packages/core/src/shared/filetypes.ts @@ -165,6 +165,7 @@ export const codefileExtensions = new Set([ '.cbl', '.cc', '.cfc', + '.cfg', '.cfm', '.cjs', '.clj', @@ -175,6 +176,7 @@ export const codefileExtensions = new Set([ '.cob', '.cobra', '.coffee', + '.config', '.cpp', '.cpy', '.cr', @@ -189,6 +191,7 @@ export const codefileExtensions = new Set([ '.e', '.el', '.elm', + '.env', '.erl', '.ex', '.exs', @@ -204,6 +207,7 @@ export const codefileExtensions = new Set([ '.fsi', '.fsx', '.gd', + '.gitignore', '.go', '.gql', '.graphql', @@ -223,6 +227,7 @@ export const codefileExtensions = new Set([ '.html', '.hy', '.idl', + '.ini', '.io', '.jar', '.java', @@ -236,6 +241,7 @@ export const codefileExtensions = new Set([ '.lgt', '.lhs', '.lisp', + '.lock', '.logtalk', '.lsp', '.lua', @@ -324,10 +330,12 @@ export const codefileExtensions = new Set([ '.t', '.tcl', '.tf', + '.toml', '.trigger', '.ts', '.tsx', '.tu', + '.txt', '.v', '.vala', '.vapi', diff --git a/packages/core/src/shared/settings-amazonq.gen.ts b/packages/core/src/shared/settings-amazonq.gen.ts index 4fbe8ab0005..a43ac32593c 100644 --- a/packages/core/src/shared/settings-amazonq.gen.ts +++ b/packages/core/src/shared/settings-amazonq.gen.ts @@ -21,6 +21,7 @@ export const amazonqSettings = { "ssoCacheError": {} }, "amazonQ.showInlineCodeSuggestionsWithCodeReferences": {}, + "amazonQ.autoBuildFeatureProjects": {}, "amazonQ.importRecommendationForInlineCodeSuggestions": {}, "amazonQ.shareContentWithAWS": {}, "amazonQ.workspaceIndex": {},