Skip to content

Commit 860ef46

Browse files
authored
Merge branch 'feature/inline-rollback' into inline-rollback
2 parents 7af12e1 + 4537a07 commit 860ef46

File tree

4 files changed

+239
-3
lines changed

4 files changed

+239
-3
lines changed

packages/amazonq/src/app/inline/activation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import vscode from 'vscode'
77
import {
8+
acceptSuggestion,
89
AuthUtil,
910
CodeSuggestionsState,
1011
CodeWhispererCodeCoverageTracker,
@@ -61,6 +62,7 @@ export async function activate(languageClient: LanguageClient) {
6162
* Automated trigger
6263
*/
6364
globals.context.subscriptions.push(
65+
acceptSuggestion.register(globals.context),
6466
vscode.window.onDidChangeActiveTextEditor(async (editor) => {
6567
await RecommendationHandler.instance.onEditorChange()
6668
}),

packages/amazonq/test/unit/codewhisperer/util/telemetryHelper.test.ts

Lines changed: 235 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,40 @@
44
*/
55

66
import assert from 'assert'
7-
import { TelemetryHelper, session } from 'aws-core-vscode/codewhisperer'
7+
import { assertTelemetryCurried, resetCodeWhispererGlobalVariables } from 'aws-core-vscode/test'
8+
import { TelemetryHelper, Completion, session } from 'aws-core-vscode/codewhisperer'
9+
import {
10+
CodewhispererCompletionType,
11+
CodewhispererSuggestionState,
12+
CodewhispererUserDecision,
13+
} from 'aws-core-vscode/shared'
814
import sinon from 'sinon'
915

16+
// TODO: improve and move the following test utils to codewhisperer/testUtils.ts
17+
function aUserDecision(
18+
completionType: CodewhispererCompletionType,
19+
codewhispererSuggestionIndex: number,
20+
codewhispererSuggestionState: CodewhispererSuggestionState
21+
): CodewhispererUserDecision {
22+
return {
23+
codewhispererCompletionType: completionType,
24+
codewhispererLanguage: 'python',
25+
codewhispererRequestId: 'aFakeRequestId',
26+
codewhispererSessionId: 'aFakeSessionId',
27+
codewhispererSuggestionIndex: codewhispererSuggestionIndex,
28+
codewhispererSuggestionReferenceCount: 0,
29+
codewhispererSuggestionState: codewhispererSuggestionState,
30+
codewhispererTriggerType: 'OnDemand',
31+
credentialStartUrl: 'https://www.amazon.com',
32+
}
33+
}
34+
35+
function aCompletion(): Completion {
36+
return {
37+
content: 'aFakeContent',
38+
}
39+
}
40+
1041
describe('telemetryHelper', function () {
1142
describe('clientComponentLatency', function () {
1243
let sut: TelemetryHelper
@@ -48,4 +79,207 @@ describe('telemetryHelper', function () {
4879
assert.ok(resetStub.calledOnce)
4980
})
5081
})
82+
83+
describe('aggregateUserDecisionByRequest', function () {
84+
let sut: TelemetryHelper
85+
86+
beforeEach(function () {
87+
sut = new TelemetryHelper()
88+
})
89+
90+
it('should return Line and Accept', function () {
91+
const decisions: CodewhispererUserDecision[] = [
92+
aUserDecision('Line', 0, 'Accept'),
93+
aUserDecision('Line', 1, 'Discard'),
94+
aUserDecision('Block', 2, 'Ignore'),
95+
aUserDecision('Block', 3, 'Ignore'),
96+
]
97+
98+
const actual = sut.aggregateUserDecisionByRequest(decisions, 'aFakeRequestId', 'aFakeSessionId')
99+
assert.ok(actual)
100+
assert.strictEqual(actual?.codewhispererCompletionType, 'Line')
101+
assert.strictEqual(actual?.codewhispererSuggestionState, 'Accept')
102+
})
103+
104+
it('should return Line and Reject', function () {
105+
const decisions: CodewhispererUserDecision[] = [
106+
aUserDecision('Line', 0, 'Discard'),
107+
aUserDecision('Line', 1, 'Reject'),
108+
aUserDecision('Line', 2, 'Unseen'),
109+
aUserDecision('Line', 3, 'Unseen'),
110+
]
111+
112+
const actual = sut.aggregateUserDecisionByRequest(decisions, 'aFakeRequestId', 'aFakeSessionId')
113+
assert.ok(actual)
114+
assert.strictEqual(actual?.codewhispererCompletionType, 'Line')
115+
assert.strictEqual(actual?.codewhispererSuggestionState, 'Reject')
116+
})
117+
118+
it('should return Block and Accept', function () {
119+
const decisions: CodewhispererUserDecision[] = [
120+
aUserDecision('Block', 0, 'Discard'),
121+
aUserDecision('Block', 1, 'Accept'),
122+
aUserDecision('Block', 2, 'Discard'),
123+
aUserDecision('Block', 3, 'Ignore'),
124+
]
125+
126+
const actual = sut.aggregateUserDecisionByRequest(decisions, 'aFakeRequestId', 'aFakeSessionId')
127+
assert.ok(actual)
128+
assert.strictEqual(actual?.codewhispererCompletionType, 'Block')
129+
assert.strictEqual(actual?.codewhispererSuggestionState, 'Accept')
130+
})
131+
})
132+
133+
describe('sendUserTriggerDecisionTelemetry', function () {
134+
let sut: TelemetryHelper
135+
136+
beforeEach(async function () {
137+
await resetCodeWhispererGlobalVariables()
138+
sut = new TelemetryHelper()
139+
})
140+
141+
it('should return Line and Accept', function () {
142+
sut.recordUserDecisionTelemetry(
143+
['aFakeRequestId', 'aFakeRequestId', 'aFakeRequestId2'],
144+
'aFakeSessionId',
145+
[aCompletion(), aCompletion(), aCompletion(), aCompletion()],
146+
0,
147+
0,
148+
new Map([
149+
[0, 'Line'],
150+
[1, 'Line'],
151+
[2, 'Block'],
152+
[3, 'Block'],
153+
])
154+
)
155+
156+
sut.sendUserTriggerDecisionTelemetry('aFakeSessionId', aCompletion().content, 0)
157+
const assertTelemetry = assertTelemetryCurried('codewhisperer_userTriggerDecision')
158+
assertTelemetry({
159+
codewhispererSessionId: 'aFakeSessionId',
160+
codewhispererFirstRequestId: 'aFakeRequestId',
161+
codewhispererLanguage: 'python',
162+
codewhispererTriggerType: 'OnDemand',
163+
codewhispererLineNumber: 0,
164+
codewhispererCursorOffset: 0,
165+
codewhispererSuggestionCount: 4,
166+
codewhispererSuggestionImportCount: 0,
167+
codewhispererSuggestionState: 'Accept',
168+
codewhispererCompletionType: 'Line',
169+
codewhispererTypeaheadLength: 0,
170+
codewhispererCharactersAccepted: aCompletion().content.length,
171+
})
172+
})
173+
174+
it('should return Line and Accept 2', function () {
175+
sut.recordUserDecisionTelemetry(
176+
['aFakeRequestId', 'aFakeRequestId', 'aFakeRequestId2'],
177+
'aFakeSessionId',
178+
[aCompletion(), aCompletion(), aCompletion(), aCompletion()],
179+
3,
180+
0,
181+
new Map([
182+
[0, 'Line'],
183+
[1, 'Line'],
184+
[2, 'Line'],
185+
[3, 'Line'],
186+
])
187+
)
188+
189+
sut.sendUserTriggerDecisionTelemetry('aFakeSessionId', aCompletion().content, 0)
190+
const assertTelemetry = assertTelemetryCurried('codewhisperer_userTriggerDecision')
191+
assertTelemetry({
192+
codewhispererSessionId: 'aFakeSessionId',
193+
codewhispererFirstRequestId: 'aFakeRequestId',
194+
codewhispererLanguage: 'python',
195+
codewhispererTriggerType: 'OnDemand',
196+
codewhispererLineNumber: 0,
197+
codewhispererCursorOffset: 0,
198+
codewhispererSuggestionCount: 4,
199+
codewhispererSuggestionImportCount: 0,
200+
codewhispererSuggestionState: 'Accept',
201+
codewhispererCompletionType: 'Line',
202+
codewhispererTypeaheadLength: 0,
203+
codewhispererCharactersAccepted: aCompletion().content.length,
204+
})
205+
})
206+
207+
it('should return Line and Reject', function () {
208+
sut.recordUserDecisionTelemetry(
209+
['aFakeRequestId', 'aFakeRequestId', 'aFakeRequestId2'],
210+
'aFakeSessionId',
211+
[aCompletion(), aCompletion(), aCompletion(), aCompletion()],
212+
-1,
213+
0,
214+
new Map([
215+
[0, 'Line'],
216+
[1, 'Line'],
217+
[2, 'Line'],
218+
[3, 'Line'],
219+
])
220+
)
221+
222+
sut.sendUserTriggerDecisionTelemetry('aFakeSessionId', '', 0)
223+
const assertTelemetry = assertTelemetryCurried('codewhisperer_userTriggerDecision')
224+
assertTelemetry({
225+
codewhispererSessionId: 'aFakeSessionId',
226+
codewhispererFirstRequestId: 'aFakeRequestId',
227+
codewhispererLanguage: 'python',
228+
codewhispererTriggerType: 'OnDemand',
229+
codewhispererLineNumber: 0,
230+
codewhispererCursorOffset: 0,
231+
codewhispererSuggestionCount: 4,
232+
codewhispererSuggestionImportCount: 0,
233+
codewhispererSuggestionState: 'Reject',
234+
codewhispererCompletionType: 'Line',
235+
codewhispererTypeaheadLength: 0,
236+
codewhispererCharactersAccepted: 0,
237+
})
238+
})
239+
})
240+
241+
describe('getSuggestionState', function () {
242+
let telemetryHelper = new TelemetryHelper()
243+
beforeEach(async function () {
244+
await resetCodeWhispererGlobalVariables()
245+
telemetryHelper = new TelemetryHelper()
246+
})
247+
248+
it('user event is discard when recommendation state is Discarded with accept index = -1', function () {
249+
const actual = telemetryHelper.getSuggestionState(0, -1, new Map([[0, 'Discard']]))
250+
assert.strictEqual(actual, 'Discard')
251+
})
252+
253+
it('user event is reject when recommendation state is Showed with accept index = -1', function () {
254+
const actual = telemetryHelper.getSuggestionState(0, -1, new Map([[0, 'Showed']]))
255+
assert.strictEqual(actual, 'Reject')
256+
})
257+
258+
it('user event is Accept when recommendation state is Showed with accept index matches', function () {
259+
const actual = telemetryHelper.getSuggestionState(0, 0, new Map([[0, 'Showed']]))
260+
assert.strictEqual(actual, 'Accept')
261+
})
262+
263+
it('user event is Ignore when recommendation state is Showed with accept index does not match', function () {
264+
const actual = telemetryHelper.getSuggestionState(0, 1, new Map([[0, 'Showed']]))
265+
assert.strictEqual(actual, 'Ignore')
266+
})
267+
268+
it('user event is Unseen when recommendation state is not Showed, is not Unseen when recommendation is showed', function () {
269+
const actual0 = telemetryHelper.getSuggestionState(0, 1, new Map([[1, 'Showed']]))
270+
assert.strictEqual(actual0, 'Unseen')
271+
const actual1 = telemetryHelper.getSuggestionState(1, 1, new Map([[1, 'Showed']]))
272+
assert.strictEqual(actual1, 'Accept')
273+
})
274+
275+
it('user event is Filter when recommendation state is Filter', function () {
276+
const actual = telemetryHelper.getSuggestionState(0, 1, new Map([[0, 'Filter']]))
277+
assert.strictEqual(actual, 'Filter')
278+
})
279+
280+
it('user event is Empty when recommendation state is Empty', function () {
281+
const actual = telemetryHelper.getSuggestionState(0, 1, new Map([[0, 'Empty']]))
282+
assert.strictEqual(actual, 'Empty')
283+
})
284+
})
51285
})

packages/core/src/codewhisperer/commands/onInlineAcceptance.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import { sleep } from '../../shared/utilities/timeoutUtils'
1515
import { handleExtraBrackets } from '../util/closingBracketUtil'
1616
import { Commands } from '../../shared/vscode/commands2'
1717
import { isInlineCompletionEnabled } from '../util/commonUtil'
18-
import { ExtContext } from '../../shared/extensions'
1918
import { onAcceptance } from './onAcceptance'
2019
import * as codewhispererClient from '../client/codewhisperer'
2120
import {
@@ -36,7 +35,7 @@ import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker'
3635

3736
export const acceptSuggestion = Commands.declare(
3837
'aws.amazonq.accept',
39-
(context: ExtContext) =>
38+
(context: vscode.ExtensionContext) =>
4039
async (
4140
range: vscode.Range,
4241
effectiveRange: vscode.Range,

packages/core/src/codewhisperer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export * as startSecurityScan from './commands/startSecurityScan'
7272
export * from './util/supplementalContext/utgUtils'
7373
export * from './util/supplementalContext/crossFileContextUtil'
7474
export * from './util/editorContext'
75+
export { acceptSuggestion } from './commands/onInlineAcceptance'
7576
export * from './util/showSsoPrompt'
7677
export * from './util/securityScanLanguageContext'
7778
export * from './util/importAdderUtil'

0 commit comments

Comments
 (0)