Skip to content

Commit c529794

Browse files
fix(dev): Q dev handle no change required (#5723)
Problem * Q dev won't be able to generate code for unrelated prompts. (e.g. explain the code) * It would be a bad CX if Q fail to generate code without guiding users the proper use cases. Solution * Handle no code change required explicitly. * Allow users to provide more details https://github.com/user-attachments/assets/cf593cea-cd4d-4b29-9142-d5810d46709c
1 parent 907754c commit c529794

File tree

7 files changed

+89
-10
lines changed

7 files changed

+89
-10
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Bug Fix",
3+
"description": "Q dev handle no change required"
4+
}

packages/core/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@
273273
"AWS.amazonq.featureDev.error.illegalStateTransition": "Illegal transition between states, restart the conversation",
274274
"AWS.amazonq.featureDev.error.prepareRepoFailedError": "Sorry, I ran into an issue while trying to upload your code. Please try again.",
275275
"AWS.amazonq.featureDev.error.promptRefusalException": "I'm sorry, I can't generate code for your request. Please make sure your message and code files comply with the <a href=\"https://aws.amazon.com/machine-learning/responsible-ai/policy/\">AWS Responsible AI Policy.</a>",
276+
"AWS.amazonq.featureDev.error.noChangeRequiredException": "I’m sorry, I ran into an issue while trying to generate your code.\n\n- `/dev` can generate code to make a change in your project. Provide a detailed description of the new feature or code changes you want to make, including the specifics of what the code should achieve.\n\n- To ask me to explain, debug, or optimize your code, you can close this chat tab to start a new conversation.",
276277
"AWS.amazonq.featureDev.error.zipFileError": "The zip file is corrupted",
277278
"AWS.amazonq.featureDev.error.codeIterationLimitError": "Sorry, you've reached the quota for number of iterations on code generation. You can insert this code in your files or discuss a new plan. For more information on quotas, see the <a href=\"https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/software-dev.html#quotas\" target=\"_blank\">Amazon Q Developer documentation.</a>",
278279
"AWS.amazonq.featureDev.error.tabIdNotFoundError": "I'm sorry, I'm having technical difficulties at the moment. Please try again.",

