Skip to content

Commit c3880f0

Browse files
authored
feat(CodeWhisperer) add sending serviceInvocationCount field in codeCoverageTracker (#2895)
Tracker will increment tracker.serviceInvocationCount by 1 if there is a successful service invocation with specific programming language, i.e. if cwspr get invoked successfully with python, only python tracker will increment serviceInvocationCount.
1 parent a84a7c1 commit c3880f0

File tree

9 files changed

+258
-84
lines changed

9 files changed

+258
-84
lines changed

src/codewhisperer/activation.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,9 @@ export async function activate(context: ExtContext): Promise<void> {
337337
}
338338
}
339339

340-
CodeWhispererCodeCoverageTracker.getTracker(
341-
e.document.languageId,
342-
context.extensionContext.globalState
343-
)?.countTotalTokens(e)
340+
const codeCoverageTracker = CodeWhispererCodeCoverageTracker.getTracker(e.document.languageId)
341+
codeCoverageTracker?.countTotalTokens(e)
342+
344343
/**
345344
* Handle this keystroke event only when
346345
* 1. It is in current active editor with cwspr supported file types
@@ -386,8 +385,7 @@ export async function activate(context: ExtContext): Promise<void> {
386385
await InlineCompletion.instance.rejectRecommendation(vscode.window.activeTextEditor)
387386
if (vscode.window.activeTextEditor) {
388387
CodeWhispererCodeCoverageTracker.getTracker(
389-
vscode.window.activeTextEditor.document.languageId,
390-
context.extensionContext.globalState
388+
vscode.window.activeTextEditor.document.languageId
391389
)?.updateAcceptedTokensCount(vscode.window.activeTextEditor)
392390
}
393391
}),
@@ -468,10 +466,7 @@ export async function activate(context: ExtContext): Promise<void> {
468466
}
469467
}
470468

471-
CodeWhispererCodeCoverageTracker.getTracker(
472-
e.document.languageId,
473-
context.extensionContext.globalState
474-
)?.countTotalTokens(e)
469+
CodeWhispererCodeCoverageTracker.getTracker(e.document.languageId)?.countTotalTokens(e)
475470

476471
if (
477472
e.document === vscode.window.activeTextEditor?.document &&

src/codewhisperer/commands/onAcceptance.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export async function onAcceptance(acceptanceEntry: OnRecommendationAcceptanceEn
9797
language: languageContext.language,
9898
})
9999
const codeRangeAfterFormat = new vscode.Range(start, acceptanceEntry.editor.selection.active)
100-
CodeWhispererCodeCoverageTracker.getTracker(languageContext.language, globalStorage)?.countAcceptedTokens(
100+
CodeWhispererCodeCoverageTracker.getTracker(languageContext.language)?.countAcceptedTokens(
101101
codeRangeAfterFormat,
102102
acceptanceEntry.editor.document.getText(codeRangeAfterFormat),
103103
acceptanceEntry.editor.document.fileName

src/codewhisperer/service/recommendationHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
CodewhispererTriggerType,
2525
Result,
2626
} from '../../shared/telemetry/telemetry'
27+
import { CodeWhispererCodeCoverageTracker } from '../tracker/codewhispererCodeCoverageTracker'
2728

2829
/**
2930
* This class is for getRecommendation/listRecommendation API calls and its states
@@ -248,6 +249,9 @@ export class RecommendationHandler {
248249
reason: reason ? reason.substring(0, 200) : undefined,
249250
})
250251
}
252+
if (invocationResult == 'Succeeded') {
253+
CodeWhispererCodeCoverageTracker.getTracker(languageContext.language)?.incrementServiceInvocationCount()
254+
}
251255
if (config.isIncludeSuggestionsWithCodeReferencesEnabled === false) {
252256
recommendation.forEach((r, index) => {
253257
if (r.references !== undefined && r.references.length) {

src/codewhisperer/tracker/codewhispererCodeCoverageTracker.ts

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { vsCodeState } from '../models/model'
1111
import { distance } from 'fastest-levenshtein'
1212
import { CodewhispererLanguage, telemetry } from '../../shared/telemetry/telemetry'
1313
import { runtimeLanguageContext } from '../util/runtimeLanguageContext'
14+
import { TelemetryHelper } from '../util/telemetryHelper'
1415

1516
interface CodeWhispererToken {
1617
range: vscode.Range
@@ -27,32 +28,46 @@ export class CodeWhispererCodeCoverageTracker {
2728
private _timer?: NodeJS.Timer
2829
private _startTime: number
2930
private _language: CodewhispererLanguage
31+
private _serviceInvocationCount: number
3032

31-
private constructor(language: CodewhispererLanguage, private readonly _globals: vscode.Memento) {
33+
private constructor(language: CodewhispererLanguage, private readonly globals: vscode.Memento) {
3234
this._acceptedTokens = {}
3335
this._totalTokens = {}
3436
this._startTime = 0
3537
this._language = language
38+
this._serviceInvocationCount = 0
39+
}
40+
41+
public get serviceInvocationCount(): number {
42+
return this._serviceInvocationCount
3643
}
3744

3845
public get acceptedTokens(): { [key: string]: CodeWhispererToken[] } {
3946
return this._acceptedTokens
4047
}
48+
4149
public get totalTokens(): { [key: string]: number } {
4250
return this._totalTokens
4351
}
4452

53+
public isActive(): boolean {
54+
const isTermsAccepted = this.globals.get<boolean>(CodeWhispererConstants.termsAcceptedKey) || false
55+
return TelemetryHelper.instance.isTelemetryEnabled() && isTermsAccepted
56+
}
57+
4558
public countAcceptedTokens(range: vscode.Range, text: string, filename: string) {
46-
const terms = this._globals.get<boolean>(CodeWhispererConstants.termsAcceptedKey) || false
47-
if (!terms) return
59+
if (!this.isActive()) return
4860
// generate accepted recommendation token and stored in collection
4961
this.addAcceptedTokens(filename, { range: range, text: text, accepted: text.length })
5062
this.addTotalTokens(filename, text.length)
5163
}
5264

65+
public incrementServiceInvocationCount() {
66+
this._serviceInvocationCount += 1
67+
}
68+
5369
public flush() {
54-
const terms = this._globals.get<boolean>(CodeWhispererConstants.termsAcceptedKey) || false
55-
if (!terms) {
70+
if (!this.isActive()) {
5671
this._totalTokens = {}
5772
this._acceptedTokens = {}
5873
this.closeTimer()
@@ -107,7 +122,7 @@ export class CodeWhispererCodeCoverageTracker {
107122
codewhispererLanguage: this._language,
108123
codewhispererAcceptedTokens: acceptedTokens,
109124
codewhispererPercentage: percentage ? percentage : 0,
110-
successCount: 0,
125+
successCount: this._serviceInvocationCount,
111126
})
112127
}
113128

@@ -136,14 +151,19 @@ export class CodeWhispererCodeCoverageTracker {
136151
} catch (e) {
137152
getLogger().verbose(`Exception Thrown from CodeWhispererCodeCoverageTracker: ${e}`)
138153
} finally {
139-
this._totalTokens = {}
140-
this._acceptedTokens = {}
141-
this._startTime = 0
154+
this.resetTracker()
142155
this.closeTimer()
143156
}
144157
}, CodeWhispererConstants.defaultCheckPeriodMillis)
145158
}
146159

160+
private resetTracker() {
161+
this._totalTokens = {}
162+
this._acceptedTokens = {}
163+
this._startTime = 0
164+
this._serviceInvocationCount = 0
165+
}
166+
147167
private closeTimer() {
148168
if (this._timer !== undefined) {
149169
clearTimeout(this._timer)
@@ -190,12 +210,20 @@ export class CodeWhispererCodeCoverageTracker {
190210
}
191211

192212
public static readonly instances = new Map<string, CodeWhispererCodeCoverageTracker>()
193-
public static getTracker(language: string, memento: vscode.Memento): CodeWhispererCodeCoverageTracker | undefined {
194-
if (runtimeLanguageContext.isLanguageSupported(language)) {
195-
const instance = this.instances.get(language) ?? new this(language as CodewhispererLanguage, memento)
196-
this.instances.set(language, instance)
197-
return instance
213+
214+
public static getTracker(
215+
language: string,
216+
memeto: vscode.Memento = globals.context.globalState
217+
): CodeWhispererCodeCoverageTracker | undefined {
218+
if (!runtimeLanguageContext.isLanguageSupported(language)) {
219+
return undefined
220+
}
221+
const cwsprLanguage = runtimeLanguageContext.mapVscLanguageToCodeWhispererLanguage(language)
222+
if (!cwsprLanguage) {
223+
return undefined
198224
}
199-
return undefined
225+
const instance = this.instances.get(language) ?? new this(cwsprLanguage, memeto)
226+
this.instances.set(language, instance)
227+
return instance
200228
}
201229
}

src/codewhisperer/util/telemetryHelper.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
5+
import globals from '../../shared/extensionGlobals'
6+
57
import { runtimeLanguageContext } from './runtimeLanguageContext'
68
import { RecommendationsList } from '../client/codewhisperer'
79
import { LicenseUtil } from './licenseUtil'
@@ -126,4 +128,8 @@ export class TelemetryHelper {
126128
}
127129
return i === acceptIndex ? 'Accept' : 'Ignore'
128130
}
131+
132+
public isTelemetryEnabled(): boolean {
133+
return globals.telemetry.telemetryEnabled
134+
}
129135
}

src/dynamicResources/model/supported_resources.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@
386386
"documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudfront-keygroup.html"
387387
},
388388
"AWS::CloudFront::OriginAccessControl": {
389-
"operations": ["CREATE", "DELETE", "LIST", "READ", "UPDATE" ]
389+
"operations": ["CREATE", "DELETE", "LIST", "READ", "UPDATE"]
390390
},
391391
"AWS::CloudFront::OriginRequestPolicy": {
392392
"operations": ["CREATE", "DELETE", "LIST", "READ", "UPDATE"],
@@ -1796,10 +1796,10 @@
17961796
"documentation": "http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lookoutvision-project.html"
17971797
},
17981798
"AWS::M2::Application": {
1799-
"operations": ["CREATE", "READ", "UPDATE", "DELETE", "LIST" ]
1799+
"operations": ["CREATE", "READ", "UPDATE", "DELETE", "LIST"]
18001800
},
18011801
"AWS::M2::Environment": {
1802-
"operations": ["CREATE", "READ", "UPDATE", "DELETE", "LIST" ]
1802+
"operations": ["CREATE", "READ", "UPDATE", "DELETE", "LIST"]
18031803
},
18041804
"AWS::MSK::BatchScramSecret": {
18051805
"operations": ["CREATE", "DELETE", "READ", "UPDATE"],

src/test/codewhisperer/service/recommendationHandler.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { createMockTextEditor, resetCodeWhispererGlobalVariables } from '../test
1414
import { TelemetryHelper } from '../../../codewhisperer/util/telemetryHelper'
1515
import { RecommendationHandler } from '../../../codewhisperer/service/recommendationHandler'
1616
import { stub } from '../../utilities/stubber'
17+
import { CodeWhispererCodeCoverageTracker } from '../../../codewhisperer/tracker/codewhispererCodeCoverageTracker'
18+
import { FakeMemento } from '../../fakeExtensionContext'
1719

1820
const performance = globalThis.performance ?? require('perf_hooks').performance
1921

@@ -29,6 +31,7 @@ describe('recommendationHandler', function () {
2931
})
3032

3133
describe('getRecommendations', async function () {
34+
const fakeMemeto = new FakeMemento()
3235
const mockClient = stub(DefaultCodeWhispererClient)
3336
const mockEditor = createMockTextEditor()
3437

@@ -44,6 +47,13 @@ describe('recommendationHandler', function () {
4447
})
4548

4649
it('should assign correct recommendations given input', async function () {
50+
assert.strictEqual(CodeWhispererCodeCoverageTracker.instances.size, 0)
51+
assert.strictEqual(
52+
CodeWhispererCodeCoverageTracker.getTracker(mockEditor.document.languageId, fakeMemeto)
53+
?.serviceInvocationCount,
54+
0
55+
)
56+
4757
const mockServerResult = {
4858
recommendations: [{ content: "print('Hello World!')" }, { content: '' }],
4959
$response: {
@@ -61,6 +71,11 @@ describe('recommendationHandler', function () {
6171
const actual = handler.recommendations
6272
const expected: RecommendationsList = [{ content: "print('Hello World!')" }, { content: '' }]
6373
assert.deepStrictEqual(actual, expected)
74+
assert.strictEqual(
75+
CodeWhispererCodeCoverageTracker.getTracker(mockEditor.document.languageId, fakeMemeto)
76+
?.serviceInvocationCount,
77+
1
78+
)
6479
})
6580

6681
it('should assign request id correctly', async function () {

src/test/codewhisperer/testUtil.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ import * as codewhispererClient from '../../codewhisperer/client/codewhisperer'
99
import { vsCodeState, AcceptedSuggestionEntry } from '../../codewhisperer/models/model'
1010
import { MockDocument } from '../fake/fakeDocument'
1111
import { getLogger } from '../../shared/logger'
12+
import { CodeWhispererCodeCoverageTracker } from '../../codewhisperer/tracker/codewhispererCodeCoverageTracker'
1213

1314
export function resetCodeWhispererGlobalVariables() {
1415
vsCodeState.isIntelliSenseActive = false
1516
vsCodeState.isCodeWhispererEditing = false
17+
CodeWhispererCodeCoverageTracker.instances.clear()
1618
}
1719

1820
export function createMockDocument(

0 commit comments

Comments
 (0)