packages/core/src/amazonq/webview/ui/quickActions/generator.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ export class QuickActionGenerator {
2929
{
3030
command: '/dev',
3131
placeholder: 'Describe your task or issue in as much detail as possible',
32-
description:
33-
'Plan and implement new functionality across multiple files in your workspace.',
32+
description: 'Generate code to make a change in your project',
3433
},
3534
]
3635
: []),

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
ContentLengthError,
1515
FeatureDevServiceError,
1616
MonthlyConversationLimitError,
17+
NoChangeRequiredException,
1718
PrepareRepoFailedError,
1819
PromptRefusalException,
1920
SelectedFolderNotInWorkspaceFolderError,
@@ -131,6 +132,11 @@ export class FeatureDevController {
131132
this.initialExamples(data)
132133
break
133134
case FollowUpTypes.NewTask:
135+
this.messenger.sendAnswer({
136+
type: 'answer',
137+
tabID: data?.tabID,
138+
message: i18n('AWS.amazonq.featureDev.answer.newTaskChanges'),
139+
})
134140
return this.newTask(data)
135141
case FollowUpTypes.CloseSession:
136142
return this.closeSession(data)
@@ -246,6 +252,15 @@ export class FeatureDevController {
246252
case ZipFileError.errorName:
247253
this.messenger.sendErrorMessage(errorMessage, message.tabID, 0, session?.conversationIdUnsafe, true)
248254
break
255+
case NoChangeRequiredException.errorName:
256+
this.messenger.sendAnswer({
257+
type: 'answer',
258+
tabID: message.tabID,
259+
message: err.message,
260+
canBeVoted: true,
261+
})
262+
// Allow users to re-work the task description.
263+
return this.newTask(message)
249264
case CodeIterationLimitError.errorName:
250265
this.messenger.sendAnswer({
251266
type: 'answer',
@@ -319,7 +334,7 @@ export class FeatureDevController {
319334
await this.onCodeGeneration(session, message.message, message.tabID)
320335
}
321336
} catch (err: any) {
322-
this.processErrorChatMessage(err, message, session)
337+
await this.processErrorChatMessage(err, message, session)
323338
// Lock the chat input until they explicitly click one of the follow ups
324339
this.messenger.sendChatInputEnabled(message.tabID, false)
325340
}
@@ -758,11 +773,7 @@ export class FeatureDevController {
758773
// Re-run the opening flow, where we check auth + create a session
759774
await this.tabOpened(message)
760775

761-
this.messenger.sendAnswer({
762-
type: 'answer',
763-
tabID: message.tabID,
764-
message: i18n('AWS.amazonq.featureDev.answer.newTaskChanges'),
765-
})
776+
this.messenger.sendChatInputEnabled(message.tabID, true)
766777
this.messenger.sendUpdatePlaceholder(message.tabID, i18n('AWS.amazonq.featureDev.placeholder.describe'))
767778
}
768779

packages/core/src/amazonqFeatureDev/errors.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ export class PromptRefusalException extends ToolkitError {
6666
}
6767
}
6868

69+
export class NoChangeRequiredException extends ToolkitError {
70+
static errorName = 'NoChangeRequiredException'
71+
constructor() {
72+
super(i18n('AWS.amazonq.featureDev.error.noChangeRequiredException'), {
73+
code: 'NoChangeRequiredException',
74+
})
75+
}
76+
}
77+
6978
export class FeatureDevServiceError extends ToolkitError {
7079
static errorName = 'FeatureDevServiceError'
7180
constructor(message: string, code: string) {

packages/core/src/amazonqFeatureDev/session/sessionState.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ import { telemetry } from '../../shared/telemetry/telemetry'
1313
import { VirtualFileSystem } from '../../shared/virtualFilesystem'
1414
import { VirtualMemoryFile } from '../../shared/virtualMemoryFile'
1515
import { featureDevScheme } from '../constants'
16-
import { FeatureDevServiceError, IllegalStateTransition, PromptRefusalException } from '../errors'
16+
import {
17+
FeatureDevServiceError,
18+
IllegalStateTransition,
19+
NoChangeRequiredException,
20+
PromptRefusalException,
21+
} from '../errors'
1722
import {
1823
CodeGenerationStatus,
1924
CurrentWsFolders,
@@ -208,6 +213,9 @@ abstract class CodeGenBase {
208213
throw new PromptRefusalException()
209214
}
210215
case codegenResult.codeGenerationStatusDetail?.includes('EmptyPatch'): {
216+
if (codegenResult.codeGenerationStatusDetail?.includes('NO_CHANGE_REQUIRED')) {
217+
throw new NoChangeRequiredException()
218+
}
211219
throw new FeatureDevServiceError(
212220
i18n('AWS.amazonq.featureDev.error.codeGen.default'),
213221
'EmptyPatchException'

packages/core/src/test/amazonqFeatureDev/controllers/chat/controller.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ import { CurrentWsFolders, FollowUpTypes, NewFileInfo, DeletedFileInfo } from '.
1313
import { Session } from '../../../../amazonqFeatureDev/session/session'
1414
import { Prompter } from '../../../../shared/ui/prompter'
1515
import { assertTelemetry, toFile } from '../../../testUtil'
16-
import { SelectedFolderNotInWorkspaceFolderError } from '../../../../amazonqFeatureDev/errors'
16+
import {
17+
NoChangeRequiredException,
18+
SelectedFolderNotInWorkspaceFolderError,
19+
} from '../../../../amazonqFeatureDev/errors'
1720
import { CodeGenState, PrepareCodeGenState } from '../../../../amazonqFeatureDev/session/sessionState'
1821
import { FeatureDevClient } from '../../../../amazonqFeatureDev/client/featureDev'
1922
import { createAmazonQUri } from '../../../../amazonq/commons/diff'
23+
import { AuthUtil } from '../../../../codewhisperer'
2024

2125
let mockGetCodeGeneration: sinon.SinonStub
2226
describe('Controller', () => {
@@ -68,6 +72,12 @@ describe('Controller', () => {
6872
beforeEach(async () => {
6973
controllerSetup = await createController()
7074
session = await createSession({ messenger: controllerSetup.messenger, conversationID, tabID, uploadID })
75+
76+
sinon.stub(AuthUtil.instance, 'getChatAuthState').resolves({
77+
codewhispererCore: 'connected',
78+
codewhispererChat: 'connected',
79+
amazonQ: 'connected',
80+
})
7181
})
7282

7383
afterEach(() => {
@@ -357,4 +367,41 @@ describe('Controller', () => {
357367
})
358368
})
359369
})
370+
371+
describe('processUserChatMessage', function () {
372+
async function fireChatMessage() {
373+
const getSessionStub = sinon.stub(controllerSetup.sessionStorage, 'getSession').resolves(session)
374+
375+
controllerSetup.emitters.processHumanChatMessage.fire({
376+
tabID,
377+
conversationID,
378+
message: 'test message',
379+
})
380+
381+
// Wait until the controller has time to process the event
382+
await waitUntil(() => {
383+
return Promise.resolve(getSessionStub.callCount > 0)
384+
}, {})
385+
}
386+
387+
describe('processErrorChatMessage', function () {
388+
it('should handle NoChangeRequiredException', async function () {
389+
const noChangeRequiredException = new NoChangeRequiredException()
390+
sinon.stub(session, 'preloader').throws(noChangeRequiredException)
391+
const sendAnswerSpy = sinon.stub(controllerSetup.messenger, 'sendAnswer')
392+
393+
await fireChatMessage()
394+
395+
assert.strictEqual(
396+
sendAnswerSpy.calledWith({
397+
type: 'answer',
398+
tabID,
399+
message: noChangeRequiredException.message,
400+
canBeVoted: true,
401+
}),
402+
true
403+
)
404+
})
405+
})
406+
})
360407
})

0 commit comments

Comments
 (0)